/*
* soc-dapm.c -- ALSA SoC Dynamic Audio Power Management
*
* Copyright 2005 Wolfson Microelectronics PLC.
* Author: Liam Girdwood
* liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com
*
* 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.
*
* Revision history
* 12th Aug 2005 Initial version.
* 25th Oct 2005 Implemented path power domain.
* 18th Dec 2005 Implemented machine and stream level power domain.
*
* Features:
* o Changes power status of internal codec blocks depending on the
* dynamic configuration of codec internal audio paths and active
* DAC's/ADC's.
* o Platform power domain - can support external components i.e. amps and
* mic/meadphone insertion events.
* o Automatic Mic Bias support
* o Jack insertion power event initiation - e.g. hp insertion will enable
* sinks, dacs, etc
* o Delayed powerdown of audio susbsytem to reduce pops between a quick
* device reopen.
*
* Todo:
* o DAPM power change sequencing - allow for configurable per
* codec sequences.
* o Support for analogue bias optimisation.
* o Support for reduced codec oversampling rates.
* o Support for reduced codec bias currents.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/bitops.h>
#include <linux/platform_device.h>
#include <linux/jiffies.h>
#include <sound/driver.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
/* debug */
#define DAPM_DEBUG 0
#if DAPM_DEBUG
#define dump_dapm(codec, action) dbg_dump_dapm(codec, action)
#define dbg(format, arg...) printk(format, ## arg)
#else
#define dump_dapm(codec, action)
#define dbg(format, arg...)
#endif
#define POP_DEBUG 0
#if POP_DEBUG
#define POP_TIME 500 /* 500 msecs - change if pop debug is too fast */
#define pop_wait(time) schedule_timeout_interruptible(msecs_to_jiffies(time))
#define pop_dbg(format, arg...) printk(format, ## arg); pop_wait(POP_TIME)
#else
#define pop_dbg(format, arg...)
#define pop_wait(time)
#endif
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_micbias, snd_soc_dapm_mic,
snd_soc_dapm_mux, snd_soc_dapm_dac, snd_soc_dapm_mixer, snd_soc_dapm_pga,
snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk, snd_soc_dapm_post
};
static int dapm_down_seq[] = {
snd_soc_dapm_pre, snd_soc_dapm_adc, snd_soc_dapm_hp, snd_soc_dapm_spk,
snd_soc_dapm_pga, snd_soc_dapm_mixer, snd_soc_dapm_dac, snd_soc_dapm_mic,
snd_soc_dapm_micbias, snd_soc_dapm_mux, snd_soc_dapm_post
};
static int dapm_status = 1;
module_param(dapm_status, int, 0);
MODULE_PARM_DESC(dapm_status, "enable DPM sysfs entries");
/* create a new dapm widget */
static struct snd_soc_dapm_widget *dapm_cnew_widget(
const struct snd_soc_dapm_widget *_widget)
{
struct snd_soc_dapm_widget* widget;
widget = kmalloc(sizeof(struct snd_soc_dapm_widget), GFP_KERNEL);
if (!widget)
return NULL;
memcpy(widget, _widget, sizeof(struct snd_soc_dapm_widget));
return widget;
}
/* set up initial codec paths */
static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
struct snd_soc_dapm_path *p, int i)
{
switch (w