From eeec12bf7b7d80d1c9cae5aae0dff7e2f928c64b Mon Sep 17 00:00:00 2001 From: Graeme Gregory Date: Wed, 30 Apr 2008 19:27:40 +0200 Subject: [ALSA] soc - DAPM - add hook to read state of DAPM widget This adds a hook to read the power state of a DAPM widget, I use this in the gta02 driver to expose certain DAPM widgets in the mixer for ease of audio routing. Signed-off-by: Graeme Gregory Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc-dapm.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index a105b01e06d..40cc695b69b 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -223,6 +223,8 @@ int snd_soc_dapm_sys_add(struct device *dev); /* dapm audio endpoint control */ int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, char *pin, int status); +int snd_soc_dapm_get_endpoint_status(struct snd_soc_codec *codec, + char *pin); int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec); /* dapm widget types */ -- cgit v1.2.3-70-g09d2 From 4ba1327ab8ce179c40862f3dedb4ebaaa491d737 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 May 2008 14:51:19 +0200 Subject: [ALSA] soc - DAPM - Add bulk control registration Most SoC drivers cut'n'paste a loop iterating over an array to register their DAPM controls. Provide a function they can call instead. Signed-off-by: Mark Brown Cc: Graeme Gregory Cc: Frank Mandarino Cc: Jarkko Nikula Cc: Richard Purdie Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc-dapm.h | 3 +++ sound/soc/soc-dapm.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 40cc695b69b..1f30616afe7 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -205,6 +205,9 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_dapm_new_control(struct snd_soc_codec *codec, const struct snd_soc_dapm_widget *widget); +int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, + const struct snd_soc_dapm_widget *widget, + int num); /* dapm path setup */ int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index c60200ccde6..811d6527101 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -1233,6 +1233,33 @@ int snd_soc_dapm_new_control(struct snd_soc_codec *codec, } EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control); +/** + * snd_soc_dapm_new_controls - create new dapm controls + * @codec: audio codec + * @widget: widget array + * @num: number of widgets + * + * Creates new DAPM controls based upon the templates. + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, + const struct snd_soc_dapm_widget *widget, + int num) +{ + int i, ret; + + for (i = 0; i < num; i++) { + ret = snd_soc_dapm_new_control(codec, widget); + if (ret < 0) + return ret; + widget++; + } + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_new_controls); + + /** * snd_soc_dapm_stream_event - send a stream event to the dapm core * @codec: audio codec -- cgit v1.2.3-70-g09d2 From 105f1c28442301237d20b05a3d52d9987614016f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Tue, 13 May 2008 14:52:19 +0200 Subject: [ALSA] soc - DAPM - Bulk route registration ASoC codecs and machine drivers that use DAPM routes all cut'n'paste a loop iterating over a null terminated array of routes. Factor out this into a bulk registration function, improving the error reporting for most users, and deprecate the old API to help out of tree users pick up the changes. Signed-off-by: Mark Brown Cc: Graeme Gregory Cc: Frank Mandarino Cc: Jarkko Nikula Cc: Richard Purdie Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc-dapm.h | 17 +++++++++++- sound/soc/soc-dapm.c | 72 ++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 73 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index 1f30616afe7..bf4cf0c1d37 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -193,6 +193,7 @@ struct snd_soc_dapm_widget; enum snd_soc_dapm_type; struct snd_soc_dapm_path; struct snd_soc_dapm_pin; +struct snd_soc_dapm_route; /* dapm controls */ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol, @@ -210,10 +211,12 @@ int snd_soc_dapm_new_controls(struct snd_soc_codec *codec, int num); /* dapm path setup */ -int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, +int __deprecated snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink_name, const char *control_name, const char *src_name); int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec); void snd_soc_dapm_free(struct snd_soc_device *socdev); +int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, + const struct snd_soc_dapm_route *route, int num); /* dapm events */ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, @@ -250,6 +253,18 @@ enum snd_soc_dapm_type { snd_soc_dapm_post, /* machine specific post widget - exec last */ }; +/* + * DAPM audio route definition. + * + * Defines an audio route originating at source via control and finishing + * at sink. + */ +struct snd_soc_dapm_route { + const char *sink; + const char *control; + const char *source; +}; + /* dapm audio path between two widgets */ struct snd_soc_dapm_path { char *name; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 811d6527101..1ef6d94b835 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -841,21 +841,8 @@ int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec) } EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints); -/** - * snd_soc_dapm_connect_input - connect dapm widgets - * @codec: audio codec - * @sink: name of target widget - * @control: mixer control name - * @source: name of source name - * - * Connects 2 dapm widgets together via a named audio path. The sink is - * the widget receiving the audio signal, whilst the source is the sender - * of the audio signal. - * - * Returns 0 for success else error. - */ -int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, - const char * control, const char *source) +static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, + const char *sink, const char *control, const char *source) { struct snd_soc_dapm_path *path; struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w; @@ -957,8 +944,63 @@ err: kfree(path); return ret; } + +/** + * snd_soc_dapm_connect_input - connect dapm widgets + * @codec: audio codec + * @sink: name of target widget + * @control: mixer control name + * @source: name of source name + * + * Connects 2 dapm widgets together via a named audio path. The sink is + * the widget receiving the audio signal, whilst the source is the sender + * of the audio signal. + * + * This function has been deprecated in favour of snd_soc_dapm_add_routes(). + * + * Returns 0 for success else error. + */ +int snd_soc_dapm_connect_input(struct snd_soc_codec *codec, const char *sink, + const char *control, const char *source) +{ + return snd_soc_dapm_add_route(codec, sink, control, source); +} EXPORT_SYMBOL_GPL(snd_soc_dapm_connect_input); +/** + * snd_soc_dapm_add_routes - Add routes between DAPM widgets + * @codec: codec + * @route: audio routes + * @num: number of routes + * + * Connects 2 dapm widgets together via a named audio path. The sink is + * the widget receiving the audio signal, whilst the source is the sender + * of the audio signal. + * + * Returns 0 for success else error. On error all resources can be freed + * with a call to snd_soc_card_free(). + */ +int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, + const struct snd_soc_dapm_route *route, int num) +{ + int i, ret; + + for (i = 0; i < num; i++) { + ret = snd_soc_dapm_add_route(codec, route->sink, + route->control, route->source); + if (ret < 0) { + printk(KERN_ERR "Failed to add route %s->%s\n", + route->source, + route->sink); + return ret; + } + route++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes); + /** * snd_soc_dapm_new_widgets - add new dapm widgets * @codec: audio codec -- cgit v1.2.3-70-g09d2 From 1ef6ab75c7deef931d6308af282ed7d8e480e77f Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 May 2008 12:31:55 +0200 Subject: [ALSA] ASoC: Make CPU and codec DAI operations have same type The CPU and codec DAI operations differ only in the presence of the digital mute operation for the codec so they may as well be the same type. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index d3c8c033dff..73accbcfbd2 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -272,9 +272,9 @@ struct snd_soc_ops { int (*trigger)(struct snd_pcm_substream *, int); }; -/* ASoC codec DAI ops */ -struct snd_soc_codec_ops { - /* codec DAI clocking configuration */ +/* ASoC DAI ops */ +struct snd_soc_dai_ops { + /* DAI clocking configuration */ int (*set_sysclk)(struct snd_soc_codec_dai *codec_dai, int clk_id, unsigned int freq, int dir); int (*set_pll)(struct snd_soc_codec_dai *codec_dai, @@ -282,7 +282,7 @@ struct snd_soc_codec_ops { int (*set_clkdiv)(struct snd_soc_codec_dai *codec_dai, int div_id, int div); - /* CPU DAI format configuration */ + /* DAI format configuration */ int (*set_fmt)(struct snd_soc_codec_dai *codec_dai, unsigned int fmt); int (*set_tdm_slot)(struct snd_soc_codec_dai *codec_dai, @@ -293,24 +293,6 @@ struct snd_soc_codec_ops { int (*digital_mute)(struct snd_soc_codec_dai *, int mute); }; -/* ASoC cpu DAI ops */ -struct snd_soc_cpu_ops { - /* CPU DAI clocking configuration */ - int (*set_sysclk)(struct snd_soc_cpu_dai *cpu_dai, - int clk_id, unsigned int freq, int dir); - int (*set_clkdiv)(struct snd_soc_cpu_dai *cpu_dai, - int div_id, int div); - int (*set_pll)(struct snd_soc_cpu_dai *cpu_dai, - int pll_id, unsigned int freq_in, unsigned int freq_out); - - /* CPU DAI format configuration */ - int (*set_fmt)(struct snd_soc_cpu_dai *cpu_dai, - unsigned int fmt); - int (*set_tdm_slot)(struct snd_soc_cpu_dai *cpu_dai, - unsigned int mask, int slots); - int (*set_tristate)(struct snd_soc_cpu_dai *, int tristate); -}; - /* SoC Codec DAI */ struct snd_soc_codec_dai { char *name; @@ -328,7 +310,7 @@ struct snd_soc_codec_dai { /* ops */ struct snd_soc_ops ops; - struct snd_soc_codec_ops dai_ops; + struct snd_soc_dai_ops dai_ops; /* DAI private data */ void *private_data; @@ -352,7 +334,7 @@ struct snd_soc_cpu_dai { /* ops */ struct snd_soc_ops ops; - struct snd_soc_cpu_ops dai_ops; + struct snd_soc_dai_ops dai_ops; /* DAI capabilities */ struct snd_soc_pcm_stream capture; -- cgit v1.2.3-70-g09d2 From 0be9898adb6f58fee44f0fec0bbc0eac997ea9eb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 19 May 2008 12:31:28 +0200 Subject: [ALSA] ASoC: Clarify API for bias configuration Currently the ASoC core configures the bias levels in the system using a callback on codecs and machines called 'dapm_event', passing it PCI style power levels as SNDRV_CTL_POWER_ constants. This is more obscure than it needs to be and has caused confusion to driver authors, especially given that DAPM is also performing power management. Address this by renaming the callback function to 'set_bias_level' and using constants explicitly representing the off, standby, pre-on and on states which DAPM transitions through. Also unexport the API for setting bias level: there are currently no in-tree users of this API other than the core itself and it is likely that the core would need to be extended to cater for any users. Signed-off-by: Mark Brown Cc: Jarkko Nikula Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc-dapm.h | 3 ++- include/sound/soc.h | 28 +++++++++++++++++++++++---- sound/soc/codecs/tlv320aic3x.c | 26 ++++++++++++------------- sound/soc/codecs/wm8731.c | 28 +++++++++++++-------------- sound/soc/codecs/wm8750.c | 36 +++++++++++++++++----------------- sound/soc/codecs/wm8753.c | 36 +++++++++++++++++----------------- sound/soc/codecs/wm9712.c | 28 +++++++++++++-------------- sound/soc/codecs/wm9713.c | 26 ++++++++++++------------- sound/soc/soc-core.c | 25 ++++++++++++------------ sound/soc/soc-dapm.c | 44 ++++++++++++++++++++---------------------- 10 files changed, 150 insertions(+), 130 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index bf4cf0c1d37..f8223fae580 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -221,7 +221,8 @@ int snd_soc_dapm_add_routes(struct snd_soc_codec *codec, /* dapm events */ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, char *stream, int event); -int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event); +int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, + enum snd_soc_bias_level level); /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); diff --git a/include/sound/soc.h b/include/sound/soc.h index 73accbcfbd2..bca9538d9e5 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -102,6 +102,24 @@ .get = xhandler_get, .put = xhandler_put, \ .private_value = (unsigned long)&xenum } +/* + * Bias levels + * + * @ON: Bias is fully on for audio playback and capture operations. + * @PREPARE: Prepare for audio operations. Called before DAPM switching for + * stream start and stop operations. + * @STANDBY: Low power standby state when no playback/capture operations are + * in progress. NOTE: The transition time between STANDBY and ON + * should be as fast as possible and no longer than 10ms. + * @OFF: Power Off. No restrictions on transition times. + */ +enum snd_soc_bias_level { + SND_SOC_BIAS_ON, + SND_SOC_BIAS_PREPARE, + SND_SOC_BIAS_STANDBY, + SND_SOC_BIAS_OFF, +}; + /* * Digital Audio Interface (DAI) types */ @@ -356,7 +374,8 @@ struct snd_soc_codec { struct mutex mutex; /* callbacks */ - int (*dapm_event)(struct snd_soc_codec *codec, int event); + int (*set_bias_level)(struct snd_soc_codec *, + enum snd_soc_bias_level level); /* runtime */ struct snd_card *card; @@ -378,8 +397,8 @@ struct snd_soc_codec { /* dapm */ struct list_head dapm_widgets; struct list_head dapm_paths; - unsigned int dapm_state; - unsigned int suspend_dapm_state; + enum snd_soc_bias_level bias_level; + enum snd_soc_bias_level suspend_bias_level; struct delayed_work delayed_work; /* codec DAI's */ @@ -449,7 +468,8 @@ struct snd_soc_machine { int (*resume_post)(struct platform_device *pdev); /* callbacks */ - int (*dapm_event)(struct snd_soc_machine *, int event); + int (*set_bias_level)(struct snd_soc_machine *, + enum snd_soc_bias_level level); /* CPU <--> Codec DAI links */ struct snd_soc_dai_link *dai_link; diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index cb8365ac0c0..dc8a38d9e53 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -847,13 +847,14 @@ static int aic3x_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, return 0; } -static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) +static int aic3x_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { struct aic3x_priv *aic3x = codec->private_data; u8 reg; - switch (event) { - case SNDRV_CTL_POWER_D0: + switch (level) { + case SND_SOC_BIAS_ON: /* all power is driven by DAPM system */ if (aic3x->master) { /* enable pll */ @@ -862,10 +863,9 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) reg | PLL_ENABLE); } break; - case SNDRV_CTL_POWER_D1: - case SNDRV_CTL_POWER_D2: + case SND_SOC_BIAS_PREPARE: break; - case SNDRV_CTL_POWER_D3hot: + case SND_SOC_BIAS_STANDBY: /* * all power is driven by DAPM system, * so output power is safe if bypass was set @@ -877,7 +877,7 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) reg & ~PLL_ENABLE); } break; - case SNDRV_CTL_POWER_D3cold: + case SND_SOC_BIAS_OFF: /* force all power off */ reg = aic3x_read_reg_cache(codec, LINE1L_2_LADC_CTRL); aic3x_write(codec, LINE1L_2_LADC_CTRL, reg & ~LADC_PWR_ON); @@ -913,7 +913,7 @@ static int aic3x_dapm_event(struct snd_soc_codec *codec, int event) } break; } - codec->dapm_state = event; + codec->bias_level = level; return 0; } @@ -979,7 +979,7 @@ static int aic3x_suspend(struct platform_device *pdev, pm_message_t state) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; - aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -999,7 +999,7 @@ static int aic3x_resume(struct platform_device *pdev) codec->hw_write(codec->control_data, data, 2); } - aic3x_dapm_event(codec, codec->suspend_dapm_state); + aic3x_set_bias_level(codec, codec->suspend_bias_level); return 0; } @@ -1018,7 +1018,7 @@ static int aic3x_init(struct snd_soc_device *socdev) codec->owner = THIS_MODULE; codec->read = aic3x_read_reg_cache; codec->write = aic3x_write; - codec->dapm_event = aic3x_dapm_event; + codec->set_bias_level = aic3x_set_bias_level; codec->dai = &aic3x_dai; codec->num_dai = 1; codec->reg_cache_size = sizeof(aic3x_reg); @@ -1100,7 +1100,7 @@ static int aic3x_init(struct snd_soc_device *socdev) aic3x_write(codec, LINE2R_2_MONOLOPM_VOL, DEFAULT_VOL); /* off, with power on */ - aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + aic3x_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* setup GPIO functions */ aic3x_write(codec, AIC3X_GPIO1_REG, (setup->gpio_func[0] & 0xf) << 4); @@ -1271,7 +1271,7 @@ static int aic3x_remove(struct platform_device *pdev) /* power down chip */ if (codec->control_data) - aic3x_dapm_event(codec, SNDRV_CTL_POWER_D3); + aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c index 0cf9265fca8..0f28aa4bccc 100644 --- a/sound/soc/codecs/wm8731.c +++ b/sound/soc/codecs/wm8731.c @@ -435,29 +435,29 @@ static int wm8731_set_dai_fmt(struct snd_soc_codec_dai *codec_dai, return 0; } -static int wm8731_dapm_event(struct snd_soc_codec *codec, int event) +static int wm8731_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { u16 reg = wm8731_read_reg_cache(codec, WM8731_PWR) & 0xff7f; - switch (event) { - case SNDRV_CTL_POWER_D0: /* full On */ + switch (level) { + case SND_SOC_BIAS_ON: /* vref/mid, osc on, dac unmute */ wm8731_write(codec, WM8731_PWR, reg); break; - case SNDRV_CTL_POWER_D1: /* partial On */ - case SNDRV_CTL_POWER_D2: /* partial On */ + case SND_SOC_BIAS_PREPARE: break; - case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + case SND_SOC_BIAS_STANDBY: /* everything off except vref/vmid, */ wm8731_write(codec, WM8731_PWR, reg | 0x0040); break; - case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + case SND_SOC_BIAS_OFF: /* everything off, dac mute, inactive */ wm8731_write(codec, WM8731_ACTIVE, 0x0); wm8731_write(codec, WM8731_PWR, 0xffff); break; } - codec->dapm_state = event; + codec->bias_level = level; return 0; } @@ -503,7 +503,7 @@ static int wm8731_suspend(struct platform_device *pdev, pm_message_t state) struct snd_soc_codec *codec = socdev->codec; wm8731_write(codec, WM8731_ACTIVE, 0x0); - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -521,8 +521,8 @@ static int wm8731_resume(struct platform_device *pdev) data[1] = cache[i] & 0x00ff; codec->hw_write(codec->control_data, data, 2); } - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); - wm8731_dapm_event(codec, codec->suspend_dapm_state); + wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); + wm8731_set_bias_level(codec, codec->suspend_bias_level); return 0; } @@ -539,7 +539,7 @@ static int wm8731_init(struct snd_soc_device *socdev) codec->owner = THIS_MODULE; codec->read = wm8731_read_reg_cache; codec->write = wm8731_write; - codec->dapm_event = wm8731_dapm_event; + codec->set_bias_level = wm8731_set_bias_level; codec->dai = &wm8731_dai; codec->num_dai = 1; codec->reg_cache_size = sizeof(wm8731_reg); @@ -557,7 +557,7 @@ static int wm8731_init(struct snd_soc_device *socdev) } /* power on device */ - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* set the update bits */ reg = wm8731_read_reg_cache(codec, WM8731_LOUT1V); @@ -730,7 +730,7 @@ static int wm8731_remove(struct platform_device *pdev) struct snd_soc_codec *codec = socdev->codec; if (codec->control_data) - wm8731_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 16cd5d4d5ad..62423f4493b 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -686,29 +686,29 @@ static int wm8750_mute(struct snd_soc_codec_dai *dai, int mute) return 0; } -static int wm8750_dapm_event(struct snd_soc_codec *codec, int event) +static int wm8750_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { u16 pwr_reg = wm8750_read_reg_cache(codec, WM8750_PWR1) & 0xfe3e; - switch (event) { - case SNDRV_CTL_POWER_D0: /* full On */ + switch (level) { + case SND_SOC_BIAS_ON: /* set vmid to 50k and unmute dac */ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x00c0); break; - case SNDRV_CTL_POWER_D1: /* partial On */ - case SNDRV_CTL_POWER_D2: /* partial On */ + case SND_SOC_BIAS_PREPARE: /* set vmid to 5k for quick power up */ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x01c1); break; - case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + case SND_SOC_BIAS_STANDBY: /* mute dac and set vmid to 500k, enable VREF */ wm8750_write(codec, WM8750_PWR1, pwr_reg | 0x0141); break; - case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + case SND_SOC_BIAS_OFF: wm8750_write(codec, WM8750_PWR1, 0x0001); break; } - codec->dapm_state = event; + codec->bias_level = level; return 0; } @@ -748,7 +748,7 @@ static void wm8750_work(struct work_struct *work) { struct snd_soc_codec *codec = container_of(work, struct snd_soc_codec, delayed_work.work); - wm8750_dapm_event(codec, codec->dapm_state); + wm8750_set_bias_level(codec, codec->bias_level); } static int wm8750_suspend(struct platform_device *pdev, pm_message_t state) @@ -756,7 +756,7 @@ static int wm8750_suspend(struct platform_device *pdev, pm_message_t state) struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; - wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -777,12 +777,12 @@ static int wm8750_resume(struct platform_device *pdev) codec->hw_write(codec->control_data, data, 2); } - wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* charge wm8750 caps */ - if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { - wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); - codec->dapm_state = SNDRV_CTL_POWER_D0; + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_ON; schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); } @@ -803,7 +803,7 @@ static int wm8750_init(struct snd_soc_device *socdev) codec->owner = THIS_MODULE; codec->read = wm8750_read_reg_cache; codec->write = wm8750_write; - codec->dapm_event = wm8750_dapm_event; + codec->set_bias_level = wm8750_set_bias_level; codec->dai = &wm8750_dai; codec->num_dai = 1; codec->reg_cache_size = sizeof(wm8750_reg); @@ -821,8 +821,8 @@ static int wm8750_init(struct snd_soc_device *socdev) } /* charge output caps */ - wm8750_dapm_event(codec, SNDRV_CTL_POWER_D2); - codec->dapm_state = SNDRV_CTL_POWER_D3hot; + wm8750_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_STANDBY; schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(1000)); /* set the update bits */ @@ -1021,7 +1021,7 @@ static int wm8750_remove(struct platform_device *pdev) struct snd_soc_codec *codec = socdev->codec; if (codec->control_data) - wm8750_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF); run_delayed_work(&codec->delayed_work); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index fb41826c4c4..9032b0c07c8 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1274,29 +1274,29 @@ static int wm8753_mute(struct snd_soc_codec_dai *dai, int mute) return 0; } -static int wm8753_dapm_event(struct snd_soc_codec *codec, int event) +static int wm8753_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { u16 pwr_reg = wm8753_read_reg_cache(codec, WM8753_PWR1) & 0xfe3e; - switch (event) { - case SNDRV_CTL_POWER_D0: /* full On */ + switch (level) { + case SND_SOC_BIAS_ON: /* set vmid to 50k and unmute dac */ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x00c0); break; - case SNDRV_CTL_POWER_D1: /* partial On */ - case SNDRV_CTL_POWER_D2: /* partial On */ + case SND_SOC_BIAS_PREPARE: /* set vmid to 5k for quick power up */ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x01c1); break; - case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + case SND_SOC_BIAS_STANDBY: /* mute dac and set vmid to 500k, enable VREF */ wm8753_write(codec, WM8753_PWR1, pwr_reg | 0x0141); break; - case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + case SND_SOC_BIAS_OFF: wm8753_write(codec, WM8753_PWR1, 0x0001); break; } - codec->dapm_state = event; + codec->bias_level = level; return 0; } @@ -1500,7 +1500,7 @@ static void wm8753_work(struct work_struct *work) { struct snd_soc_codec *codec = container_of(work, struct snd_soc_codec, delayed_work.work); - wm8753_dapm_event(codec, codec->dapm_state); + wm8753_set_bias_level(codec, codec->bias_level); } static int wm8753_suspend(struct platform_device *pdev, pm_message_t state) @@ -1512,7 +1512,7 @@ static int wm8753_suspend(struct platform_device *pdev, pm_message_t state) if (!codec->card) return 0; - wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -1537,12 +1537,12 @@ static int wm8753_resume(struct platform_device *pdev) codec->hw_write(codec->control_data, data, 2); } - wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm8753_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* charge wm8753 caps */ - if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) { - wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2); - codec->dapm_state = SNDRV_CTL_POWER_D0; + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) { + wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_ON; schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(caps_charge)); } @@ -1563,7 +1563,7 @@ static int wm8753_init(struct snd_soc_device *socdev) codec->owner = THIS_MODULE; codec->read = wm8753_read_reg_cache; codec->write = wm8753_write; - codec->dapm_event = wm8753_dapm_event; + codec->set_bias_level = wm8753_set_bias_level; codec->dai = wm8753_dai; codec->num_dai = 2; codec->reg_cache_size = sizeof(wm8753_reg); @@ -1584,8 +1584,8 @@ static int wm8753_init(struct snd_soc_device *socdev) } /* charge output caps */ - wm8753_dapm_event(codec, SNDRV_CTL_POWER_D2); - codec->dapm_state = SNDRV_CTL_POWER_D3hot; + wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE); + codec->bias_level = SND_SOC_BIAS_STANDBY; schedule_delayed_work(&codec->delayed_work, msecs_to_jiffies(caps_charge)); @@ -1792,7 +1792,7 @@ static int wm8753_remove(struct platform_device *pdev) struct snd_soc_codec *codec = socdev->codec; if (codec->control_data) - wm8753_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm8753_set_bias_level(codec, SND_SOC_BIAS_OFF); run_delayed_work(&codec->delayed_work); snd_soc_free_pcms(socdev); snd_soc_dapm_free(socdev); diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c index 89efe40c7c3..e26cfcf0b4f 100644 --- a/sound/soc/codecs/wm9712.c +++ b/sound/soc/codecs/wm9712.c @@ -571,23 +571,23 @@ struct snd_soc_codec_dai wm9712_dai[] = { }; EXPORT_SYMBOL_GPL(wm9712_dai); -static int wm9712_dapm_event(struct snd_soc_codec *codec, int event) +static int wm9712_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { - switch (event) { - case SNDRV_CTL_POWER_D0: /* full On */ - case SNDRV_CTL_POWER_D1: /* partial On */ - case SNDRV_CTL_POWER_D2: /* partial On */ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: break; - case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + case SND_SOC_BIAS_STANDBY: ac97_write(codec, AC97_POWERDOWN, 0x0000); break; - case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + case SND_SOC_BIAS_OFF: /* disable everything including AC link */ ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm_state = event; + codec->bias_level = level; return 0; } @@ -615,7 +615,7 @@ static int wm9712_soc_suspend(struct platform_device *pdev, struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_codec *codec = socdev->codec; - wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3cold); + wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF); return 0; } @@ -632,7 +632,7 @@ static int wm9712_soc_resume(struct platform_device *pdev) return ret; } - wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); if (ret == 0) { /* Sync reg_cache with the hardware after cold reset */ @@ -644,8 +644,8 @@ static int wm9712_soc_resume(struct platform_device *pdev) } } - if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) - wm9712_dapm_event(codec, SNDRV_CTL_POWER_D0); + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) + wm9712_set_bias_level(codec, SND_SOC_BIAS_ON); return ret; } @@ -679,7 +679,7 @@ static int wm9712_soc_probe(struct platform_device *pdev) codec->num_dai = ARRAY_SIZE(wm9712_dai); codec->write = ac97_write; codec->read = ac97_read; - codec->dapm_event = wm9712_dapm_event; + codec->set_bias_level = wm9712_set_bias_level; INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); @@ -703,7 +703,7 @@ static int wm9712_soc_probe(struct platform_device *pdev) /* set alc mux to none */ ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000); - wm9712_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY); wm9712_add_controls(codec); wm9712_add_widgets(codec); ret = snd_soc_register_card(socdev); diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c index 9e6b2fd7262..4863636e9d5 100644 --- a/sound/soc/codecs/wm9713.c +++ b/sound/soc/codecs/wm9713.c @@ -1094,33 +1094,33 @@ int wm9713_reset(struct snd_soc_codec *codec, int try_warm) } EXPORT_SYMBOL_GPL(wm9713_reset); -static int wm9713_dapm_event(struct snd_soc_codec *codec, int event) +static int wm9713_set_bias_level(struct snd_soc_codec *codec, + enum snd_soc_bias_level level) { u16 reg; - switch (event) { - case SNDRV_CTL_POWER_D0: /* full On */ + switch (level) { + case SND_SOC_BIAS_ON: /* enable thermal shutdown */ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x1bff; ac97_write(codec, AC97_EXTENDED_MID, reg); break; - case SNDRV_CTL_POWER_D1: /* partial On */ - case SNDRV_CTL_POWER_D2: /* partial On */ + case SND_SOC_BIAS_PREPARE: break; - case SNDRV_CTL_POWER_D3hot: /* Off, with power */ + case SND_SOC_BIAS_STANDBY: /* enable master bias and vmid */ reg = ac97_read(codec, AC97_EXTENDED_MID) & 0x3bff; ac97_write(codec, AC97_EXTENDED_MID, reg); ac97_write(codec, AC97_POWERDOWN, 0x0000); break; - case SNDRV_CTL_POWER_D3cold: /* Off, without power */ + case SND_SOC_BIAS_OFF: /* disable everything including AC link */ ac97_write(codec, AC97_EXTENDED_MID, 0xffff); ac97_write(codec, AC97_EXTENDED_MSTATUS, 0xffff); ac97_write(codec, AC97_POWERDOWN, 0xffff); break; } - codec->dapm_state = event; + codec->bias_level = level; return 0; } @@ -1157,7 +1157,7 @@ static int wm9713_soc_resume(struct platform_device *pdev) return ret; } - wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* do we need to re-start the PLL ? */ if (wm9713->pll_out) @@ -1173,8 +1173,8 @@ static int wm9713_soc_resume(struct platform_device *pdev) } } - if (codec->suspend_dapm_state == SNDRV_CTL_POWER_D0) - wm9713_dapm_event(codec, SNDRV_CTL_POWER_D0); + if (codec->suspend_bias_level == SND_SOC_BIAS_ON) + wm9713_set_bias_level(codec, SND_SOC_BIAS_ON); return ret; } @@ -1213,7 +1213,7 @@ static int wm9713_soc_probe(struct platform_device *pdev) codec->num_dai = ARRAY_SIZE(wm9713_dai); codec->write = ac97_write; codec->read = ac97_read; - codec->dapm_event = wm9713_dapm_event; + codec->set_bias_level = wm9713_set_bias_level; INIT_LIST_HEAD(&codec->dapm_widgets); INIT_LIST_HEAD(&codec->dapm_paths); @@ -1235,7 +1235,7 @@ static int wm9713_soc_probe(struct platform_device *pdev) goto reset_err; } - wm9713_dapm_event(codec, SNDRV_CTL_POWER_D3hot); + wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY); /* unmute the adc - move to kcontrol */ reg = ac97_read(codec, AC97_CD) & 0x7fff; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 0318d8abe3e..a05b3450aee 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -283,12 +283,12 @@ static void close_delayed_work(struct work_struct *work) /* are we waiting on this codec DAI stream */ if (codec_dai->pop_wait == 1) { - /* power down the codec to D1 if no longer active */ + /* Reduce power if no longer active */ if (codec->active == 0) { dbg("pop wq D1 %s %s\n", codec->name, codec_dai->playback.stream_name); - snd_soc_dapm_device_event(socdev, - SNDRV_CTL_POWER_D1); + snd_soc_dapm_set_bias_level(socdev, + SND_SOC_BIAS_PREPARE); } codec_dai->pop_wait = 0; @@ -296,12 +296,12 @@ static void close_delayed_work(struct work_struct *work) codec_dai->playback.stream_name, SND_SOC_DAPM_STREAM_STOP); - /* power down the codec power domain if no longer active */ + /* Fall into standby if no longer active */ if (codec->active == 0) { dbg("pop wq D3 %s %s\n", codec->name, codec_dai->playback.stream_name); - snd_soc_dapm_device_event(socdev, - SNDRV_CTL_POWER_D3hot); + snd_soc_dapm_set_bias_level(socdev, + SND_SOC_BIAS_STANDBY); } } } @@ -361,8 +361,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream) SND_SOC_DAPM_STREAM_STOP); if (codec->active == 0 && codec_dai->pop_wait == 0) - snd_soc_dapm_device_event(socdev, - SNDRV_CTL_POWER_D3hot); + snd_soc_dapm_set_bias_level(socdev, + SND_SOC_BIAS_STANDBY); } mutex_unlock(&pcm_mutex); @@ -435,9 +435,10 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) } } else { /* no delayed work - do we need to power up codec */ - if (codec->dapm_state != SNDRV_CTL_POWER_D0) { + if (codec->bias_level != SND_SOC_BIAS_ON) { - snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D1); + snd_soc_dapm_set_bias_level(socdev, + SND_SOC_BIAS_PREPARE); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) snd_soc_dapm_stream_event(codec, @@ -448,7 +449,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START); - snd_soc_dapm_device_event(socdev, SNDRV_CTL_POWER_D0); + snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON); if (codec_dai->dai_ops.digital_mute) codec_dai->dai_ops.digital_mute(codec_dai, 0); @@ -658,7 +659,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) /* close any waiting streams and save state */ run_delayed_work(&socdev->delayed_work); - codec->suspend_dapm_state = codec->dapm_state; + codec->suspend_bias_level = codec->bias_level; for(i = 0; i < codec->num_dai; i++) { char *stream = codec->dai[i].playback.stream_name; diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 8a3192bcee7..728f3ac2f30 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -763,21 +763,18 @@ static ssize_t dapm_widget_show(struct device *dev, } } - switch(codec->dapm_state){ - case SNDRV_CTL_POWER_D0: - state = "D0"; + switch (codec->bias_level) { + case SND_SOC_BIAS_ON: + state = "On"; break; - case SNDRV_CTL_POWER_D1: - state = "D1"; + case SND_SOC_BIAS_PREPARE: + state = "Prepare"; break; - case SNDRV_CTL_POWER_D2: - state = "D2"; + case SND_SOC_BIAS_STANDBY: + state = "Standby"; break; - case SNDRV_CTL_POWER_D3hot: - state = "D3hot"; - break; - case SNDRV_CTL_POWER_D3cold: - state = "D3cold"; + case SND_SOC_BIAS_OFF: + state = "Off"; break; } count += sprintf(buf + count, "PM State: %s\n", state); @@ -1358,27 +1355,28 @@ int snd_soc_dapm_stream_event(struct snd_soc_codec *codec, EXPORT_SYMBOL_GPL(snd_soc_dapm_stream_event); /** - * snd_soc_dapm_device_event - send a device event to the dapm core + * snd_soc_dapm_set_bias_level - set the bias level for the system * @socdev: audio device - * @event: device event + * @level: level to configure * - * Sends a device event to the dapm core. The core then makes any - * necessary machine or codec power changes.. + * Configure the bias (power) levels for the SoC audio device. * * Returns 0 for success else error. */ -int snd_soc_dapm_device_event(struct snd_soc_device *socdev, int event) +int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, + enum snd_soc_bias_level level) { struct snd_soc_codec *codec = socdev->codec; struct snd_soc_machine *machine = socdev->machine; + int ret = 0; - if (machine->dapm_event) - machine->dapm_event(machine, event); - if (codec->dapm_event) - codec->dapm_event(codec, event); - return 0; + if (machine->set_bias_level) + ret = machine->set_bias_level(machine, level); + if (ret == 0 && codec->set_bias_level) + ret = codec->set_bias_level(codec, level); + + return ret; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_device_event); /** * snd_soc_dapm_set_endpoint - set audio endpoint status -- cgit v1.2.3-70-g09d2 From a72e72469a166c825196c3f20dabd352877fec2b Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Tue, 20 May 2008 01:06:55 +0300 Subject: [ALSA] remove CVS keywords This patch removes CVS keywords that weren't updated for a long time from comments. Signed-off-by: Adrian Bunk Signed-off-by: Takashi Iwai --- include/sound/uda1341.h | 2 -- sound/arm/sa11xx-uda1341.c | 2 -- sound/i2c/l3/uda1341.c | 2 -- sound/pci/au88x0/au88x0_game.c | 2 -- 4 files changed, 8 deletions(-) (limited to 'include') diff --git a/include/sound/uda1341.h b/include/sound/uda1341.h index 2e564bfb37f..110d5dc3a2b 100644 --- a/include/sound/uda1341.h +++ b/include/sound/uda1341.h @@ -15,8 +15,6 @@ * features support */ -/* $Id: uda1341.h,v 1.8 2005/11/17 14:17:21 tiwai Exp $ */ - #define UDA1341_ALSA_NAME "snd-uda1341" /* diff --git a/sound/arm/sa11xx-uda1341.c b/sound/arm/sa11xx-uda1341.c index 0eff33ca0f7..faeddf3eced 100644 --- a/sound/arm/sa11xx-uda1341.c +++ b/sound/arm/sa11xx-uda1341.c @@ -21,8 +21,6 @@ * merged HAL layer (patches from Brian) */ -/* $Id: sa11xx-uda1341.c,v 1.27 2005/12/07 09:13:42 cladisch Exp $ */ - /*************************************************************************************************** * * To understand what Alsa Drivers should be doing look at "Writing an Alsa Driver" by Takashi Iwai diff --git a/sound/i2c/l3/uda1341.c b/sound/i2c/l3/uda1341.c index bfa5d2c3608..1f4942ea141 100644 --- a/sound/i2c/l3/uda1341.c +++ b/sound/i2c/l3/uda1341.c @@ -17,8 +17,6 @@ * 2002-05-12 Tomas Kasparek another code cleanup */ -/* $Id: uda1341.c,v 1.18 2005/11/17 14:17:21 tiwai Exp $ */ - #include #include #include diff --git a/sound/pci/au88x0/au88x0_game.c b/sound/pci/au88x0/au88x0_game.c index bc212f41a38..e291aa59742 100644 --- a/sound/pci/au88x0/au88x0_game.c +++ b/sound/pci/au88x0/au88x0_game.c @@ -1,6 +1,4 @@ /* - * $Id: au88x0_game.c,v 1.9 2003/09/22 03:51:28 mjander Exp $ - * * Manuel Jander. * * Based on the work of: -- cgit v1.2.3-70-g09d2 From 62cf872a8eec1f11aacbec0ac3fe3698bfa9b403 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Tue, 20 May 2008 12:15:15 +0200 Subject: [ALSA] Replace CONFIG_SND_DEBUG_DETECT with CONFIG_SND_DEBUG_VERBOSE Replace CONFIG_SND_DEBUG_DETECT with CONFIG_SND_DEBUG_VERBOSE to represent its meaning more better. This config isn't provided only for the detection but for more verbose debug prints in general. Signed-off-by: Takashi Iwai --- Documentation/sound/alsa/ALSA-Configuration.txt | 2 +- .../sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 4 ++-- include/sound/core.h | 8 ++++---- sound/core/Kconfig | 10 ++++++---- sound/pci/hda/hda_codec.c | 2 +- sound/pci/hda/hda_hwdep.c | 2 +- sound/pci/pcxhr/pcxhr.c | 4 ++-- sound/pci/pcxhr/pcxhr_core.c | 18 +++++++++--------- 8 files changed, 26 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/Documentation/sound/alsa/ALSA-Configuration.txt b/Documentation/sound/alsa/ALSA-Configuration.txt index 0bbee38acd2..e59569462cb 100644 --- a/Documentation/sound/alsa/ALSA-Configuration.txt +++ b/Documentation/sound/alsa/ALSA-Configuration.txt @@ -1091,7 +1091,7 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed. This occurs when the access to non-existing or non-working codec slot (likely a modem one) causes a stall of the communication via HD-audio bus. You can see which codec slots are probed by enabling - CONFIG_SND_DEBUG_DETECT, or simply from the file name of the codec + CONFIG_SND_DEBUG_VERBOSE, or simply from the file name of the codec proc files. Then limit the slots to probe by probe_mask option. For example, probe_mask=1 means to probe only the first slot, and probe_mask=4 means only the third slot. diff --git a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl index b03df4d4795..e13c4e67029 100644 --- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl +++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl @@ -6127,8 +6127,8 @@ struct _snd_pcm_runtime { snd_printdd() is compiled in only when - CONFIG_SND_DEBUG_DETECT is set. Please note - that DEBUG_DETECT is not set as default + CONFIG_SND_DEBUG_VERBOSE is set. Please note + that CONFIG_SND_DEBUG_VERBOSE is not set as default even if you configure the alsa-driver with option. You need to give explicitly option instead. diff --git a/include/sound/core.h b/include/sound/core.h index 695ee53488a..558b96284bd 100644 --- a/include/sound/core.h +++ b/include/sound/core.h @@ -412,13 +412,13 @@ void snd_verbose_printd(const char *file, int line, const char *format, ...) #endif /* CONFIG_SND_DEBUG */ -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE /** * snd_printdd - debug printk * @format: format string * * Works like snd_printk() for debugging purposes. - * Ignored when CONFIG_SND_DEBUG_DETECT is not set. + * Ignored when CONFIG_SND_DEBUG_VERBOSE is not set. */ #define snd_printdd(format, args...) snd_printk(format, ##args) #else @@ -442,7 +442,7 @@ struct snd_pci_quirk { unsigned short subvendor; /* PCI subvendor ID */ unsigned short subdevice; /* PCI subdevice ID */ int value; /* value */ -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE const char *name; /* name of the device (optional) */ #endif }; @@ -450,7 +450,7 @@ struct snd_pci_quirk { #define _SND_PCI_QUIRK_ID(vend,dev) \ .subvendor = (vend), .subdevice = (dev) #define SND_PCI_QUIRK_ID(vend,dev) {_SND_PCI_QUIRK_ID(vend, dev)} -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE #define SND_PCI_QUIRK(vend,dev,xname,val) \ {_SND_PCI_QUIRK_ID(vend, dev), .value = (val), .name = (xname)} #else diff --git a/sound/core/Kconfig b/sound/core/Kconfig index db211137578..335d45ecde6 100644 --- a/sound/core/Kconfig +++ b/sound/core/Kconfig @@ -153,12 +153,14 @@ config SND_DEBUG help Say Y here to enable ALSA debug code. -config SND_DEBUG_DETECT - bool "Debug detection" +config SND_DEBUG_VERBOSE + bool "More verbose debug" depends on SND_DEBUG help - Say Y here to enable extra-verbose log messages printed when - detecting devices. + Say Y here to enable extra-verbose debugging messages. + + Let me repeat: it enables EXTRA-VERBOSE DEBUGGING messages. + So, say Y only if you are ready to be annoyed. config SND_PCM_XRUN_DEBUG bool "Enable PCM ring buffer overrun/underrun debugging" diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a6be6e3e871..d2e1093f8e9 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -2335,7 +2335,7 @@ int snd_hda_check_board_config(struct hda_codec *codec, if (!tbl) return -1; if (tbl->value >= 0 && tbl->value < num_configs) { -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE char tmp[10]; const char *model = NULL; if (models) diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index 2177d9af533..6e18a422d99 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -88,7 +88,7 @@ static int hda_hwdep_ioctl_compat(struct snd_hwdep *hw, struct file *file, static int hda_hwdep_open(struct snd_hwdep *hw, struct file *file) { -#ifndef CONFIG_SND_DEBUG_DETECT +#ifndef CONFIG_SND_DEBUG_VERBOSE if (!capable(CAP_SYS_RAWIO)) return -EACCES; #endif diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c index 7fdcdc8c6b6..2c7e2533679 100644 --- a/sound/pci/pcxhr/pcxhr.c +++ b/sound/pci/pcxhr/pcxhr.c @@ -516,7 +516,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) int capture_mask = 0; int playback_mask = 0; -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE struct timeval my_tv1, my_tv2; do_gettimeofday(&my_tv1); #endif @@ -623,7 +623,7 @@ static void pcxhr_trigger_tasklet(unsigned long arg) mutex_unlock(&mgr->setup_mutex); -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE do_gettimeofday(&my_tv2); snd_printdd("***TRIGGER TASKLET*** TIME = %ld (err = %x)\n", (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); diff --git a/sound/pci/pcxhr/pcxhr_core.c b/sound/pci/pcxhr/pcxhr_core.c index 78aa81feaa4..abe5c59b72d 100644 --- a/sound/pci/pcxhr/pcxhr_core.c +++ b/sound/pci/pcxhr/pcxhr_core.c @@ -473,7 +473,7 @@ static struct pcxhr_cmd_info pcxhr_dsp_cmds[] = { [CMD_AUDIO_LEVEL_ADJUST] = { 0xc22000, 0, RMH_SSIZE_FIXED }, }; -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE static char* cmd_names[] = { [CMD_VERSION] = "CMD_VERSION", [CMD_SUPPORTED] = "CMD_SUPPORTED", @@ -549,7 +549,7 @@ static int pcxhr_read_rmh_status(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh) } } } -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE if (rmh->cmd_idx < CMD_LAST_INDEX) snd_printdd(" stat[%d]=%x\n", i, data); #endif @@ -597,7 +597,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh) data |= 0x008000; /* MASK_MORE_THAN_1_WORD_COMMAND */ else data &= 0xff7fff; /* MASK_1_WORD_COMMAND */ -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE if (rmh->cmd_idx < CMD_LAST_INDEX) snd_printdd("MSG cmd[0]=%x (%s)\n", data, cmd_names[rmh->cmd_idx]); #endif @@ -624,7 +624,7 @@ static int pcxhr_send_msg_nolock(struct pcxhr_mgr *mgr, struct pcxhr_rmh *rmh) for (i=1; i < rmh->cmd_len; i++) { /* send other words */ data = rmh->cmd[i]; -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE if (rmh->cmd_idx < CMD_LAST_INDEX) snd_printdd(" cmd[%d]=%x\n", i, data); #endif @@ -847,7 +847,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m int state, i, err; int audio_mask; -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE struct timeval my_tv1, my_tv2; do_gettimeofday(&my_tv1); #endif @@ -894,7 +894,7 @@ int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_m if (err) return err; } -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE do_gettimeofday(&my_tv2); snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n", (long)(my_tv2.tv_usec - my_tv1.tv_usec), err); @@ -951,7 +951,7 @@ static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err, enum pcxhr_async_err_src err_src, int pipe, int is_capture) { -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE static char* err_src_name[] = { [PCXHR_ERR_PIPE] = "Pipe", [PCXHR_ERR_STREAM] = "Stream", @@ -1169,7 +1169,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id) mgr->dsp_time_last, dsp_time_new); mgr->dsp_time_err++; } -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE if (dsp_time_diff == 0) snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new); else if (dsp_time_diff >= (2*PCXHR_GRANULARITY)) @@ -1208,7 +1208,7 @@ irqreturn_t pcxhr_interrupt(int irq, void *dev_id) mgr->src_it_dsp = reg; tasklet_hi_schedule(&mgr->msg_taskq); } -#ifdef CONFIG_SND_DEBUG_DETECT +#ifdef CONFIG_SND_DEBUG_VERBOSE if (reg & PCXHR_FATAL_DSP_ERR) snd_printdd("FATAL DSP ERROR : %x\n", reg); #endif -- cgit v1.2.3-70-g09d2 From e13ac2e9b18bde51cf32c69c2209df25791ab3e5 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 28 May 2008 17:58:05 +0100 Subject: [ALSA] ASoC: Add SOC_DOUBLE_S8_TLV control type The SOC_DOUBLE_S8_TLV control type was originally implemented in the UDA1380 driver by Philipp Zabel and was moved into the core by me. Signed-off-by: Philipp Zabel Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai --- include/sound/soc.h | 15 +++++++++++ sound/soc/soc-core.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index bca9538d9e5..9fa2093e74e 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -73,6 +73,15 @@ .get = snd_soc_get_volsw_2r, .put = snd_soc_put_volsw_2r, \ .private_value = (reg_left) | ((shift) << 8) | \ ((max) << 12) | ((invert) << 20) | ((reg_right) << 24) } +#define SOC_DOUBLE_S8_TLV(xname, reg, min, max, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \ + .put = snd_soc_put_volsw_s8, \ + .private_value = (reg) | (((signed char)max) << 16) | \ + (((signed char)min) << 24) } #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xmask, xtexts) \ { .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \ .mask = xmask, .texts = xtexts } @@ -267,6 +276,12 @@ int snd_soc_get_volsw_2r(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol); +int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo); +int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); +int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol); /* SoC PCM stream information */ struct snd_soc_pcm_stream { diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index a3f091e0843..f594ab888e1 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -1589,6 +1589,78 @@ int snd_soc_put_volsw_2r(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_2r); +/** + * snd_soc_info_volsw_s8 - signed mixer info callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to provide information about a signed mixer control. + * + * Returns 0 for success. + */ +int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int max = (signed char)((kcontrol->private_value >> 16) & 0xff); + int min = (signed char)((kcontrol->private_value >> 24) & 0xff); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = max-min; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8); + +/** + * snd_soc_get_volsw_s8 - signed mixer get callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to get the value of a signed mixer control. + * + * Returns 0 for success. + */ +int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int min = (signed char)((kcontrol->private_value >> 24) & 0xff); + int val = snd_soc_read(codec, reg); + + ucontrol->value.integer.value[0] = + ((signed char)(val & 0xff))-min; + ucontrol->value.integer.value[1] = + ((signed char)((val >> 8) & 0xff))-min; + return 0; +} +EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8); + +/** + * snd_soc_put_volsw_sgn - signed mixer put callback + * @kcontrol: mixer control + * @uinfo: control element information + * + * Callback to set the value of a signed mixer control. + * + * Returns 0 for success. + */ +int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); + int reg = kcontrol->private_value & 0xff; + int min = (signed char)((kcontrol->private_value >> 24) & 0xff); + unsigned short val; + + val = (ucontrol->value.integer.value[0]+min) & 0xff; + val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8; + + return snd_soc_update_bits(codec, reg, 0xffff, val); +} +EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); + static int __devinit snd_soc_init(void) { printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION); -- cgit v1.2.3-70-g09d2 From d2cd74b158d7214a556226e3312f9fb1de64d7ae Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Jun 2008 11:45:53 +0200 Subject: [ALSA] emu10k1 - Fix inverted Analog/Digital mixer switch on Audigy2 On Audigy2 Platinum, the Analog/Digital mixer switch is inverted. https://bugzilla.novell.com/show_bug.cgi?id=396204 The patch adds a simple workaround. There might be another device requiring a similar fix, too (or fix for audigy2 generically), but right now I fix only the known broken one. Signed-off-by: Takashi Iwai --- include/sound/emu10k1.h | 1 + sound/pci/emu10k1/emu10k1_main.c | 1 + sound/pci/emu10k1/emumixer.c | 13 ++++++++++--- 3 files changed, 12 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/emu10k1.h b/include/sound/emu10k1.h index 7b7b9b13b4d..10ee28eac01 100644 --- a/include/sound/emu10k1.h +++ b/include/sound/emu10k1.h @@ -1670,6 +1670,7 @@ struct snd_emu_chip_details { unsigned char spi_dac; /* SPI interface for DAC */ unsigned char i2c_adc; /* I2C interface for ADC */ unsigned char adc_1361t; /* Use Philips 1361T ADC */ + unsigned char invert_shared_spdif; /* analog/digital switch inverted */ const char *driver; const char *name; const char *id; /* for backward compatibility - can be NULL if not needed */ diff --git a/sound/pci/emu10k1/emu10k1_main.c b/sound/pci/emu10k1/emu10k1_main.c index 548c9cc81af..2f283ea6ad9 100644 --- a/sound/pci/emu10k1/emu10k1_main.c +++ b/sound/pci/emu10k1/emu10k1_main.c @@ -1528,6 +1528,7 @@ static struct snd_emu_chip_details emu_chip_details[] = { .ca0151_chip = 1, .spk71 = 1, .spdif_bug = 1, + .invert_shared_spdif = 1, /* digital/analog switch swapped */ .adc_1361t = 1, /* 24 bit capture instead of 16bit. Fixes ALSA bug#324 */ .ac97_chip = 1} , {.vendor = 0x1102, .device = 0x0004, .revision = 0x04, diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c index fd221209abc..f34bbfb705f 100644 --- a/sound/pci/emu10k1/emumixer.c +++ b/sound/pci/emu10k1/emumixer.c @@ -1578,6 +1578,10 @@ static int snd_emu10k1_shared_spdif_get(struct snd_kcontrol *kcontrol, ucontrol->value.integer.value[0] = inl(emu->port + A_IOCFG) & A_IOCFG_GPOUT0 ? 1 : 0; else ucontrol->value.integer.value[0] = inl(emu->port + HCFG) & HCFG_GPOUT0 ? 1 : 0; + if (emu->card_capabilities->invert_shared_spdif) + ucontrol->value.integer.value[0] = + !ucontrol->value.integer.value[0]; + return 0; } @@ -1586,15 +1590,18 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, { unsigned long flags; struct snd_emu10k1 *emu = snd_kcontrol_chip(kcontrol); - unsigned int reg, val; + unsigned int reg, val, sw; int change = 0; + sw = ucontrol->value.integer.value[0]; + if (emu->card_capabilities->invert_shared_spdif) + sw = !sw; spin_lock_irqsave(&emu->reg_lock, flags); if ( emu->card_capabilities->i2c_adc) { /* Do nothing for Audigy 2 ZS Notebook */ } else if (emu->audigy) { reg = inl(emu->port + A_IOCFG); - val = ucontrol->value.integer.value[0] ? A_IOCFG_GPOUT0 : 0; + val = sw ? A_IOCFG_GPOUT0 : 0; change = (reg & A_IOCFG_GPOUT0) != val; if (change) { reg &= ~A_IOCFG_GPOUT0; @@ -1603,7 +1610,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol, } } reg = inl(emu->port + HCFG); - val = ucontrol->value.integer.value[0] ? HCFG_GPOUT0 : 0; + val = sw ? HCFG_GPOUT0 : 0; change |= (reg & HCFG_GPOUT0) != val; if (change) { reg &= ~HCFG_GPOUT0; -- cgit v1.2.3-70-g09d2 From 5a85bb02ba473aacd29f4039a74744f729443b31 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Tue, 3 Jun 2008 09:06:20 +0200 Subject: ALSA: Release v1.0.17rc1 Signed-off-by: Jaroslav Kysela --- include/sound/version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index ed6fb2eb1ea..2474b8ee99f 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ -/* include/version.h. Generated by alsa/ksync script. */ -#define CONFIG_SND_VERSION "1.0.16" +/* include/version.h */ +#define CONFIG_SND_VERSION "1.0.17rc1" #define CONFIG_SND_DATE "" -- cgit v1.2.3-70-g09d2 From abf1f5aafc1939db1f252e33914a3689e0f5830f Mon Sep 17 00:00:00 2001 From: Krzysztof Helt Date: Mon, 9 Jun 2008 23:07:28 +0200 Subject: ALSA: opti93x: add support for Opti93x codec in cs4231-lib This patch adds support for WSS compatible Opti93x codec to the cs4231-lib. Signed-off-by: Krzysztof Helt Tested-by: Rene Herman Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/cs4231-regs.h | 8 +++ include/sound/cs4231.h | 3 ++ sound/isa/cs423x/cs4231_lib.c | 118 ++++++++++++++++++++++++++++++++++++++---- 3 files changed, 120 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/sound/cs4231-regs.h b/include/sound/cs4231-regs.h index e8d1f3e31f9..92647532c45 100644 --- a/include/sound/cs4231-regs.h +++ b/include/sound/cs4231-regs.h @@ -177,4 +177,12 @@ #define CS4236_RIGHT_WAVE 0x1c /* right wavetable serial port volume */ #define CS4236_VERSION 0x9c /* chip version and ID */ +/* definitions for extended registers - OPTI93X */ +#define OPTi931_AUX_LEFT_INPUT 0x10 +#define OPTi931_AUX_RIGHT_INPUT 0x11 +#define OPTi93X_MIC_LEFT_INPUT 0x14 +#define OPTi93X_MIC_RIGHT_INPUT 0x15 +#define OPTi93X_OUT_LEFT 0x16 +#define OPTi93X_OUT_RIGHT 0x17 + #endif /* __SOUND_CS4231_REGS_H */ diff --git a/include/sound/cs4231.h b/include/sound/cs4231.h index 66055d702aa..f0785f9f4ae 100644 --- a/include/sound/cs4231.h +++ b/include/sound/cs4231.h @@ -58,6 +58,7 @@ /* compatible, but clones */ #define CS4231_HW_INTERWAVE 0x1000 /* InterWave chip */ #define CS4231_HW_OPL3SA2 0x1101 /* OPL3-SA2 chip, similar to cs4231 */ +#define CS4231_HW_OPTI93X 0x1102 /* Opti 930/931/933 */ /* defines for codec.hwshare */ #define CS4231_HWSHARE_IRQ (1<<0) @@ -120,6 +121,8 @@ unsigned char snd_cs4236_ext_in(struct snd_cs4231 *chip, unsigned char reg); void snd_cs4231_mce_up(struct snd_cs4231 *chip); void snd_cs4231_mce_down(struct snd_cs4231 *chip); +void snd_cs4231_overrange(struct snd_cs4231 *chip); + irqreturn_t snd_cs4231_interrupt(int irq, void *dev_id); const char *snd_cs4231_chip_id(struct snd_cs4231 *chip); diff --git a/sound/isa/cs423x/cs4231_lib.c b/sound/isa/cs423x/cs4231_lib.c index 0aa8649e5c7..521db705d17 100644 --- a/sound/isa/cs423x/cs4231_lib.c +++ b/sound/isa/cs423x/cs4231_lib.c @@ -119,6 +119,42 @@ static unsigned char snd_cs4231_original_image[32] = 0x00, /* 1f/31 - cbrl */ }; +static unsigned char snd_opti93x_original_image[32] = +{ + 0x00, /* 00/00 - l_mixout_outctrl */ + 0x00, /* 01/01 - r_mixout_outctrl */ + 0x88, /* 02/02 - l_cd_inctrl */ + 0x88, /* 03/03 - r_cd_inctrl */ + 0x88, /* 04/04 - l_a1/fm_inctrl */ + 0x88, /* 05/05 - r_a1/fm_inctrl */ + 0x80, /* 06/06 - l_dac_inctrl */ + 0x80, /* 07/07 - r_dac_inctrl */ + 0x00, /* 08/08 - ply_dataform_reg */ + 0x00, /* 09/09 - if_conf */ + 0x00, /* 0a/10 - pin_ctrl */ + 0x00, /* 0b/11 - err_init_reg */ + 0x0a, /* 0c/12 - id_reg */ + 0x00, /* 0d/13 - reserved */ + 0x00, /* 0e/14 - ply_upcount_reg */ + 0x00, /* 0f/15 - ply_lowcount_reg */ + 0x88, /* 10/16 - reserved/l_a1_inctrl */ + 0x88, /* 11/17 - reserved/r_a1_inctrl */ + 0x88, /* 12/18 - l_line_inctrl */ + 0x88, /* 13/19 - r_line_inctrl */ + 0x88, /* 14/20 - l_mic_inctrl */ + 0x88, /* 15/21 - r_mic_inctrl */ + 0x80, /* 16/22 - l_out_outctrl */ + 0x80, /* 17/23 - r_out_outctrl */ + 0x00, /* 18/24 - reserved */ + 0x00, /* 19/25 - reserved */ + 0x00, /* 1a/26 - reserved */ + 0x00, /* 1b/27 - reserved */ + 0x00, /* 1c/28 - cap_dataform_reg */ + 0x00, /* 1d/29 - reserved */ + 0x00, /* 1e/30 - cap_upcount_reg */ + 0x00 /* 1f/31 - cap_lowcount_reg */ +}; + /* * Basic I/O functions */ @@ -895,7 +931,7 @@ static int snd_cs4231_capture_prepare(struct snd_pcm_substream *substream) return 0; } -static void snd_cs4231_overrange(struct snd_cs4231 *chip) +void snd_cs4231_overrange(struct snd_cs4231 *chip) { unsigned long flags; unsigned char res; @@ -1054,8 +1090,11 @@ static int snd_cs4231_probe(struct snd_cs4231 *chip) chip->image[CS4231_IFACE_CTRL] = (chip->image[CS4231_IFACE_CTRL] & ~CS4231_SINGLE_DMA) | (chip->single_dma ? CS4231_SINGLE_DMA : 0); - chip->image[CS4231_ALT_FEATURE_1] = 0x80; - chip->image[CS4231_ALT_FEATURE_2] = chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; + if (chip->hardware != CS4231_HW_OPTI93X) { + chip->image[CS4231_ALT_FEATURE_1] = 0x80; + chip->image[CS4231_ALT_FEATURE_2] = + chip->hardware == CS4231_HW_INTERWAVE ? 0xc2 : 0x01; + } ptr = (unsigned char *) &chip->image; snd_cs4231_mce_down(chip); spin_lock_irqsave(&chip->reg_lock, flags); @@ -1376,6 +1415,7 @@ const char *snd_cs4231_chip_id(struct snd_cs4231 *chip) case CS4231_HW_INTERWAVE: return "AMD InterWave"; case CS4231_HW_OPL3SA2: return chip->card->shortname; case CS4231_HW_AD1845: return "AD1845"; + case CS4231_HW_OPTI93X: return "OPTi 93x"; default: return "???"; } } @@ -1401,8 +1441,13 @@ static int snd_cs4231_new(struct snd_card *card, chip->rate_constraint = snd_cs4231_xrate; chip->set_playback_format = snd_cs4231_playback_format; chip->set_capture_format = snd_cs4231_capture_format; - memcpy(&chip->image, &snd_cs4231_original_image, sizeof(snd_cs4231_original_image)); - + if (chip->hardware == CS4231_HW_OPTI93X) + memcpy(&chip->image, &snd_opti93x_original_image, + sizeof(snd_opti93x_original_image)); + else + memcpy(&chip->image, &snd_cs4231_original_image, + sizeof(snd_cs4231_original_image)); + *rchip = chip; return 0; } @@ -1790,6 +1835,48 @@ CS4231_SINGLE("Loopback Capture Switch", 0, CS4231_LOOPBACK, 0, 1, 0), CS4231_SINGLE("Loopback Capture Volume", 0, CS4231_LOOPBACK, 2, 63, 1) }; +static struct snd_kcontrol_new snd_opti93x_controls[] = { +CS4231_DOUBLE("Master Playback Switch", 0, + OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 7, 7, 1, 1), +CS4231_DOUBLE("Master Playback Volume", 0, + OPTi93X_OUT_LEFT, OPTi93X_OUT_RIGHT, 1, 1, 31, 1), +CS4231_DOUBLE("PCM Playback Switch", 0, + CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 7, 7, 1, 1), +CS4231_DOUBLE("PCM Playback Volume", 0, + CS4231_LEFT_OUTPUT, CS4231_RIGHT_OUTPUT, 0, 0, 31, 1), +CS4231_DOUBLE("FM Playback Switch", 0, + CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("FM Playback Volume", 0, + CS4231_AUX2_LEFT_INPUT, CS4231_AUX2_RIGHT_INPUT, 1, 1, 15, 1), +CS4231_DOUBLE("Line Playback Switch", 0, + CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 7, 7, 1, 1), +CS4231_DOUBLE("Line Playback Volume", 0, + CS4231_LEFT_LINE_IN, CS4231_RIGHT_LINE_IN, 0, 0, 15, 1), +CS4231_DOUBLE("Mic Playback Switch", 0, + OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Mic Playback Volume", 0, + OPTi93X_MIC_LEFT_INPUT, OPTi93X_MIC_RIGHT_INPUT, 1, 1, 15, 1), +CS4231_DOUBLE("Mic Boost", 0, + CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 5, 5, 1, 0), +CS4231_DOUBLE("CD Playback Switch", 0, + CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("CD Playback Volume", 0, + CS4231_AUX1_LEFT_INPUT, CS4231_AUX1_RIGHT_INPUT, 1, 1, 15, 1), +CS4231_DOUBLE("Aux Playback Switch", 0, + OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 7, 7, 1, 1), +CS4231_DOUBLE("Aux Playback Volume", 0, + OPTi931_AUX_LEFT_INPUT, OPTi931_AUX_RIGHT_INPUT, 1, 1, 15, 1), +CS4231_DOUBLE("Capture Volume", 0, + CS4231_LEFT_INPUT, CS4231_RIGHT_INPUT, 0, 0, 15, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .info = snd_cs4231_info_mux, + .get = snd_cs4231_get_mux, + .put = snd_cs4231_put_mux, +} +}; + int snd_cs4231_mixer(struct snd_cs4231 *chip) { struct snd_card *card; @@ -1802,10 +1889,22 @@ int snd_cs4231_mixer(struct snd_cs4231 *chip) strcpy(card->mixername, chip->pcm->name); - for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { - if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_cs4231_controls[idx], chip))) < 0) - return err; - } + if (chip->hardware == CS4231_HW_OPTI93X) + for (idx = 0; idx < ARRAY_SIZE(snd_opti93x_controls); idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_opti93x_controls[idx], + chip)); + if (err < 0) + return err; + } + else + for (idx = 0; idx < ARRAY_SIZE(snd_cs4231_controls); idx++) { + err = snd_ctl_add(card, + snd_ctl_new1(&snd_cs4231_controls[idx], + chip)); + if (err < 0) + return err; + } return 0; } @@ -1815,6 +1914,7 @@ EXPORT_SYMBOL(snd_cs4236_ext_out); EXPORT_SYMBOL(snd_cs4236_ext_in); EXPORT_SYMBOL(snd_cs4231_mce_up); EXPORT_SYMBOL(snd_cs4231_mce_down); +EXPORT_SYMBOL(snd_cs4231_overrange); EXPORT_SYMBOL(snd_cs4231_interrupt); EXPORT_SYMBOL(snd_cs4231_chip_id); EXPORT_SYMBOL(snd_cs4231_create); -- cgit v1.2.3-70-g09d2 From bdb92876f0a9d2b431199e385732ede89ff0b97d Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Wed, 11 Jun 2008 13:47:10 +0100 Subject: ALSA: ASoC: Pass the DAI being configured into CPU DAI probe and remove This allows per-DAI initialisation to be done by the CPU DAI drivers. Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 6 ++++-- sound/soc/davinci/davinci-i2s.c | 6 ++++-- sound/soc/pxa/pxa2xx-ac97.c | 3 ++- sound/soc/s3c24xx/s3c2412-i2s.c | 3 ++- sound/soc/s3c24xx/s3c2443-ac97.c | 6 ++++-- sound/soc/s3c24xx/s3c24xx-i2s.c | 3 ++- sound/soc/soc-core.c | 6 +++--- 7 files changed, 21 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 9fa2093e74e..56d2224c2c0 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -358,8 +358,10 @@ struct snd_soc_cpu_dai { unsigned char type; /* DAI callbacks */ - int (*probe)(struct platform_device *pdev); - void (*remove)(struct platform_device *pdev); + int (*probe)(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai); + void (*remove)(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai); int (*suspend)(struct platform_device *pdev, struct snd_soc_cpu_dai *cpu_dai); int (*resume)(struct platform_device *pdev, diff --git a/sound/soc/davinci/davinci-i2s.c b/sound/soc/davinci/davinci-i2s.c index c421774b33e..c3b545ccff7 100644 --- a/sound/soc/davinci/davinci-i2s.c +++ b/sound/soc/davinci/davinci-i2s.c @@ -295,7 +295,8 @@ static int davinci_i2s_trigger(struct snd_pcm_substream *substream, int cmd) return ret; } -static int davinci_i2s_probe(struct platform_device *pdev) +static int davinci_i2s_probe(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_machine *machine = socdev->machine; @@ -356,7 +357,8 @@ err_release_region: return ret; } -static void davinci_i2s_remove(struct platform_device *pdev) +static void davinci_i2s_remove(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { struct snd_soc_device *socdev = platform_get_drvdata(pdev); struct snd_soc_machine *machine = socdev->machine; diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c index 97ec2d90547..cb947956ed1 100644 --- a/sound/soc/pxa/pxa2xx-ac97.c +++ b/sound/soc/pxa/pxa2xx-ac97.c @@ -310,7 +310,8 @@ static int pxa2xx_ac97_resume(struct platform_device *pdev, #define pxa2xx_ac97_resume NULL #endif -static int pxa2xx_ac97_probe(struct platform_device *pdev) +static int pxa2xx_ac97_probe(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { int ret; diff --git a/sound/soc/s3c24xx/s3c2412-i2s.c b/sound/soc/s3c24xx/s3c2412-i2s.c index c4a46dd589b..c463a82dec3 100644 --- a/sound/soc/s3c24xx/s3c2412-i2s.c +++ b/sound/soc/s3c24xx/s3c2412-i2s.c @@ -601,7 +601,8 @@ struct clk *s3c2412_get_iisclk(void) EXPORT_SYMBOL_GPL(s3c2412_get_iisclk); -static int s3c2412_i2s_probe(struct platform_device *pdev) +static int s3c2412_i2s_probe(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { DBG("Entered %s\n", __func__); diff --git a/sound/soc/s3c24xx/s3c2443-ac97.c b/sound/soc/s3c24xx/s3c2443-ac97.c index 0eed140dcd9..533565b61b2 100644 --- a/sound/soc/s3c24xx/s3c2443-ac97.c +++ b/sound/soc/s3c24xx/s3c2443-ac97.c @@ -209,7 +209,8 @@ static struct s3c24xx_pcm_dma_params s3c2443_ac97_mic_mono_in = { .dma_size = 4, }; -static int s3c2443_ac97_probe(struct platform_device *pdev) +static int s3c2443_ac97_probe(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { int ret; u32 ac_glbctrl; @@ -260,7 +261,8 @@ static int s3c2443_ac97_probe(struct platform_device *pdev) return ret; } -static void s3c2443_ac97_remove(struct platform_device *pdev) +static void s3c2443_ac97_remove(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { free_irq(IRQ_S3C244x_AC97, NULL); clk_disable(s3c24xx_ac97.ac97_clk); diff --git a/sound/soc/s3c24xx/s3c24xx-i2s.c b/sound/soc/s3c24xx/s3c24xx-i2s.c index 4c52f7946d9..42e96b5ff82 100644 --- a/sound/soc/s3c24xx/s3c24xx-i2s.c +++ b/sound/soc/s3c24xx/s3c24xx-i2s.c @@ -377,7 +377,8 @@ u32 s3c24xx_i2s_get_clockrate(void) } EXPORT_SYMBOL_GPL(s3c24xx_i2s_get_clockrate); -static int s3c24xx_i2s_probe(struct platform_device *pdev) +static int s3c24xx_i2s_probe(struct platform_device *pdev, + struct snd_soc_cpu_dai *dai) { DBG("Entered %s\n", __func__); diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index f594ab888e1..c96a6184d66 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -768,7 +768,7 @@ static int soc_probe(struct platform_device *pdev) for (i = 0; i < machine->num_links; i++) { struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->probe) { - ret = cpu_dai->probe(pdev); + ret = cpu_dai->probe(pdev, cpu_dai); if (ret < 0) goto cpu_dai_err; } @@ -798,7 +798,7 @@ cpu_dai_err: for (i--; i >= 0; i--) { struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->remove) - cpu_dai->remove(pdev); + cpu_dai->remove(pdev, cpu_dai); } if (machine->remove) @@ -827,7 +827,7 @@ static int soc_remove(struct platform_device *pdev) for (i = 0; i < machine->num_links; i++) { struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->remove) - cpu_dai->remove(pdev); + cpu_dai->remove(pdev, cpu_dai); } if (machine->remove) -- cgit v1.2.3-70-g09d2 From 10144c09a0d6a62e1d56e25f142743c7a00e5dba Mon Sep 17 00:00:00 2001 From: Mike Montour Date: Wed, 11 Jun 2008 13:47:13 +0100 Subject: ALSA: ASoC: Add SOC_SINGLE_EXT_TLV control type Signed-off-by: Mike Montour Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 56d2224c2c0..1f5c6218100 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -100,6 +100,15 @@ .info = snd_soc_info_volsw, \ .get = xhandler_get, .put = xhandler_put, \ .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) } +#define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmask, xinvert,\ + xhandler_get, xhandler_put, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\ + SNDRV_CTL_ELEM_ACCESS_READWRITE,\ + .tlv.p = (tlv_array), \ + .info = snd_soc_info_volsw, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmask, xinvert) } #define SOC_SINGLE_BOOL_EXT(xname, xdata, xhandler_get, xhandler_put) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ .info = snd_soc_info_bool_ext, \ -- cgit v1.2.3-70-g09d2 From d3bd67cdbb5d12f30dfd1b627fd95bd6dcc7ff1e Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Thu, 12 Jun 2008 18:17:26 +0200 Subject: ALSA: make snd_ctl_elem_read() and snd_ctl_elem_write() static snd_ctl_elem_read() and snd_ctl_elem_write() are no longer used by any other drivers. Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/control.h | 3 --- sound/core/control.c | 7 ++++--- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/include/sound/control.h b/include/sound/control.h index 3dc1291f52d..4721b4bba05 100644 --- a/include/sound/control.h +++ b/include/sound/control.h @@ -129,9 +129,6 @@ int snd_ctl_unregister_ioctl_compat(snd_kctl_ioctl_func_t fcn); #define snd_ctl_unregister_ioctl_compat(fcn) #endif -int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control); -int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, struct snd_ctl_elem_value *control); - static inline unsigned int snd_ctl_get_ioffnum(struct snd_kcontrol *kctl, struct snd_ctl_elem_id *id) { return id->numid - kctl->id.numid; diff --git a/sound/core/control.c b/sound/core/control.c index 01a1a5af47b..281b2e2ef0e 100644 --- a/sound/core/control.c +++ b/sound/core/control.c @@ -684,7 +684,8 @@ static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, return result; } -int snd_ctl_elem_read(struct snd_card *card, struct snd_ctl_elem_value *control) +static int snd_ctl_elem_read(struct snd_card *card, + struct snd_ctl_elem_value *control) { struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; @@ -734,8 +735,8 @@ static int snd_ctl_elem_read_user(struct snd_card *card, return result; } -int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, - struct snd_ctl_elem_value *control) +static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file, + struct snd_ctl_elem_value *control) { struct snd_kcontrol *kctl; struct snd_kcontrol_volatile *vd; -- cgit v1.2.3-70-g09d2 From 6ed2597883b1b03ca94f62f0cfe908314cba6d6b Mon Sep 17 00:00:00 2001 From: Andy Green Date: Fri, 13 Jun 2008 16:24:05 +0100 Subject: ALSA: ASoC: Don't block system resume On OpenMoko soc-audio resume is taking 700ms of the whole resume time of 1.3s, dominated by writes to the codec over I2C. This patch shunts the resume guts into a workqueue which then is done asynchronously. The "card" is locked using the ALSA power state APIs as suggested by Mark Brown. [Added fix for race with resume to suspend and fixed a couple of nits from checkpatch -- broonie.] Signed-off-by: Andy Green Signed-off-by: Mark Brown Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 1 + sound/soc/soc-core.c | 46 +++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 1f5c6218100..340223a8f24 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -510,6 +510,7 @@ struct snd_soc_device { struct snd_soc_codec *codec; struct snd_soc_codec_device *codec_dev; struct delayed_work delayed_work; + struct work_struct deferred_resume_work; void *codec_data; }; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index c96a6184d66..b931039632c 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -639,6 +639,16 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) struct snd_soc_codec *codec = socdev->codec; int i; + /* Due to the resume being scheduled into a workqueue we could + * suspend before that's finished - wait for it to complete. + */ + snd_power_lock(codec->card); + snd_power_wait(codec->card, SNDRV_CTL_POWER_D0); + snd_power_unlock(codec->card); + + /* we're going to block userspace touching us until resume completes */ + snd_power_change_state(codec->card, SNDRV_CTL_POWER_D3hot); + /* mute any active DAC's */ for (i = 0; i < machine->num_links; i++) { struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; @@ -691,16 +701,27 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) return 0; } -/* powers up audio subsystem after a suspend */ -static int soc_resume(struct platform_device *pdev) +/* deferred resume work, so resume can complete before we finished + * setting our codec back up, which can be very slow on I2C + */ +static void soc_resume_deferred(struct work_struct *work) { - struct snd_soc_device *socdev = platform_get_drvdata(pdev); + struct snd_soc_device *socdev = container_of(work, + struct snd_soc_device, + deferred_resume_work); struct snd_soc_machine *machine = socdev->machine; struct snd_soc_platform *platform = socdev->platform; struct snd_soc_codec_device *codec_dev = socdev->codec_dev; struct snd_soc_codec *codec = socdev->codec; + struct platform_device *pdev = to_platform_device(socdev->dev); int i; + /* our power state is still SNDRV_CTL_POWER_D3hot from suspend time, + * so userspace apps are blocked from touching us + */ + + dev_info(socdev->dev, "starting resume work\n"); + if (machine->resume_pre) machine->resume_pre(pdev); @@ -742,6 +763,22 @@ static int soc_resume(struct platform_device *pdev) if (machine->resume_post) machine->resume_post(pdev); + dev_info(socdev->dev, "resume work completed\n"); + + /* userspace can access us now we are back as we were before */ + snd_power_change_state(codec->card, SNDRV_CTL_POWER_D0); +} + +/* powers up audio subsystem after a suspend */ +static int soc_resume(struct platform_device *pdev) +{ + struct snd_soc_device *socdev = platform_get_drvdata(pdev); + + dev_info(socdev->dev, "scheduling resume work\n"); + + if (!schedule_work(&socdev->deferred_resume_work)) + dev_err(socdev->dev, "work item may be lost\n"); + return 0; } @@ -788,6 +825,9 @@ static int soc_probe(struct platform_device *pdev) /* DAPM stream work */ INIT_DELAYED_WORK(&socdev->delayed_work, close_delayed_work); + /* deferred resume work */ + INIT_WORK(&socdev->deferred_resume_work, soc_resume_deferred); + return 0; platform_err: -- cgit v1.2.3-70-g09d2 From b7d2399242b639f0b0be5f901506a7180d961b64 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 16 Jun 2008 09:42:25 +0200 Subject: ALSA: Release 1.0.17rc2 Signed-off-by: Jaroslav Kysela --- include/sound/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index 2474b8ee99f..1802cc59333 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h */ -#define CONFIG_SND_VERSION "1.0.17rc1" +#define CONFIG_SND_VERSION "1.0.17rc2" #define CONFIG_SND_DATE "" -- cgit v1.2.3-70-g09d2 From 28bb171169247754bae47bad0609208333e7a34b Mon Sep 17 00:00:00 2001 From: Harvey Harrison Date: Wed, 18 Jun 2008 13:45:13 -0700 Subject: alsa: add annotations to bitwise type snd_pcm_hw_param_t Fully half of all alsa sparse warnings are from snd_pcm_hw_param_t degrading to integer type, this goes a long way towards eliminating them. Signed-off-by: Harvey Harrison Signed-off-by: Jaroslav Kysela --- include/sound/asound.h | 12 ++++++++---- include/sound/pcm.h | 32 ++++++++++++++++---------------- 2 files changed, 24 insertions(+), 20 deletions(-) (limited to 'include') diff --git a/include/sound/asound.h b/include/sound/asound.h index 3eaf155b850..0309da2f11d 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -302,6 +302,8 @@ typedef int __bitwise snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAM_SUBFORMAT ((__force snd_pcm_hw_param_t) 2) /* Subformat */ #define SNDRV_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_ACCESS #define SNDRV_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_SUBFORMAT +#define SNDRV_PCM_HW_PARAM_MASK_INDEX(var) \ + ((__force int)(var) - (__force int)SNDRV_PCM_HW_PARAM_FIRST_MASK) #define SNDRV_PCM_HW_PARAM_SAMPLE_BITS ((__force snd_pcm_hw_param_t) 8) /* Bits per sample */ #define SNDRV_PCM_HW_PARAM_FRAME_BITS ((__force snd_pcm_hw_param_t) 9) /* Bits per frame */ @@ -317,6 +319,8 @@ typedef int __bitwise snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAM_TICK_TIME ((__force snd_pcm_hw_param_t) 19) /* Approx tick duration in us */ #define SNDRV_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_SAMPLE_BITS #define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME +#define SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var) \ + ((__force int)(var) - (__force int)SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ @@ -336,11 +340,11 @@ struct snd_mask { struct snd_pcm_hw_params { unsigned int flags; - struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct snd_mask masks[ + SNDRV_PCM_HW_PARAM_MASK_INDEX(SNDRV_PCM_HW_PARAM_LAST_MASK) + 1]; struct snd_mask mres[5]; /* reserved masks */ - struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct snd_interval intervals[ + SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(SNDRV_PCM_HW_PARAM_LAST_INTERVAL) + 1]; struct snd_interval ires[9]; /* reserved intervals */ unsigned int rmask; /* W: requested masks */ unsigned int cmask; /* R: changed masks */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 51d58ccda2d..5315b53f9b0 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -209,10 +209,10 @@ struct snd_pcm_hw_rule { }; struct snd_pcm_hw_constraints { - struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - - SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; - struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; + struct snd_mask masks[ + SNDRV_PCM_HW_PARAM_MASK_INDEX(SNDRV_PCM_HW_PARAM_LAST_MASK) + 1]; + struct snd_interval intervals[ + SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(SNDRV_PCM_HW_PARAM_LAST_INTERVAL) + 1]; unsigned int rules_num; unsigned int rules_all; struct snd_pcm_hw_rule *rules; @@ -221,13 +221,13 @@ struct snd_pcm_hw_constraints { static inline struct snd_mask *constrs_mask(struct snd_pcm_hw_constraints *constrs, snd_pcm_hw_param_t var) { - return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + return &constrs->masks[SNDRV_PCM_HW_PARAM_MASK_INDEX(var)]; } static inline struct snd_interval *constrs_interval(struct snd_pcm_hw_constraints *constrs, snd_pcm_hw_param_t var) { - return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + return &constrs->intervals[SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var)]; } struct snd_ratnum { @@ -761,40 +761,40 @@ static inline void snd_pcm_trigger_done(struct snd_pcm_substream *substream, substream->runtime->trigger_master = master; } -static inline int hw_is_mask(int var) +static inline int hw_is_mask(snd_pcm_hw_param_t var) { - return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && - var <= SNDRV_PCM_HW_PARAM_LAST_MASK; + return (__force int)var >= (__force int)SNDRV_PCM_HW_PARAM_FIRST_MASK && + (__force int)var <= (__force int)SNDRV_PCM_HW_PARAM_LAST_MASK; } -static inline int hw_is_interval(int var) +static inline int hw_is_interval(snd_pcm_hw_param_t var) { - return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && - var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; + return (__force int)var >= (__force int)SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && + (__force int)var <= (__force int)SNDRV_PCM_HW_PARAM_LAST_INTERVAL; } static inline struct snd_mask *hw_param_mask(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + return ¶ms->masks[SNDRV_PCM_HW_PARAM_MASK_INDEX(var)]; } static inline struct snd_interval *hw_param_interval(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + return ¶ms->intervals[SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var)]; } static inline const struct snd_mask *hw_param_mask_c(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; + return ¶ms->masks[SNDRV_PCM_HW_PARAM_MASK_INDEX(var)]; } static inline const struct snd_interval *hw_param_interval_c(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; + return ¶ms->intervals[SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var)]; } #define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) -- cgit v1.2.3-70-g09d2 From eabe3228a80728a21f871ccc86c72c170ca1a2dc Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Thu, 26 Jun 2008 08:51:31 +0200 Subject: [ALSA] Revert "alsa: add annotations to bitwise type snd_pcm_hw_param_t" This reverts commit 36b34d2437104f323e09d7c6af6451d3c0b9c0cd. From: Al Viro WIW, *all* this stuff is not bitwise at all. For crying out loud, half of these types are routinely used as array indices and loop variables... If anything, we want a different set of allowed operations - subtraction between elements of type (yielding integer), addition/subtraction of integer types not bigger than ours (yielding our type), comparisons, assignments (=, +=, -=, passing to function as argument, return from function, initializers) and second/third arguments in ?:. With 0 *not* being allowed as a constant of such type. It's not bitwise; we may use the same infrastructure in sparse, but it should be a separate class of types (__attribute__((affine))). dma_addr_t is another candidate for the same treatment, but there we'll need helpers for conversions to hw-acceptable form (dma_to_le32(), etc.) and gradual conversion of drivers. ALSA ones and pm mess are absolutely straightforward cases, though. Signed-off-by: Jaroslav Kysela --- include/sound/asound.h | 12 ++++-------- include/sound/pcm.h | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/sound/asound.h b/include/sound/asound.h index 0309da2f11d..3eaf155b850 100644 --- a/include/sound/asound.h +++ b/include/sound/asound.h @@ -302,8 +302,6 @@ typedef int __bitwise snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAM_SUBFORMAT ((__force snd_pcm_hw_param_t) 2) /* Subformat */ #define SNDRV_PCM_HW_PARAM_FIRST_MASK SNDRV_PCM_HW_PARAM_ACCESS #define SNDRV_PCM_HW_PARAM_LAST_MASK SNDRV_PCM_HW_PARAM_SUBFORMAT -#define SNDRV_PCM_HW_PARAM_MASK_INDEX(var) \ - ((__force int)(var) - (__force int)SNDRV_PCM_HW_PARAM_FIRST_MASK) #define SNDRV_PCM_HW_PARAM_SAMPLE_BITS ((__force snd_pcm_hw_param_t) 8) /* Bits per sample */ #define SNDRV_PCM_HW_PARAM_FRAME_BITS ((__force snd_pcm_hw_param_t) 9) /* Bits per frame */ @@ -319,8 +317,6 @@ typedef int __bitwise snd_pcm_hw_param_t; #define SNDRV_PCM_HW_PARAM_TICK_TIME ((__force snd_pcm_hw_param_t) 19) /* Approx tick duration in us */ #define SNDRV_PCM_HW_PARAM_FIRST_INTERVAL SNDRV_PCM_HW_PARAM_SAMPLE_BITS #define SNDRV_PCM_HW_PARAM_LAST_INTERVAL SNDRV_PCM_HW_PARAM_TICK_TIME -#define SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var) \ - ((__force int)(var) - (__force int)SNDRV_PCM_HW_PARAM_FIRST_INTERVAL) #define SNDRV_PCM_HW_PARAMS_NORESAMPLE (1<<0) /* avoid rate resampling */ @@ -340,11 +336,11 @@ struct snd_mask { struct snd_pcm_hw_params { unsigned int flags; - struct snd_mask masks[ - SNDRV_PCM_HW_PARAM_MASK_INDEX(SNDRV_PCM_HW_PARAM_LAST_MASK) + 1]; + struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; struct snd_mask mres[5]; /* reserved masks */ - struct snd_interval intervals[ - SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(SNDRV_PCM_HW_PARAM_LAST_INTERVAL) + 1]; + struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; struct snd_interval ires[9]; /* reserved intervals */ unsigned int rmask; /* W: requested masks */ unsigned int cmask; /* R: changed masks */ diff --git a/include/sound/pcm.h b/include/sound/pcm.h index 5315b53f9b0..51d58ccda2d 100644 --- a/include/sound/pcm.h +++ b/include/sound/pcm.h @@ -209,10 +209,10 @@ struct snd_pcm_hw_rule { }; struct snd_pcm_hw_constraints { - struct snd_mask masks[ - SNDRV_PCM_HW_PARAM_MASK_INDEX(SNDRV_PCM_HW_PARAM_LAST_MASK) + 1]; - struct snd_interval intervals[ - SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(SNDRV_PCM_HW_PARAM_LAST_INTERVAL) + 1]; + struct snd_mask masks[SNDRV_PCM_HW_PARAM_LAST_MASK - + SNDRV_PCM_HW_PARAM_FIRST_MASK + 1]; + struct snd_interval intervals[SNDRV_PCM_HW_PARAM_LAST_INTERVAL - + SNDRV_PCM_HW_PARAM_FIRST_INTERVAL + 1]; unsigned int rules_num; unsigned int rules_all; struct snd_pcm_hw_rule *rules; @@ -221,13 +221,13 @@ struct snd_pcm_hw_constraints { static inline struct snd_mask *constrs_mask(struct snd_pcm_hw_constraints *constrs, snd_pcm_hw_param_t var) { - return &constrs->masks[SNDRV_PCM_HW_PARAM_MASK_INDEX(var)]; + return &constrs->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; } static inline struct snd_interval *constrs_interval(struct snd_pcm_hw_constraints *constrs, snd_pcm_hw_param_t var) { - return &constrs->intervals[SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var)]; + return &constrs->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } struct snd_ratnum { @@ -761,40 +761,40 @@ static inline void snd_pcm_trigger_done(struct snd_pcm_substream *substream, substream->runtime->trigger_master = master; } -static inline int hw_is_mask(snd_pcm_hw_param_t var) +static inline int hw_is_mask(int var) { - return (__force int)var >= (__force int)SNDRV_PCM_HW_PARAM_FIRST_MASK && - (__force int)var <= (__force int)SNDRV_PCM_HW_PARAM_LAST_MASK; + return var >= SNDRV_PCM_HW_PARAM_FIRST_MASK && + var <= SNDRV_PCM_HW_PARAM_LAST_MASK; } -static inline int hw_is_interval(snd_pcm_hw_param_t var) +static inline int hw_is_interval(int var) { - return (__force int)var >= (__force int)SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && - (__force int)var <= (__force int)SNDRV_PCM_HW_PARAM_LAST_INTERVAL; + return var >= SNDRV_PCM_HW_PARAM_FIRST_INTERVAL && + var <= SNDRV_PCM_HW_PARAM_LAST_INTERVAL; } static inline struct snd_mask *hw_param_mask(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->masks[SNDRV_PCM_HW_PARAM_MASK_INDEX(var)]; + return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; } static inline struct snd_interval *hw_param_interval(struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->intervals[SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var)]; + return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } static inline const struct snd_mask *hw_param_mask_c(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->masks[SNDRV_PCM_HW_PARAM_MASK_INDEX(var)]; + return ¶ms->masks[var - SNDRV_PCM_HW_PARAM_FIRST_MASK]; } static inline const struct snd_interval *hw_param_interval_c(const struct snd_pcm_hw_params *params, snd_pcm_hw_param_t var) { - return ¶ms->intervals[SNDRV_PCM_HW_PARAM_INTERVAL_INDEX(var)]; + return ¶ms->intervals[var - SNDRV_PCM_HW_PARAM_FIRST_INTERVAL]; } #define params_access(p) snd_mask_min(hw_param_mask((p), SNDRV_PCM_HW_PARAM_ACCESS)) -- cgit v1.2.3-70-g09d2 From e2be2ccf9416bb4e0eb5f851967e79261f41d7e5 Mon Sep 17 00:00:00 2001 From: Jarkko Nikula Date: Wed, 25 Jun 2008 14:42:07 +0300 Subject: ALSA: ASoC: Add support for generic DAPM register modifier widget This generic register modifier widget is for updating multiple codec register bits at once when the widget changes its power state. Signed-off-by: Jarkko Nikula Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc-dapm.h | 14 ++++++++++++++ sound/soc/soc-dapm.c | 19 +++++++++++++++++++ 2 files changed, 33 insertions(+) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index f8223fae580..b2849538cbf 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -130,6 +130,13 @@ { .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \ .shift = wshift, .invert = winvert} +/* generic register modifier widget */ +#define SND_SOC_DAPM_REG(wid, wname, wreg, wshift, wmask, won_val, woff_val) \ +{ .id = wid, .name = wname, .kcontrols = NULL, .num_kcontrols = 0, \ + .reg = -((wreg) + 1), .shift = wshift, .mask = wmask, \ + .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \ + .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD} + /* dapm kcontrol types */ #define SOC_DAPM_SINGLE(xname, reg, shift, max, invert) \ { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ @@ -227,6 +234,10 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); +/* event handler for register modifier widget - used by the soc-dapm */ +int dapm_reg_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event); + /* dapm audio endpoint control */ int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, char *pin, int status); @@ -298,6 +309,9 @@ struct snd_soc_dapm_widget { unsigned char shift; /* bits to shift */ unsigned int saved_value; /* widget saved value */ unsigned int value; /* widget current value */ + unsigned int mask; /* non-shifted mask */ + unsigned int on_val; /* on state value */ + unsigned int off_val; /* off state value */ unsigned char power:1; /* block power status */ unsigned char invert:1; /* invert the power bit */ unsigned char active:1; /* active stream on DAC, ADC's */ diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 728f3ac2f30..25363829e60 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -442,6 +442,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget) return con; } +/* + * Handler for generic register modifier widget. + */ +int dapm_reg_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + unsigned int val; + + if (SND_SOC_DAPM_EVENT_ON(event)) + val = w->on_val; + else + val = w->off_val; + + snd_soc_update_bits(w->codec, -(w->reg + 1), + w->mask << w->shift, val << w->shift); + + return 0; +} + /* * Scan each dapm widget for complete audio path. * A complete path is a route that has valid endpoints i.e.:- -- cgit v1.2.3-70-g09d2 From 69776bf3a7263a4e47751f475d540b0f73bf8337 Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Fri, 27 Jun 2008 10:11:58 +0200 Subject: ALSA: Release v1.0.17rc3 Signed-off-by: Jaroslav Kysela --- include/sound/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index 1802cc59333..29a5b227312 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h */ -#define CONFIG_SND_VERSION "1.0.17rc2" +#define CONFIG_SND_VERSION "1.0.17rc3" #define CONFIG_SND_DATE "" -- cgit v1.2.3-70-g09d2 From a5302181e5321664047f75715242aac4e0bbd17c Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 7 Jul 2008 13:35:17 +0100 Subject: ALSA: asoc: core - refactored DAPM pin control API. Refactored snd_soc_dapm_set_endpoint() to snd_soc_dapm_enable_pin() and snd_soc_dapm_disable_pin(). Renamed snd_soc_dapm_sync_endpoints() to snd_soc_dapm_sync(). Renamed snd_soc_dapm_get_endpoint_status() to snd_soc_dapm_get_pin_status(). Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc-dapm.h | 15 ++--- sound/soc/at91/eti_b1_wm8731.c | 10 ++-- sound/soc/codecs/tlv320aic3x.c | 4 +- sound/soc/davinci/davinci-evm.c | 16 ++--- sound/soc/omap/n810.c | 27 ++++++--- sound/soc/pxa/corgi.c | 42 ++++++++------ sound/soc/pxa/poodle.c | 24 ++++---- sound/soc/pxa/spitz.c | 62 ++++++++++---------- sound/soc/pxa/tosa.c | 30 +++++----- sound/soc/s3c24xx/neo1973_wm8753.c | 116 ++++++++++++++++++------------------- sound/soc/sh/sh7760-ac97.c | 2 +- sound/soc/soc-dapm.c | 81 ++++++++++++++++---------- 12 files changed, 230 insertions(+), 199 deletions(-) (limited to 'include') diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h index b2849538cbf..3030fdc6981 100644 --- a/include/sound/soc-dapm.h +++ b/include/sound/soc-dapm.h @@ -234,16 +234,11 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, /* dapm sys fs - used by the core */ int snd_soc_dapm_sys_add(struct device *dev); -/* event handler for register modifier widget - used by the soc-dapm */ -int dapm_reg_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *kcontrol, int event); - -/* dapm audio endpoint control */ -int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, - char *pin, int status); -int snd_soc_dapm_get_endpoint_status(struct snd_soc_codec *codec, - char *pin); -int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec); +/* dapm audio pin control and status */ +int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin); +int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin); +int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin); +int snd_soc_dapm_sync(struct snd_soc_codec *codec); /* dapm widget types */ enum snd_soc_dapm_type { diff --git a/sound/soc/at91/eti_b1_wm8731.c b/sound/soc/at91/eti_b1_wm8731.c index 4a383a4a0ff..ad971e7061f 100644 --- a/sound/soc/at91/eti_b1_wm8731.c +++ b/sound/soc/at91/eti_b1_wm8731.c @@ -216,14 +216,14 @@ static int eti_b1_wm8731_init(struct snd_soc_codec *codec) snd_soc_dapm_add_route(codec, intercon, ARRAY_SIZE(intercon)); /* not connected */ - snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); - snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); + snd_soc_dapm_disable_pin(codec, "RLINEIN"); + snd_soc_dapm_disable_pin(codec, "LLINEIN"); /* always connected */ - snd_soc_dapm_set_endpoint(codec, "Int Mic", 1); - snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); + snd_soc_dapm_enable_pin(codec, "Int Mic"); + snd_soc_dapm_enable_pin(codec, "Ext Spk"); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index d13830623db..954d39b7c04 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -29,7 +29,7 @@ * --------------------------------------- * * Hence the machine layer should disable unsupported inputs/outputs by - * snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0), etc. + * snd_soc_dapm_disable_pin(codec, "MONO_LOUT"), etc. */ #include @@ -206,7 +206,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol, } if (found) - snd_soc_dapm_sync_endpoints(widget->codec); + snd_soc_dapm_sync(widget->codec); } ret = snd_soc_update_bits(widget->codec, reg, val_mask, val); diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index 4c70a0ed339..091eae3a963 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -103,17 +103,17 @@ static int evm_aic3x_init(struct snd_soc_codec *codec) snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); /* not connected */ - snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0); - snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0); - snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0); + snd_soc_dapm_disable_pin(codec, "MONO_LOUT"); + snd_soc_dapm_disable_pin(codec, "HPLCOM"); + snd_soc_dapm_disable_pin(codec, "HPRCOM"); /* always connected */ - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); - snd_soc_dapm_set_endpoint(codec, "Line Out", 1); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); - snd_soc_dapm_set_endpoint(codec, "Line In", 1); + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Line Out"); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(codec, "Line In"); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c index 767b39f339a..74f4599b4d7 100644 --- a/sound/soc/omap/n810.c +++ b/sound/soc/omap/n810.c @@ -50,11 +50,22 @@ static int n810_dmic_func; static void n810_ext_control(struct snd_soc_codec *codec) { - snd_soc_dapm_set_endpoint(codec, "Ext Spk", n810_spk_func); - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", n810_jack_func); - snd_soc_dapm_set_endpoint(codec, "DMic", n810_dmic_func); + if (n810_spk_func) + snd_soc_dapm_enable_pin(codec, "Ext Spk"); + else + snd_soc_dapm_disable_pin(codec, "Ext Spk"); + + if (n810_jack_func) + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + else + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + + if (n810_dmic_func) + snd_soc_dapm_enable_pin(codec, "DMic"); + else + snd_soc_dapm_disable_pin(codec, "DMic); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); } static int n810_startup(struct snd_pcm_substream *substream) @@ -236,9 +247,9 @@ static int n810_aic33_init(struct snd_soc_codec *codec) int i, err; /* Not connected */ - snd_soc_dapm_set_endpoint(codec, "MONO_LOUT", 0); - snd_soc_dapm_set_endpoint(codec, "HPLCOM", 0); - snd_soc_dapm_set_endpoint(codec, "HPRCOM", 0); + snd_soc_dapm_disable_pin(codec, "MONO_LOUT"); + snd_soc_dapm_disable_pin(codec, "HPLCOM"); + snd_soc_dapm_disable_pin(codec, "HPRCOM"); /* Add N810 specific controls */ for (i = 0; i < ARRAY_SIZE(aic33_n810_controls); i++) { @@ -255,7 +266,7 @@ static int n810_aic33_init(struct snd_soc_codec *codec) /* Set up N810 specific audio path audio_map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c index edeea63e80e..db18ef68b69 100644 --- a/sound/soc/pxa/corgi.c +++ b/sound/soc/pxa/corgi.c @@ -50,47 +50,51 @@ static int corgi_spk_func; static void corgi_ext_control(struct snd_soc_codec *codec) { - int spk = 0, mic = 0, line = 0, hp = 0, hs = 0; - /* set up jack connection */ switch (corgi_jack_func) { case CORGI_HP: - hp = 1; /* set = unmute headphone */ set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + snd_soc_dapm_disable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); break; case CORGI_MIC: - mic = 1; /* reset = mute headphone */ reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); break; case CORGI_LINE: - line = 1; reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + snd_soc_dapm_disable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(codec, "Line Jack"); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); break; case CORGI_HEADSET: - hs = 1; - mic = 1; reset_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_L); set_scoop_gpio(&corgiscoop_device.dev, CORGI_SCP_MUTE_R); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Headset Jack"); break; } if (corgi_spk_func == CORGI_SPK_ON) - spk = 1; - - /* set the enpoints to their new connetion states */ - snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", mic); - snd_soc_dapm_set_endpoint(codec, "Line Jack", line); - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); - snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); + snd_soc_dapm_enable_pin(codec, "Ext Spk"); + else + snd_soc_dapm_disable_pin(codec, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); } static int corgi_startup(struct snd_pcm_substream *substream) @@ -285,8 +289,8 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); - snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); + snd_soc_dapm_disable_pin(codec, "LLINEIN"); + snd_soc_dapm_disable_pin(codec, "RLINEIN"); /* Add corgi specific controls */ for (i = 0; i < ARRAY_SIZE(wm8731_corgi_controls); i++) { @@ -303,7 +307,7 @@ static int corgi_wm8731_init(struct snd_soc_codec *codec) /* Set up corgi specific audio path audio_map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c index 810f1fe158a..36cbf69f5f8 100644 --- a/sound/soc/pxa/poodle.c +++ b/sound/soc/pxa/poodle.c @@ -48,8 +48,6 @@ static int poodle_spk_func; static void poodle_ext_control(struct snd_soc_codec *codec) { - int spk = 0; - /* set up jack connection */ if (poodle_jack_func == POODLE_HP) { /* set = unmute headphone */ @@ -57,23 +55,23 @@ static void poodle_ext_control(struct snd_soc_codec *codec) POODLE_LOCOMO_GPIO_MUTE_L, 1); locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_R, 1); - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); } else { locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_L, 0); locomo_gpio_write(&poodle_locomo_device.dev, POODLE_LOCOMO_GPIO_MUTE_R, 0); - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); } - if (poodle_spk_func == POODLE_SPK_ON) - spk = 1; - /* set the enpoints to their new connetion states */ - snd_soc_dapm_set_endpoint(codec, "Ext Spk", spk); + if (poodle_spk_func == POODLE_SPK_ON) + snd_soc_dapm_enable_pin(codec, "Ext Spk"); + else + snd_soc_dapm_disable_pin(codec, "Ext Spk"); /* signal a DAPM event */ - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); } static int poodle_startup(struct snd_pcm_substream *substream) @@ -248,9 +246,9 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_set_endpoint(codec, "LLINEIN", 0); - snd_soc_dapm_set_endpoint(codec, "RLINEIN", 0); - snd_soc_dapm_set_endpoint(codec, "MICIN", 1); + snd_soc_dapm_disable_pin(codec, "LLINEIN"); + snd_soc_dapm_disable_pin(codec, "RLINEIN"); + snd_soc_dapm_enable_pin(codec, "MICIN"); /* Add poodle specific controls */ for (i = 0; i < ARRAY_SIZE(wm8731_poodle_controls); i++) { @@ -267,7 +265,7 @@ static int poodle_wm8731_init(struct snd_soc_codec *codec) /* Set up poodle specific audio path audio_map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c index 092b5c776b4..ec18163fddd 100644 --- a/sound/soc/pxa/spitz.c +++ b/sound/soc/pxa/spitz.c @@ -51,60 +51,60 @@ static int spitz_spk_func; static void spitz_ext_control(struct snd_soc_codec *codec) { if (spitz_spk_func == SPITZ_SPK_ON) - snd_soc_dapm_set_endpoint(codec, "Ext Spk", 1); + snd_soc_dapm_enable_pin(codec, "Ext Spk"); else - snd_soc_dapm_set_endpoint(codec, "Ext Spk", 0); + snd_soc_dapm_disable_pin(codec, "Ext Spk"); /* set up jack connection */ switch (spitz_jack_func) { case SPITZ_HP: /* enable and unmute hp jack, disable mic bias */ - snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 1); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); break; case SPITZ_MIC: /* enable mic jack and bias, mute hp */ - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); break; case SPITZ_LINE: /* enable line jack, disable mic bias and mute hp */ - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Line Jack", 1); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(codec, "Mic Jack"); + snd_soc_dapm_enable_pin(codec, "Line Jack"); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); break; case SPITZ_HEADSET: /* enable and unmute headset jack enable mic bias, mute L hp */ - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", 1); - snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Jack", 1); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); + snd_soc_dapm_enable_pin(codec, "Headset Jack"); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); set_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); break; case SPITZ_HP_OFF: /* jack removed, everything off */ - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Mic Jack", 0); - snd_soc_dapm_set_endpoint(codec, "Line Jack", 0); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); + snd_soc_dapm_disable_pin(codec, "Mic Jack"); + snd_soc_dapm_disable_pin(codec, "Line Jack"); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_L); reset_scoop_gpio(&spitzscoop_device.dev, SPITZ_SCP_MUTE_R); break; } - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); } static int spitz_startup(struct snd_pcm_substream *substream) @@ -291,13 +291,13 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) int i, err; /* NC codec pins */ - snd_soc_dapm_set_endpoint(codec, "RINPUT1", 0); - snd_soc_dapm_set_endpoint(codec, "LINPUT2", 0); - snd_soc_dapm_set_endpoint(codec, "RINPUT2", 0); - snd_soc_dapm_set_endpoint(codec, "LINPUT3", 0); - snd_soc_dapm_set_endpoint(codec, "RINPUT3", 0); - snd_soc_dapm_set_endpoint(codec, "OUT3", 0); - snd_soc_dapm_set_endpoint(codec, "MONO", 0); + snd_soc_dapm_disable_pin(codec, "RINPUT1"); + snd_soc_dapm_disable_pin(codec, "LINPUT2"); + snd_soc_dapm_disable_pin(codec, "RINPUT2"); + snd_soc_dapm_disable_pin(codec, "LINPUT3"); + snd_soc_dapm_disable_pin(codec, "RINPUT3"); + snd_soc_dapm_disable_pin(codec, "OUT3"); + snd_soc_dapm_disable_pin(codec, "MONO"); /* Add spitz specific controls */ for (i = 0; i < ARRAY_SIZE(wm8750_spitz_controls); i++) { @@ -314,7 +314,7 @@ static int spitz_wm8750_init(struct snd_soc_codec *codec) /* Set up spitz specific audio paths */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c index 465ff0f458e..dba7689c508 100644 --- a/sound/soc/pxa/tosa.c +++ b/sound/soc/pxa/tosa.c @@ -52,29 +52,31 @@ static int tosa_spk_func; static void tosa_ext_control(struct snd_soc_codec *codec) { - int spk = 0, mic_int = 0, hp = 0, hs = 0; - /* set up jack connection */ switch (tosa_jack_func) { case TOSA_HP: - hp = 1; + snd_soc_dapm_disable_pin(codec, "Mic (Internal)"); + snd_soc_dapm_enable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); break; case TOSA_MIC_INT: - mic_int = 1; + snd_soc_dapm_enable_pin(codec, "Mic (Internal)"); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_disable_pin(codec, "Headset Jack"); break; case TOSA_HEADSET: - hs = 1; + snd_soc_dapm_disable_pin(codec, "Mic (Internal)"); + snd_soc_dapm_disable_pin(codec, "Headphone Jack"); + snd_soc_dapm_enable_pin(codec, "Headset Jack"); break; } if (tosa_spk_func == TOSA_SPK_ON) - spk = 1; + snd_soc_dapm_enable_pin(codec, "Speaker"); + else + snd_soc_dapm_disable_pin(codec, "Speaker"); - snd_soc_dapm_set_endpoint(codec, "Speaker", spk); - snd_soc_dapm_set_endpoint(codec, "Mic (Internal)", mic_int); - snd_soc_dapm_set_endpoint(codec, "Headphone Jack", hp); - snd_soc_dapm_set_endpoint(codec, "Headset Jack", hs); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); } static int tosa_startup(struct snd_pcm_substream *substream) @@ -191,8 +193,8 @@ static int tosa_ac97_init(struct snd_soc_codec *codec) { int i, err; - snd_soc_dapm_set_endpoint(codec, "OUT3", 0); - snd_soc_dapm_set_endpoint(codec, "MONOOUT", 0); + snd_soc_dapm_disable_pin(codec, "OUT3"); + snd_soc_dapm_disable_pin(codec, "MONOOUT"); /* add tosa specific controls */ for (i = 0; i < ARRAY_SIZE(tosa_controls); i++) { @@ -209,7 +211,7 @@ static int tosa_ac97_init(struct snd_soc_codec *codec) /* set up tosa specific audio path audio_map */ snd_soc_dapm_add_routes(codec, audio_map, ARRAY_SIZE(audio_map)); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/s3c24xx/neo1973_wm8753.c b/sound/soc/s3c24xx/neo1973_wm8753.c index 34851238dea..f053e85ff60 100644 --- a/sound/soc/s3c24xx/neo1973_wm8753.c +++ b/sound/soc/s3c24xx/neo1973_wm8753.c @@ -250,77 +250,77 @@ static int set_scenario_endpoints(struct snd_soc_codec *codec, int scenario) switch (neo1973_scenario) { case NEO_AUDIO_OFF: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_disable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; case NEO_GSM_CALL_AUDIO_HANDSET: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 1); + snd_soc_dapm_enable_pin(codec, "Audio Out"); + snd_soc_dapm_enable_pin(codec, "GSM Line Out"); + snd_soc_dapm_enable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_enable_pin(codec, "Call Mic"); break; case NEO_GSM_CALL_AUDIO_HEADSET: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_enable_pin(codec, "Audio Out"); + snd_soc_dapm_enable_pin(codec, "GSM Line Out"); + snd_soc_dapm_enable_pin(codec, "GSM Line In"); + snd_soc_dapm_enable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; case NEO_GSM_CALL_AUDIO_BLUETOOTH: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 1); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_disable_pin(codec, "Audio Out"); + snd_soc_dapm_enable_pin(codec, "GSM Line Out"); + snd_soc_dapm_enable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; case NEO_STEREO_TO_SPEAKERS: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_enable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; case NEO_STEREO_TO_HEADPHONES: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 1); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_enable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; case NEO_CAPTURE_HANDSET: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 1); + snd_soc_dapm_disable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_enable_pin(codec, "Call Mic"); break; case NEO_CAPTURE_HEADSET: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 1); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_disable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_enable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; case NEO_CAPTURE_BLUETOOTH: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_disable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); break; default: - snd_soc_dapm_set_endpoint(codec, "Audio Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line Out", 0); - snd_soc_dapm_set_endpoint(codec, "GSM Line In", 0); - snd_soc_dapm_set_endpoint(codec, "Headset Mic", 0); - snd_soc_dapm_set_endpoint(codec, "Call Mic", 0); + snd_soc_dapm_disable_pin(codec, "Audio Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line Out"); + snd_soc_dapm_disable_pin(codec, "GSM Line In"); + snd_soc_dapm_disable_pin(codec, "Headset Mic"); + snd_soc_dapm_disable_pin(codec, "Call Mic"); } - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } @@ -511,12 +511,12 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) DBG("Entered %s\n", __func__); /* set up NC codec pins */ - snd_soc_dapm_set_endpoint(codec, "LOUT2", 0); - snd_soc_dapm_set_endpoint(codec, "ROUT2", 0); - snd_soc_dapm_set_endpoint(codec, "OUT3", 0); - snd_soc_dapm_set_endpoint(codec, "OUT4", 0); - snd_soc_dapm_set_endpoint(codec, "LINE1", 0); - snd_soc_dapm_set_endpoint(codec, "LINE2", 0); + snd_soc_dapm_disable_pin(codec, "LOUT2"); + snd_soc_dapm_disable_pin(codec, "ROUT2"); + snd_soc_dapm_disable_pin(codec, "OUT3"); + snd_soc_dapm_disable_pin(codec, "OUT4"); + snd_soc_dapm_disable_pin(codec, "LINE1"); + snd_soc_dapm_disable_pin(codec, "LINE2"); /* set endpoints to default mode */ @@ -539,7 +539,7 @@ static int neo1973_wm8753_init(struct snd_soc_codec *codec) err = snd_soc_dapm_add_routes(codec, dapm_routes, ARRAY_SIZE(dapm_routes)); - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/sh/sh7760-ac97.c b/sound/soc/sh/sh7760-ac97.c index 2f91de84c5c..846d1b3a630 100644 --- a/sound/soc/sh/sh7760-ac97.c +++ b/sound/soc/sh/sh7760-ac97.c @@ -25,7 +25,7 @@ extern struct snd_soc_platform sh7760_soc_platform; static int machine_init(struct snd_soc_codec *codec) { - snd_soc_dapm_sync_endpoints(codec); + snd_soc_dapm_sync(codec); return 0; } diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c index 91cbbefefb0..94296b5dc58 100644 --- a/sound/soc/soc-dapm.c +++ b/sound/soc/soc-dapm.c @@ -880,8 +880,25 @@ static void dapm_free_widgets(struct snd_soc_codec *codec) } } +static int snd_soc_dapm_set_pin(struct snd_soc_codec *codec, + char *pin, int status) +{ + struct snd_soc_dapm_widget *w; + + list_for_each_entry(w, &codec->dapm_widgets, list) { + if (!strcmp(w->name, pin)) { + dbg("dapm: %s: pin %s\n", codec->name, pin); + w->connected = status; + return 0; + } + } + + dbg("dapm: %s: configuring unknown pin %s\n", codec->name, pin); + return -EINVAL; +} + /** - * snd_soc_dapm_sync_endpoints - scan and power dapm paths + * snd_soc_dapm_sync - scan and power dapm paths * @codec: audio codec * * Walks all dapm audio paths and powers widgets according to their @@ -889,11 +906,11 @@ static void dapm_free_widgets(struct snd_soc_codec *codec) * * Returns 0 for success. */ -int snd_soc_dapm_sync_endpoints(struct snd_soc_codec *codec) +int snd_soc_dapm_sync(struct snd_soc_codec *codec) { return dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); } -EXPORT_SYMBOL_GPL(snd_soc_dapm_sync_endpoints); +EXPORT_SYMBOL_GPL(snd_soc_dapm_sync); static int snd_soc_dapm_add_route(struct snd_soc_codec *codec, const char *sink, const char *control, const char *source) @@ -1441,53 +1458,57 @@ int snd_soc_dapm_set_bias_level(struct snd_soc_device *socdev, } /** - * snd_soc_dapm_set_endpoint - set audio endpoint status - * @codec: audio codec - * @endpoint: audio signal endpoint (or start point) - * @status: point status - * - * Set audio endpoint status - connected or disconnected. + * snd_soc_dapm_enable_pin - enable pin. + * @snd_soc_codec: SoC codec + * @pin: pin name * - * Returns 0 for success else error. + * Enables input/output pin and it's parents or children widgets iff there is + * a valid audio route and active audio stream. + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. */ -int snd_soc_dapm_set_endpoint(struct snd_soc_codec *codec, - char *endpoint, int status) +int snd_soc_dapm_enable_pin(struct snd_soc_codec *codec, char *pin) { - struct snd_soc_dapm_widget *w; - - list_for_each_entry(w, &codec->dapm_widgets, list) { - if (!strcmp(w->name, endpoint)) { - w->connected = status; - return 0; - } - } + return snd_soc_dapm_set_pin(codec, pin, 1); +} +EXPORT_SYMBOL_GPL(snd_soc_dapm_enable_pin); - return -ENODEV; +/** + * snd_soc_dapm_disable_pin - disable pin. + * @codec: SoC codec + * @pin: pin name + * + * Disables input/output pin and it's parents or children widgets. + * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to + * do any widget power switching. + */ +int snd_soc_dapm_disable_pin(struct snd_soc_codec *codec, char *pin) +{ + return snd_soc_dapm_set_pin(codec, pin, 0); } -EXPORT_SYMBOL_GPL(snd_soc_dapm_set_endpoint); +EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin); /** - * snd_soc_dapm_get_endpoint_status - get audio endpoint status + * snd_soc_dapm_get_pin_status - get audio pin status * @codec: audio codec - * @endpoint: audio signal endpoint (or start point) + * @pin: audio signal pin endpoint (or start point) * - * Get audio endpoint status - connected or disconnected. + * Get audio pin status - connected or disconnected. * - * Returns status + * Returns 1 for connected otherwise 0. */ -int snd_soc_dapm_get_endpoint_status(struct snd_soc_codec *codec, - char *endpoint) +int snd_soc_dapm_get_pin_status(struct snd_soc_codec *codec, char *pin) { struct snd_soc_dapm_widget *w; list_for_each_entry(w, &codec->dapm_widgets, list) { - if (!strcmp(w->name, endpoint)) + if (!strcmp(w->name, pin)) return w->connected; } return 0; } -EXPORT_SYMBOL_GPL(snd_soc_dapm_get_endpoint_status); +EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_status); /** * snd_soc_dapm_free - free dapm resources -- cgit v1.2.3-70-g09d2 From 3c4b266fe642bcaebe2b95edb56c3f8802924ff9 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Mon, 7 Jul 2008 16:07:17 +0100 Subject: ALSA: asoc: core - merge structs snd_soc_codec_dai and snd_soc_cpu_dai. This patch series merges struct snd_soc_codec_dai and struct snd_soc_cpu_dai into struct snd_soc_dai in preparation for further ASoC v2 patches. This merger removes duplication in both DAI structures and simplifies the API for other users. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 71 +++++++++++++++++----------------------------------- sound/soc/soc-core.c | 50 ++++++++++++++++++------------------ 2 files changed, 48 insertions(+), 73 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 340223a8f24..778e57e74dc 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -221,8 +221,7 @@ struct snd_soc_pcm_stream; struct snd_soc_ops; struct snd_soc_dai_mode; struct snd_soc_pcm_runtime; -struct snd_soc_codec_dai; -struct snd_soc_cpu_dai; +struct snd_soc_dai; struct snd_soc_codec; struct snd_soc_machine_config; struct soc_enum; @@ -317,50 +316,24 @@ struct snd_soc_ops { /* ASoC DAI ops */ struct snd_soc_dai_ops { /* DAI clocking configuration */ - int (*set_sysclk)(struct snd_soc_codec_dai *codec_dai, + int (*set_sysclk)(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir); - int (*set_pll)(struct snd_soc_codec_dai *codec_dai, + int (*set_pll)(struct snd_soc_dai *dai, int pll_id, unsigned int freq_in, unsigned int freq_out); - int (*set_clkdiv)(struct snd_soc_codec_dai *codec_dai, - int div_id, int div); + int (*set_clkdiv)(struct snd_soc_dai *dai, int div_id, int div); /* DAI format configuration */ - int (*set_fmt)(struct snd_soc_codec_dai *codec_dai, - unsigned int fmt); - int (*set_tdm_slot)(struct snd_soc_codec_dai *codec_dai, + int (*set_fmt)(struct snd_soc_dai *dai, unsigned int fmt); + int (*set_tdm_slot)(struct snd_soc_dai *dai, unsigned int mask, int slots); - int (*set_tristate)(struct snd_soc_codec_dai *, int tristate); + int (*set_tristate)(struct snd_soc_dai *dai, int tristate); /* digital mute */ - int (*digital_mute)(struct snd_soc_codec_dai *, int mute); + int (*digital_mute)(struct snd_soc_dai *dai, int mute); }; -/* SoC Codec DAI */ -struct snd_soc_codec_dai { - char *name; - int id; - unsigned char type; - - /* DAI capabilities */ - struct snd_soc_pcm_stream playback; - struct snd_soc_pcm_stream capture; - - /* DAI runtime info */ - struct snd_soc_codec *codec; - unsigned int active; - unsigned char pop_wait:1; - - /* ops */ - struct snd_soc_ops ops; - struct snd_soc_dai_ops dai_ops; - - /* DAI private data */ - void *private_data; -}; - -/* SoC CPU DAI */ -struct snd_soc_cpu_dai { - +/* SoC DAI (Digital Audio Interface) */ +struct snd_soc_dai { /* DAI description */ char *name; unsigned int id; @@ -368,13 +341,13 @@ struct snd_soc_cpu_dai { /* DAI callbacks */ int (*probe)(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai); + struct snd_soc_dai *dai); void (*remove)(struct platform_device *pdev, - struct snd_soc_cpu_dai *dai); + struct snd_soc_dai *dai); int (*suspend)(struct platform_device *pdev, - struct snd_soc_cpu_dai *cpu_dai); + struct snd_soc_dai *dai); int (*resume)(struct platform_device *pdev, - struct snd_soc_cpu_dai *cpu_dai); + struct snd_soc_dai *dai); /* ops */ struct snd_soc_ops ops; @@ -386,7 +359,9 @@ struct snd_soc_cpu_dai { /* DAI runtime info */ struct snd_pcm_runtime *runtime; - unsigned char active:1; + struct snd_soc_codec *codec; + unsigned int active; + unsigned char pop_wait:1; void *dma_data; /* DAI private data */ @@ -428,7 +403,7 @@ struct snd_soc_codec { struct delayed_work delayed_work; /* codec DAI's */ - struct snd_soc_codec_dai *dai; + struct snd_soc_dai *dai; unsigned int num_dai; }; @@ -447,12 +422,12 @@ struct snd_soc_platform { int (*probe)(struct platform_device *pdev); int (*remove)(struct platform_device *pdev); int (*suspend)(struct platform_device *pdev, - struct snd_soc_cpu_dai *cpu_dai); + struct snd_soc_dai *dai); int (*resume)(struct platform_device *pdev, - struct snd_soc_cpu_dai *cpu_dai); + struct snd_soc_dai *dai); /* pcm creation and destruction */ - int (*pcm_new)(struct snd_card *, struct snd_soc_codec_dai *, + int (*pcm_new)(struct snd_card *, struct snd_soc_dai *, struct snd_pcm *); void (*pcm_free)(struct snd_pcm *); @@ -466,8 +441,8 @@ struct snd_soc_dai_link { char *stream_name; /* Stream name */ /* DAI */ - struct snd_soc_codec_dai *codec_dai; - struct snd_soc_cpu_dai *cpu_dai; + struct snd_soc_dai *codec_dai; + struct snd_soc_dai *cpu_dai; /* machine stream operations */ struct snd_soc_ops *ops; diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index bdbbc6a980f..4d626b47b2f 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -134,8 +134,8 @@ static int soc_pcm_open(struct snd_pcm_substream *substream) struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; int ret = 0; mutex_lock(&pcm_mutex); @@ -272,7 +272,7 @@ static void close_delayed_work(struct work_struct *work) struct snd_soc_device *socdev = container_of(work, struct snd_soc_device, delayed_work.work); struct snd_soc_codec *codec = socdev->codec; - struct snd_soc_codec_dai *codec_dai; + struct snd_soc_dai *codec_dai; int i; mutex_lock(&pcm_mutex); @@ -323,8 +323,8 @@ static int soc_codec_close(struct snd_pcm_substream *substream) struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; struct snd_soc_codec *codec = socdev->codec; mutex_lock(&pcm_mutex); @@ -384,8 +384,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; struct snd_soc_codec *codec = socdev->codec; int ret = 0; @@ -489,8 +489,8 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; int ret = 0; mutex_lock(&pcm_mutex); @@ -559,8 +559,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; struct snd_soc_codec *codec = socdev->codec; mutex_lock(&pcm_mutex); @@ -594,8 +594,8 @@ static int soc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) struct snd_soc_device *socdev = rtd->socdev; struct snd_soc_dai_link *machine = rtd->dai; struct snd_soc_platform *platform = socdev->platform; - struct snd_soc_cpu_dai *cpu_dai = machine->cpu_dai; - struct snd_soc_codec_dai *codec_dai = machine->codec_dai; + struct snd_soc_dai *cpu_dai = machine->cpu_dai; + struct snd_soc_dai *codec_dai = machine->codec_dai; int ret; if (codec_dai->ops.trigger) { @@ -651,7 +651,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) /* mute any active DAC's */ for (i = 0; i < machine->num_links; i++) { - struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; + struct snd_soc_dai *dai = machine->dai_link[i].codec_dai; if (dai->dai_ops.digital_mute && dai->playback.active) dai->dai_ops.digital_mute(dai, 1); } @@ -664,7 +664,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) machine->suspend_pre(pdev, state); for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->suspend && cpu_dai->type != SND_SOC_DAI_AC97) cpu_dai->suspend(pdev, cpu_dai); if (platform->suspend) @@ -690,7 +690,7 @@ static int soc_suspend(struct platform_device *pdev, pm_message_t state) codec_dev->suspend(pdev, state); for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->suspend && cpu_dai->type == SND_SOC_DAI_AC97) cpu_dai->suspend(pdev, cpu_dai); } @@ -726,7 +726,7 @@ static void soc_resume_deferred(struct work_struct *work) machine->resume_pre(pdev); for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->resume && cpu_dai->type == SND_SOC_DAI_AC97) cpu_dai->resume(pdev, cpu_dai); } @@ -747,13 +747,13 @@ static void soc_resume_deferred(struct work_struct *work) /* unmute any active DACs */ for (i = 0; i < machine->num_links; i++) { - struct snd_soc_codec_dai *dai = machine->dai_link[i].codec_dai; + struct snd_soc_dai *dai = machine->dai_link[i].codec_dai; if (dai->dai_ops.digital_mute && dai->playback.active) dai->dai_ops.digital_mute(dai, 0); } for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->resume && cpu_dai->type != SND_SOC_DAI_AC97) cpu_dai->resume(pdev, cpu_dai); if (platform->resume) @@ -803,7 +803,7 @@ static int soc_probe(struct platform_device *pdev) } for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->probe) { ret = cpu_dai->probe(pdev, cpu_dai); if (ret < 0) @@ -838,7 +838,7 @@ platform_err: cpu_dai_err: for (i--; i >= 0; i--) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->remove) cpu_dai->remove(pdev, cpu_dai); } @@ -867,7 +867,7 @@ static int soc_remove(struct platform_device *pdev) codec_dev->remove(pdev); for (i = 0; i < machine->num_links; i++) { - struct snd_soc_cpu_dai *cpu_dai = machine->dai_link[i].cpu_dai; + struct snd_soc_dai *cpu_dai = machine->dai_link[i].cpu_dai; if (cpu_dai->remove) cpu_dai->remove(pdev, cpu_dai); } @@ -895,8 +895,8 @@ static int soc_new_pcm(struct snd_soc_device *socdev, struct snd_soc_dai_link *dai_link, int num) { struct snd_soc_codec *codec = socdev->codec; - struct snd_soc_codec_dai *codec_dai = dai_link->codec_dai; - struct snd_soc_cpu_dai *cpu_dai = dai_link->cpu_dai; + struct snd_soc_dai *codec_dai = dai_link->codec_dai; + struct snd_soc_dai *cpu_dai = dai_link->cpu_dai; struct snd_soc_pcm_runtime *rtd; struct snd_pcm *pcm; char new_name[64]; @@ -1211,7 +1211,7 @@ void snd_soc_free_pcms(struct snd_soc_device *socdev) { struct snd_soc_codec *codec = socdev->codec; #ifdef CONFIG_SND_SOC_AC97_BUS - struct snd_soc_codec_dai *codec_dai; + struct snd_soc_dai *codec_dai; int i; #endif -- cgit v1.2.3-70-g09d2 From 8c6529dbf881303920a415c2d14a500218661949 Mon Sep 17 00:00:00 2001 From: Liam Girdwood Date: Tue, 8 Jul 2008 13:19:13 +0100 Subject: ALSA: asoc: core - add Digital Audio Interface (DAI) control functions. This patch adds several functions for DAI control and config and replaces the current method of calling function pointers within the DAI struct. Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/soc.h | 21 ++++++++ sound/soc/soc-core.c | 140 ++++++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 153 insertions(+), 8 deletions(-) (limited to 'include') diff --git a/include/sound/soc.h b/include/sound/soc.h index 778e57e74dc..1890d87c520 100644 --- a/include/sound/soc.h +++ b/include/sound/soc.h @@ -256,6 +256,27 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec, struct snd_ac97_bus_ops *ops, int num); void snd_soc_free_ac97_codec(struct snd_soc_codec *codec); +/* Digital Audio Interface clocking API.*/ +int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir); + +int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, + int div_id, int div); + +int snd_soc_dai_set_pll(struct snd_soc_dai *dai, + int pll_id, unsigned int freq_in, unsigned int freq_out); + +/* Digital Audio interface formatting */ +int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt); + +int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int mask, int slots); + +int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate); + +/* Digital Audio Interface mute */ +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute); + /* *Controls */ diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c index 4d626b47b2f..83f1190293a 100644 --- a/sound/soc/soc-core.c +++ b/sound/soc/soc-core.c @@ -434,8 +434,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) else { codec_dai->pop_wait = 0; cancel_delayed_work(&socdev->delayed_work); - if (codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 0); + snd_soc_dai_digital_mute(codec_dai, 0); } } else { /* no delayed work - do we need to power up codec */ @@ -454,8 +453,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) SND_SOC_DAPM_STREAM_START); snd_soc_dapm_set_bias_level(socdev, SND_SOC_BIAS_ON); - if (codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 0); + snd_soc_dai_digital_mute(codec_dai, 0); } else { /* codec already powered - power on widgets */ @@ -467,8 +465,8 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream) snd_soc_dapm_stream_event(codec, codec_dai->capture.stream_name, SND_SOC_DAPM_STREAM_START); - if (codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 0); + + snd_soc_dai_digital_mute(codec_dai, 0); } } @@ -566,8 +564,8 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream) mutex_lock(&pcm_mutex); /* apply codec digital mute */ - if (!codec->active && codec_dai->dai_ops.digital_mute) - codec_dai->dai_ops.digital_mute(codec_dai, 1); + if (!codec->active) + snd_soc_dai_digital_mute(codec_dai, 1); /* free any machine hw params */ if (machine->ops && machine->ops->hw_free) @@ -1703,6 +1701,132 @@ int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol, } EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8); +/** + * snd_soc_dai_set_sysclk - configure DAI system or master clock. + * @dai: DAI + * @clk_id: DAI specific clock ID + * @freq: new clock frequency in Hz + * @dir: new clock direction - input/output. + * + * Configures the DAI master (MCLK) or system (SYSCLK) clocking. + */ +int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + if (dai->dai_ops.set_sysclk) + return dai->dai_ops.set_sysclk(dai, clk_id, freq, dir); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk); + +/** + * snd_soc_dai_set_clkdiv - configure DAI clock dividers. + * @dai: DAI + * @clk_id: DAI specific clock divider ID + * @div: new clock divisor. + * + * Configures the clock dividers. This is used to derive the best DAI bit and + * frame clocks from the system or master clock. It's best to set the DAI bit + * and frame clocks as low as possible to save system power. + */ +int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai, + int div_id, int div) +{ + if (dai->dai_ops.set_clkdiv) + return dai->dai_ops.set_clkdiv(dai, div_id, div); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv); + +/** + * snd_soc_dai_set_pll - configure DAI PLL. + * @dai: DAI + * @pll_id: DAI specific PLL ID + * @freq_in: PLL input clock frequency in Hz + * @freq_out: requested PLL output clock frequency in Hz + * + * Configures and enables PLL to generate output clock based on input clock. + */ +int snd_soc_dai_set_pll(struct snd_soc_dai *dai, + int pll_id, unsigned int freq_in, unsigned int freq_out) +{ + if (dai->dai_ops.set_pll) + return dai->dai_ops.set_pll(dai, pll_id, freq_in, freq_out); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll); + +/** + * snd_soc_dai_set_fmt - configure DAI hardware audio format. + * @dai: DAI + * @clk_id: DAI specific clock ID + * @fmt: SND_SOC_DAIFMT_ format value. + * + * Configures the DAI hardware format and clocking. + */ +int snd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + if (dai->dai_ops.set_fmt) + return dai->dai_ops.set_fmt(dai, fmt); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_fmt); + +/** + * snd_soc_dai_set_tdm_slot - configure DAI TDM. + * @dai: DAI + * @mask: DAI specific mask representing used slots. + * @slots: Number of slots in use. + * + * Configures a DAI for TDM operation. Both mask and slots are codec and DAI + * specific. + */ +int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int mask, int slots) +{ + if (dai->dai_ops.set_sysclk) + return dai->dai_ops.set_tdm_slot(dai, mask, slots); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_tdm_slot); + +/** + * snd_soc_dai_set_tristate - configure DAI system or master clock. + * @dai: DAI + * @tristate: tristate enable + * + * Tristates the DAI so that others can use it. + */ +int snd_soc_dai_set_tristate(struct snd_soc_dai *dai, int tristate) +{ + if (dai->dai_ops.set_sysclk) + return dai->dai_ops.set_tristate(dai, tristate); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate); + +/** + * snd_soc_dai_digital_mute - configure DAI system or master clock. + * @dai: DAI + * @mute: mute enable + * + * Mutes the DAI DAC. + */ +int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute) +{ + if (dai->dai_ops.digital_mute) + return dai->dai_ops.digital_mute(dai, mute); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_soc_dai_digital_mute); + static int __devinit snd_soc_init(void) { printk(KERN_INFO "ASoC version %s\n", SND_SOC_VERSION); -- cgit v1.2.3-70-g09d2 From ee2da99782d717b264d5c309a2084ca85e9a64ff Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 9 Jul 2008 10:28:41 +0200 Subject: ALSA: remove CONFIG_KMOD from sound A bunch of things in alsa depend on CONFIG_KMOD, use CONFIG_MODULES instead where the dependency is needed at all. Signed-off-by: Johannes Berg Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/seq_kernel.h | 2 +- sound/core/seq/seq_clientmgr.c | 2 +- sound/core/seq/seq_device.c | 6 ++---- sound/core/sound.c | 8 ++++---- sound/core/timer.c | 6 +++--- sound/ppc/daca.c | 2 -- sound/ppc/tumbler.c | 2 -- 7 files changed, 11 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/sound/seq_kernel.h b/include/sound/seq_kernel.h index f023c1b97f8..3d9afb6a8c9 100644 --- a/include/sound/seq_kernel.h +++ b/include/sound/seq_kernel.h @@ -105,7 +105,7 @@ int snd_seq_event_port_attach(int client, struct snd_seq_port_callback *pcbp, int cap, int type, int midi_channels, int midi_voices, char *portname); int snd_seq_event_port_detach(int client, int port); -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES void snd_seq_autoload_lock(void); void snd_seq_autoload_unlock(void); #else diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c index 47cfa5186e3..7a1545d2d95 100644 --- a/sound/core/seq/seq_clientmgr.c +++ b/sound/core/seq/seq_clientmgr.c @@ -148,7 +148,7 @@ struct snd_seq_client *snd_seq_client_use_ptr(int clientid) return NULL; } spin_unlock_irqrestore(&clients_lock, flags); -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES if (!in_interrupt()) { static char client_requested[SNDRV_SEQ_GLOBAL_CLIENTS]; static char card_requested[SNDRV_CARDS]; diff --git a/sound/core/seq/seq_device.c b/sound/core/seq/seq_device.c index 2f00ad28a2b..05410e536a4 100644 --- a/sound/core/seq/seq_device.c +++ b/sound/core/seq/seq_device.c @@ -124,7 +124,7 @@ static void snd_seq_device_info(struct snd_info_entry *entry, * load all registered drivers (called from seq_clientmgr.c) */ -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /* avoid auto-loading during module_init() */ static int snd_seq_in_init; void snd_seq_autoload_lock(void) @@ -140,7 +140,7 @@ void snd_seq_autoload_unlock(void) void snd_seq_device_load_drivers(void) { -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES struct ops_list *ops; /* Calling request_module during module_init() @@ -566,7 +566,5 @@ EXPORT_SYMBOL(snd_seq_device_load_drivers); EXPORT_SYMBOL(snd_seq_device_new); EXPORT_SYMBOL(snd_seq_device_register_driver); EXPORT_SYMBOL(snd_seq_device_unregister_driver); -#ifdef CONFIG_KMOD EXPORT_SYMBOL(snd_seq_autoload_lock); EXPORT_SYMBOL(snd_seq_autoload_unlock); -#endif diff --git a/sound/core/sound.c b/sound/core/sound.c index 6c8ab48c689..09a94953745 100644 --- a/sound/core/sound.c +++ b/sound/core/sound.c @@ -60,14 +60,14 @@ EXPORT_SYMBOL(snd_ecards_limit); static struct snd_minor *snd_minors[SNDRV_OS_MINORS]; static DEFINE_MUTEX(sound_mutex); -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES /** * snd_request_card - try to load the card module * @card: the card number * * Tries to load the module "snd-card-X" for the given card number - * via KMOD. Returns immediately if already loaded. + * via request_module. Returns immediately if already loaded. */ void snd_request_card(int card) { @@ -92,7 +92,7 @@ static void snd_request_other(int minor) request_module(str); } -#endif /* request_module support */ +#endif /* modular kernel */ /** * snd_lookup_minor_data - get user data of a registered device @@ -132,7 +132,7 @@ static int snd_open(struct inode *inode, struct file *file) return -ENODEV; mptr = snd_minors[minor]; if (mptr == NULL) { -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES int dev = SNDRV_MINOR_DEVICE(minor); if (dev == SNDRV_MINOR_CONTROL) { /* /dev/aloadC? */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 9d8184a2c2d..0af337efc64 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -146,7 +146,7 @@ static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) return NULL; } -#ifdef CONFIG_KMOD +#ifdef CONFIG_MODULES static void snd_timer_request(struct snd_timer_id *tid) { @@ -259,8 +259,8 @@ int snd_timer_open(struct snd_timer_instance **ti, /* open a master instance */ mutex_lock(®ister_mutex); timer = snd_timer_find(tid); -#ifdef CONFIG_KMOD - if (timer == NULL) { +#ifdef CONFIG_MODULES + if (!timer) { mutex_unlock(®ister_mutex); snd_timer_request(tid); mutex_lock(®ister_mutex); diff --git a/sound/ppc/daca.c b/sound/ppc/daca.c index ca9452901a5..8a5b2903193 100644 --- a/sound/ppc/daca.c +++ b/sound/ppc/daca.c @@ -249,9 +249,7 @@ int __init snd_pmac_daca_init(struct snd_pmac *chip) int i, err; struct pmac_daca *mix; -#ifdef CONFIG_KMOD request_module("i2c-powermac"); -#endif /* CONFIG_KMOD */ mix = kzalloc(sizeof(*mix), GFP_KERNEL); if (! mix) diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c index 3f8d7164cef..009df8dd37a 100644 --- a/sound/ppc/tumbler.c +++ b/sound/ppc/tumbler.c @@ -1350,9 +1350,7 @@ int __init snd_pmac_tumbler_init(struct snd_pmac *chip) struct device_node *tas_node, *np; char *chipname; -#ifdef CONFIG_KMOD request_module("i2c-powermac"); -#endif /* CONFIG_KMOD */ mix = kzalloc(sizeof(*mix), GFP_KERNEL); if (! mix) -- cgit v1.2.3-70-g09d2 From 4a161d235b68eb7234f40106560c488a1bdb3851 Mon Sep 17 00:00:00 2001 From: Manuel Lauss Date: Wed, 9 Jul 2008 16:27:56 +0200 Subject: ALSA: ASoC: Au12x0/Au1550 PSC Audio support Audio for Au12x0/Au1550 PSCs in AC97 and I2S mode, for ASoC v1 framework. - DBDMA, AC97 and I2S drivers - sample AC97 machine code (Db1200) Signed-off-by: Manuel Lauss Signed-off-by: Liam Girdwood Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/asm-mips/mach-au1x00/au1xxx_psc.h | 8 + sound/soc/Kconfig | 1 + sound/soc/Makefile | 2 +- sound/soc/au1x/Kconfig | 32 +++ sound/soc/au1x/Makefile | 13 + sound/soc/au1x/dbdma2.c | 421 ++++++++++++++++++++++++++++++ sound/soc/au1x/psc-ac97.c | 387 +++++++++++++++++++++++++++ sound/soc/au1x/psc-i2s.c | 414 +++++++++++++++++++++++++++++ sound/soc/au1x/psc.h | 53 ++++ sound/soc/au1x/sample-ac97.c | 144 ++++++++++ 10 files changed, 1474 insertions(+), 1 deletion(-) create mode 100644 sound/soc/au1x/Kconfig create mode 100644 sound/soc/au1x/Makefile create mode 100644 sound/soc/au1x/dbdma2.c create mode 100644 sound/soc/au1x/psc-ac97.c create mode 100644 sound/soc/au1x/psc-i2s.c create mode 100644 sound/soc/au1x/psc.h create mode 100644 sound/soc/au1x/sample-ac97.c (limited to 'include') diff --git a/include/asm-mips/mach-au1x00/au1xxx_psc.h b/include/asm-mips/mach-au1x00/au1xxx_psc.h index dae4eca2417..892b7f168eb 100644 --- a/include/asm-mips/mach-au1x00/au1xxx_psc.h +++ b/include/asm-mips/mach-au1x00/au1xxx_psc.h @@ -204,6 +204,14 @@ typedef struct psc_i2s { u32 psc_i2sudf; } psc_i2s_t; +#define PSC_I2SCFG_OFFSET 0x08 +#define PSC_I2SMASK_OFFSET 0x0C +#define PSC_I2SPCR_OFFSET 0x10 +#define PSC_I2SSTAT_OFFSET 0x14 +#define PSC_I2SEVENT_OFFSET 0x18 +#define PSC_I2SRXTX_OFFSET 0x1C +#define PSC_I2SUDF_OFFSET 0x20 + /* I2S Config Register. */ #define PSC_I2SCFG_RT_MASK (3 << 30) #define PSC_I2SCFG_RT_FIFO1 (0 << 30) diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index b939e22db7b..f743530add8 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -24,6 +24,7 @@ config SND_SOC_AC97_BUS # All the supported Soc's source "sound/soc/at32/Kconfig" source "sound/soc/at91/Kconfig" +source "sound/soc/au1x/Kconfig" source "sound/soc/pxa/Kconfig" source "sound/soc/s3c24xx/Kconfig" source "sound/soc/sh/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index 3645f959c26..933a66d3080 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -2,4 +2,4 @@ snd-soc-core-objs := soc-core.o soc-dapm.o obj-$(CONFIG_SND_SOC) += snd-soc-core.o obj-$(CONFIG_SND_SOC) += codecs/ at32/ at91/ pxa/ s3c24xx/ sh/ fsl/ davinci/ -obj-$(CONFIG_SND_SOC) += omap/ +obj-$(CONFIG_SND_SOC) += omap/ au1x/ diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig new file mode 100644 index 00000000000..410a893aa66 --- /dev/null +++ b/sound/soc/au1x/Kconfig @@ -0,0 +1,32 @@ +## +## Au1200/Au1550 PSC + DBDMA +## +config SND_SOC_AU1XPSC + tristate "SoC Audio for Au1200/Au1250/Au1550" + depends on SOC_AU1200 || SOC_AU1550 + help + This option enables support for the Programmable Serial + Controllers in AC97 and I2S mode, and the Descriptor-Based DMA + Controller (DBDMA) as found on the Au1200/Au1250/Au1550 SoC. + +config SND_SOC_AU1XPSC_I2S + tristate + +config SND_SOC_AU1XPSC_AC97 + tristate + select AC97_BUS + select SND_AC97_CODEC + select SND_SOC_AC97_BUS + + +## +## Boards +## +config SND_SOC_SAMPLE_PSC_AC97 + tristate "Sample Au12x0/Au1550 PSC AC97 sound machine" + depends on SND_SOC_AU1XPSC + select SND_SOC_AU1XPSC_AC97 + select SND_SOC_AC97_CODEC + help + This is a sample AC97 sound machine for use in Au12x0/Au1550 + based systems which have audio on PSC1 (e.g. Db1200 demoboard). diff --git a/sound/soc/au1x/Makefile b/sound/soc/au1x/Makefile new file mode 100644 index 00000000000..6c6950b8003 --- /dev/null +++ b/sound/soc/au1x/Makefile @@ -0,0 +1,13 @@ +# Au1200/Au1550 PSC audio +snd-soc-au1xpsc-dbdma-objs := dbdma2.o +snd-soc-au1xpsc-i2s-objs := psc-i2s.o +snd-soc-au1xpsc-ac97-objs := psc-ac97.o + +obj-$(CONFIG_SND_SOC_AU1XPSC) += snd-soc-au1xpsc-dbdma.o +obj-$(CONFIG_SND_SOC_AU1XPSC_I2S) += snd-soc-au1xpsc-i2s.o +obj-$(CONFIG_SND_SOC_AU1XPSC_AC97) += snd-soc-au1xpsc-ac97.o + +# Boards +snd-soc-sample-ac97-objs := sample-ac97.o + +obj-$(CONFIG_SND_SOC_SAMPLE_PSC_AC97) += snd-soc-sample-ac97.o diff --git a/sound/soc/au1x/dbdma2.c b/sound/soc/au1x/dbdma2.c new file mode 100644 index 00000000000..1466d932880 --- /dev/null +++ b/sound/soc/au1x/dbdma2.c @@ -0,0 +1,421 @@ +/* + * Au12x0/Au1550 PSC ALSA ASoC audio support. + * + * (c) 2007-2008 MSC Vertriebsges.m.b.H., + * Manuel Lauss + * + * 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. + * + * DMA glue for Au1x-PSC audio. + * + * NOTE: all of these drivers can only work with a SINGLE instance + * of a PSC. Multiple independent audio devices are impossible + * with ASoC v1. + */ + + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "psc.h" + +/*#define PCM_DEBUG*/ + +#define MSG(x...) printk(KERN_INFO "au1xpsc_pcm: " x) +#ifdef PCM_DEBUG +#define DBG MSG +#else +#define DBG(x...) do {} while (0) +#endif + +struct au1xpsc_audio_dmadata { + /* DDMA control data */ + unsigned int ddma_id; /* DDMA direction ID for this PSC */ + u32 ddma_chan; /* DDMA context */ + + /* PCM context (for irq handlers) */ + struct snd_pcm_substream *substream; + unsigned long curr_period; /* current segment DDMA is working on */ + unsigned long q_period; /* queue period(s) */ + unsigned long dma_area; /* address of queued DMA area */ + unsigned long dma_area_s; /* start address of DMA area */ + unsigned long pos; /* current byte position being played */ + unsigned long periods; /* number of SG segments in total */ + unsigned long period_bytes; /* size in bytes of one SG segment */ + + /* runtime data */ + int msbits; +}; + +/* instance data. There can be only one, MacLeod!!!! */ +static struct au1xpsc_audio_dmadata *au1xpsc_audio_pcmdma[2]; + +/* + * These settings are somewhat okay, at least on my machine audio plays + * almost skip-free. Especially the 64kB buffer seems to help a LOT. + */ +#define AU1XPSC_PERIOD_MIN_BYTES 1024 +#define AU1XPSC_BUFFER_MIN_BYTES 65536 + +#define AU1XPSC_PCM_FMTS \ + (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | \ + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | \ + SNDRV_PCM_FMTBIT_U32_LE | SNDRV_PCM_FMTBIT_U32_BE | \ + 0) + +/* PCM hardware DMA capabilities - platform specific */ +static const struct snd_pcm_hardware au1xpsc_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED, + .formats = AU1XPSC_PCM_FMTS, + .period_bytes_min = AU1XPSC_PERIOD_MIN_BYTES, + .period_bytes_max = 4096 * 1024 - 1, + .periods_min = 2, + .periods_max = 4096, /* 2 to as-much-as-you-like */ + .buffer_bytes_max = 4096 * 1024 - 1, + .fifo_size = 16, /* fifo entries of AC97/I2S PSC */ +}; + +static void au1x_pcm_queue_tx(struct au1xpsc_audio_dmadata *cd) +{ + au1xxx_dbdma_put_source_flags(cd->ddma_chan, + (void *)phys_to_virt(cd->dma_area), + cd->period_bytes, DDMA_FLAGS_IE); + + /* update next-to-queue period */ + ++cd->q_period; + cd->dma_area += cd->period_bytes; + if (cd->q_period >= cd->periods) { + cd->q_period = 0; + cd->dma_area = cd->dma_area_s; + } +} + +static void au1x_pcm_queue_rx(struct au1xpsc_audio_dmadata *cd) +{ + au1xxx_dbdma_put_dest_flags(cd->ddma_chan, + (void *)phys_to_virt(cd->dma_area), + cd->period_bytes, DDMA_FLAGS_IE); + + /* update next-to-queue period */ + ++cd->q_period; + cd->dma_area += cd->period_bytes; + if (cd->q_period >= cd->periods) { + cd->q_period = 0; + cd->dma_area = cd->dma_area_s; + } +} + +static void au1x_pcm_dmatx_cb(int irq, void *dev_id) +{ + struct au1xpsc_audio_dmadata *cd = dev_id; + + cd->pos += cd->period_bytes; + if (++cd->curr_period >= cd->periods) { + cd->pos = 0; + cd->curr_period = 0; + } + snd_pcm_period_elapsed(cd->substream); + au1x_pcm_queue_tx(cd); +} + +static void au1x_pcm_dmarx_cb(int irq, void *dev_id) +{ + struct au1xpsc_audio_dmadata *cd = dev_id; + + cd->pos += cd->period_bytes; + if (++cd->curr_period >= cd->periods) { + cd->pos = 0; + cd->curr_period = 0; + } + snd_pcm_period_elapsed(cd->substream); + au1x_pcm_queue_rx(cd); +} + +static void au1x_pcm_dbdma_free(struct au1xpsc_audio_dmadata *pcd) +{ + if (pcd->ddma_chan) { + au1xxx_dbdma_stop(pcd->ddma_chan); + au1xxx_dbdma_reset(pcd->ddma_chan); + au1xxx_dbdma_chan_free(pcd->ddma_chan); + pcd->ddma_chan = 0; + pcd->msbits = 0; + } +} + +/* in case of missing DMA ring or changed TX-source / RX-dest bit widths, + * allocate (or reallocate) a 2-descriptor DMA ring with bit depth according + * to ALSA-supplied sample depth. This is due to limitations in the dbdma api + * (cannot adjust source/dest widths of already allocated descriptor ring). + */ +static int au1x_pcm_dbdma_realloc(struct au1xpsc_audio_dmadata *pcd, + int stype, int msbits) +{ + /* DMA only in 8/16/32 bit widths */ + if (msbits == 24) + msbits = 32; + + /* check current config: correct bits and descriptors allocated? */ + if ((pcd->ddma_chan) && (msbits == pcd->msbits)) + goto out; /* all ok! */ + + au1x_pcm_dbdma_free(pcd); + + if (stype == PCM_RX) + pcd->ddma_chan = au1xxx_dbdma_chan_alloc(pcd->ddma_id, + DSCR_CMD0_ALWAYS, + au1x_pcm_dmarx_cb, (void *)pcd); + else + pcd->ddma_chan = au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS, + pcd->ddma_id, + au1x_pcm_dmatx_cb, (void *)pcd); + + if (!pcd->ddma_chan) + return -ENOMEM;; + + au1xxx_dbdma_set_devwidth(pcd->ddma_chan, msbits); + au1xxx_dbdma_ring_alloc(pcd->ddma_chan, 2); + + pcd->msbits = msbits; + + au1xxx_dbdma_stop(pcd->ddma_chan); + au1xxx_dbdma_reset(pcd->ddma_chan); + +out: + return 0; +} + +static int au1xpsc_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct au1xpsc_audio_dmadata *pcd; + int stype, ret; + + ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); + if (ret < 0) + goto out; + + stype = SUBSTREAM_TYPE(substream); + pcd = au1xpsc_audio_pcmdma[stype]; + + DBG("runtime->dma_area = 0x%08lx dma_addr_t = 0x%08lx dma_size = %d " + "runtime->min_align %d\n", + (unsigned long)runtime->dma_area, + (unsigned long)runtime->dma_addr, runtime->dma_bytes, + runtime->min_align); + + DBG("bits %d frags %d frag_bytes %d is_rx %d\n", params->msbits, + params_periods(params), params_period_bytes(params), stype); + + ret = au1x_pcm_dbdma_realloc(pcd, stype, params->msbits); + if (ret) { + MSG("DDMA channel (re)alloc failed!\n"); + goto out; + } + + pcd->substream = substream; + pcd->period_bytes = params_period_bytes(params); + pcd->periods = params_periods(params); + pcd->dma_area_s = pcd->dma_area = (unsigned long)runtime->dma_addr; + pcd->q_period = 0; + pcd->curr_period = 0; + pcd->pos = 0; + + ret = 0; +out: + return ret; +} + +static int au1xpsc_pcm_hw_free(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_free_pages(substream); + return 0; +} + +static int au1xpsc_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct au1xpsc_audio_dmadata *pcd = + au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]; + + au1xxx_dbdma_reset(pcd->ddma_chan); + + if (SUBSTREAM_TYPE(substream) == PCM_RX) { + au1x_pcm_queue_rx(pcd); + au1x_pcm_queue_rx(pcd); + } else { + au1x_pcm_queue_tx(pcd); + au1x_pcm_queue_tx(pcd); + } + + return 0; +} + +static int au1xpsc_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + u32 c = au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->ddma_chan; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + au1xxx_dbdma_start(c); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + au1xxx_dbdma_stop(c); + break; + default: + return -EINVAL; + } + return 0; +} + +static snd_pcm_uframes_t +au1xpsc_pcm_pointer(struct snd_pcm_substream *substream) +{ + return bytes_to_frames(substream->runtime, + au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]->pos); +} + +static int au1xpsc_pcm_open(struct snd_pcm_substream *substream) +{ + snd_soc_set_runtime_hwparams(substream, &au1xpsc_pcm_hardware); + return 0; +} + +static int au1xpsc_pcm_close(struct snd_pcm_substream *substream) +{ + au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[SUBSTREAM_TYPE(substream)]); + return 0; +} + +struct snd_pcm_ops au1xpsc_pcm_ops = { + .open = au1xpsc_pcm_open, + .close = au1xpsc_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = au1xpsc_pcm_hw_params, + .hw_free = au1xpsc_pcm_hw_free, + .prepare = au1xpsc_pcm_prepare, + .trigger = au1xpsc_pcm_trigger, + .pointer = au1xpsc_pcm_pointer, +}; + +static void au1xpsc_pcm_free_dma_buffers(struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_free_for_all(pcm); +} + +static int au1xpsc_pcm_new(struct snd_card *card, + struct snd_soc_dai *dai, + struct snd_pcm *pcm) +{ + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + card->dev, AU1XPSC_BUFFER_MIN_BYTES, (4096 * 1024) - 1); + + return 0; +} + +static int au1xpsc_pcm_probe(struct platform_device *pdev) +{ + struct resource *r; + int ret; + + if (au1xpsc_audio_pcmdma[PCM_TX] || au1xpsc_audio_pcmdma[PCM_RX]) + return -EBUSY; + + /* TX DMA */ + au1xpsc_audio_pcmdma[PCM_TX] + = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL); + if (!au1xpsc_audio_pcmdma[PCM_TX]) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 0); + if (!r) { + ret = -ENODEV; + goto out1; + } + (au1xpsc_audio_pcmdma[PCM_TX])->ddma_id = r->start; + + /* RX DMA */ + au1xpsc_audio_pcmdma[PCM_RX] + = kzalloc(sizeof(struct au1xpsc_audio_dmadata), GFP_KERNEL); + if (!au1xpsc_audio_pcmdma[PCM_RX]) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_DMA, 1); + if (!r) { + ret = -ENODEV; + goto out2; + } + (au1xpsc_audio_pcmdma[PCM_RX])->ddma_id = r->start; + + return 0; + +out2: + kfree(au1xpsc_audio_pcmdma[PCM_RX]); + au1xpsc_audio_pcmdma[PCM_RX] = NULL; +out1: + kfree(au1xpsc_audio_pcmdma[PCM_TX]); + au1xpsc_audio_pcmdma[PCM_TX] = NULL; + return ret; +} + +static int au1xpsc_pcm_remove(struct platform_device *pdev) +{ + int i; + + for (i = 0; i < 2; i++) { + if (au1xpsc_audio_pcmdma[i]) { + au1x_pcm_dbdma_free(au1xpsc_audio_pcmdma[i]); + kfree(au1xpsc_audio_pcmdma[i]); + au1xpsc_audio_pcmdma[i] = NULL; + } + } + + return 0; +} + +/* au1xpsc audio platform */ +struct snd_soc_platform au1xpsc_soc_platform = { + .name = "au1xpsc-pcm-dbdma", + .probe = au1xpsc_pcm_probe, + .remove = au1xpsc_pcm_remove, + .pcm_ops = &au1xpsc_pcm_ops, + .pcm_new = au1xpsc_pcm_new, + .pcm_free = au1xpsc_pcm_free_dma_buffers, +}; +EXPORT_SYMBOL_GPL(au1xpsc_soc_platform); + +static int __init au1xpsc_audio_dbdma_init(void) +{ + au1xpsc_audio_pcmdma[PCM_TX] = NULL; + au1xpsc_audio_pcmdma[PCM_RX] = NULL; + return 0; +} + +static void __exit au1xpsc_audio_dbdma_exit(void) +{ +} + +module_init(au1xpsc_audio_dbdma_init); +module_exit(au1xpsc_audio_dbdma_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Au12x0/Au1550 PSC Audio DMA driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/au1x/psc-ac97.c b/sound/soc/au1x/psc-ac97.c new file mode 100644 index 00000000000..57facbad682 --- /dev/null +++ b/sound/soc/au1x/psc-ac97.c @@ -0,0 +1,387 @@ +/* + * Au12x0/Au1550 PSC ALSA ASoC audio support. + * + * (c) 2007-2008 MSC Vertriebsges.m.b.H., + * Manuel Lauss + * + * 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. + * + * Au1xxx-PSC AC97 glue. + * + * NOTE: all of these drivers can only work with a SINGLE instance + * of a PSC. Multiple independent audio devices are impossible + * with ASoC v1. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psc.h" + +#define AC97_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define AC97_RATES \ + SNDRV_PCM_RATE_8000_48000 + +#define AC97_FMTS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3BE) + +#define AC97PCR_START(stype) \ + ((stype) == PCM_TX ? PSC_AC97PCR_TS : PSC_AC97PCR_RS) +#define AC97PCR_STOP(stype) \ + ((stype) == PCM_TX ? PSC_AC97PCR_TP : PSC_AC97PCR_RP) +#define AC97PCR_CLRFIFO(stype) \ + ((stype) == PCM_TX ? PSC_AC97PCR_TC : PSC_AC97PCR_RC) + +/* instance data. There can be only one, MacLeod!!!! */ +static struct au1xpsc_audio_data *au1xpsc_ac97_workdata; + +/* AC97 controller reads codec register */ +static unsigned short au1xpsc_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + /* FIXME */ + struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; + unsigned short data, tmo; + + au_writel(PSC_AC97CDC_RD | PSC_AC97CDC_INDX(reg), AC97_CDC(pscdata)); + au_sync(); + + tmo = 1000; + while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo) + udelay(2); + + if (!tmo) + data = 0xffff; + else + data = au_readl(AC97_CDC(pscdata)) & 0xffff; + + au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); + au_sync(); + + return data; +} + +/* AC97 controller writes to codec register */ +static void au1xpsc_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + /* FIXME */ + struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; + unsigned int tmo; + + au_writel(PSC_AC97CDC_INDX(reg) | (val & 0xffff), AC97_CDC(pscdata)); + au_sync(); + tmo = 1000; + while ((!(au_readl(AC97_EVNT(pscdata)) & PSC_AC97EVNT_CD)) && --tmo) + au_sync(); + + au_writel(PSC_AC97EVNT_CD, AC97_EVNT(pscdata)); + au_sync(); +} + +/* AC97 controller asserts a warm reset */ +static void au1xpsc_ac97_warm_reset(struct snd_ac97 *ac97) +{ + /* FIXME */ + struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; + + au_writel(PSC_AC97RST_SNC, AC97_RST(pscdata)); + au_sync(); + msleep(10); + au_writel(0, AC97_RST(pscdata)); + au_sync(); +} + +static void au1xpsc_ac97_cold_reset(struct snd_ac97 *ac97) +{ + /* FIXME */ + struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; + int i; + + /* disable PSC during cold reset */ + au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_sync(); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(pscdata)); + au_sync(); + + /* issue cold reset */ + au_writel(PSC_AC97RST_RST, AC97_RST(pscdata)); + au_sync(); + msleep(500); + au_writel(0, AC97_RST(pscdata)); + au_sync(); + + /* enable PSC */ + au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata)); + au_sync(); + + /* wait for PSC to indicate it's ready */ + i = 100000; + while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_SR)) && (--i)) + au_sync(); + + if (i == 0) { + printk(KERN_ERR "au1xpsc-ac97: PSC not ready!\n"); + return; + } + + /* enable the ac97 function */ + au_writel(pscdata->cfg | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); + au_sync(); + + /* wait for AC97 core to become ready */ + i = 100000; + while (!((au_readl(AC97_STAT(pscdata)) & PSC_AC97STAT_DR)) && (--i)) + au_sync(); + if (i == 0) + printk(KERN_ERR "au1xpsc-ac97: AC97 ctrl not ready\n"); +} + +/* AC97 controller operations */ +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = au1xpsc_ac97_read, + .write = au1xpsc_ac97_write, + .reset = au1xpsc_ac97_cold_reset, + .warm_reset = au1xpsc_ac97_warm_reset, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static int au1xpsc_ac97_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + /* FIXME */ + struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; + unsigned long r, stat; + int chans, stype = SUBSTREAM_TYPE(substream); + + chans = params_channels(params); + + r = au_readl(AC97_CFG(pscdata)); + stat = au_readl(AC97_STAT(pscdata)); + + /* already active? */ + if (stat & (PSC_AC97STAT_TB | PSC_AC97STAT_RB)) { + /* reject parameters not currently set up */ + if ((PSC_AC97CFG_GET_LEN(r) != params->msbits) || + (pscdata->rate != params_rate(params))) + return -EINVAL; + } else { + /* disable AC97 device controller first */ + au_writel(r & ~PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); + au_sync(); + + /* set sample bitdepth: REG[24:21]=(BITS-2)/2 */ + r &= ~PSC_AC97CFG_LEN_MASK; + r |= PSC_AC97CFG_SET_LEN(params->msbits); + + /* channels: enable slots for front L/R channel */ + if (stype == PCM_TX) { + r &= ~PSC_AC97CFG_TXSLOT_MASK; + r |= PSC_AC97CFG_TXSLOT_ENA(3); + r |= PSC_AC97CFG_TXSLOT_ENA(4); + } else { + r &= ~PSC_AC97CFG_RXSLOT_MASK; + r |= PSC_AC97CFG_RXSLOT_ENA(3); + r |= PSC_AC97CFG_RXSLOT_ENA(4); + } + + /* finally enable the AC97 controller again */ + au_writel(r | PSC_AC97CFG_DE_ENABLE, AC97_CFG(pscdata)); + au_sync(); + + pscdata->cfg = r; + pscdata->rate = params_rate(params); + } + + return 0; +} + +static int au1xpsc_ac97_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + /* FIXME */ + struct au1xpsc_audio_data *pscdata = au1xpsc_ac97_workdata; + int ret, stype = SUBSTREAM_TYPE(substream); + + ret = 0; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + au_writel(AC97PCR_START(stype), AC97_PCR(pscdata)); + au_sync(); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + au_writel(AC97PCR_STOP(stype), AC97_PCR(pscdata)); + au_sync(); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int au1xpsc_ac97_probe(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + int ret; + struct resource *r; + unsigned long sel; + + if (au1xpsc_ac97_workdata) + return -EBUSY; + + au1xpsc_ac97_workdata = + kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); + if (!au1xpsc_ac97_workdata) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENODEV; + goto out0; + } + + ret = -EBUSY; + au1xpsc_ac97_workdata->ioarea = + request_mem_region(r->start, r->end - r->start + 1, + "au1xpsc_ac97"); + if (!au1xpsc_ac97_workdata->ioarea) + goto out0; + + au1xpsc_ac97_workdata->mmio = ioremap(r->start, 0xffff); + if (!au1xpsc_ac97_workdata->mmio) + goto out1; + + /* configuration: max dma trigger threshold, enable ac97 */ + au1xpsc_ac97_workdata->cfg = PSC_AC97CFG_RT_FIFO8 | + PSC_AC97CFG_TT_FIFO8 | + PSC_AC97CFG_DE_ENABLE; + + /* preserve PSC clock source set up by platform (dev.platform_data + * is already occupied by soc layer) + */ + sel = au_readl(PSC_SEL(au1xpsc_ac97_workdata)) & PSC_SEL_CLK_MASK; + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_sync(); + au_writel(0, PSC_SEL(au1xpsc_ac97_workdata)); + au_sync(); + au_writel(PSC_SEL_PS_AC97MODE | sel, PSC_SEL(au1xpsc_ac97_workdata)); + au_sync(); + /* next up: cold reset. Dont check for PSC-ready now since + * there may not be any codec clock yet. + */ + + return 0; + +out1: + release_resource(au1xpsc_ac97_workdata->ioarea); + kfree(au1xpsc_ac97_workdata->ioarea); +out0: + kfree(au1xpsc_ac97_workdata); + au1xpsc_ac97_workdata = NULL; + return ret; +} + +static void au1xpsc_ac97_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + /* disable PSC completely */ + au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_sync(); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_sync(); + + iounmap(au1xpsc_ac97_workdata->mmio); + release_resource(au1xpsc_ac97_workdata->ioarea); + kfree(au1xpsc_ac97_workdata->ioarea); + kfree(au1xpsc_ac97_workdata); + au1xpsc_ac97_workdata = NULL; +} + +static int au1xpsc_ac97_suspend(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + /* save interesting registers and disable PSC */ + au1xpsc_ac97_workdata->pm[0] = + au_readl(PSC_SEL(au1xpsc_ac97_workdata)); + + au_writel(0, AC97_CFG(au1xpsc_ac97_workdata)); + au_sync(); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_ac97_workdata)); + au_sync(); + + return 0; +} + +static int au1xpsc_ac97_resume(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + /* restore PSC clock config */ + au_writel(au1xpsc_ac97_workdata->pm[0] | PSC_SEL_PS_AC97MODE, + PSC_SEL(au1xpsc_ac97_workdata)); + au_sync(); + + /* after this point the ac97 core will cold-reset the codec. + * During cold-reset the PSC is reinitialized and the last + * configuration set up in hw_params() is restored. + */ + return 0; +} + +struct snd_soc_dai au1xpsc_ac97_dai = { + .name = "au1xpsc_ac97", + .type = SND_SOC_DAI_AC97, + .probe = au1xpsc_ac97_probe, + .remove = au1xpsc_ac97_remove, + .suspend = au1xpsc_ac97_suspend, + .resume = au1xpsc_ac97_resume, + .playback = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .capture = { + .rates = AC97_RATES, + .formats = AC97_FMTS, + .channels_min = 2, + .channels_max = 2, + }, + .ops = { + .trigger = au1xpsc_ac97_trigger, + .hw_params = au1xpsc_ac97_hw_params, + }, +}; +EXPORT_SYMBOL_GPL(au1xpsc_ac97_dai); + +static int __init au1xpsc_ac97_init(void) +{ + au1xpsc_ac97_workdata = NULL; + return 0; +} + +static void __exit au1xpsc_ac97_exit(void) +{ +} + +module_init(au1xpsc_ac97_init); +module_exit(au1xpsc_ac97_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Au12x0/Au1550 PSC AC97 ALSA ASoC audio driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/au1x/psc-i2s.c b/sound/soc/au1x/psc-i2s.c new file mode 100644 index 00000000000..ba4b5c199f2 --- /dev/null +++ b/sound/soc/au1x/psc-i2s.c @@ -0,0 +1,414 @@ +/* + * Au12x0/Au1550 PSC ALSA ASoC audio support. + * + * (c) 2007-2008 MSC Vertriebsges.m.b.H., + * Manuel Lauss + * + * 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. + * + * Au1xxx-PSC I2S glue. + * + * NOTE: all of these drivers can only work with a SINGLE instance + * of a PSC. Multiple independent audio devices are impossible + * with ASoC v1. + * NOTE: so far only PSC slave mode (bit- and frameclock) is supported. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "psc.h" + +/* supported I2S DAI hardware formats */ +#define AU1XPSC_I2S_DAIFMT \ + (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J | \ + SND_SOC_DAIFMT_NB_NF) + +/* supported I2S direction */ +#define AU1XPSC_I2S_DIR \ + (SND_SOC_DAIDIR_PLAYBACK | SND_SOC_DAIDIR_CAPTURE) + +#define AU1XPSC_I2S_RATES \ + SNDRV_PCM_RATE_8000_192000 + +#define AU1XPSC_I2S_FMTS \ + (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) + +#define I2SSTAT_BUSY(stype) \ + ((stype) == PCM_TX ? PSC_I2SSTAT_TB : PSC_I2SSTAT_RB) +#define I2SPCR_START(stype) \ + ((stype) == PCM_TX ? PSC_I2SPCR_TS : PSC_I2SPCR_RS) +#define I2SPCR_STOP(stype) \ + ((stype) == PCM_TX ? PSC_I2SPCR_TP : PSC_I2SPCR_RP) +#define I2SPCR_CLRFIFO(stype) \ + ((stype) == PCM_TX ? PSC_I2SPCR_TC : PSC_I2SPCR_RC) + + +/* instance data. There can be only one, MacLeod!!!! */ +static struct au1xpsc_audio_data *au1xpsc_i2s_workdata; + +static int au1xpsc_i2s_set_fmt(struct snd_soc_dai *cpu_dai, + unsigned int fmt) +{ + struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata; + unsigned long ct; + int ret; + + ret = -EINVAL; + + ct = pscdata->cfg; + + ct &= ~(PSC_I2SCFG_XM | PSC_I2SCFG_MLJ); /* left-justified */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ct |= PSC_I2SCFG_XM; /* enable I2S mode */ + break; + case SND_SOC_DAIFMT_MSB: + break; + case SND_SOC_DAIFMT_LSB: + ct |= PSC_I2SCFG_MLJ; /* LSB (right-) justified */ + break; + default: + goto out; + } + + ct &= ~(PSC_I2SCFG_BI | PSC_I2SCFG_WI); /* IB-IF */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + ct |= PSC_I2SCFG_BI | PSC_I2SCFG_WI; + break; + case SND_SOC_DAIFMT_NB_IF: + ct |= PSC_I2SCFG_BI; + break; + case SND_SOC_DAIFMT_IB_NF: + ct |= PSC_I2SCFG_WI; + break; + case SND_SOC_DAIFMT_IB_IF: + break; + default: + goto out; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: /* CODEC master */ + ct |= PSC_I2SCFG_MS; /* PSC I2S slave mode */ + break; + case SND_SOC_DAIFMT_CBS_CFS: /* CODEC slave */ + ct &= ~PSC_I2SCFG_MS; /* PSC I2S Master mode */ + break; + default: + goto out; + } + + pscdata->cfg = ct; + ret = 0; +out: + return ret; +} + +static int au1xpsc_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata; + + int cfgbits; + unsigned long stat; + + /* check if the PSC is already streaming data */ + stat = au_readl(I2S_STAT(pscdata)); + if (stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB)) { + /* reject parameters not currently set up in hardware */ + cfgbits = au_readl(I2S_CFG(pscdata)); + if ((PSC_I2SCFG_GET_LEN(cfgbits) != params->msbits) || + (params_rate(params) != pscdata->rate)) + return -EINVAL; + } else { + /* set sample bitdepth */ + pscdata->cfg &= ~(0x1f << 4); + pscdata->cfg |= PSC_I2SCFG_SET_LEN(params->msbits); + /* remember current rate for other stream */ + pscdata->rate = params_rate(params); + } + return 0; +} + +/* Configure PSC late: on my devel systems the codec is I2S master and + * supplies the i2sbitclock __AND__ i2sMclk (!) to the PSC unit. ASoC + * uses aggressive PM and switches the codec off when it is not in use + * which also means the PSC unit doesn't get any clocks and is therefore + * dead. That's why this chunk here gets called from the trigger callback + * because I can be reasonably certain the codec is driving the clocks. + */ +static int au1xpsc_i2s_configure(struct au1xpsc_audio_data *pscdata) +{ + unsigned long tmo; + + /* bring PSC out of sleep, and configure I2S unit */ + au_writel(PSC_CTRL_ENABLE, PSC_CTRL(pscdata)); + au_sync(); + + tmo = 1000000; + while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_SR) && tmo) + tmo--; + + if (!tmo) + goto psc_err; + + au_writel(0, I2S_CFG(pscdata)); + au_sync(); + au_writel(pscdata->cfg | PSC_I2SCFG_DE_ENABLE, I2S_CFG(pscdata)); + au_sync(); + + /* wait for I2S controller to become ready */ + tmo = 1000000; + while (!(au_readl(I2S_STAT(pscdata)) & PSC_I2SSTAT_DR) && tmo) + tmo--; + + if (tmo) + return 0; + +psc_err: + au_writel(0, I2S_CFG(pscdata)); + au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata)); + au_sync(); + return -ETIMEDOUT; +} + +static int au1xpsc_i2s_start(struct au1xpsc_audio_data *pscdata, int stype) +{ + unsigned long tmo, stat; + int ret; + + ret = 0; + + /* if both TX and RX are idle, configure the PSC */ + stat = au_readl(I2S_STAT(pscdata)); + if (!(stat & (PSC_I2SSTAT_TB | PSC_I2SSTAT_RB))) { + ret = au1xpsc_i2s_configure(pscdata); + if (ret) + goto out; + } + + au_writel(I2SPCR_CLRFIFO(stype), I2S_PCR(pscdata)); + au_sync(); + au_writel(I2SPCR_START(stype), I2S_PCR(pscdata)); + au_sync(); + + /* wait for start confirmation */ + tmo = 1000000; + while (!(au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo) + tmo--; + + if (!tmo) { + au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata)); + au_sync(); + ret = -ETIMEDOUT; + } +out: + return ret; +} + +static int au1xpsc_i2s_stop(struct au1xpsc_audio_data *pscdata, int stype) +{ + unsigned long tmo, stat; + + au_writel(I2SPCR_STOP(stype), I2S_PCR(pscdata)); + au_sync(); + + /* wait for stop confirmation */ + tmo = 1000000; + while ((au_readl(I2S_STAT(pscdata)) & I2SSTAT_BUSY(stype)) && tmo) + tmo--; + + /* if both TX and RX are idle, disable PSC */ + stat = au_readl(I2S_STAT(pscdata)); + if (!(stat & (PSC_I2SSTAT_RB | PSC_I2SSTAT_RB))) { + au_writel(0, I2S_CFG(pscdata)); + au_sync(); + au_writel(PSC_CTRL_SUSPEND, PSC_CTRL(pscdata)); + au_sync(); + } + return 0; +} + +static int au1xpsc_i2s_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct au1xpsc_audio_data *pscdata = au1xpsc_i2s_workdata; + int ret, stype = SUBSTREAM_TYPE(substream); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + case SNDRV_PCM_TRIGGER_RESUME: + ret = au1xpsc_i2s_start(pscdata, stype); + break; + case SNDRV_PCM_TRIGGER_STOP: + case SNDRV_PCM_TRIGGER_SUSPEND: + ret = au1xpsc_i2s_stop(pscdata, stype); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static int au1xpsc_i2s_probe(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + struct resource *r; + unsigned long sel; + int ret; + + if (au1xpsc_i2s_workdata) + return -EBUSY; + + au1xpsc_i2s_workdata = + kzalloc(sizeof(struct au1xpsc_audio_data), GFP_KERNEL); + if (!au1xpsc_i2s_workdata) + return -ENOMEM; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + ret = -ENODEV; + goto out0; + } + + ret = -EBUSY; + au1xpsc_i2s_workdata->ioarea = + request_mem_region(r->start, r->end - r->start + 1, + "au1xpsc_i2s"); + if (!au1xpsc_i2s_workdata->ioarea) + goto out0; + + au1xpsc_i2s_workdata->mmio = ioremap(r->start, 0xffff); + if (!au1xpsc_i2s_workdata->mmio) + goto out1; + + /* preserve PSC clock source set up by platform (dev.platform_data + * is already occupied by soc layer) + */ + sel = au_readl(PSC_SEL(au1xpsc_i2s_workdata)) & PSC_SEL_CLK_MASK; + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_sync(); + au_writel(PSC_SEL_PS_I2SMODE | sel, PSC_SEL(au1xpsc_i2s_workdata)); + au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_sync(); + + /* preconfigure: set max rx/tx fifo depths */ + au1xpsc_i2s_workdata->cfg |= + PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8; + + /* don't wait for I2S core to become ready now; clocks may not + * be running yet; depending on clock input for PSC a wait might + * time out. + */ + + return 0; + +out1: + release_resource(au1xpsc_i2s_workdata->ioarea); + kfree(au1xpsc_i2s_workdata->ioarea); +out0: + kfree(au1xpsc_i2s_workdata); + au1xpsc_i2s_workdata = NULL; + return ret; +} + +static void au1xpsc_i2s_remove(struct platform_device *pdev, + struct snd_soc_dai *dai) +{ + au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_sync(); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_sync(); + + iounmap(au1xpsc_i2s_workdata->mmio); + release_resource(au1xpsc_i2s_workdata->ioarea); + kfree(au1xpsc_i2s_workdata->ioarea); + kfree(au1xpsc_i2s_workdata); + au1xpsc_i2s_workdata = NULL; +} + +static int au1xpsc_i2s_suspend(struct platform_device *pdev, + struct snd_soc_dai *cpu_dai) +{ + /* save interesting register and disable PSC */ + au1xpsc_i2s_workdata->pm[0] = + au_readl(PSC_SEL(au1xpsc_i2s_workdata)); + + au_writel(0, I2S_CFG(au1xpsc_i2s_workdata)); + au_sync(); + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_sync(); + + return 0; +} + +static int au1xpsc_i2s_resume(struct platform_device *pdev, + struct snd_soc_dai *cpu_dai) +{ + /* select I2S mode and PSC clock */ + au_writel(PSC_CTRL_DISABLE, PSC_CTRL(au1xpsc_i2s_workdata)); + au_sync(); + au_writel(0, PSC_SEL(au1xpsc_i2s_workdata)); + au_sync(); + au_writel(au1xpsc_i2s_workdata->pm[0], + PSC_SEL(au1xpsc_i2s_workdata)); + au_sync(); + + return 0; +} + +struct snd_soc_dai au1xpsc_i2s_dai = { + .name = "au1xpsc_i2s", + .type = SND_SOC_DAI_I2S, + .probe = au1xpsc_i2s_probe, + .remove = au1xpsc_i2s_remove, + .suspend = au1xpsc_i2s_suspend, + .resume = au1xpsc_i2s_resume, + .playback = { + .rates = AU1XPSC_I2S_RATES, + .formats = AU1XPSC_I2S_FMTS, + .channels_min = 2, + .channels_max = 8, /* 2 without external help */ + }, + .capture = { + .rates = AU1XPSC_I2S_RATES, + .formats = AU1XPSC_I2S_FMTS, + .channels_min = 2, + .channels_max = 8, /* 2 without external help */ + }, + .ops = { + .trigger = au1xpsc_i2s_trigger, + .hw_params = au1xpsc_i2s_hw_params, + }, + .dai_ops = { + .set_fmt = au1xpsc_i2s_set_fmt, + }, +}; +EXPORT_SYMBOL(au1xpsc_i2s_dai); + +static int __init au1xpsc_i2s_init(void) +{ + au1xpsc_i2s_workdata = NULL; + return 0; +} + +static void __exit au1xpsc_i2s_exit(void) +{ +} + +module_init(au1xpsc_i2s_init); +module_exit(au1xpsc_i2s_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Au12x0/Au1550 PSC I2S ALSA ASoC audio driver"); +MODULE_AUTHOR("Manuel Lauss "); diff --git a/sound/soc/au1x/psc.h b/sound/soc/au1x/psc.h new file mode 100644 index 00000000000..8fdb1a04a07 --- /dev/null +++ b/sound/soc/au1x/psc.h @@ -0,0 +1,53 @@ +/* + * Au12x0/Au1550 PSC ALSA ASoC audio support. + * + * (c) 2007-2008 MSC Vertriebsges.m.b.H., + * Manuel Lauss + * + * 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. + * + * NOTE: all of these drivers can only work with a SINGLE instance + * of a PSC. Multiple independent audio devices are impossible + * with ASoC v1. + */ + +#ifndef _AU1X_PCM_H +#define _AU1X_PCM_H + +extern struct snd_soc_dai au1xpsc_ac97_dai; +extern struct snd_soc_dai au1xpsc_i2s_dai; +extern struct snd_soc_platform au1xpsc_soc_platform; +extern struct snd_ac97_bus_ops soc_ac97_ops; + +struct au1xpsc_audio_data { + void __iomem *mmio; + + unsigned long cfg; + unsigned long rate; + + unsigned long pm[2]; + struct resource *ioarea; +}; + +#define PCM_TX 0 +#define PCM_RX 1 + +#define SUBSTREAM_TYPE(substream) \ + ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK ? PCM_TX : PCM_RX) + +/* easy access macros */ +#define PSC_CTRL(x) ((unsigned long)((x)->mmio) + PSC_CTRL_OFFSET) +#define PSC_SEL(x) ((unsigned long)((x)->mmio) + PSC_SEL_OFFSET) +#define I2S_STAT(x) ((unsigned long)((x)->mmio) + PSC_I2SSTAT_OFFSET) +#define I2S_CFG(x) ((unsigned long)((x)->mmio) + PSC_I2SCFG_OFFSET) +#define I2S_PCR(x) ((unsigned long)((x)->mmio) + PSC_I2SPCR_OFFSET) +#define AC97_CFG(x) ((unsigned long)((x)->mmio) + PSC_AC97CFG_OFFSET) +#define AC97_CDC(x) ((unsigned long)((x)->mmio) + PSC_AC97CDC_OFFSET) +#define AC97_EVNT(x) ((unsigned long)((x)->mmio) + PSC_AC97EVNT_OFFSET) +#define AC97_PCR(x) ((unsigned long)((x)->mmio) + PSC_AC97PCR_OFFSET) +#define AC97_RST(x) ((unsigned long)((x)->mmio) + PSC_AC97RST_OFFSET) +#define AC97_STAT(x) ((unsigned long)((x)->mmio) + PSC_AC97STAT_OFFSET) + +#endif diff --git a/sound/soc/au1x/sample-ac97.c b/sound/soc/au1x/sample-ac97.c new file mode 100644 index 00000000000..f75ae7f62c3 --- /dev/null +++ b/sound/soc/au1x/sample-ac97.c @@ -0,0 +1,144 @@ +/* + * Sample Au12x0/Au1550 PSC AC97 sound machine. + * + * Copyright (c) 2007-2008 Manuel Lauss + * + * This program is free software; you can redistribute it and/or modify + * it under the terms outlined in the file COPYING at the root of this + * source archive. + * + * This is a very generic AC97 sound machine driver for boards which + * have (AC97) audio at PSC1 (e.g. DB1200 demoboards). + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../codecs/ac97.h" +#include "psc.h" + +static int au1xpsc_sample_ac97_init(struct snd_soc_codec *codec) +{ + snd_soc_dapm_sync(codec); + return 0; +} + +static struct snd_soc_dai_link au1xpsc_sample_ac97_dai = { + .name = "AC97", + .stream_name = "AC97 HiFi", + .cpu_dai = &au1xpsc_ac97_dai, /* see psc-ac97.c */ + .codec_dai = &ac97_dai, /* see codecs/ac97.c */ + .init = au1xpsc_sample_ac97_init, + .ops = NULL, +}; + +static struct snd_soc_machine au1xpsc_sample_ac97_machine = { + .name = "Au1xxx PSC AC97 Audio", + .dai_link = &au1xpsc_sample_ac97_dai, + .num_links = 1, +}; + +static struct snd_soc_device au1xpsc_sample_ac97_devdata = { + .machine = &au1xpsc_sample_ac97_machine, + .platform = &au1xpsc_soc_platform, /* see dbdma2.c */ + .codec_dev = &soc_codec_dev_ac97, +}; + +static struct resource au1xpsc_psc1_res[] = { + [0] = { + .start = CPHYSADDR(PSC1_BASE_ADDR), + .end = CPHYSADDR(PSC1_BASE_ADDR) + 0x000fffff, + .flags = IORESOURCE_MEM, + }, + [1] = { +#ifdef CONFIG_SOC_AU1200 + .start = AU1200_PSC1_INT, + .end = AU1200_PSC1_INT, +#elif defined(CONFIG_SOC_AU1550) + .start = AU1550_PSC1_INT, + .end = AU1550_PSC1_INT, +#endif + .flags = IORESOURCE_IRQ, + }, + [2] = { + .start = DSCR_CMD0_PSC1_TX, + .end = DSCR_CMD0_PSC1_TX, + .flags = IORESOURCE_DMA, + }, + [3] = { + .start = DSCR_CMD0_PSC1_RX, + .end = DSCR_CMD0_PSC1_RX, + .flags = IORESOURCE_DMA, + }, +}; + +static struct platform_device *au1xpsc_sample_ac97_dev; + +static int __init au1xpsc_sample_ac97_load(void) +{ + int ret; + +#ifdef CONFIG_SOC_AU1200 + unsigned long io; + + /* modify sys_pinfunc for AC97 on PSC1 */ + io = au_readl(SYS_PINFUNC); + io |= SYS_PINFUNC_P1C; + io &= ~(SYS_PINFUNC_P1A | SYS_PINFUNC_P1B); + au_writel(io, SYS_PINFUNC); + au_sync(); +#endif + + ret = -ENOMEM; + + /* setup PSC clock source for AC97 part: external clock provided + * by codec. The psc-ac97.c driver depends on this setting! + */ + au_writel(PSC_SEL_CLK_SERCLK, PSC1_BASE_ADDR + PSC_SEL_OFFSET); + au_sync(); + + au1xpsc_sample_ac97_dev = platform_device_alloc("soc-audio", -1); + if (!au1xpsc_sample_ac97_dev) + goto out; + + au1xpsc_sample_ac97_dev->resource = + kmemdup(au1xpsc_psc1_res, sizeof(struct resource) * + ARRAY_SIZE(au1xpsc_psc1_res), GFP_KERNEL); + au1xpsc_sample_ac97_dev->num_resources = ARRAY_SIZE(au1xpsc_psc1_res); + au1xpsc_sample_ac97_dev->id = 1; + + platform_set_drvdata(au1xpsc_sample_ac97_dev, + &au1xpsc_sample_ac97_devdata); + au1xpsc_sample_ac97_devdata.dev = &au1xpsc_sample_ac97_dev->dev; + ret = platform_device_add(au1xpsc_sample_ac97_dev); + + if (ret) { + platform_device_put(au1xpsc_sample_ac97_dev); + au1xpsc_sample_ac97_dev = NULL; + } + +out: + return ret; +} + +static void __exit au1xpsc_sample_ac97_exit(void) +{ + platform_device_unregister(au1xpsc_sample_ac97_dev); +} + +module_init(au1xpsc_sample_ac97_load); +module_exit(au1xpsc_sample_ac97_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Au1xxx PSC sample AC97 machine"); +MODULE_AUTHOR("Manuel Lauss "); -- cgit v1.2.3-70-g09d2 From 862c2c0a61c515f2e9f63f689215bcf99a607eaf Mon Sep 17 00:00:00 2001 From: Thomas Bogendoerfer Date: Sat, 12 Jul 2008 22:43:50 +0200 Subject: ALSA: ALSA driver for SGI O2 audio board This patch adds a new ALSA driver for the audio device found inside most of the SGI O2 workstation. The hardware uses a SGI custom chip, which feeds a AD codec chip. Signed-off-by: Thomas Bogendoerfer Signed-off-by: Takashi Iwai Signed-off-by: Jaroslav Kysela --- include/sound/ad1843.h | 46 +++ sound/mips/Kconfig | 6 + sound/mips/Makefile | 2 + sound/mips/ad1843.c | 561 ++++++++++++++++++++++++++ sound/mips/sgio2audio.c | 1006 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1621 insertions(+) create mode 100644 include/sound/ad1843.h create mode 100644 sound/mips/ad1843.c create mode 100644 sound/mips/sgio2audio.c (limited to 'include') diff --git a/include/sound/ad1843.h b/include/sound/ad1843.h new file mode 100644 index 00000000000..b236a9d1d6e --- /dev/null +++ b/include/sound/ad1843.h @@ -0,0 +1,46 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright 2003 Vivien Chappelier + * Copyright 2008 Thomas Bogendoerfer + */ + +#ifndef __SOUND_AD1843_H +#define __SOUND_AD1843_H + +struct snd_ad1843 { + void *chip; + int (*read)(void *chip, int reg); + int (*write)(void *chip, int reg, int val); +}; + +#define AD1843_GAIN_RECLEV 0 +#define AD1843_GAIN_LINE 1 +#define AD1843_GAIN_LINE_2 2 +#define AD1843_GAIN_MIC 3 +#define AD1843_GAIN_PCM_0 4 +#define AD1843_GAIN_PCM_1 5 +#define AD1843_GAIN_SIZE (AD1843_GAIN_PCM_1+1) + +int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id); +int ad1843_get_gain(struct snd_ad1843 *ad1843, int id); +int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval); +int ad1843_get_recsrc(struct snd_ad1843 *ad1843); +int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc); +void ad1843_setup_dac(struct snd_ad1843 *ad1843, + unsigned int id, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels); +void ad1843_shutdown_dac(struct snd_ad1843 *ad1843, + unsigned int id); +void ad1843_setup_adc(struct snd_ad1843 *ad1843, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels); +void ad1843_shutdown_adc(struct snd_ad1843 *ad1843); +int ad1843_init(struct snd_ad1843 *ad1843); + +#endif /* __SOUND_AD1843_H */ diff --git a/sound/mips/Kconfig b/sound/mips/Kconfig index 2a61cade4ac..a9823fad85c 100644 --- a/sound/mips/Kconfig +++ b/sound/mips/Kconfig @@ -9,6 +9,12 @@ menuconfig SND_MIPS if SND_MIPS +config SND_SGI_O2 + tristate "SGI O2 Audio" + depends on SGI_IP32 + help + Sound support for the SGI O2 Workstation. + config SND_SGI_HAL2 tristate "SGI HAL2 Audio" depends on SGI_HAS_HAL2 diff --git a/sound/mips/Makefile b/sound/mips/Makefile index 63f4a9c0a8d..861ec0a574b 100644 --- a/sound/mips/Makefile +++ b/sound/mips/Makefile @@ -3,8 +3,10 @@ # snd-au1x00-objs := au1x00.o +snd-sgi-o2-objs := sgio2audio.o ad1843.o snd-sgi-hal2-objs := hal2.o # Toplevel Module Dependency obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o +obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o diff --git a/sound/mips/ad1843.c b/sound/mips/ad1843.c new file mode 100644 index 00000000000..c624510ec37 --- /dev/null +++ b/sound/mips/ad1843.c @@ -0,0 +1,561 @@ +/* + * AD1843 low level driver + * + * Copyright 2003 Vivien Chappelier + * Copyright 2008 Thomas Bogendoerfer + * + * inspired from vwsnd.c (SGI VW audio driver) + * Copyright 1999 Silicon Graphics, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include + +/* + * AD1843 bitfield definitions. All are named as in the AD1843 data + * sheet, with ad1843_ prepended and individual bit numbers removed. + * + * E.g., bits LSS0 through LSS2 become ad1843_LSS. + * + * Only the bitfields we need are defined. + */ + +struct ad1843_bitfield { + char reg; + char lo_bit; + char nbits; +}; + +static const struct ad1843_bitfield + ad1843_PDNO = { 0, 14, 1 }, /* Converter Power-Down Flag */ + ad1843_INIT = { 0, 15, 1 }, /* Clock Initialization Flag */ + ad1843_RIG = { 2, 0, 4 }, /* Right ADC Input Gain */ + ad1843_RMGE = { 2, 4, 1 }, /* Right ADC Mic Gain Enable */ + ad1843_RSS = { 2, 5, 3 }, /* Right ADC Source Select */ + ad1843_LIG = { 2, 8, 4 }, /* Left ADC Input Gain */ + ad1843_LMGE = { 2, 12, 1 }, /* Left ADC Mic Gain Enable */ + ad1843_LSS = { 2, 13, 3 }, /* Left ADC Source Select */ + ad1843_RD2M = { 3, 0, 5 }, /* Right DAC 2 Mix Gain/Atten */ + ad1843_RD2MM = { 3, 7, 1 }, /* Right DAC 2 Mix Mute */ + ad1843_LD2M = { 3, 8, 5 }, /* Left DAC 2 Mix Gain/Atten */ + ad1843_LD2MM = { 3, 15, 1 }, /* Left DAC 2 Mix Mute */ + ad1843_RX1M = { 4, 0, 5 }, /* Right Aux 1 Mix Gain/Atten */ + ad1843_RX1MM = { 4, 7, 1 }, /* Right Aux 1 Mix Mute */ + ad1843_LX1M = { 4, 8, 5 }, /* Left Aux 1 Mix Gain/Atten */ + ad1843_LX1MM = { 4, 15, 1 }, /* Left Aux 1 Mix Mute */ + ad1843_RX2M = { 5, 0, 5 }, /* Right Aux 2 Mix Gain/Atten */ + ad1843_RX2MM = { 5, 7, 1 }, /* Right Aux 2 Mix Mute */ + ad1843_LX2M = { 5, 8, 5 }, /* Left Aux 2 Mix Gain/Atten */ + ad1843_LX2MM = { 5, 15, 1 }, /* Left Aux 2 Mix Mute */ + ad1843_RMCM = { 7, 0, 5 }, /* Right Mic Mix Gain/Atten */ + ad1843_RMCMM = { 7, 7, 1 }, /* Right Mic Mix Mute */ + ad1843_LMCM = { 7, 8, 5 }, /* Left Mic Mix Gain/Atten */ + ad1843_LMCMM = { 7, 15, 1 }, /* Left Mic Mix Mute */ + ad1843_HPOS = { 8, 4, 1 }, /* Headphone Output Voltage Swing */ + ad1843_HPOM = { 8, 5, 1 }, /* Headphone Output Mute */ + ad1843_MPOM = { 8, 6, 1 }, /* Mono Output Mute */ + ad1843_RDA1G = { 9, 0, 6 }, /* Right DAC1 Analog/Digital Gain */ + ad1843_RDA1GM = { 9, 7, 1 }, /* Right DAC1 Analog Mute */ + ad1843_LDA1G = { 9, 8, 6 }, /* Left DAC1 Analog/Digital Gain */ + ad1843_LDA1GM = { 9, 15, 1 }, /* Left DAC1 Analog Mute */ + ad1843_RDA2G = { 10, 0, 6 }, /* Right DAC2 Analog/Digital Gain */ + ad1843_RDA2GM = { 10, 7, 1 }, /* Right DAC2 Analog Mute */ + ad1843_LDA2G = { 10, 8, 6 }, /* Left DAC2 Analog/Digital Gain */ + ad1843_LDA2GM = { 10, 15, 1 }, /* Left DAC2 Analog Mute */ + ad1843_RDA1AM = { 11, 7, 1 }, /* Right DAC1 Digital Mute */ + ad1843_LDA1AM = { 11, 15, 1 }, /* Left DAC1 Digital Mute */ + ad1843_RDA2AM = { 12, 7, 1 }, /* Right DAC2 Digital Mute */ + ad1843_LDA2AM = { 12, 15, 1 }, /* Left DAC2 Digital Mute */ + ad1843_ADLC = { 15, 0, 2 }, /* ADC Left Sample Rate Source */ + ad1843_ADRC = { 15, 2, 2 }, /* ADC Right Sample Rate Source */ + ad1843_DA1C = { 15, 8, 2 }, /* DAC1 Sample Rate Source */ + ad1843_DA2C = { 15, 10, 2 }, /* DAC2 Sample Rate Source */ + ad1843_C1C = { 17, 0, 16 }, /* Clock 1 Sample Rate Select */ + ad1843_C2C = { 20, 0, 16 }, /* Clock 2 Sample Rate Select */ + ad1843_C3C = { 23, 0, 16 }, /* Clock 3 Sample Rate Select */ + ad1843_DAADL = { 25, 4, 2 }, /* Digital ADC Left Source Select */ + ad1843_DAADR = { 25, 6, 2 }, /* Digital ADC Right Source Select */ + ad1843_DAMIX = { 25, 14, 1 }, /* DAC Digital Mix Enable */ + ad1843_DRSFLT = { 25, 15, 1 }, /* Digital Reampler Filter Mode */ + ad1843_ADLF = { 26, 0, 2 }, /* ADC Left Channel Data Format */ + ad1843_ADRF = { 26, 2, 2 }, /* ADC Right Channel Data Format */ + ad1843_ADTLK = { 26, 4, 1 }, /* ADC Transmit Lock Mode Select */ + ad1843_SCF = { 26, 7, 1 }, /* SCLK Frequency Select */ + ad1843_DA1F = { 26, 8, 2 }, /* DAC1 Data Format Select */ + ad1843_DA2F = { 26, 10, 2 }, /* DAC2 Data Format Select */ + ad1843_DA1SM = { 26, 14, 1 }, /* DAC1 Stereo/Mono Mode Select */ + ad1843_DA2SM = { 26, 15, 1 }, /* DAC2 Stereo/Mono Mode Select */ + ad1843_ADLEN = { 27, 0, 1 }, /* ADC Left Channel Enable */ + ad1843_ADREN = { 27, 1, 1 }, /* ADC Right Channel Enable */ + ad1843_AAMEN = { 27, 4, 1 }, /* Analog to Analog Mix Enable */ + ad1843_ANAEN = { 27, 7, 1 }, /* Analog Channel Enable */ + ad1843_DA1EN = { 27, 8, 1 }, /* DAC1 Enable */ + ad1843_DA2EN = { 27, 9, 1 }, /* DAC2 Enable */ + ad1843_DDMEN = { 27, 12, 1 }, /* DAC2 to DAC1 Mix Enable */ + ad1843_C1EN = { 28, 11, 1 }, /* Clock Generator 1 Enable */ + ad1843_C2EN = { 28, 12, 1 }, /* Clock Generator 2 Enable */ + ad1843_C3EN = { 28, 13, 1 }, /* Clock Generator 3 Enable */ + ad1843_PDNI = { 28, 15, 1 }; /* Converter Power Down */ + +/* + * The various registers of the AD1843 use three different formats for + * specifying gain. The ad1843_gain structure parameterizes the + * formats. + */ + +struct ad1843_gain { + int negative; /* nonzero if gain is negative. */ + const struct ad1843_bitfield *lfield; + const struct ad1843_bitfield *rfield; + const struct ad1843_bitfield *lmute; + const struct ad1843_bitfield *rmute; +}; + +static const struct ad1843_gain ad1843_gain_RECLEV = { + .negative = 0, + .lfield = &ad1843_LIG, + .rfield = &ad1843_RIG +}; +static const struct ad1843_gain ad1843_gain_LINE = { + .negative = 1, + .lfield = &ad1843_LX1M, + .rfield = &ad1843_RX1M, + .lmute = &ad1843_LX1MM, + .rmute = &ad1843_RX1MM +}; +static const struct ad1843_gain ad1843_gain_LINE_2 = { + .negative = 1, + .lfield = &ad1843_LDA2G, + .rfield = &ad1843_RDA2G, + .lmute = &ad1843_LDA2GM, + .rmute = &ad1843_RDA2GM +}; +static const struct ad1843_gain ad1843_gain_MIC = { + .negative = 1, + .lfield = &ad1843_LMCM, + .rfield = &ad1843_RMCM, + .lmute = &ad1843_LMCMM, + .rmute = &ad1843_RMCMM +}; +static const struct ad1843_gain ad1843_gain_PCM_0 = { + .negative = 1, + .lfield = &ad1843_LDA1G, + .rfield = &ad1843_RDA1G, + .lmute = &ad1843_LDA1GM, + .rmute = &ad1843_RDA1GM +}; +static const struct ad1843_gain ad1843_gain_PCM_1 = { + .negative = 1, + .lfield = &ad1843_LD2M, + .rfield = &ad1843_RD2M, + .lmute = &ad1843_LD2MM, + .rmute = &ad1843_RD2MM +}; + +static const struct ad1843_gain *ad1843_gain[AD1843_GAIN_SIZE] = +{ + &ad1843_gain_RECLEV, + &ad1843_gain_LINE, + &ad1843_gain_LINE_2, + &ad1843_gain_MIC, + &ad1843_gain_PCM_0, + &ad1843_gain_PCM_1, +}; + +/* read the current value of an AD1843 bitfield. */ + +static int ad1843_read_bits(struct snd_ad1843 *ad1843, + const struct ad1843_bitfield *field) +{ + int w; + + w = ad1843->read(ad1843->chip, field->reg); + return w >> field->lo_bit & ((1 << field->nbits) - 1); +} + +/* + * write a new value to an AD1843 bitfield and return the old value. + */ + +static int ad1843_write_bits(struct snd_ad1843 *ad1843, + const struct ad1843_bitfield *field, + int newval) +{ + int w, mask, oldval, newbits; + + w = ad1843->read(ad1843->chip, field->reg); + mask = ((1 << field->nbits) - 1) << field->lo_bit; + oldval = (w & mask) >> field->lo_bit; + newbits = (newval << field->lo_bit) & mask; + w = (w & ~mask) | newbits; + ad1843->write(ad1843->chip, field->reg, w); + + return oldval; +} + +/* + * ad1843_read_multi reads multiple bitfields from the same AD1843 + * register. It uses a single read cycle to do it. (Reading the + * ad1843 requires 256 bit times at 12.288 MHz, or nearly 20 + * microseconds.) + * + * Called like this. + * + * ad1843_read_multi(ad1843, nfields, + * &ad1843_FIELD1, &val1, + * &ad1843_FIELD2, &val2, ...); + */ + +static void ad1843_read_multi(struct snd_ad1843 *ad1843, int argcount, ...) +{ + va_list ap; + const struct ad1843_bitfield *fp; + int w = 0, mask, *value, reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const struct ad1843_bitfield *); + value = va_arg(ap, int *); + if (reg == -1) { + reg = fp->reg; + w = ad1843->read(ad1843->chip, reg); + } + + mask = (1 << fp->nbits) - 1; + *value = w >> fp->lo_bit & mask; + } + va_end(ap); +} + +/* + * ad1843_write_multi stores multiple bitfields into the same AD1843 + * register. It uses one read and one write cycle to do it. + * + * Called like this. + * + * ad1843_write_multi(ad1843, nfields, + * &ad1843_FIELD1, val1, + * &ad1843_FIELF2, val2, ...); + */ + +static void ad1843_write_multi(struct snd_ad1843 *ad1843, int argcount, ...) +{ + va_list ap; + int reg; + const struct ad1843_bitfield *fp; + int value; + int w, m, mask, bits; + + mask = 0; + bits = 0; + reg = -1; + + va_start(ap, argcount); + while (--argcount >= 0) { + fp = va_arg(ap, const struct ad1843_bitfield *); + value = va_arg(ap, int); + if (reg == -1) + reg = fp->reg; + else + BUG_ON(reg != fp->reg); + m = ((1 << fp->nbits) - 1) << fp->lo_bit; + mask |= m; + bits |= (value << fp->lo_bit) & m; + } + va_end(ap); + + if (~mask & 0xFFFF) + w = ad1843->read(ad1843->chip, reg); + else + w = 0; + w = (w & ~mask) | bits; + ad1843->write(ad1843->chip, reg, w); +} + +int ad1843_get_gain_max(struct snd_ad1843 *ad1843, int id) +{ + const struct ad1843_gain *gp = ad1843_gain[id]; + int ret; + + ret = (1 << gp->lfield->nbits); + if (!gp->lmute) + ret -= 1; + return ret; +} + +/* + * ad1843_get_gain reads the specified register and extracts the gain value + * using the supplied gain type. + */ + +int ad1843_get_gain(struct snd_ad1843 *ad1843, int id) +{ + int lg, rg, lm, rm; + const struct ad1843_gain *gp = ad1843_gain[id]; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + ad1843_read_multi(ad1843, 2, gp->lfield, &lg, gp->rfield, &rg); + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + if (gp->lmute) { + ad1843_read_multi(ad1843, 2, gp->lmute, &lm, gp->rmute, &rm); + if (lm) + lg = 0; + if (rm) + rg = 0; + } + return lg << 0 | rg << 8; +} + +/* + * Set an audio channel's gain. + * + * Returns the new gain, which may be lower than the old gain. + */ + +int ad1843_set_gain(struct snd_ad1843 *ad1843, int id, int newval) +{ + const struct ad1843_gain *gp = ad1843_gain[id]; + unsigned short mask = (1 << gp->lfield->nbits) - 1; + + int lg = (newval >> 0) & mask; + int rg = (newval >> 8) & mask; + int lm = (lg == 0) ? 1 : 0; + int rm = (rg == 0) ? 1 : 0; + + if (gp->negative) { + lg = mask - lg; + rg = mask - rg; + } + if (gp->lmute) + ad1843_write_multi(ad1843, 2, gp->lmute, lm, gp->rmute, rm); + ad1843_write_multi(ad1843, 2, gp->lfield, lg, gp->rfield, rg); + return ad1843_get_gain(ad1843, id); +} + +/* Returns the current recording source */ + +int ad1843_get_recsrc(struct snd_ad1843 *ad1843) +{ + int val = ad1843_read_bits(ad1843, &ad1843_LSS); + + if (val < 0 || val > 2) { + val = 2; + ad1843_write_multi(ad1843, 2, + &ad1843_LSS, val, &ad1843_RSS, val); + } + return val; +} + +/* + * Set recording source. + * + * Returns newsrc on success, -errno on failure. + */ + +int ad1843_set_recsrc(struct snd_ad1843 *ad1843, int newsrc) +{ + if (newsrc < 0 || newsrc > 2) + return -EINVAL; + + ad1843_write_multi(ad1843, 2, &ad1843_LSS, newsrc, &ad1843_RSS, newsrc); + return newsrc; +} + +/* Setup ad1843 for D/A conversion. */ + +void ad1843_setup_dac(struct snd_ad1843 *ad1843, + unsigned int id, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels) +{ + int ad_fmt = 0, ad_mode = 0; + + switch (fmt) { + case SNDRV_PCM_FORMAT_S8: + ad_fmt = 0; + break; + case SNDRV_PCM_FORMAT_U8: + ad_fmt = 0; + break; + case SNDRV_PCM_FORMAT_S16_LE: + ad_fmt = 1; + break; + case SNDRV_PCM_FORMAT_MU_LAW: + ad_fmt = 2; + break; + case SNDRV_PCM_FORMAT_A_LAW: + ad_fmt = 3; + break; + default: + break; + } + + switch (channels) { + case 2: + ad_mode = 0; + break; + case 1: + ad_mode = 1; + break; + default: + break; + } + + if (id) { + ad1843_write_bits(ad1843, &ad1843_C2C, framerate); + ad1843_write_multi(ad1843, 2, + &ad1843_DA2SM, ad_mode, + &ad1843_DA2F, ad_fmt); + } else { + ad1843_write_bits(ad1843, &ad1843_C1C, framerate); + ad1843_write_multi(ad1843, 2, + &ad1843_DA1SM, ad_mode, + &ad1843_DA1F, ad_fmt); + } +} + +void ad1843_shutdown_dac(struct snd_ad1843 *ad1843, unsigned int id) +{ + if (id) + ad1843_write_bits(ad1843, &ad1843_DA2F, 1); + else + ad1843_write_bits(ad1843, &ad1843_DA1F, 1); +} + +void ad1843_setup_adc(struct snd_ad1843 *ad1843, + unsigned int framerate, + snd_pcm_format_t fmt, + unsigned int channels) +{ + int da_fmt = 0; + + switch (fmt) { + case SNDRV_PCM_FORMAT_S8: da_fmt = 0; break; + case SNDRV_PCM_FORMAT_U8: da_fmt = 0; break; + case SNDRV_PCM_FORMAT_S16_LE: da_fmt = 1; break; + case SNDRV_PCM_FORMAT_MU_LAW: da_fmt = 2; break; + case SNDRV_PCM_FORMAT_A_LAW: da_fmt = 3; break; + default: break; + } + + ad1843_write_bits(ad1843, &ad1843_C3C, framerate); + ad1843_write_multi(ad1843, 2, + &ad1843_ADLF, da_fmt, &ad1843_ADRF, da_fmt); +} + +void ad1843_shutdown_adc(struct snd_ad1843 *ad1843) +{ + /* nothing to do */ +} + +/* + * Fully initialize the ad1843. As described in the AD1843 data + * sheet, section "START-UP SEQUENCE". The numbered comments are + * subsection headings from the data sheet. See the data sheet, pages + * 52-54, for more info. + * + * return 0 on success, -errno on failure. */ + +int ad1843_init(struct snd_ad1843 *ad1843) +{ + unsigned long later; + + if (ad1843_read_bits(ad1843, &ad1843_INIT) != 0) { + printk(KERN_ERR "ad1843: AD1843 won't initialize\n"); + return -EIO; + } + + ad1843_write_bits(ad1843, &ad1843_SCF, 1); + + /* 4. Put the conversion resources into standby. */ + ad1843_write_bits(ad1843, &ad1843_PDNI, 0); + later = jiffies + msecs_to_jiffies(500); + + while (ad1843_read_bits(ad1843, &ad1843_PDNO)) { + if (time_after(jiffies, later)) { + printk(KERN_ERR + "ad1843: AD1843 won't power up\n"); + return -EIO; + } + schedule_timeout_interruptible(5); + } + + /* 5. Power up the clock generators and enable clock output pins. */ + ad1843_write_multi(ad1843, 3, + &ad1843_C1EN, 1, + &ad1843_C2EN, 1, + &ad1843_C3EN, 1); + + /* 6. Configure conversion resources while they are in standby. */ + + /* DAC1/2 use clock 1/2 as source, ADC uses clock 3. Always. */ + ad1843_write_multi(ad1843, 4, + &ad1843_DA1C, 1, + &ad1843_DA2C, 2, + &ad1843_ADLC, 3, + &ad1843_ADRC, 3); + + /* 7. Enable conversion resources. */ + ad1843_write_bits(ad1843, &ad1843_ADTLK, 1); + ad1843_write_multi(ad1843, 7, + &ad1843_ANAEN, 1, + &ad1843_AAMEN, 1, + &ad1843_DA1EN, 1, + &ad1843_DA2EN, 1, + &ad1843_DDMEN, 1, + &ad1843_ADLEN, 1, + &ad1843_ADREN, 1); + + /* 8. Configure conversion resources while they are enabled. */ + + /* set gain to 0 for all channels */ + ad1843_set_gain(ad1843, AD1843_GAIN_RECLEV, 0); + ad1843_set_gain(ad1843, AD1843_GAIN_LINE, 0); + ad1843_set_gain(ad1843, AD1843_GAIN_LINE_2, 0); + ad1843_set_gain(ad1843, AD1843_GAIN_MIC, 0); + ad1843_set_gain(ad1843, AD1843_GAIN_PCM_0, 0); + ad1843_set_gain(ad1843, AD1843_GAIN_PCM_1, 0); + + /* Unmute all channels. */ + /* DAC1 */ + ad1843_write_multi(ad1843, 2, &ad1843_LDA1GM, 0, &ad1843_RDA1GM, 0); + /* DAC2 */ + ad1843_write_multi(ad1843, 2, &ad1843_LDA2GM, 0, &ad1843_RDA2GM, 0); + + /* Set default recording source to Line In and set + * mic gain to +20 dB. + */ + ad1843_set_recsrc(ad1843, 2); + ad1843_write_multi(ad1843, 2, &ad1843_LMGE, 1, &ad1843_RMGE, 1); + + /* Set Speaker Out level to +/- 4V and unmute it. */ + ad1843_write_multi(ad1843, 3, + &ad1843_HPOS, 1, + &ad1843_HPOM, 0, + &ad1843_MPOM, 0); + + return 0; +} diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c new file mode 100644 index 00000000000..4c63504348d --- /dev/null +++ b/sound/mips/sgio2audio.c @@ -0,0 +1,1006 @@ +/* + * Sound driver for Silicon Graphics O2 Workstations A/V board audio. + * + * Copyright 2003 Vivien Chappelier + * Copyright 2008 Thomas Bogendoerfer + * Mxier part taken from mace_audio.c: + * Copyright 2007 Thorben Jändling + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#define SNDRV_GET_ID +#include +#include + + +MODULE_AUTHOR("Vivien Chappelier "); +MODULE_DESCRIPTION("SGI O2 Audio"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Silicon Graphics, O2 Audio}}"); + +static int index = SNDRV_DEFAULT_IDX1; /* Index 0-MAX */ +static char *id = SNDRV_DEFAULT_STR1; /* ID for this card */ + +module_param(index, int, 0444); +MODULE_PARM_DESC(index, "Index value for SGI O2 soundcard."); +module_param(id, charp, 0444); +MODULE_PARM_DESC(id, "ID string for SGI O2 soundcard."); + + +#define AUDIO_CONTROL_RESET BIT(0) /* 1: reset audio interface */ +#define AUDIO_CONTROL_CODEC_PRESENT BIT(1) /* 1: codec detected */ + +#define CODEC_CONTROL_WORD_SHIFT 0 +#define CODEC_CONTROL_READ BIT(16) +#define CODEC_CONTROL_ADDRESS_SHIFT 17 + +#define CHANNEL_CONTROL_RESET BIT(10) /* 1: reset channel */ +#define CHANNEL_DMA_ENABLE BIT(9) /* 1: enable DMA transfer */ +#define CHANNEL_INT_THRESHOLD_DISABLED (0 << 5) /* interrupt disabled */ +#define CHANNEL_INT_THRESHOLD_25 (1 << 5) /* int on buffer >25% full */ +#define CHANNEL_INT_THRESHOLD_50 (2 << 5) /* int on buffer >50% full */ +#define CHANNEL_INT_THRESHOLD_75 (3 << 5) /* int on buffer >75% full */ +#define CHANNEL_INT_THRESHOLD_EMPTY (4 << 5) /* int on buffer empty */ +#define CHANNEL_INT_THRESHOLD_NOT_EMPTY (5 << 5) /* int on buffer !empty */ +#define CHANNEL_INT_THRESHOLD_FULL (6 << 5) /* int on buffer empty */ +#define CHANNEL_INT_THRESHOLD_NOT_FULL (7 << 5) /* int on buffer !empty */ + +#define CHANNEL_RING_SHIFT 12 +#define CHANNEL_RING_SIZE (1 << CHANNEL_RING_SHIFT) +#define CHANNEL_RING_MASK (CHANNEL_RING_SIZE - 1) + +#define CHANNEL_LEFT_SHIFT 40 +#define CHANNEL_RIGHT_SHIFT 8 + +struct snd_sgio2audio_chan { + int idx; + struct snd_pcm_substream *substream; + int pos; + snd_pcm_uframes_t size; + spinlock_t lock; +}; + +/* definition of the chip-specific record */ +struct snd_sgio2audio { + struct snd_card *card; + + /* codec */ + struct snd_ad1843 ad1843; + spinlock_t ad1843_lock; + + /* channels */ + struct snd_sgio2audio_chan channel[3]; + + /* resources */ + void *ring_base; + dma_addr_t ring_base_dma; +}; + +/* AD1843 access */ + +/* + * read_ad1843_reg returns the current contents of a 16 bit AD1843 register. + * + * Returns unsigned register value on success, -errno on failure. + */ +static int read_ad1843_reg(void *priv, int reg) +{ + struct snd_sgio2audio *chip = priv; + int val; + unsigned long flags; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + + writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) | + CODEC_CONTROL_READ, &mace->perif.audio.codec_control); + wmb(); + val = readq(&mace->perif.audio.codec_control); /* flush bus */ + udelay(200); + + val = readq(&mace->perif.audio.codec_read); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + return val; +} + +/* + * write_ad1843_reg writes the specified value to a 16 bit AD1843 register. + */ +static int write_ad1843_reg(void *priv, int reg, int word) +{ + struct snd_sgio2audio *chip = priv; + int val; + unsigned long flags; + + spin_lock_irqsave(&chip->ad1843_lock, flags); + + writeq((reg << CODEC_CONTROL_ADDRESS_SHIFT) | + (word << CODEC_CONTROL_WORD_SHIFT), + &mace->perif.audio.codec_control); + wmb(); + val = readq(&mace->perif.audio.codec_control); /* flush bus */ + udelay(200); + + spin_unlock_irqrestore(&chip->ad1843_lock, flags); + return 0; +} + +static int sgio2audio_gain_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = ad1843_get_gain_max(&chip->ad1843, + (int)kcontrol->private_value); + return 0; +} + +static int sgio2audio_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); + int vol; + + vol = ad1843_get_gain(&chip->ad1843, (int)kcontrol->private_value); + + ucontrol->value.integer.value[0] = (vol >> 8) & 0xFF; + ucontrol->value.integer.value[1] = vol & 0xFF; + + return 0; +} + +static int sgio2audio_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); + int newvol, oldvol; + + oldvol = ad1843_get_gain(&chip->ad1843, kcontrol->private_value); + newvol = (ucontrol->value.integer.value[0] << 8) | + ucontrol->value.integer.value[1]; + + newvol = ad1843_set_gain(&chip->ad1843, kcontrol->private_value, + newvol); + + return newvol != oldvol; +} + +static int sgio2audio_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + static const char *texts[3] = { + "Cam Mic", "Mic", "Line" + }; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 3; + if (uinfo->value.enumerated.item >= 3) + uinfo->value.enumerated.item = 1; + strcpy(uinfo->value.enumerated.name, + texts[uinfo->value.enumerated.item]); + return 0; +} + +static int sgio2audio_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); + + ucontrol->value.enumerated.item[0] = ad1843_get_recsrc(&chip->ad1843); + return 0; +} + +static int sgio2audio_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_sgio2audio *chip = snd_kcontrol_chip(kcontrol); + int newsrc, oldsrc; + + oldsrc = ad1843_get_recsrc(&chip->ad1843); + newsrc = ad1843_set_recsrc(&chip->ad1843, + ucontrol->value.enumerated.item[0]); + + return newsrc != oldsrc; +} + +/* dac1/pcm0 mixer control */ +static struct snd_kcontrol_new sgio2audio_ctrl_pcm0 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_PCM_0, + .info = sgio2audio_gain_info, + .get = sgio2audio_gain_get, + .put = sgio2audio_gain_put, +}; + +/* dac2/pcm1 mixer control */ +static struct snd_kcontrol_new sgio2audio_ctrl_pcm1 __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PCM Playback Volume", + .index = 1, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_PCM_1, + .info = sgio2audio_gain_info, + .get = sgio2audio_gain_get, + .put = sgio2audio_gain_put, +}; + +/* record level mixer control */ +static struct snd_kcontrol_new sgio2audio_ctrl_reclevel __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_RECLEV, + .info = sgio2audio_gain_info, + .get = sgio2audio_gain_get, + .put = sgio2audio_gain_put, +}; + +/* record level source control */ +static struct snd_kcontrol_new sgio2audio_ctrl_recsource __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Source", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .info = sgio2audio_source_info, + .get = sgio2audio_source_get, + .put = sgio2audio_source_put, +}; + +/* line mixer control */ +static struct snd_kcontrol_new sgio2audio_ctrl_line __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Playback Volume", + .index = 0, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_LINE, + .info = sgio2audio_gain_info, + .get = sgio2audio_gain_get, + .put = sgio2audio_gain_put, +}; + +/* cd mixer control */ +static struct snd_kcontrol_new sgio2audio_ctrl_cd __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Playback Volume", + .index = 1, + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_LINE_2, + .info = sgio2audio_gain_info, + .get = sgio2audio_gain_get, + .put = sgio2audio_gain_put, +}; + +/* mic mixer control */ +static struct snd_kcontrol_new sgio2audio_ctrl_mic __devinitdata = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Mic Playback Volume", + .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, + .private_value = AD1843_GAIN_MIC, + .info = sgio2audio_gain_info, + .get = sgio2audio_gain_get, + .put = sgio2audio_gain_put, +}; + + +static int __devinit snd_sgio2audio_new_mixer(struct snd_sgio2audio *chip) +{ + int err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_pcm0, chip)); + if (err < 0) + return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_pcm1, chip)); + if (err < 0) + return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_reclevel, chip)); + if (err < 0) + return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_recsource, chip)); + if (err < 0) + return err; + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_line, chip)); + if (err < 0) + return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_cd, chip)); + if (err < 0) + return err; + + err = snd_ctl_add(chip->card, + snd_ctl_new1(&sgio2audio_ctrl_mic, chip)); + if (err < 0) + return err; + + return 0; +} + +/* low-level audio interface DMA */ + +/* get data out of bounce buffer, count must be a multiple of 32 */ +/* returns 1 if a period has elapsed */ +static int snd_sgio2audio_dma_pull_frag(struct snd_sgio2audio *chip, + unsigned int ch, unsigned int count) +{ + int ret; + unsigned long src_base, src_pos, dst_mask; + unsigned char *dst_base; + int dst_pos; + u64 *src; + s16 *dst; + u64 x; + unsigned long flags; + struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime; + + spin_lock_irqsave(&chip->channel[ch].lock, flags); + + src_base = (unsigned long) chip->ring_base | (ch << CHANNEL_RING_SHIFT); + src_pos = readq(&mace->perif.audio.chan[ch].read_ptr); + dst_base = runtime->dma_area; + dst_pos = chip->channel[ch].pos; + dst_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; + + /* check if a period has elapsed */ + chip->channel[ch].size += (count >> 3); /* in frames */ + ret = chip->channel[ch].size >= runtime->period_size; + chip->channel[ch].size %= runtime->period_size; + + while (count) { + src = (u64 *)(src_base + src_pos); + dst = (s16 *)(dst_base + dst_pos); + + x = *src; + dst[0] = (x >> CHANNEL_LEFT_SHIFT) & 0xffff; + dst[1] = (x >> CHANNEL_RIGHT_SHIFT) & 0xffff; + + src_pos = (src_pos + sizeof(u64)) & CHANNEL_RING_MASK; + dst_pos = (dst_pos + 2 * sizeof(s16)) & dst_mask; + count -= sizeof(u64); + } + + writeq(src_pos, &mace->perif.audio.chan[ch].read_ptr); /* in bytes */ + chip->channel[ch].pos = dst_pos; + + spin_unlock_irqrestore(&chip->channel[ch].lock, flags); + return ret; +} + +/* put some DMA data in bounce buffer, count must be a multiple of 32 */ +/* returns 1 if a period has elapsed */ +static int snd_sgio2audio_dma_push_frag(struct snd_sgio2audio *chip, + unsigned int ch, unsigned int count) +{ + int ret; + s64 l, r; + unsigned long dst_base, dst_pos, src_mask; + unsigned char *src_base; + int src_pos; + u64 *dst; + s16 *src; + unsigned long flags; + struct snd_pcm_runtime *runtime = chip->channel[ch].substream->runtime; + + spin_lock_irqsave(&chip->channel[ch].lock, flags); + + dst_base = (unsigned long)chip->ring_base | (ch << CHANNEL_RING_SHIFT); + dst_pos = readq(&mace->perif.audio.chan[ch].write_ptr); + src_base = runtime->dma_area; + src_pos = chip->channel[ch].pos; + src_mask = frames_to_bytes(runtime, runtime->buffer_size) - 1; + + /* check if a period has elapsed */ + chip->channel[ch].size += (count >> 3); /* in frames */ + ret = chip->channel[ch].size >= runtime->period_size; + chip->channel[ch].size %= runtime->period_size; + + while (count) { + src = (s16 *)(src_base + src_pos); + dst = (u64 *)(dst_base + dst_pos); + + l = src[0]; /* sign extend */ + r = src[1]; /* sign extend */ + + *dst = ((l & 0x00ffffff) << CHANNEL_LEFT_SHIFT) | + ((r & 0x00ffffff) << CHANNEL_RIGHT_SHIFT); + + dst_pos = (dst_pos + sizeof(u64)) & CHANNEL_RING_MASK; + src_pos = (src_pos + 2 * sizeof(s16)) & src_mask; + count -= sizeof(u64); + } + + writeq(dst_pos, &mace->perif.audio.chan[ch].write_ptr); /* in bytes */ + chip->channel[ch].pos = src_pos; + + spin_unlock_irqrestore(&chip->channel[ch].lock, flags); + return ret; +} + +static int snd_sgio2audio_dma_start(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); + struct snd_sgio2audio_chan *chan = substream->runtime->private_data; + int ch = chan->idx; + + /* reset DMA channel */ + writeq(CHANNEL_CONTROL_RESET, &mace->perif.audio.chan[ch].control); + udelay(10); + writeq(0, &mace->perif.audio.chan[ch].control); + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* push a full buffer */ + snd_sgio2audio_dma_push_frag(chip, ch, CHANNEL_RING_SIZE - 32); + } + /* set DMA to wake on 50% empty and enable interrupt */ + writeq(CHANNEL_DMA_ENABLE | CHANNEL_INT_THRESHOLD_50, + &mace->perif.audio.chan[ch].control); + return 0; +} + +static int snd_sgio2audio_dma_stop(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio_chan *chan = substream->runtime->private_data; + + writeq(0, &mace->perif.audio.chan[chan->idx].control); + return 0; +} + +static irqreturn_t snd_sgio2audio_dma_in_isr(int irq, void *dev_id) +{ + struct snd_sgio2audio_chan *chan = dev_id; + struct snd_pcm_substream *substream; + struct snd_sgio2audio *chip; + int count, ch; + + substream = chan->substream; + chip = snd_pcm_substream_chip(substream); + ch = chan->idx; + + /* empty the ring */ + count = CHANNEL_RING_SIZE - + readq(&mace->perif.audio.chan[ch].depth) - 32; + if (snd_sgio2audio_dma_pull_frag(chip, ch, count)) + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +static irqreturn_t snd_sgio2audio_dma_out_isr(int irq, void *dev_id) +{ + struct snd_sgio2audio_chan *chan = dev_id; + struct snd_pcm_substream *substream; + struct snd_sgio2audio *chip; + int count, ch; + + substream = chan->substream; + chip = snd_pcm_substream_chip(substream); + ch = chan->idx; + /* fill the ring */ + count = CHANNEL_RING_SIZE - + readq(&mace->perif.audio.chan[ch].depth) - 32; + if (snd_sgio2audio_dma_push_frag(chip, ch, count)) + snd_pcm_period_elapsed(substream); + + return IRQ_HANDLED; +} + +static irqreturn_t snd_sgio2audio_error_isr(int irq, void *dev_id) +{ + struct snd_sgio2audio_chan *chan = dev_id; + struct snd_pcm_substream *substream; + + substream = chan->substream; + snd_sgio2audio_dma_stop(substream); + snd_sgio2audio_dma_start(substream); + return IRQ_HANDLED; +} + +/* PCM part */ +/* PCM hardware definition */ +static struct snd_pcm_hardware snd_sgio2audio_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER), + .formats = SNDRV_PCM_FMTBIT_S16_BE, + .rates = SNDRV_PCM_RATE_8000_48000, + .rate_min = 8000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 65536, + .period_bytes_min = 32768, + .period_bytes_max = 65536, + .periods_min = 1, + .periods_max = 1024, +}; + +/* PCM playback open callback */ +static int snd_sgio2audio_playback1_open(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = snd_sgio2audio_pcm_hw; + runtime->private_data = &chip->channel[1]; + return 0; +} + +static int snd_sgio2audio_playback2_open(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = snd_sgio2audio_pcm_hw; + runtime->private_data = &chip->channel[2]; + return 0; +} + +/* PCM capture open callback */ +static int snd_sgio2audio_capture_open(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw = snd_sgio2audio_pcm_hw; + runtime->private_data = &chip->channel[0]; + return 0; +} + +/* PCM close callback */ +static int snd_sgio2audio_pcm_close(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->private_data = NULL; + return 0; +} + + +/* hw_params callback */ +static int snd_sgio2audio_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + int size = params_buffer_bytes(hw_params); + + /* alloc virtual 'dma' area */ + if (runtime->dma_area) + vfree(runtime->dma_area); + runtime->dma_area = vmalloc(size); + if (runtime->dma_area == NULL) + return -ENOMEM; + runtime->dma_bytes = size; + return 0; +} + +/* hw_free callback */ +static int snd_sgio2audio_pcm_hw_free(struct snd_pcm_substream *substream) +{ + if (substream->runtime->dma_area) + vfree(substream->runtime->dma_area); + substream->runtime->dma_area = NULL; + return 0; +} + +/* prepare callback */ +static int snd_sgio2audio_pcm_prepare(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_sgio2audio_chan *chan = substream->runtime->private_data; + int ch = chan->idx; + unsigned long flags; + + spin_lock_irqsave(&chip->channel[ch].lock, flags); + + /* Setup the pseudo-dma transfer pointers. */ + chip->channel[ch].pos = 0; + chip->channel[ch].size = 0; + chip->channel[ch].substream = substream; + + /* set AD1843 format */ + /* hardware format is always S16_LE */ + switch (substream->stream) { + case SNDRV_PCM_STREAM_PLAYBACK: + ad1843_setup_dac(&chip->ad1843, + ch - 1, + runtime->rate, + SNDRV_PCM_FORMAT_S16_LE, + runtime->channels); + break; + case SNDRV_PCM_STREAM_CAPTURE: + ad1843_setup_adc(&chip->ad1843, + runtime->rate, + SNDRV_PCM_FORMAT_S16_LE, + runtime->channels); + break; + } + spin_unlock_irqrestore(&chip->channel[ch].lock, flags); + return 0; +} + +/* trigger callback */ +static int snd_sgio2audio_pcm_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* start the PCM engine */ + snd_sgio2audio_dma_start(substream); + break; + case SNDRV_PCM_TRIGGER_STOP: + /* stop the PCM engine */ + snd_sgio2audio_dma_stop(substream); + break; + default: + return -EINVAL; + } + return 0; +} + +/* pointer callback */ +static snd_pcm_uframes_t +snd_sgio2audio_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct snd_sgio2audio *chip = snd_pcm_substream_chip(substream); + struct snd_sgio2audio_chan *chan = substream->runtime->private_data; + + /* get the current hardware pointer */ + return bytes_to_frames(substream->runtime, + chip->channel[chan->idx].pos); +} + +/* get the physical page pointer on the given offset */ +static struct page *snd_sgio2audio_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + return vmalloc_to_page(substream->runtime->dma_area + offset); +} + +/* operators */ +static struct snd_pcm_ops snd_sgio2audio_playback1_ops = { + .open = snd_sgio2audio_playback1_open, + .close = snd_sgio2audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sgio2audio_pcm_hw_params, + .hw_free = snd_sgio2audio_pcm_hw_free, + .prepare = snd_sgio2audio_pcm_prepare, + .trigger = snd_sgio2audio_pcm_trigger, + .pointer = snd_sgio2audio_pcm_pointer, + .page = snd_sgio2audio_page, +}; + +static struct snd_pcm_ops snd_sgio2audio_playback2_ops = { + .open = snd_sgio2audio_playback2_open, + .close = snd_sgio2audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sgio2audio_pcm_hw_params, + .hw_free = snd_sgio2audio_pcm_hw_free, + .prepare = snd_sgio2audio_pcm_prepare, + .trigger = snd_sgio2audio_pcm_trigger, + .pointer = snd_sgio2audio_pcm_pointer, + .page = snd_sgio2audio_page, +}; + +static struct snd_pcm_ops snd_sgio2audio_capture_ops = { + .open = snd_sgio2audio_capture_open, + .close = snd_sgio2audio_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_sgio2audio_pcm_hw_params, + .hw_free = snd_sgio2audio_pcm_hw_free, + .prepare = snd_sgio2audio_pcm_prepare, + .trigger = snd_sgio2audio_pcm_trigger, + .pointer = snd_sgio2audio_pcm_pointer, + .page = snd_sgio2audio_page, +}; + +/* + * definitions of capture are omitted here... + */ + +/* create a pcm device */ +static int __devinit snd_sgio2audio_new_pcm(struct snd_sgio2audio *chip) +{ + struct snd_pcm *pcm; + int err; + + /* create first pcm device with one outputs and one input */ + err = snd_pcm_new(chip->card, "SGI O2 Audio", 0, 1, 1, &pcm); + if (err < 0) + return err; + + pcm->private_data = chip; + strcpy(pcm->name, "SGI O2 DAC1"); + + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_sgio2audio_playback1_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_sgio2audio_capture_ops); + + /* create second pcm device with one outputs and no input */ + err = snd_pcm_new(chip->card, "SGI O2 Audio", 1, 1, 0, &pcm); + if (err < 0) + return err; + + pcm->private_data = chip; + strcpy(pcm->name, "SGI O2 DAC2"); + + /* set operators */ + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_sgio2audio_playback2_ops); + + return 0; +} + +static struct { + int idx; + int irq; + irqreturn_t (*isr)(int, void *); + const char *desc; +} snd_sgio2_isr_table[] = { + { + .idx = 0, + .irq = MACEISA_AUDIO1_DMAT_IRQ, + .isr = snd_sgio2audio_dma_in_isr, + .desc = "Capture DMA Channel 0" + }, { + .idx = 0, + .irq = MACEISA_AUDIO1_OF_IRQ, + .isr = snd_sgio2audio_error_isr, + .desc = "Capture Overflow" + }, { + .idx = 1, + .irq = MACEISA_AUDIO2_DMAT_IRQ, + .isr = snd_sgio2audio_dma_out_isr, + .desc = "Playback DMA Channel 1" + }, { + .idx = 1, + .irq = MACEISA_AUDIO2_MERR_IRQ, + .isr = snd_sgio2audio_error_isr, + .desc = "Memory Error Channel 1" + }, { + .idx = 2, + .irq = MACEISA_AUDIO3_DMAT_IRQ, + .isr = snd_sgio2audio_dma_out_isr, + .desc = "Playback DMA Channel 2" + }, { + .idx = 2, + .irq = MACEISA_AUDIO3_MERR_IRQ, + .isr = snd_sgio2audio_error_isr, + .desc = "Memory Error Channel 2" + } +}; + +/* ALSA driver */ + +static int snd_sgio2audio_free(struct snd_sgio2audio *chip) +{ + int i; + + /* reset interface */ + writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control); + udelay(1); + writeq(0, &mace->perif.audio.control); + + /* release IRQ's */ + for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) + free_irq(snd_sgio2_isr_table[i].irq, + &chip->channel[snd_sgio2_isr_table[i].idx]); + + dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE, + chip->ring_base, chip->ring_base_dma); + + /* release card data */ + kfree(chip); + return 0; +} + +static int snd_sgio2audio_dev_free(struct snd_device *device) +{ + struct snd_sgio2audio *chip = device->device_data; + + return snd_sgio2audio_free(chip); +} + +static struct snd_device_ops ops = { + .dev_free = snd_sgio2audio_dev_free, +}; + +static int __devinit snd_sgio2audio_create(struct snd_card *card, + struct snd_sgio2audio **rchip) +{ + struct snd_sgio2audio *chip; + int i, err; + + *rchip = NULL; + + /* check if a codec is attached to the interface */ + /* (Audio or Audio/Video board present) */ + if (!(readq(&mace->perif.audio.control) & AUDIO_CONTROL_CODEC_PRESENT)) + return -ENOENT; + + chip = kzalloc(sizeof(struct snd_sgio2audio), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->card = card; + + chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE, + &chip->ring_base_dma, GFP_USER); + if (chip->ring_base == NULL) { + printk(KERN_ERR + "sgio2audio: could not allocate ring buffers\n"); + kfree(chip); + return -ENOMEM; + } + + spin_lock_init(&chip->ad1843_lock); + + /* initialize channels */ + for (i = 0; i < 3; i++) { + spin_lock_init(&chip->channel[i].lock); + chip->channel[i].idx = i; + } + + /* allocate IRQs */ + for (i = 0; i < ARRAY_SIZE(snd_sgio2_isr_table); i++) { + if (request_irq(snd_sgio2_isr_table[i].irq, + snd_sgio2_isr_table[i].isr, + 0, + snd_sgio2_isr_table[i].desc, + &chip->channel[snd_sgio2_isr_table[i].idx])) { + snd_sgio2audio_free(chip); + printk(KERN_ERR "sgio2audio: cannot allocate irq %d\n", + snd_sgio2_isr_table[i].irq); + return -EBUSY; + } + } + + /* reset the interface */ + writeq(AUDIO_CONTROL_RESET, &mace->perif.audio.control); + udelay(1); + writeq(0, &mace->perif.audio.control); + msleep_interruptible(1); /* give time to recover */ + + /* set ring base */ + writeq(chip->ring_base_dma, &mace->perif.ctrl.ringbase); + + /* attach the AD1843 codec */ + chip->ad1843.read = read_ad1843_reg; + chip->ad1843.write = write_ad1843_reg; + chip->ad1843.chip = chip; + + /* initialize the AD1843 codec */ + err = ad1843_init(&chip->ad1843); + if (err < 0) { + snd_sgio2audio_free(chip); + return err; + } + + err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (err < 0) { + snd_sgio2audio_free(chip); + return err; + } + *rchip = chip; + return 0; +} + +static int __devinit snd_sgio2audio_probe(struct platform_device *pdev) +{ + struct snd_card *card; + struct snd_sgio2audio *chip; + int err; + + card = snd_card_new(index, id, THIS_MODULE, 0); + if (card == NULL) + return -ENOMEM; + + err = snd_sgio2audio_create(card, &chip); + if (err < 0) { + snd_card_free(card); + return err; + } + snd_card_set_dev(card, &pdev->dev); + + err = snd_sgio2audio_new_pcm(chip); + if (err < 0) { + snd_card_free(card); + return err; + } + err = snd_sgio2audio_new_mixer(chip); + if (err < 0) { + snd_card_free(card); + return err; + } + + strcpy(card->driver, "SGI O2 Audio"); + strcpy(card->shortname, "SGI O2 Audio"); + sprintf(card->longname, "%s irq %i-%i", + card->shortname, + MACEISA_AUDIO1_DMAT_IRQ, + MACEISA_AUDIO3_MERR_IRQ); + + err = snd_card_register(card); + if (err < 0) { + snd_card_free(card); + return err; + } + platform_set_drvdata(pdev, card); + return 0; +} + +static int __exit snd_sgio2audio_remove(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + + snd_card_free(card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver sgio2audio_driver = { + .probe = snd_sgio2audio_probe, + .remove = __devexit_p(snd_sgio2audio_remove), + .driver = { + .name = "sgio2audio", + .owner = THIS_MODULE, + } +}; + +static int __init alsa_card_sgio2audio_init(void) +{ + return platform_driver_register(&sgio2audio_driver); +} + +static void __exit alsa_card_sgio2audio_exit(void) +{ + platform_driver_unregister(&sgio2audio_driver); +} + +module_init(alsa_card_sgio2audio_init) +module_exit(alsa_card_sgio2audio_exit) -- cgit v1.2.3-70-g09d2 From fe0a3fe324811385b64790d42079bf534798a0cd Mon Sep 17 00:00:00 2001 From: Jaroslav Kysela Date: Mon, 14 Jul 2008 09:54:43 +0200 Subject: ALSA: Release v1.0.17 Signed-off-by: Jaroslav Kysela --- include/sound/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/sound/version.h b/include/sound/version.h index 29a5b227312..6b78aff273a 100644 --- a/include/sound/version.h +++ b/include/sound/version.h @@ -1,3 +1,3 @@ /* include/version.h */ -#define CONFIG_SND_VERSION "1.0.17rc3" +#define CONFIG_SND_VERSION "1.0.17" #define CONFIG_SND_DATE "" -- cgit v1.2.3-70-g09d2