[alsa-devel] [PATCH 1/3] ASoC: magician: fix PXA SSP clock polarity
Follow-up fix needed since "ASoC: pxa-ssp.c fix clock/frame invert".
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com --- sound/soc/pxa/magician.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index a04ddc3..fa0e7e6 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -183,7 +183,7 @@ static int magician_playback_hw_params(struct snd_pcm_substream *substream,
/* set cpu DAI configuration */ ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_DSP_A | - SND_SOC_DAIFMT_IB_IF | SND_SOC_DAIFMT_CBS_CFS); + SND_SOC_DAIFMT_NB_IF | SND_SOC_DAIFMT_CBS_CFS); if (ret < 0) return ret;
This patch removes the I2C board info registration from the codec driver. Instead, device instantiation will be done via global i2c_board_info from board files. At the same time, platform specific configuration is moved to platform data and common power/reset GPIO handling moves into the codec driver.
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com --- include/sound/uda1380.h | 22 ++++++++++ sound/soc/codecs/uda1380.c | 95 +++++++++++++++++++++---------------------- sound/soc/codecs/uda1380.h | 13 ------ sound/soc/pxa/magician.c | 26 ------------ 4 files changed, 68 insertions(+), 88 deletions(-) create mode 100644 include/sound/uda1380.h
diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h new file mode 100644 index 0000000..69f8775 --- /dev/null +++ b/include/sound/uda1380.h @@ -0,0 +1,22 @@ +/* + * UDA1380 ALSA SoC Codec driver + * + * Copyright 2009 Philipp Zabel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef _UDA1380_H +#define _UDA1380_H + +struct uda1380_platform_data { + int gpio_power; + int gpio_reset; + int dac_clk; +#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1 +}; + +#endif /* _UDA1380_H */ diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 5b21594..87f3e12 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -19,20 +19,19 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/string.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/ioctl.h> +#include <linux/gpio.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> -#include <sound/info.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> +#include <sound/uda1380.h>
#include "uda1380.h"
@@ -740,28 +739,59 @@ static struct snd_soc_device *uda1380_socdev;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static int uda1380_i2c_probe(struct i2c_client *i2c, +static int uda1380_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct uda1380_platform_data *pdata = client->dev.platform_data; struct snd_soc_device *socdev = uda1380_socdev; - struct uda1380_setup_data *setup = socdev->codec_data; struct snd_soc_codec *codec = socdev->card->codec; int ret;
- i2c_set_clientdata(i2c, codec); - codec->control_data = i2c; + if (!pdata || !pdata->gpio_power || !pdata->gpio_reset) + return -EINVAL; + + ret = gpio_request(pdata->gpio_power, "uda1380 power"); + if (ret) + goto err_power; + ret = gpio_request(pdata->gpio_reset, "uda1380 reset"); + if (ret) + goto err_reset; + + gpio_direction_output(pdata->gpio_power, 1);
- ret = uda1380_init(socdev, setup->dac_clk); - if (ret < 0) + /* we may need to have the clock running here - pH5 */ + gpio_direction_output(pdata->gpio_reset, 1); + udelay(5); + gpio_set_value(pdata->gpio_reset, 0); + + i2c_set_clientdata(client, codec); + codec->control_data = client; + + ret = uda1380_init(socdev, pdata->dac_clk); + if (ret) { pr_err("uda1380: failed to initialise UDA1380\n"); + goto err_init; + }
+ return 0; + +err_init: + gpio_free(pdata->gpio_reset); +err_reset: + gpio_free(pdata->gpio_power); +err_power: return ret; }
static int uda1380_i2c_remove(struct i2c_client *client) { + struct uda1380_platform_data *pdata = client->dev.platform_data; struct snd_soc_codec *codec = i2c_get_clientdata(client); + kfree(codec->reg_cache); + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_reset); + gpio_free(pdata->gpio_power); return 0; }
@@ -781,55 +811,24 @@ static struct i2c_driver uda1380_i2c_driver = { .id_table = uda1380_i2c_id, };
-static int uda1380_add_i2c_device(struct platform_device *pdev, - const struct uda1380_setup_data *setup) +static int uda1380_add_i2c_device(struct platform_device *pdev) { - struct i2c_board_info info; - struct i2c_adapter *adapter; - struct i2c_client *client; - int ret; + int ret = 0;
ret = i2c_add_driver(&uda1380_i2c_driver); - if (ret != 0) { - dev_err(&pdev->dev, "can't add i2c driver\n"); - return ret; - } - - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = setup->i2c_address; - strlcpy(info.type, "uda1380", I2C_NAME_SIZE); - - adapter = i2c_get_adapter(setup->i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "can't get i2c adapter %d\n", - setup->i2c_bus); - goto err_driver; - } - - client = i2c_new_device(adapter, &info); - i2c_put_adapter(adapter); - if (!client) { - dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", - (unsigned int)info.addr); - goto err_driver; - } - - return 0; + if (ret != 0) + dev_err(&pdev->dev, "can't add i2c driver: %d\n", ret);
-err_driver: - i2c_del_driver(&uda1380_i2c_driver); - return -ENODEV; + return ret; } #endif
static int uda1380_probe(struct platform_device *pdev) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct uda1380_setup_data *setup; struct snd_soc_codec *codec; int ret;
- setup = socdev->codec_data; codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); if (codec == NULL) return -ENOMEM; @@ -843,10 +842,8 @@ static int uda1380_probe(struct platform_device *pdev) ret = -ENODEV;
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; - ret = uda1380_add_i2c_device(pdev, setup); - } + codec->hw_write = (hw_write_t)i2c_master_send; + ret = uda1380_add_i2c_device(pdev); #endif
if (ret != 0) diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index c55c17a..d7904c5 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -8,9 +8,6 @@ * Copyright (c) 2005 Giorgio Padrin giorgio@mandarinlogiq.org */
-#ifndef _UDA1380_H -#define _UDA1380_H - #define UDA1380_CLK 0x00 #define UDA1380_IFACE 0x01 #define UDA1380_PM 0x02 @@ -72,19 +69,9 @@ #define R22_SKIP_DCFIL 0x0002 #define R23_AGC_EN 0x0001
-struct uda1380_setup_data { - int i2c_bus; - unsigned short i2c_address; - int dac_clk; -#define UDA1380_DAC_CLK_SYSCLK 0 -#define UDA1380_DAC_CLK_WSPLL 1 -}; - #define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */ #define UDA1380_DAI_PLAYBACK 1 /* playback DAI */ #define UDA1380_DAI_CAPTURE 2 /* capture DAI */
extern struct snd_soc_dai uda1380_dai[3]; extern struct snd_soc_codec_device soc_codec_dev_uda1380; - -#endif /* _UDA1380_H */ diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index fa0e7e6..cc7dbe5 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -448,17 +448,10 @@ static struct snd_soc_card snd_soc_card_magician = { .platform = &pxa2xx_soc_platform, };
-/* magician audio private data */ -static struct uda1380_setup_data magician_uda1380_setup = { - .i2c_address = 0x18, - .dac_clk = UDA1380_DAC_CLK_WSPLL, -}; - /* magician audio subsystem */ static struct snd_soc_device magician_snd_devdata = { .card = &snd_soc_card_magician, .codec_dev = &soc_codec_dev_uda1380, - .codec_data = &magician_uda1380_setup, };
static struct platform_device *magician_snd_device; @@ -470,12 +463,6 @@ static int __init magician_init(void) if (!machine_is_magician()) return -ENODEV;
- ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER"); - if (ret) - goto err_request_power; - ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET"); - if (ret) - goto err_request_reset; ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); if (ret) goto err_request_spk; @@ -492,14 +479,8 @@ static int __init magician_init(void) if (ret) goto err_request_in_sel1;
- gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1); gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
- /* we may need to have the clock running here - pH5 */ - gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1); - udelay(5); - gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0); - magician_snd_device = platform_device_alloc("soc-audio", -1); if (!magician_snd_device) { ret = -ENOMEM; @@ -527,10 +508,6 @@ err_request_mic: err_request_ep: gpio_free(EGPIO_MAGICIAN_SPK_POWER); err_request_spk: - gpio_free(EGPIO_MAGICIAN_CODEC_RESET); -err_request_reset: - gpio_free(EGPIO_MAGICIAN_CODEC_POWER); -err_request_power: return ret; }
@@ -541,15 +518,12 @@ static void __exit magician_exit(void) gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0); - gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
gpio_free(EGPIO_MAGICIAN_IN_SEL1); gpio_free(EGPIO_MAGICIAN_IN_SEL0); gpio_free(EGPIO_MAGICIAN_MIC_POWER); gpio_free(EGPIO_MAGICIAN_EP_POWER); gpio_free(EGPIO_MAGICIAN_SPK_POWER); - gpio_free(EGPIO_MAGICIAN_CODEC_RESET); - gpio_free(EGPIO_MAGICIAN_CODEC_POWER); }
module_init(magician_init);
Configures I2C address, power/reset GPIOs and DAC clock from WSPLL.
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com --- arch/arm/mach-pxa/magician.c | 18 +++++++++++++++++- 1 files changed, 17 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c index 869f083..3da8284 100644 --- a/arch/arm/mach-pxa/magician.c +++ b/arch/arm/mach-pxa/magician.c @@ -43,6 +43,8 @@ #include <mach/irda.h> #include <mach/ohci.h>
+#include <sound/uda1380.h> + #include "devices.h" #include "generic.h"
@@ -862,9 +864,22 @@ static struct platform_device calypso = { };
/* - * I2C + * Philips UDA1380 on I2C */
+static struct uda1380_platform_data uda1380_info = { + .gpio_power = EGPIO_MAGICIAN_CODEC_POWER, + .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET, + .dac_clk = UDA1380_DAC_CLK_WSPLL, +}; + +static struct i2c_board_info i2c_board_info[] = { + { + I2C_BOARD_INFO("uda1380", 0x18), + .platform_data = &uda1380_info, + }, +}; + static struct i2c_pxa_platform_data i2c_info = { .fast_mode = 1, }; @@ -906,6 +921,7 @@ static void __init magician_init(void) } pxa27x_set_i2c_power_info(NULL); pxa_set_i2c_info(&i2c_info); + i2c_register_board_info(0, ARRAY_AND_SIZE(i2c_board_info)); i2c_register_board_info(1, ARRAY_AND_SIZE(pi2c_board_info)); pxa_set_mci_info(&magician_mci_info); pxa_set_ohci_info(&magician_ohci_info);
On Sun, Jun 14, 2009 at 12:51:32PM +0200, Philipp Zabel wrote:
Configures I2C address, power/reset GPIOs and DAC clock from WSPLL.
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com
Eric, I don't know how you want to deal with the cross tree issues for this patch? It's part of a conversion of the UDA1380 driver to the standard device model so it'll depend on ASoC changes to the UDA1380 driver but obviously there's arch/arm/mach-pxa changes too.
One way of making sure there aren't any problem would be for one of us to apply both patches to a branch which can then be pulled in to both the ASoC and PXA trees. Does that sound sane?
arch/arm/mach-pxa/magician.c | 18 +++++++++++++++++- 1 files changed, 17 insertions(+), 1 deletions(-)
diff --git a/arch/arm/mach-pxa/magician.c b/arch/arm/mach-pxa/magician.c index 869f083..3da8284 100644 --- a/arch/arm/mach-pxa/magician.c +++ b/arch/arm/mach-pxa/magician.c @@ -43,6 +43,8 @@ #include <mach/irda.h> #include <mach/ohci.h>
+#include <sound/uda1380.h>
#include "devices.h" #include "generic.h"
@@ -862,9 +864,22 @@ static struct platform_device calypso = { };
/*
- I2C
*/
- Philips UDA1380 on I2C
+static struct uda1380_platform_data uda1380_info = {
- .gpio_power = EGPIO_MAGICIAN_CODEC_POWER,
- .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET,
- .dac_clk = UDA1380_DAC_CLK_WSPLL,
+};
+static struct i2c_board_info i2c_board_info[] = {
- {
I2C_BOARD_INFO("uda1380", 0x18),
.platform_data = &uda1380_info,
- },
+};
static struct i2c_pxa_platform_data i2c_info = { .fast_mode = 1, }; @@ -906,6 +921,7 @@ static void __init magician_init(void) } pxa27x_set_i2c_power_info(NULL); pxa_set_i2c_info(&i2c_info);
- i2c_register_board_info(0, ARRAY_AND_SIZE(i2c_board_info)); i2c_register_board_info(1, ARRAY_AND_SIZE(pi2c_board_info)); pxa_set_mci_info(&magician_mci_info); pxa_set_ohci_info(&magician_ohci_info);
-- 1.6.3.1
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
On Sun, Jun 14, 2009 at 11:00 PM, Mark Brownbroonie@opensource.wolfsonmicro.com wrote:
On Sun, Jun 14, 2009 at 12:51:32PM +0200, Philipp Zabel wrote:
Configures I2C address, power/reset GPIOs and DAC clock from WSPLL.
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com
Eric, I don't know how you want to deal with the cross tree issues for this patch? It's part of a conversion of the UDA1380 driver to the standard device model so it'll depend on ASoC changes to the UDA1380 driver but obviously there's arch/arm/mach-pxa changes too.
One way of making sure there aren't any problem would be for one of us to apply both patches to a branch which can then be pulled in to both the ASoC and PXA trees. Does that sound sane?
Normally, I'll queue such patch in my pending branch and will be pushed out until the other patch(es) with dependency has been merged upstream.
On Mon, Jun 15, 2009 at 10:06:21AM +0800, Eric Miao wrote:
Normally, I'll queue such patch in my pending branch and will be pushed out until the other patch(es) with dependency has been merged upstream.
That's fine for me.
Philipp, if you want to avoid bisection issues I'd suggest making the first patch shuffle the registration code into the ASoC machine driver then have your PXA patch shuffle that into arch/arm. I did something similar for wm8731 when converting that.
On Sun, Jun 14, 2009 at 12:51:31PM +0200, Philipp Zabel wrote:
This patch removes the I2C board info registration from the codec driver. Instead, device instantiation will be done via global i2c_board_info from board files. At the same time, platform specific configuration is moved to platform data and common power/reset GPIO handling moves into the codec driver.
Thanks for doing this. This should probably either be squashed in with the arch patch that follows.
+struct uda1380_platform_data {
- int gpio_power;
- int gpio_reset;
- int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1
Is it worth changing this to make both options non-zero so that there's no possibility that it could be left zero by default?
+static int uda1380_add_i2c_device(struct platform_device *pdev) {
...
ret = i2c_add_driver(&uda1380_i2c_driver);
If you're doing this conversion the ideal thing is to convert the driver completely so that the driver is registered when the module loads and does the DAI registration once the I2C device probes. The I2C device probe should also do any other initialisation that can be done without the rest of the ASoC subsystem such as registering the GPIOs.
This implies a bit more rearrangment of the registration functions - most of the initialisation can stay in the I2C function but anything that uses the entire ASoC device and the final instantiation of the card should be moved into the ASoC probe function which will be called once the entire card has started. WM8731 is a good template to look at here.
--- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -8,9 +8,6 @@
- Copyright (c) 2005 Giorgio Padrin giorgio@mandarinlogiq.org
*/
-#ifndef _UDA1380_H -#define _UDA1380_H
Best practice would be to keep these with a different name.
On Sun, Jun 14, 2009 at 4:51 PM, Mark Brownbroonie@opensource.wolfsonmicro.com wrote:
On Sun, Jun 14, 2009 at 12:51:31PM +0200, Philipp Zabel wrote:
This patch removes the I2C board info registration from the codec driver. Instead, device instantiation will be done via global i2c_board_info from board files. At the same time, platform specific configuration is moved to platform data and common power/reset GPIO handling moves into the codec driver.
Thanks for doing this. This should probably either be squashed in with the arch patch that follows.
Ok. If Eric agrees, I'll squash as you propose.
+struct uda1380_platform_data {
- int gpio_power;
- int gpio_reset;
- int dac_clk;
+#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1
Is it worth changing this to make both options non-zero so that there's no possibility that it could be left zero by default?
I think this is fine as-is: SYSCLK is the default setting for DAC clock input when the chip comes out of reset.
+static int uda1380_add_i2c_device(struct platform_device *pdev) {
...
ret = i2c_add_driver(&uda1380_i2c_driver);
If you're doing this conversion the ideal thing is to convert the driver completely so that the driver is registered when the module loads and does the DAI registration once the I2C device probes. The I2C device probe should also do any other initialisation that can be done without the rest of the ASoC subsystem such as registering the GPIOs.
Ok.
This implies a bit more rearrangment of the registration functions - most of the initialisation can stay in the I2C function but anything that uses the entire ASoC device and the final instantiation of the card should be moved into the ASoC probe function which will be called once the entire card has started. WM8731 is a good template to look at here.
I'll look at WM8731 for guidance, thanks.
--- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -8,9 +8,6 @@ * Copyright (c) 2005 Giorgio Padrin giorgio@mandarinlogiq.org */
-#ifndef _UDA1380_H -#define _UDA1380_H
Best practice would be to keep these with a different name.
uda1380.h is only a one-time private header file, but I won't say no to best practice.
cheers Philipp
ALSA lib pcm_bluetooth.c:1607:(audioservice_expect) BT_OPEN failed : Invalid argument(22)
** WARNING **: alsa_setup(): Unable to install hw params
I get this all the time now. What does this error mean?
arecord -D bluetoothh550 -f S16_LE | aplay -D bluetoothh550 -f S16_LE Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono ALSA lib pcm_bluetooth.c:1607:(audioservice_expect) BT_OPEN failed : Invalid argument(22) arecord: set_params:1041: Unable to install hw params: ACCESS: RW_INTERLEAVED FORMAT: S16_LE SUBFORMAT: STD SAMPLE_BITS: 16 FRAME_BITS: 16 CHANNELS: 1 RATE: 8000 PERIOD_TIME: 125000 PERIOD_SIZE: 1000 PERIOD_BYTES: 2000 PERIODS: 4 BUFFER_TIME: 500000 BUFFER_SIZE: 4000 BUFFER_BYTES: 8000 TICK_TIME: [0 0] aplay: playback:2297: read error
and here with mplayer
Playing skypering.wav. Audio only file format detected. ========================================================================== Opening audio decoder: [pcm] Uncompressed PCM audio decoder AUDIO: 16000 Hz, 2 ch, s16le, 512.0 kbit/100.00% (ratio: 64000->64000) Selected audio codec: [pcm] afm: pcm (Uncompressed PCM) ========================================================================== [AO_ALSA] alsa-lib: pcm_bluetooth.c:1607:(audioservice_expect) BT_OPEN failed : Invalid argument(22) [AO_ALSA] Unable to set hw-parameters: Invalid argument Failed to initialize audio driver 'alsa:device=bluetoothh550' Could not open/initialize audio device -> no sound. Audio: no sound Video: no video
Mates...
I am running a simple app that I found called pcm.c and I can't get through with that. It creates and send simple sine wave to the speakers!
When I run the app I get the following error:
guilherme@lap:/home/guilherme/Experimentos# ./PCM Playback device is plughw:0,0 Stream parameters are 44100Hz, S16_LE, 1 channels sine wave rate is 0 0.0000Hz Using transfer method: write Playback open error: Device or resource busy
Every application I start playbacks the sound without a problem. Is there any special conf I should do to free the Device?
Here is the code:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sched.h> #include <errno.h> #include <getopt.h> #include <alsa/asoundlib.h> #include <sys/time.h> #include <math.h>
static char *device = "plughw:0,0"; //playback device aplay -l static snd_pcm_format_t format = SND_PCM_FORMAT_S16; //sample format unsigned 16 bit endian static unsigned int rate = 44100; //stream rate static unsigned int channels = 1; //contagem dos canais static unsigned int buffer_time = 500000; //ring buffer length on us static unsigned int period_time = 100000; //period time in us static double freq = 400; // frequencia da senóide em Hz static int verbose = 0; //verbose flag static int resample = 1; //abilitar alsa-lib resampling static int period_event = 0; // produz um event pool a cada periodo
static snd_pcm_sframes_t buffer_size; //quantidade de buffers frames static snd_pcm_sframes_t period_size; //quantidade de periods frames static snd_output_t *output = NULL; //ALSA usa esse ponteiro para lidar com output objects
static void generate_sine(const snd_pcm_channel_area_t *areas, snd_pcm_uframes_t offset, int count, double *_phase) { static double max_phase = 2. * M_PI; double phase = *_phase; double step = max_phase*freq / (double)rate; double res; unsigned char *samples[channels], *tmp; int steps[channels]; unsigned int chn, byte;
union { int i; unsigned char c[4]; } ires; unsigned int maxval = (1 << (snd_pcm_format_width(format) - 1)) - 1; int bps = snd_pcm_format_width(format) / 8; //bits per sample
/*verificar e preparar o conteúdo das areas*/ for (chn = 0; chn < channels; chn++) { if ((areas[chn].first % 8) != 0) { printf("areas[%i].first == %i, abortando...\n", chn, areas[chn].first); exit(EXIT_FAILURE); } //pega endereço e offset da area samples[chn] = /*(signed short *)*/(((unsigned char *)areas[chn].addr) + (areas[chn].first / 8));
if ((areas[chn].step % 16) != 0) { printf("areas[%i].step == %i, aborting...\n", chn, areas[chn].step); exit(EXIT_FAILURE); }
steps[chn] = areas[chn].step / 8; samples[chn] += offset * steps[chn]; }
//completa os channels da area while(count-- > 0) { res = sin(phase) * maxval; ires.i = res; tmp = ires.c; for (chn = 0; chn < channels; chn++) { for (byte = 0; byte < (unsigned int)bps; byte++) *(samples[chn] + byte) = tmp[byte]; samples[chn] += steps[chn]; } phase += step; if (phase >= max_phase) phase -= max_phase; } *_phase = phase;
}
static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { unsigned int rrate; snd_pcm_uframes_t size; int err, dir;
/*escolha todos os parametros*/ err = snd_pcm_hw_params_any(handle, params); if (err < 0) { printf("Broken configuration for playback: no configuration available: %s\n", snd_strerror(err)); return err; }
/*set hardware resampling*/ err = snd_pcm_hw_params_set_rate_resample(handle, params, resample); if (err < 0) { printf("Resampling setup failed for playback: %s\n", snd_strerror(err)); return err; }
/* set the interleaved read/write format */ err = snd_pcm_hw_params_set_access(handle, params, access); if (err < 0) { printf("Access type not available for playback: %s\n", snd_strerror(err)); return err; }
/* set the sample format SND_PCM_FORMAT_S16 */ err = snd_pcm_hw_params_set_format(handle, params, format); if (err < 0) { printf("Sample format not available for playback: %s\n", snd_strerror(err)); return err; }
/* set the count of channels 1 */ err = snd_pcm_hw_params_set_channels(handle, params, channels); if (err < 0) { printf("Channels count (%i) not available for playbacks: %s\n", channels, snd_strerror(err)); return err; }
/* set the stream rate */ rrate = rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &rrate, 0); if (err < 0) { printf("Rate %iHz not available for playback: %s\n", rate, snd_strerror(err)); return err; } if (rrate != rate) { printf("Rate doesn't match (requested %iHz, get %iHz)\n", rate, err); return -EINVAL; } /* set the buffer time */ err = snd_pcm_hw_params_set_buffer_time_near(handle, params, &buffer_time, &dir); if (err < 0) { printf("Unable to set buffer time %i for playback: %s\n", buffer_time, snd_strerror(err)); return err; } err = snd_pcm_hw_params_get_buffer_size(params, &size); if (err < 0) { printf("Unable to get buffer size for playback: %s\n", snd_strerror(err)); return err; } buffer_size = size; /* set the period time */ err = snd_pcm_hw_params_set_period_time_near(handle, params, &period_time, &dir); if (err < 0) { printf("Unable to set period time %i for playback: %s\n", period_time, snd_strerror(err)); return err; } err = snd_pcm_hw_params_get_period_size(params, &size, &dir); if (err < 0) { printf("Unable to get period size for playback: %s\n", snd_strerror(err)); return err; } period_size = size; /* write the parameters to device */ err = snd_pcm_hw_params(handle, params); if (err < 0) { printf("Unable to set hw params for playback: %s\n", snd_strerror(err)); return err; } return 0; }
static int set_swparams(snd_pcm_t *handle, snd_pcm_sw_params_t *swparams) {
int err;
/*pegar os parametros atuais swparams*/ err = snd_pcm_sw_params_current(handle, swparams); if (err < 0) { printf("Unable to determine current swparams for playback: %s\n", snd_strerror(err)); return err; } /*Começa a transferência quando o buffer está quase cheio*/ /*(buffer_size / avail_min) * avail_min */ err = snd_pcm_sw_params_set_avail_min(handle, swparams, (buffer_size / period_size) * period_size); if (err < 0) { printf("Unable to set start threshold mode for playback: %s\n", snd_strerror(err)); return err; } /* allow the transfer when at least period_size samples can be processed */ /* or disable this mechanism when period event is enabled (aka interrupt like style processing) */ err = snd_pcm_sw_params_set_avail_min(handle, swparams, period_event ? buffer_size : period_size); if (err < 0) { printf("Unable to set avail min for playback: %s\n", snd_strerror(err)); return err; } /* enable period events when requested */ if (period_event) { err = snd_pcm_sw_params_set_period_event(handle, swparams, 1); if (err < 0) { printf("Unable to set period event: %s\n", snd_strerror(err)); return err; } } /* write the parameters to the playback device */ err = snd_pcm_sw_params(handle, swparams); if (err < 0) { printf("Unable to set sw params for playback: %s\n", snd_strerror(err)); return err; } return 0; }
/* Tenta recuperar estados de underrun e suspend */
static int xrun_recovery(snd_pcm_t *handle, int err) { if (verbose) printf("stream recovery\n"); if (err == -EPIPE) { //under-run*/ err = snd_pcm_prepare(handle); if (err < 0) printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err)); return 0; } else if (err == -ESTRPIPE) { while ((err = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); /*espera pela release flag*/ if (err < 0) { err = snd_pcm_prepare(handle); if (err < 0) printf("Can't recovery from suspend, prepare failed: %s\n", snd_strerror(err)); } return 0; } return err; }
/* Metodo de transferência *WRITE-ONLY* */ static int write_loop(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas) { double phase = 0; signed short *ptr; int err, cptr;
while(1) { generate_sine(areas, 0, period_size, &phase); ptr = samples; cptr = period_size; while (cptr > 0) { err = snd_pcm_writei(handle, ptr, cptr); if (err == -EAGAIN) continue; if (err < 0) { if (xrun_recovery(handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); }//if ptr += err * channels; cptr -= err; }//if }//if
}//while }
/* * Método de transferência - write and wait por espaço no buffer */
static int wait_for_poll(snd_pcm_t *handle, struct pollfd *ufds, unsigned int count) { unsigned short revents;
while (1) { poll(ufds, count, -1); snd_pcm_poll_descriptors_revents(handle, ufds, count, &revents); if (revents & POLLERR) return -EIO; if (revents & POLLOUT) return 0; } }
static int write_and_poll_loop(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas) { struct pollfd *ufds; double phase = 0; signed short *ptr; int err, count, cptr, init;
count = snd_pcm_poll_descriptors_count (handle); if (count <= 0) { printf("Invalid poll descriptors count\n"); return count; }
ufds = malloc(sizeof(struct pollfd) * count); if (ufds == NULL) { printf("No enough memory\n"); return -ENOMEM; } if ((err = snd_pcm_poll_descriptors(handle, ufds, count)) < 0) { printf("Unable to obtain poll descriptors for playback: %s\n", snd_strerror(err)); return err; }
init = 1; while (1) { if (!init) { err = wait_for_poll(handle, ufds, count); if (err < 0) { if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN || snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; if (xrun_recovery(handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } init = 1; } else { printf("Wait for poll failed\n"); return err; } } }
generate_sine(areas, 0, period_size, &phase); ptr = samples; cptr = period_size; while (cptr > 0) { err = snd_pcm_writei(handle, ptr, cptr); if (err < 0) { if (xrun_recovery(handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } init = 1; break; /* skip one period */ } if (snd_pcm_state(handle) == SND_PCM_STATE_RUNNING) init = 0; ptr += err * channels; cptr -= err; if (cptr == 0) break; /* it is possible, that the initial buffer cannot store */ /* all data from the last period, so wait awhile */ err = wait_for_poll(handle, ufds, count); if (err < 0) { if (snd_pcm_state(handle) == SND_PCM_STATE_XRUN || snd_pcm_state(handle) == SND_PCM_STATE_SUSPENDED) { err = snd_pcm_state(handle) == SND_PCM_STATE_XRUN ? -EPIPE : -ESTRPIPE; if (xrun_recovery(handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } init = 1; } else { printf("Wait for poll failed\n"); return err; } } } } }
/* * Metodo de transferencia - Notificação assincrona */
struct async_private_data { signed short *samples; snd_pcm_channel_area_t *areas; double phase; };
static void async_callback(snd_async_handler_t *ahandler) { snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); signed short *samples = data->samples; snd_pcm_channel_area_t *areas = data->areas; snd_pcm_sframes_t avail; int err;
avail = snd_pcm_avail_update(handle); while (avail >= period_size) { generate_sine(areas, 0, period_size, &data->phase); err = snd_pcm_writei(handle, samples, period_size); if (err < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if (err != period_size) { printf("Write error: written %i expected %li\n", err, period_size); exit(EXIT_FAILURE); } avail = snd_pcm_avail_update(handle); } }
static int async_loop(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas) { struct async_private_data data; snd_async_handler_t *ahandler; int err, count;
data.samples = samples; data.areas = areas; data.phase = 0; err = snd_async_add_pcm_handler(&ahandler, handle, async_callback, &data); if (err < 0) { printf("Unable to register async handler\n"); exit(EXIT_FAILURE); } for (count = 0; count < 2; count++) { generate_sine(areas, 0, period_size, &data.phase); err = snd_pcm_writei(handle, samples, period_size); if (err < 0) { printf("Initial write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if (err != period_size) { printf("Initial write error: written %i expected %li\n", err, period_size); exit(EXIT_FAILURE); } } if (snd_pcm_state(handle) == SND_PCM_STATE_PREPARED) { err = snd_pcm_start(handle); if (err < 0) { printf("Start error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } }
/* because all other work is done in the signal handler, suspend the process */ while (1) { sleep(1); } }
/* * Transfer method - asynchronous notification + direct write */
static void async_direct_callback(snd_async_handler_t *ahandler) { snd_pcm_t *handle = snd_async_handler_get_pcm(ahandler); struct async_private_data *data = snd_async_handler_get_callback_private(ahandler); const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames, size; snd_pcm_sframes_t avail, commitres; snd_pcm_state_t state; int first = 0, err;
while (1) { state = snd_pcm_state(handle); if (state == SND_PCM_STATE_XRUN) { err = xrun_recovery(handle, -EPIPE); if (err < 0) { printf("XRUN recovery failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } else if (state == SND_PCM_STATE_SUSPENDED) { err = xrun_recovery(handle, -ESTRPIPE); if (err < 0) { printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } avail = snd_pcm_avail_update(handle); if (avail < 0) { err = xrun_recovery(handle, avail); if (err < 0) { printf("avail update failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; continue; } if (avail < period_size) { if (first) { first = 0; err = snd_pcm_start(handle); if (err < 0) { printf("Start error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } else { break; } continue; } size = period_size; while (size > 0) { frames = size; err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); if (err < 0) { if ((err = xrun_recovery(handle, err)) < 0) { printf("MMAP begin avail error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } generate_sine(my_areas, offset, frames, &data->phase); commitres = snd_pcm_mmap_commit(handle, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { printf("MMAP commit error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } size -= frames; } } }
static int async_direct_loop(snd_pcm_t *handle, signed short *samples ATTRIBUTE_UNUSED, snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED) { struct async_private_data data; snd_async_handler_t *ahandler; const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames, size; snd_pcm_sframes_t commitres; int err, count;
data.samples = NULL; /* we do not require the global sample area for direct write */ data.areas = NULL; /* we do not require the global areas for direct write */ data.phase = 0; err = snd_async_add_pcm_handler(&ahandler, handle, async_direct_callback, &data); if (err < 0) { printf("Unable to register async handler\n"); exit(EXIT_FAILURE); } for (count = 0; count < 2; count++) { size = period_size; while (size > 0) { frames = size; err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); if (err < 0) { if ((err = xrun_recovery(handle, err)) < 0) { printf("MMAP begin avail error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } generate_sine(my_areas, offset, frames, &data.phase); commitres = snd_pcm_mmap_commit(handle, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { printf("MMAP commit error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } size -= frames; } } err = snd_pcm_start(handle); if (err < 0) { printf("Start error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); }
/* because all other work is done in the signal handler, suspend the process */ while (1) { sleep(1); } }
/* * Transfer method - direct write only */
static int direct_loop(snd_pcm_t *handle, signed short *samples ATTRIBUTE_UNUSED, snd_pcm_channel_area_t *areas ATTRIBUTE_UNUSED) { double phase = 0; const snd_pcm_channel_area_t *my_areas; snd_pcm_uframes_t offset, frames, size; snd_pcm_sframes_t avail, commitres; snd_pcm_state_t state; int err, first = 1;
while (1) { state = snd_pcm_state(handle); if (state == SND_PCM_STATE_XRUN) { err = xrun_recovery(handle, -EPIPE); if (err < 0) { printf("XRUN recovery failed: %s\n", snd_strerror(err)); return err; } first = 1; } else if (state == SND_PCM_STATE_SUSPENDED) { err = xrun_recovery(handle, -ESTRPIPE); if (err < 0) { printf("SUSPEND recovery failed: %s\n", snd_strerror(err)); return err; } } avail = snd_pcm_avail_update(handle); if (avail < 0) { err = xrun_recovery(handle, avail); if (err < 0) { printf("avail update failed: %s\n", snd_strerror(err)); return err; } first = 1; continue; } if (avail < period_size) { if (first) { first = 0; err = snd_pcm_start(handle); if (err < 0) { printf("Start error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } } else { err = snd_pcm_wait(handle, -1); if (err < 0) { if ((err = xrun_recovery(handle, err)) < 0) { printf("snd_pcm_wait error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } } continue; } size = period_size; while (size > 0) { frames = size; err = snd_pcm_mmap_begin(handle, &my_areas, &offset, &frames); if (err < 0) { if ((err = xrun_recovery(handle, err)) < 0) { printf("MMAP begin avail error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } generate_sine(my_areas, offset, frames, &phase); commitres = snd_pcm_mmap_commit(handle, offset, frames); if (commitres < 0 || (snd_pcm_uframes_t)commitres != frames) { if ((err = xrun_recovery(handle, commitres >= 0 ? -EPIPE : commitres)) < 0) { printf("MMAP commit error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } first = 1; } size -= frames; } } }
/* * Transfer method - direct write only using mmap_write functions */
static int direct_write_loop(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas) { double phase = 0; signed short *ptr; int err, cptr;
while (1) { generate_sine(areas, 0, period_size, &phase); ptr = samples; cptr = period_size; while (cptr > 0) { err = snd_pcm_mmap_writei(handle, ptr, cptr); if (err == -EAGAIN) continue; if (err < 0) { if (xrun_recovery(handle, err) < 0) { printf("Write error: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } break; /* skip one period */ } ptr += err * channels; cptr -= err; } } }
struct transfer_method { const char *name; snd_pcm_access_t access; int (*transfer_loop)(snd_pcm_t *handle, signed short *samples, snd_pcm_channel_area_t *areas); };
static struct transfer_method transfer_methods[] = { { "write", SND_PCM_ACCESS_RW_INTERLEAVED, write_loop }, { "write_and_poll", SND_PCM_ACCESS_RW_INTERLEAVED, write_and_poll_loop }, { "async", SND_PCM_ACCESS_RW_INTERLEAVED, async_loop }, { "async_direct", SND_PCM_ACCESS_MMAP_INTERLEAVED, async_direct_loop }, { "direct_interleaved", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_loop }, { "direct_noninterleaved", SND_PCM_ACCESS_MMAP_NONINTERLEAVED, direct_loop }, { "direct_write", SND_PCM_ACCESS_MMAP_INTERLEAVED, direct_write_loop }, { NULL, SND_PCM_ACCESS_RW_INTERLEAVED, NULL } };
static void help(void) { int k; printf( "Usage: pcm [OPTION]... [FILE]...\n" "-h,--help help\n" "-D,--device playback device\n" "-r,--rate stream rate in Hz\n" "-c,--channels count of channels in stream\n" "-f,--frequency sine wave frequency in Hz\n" "-b,--buffer ring buffer size in us\n" "-p,--period period size in us\n" "-m,--method transfer method\n" "-o,--format sample format\n" "-v,--verbose show the PCM setup parameters\n" "-n,--noresample do not resample\n" "-e,--pevent enable poll event after each period\n" "\n"); printf("Recognized sample formats are:"); for (k = 0; k < SND_PCM_FORMAT_LAST; ++k) { const char *s = snd_pcm_format_name(k); if (s) printf(" %s", s); } printf("\n"); printf("Recognized transfer methods are:"); for (k = 0; transfer_methods[k].name; k++) printf(" %s", transfer_methods[k].name); printf("\n"); }
int main (int argc, char *argv[]) { struct option long_option[] = { {"help", 0, NULL, 'h'}, {"device", 1, NULL, 'd'}, {"rate", 1, NULL, 'r'}, {"channels", 1, NULL, 'c'}, {"frequency", 1, NULL, 'f'}, {"buffer", 1, NULL, 'b'}, {"period", 1, NULL, 'p'}, {"method", 1, NULL, 'm'}, {"format", 1, NULL, 'o'}, {"verbose", 1, NULL, 'v'}, {"noresample", 1, NULL, 'n'}, {"pevent", 1, NULL, 'e'}, {NULL, 0, NULL, 0}, };
snd_pcm_t *handle; //ponteiro para pcm handle int err, morehelp; snd_pcm_hw_params_t *hwparams; //parametros de hardware snd_pcm_sw_params_t *swparams; //parametros de software int method = 0; signed short *samples; unsigned int chn; snd_pcm_channel_area_t *areas;
//snd_pcm_hw_params_alloca(&hwparams); //snd_pcm_sw_params_alloca(&swparams);
morehelp = 0; while (1) { int c; if ((c = getopt_long(argc, argv, "hd:r:c:f:b:p:m:o:vne", long_option, NULL)) < 0) break; switch(c) { case 'h': morehelp++; break; case 'd': device = strdup(optarg); break; case 'r': rate = atoi(optarg); rate = rate < 4000 ? 4000 : rate; rate = rate > 196000 ? 19600 : rate; break; case 'c': channels = atoi(optarg); channels = channels < 1 ? 1 : channels; channels = channels > 1024 ? 1024 : channels; break; case 'f': freq = atoi(optarg); freq = freq < 50 ? 50 : freq; freq = freq > 5000 ? 5000 : freq; break; case 'b': buffer_time = atoi(optarg); buffer_time = buffer_time < 1000 ? 1000 : buffer_time; buffer_time = buffer_time > 1000000 ? 1000000 : buffer_time; break; case 'p': period_time = atoi(optarg); period_time = period_time < 1000 ? 1000 : period_time; period_time = period_time > 1000000 ? 1000000 : period_time; break; case 'm': for (method = 0; transfer_methods[method].name; method++) if (!strcasecmp(transfer_methods[method].name, optarg)) break; if (transfer_methods[method].name == NULL) method = 0; break; case 'o': for (format = 0; format < SND_PCM_FORMAT_LAST; format++) { const char *format_name = snd_pcm_format_name(format); if (format_name) if (!strcasecmp(format_name, optarg)) break; } if (format == SND_PCM_FORMAT_LAST) format = SND_PCM_FORMAT_S16; break; case 'v': verbose = 1; break; case 'n': resample = 0; break; case 'e': period_event = 1; break; }//switch }//while
if(morehelp) { help(); return 0; }
//cria um novo output objeto err = snd_output_stdio_attach(&output, &stdout, 0); if (err < 0) { printf("Output failed: %s\n", snd_strerror(err)); return 0; }
printf("Playback device is %s\n", device); printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels); printf("sine wave rate is %i %.4fHz\n", freq); printf("Using transfer method: %s\n", transfer_methods[method].name);
if ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { printf("Playback open error: %s\n", snd_strerror(err)); return 0; }
if ((err = set_hwparams(handle, hwparams, transfer_methods[method].access)) < 0) { printf("Setting of hwparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); } if ((err = set_swparams(handle, swparams)) < 0) { printf("Setting of swparams failed: %s\n", snd_strerror(err)); exit(EXIT_FAILURE); }
if (verbose > 0) snd_pcm_dump(handle, output);
samples = malloc((period_size * channels * snd_pcm_format_physical_width(format)) / 8); if (samples == NULL) { printf("No enough memory\n"); exit(EXIT_FAILURE); }
areas = calloc(channels, sizeof(snd_pcm_channel_area_t)); if (areas == NULL) { printf("No enough memory\n"); exit(EXIT_FAILURE); } for (chn = 0; chn < channels; chn++) { areas[chn].addr = samples; areas[chn].first = chn * snd_pcm_format_physical_width(format); areas[chn].step = channels * snd_pcm_format_physical_width(format); }
err = transfer_methods[method].transfer_loop(handle, samples, areas); if (err < 0) printf("Transfer failed: %s\n", snd_strerror(err));
free(areas); free(samples); snd_pcm_close(handle); return 0; }
Thanks in advance;
Tks!
-------------------
Guilherme Longo Dept. Eng. da Computação Unaerp
Linux User - #484927
*Before Asking http://www.istf.com.br/?page=perguntas
!- I'd rather die on my feet than live on my knees -!
Ted T. Logan wrote:
ALSA lib pcm_bluetooth.c:1607:(audioservice_expect) BT_OPEN failed : Invalid argument(22)
** WARNING **: alsa_setup(): Unable to install hw params
I get this all the time now. What does this error mean?
arecord -D bluetoothh550 -f S16_LE | aplay -D bluetoothh550 -f S16_LE Recording WAVE 'stdin' : Signed 16 bit Little Endian, Rate 8000 Hz, Mono ALSA lib pcm_bluetooth.c:1607:(audioservice_expect) BT_OPEN failed : Invalid argument(22) arecord: set_params:1041: Unable to install hw params: ACCESS: RW_INTERLEAVED FORMAT: S16_LE SUBFORMAT: STD SAMPLE_BITS: 16 FRAME_BITS: 16 CHANNELS: 1 RATE: 8000 PERIOD_TIME: 125000 PERIOD_SIZE: 1000 PERIOD_BYTES: 2000 PERIODS: 4 BUFFER_TIME: 500000 BUFFER_SIZE: 4000 BUFFER_BYTES: 8000 TICK_TIME: [0 0] aplay: playback:2297: read error
and here with mplayer
Playing skypering.wav. Audio only file format detected. ========================================================================== Opening audio decoder: [pcm] Uncompressed PCM audio decoder AUDIO: 16000 Hz, 2 ch, s16le, 512.0 kbit/100.00% (ratio: 64000->64000) Selected audio codec: [pcm] afm: pcm (Uncompressed PCM) ========================================================================== [AO_ALSA] alsa-lib: pcm_bluetooth.c:1607:(audioservice_expect) BT_OPEN failed : Invalid argument(22) [AO_ALSA] Unable to set hw-parameters: Invalid argument Failed to initialize audio driver 'alsa:device=bluetoothh550' Could not open/initialize audio device -> no sound. Audio: no sound Video: no video
Alsa-devel mailing list Alsa-devel@alsa-project.org http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
This patch mostly follows commit 5998102b9095fdb7c67755812038612afea315c5 "ASoC: Refactor WM8731 device registration" to make UDA1380 use standard device instantiation. Similarly, the I2C device registration temporarily moves into the magician machine driver before it will find its final resting place in the board file.
At the same time, platform specific configuration is moved to platform data and common power/reset GPIO handling moves into the codec driver.
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com --- include/sound/uda1380.h | 22 +++ sound/soc/codecs/uda1380.c | 313 ++++++++++++++++++++++++-------------------- sound/soc/codecs/uda1380.h | 8 - sound/soc/pxa/magician.c | 54 ++++---- 4 files changed, 221 insertions(+), 176 deletions(-) create mode 100644 include/sound/uda1380.h
diff --git a/include/sound/uda1380.h b/include/sound/uda1380.h new file mode 100644 index 0000000..381319c --- /dev/null +++ b/include/sound/uda1380.h @@ -0,0 +1,22 @@ +/* + * UDA1380 ALSA SoC Codec driver + * + * Copyright 2009 Philipp Zabel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __UDA1380_H +#define __UDA1380_H + +struct uda1380_platform_data { + int gpio_power; + int gpio_reset; + int dac_clk; +#define UDA1380_DAC_CLK_SYSCLK 0 +#define UDA1380_DAC_CLK_WSPLL 1 +}; + +#endif /* __UDA1380_H */ diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 5b21594..92ec034 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -5,9 +5,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * - * Copyright (c) 2007 Philipp Zabel philipp.zabel@gmail.com - * Improved support for DAPM and audio routing/mixing capabilities, - * added TLV support. + * Copyright (c) 2007-2009 Philipp Zabel philipp.zabel@gmail.com * * Modified by Richard Purdie richard@openedhand.com to fit into SoC * codec model. @@ -19,26 +17,32 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> -#include <linux/string.h> #include <linux/slab.h> #include <linux/errno.h> -#include <linux/ioctl.h> +#include <linux/gpio.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/workqueue.h> #include <sound/core.h> #include <sound/control.h> #include <sound/initval.h> -#include <sound/info.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> +#include <sound/uda1380.h>
#include "uda1380.h"
-static struct work_struct uda1380_work; static struct snd_soc_codec *uda1380_codec;
+/* codec private data */ +struct uda1380_priv { + struct snd_soc_codec codec; + u16 reg_cache[UDA1380_CACHEREGNUM]; + unsigned int dac_clk; + struct work_struct work; +}; + /* * uda1380 register cache */ @@ -473,6 +477,7 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_codec *codec = socdev->card->codec; + struct uda1380_priv *uda1380 = codec->private_data; int mixer = uda1380_read_reg_cache(codec, UDA1380_MIXER);
switch (cmd) { @@ -480,13 +485,13 @@ static int uda1380_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: uda1380_write_reg_cache(codec, UDA1380_MIXER, mixer & ~R14_SILENCE); - schedule_work(&uda1380_work); + schedule_work(&uda1380->work); break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: uda1380_write_reg_cache(codec, UDA1380_MIXER, mixer | R14_SILENCE); - schedule_work(&uda1380_work); + schedule_work(&uda1380->work); break; } return 0; @@ -670,44 +675,33 @@ static int uda1380_resume(struct platform_device *pdev) return 0; }
-/* - * initialise the UDA1380 driver - * register mixer and dsp interfaces with the kernel - */ -static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) +static int uda1380_probe(struct platform_device *pdev) { - struct snd_soc_codec *codec = socdev->card->codec; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_codec *codec; + struct uda1380_platform_data *pdata; int ret = 0;
- codec->name = "UDA1380"; - codec->owner = THIS_MODULE; - codec->read = uda1380_read_reg_cache; - codec->write = uda1380_write; - codec->set_bias_level = uda1380_set_bias_level; - codec->dai = uda1380_dai; - codec->num_dai = ARRAY_SIZE(uda1380_dai); - codec->reg_cache = kmemdup(uda1380_reg, sizeof(uda1380_reg), - GFP_KERNEL); - if (codec->reg_cache == NULL) - return -ENOMEM; - codec->reg_cache_size = ARRAY_SIZE(uda1380_reg); - codec->reg_cache_step = 1; - uda1380_reset(codec); + if (uda1380_codec == NULL) { + dev_err(&pdev->dev, "Codec device not registered\n"); + return -ENODEV; + }
- uda1380_codec = codec; - INIT_WORK(&uda1380_work, uda1380_flush_work); + socdev->card->codec = uda1380_codec; + codec = uda1380_codec; + pdata = codec->dev->platform_data;
/* register pcms */ ret = snd_soc_new_pcms(socdev, SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1); if (ret < 0) { - pr_err("uda1380: failed to create pcms\n"); + dev_err(codec->dev, "failed to create pcms: %d\n", ret); goto pcm_err; }
/* power on device */ uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* set clock input */ - switch (dac_clk) { + switch (pdata->dac_clk) { case UDA1380_DAC_CLK_SYSCLK: uda1380_write(codec, UDA1380_CLK, 0); break; @@ -716,13 +710,12 @@ static int uda1380_init(struct snd_soc_device *socdev, int dac_clk) break; }
- /* uda1380 init */ snd_soc_add_controls(codec, uda1380_snd_controls, ARRAY_SIZE(uda1380_snd_controls)); uda1380_add_widgets(codec); ret = snd_soc_init_card(socdev); if (ret < 0) { - pr_err("uda1380: failed to register card\n"); + dev_err(codec->dev, "failed to register card: %d\n", ret); goto card_err; }
@@ -732,165 +725,201 @@ card_err: snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); pcm_err: - kfree(codec->reg_cache); return ret; }
-static struct snd_soc_device *uda1380_socdev; - -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - -static int uda1380_i2c_probe(struct i2c_client *i2c, - const struct i2c_device_id *id) +/* power down chip */ +static int uda1380_remove(struct platform_device *pdev) { - struct snd_soc_device *socdev = uda1380_socdev; - struct uda1380_setup_data *setup = socdev->codec_data; + struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->card->codec; - int ret; - - i2c_set_clientdata(i2c, codec); - codec->control_data = i2c;
- ret = uda1380_init(socdev, setup->dac_clk); - if (ret < 0) - pr_err("uda1380: failed to initialise UDA1380\n"); + if (codec->control_data) + uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
- return ret; -} + snd_soc_free_pcms(socdev); + snd_soc_dapm_free(socdev);
-static int uda1380_i2c_remove(struct i2c_client *client) -{ - struct snd_soc_codec *codec = i2c_get_clientdata(client); - kfree(codec->reg_cache); return 0; }
-static const struct i2c_device_id uda1380_i2c_id[] = { - { "uda1380", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); - -static struct i2c_driver uda1380_i2c_driver = { - .driver = { - .name = "UDA1380 I2C Codec", - .owner = THIS_MODULE, - }, - .probe = uda1380_i2c_probe, - .remove = uda1380_i2c_remove, - .id_table = uda1380_i2c_id, +struct snd_soc_codec_device soc_codec_dev_uda1380 = { + .probe = uda1380_probe, + .remove = uda1380_remove, + .suspend = uda1380_suspend, + .resume = uda1380_resume, }; +EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380);
-static int uda1380_add_i2c_device(struct platform_device *pdev, - const struct uda1380_setup_data *setup) +static int uda1380_register(struct uda1380_priv *uda1380) { - struct i2c_board_info info; - struct i2c_adapter *adapter; - struct i2c_client *client; - int ret; + int ret, i; + struct snd_soc_codec *codec = &uda1380->codec; + struct uda1380_platform_data *pdata = codec->dev->platform_data;
- ret = i2c_add_driver(&uda1380_i2c_driver); - if (ret != 0) { - dev_err(&pdev->dev, "can't add i2c driver\n"); - return ret; + if (uda1380_codec) { + dev_err(codec->dev, "Another UDA1380 is registered\n"); + return -EINVAL; + } + + if (!pdata || !pdata->gpio_power || !pdata->gpio_reset) + return -EINVAL; + + ret = gpio_request(pdata->gpio_power, "uda1380 power"); + if (ret) + goto err_out; + ret = gpio_request(pdata->gpio_reset, "uda1380 reset"); + if (ret) + goto err_gpio; + + gpio_direction_output(pdata->gpio_power, 1); + + /* we may need to have the clock running here - pH5 */ + gpio_direction_output(pdata->gpio_reset, 1); + udelay(5); + gpio_set_value(pdata->gpio_reset, 0); + + mutex_init(&codec->mutex); + INIT_LIST_HEAD(&codec->dapm_widgets); + INIT_LIST_HEAD(&codec->dapm_paths); + + codec->private_data = uda1380; + codec->name = "UDA1380"; + codec->owner = THIS_MODULE; + codec->read = uda1380_read_reg_cache; + codec->write = uda1380_write; + codec->bias_level = SND_SOC_BIAS_OFF; + codec->set_bias_level = uda1380_set_bias_level; + codec->dai = uda1380_dai; + codec->num_dai = ARRAY_SIZE(uda1380_dai); + codec->reg_cache_size = ARRAY_SIZE(uda1380_reg); + codec->reg_cache = &uda1380->reg_cache; + codec->reg_cache_step = 1; + + memcpy(codec->reg_cache, uda1380_reg, sizeof(uda1380_reg)); + + ret = uda1380_reset(codec); + if (ret < 0) { + dev_err(codec->dev, "Failed to issue reset\n"); + goto err_reset; }
- memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = setup->i2c_address; - strlcpy(info.type, "uda1380", I2C_NAME_SIZE); + INIT_WORK(&uda1380->work, uda1380_flush_work); + + for (i = 0; i < ARRAY_SIZE(uda1380_dai); i++) + uda1380_dai[i].dev = codec->dev;
- adapter = i2c_get_adapter(setup->i2c_bus); - if (!adapter) { - dev_err(&pdev->dev, "can't get i2c adapter %d\n", - setup->i2c_bus); - goto err_driver; + uda1380_codec = codec; + + ret = snd_soc_register_codec(codec); + if (ret != 0) { + dev_err(codec->dev, "Failed to register codec: %d\n", ret); + goto err_reset; }
- client = i2c_new_device(adapter, &info); - i2c_put_adapter(adapter); - if (!client) { - dev_err(&pdev->dev, "can't add i2c device at 0x%x\n", - (unsigned int)info.addr); - goto err_driver; + ret = snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); + if (ret != 0) { + dev_err(codec->dev, "Failed to register DAIs: %d\n", ret); + goto err_dai; }
return 0;
-err_driver: - i2c_del_driver(&uda1380_i2c_driver); - return -ENODEV; +err_dai: + snd_soc_unregister_codec(codec); +err_reset: + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_reset); +err_gpio: + gpio_free(pdata->gpio_power); +err_out: + return ret; } -#endif
-static int uda1380_probe(struct platform_device *pdev) +static void uda1380_unregister(struct uda1380_priv *uda1380) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct uda1380_setup_data *setup; + struct snd_soc_codec *codec = &uda1380->codec; + struct uda1380_platform_data *pdata = codec->dev->platform_data; + + snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); + snd_soc_unregister_codec(&uda1380->codec); + + gpio_set_value(pdata->gpio_power, 0); + gpio_free(pdata->gpio_reset); + gpio_free(pdata->gpio_power); + + kfree(uda1380); + uda1380_codec = NULL; +} + +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) +static __devinit int uda1380_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct uda1380_priv *uda1380; struct snd_soc_codec *codec; int ret;
- setup = socdev->codec_data; - codec = kzalloc(sizeof(struct snd_soc_codec), GFP_KERNEL); - if (codec == NULL) + uda1380 = kzalloc(sizeof(struct uda1380_priv), GFP_KERNEL); + if (uda1380 == NULL) return -ENOMEM;
- socdev->card->codec = codec; - mutex_init(&codec->mutex); - INIT_LIST_HEAD(&codec->dapm_widgets); - INIT_LIST_HEAD(&codec->dapm_paths); + codec = &uda1380->codec; + codec->hw_write = (hw_write_t)i2c_master_send;
- uda1380_socdev = socdev; - ret = -ENODEV; + i2c_set_clientdata(i2c, uda1380); + codec->control_data = i2c;
-#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - if (setup->i2c_address) { - codec->hw_write = (hw_write_t)i2c_master_send; - ret = uda1380_add_i2c_device(pdev, setup); - } -#endif + codec->dev = &i2c->dev;
+ ret = uda1380_register(uda1380); if (ret != 0) - kfree(codec); + kfree(uda1380); + return ret; }
-/* power down chip */ -static int uda1380_remove(struct platform_device *pdev) +static int __devexit uda1380_i2c_remove(struct i2c_client *i2c) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); - struct snd_soc_codec *codec = socdev->card->codec; - - if (codec->control_data) - uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF); - - snd_soc_free_pcms(socdev); - snd_soc_dapm_free(socdev); -#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) - i2c_unregister_device(codec->control_data); - i2c_del_driver(&uda1380_i2c_driver); -#endif - kfree(codec); - + struct uda1380_priv *uda1380 = i2c_get_clientdata(i2c); + uda1380_unregister(uda1380); return 0; }
-struct snd_soc_codec_device soc_codec_dev_uda1380 = { - .probe = uda1380_probe, - .remove = uda1380_remove, - .suspend = uda1380_suspend, - .resume = uda1380_resume, +static const struct i2c_device_id uda1380_i2c_id[] = { + { "uda1380", 0 }, + { } }; -EXPORT_SYMBOL_GPL(soc_codec_dev_uda1380); +MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); + +static struct i2c_driver uda1380_i2c_driver = { + .driver = { + .name = "UDA1380 I2C Codec", + .owner = THIS_MODULE, + }, + .probe = uda1380_i2c_probe, + .remove = __devexit_p(uda1380_i2c_remove), + .id_table = uda1380_i2c_id, +}; +#endif
static int __init uda1380_modinit(void) { - return snd_soc_register_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); + int ret; +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + ret = i2c_add_driver(&uda1380_i2c_driver); + if (ret != 0) + pr_err("Failed to register UDA1380 I2C driver: %d\n", ret); +#endif + return 0; } module_init(uda1380_modinit);
static void __exit uda1380_exit(void) { - snd_soc_unregister_dais(uda1380_dai, ARRAY_SIZE(uda1380_dai)); +#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE) + i2c_del_driver(&uda1380_i2c_driver); +#endif } module_exit(uda1380_exit);
diff --git a/sound/soc/codecs/uda1380.h b/sound/soc/codecs/uda1380.h index c55c17a..9cefa8a 100644 --- a/sound/soc/codecs/uda1380.h +++ b/sound/soc/codecs/uda1380.h @@ -72,14 +72,6 @@ #define R22_SKIP_DCFIL 0x0002 #define R23_AGC_EN 0x0001
-struct uda1380_setup_data { - int i2c_bus; - unsigned short i2c_address; - int dac_clk; -#define UDA1380_DAC_CLK_SYSCLK 0 -#define UDA1380_DAC_CLK_WSPLL 1 -}; - #define UDA1380_DAI_DUPLEX 0 /* playback and capture on single DAI */ #define UDA1380_DAI_PLAYBACK 1 /* playback DAI */ #define UDA1380_DAI_CAPTURE 2 /* capture DAI */ diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c index c89a3cd..9fe4ad2 100644 --- a/sound/soc/pxa/magician.c +++ b/sound/soc/pxa/magician.c @@ -20,12 +20,14 @@ #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/gpio.h> +#include <linux/i2c.h>
#include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> +#include <sound/uda1380.h>
#include <mach/magician.h> #include <asm/mach-types.h> @@ -447,34 +449,47 @@ static struct snd_soc_card snd_soc_card_magician = { .platform = &pxa2xx_soc_platform, };
-/* magician audio private data */ -static struct uda1380_setup_data magician_uda1380_setup = { - .i2c_address = 0x18, - .dac_clk = UDA1380_DAC_CLK_WSPLL, -}; - /* magician audio subsystem */ static struct snd_soc_device magician_snd_devdata = { .card = &snd_soc_card_magician, .codec_dev = &soc_codec_dev_uda1380, - .codec_data = &magician_uda1380_setup, };
static struct platform_device *magician_snd_device;
+/* + * FIXME: move into magician board file once merged into the pxa tree + */ +static struct uda1380_platform_data uda1380_info = { + .gpio_power = EGPIO_MAGICIAN_CODEC_POWER, + .gpio_reset = EGPIO_MAGICIAN_CODEC_RESET, + .dac_clk = UDA1380_DAC_CLK_WSPLL, +}; + +static struct i2c_board_info i2c_board_info[] = { + { + I2C_BOARD_INFO("uda1380", 0x18), + .platform_data = &uda1380_info, + }, +}; + static int __init magician_init(void) { int ret; + struct i2c_adapter *adapter; + struct i2c_client *client;
if (!machine_is_magician()) return -ENODEV;
- ret = gpio_request(EGPIO_MAGICIAN_CODEC_POWER, "CODEC_POWER"); - if (ret) - goto err_request_power; - ret = gpio_request(EGPIO_MAGICIAN_CODEC_RESET, "CODEC_RESET"); - if (ret) - goto err_request_reset; + adapter = i2c_get_adapter(0); + if (!adapter) + return -ENODEV; + client = i2c_new_device(adapter, i2c_board_info); + i2c_put_adapter(adapter); + if (!client) + return -ENODEV; + ret = gpio_request(EGPIO_MAGICIAN_SPK_POWER, "SPK_POWER"); if (ret) goto err_request_spk; @@ -491,14 +506,8 @@ static int __init magician_init(void) if (ret) goto err_request_in_sel1;
- gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 1); gpio_set_value(EGPIO_MAGICIAN_IN_SEL0, 0);
- /* we may need to have the clock running here - pH5 */ - gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 1); - udelay(5); - gpio_set_value(EGPIO_MAGICIAN_CODEC_RESET, 0); - magician_snd_device = platform_device_alloc("soc-audio", -1); if (!magician_snd_device) { ret = -ENOMEM; @@ -526,10 +535,6 @@ err_request_mic: err_request_ep: gpio_free(EGPIO_MAGICIAN_SPK_POWER); err_request_spk: - gpio_free(EGPIO_MAGICIAN_CODEC_RESET); -err_request_reset: - gpio_free(EGPIO_MAGICIAN_CODEC_POWER); -err_request_power: return ret; }
@@ -540,15 +545,12 @@ static void __exit magician_exit(void) gpio_set_value(EGPIO_MAGICIAN_SPK_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_EP_POWER, 0); gpio_set_value(EGPIO_MAGICIAN_MIC_POWER, 0); - gpio_set_value(EGPIO_MAGICIAN_CODEC_POWER, 0);
gpio_free(EGPIO_MAGICIAN_IN_SEL1); gpio_free(EGPIO_MAGICIAN_IN_SEL0); gpio_free(EGPIO_MAGICIAN_MIC_POWER); gpio_free(EGPIO_MAGICIAN_EP_POWER); gpio_free(EGPIO_MAGICIAN_SPK_POWER); - gpio_free(EGPIO_MAGICIAN_CODEC_RESET); - gpio_free(EGPIO_MAGICIAN_CODEC_POWER); }
module_init(magician_init);
On Mon, Jun 15, 2009 at 10:18:23PM +0200, Philipp Zabel wrote:
This patch mostly follows commit 5998102b9095fdb7c67755812038612afea315c5 "ASoC: Refactor WM8731 device registration" to make UDA1380 use standard device instantiation. Similarly, the I2C device registration temporarily moves into the magician machine driver before it will find its final resting place in the board file.
At the same time, platform specific configuration is moved to platform data and common power/reset GPIO handling moves into the codec driver.
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com
Great stuff, applied thanks.
On Sun, Jun 14, 2009 at 12:51:30PM +0200, Philipp Zabel wrote:
Follow-up fix needed since "ASoC: pxa-ssp.c fix clock/frame invert".
Signed-off-by: Philipp Zabel philipp.zabel@gmail.com
Applied, thanks.
participants (6)
-
Eric Miao
-
Guilherme
-
Mark Brown
-
Mark Brown
-
Philipp Zabel
-
Ted T. Logan