Merge remote-tracking branch 'asoc/fix/fsl' into asoc-linus

This commit is contained in:
Mark Brown 2013-09-11 11:17:15 +01:00
commit c34c0d7684
63 changed files with 2174 additions and 5017 deletions

View File

@ -0,0 +1,34 @@
Freescale i.MX audio complex with S/PDIF transceiver
Required properties:
- compatible : "fsl,imx-audio-spdif"
- model : The user-visible name of this sound complex
- spdif-controller : The phandle of the i.MX S/PDIF controller
Optional properties:
- spdif-out : This is a boolean property. If present, the transmitting
function of S/PDIF will be enabled, indicating there's a physical
S/PDIF out connector/jack on the board or it's connecting to some
other IP block, such as an HDMI encoder/display-controller.
- spdif-in : This is a boolean property. If present, the receiving
function of S/PDIF will be enabled, indicating there's a physical
S/PDIF in connector/jack on the board.
* Note: At least one of these two properties should be set in the DT binding.
Example:
sound-spdif {
compatible = "fsl,imx-audio-spdif";
model = "imx-spdif";
spdif-controller = <&spdif>;
spdif-out;
spdif-in;
};

View File

@ -0,0 +1,29 @@
* mvebu (Kirkwood, Dove, Armada 370) audio controller
Required properties:
- compatible: "marvell,mvebu-audio"
- reg: physical base address of the controller and length of memory mapped
region.
- interrupts: list of two irq numbers.
The first irq is used for data flow and the second one is used for errors.
- clocks: one or two phandles.
The first one is mandatory and defines the internal clock.
The second one is optional and defines an external clock.
- clock-names: names associated to the clocks:
"internal" for the internal clock
"extclk" for the external clock
Example:
i2s1: audio-controller@b4000 {
compatible = "marvell,mvebu-audio";
reg = <0xb4000 0x2210>;
interrupts = <21>, <22>;
clocks = <&gate_clk 13>;
clock-names = "internal";
};

View File

@ -244,6 +244,7 @@ STAC9227/9228/9229/927x
5stack-no-fp D965 5stack without front panel 5stack-no-fp D965 5stack without front panel
dell-3stack Dell Dimension E520 dell-3stack Dell Dimension E520
dell-bios Fixes with Dell BIOS setup dell-bios Fixes with Dell BIOS setup
dell-bios-amic Fixes with Dell BIOS setup including analog mic
volknob Fixes with volume-knob widget 0x24 volknob Fixes with volume-knob widget 0x24
auto BIOS setup (default) auto BIOS setup (default)

View File

@ -454,6 +454,8 @@ The generic parser supports the following hints:
- need_dac_fix (bool): limits the DACs depending on the channel count - need_dac_fix (bool): limits the DACs depending on the channel count
- primary_hp (bool): probe headphone jacks as the primary outputs; - primary_hp (bool): probe headphone jacks as the primary outputs;
default true default true
- multi_io (bool): try probing multi-I/O config (e.g. shared
line-in/surround, mic/clfe jacks)
- multi_cap_vol (bool): provide multiple capture volumes - multi_cap_vol (bool): provide multiple capture volumes
- inv_dmic_split (bool): provide split internal mic volume/switch for - inv_dmic_split (bool): provide split internal mic volume/switch for
phase-inverted digital mics phase-inverted digital mics

View File

@ -7676,6 +7676,17 @@ F: include/sound/
F: include/uapi/sound/ F: include/uapi/sound/
F: sound/ F: sound/
SOUND - COMPRESSED AUDIO
M: Vinod Koul <vinod.koul@intel.com>
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
S: Supported
F: Documentation/sound/alsa/compress_offload.txt
F: include/sound/compress_driver.h
F: include/uapi/sound/compress_*
F: sound/core/compress_offload.c
F: sound/soc/soc-compress.c
SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC) SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
M: Liam Girdwood <lgirdwood@gmail.com> M: Liam Girdwood <lgirdwood@gmail.com>
M: Mark Brown <broonie@kernel.org> M: Mark Brown <broonie@kernel.org>

View File

@ -82,7 +82,8 @@ static int s3c_dma_config(unsigned ch, struct samsung_dma_config *param)
static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param) static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param)
{ {
struct cb_data *data; struct cb_data *data;
int len = (param->cap == DMA_CYCLIC) ? param->period : param->len; dma_addr_t pos = param->buf;
dma_addr_t end = param->buf + param->len;
list_for_each_entry(data, &dma_list, node) list_for_each_entry(data, &dma_list, node)
if (data->ch == ch) if (data->ch == ch)
@ -94,7 +95,15 @@ static int s3c_dma_prepare(unsigned ch, struct samsung_dma_prep *param)
data->fp_param = param->fp_param; data->fp_param = param->fp_param;
} }
s3c2410_dma_enqueue(ch, (void *)data, param->buf, len); if (param->cap != DMA_CYCLIC) {
s3c2410_dma_enqueue(ch, (void *)data, param->buf, param->len);
return 0;
}
while (pos < end) {
s3c2410_dma_enqueue(ch, (void *)data, pos, param->period);
pos += param->period;
}
return 0; return 0;
} }

View File

@ -27,6 +27,7 @@
#include <linux/rwsem.h> /* struct rw_semaphore */ #include <linux/rwsem.h> /* struct rw_semaphore */
#include <linux/pm.h> /* pm_message_t */ #include <linux/pm.h> /* pm_message_t */
#include <linux/stringify.h> #include <linux/stringify.h>
#include <linux/printk.h>
/* number of supported soundcards */ /* number of supported soundcards */
#ifdef CONFIG_SND_DYNAMIC_MINORS #ifdef CONFIG_SND_DYNAMIC_MINORS
@ -375,6 +376,11 @@ void __snd_printk(unsigned int level, const char *file, int line,
*/ */
#define snd_BUG() WARN(1, "BUG?\n") #define snd_BUG() WARN(1, "BUG?\n")
/**
* Suppress high rates of output when CONFIG_SND_DEBUG is enabled.
*/
#define snd_printd_ratelimit() printk_ratelimit()
/** /**
* snd_BUG_ON - debugging check macro * snd_BUG_ON - debugging check macro
* @cond: condition to evaluate * @cond: condition to evaluate
@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {}
unlikely(__ret_warn_on); \ unlikely(__ret_warn_on); \
}) })
static inline bool snd_printd_ratelimit(void) { return false; }
#endif /* CONFIG_SND_DEBUG */ #endif /* CONFIG_SND_DEBUG */
#ifdef CONFIG_SND_DEBUG_VERBOSE #ifdef CONFIG_SND_DEBUG_VERBOSE

View File

@ -413,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
struct snd_soc_dapm_widget *sink); struct snd_soc_dapm_widget *sink);
/* dapm path setup */ /* dapm path setup */
int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm); int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm); void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm, int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route, int num); const struct snd_soc_dapm_route *route, int num);

View File

@ -697,7 +697,6 @@ struct snd_soc_codec {
unsigned int probed:1; /* Codec has been probed */ unsigned int probed:1; /* Codec has been probed */
unsigned int ac97_registered:1; /* Codec has been AC97 registered */ unsigned int ac97_registered:1; /* Codec has been AC97 registered */
unsigned int ac97_created:1; /* Codec has been created by SoC */ unsigned int ac97_created:1; /* Codec has been created by SoC */
unsigned int sysfs_registered:1; /* codec has been sysfs registered */
unsigned int cache_init:1; /* codec cache has been initialized */ unsigned int cache_init:1; /* codec cache has been initialized */
unsigned int using_regmap:1; /* using regmap access */ unsigned int using_regmap:1; /* using regmap access */
u32 cache_only; /* Suppress writes to hardware */ u32 cache_only; /* Suppress writes to hardware */
@ -705,7 +704,6 @@ struct snd_soc_codec {
/* codec IO */ /* codec IO */
void *control_data; /* codec control (i2c/3wire) data */ void *control_data; /* codec control (i2c/3wire) data */
enum snd_soc_control_type control_type;
hw_write_t hw_write; hw_write_t hw_write;
unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int); unsigned int (*hw_read)(struct snd_soc_codec *, unsigned int);
unsigned int (*read)(struct snd_soc_codec *, unsigned int); unsigned int (*read)(struct snd_soc_codec *, unsigned int);
@ -724,7 +722,6 @@ struct snd_soc_codec {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_codec_root; struct dentry *debugfs_codec_root;
struct dentry *debugfs_reg; struct dentry *debugfs_reg;
struct dentry *debugfs_dapm;
#endif #endif
}; };
@ -849,7 +846,6 @@ struct snd_soc_platform {
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_platform_root; struct dentry *debugfs_platform_root;
struct dentry *debugfs_dapm;
#endif #endif
}; };
@ -934,6 +930,10 @@ struct snd_soc_dai_link {
/* machine stream operations */ /* machine stream operations */
const struct snd_soc_ops *ops; const struct snd_soc_ops *ops;
const struct snd_soc_compr_ops *compr_ops; const struct snd_soc_compr_ops *compr_ops;
/* For unidirectional dai links */
bool playback_only;
bool capture_only;
}; };
struct snd_soc_codec_conf { struct snd_soc_codec_conf {

View File

@ -111,7 +111,7 @@ struct hdspm_ltc {
enum hdspm_ltc_input_format input_format; enum hdspm_ltc_input_format input_format;
}; };
#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl) #define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc)
/** /**
* The status data reflects the device's current state * The status data reflects the device's current state

View File

@ -184,7 +184,7 @@ static void xrun(struct snd_pcm_substream *substream)
do { \ do { \
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \ if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
xrun_log_show(substream); \ xrun_log_show(substream); \
if (printk_ratelimit()) { \ if (snd_printd_ratelimit()) { \
snd_printd("PCM: " fmt, ##args); \ snd_printd("PCM: " fmt, ##args); \
} \ } \
dump_stack_on_xrun(substream); \ dump_stack_on_xrun(substream); \
@ -342,7 +342,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
return -EPIPE; return -EPIPE;
} }
if (pos >= runtime->buffer_size) { if (pos >= runtime->buffer_size) {
if (printk_ratelimit()) { if (snd_printd_ratelimit()) {
char name[16]; char name[16];
snd_pcm_debug_name(substream, name, sizeof(name)); snd_pcm_debug_name(substream, name, sizeof(name));
xrun_log_show(substream); xrun_log_show(substream);

View File

@ -1022,7 +1022,7 @@ static void dummy_proc_write(struct snd_info_entry *entry,
if (i >= ARRAY_SIZE(fields)) if (i >= ARRAY_SIZE(fields))
continue; continue;
snd_info_get_str(item, ptr, sizeof(item)); snd_info_get_str(item, ptr, sizeof(item));
if (strict_strtoull(item, 0, &val)) if (kstrtoull(item, 0, &val))
continue; continue;
if (fields[i].size == sizeof(int)) if (fields[i].size == sizeof(int))
*get_dummy_int_ptr(dummy, fields[i].offset) = val; *get_dummy_int_ptr(dummy, fields[i].offset) = val;

View File

@ -49,7 +49,6 @@ struct fwspk {
struct snd_card *card; struct snd_card *card;
struct fw_unit *unit; struct fw_unit *unit;
const struct device_info *device_info; const struct device_info *device_info;
struct snd_pcm_substream *pcm;
struct mutex mutex; struct mutex mutex;
struct cmp_connection connection; struct cmp_connection connection;
struct amdtp_out_stream stream; struct amdtp_out_stream stream;
@ -363,8 +362,7 @@ static int fwspk_create_pcm(struct fwspk *fwspk)
return err; return err;
pcm->private_data = fwspk; pcm->private_data = fwspk;
strcpy(pcm->name, fwspk->device_info->short_name); strcpy(pcm->name, fwspk->device_info->short_name);
fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
fwspk->pcm->ops = &ops;
return 0; return 0;
} }

View File

@ -443,8 +443,7 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
for (i = 0; i < 8; ++i) for (i = 0; i < 8; ++i)
iwave[i] = snd_gf1_peek(gus, bank_pos + i); iwave[i] = snd_gf1_peek(gus, bank_pos + i);
#ifdef CONFIG_SND_DEBUG_ROM #ifdef CONFIG_SND_DEBUG_ROM
printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos, printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave);
8, iwave);
#endif #endif
if (strncmp(iwave, "INTRWAVE", 8)) if (strncmp(iwave, "INTRWAVE", 8))
continue; /* first check */ continue; /* first check */

View File

@ -557,7 +557,6 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
unsigned long flags; unsigned long flags;
int err = 0, n = 0; int err = 0, n = 0;
struct dma_buffparms *dmap = adev->dmap_in; struct dma_buffparms *dmap = adev->dmap_in;
int go;
if (!(adev->open_mode & OPEN_READ)) if (!(adev->open_mode & OPEN_READ))
return -EIO; return -EIO;
@ -584,7 +583,7 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
spin_unlock_irqrestore(&dmap->lock,flags); spin_unlock_irqrestore(&dmap->lock,flags);
return -EAGAIN; return -EAGAIN;
} }
if ((go = adev->go)) if (adev->go)
timeout = dmabuf_timeout(dmap); timeout = dmabuf_timeout(dmap);
spin_unlock_irqrestore(&dmap->lock,flags); spin_unlock_irqrestore(&dmap->lock,flags);

View File

@ -152,14 +152,9 @@ config SND_HDA_CODEC_HDMI
This module is automatically loaded at probing. This module is automatically loaded at probing.
config SND_HDA_I915 config SND_HDA_I915
bool "Build Display HD-audio controller/codec power well support for i915 cards" bool
default y
depends on DRM_I915 depends on DRM_I915
help
Say Y here to include full HDMI and DisplayPort HD-audio controller/codec
power-well support for Intel Haswell graphics cards based on the i915 driver.
Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise
the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode.
config SND_HDA_CODEC_CIRRUS config SND_HDA_CODEC_CIRRUS
bool "Build Cirrus Logic codec support" bool "Build Cirrus Logic codec support"

View File

@ -666,6 +666,64 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
} }
EXPORT_SYMBOL_HDA(snd_hda_get_conn_index); EXPORT_SYMBOL_HDA(snd_hda_get_conn_index);
/* return DEVLIST_LEN parameter of the given widget */
static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
{
unsigned int wcaps = get_wcaps(codec, nid);
unsigned int parm;
if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
get_wcaps_type(wcaps) != AC_WID_PIN)
return 0;
parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN);
if (parm == -1 && codec->bus->rirb_error)
parm = 0;
return parm & AC_DEV_LIST_LEN_MASK;
}
/**
* snd_hda_get_devices - copy device list without cache
* @codec: the HDA codec
* @nid: NID of the pin to parse
* @dev_list: device list array
* @max_devices: max. number of devices to store
*
* Copy the device list. This info is dynamic and so not cached.
* Currently called only from hda_proc.c, so not exported.
*/
int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
u8 *dev_list, int max_devices)
{
unsigned int parm;
int i, dev_len, devices;
parm = get_num_devices(codec, nid);
if (!parm) /* not multi-stream capable */
return 0;
dev_len = parm + 1;
dev_len = dev_len < max_devices ? dev_len : max_devices;
devices = 0;
while (devices < dev_len) {
parm = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DEVICE_LIST, devices);
if (parm == -1 && codec->bus->rirb_error)
break;
for (i = 0; i < 8; i++) {
dev_list[devices] = (u8)parm;
parm >>= 4;
devices++;
if (devices >= dev_len)
break;
}
}
return devices;
}
/** /**
* snd_hda_queue_unsol_event - add an unsolicited event to queue * snd_hda_queue_unsol_event - add an unsolicited event to queue
* @bus: the BUS * @bus: the BUS
@ -1216,11 +1274,13 @@ static void hda_jackpoll_work(struct work_struct *work)
{ {
struct hda_codec *codec = struct hda_codec *codec =
container_of(work, struct hda_codec, jackpoll_work.work); container_of(work, struct hda_codec, jackpoll_work.work);
if (!codec->jackpoll_interval)
return;
snd_hda_jack_set_dirty_all(codec); snd_hda_jack_set_dirty_all(codec);
snd_hda_jack_poll_all(codec); snd_hda_jack_poll_all(codec);
if (!codec->jackpoll_interval)
return;
queue_delayed_work(codec->bus->workq, &codec->jackpoll_work, queue_delayed_work(codec->bus->workq, &codec->jackpoll_work,
codec->jackpoll_interval); codec->jackpoll_interval);
} }

View File

@ -94,6 +94,8 @@ enum {
#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32 #define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33 #define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34 #define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
#define AC_VERB_GET_DEVICE_SEL 0xf35
#define AC_VERB_GET_DEVICE_LIST 0xf36
/* /*
* SET verbs * SET verbs
@ -133,6 +135,7 @@ enum {
#define AC_VERB_SET_HDMI_DIP_XMIT 0x732 #define AC_VERB_SET_HDMI_DIP_XMIT 0x732
#define AC_VERB_SET_HDMI_CP_CTRL 0x733 #define AC_VERB_SET_HDMI_CP_CTRL 0x733
#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734 #define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
#define AC_VERB_SET_DEVICE_SEL 0x735
/* /*
* Parameter IDs * Parameter IDs
@ -154,6 +157,7 @@ enum {
#define AC_PAR_GPIO_CAP 0x11 #define AC_PAR_GPIO_CAP 0x11
#define AC_PAR_AMP_OUT_CAP 0x12 #define AC_PAR_AMP_OUT_CAP 0x12
#define AC_PAR_VOL_KNB_CAP 0x13 #define AC_PAR_VOL_KNB_CAP 0x13
#define AC_PAR_DEVLIST_LEN 0x15
#define AC_PAR_HDMI_LPCM_CAP 0x20 #define AC_PAR_HDMI_LPCM_CAP 0x20
/* /*
@ -251,6 +255,11 @@ enum {
#define AC_UNSOL_RES_TAG_SHIFT 26 #define AC_UNSOL_RES_TAG_SHIFT 26
#define AC_UNSOL_RES_SUBTAG (0x1f<<21) #define AC_UNSOL_RES_SUBTAG (0x1f<<21)
#define AC_UNSOL_RES_SUBTAG_SHIFT 21 #define AC_UNSOL_RES_SUBTAG_SHIFT 21
#define AC_UNSOL_RES_DE (0x3f<<15) /* Device Entry
* (for DP1.2 MST)
*/
#define AC_UNSOL_RES_DE_SHIFT 15
#define AC_UNSOL_RES_IA (1<<2) /* Inactive (for DP1.2 MST) */
#define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */ #define AC_UNSOL_RES_ELDV (1<<1) /* ELD Data valid (for HDMI) */
#define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */ #define AC_UNSOL_RES_PD (1<<0) /* pinsense detect */
#define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */ #define AC_UNSOL_RES_CP_STATE (1<<1) /* content protection */
@ -352,6 +361,10 @@ enum {
#define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */ #define AC_LPCMCAP_44K (1<<30) /* 44.1kHz support */
#define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */ #define AC_LPCMCAP_44K_MS (1<<31) /* 44.1kHz-multiplies support */
/* Display pin's device list length */
#define AC_DEV_LIST_LEN_MASK 0x3f
#define AC_MAX_DEV_LIST_LEN 64
/* /*
* Control Parameters * Control Parameters
*/ */
@ -460,6 +473,11 @@ enum {
#define AC_DEFCFG_PORT_CONN (0x3<<30) #define AC_DEFCFG_PORT_CONN (0x3<<30)
#define AC_DEFCFG_PORT_CONN_SHIFT 30 #define AC_DEFCFG_PORT_CONN_SHIFT 30
/* Display pin's device list entry */
#define AC_DE_PD (1<<0)
#define AC_DE_ELDV (1<<1)
#define AC_DE_IA (1<<2)
/* device device types (0x0-0xf) */ /* device device types (0x0-0xf) */
enum { enum {
AC_JACK_LINE_OUT, AC_JACK_LINE_OUT,
@ -885,6 +903,7 @@ struct hda_codec {
unsigned int pcm_format_first:1; /* PCM format must be set first */ unsigned int pcm_format_first:1; /* PCM format must be set first */
unsigned int epss:1; /* supporting EPSS? */ unsigned int epss:1; /* supporting EPSS? */
unsigned int cached_write:1; /* write only to caches */ unsigned int cached_write:1; /* write only to caches */
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
#ifdef CONFIG_PM #ifdef CONFIG_PM
unsigned int power_on :1; /* current (global) power-state */ unsigned int power_on :1; /* current (global) power-state */
unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */ unsigned int d3_stop_clk:1; /* support D3 operation without BCLK */
@ -972,6 +991,8 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
const hda_nid_t *list); const hda_nid_t *list);
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux, int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
hda_nid_t nid, int recursive); hda_nid_t nid, int recursive);
int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
u8 *dev_list, int max_devices);
int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid, int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
u32 *ratesp, u64 *formatsp, unsigned int *bpsp); u32 *ratesp, u64 *formatsp, unsigned int *bpsp);

View File

@ -142,6 +142,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "primary_hp"); val = snd_hda_get_bool_hint(codec, "primary_hp");
if (val >= 0) if (val >= 0)
spec->no_primary_hp = !val; spec->no_primary_hp = !val;
val = snd_hda_get_bool_hint(codec, "multi_io");
if (val >= 0)
spec->no_multi_io = !val;
val = snd_hda_get_bool_hint(codec, "multi_cap_vol"); val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
if (val >= 0) if (val >= 0)
spec->multi_cap_vol = !!val; spec->multi_cap_vol = !!val;
@ -813,6 +816,8 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol); struct snd_ctl_elem_value *ucontrol);
static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol);
enum { enum {
HDA_CTL_WIDGET_VOL, HDA_CTL_WIDGET_VOL,
@ -830,7 +835,13 @@ static const struct snd_kcontrol_new control_templates[] = {
.put = hda_gen_mixer_mute_put, /* replaced */ .put = hda_gen_mixer_mute_put, /* replaced */
.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0), .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
}, },
HDA_BIND_MUTE(NULL, 0, 0, 0), {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_hda_mixer_amp_switch_info,
.get = snd_hda_mixer_bind_switch_get,
.put = hda_gen_bind_mute_put, /* replaced */
.private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
},
}; };
/* add dynamic controls from template */ /* add dynamic controls from template */
@ -937,8 +948,8 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
} }
/* playback mute control with the software mute bit check */ /* playback mute control with the software mute bit check */
static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol, static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol) struct snd_ctl_elem_value *ucontrol)
{ {
struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
@ -949,10 +960,22 @@ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] &= enabled; ucontrol->value.integer.value[0] &= enabled;
ucontrol->value.integer.value[1] &= enabled; ucontrol->value.integer.value[1] &= enabled;
} }
}
static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
sync_auto_mute_bits(kcontrol, ucontrol);
return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol); return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
} }
static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
sync_auto_mute_bits(kcontrol, ucontrol);
return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol);
}
/* any ctl assigned to the path with the given index? */ /* any ctl assigned to the path with the given index? */
static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type) static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
{ {
@ -1541,7 +1564,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
cfg->speaker_pins, cfg->speaker_pins,
spec->multiout.extra_out_nid, spec->multiout.extra_out_nid,
spec->speaker_paths); spec->speaker_paths);
if (fill_mio_first && cfg->line_outs == 1 && if (!spec->no_multi_io &&
fill_mio_first && cfg->line_outs == 1 &&
cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = fill_multi_ios(codec, cfg->line_out_pins[0], true); err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
if (!err) if (!err)
@ -1554,7 +1578,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
spec->private_dac_nids, spec->out_paths, spec->private_dac_nids, spec->out_paths,
spec->main_out_badness); spec->main_out_badness);
if (fill_mio_first && if (!spec->no_multi_io && fill_mio_first &&
cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
/* try to fill multi-io first */ /* try to fill multi-io first */
err = fill_multi_ios(codec, cfg->line_out_pins[0], false); err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
@ -1582,7 +1606,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
return err; return err;
badness += err; badness += err;
} }
if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) { if (!spec->no_multi_io &&
cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
err = fill_multi_ios(codec, cfg->line_out_pins[0], false); err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
if (err < 0) if (err < 0)
return err; return err;
@ -1600,7 +1625,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
check_aamix_out_path(codec, spec->speaker_paths[0]); check_aamix_out_path(codec, spec->speaker_paths[0]);
} }
if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT) if (!spec->no_multi_io &&
cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2) if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
spec->multi_ios = 1; /* give badness */ spec->multi_ios = 1; /* give badness */
@ -3724,7 +3750,8 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
/* check each pin in the given array; returns true if any of them is plugged */ /* check each pin in the given array; returns true if any of them is plugged */
static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins) static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
{ {
int i, present = 0; int i;
bool present = false;
for (i = 0; i < num_pins; i++) { for (i = 0; i < num_pins; i++) {
hda_nid_t nid = pins[i]; hda_nid_t nid = pins[i];
@ -3733,14 +3760,15 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
/* don't detect pins retasked as inputs */ /* don't detect pins retasked as inputs */
if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN) if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
continue; continue;
present |= snd_hda_jack_detect(codec, nid); if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
present = true;
} }
return present; return present;
} }
/* standard HP/line-out auto-mute helper */ /* standard HP/line-out auto-mute helper */
static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins, static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
bool mute) int *paths, bool mute)
{ {
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
int i; int i;
@ -3752,10 +3780,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
break; break;
if (spec->auto_mute_via_amp) { if (spec->auto_mute_via_amp) {
struct nid_path *path;
hda_nid_t mute_nid;
path = snd_hda_get_path_from_idx(codec, paths[i]);
if (!path)
continue;
mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]);
if (!mute_nid)
continue;
if (mute) if (mute)
spec->mute_bits |= (1ULL << nid); spec->mute_bits |= (1ULL << mute_nid);
else else
spec->mute_bits &= ~(1ULL << nid); spec->mute_bits &= ~(1ULL << mute_nid);
set_pin_eapd(codec, nid, !mute); set_pin_eapd(codec, nid, !mute);
continue; continue;
} }
@ -3786,14 +3823,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
void snd_hda_gen_update_outputs(struct hda_codec *codec) void snd_hda_gen_update_outputs(struct hda_codec *codec)
{ {
struct hda_gen_spec *spec = codec->spec; struct hda_gen_spec *spec = codec->spec;
int *paths;
int on; int on;
/* Control HP pins/amps depending on master_mute state; /* Control HP pins/amps depending on master_mute state;
* in general, HP pins/amps control should be enabled in all cases, * in general, HP pins/amps control should be enabled in all cases,
* but currently set only for master_mute, just to be safe * but currently set only for master_mute, just to be safe
*/ */
if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
paths = spec->out_paths;
else
paths = spec->hp_paths;
do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins), do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
spec->autocfg.hp_pins, spec->master_mute); spec->autocfg.hp_pins, paths, spec->master_mute);
if (!spec->automute_speaker) if (!spec->automute_speaker)
on = 0; on = 0;
@ -3801,8 +3843,12 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
on = spec->hp_jack_present | spec->line_jack_present; on = spec->hp_jack_present | spec->line_jack_present;
on |= spec->master_mute; on |= spec->master_mute;
spec->speaker_muted = on; spec->speaker_muted = on;
if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
paths = spec->out_paths;
else
paths = spec->speaker_paths;
do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins), do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
spec->autocfg.speaker_pins, on); spec->autocfg.speaker_pins, paths, on);
/* toggle line-out mutes if needed, too */ /* toggle line-out mutes if needed, too */
/* if LO is a copy of either HP or Speaker, don't need to handle it */ /* if LO is a copy of either HP or Speaker, don't need to handle it */
@ -3815,8 +3861,9 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
on = spec->hp_jack_present; on = spec->hp_jack_present;
on |= spec->master_mute; on |= spec->master_mute;
spec->line_out_muted = on; spec->line_out_muted = on;
paths = spec->out_paths;
do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins), do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
spec->autocfg.line_out_pins, on); spec->autocfg.line_out_pins, paths, on);
} }
EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs); EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
@ -3887,7 +3934,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
/* don't detect pins retasked as outputs */ /* don't detect pins retasked as outputs */
if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN) if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
continue; continue;
if (snd_hda_jack_detect(codec, pin)) { if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) {
mux_select(codec, 0, spec->am_entry[i].idx); mux_select(codec, 0, spec->am_entry[i].idx);
return; return;
} }

View File

@ -220,6 +220,7 @@ struct hda_gen_spec {
unsigned int hp_mic:1; /* Allow HP as a mic-in */ unsigned int hp_mic:1; /* Allow HP as a mic-in */
unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */ unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */ unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
unsigned int no_multi_io:1; /* Don't try multi I/O config */
unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */ unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */ unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
unsigned int own_eapd_ctl:1; /* set EAPD by own function */ unsigned int own_eapd_ctl:1; /* set EAPD by own function */

View File

@ -295,7 +295,7 @@ static ssize_t type##_store(struct device *dev, \
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \ struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
struct hda_codec *codec = hwdep->private_data; \ struct hda_codec *codec = hwdep->private_data; \
unsigned long val; \ unsigned long val; \
int err = strict_strtoul(buf, 0, &val); \ int err = kstrtoul(buf, 0, &val); \
if (err < 0) \ if (err < 0) \
return err; \ return err; \
codec->type = val; \ codec->type = val; \
@ -654,7 +654,7 @@ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
p = snd_hda_get_hint(codec, key); p = snd_hda_get_hint(codec, key);
if (!p) if (!p)
ret = -ENOENT; ret = -ENOENT;
else if (strict_strtoul(p, 0, &val)) else if (kstrtoul(p, 0, &val))
ret = -EINVAL; ret = -EINVAL;
else { else {
*valp = val; *valp = val;
@ -751,7 +751,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
struct hda_codec **codecp) \ struct hda_codec **codecp) \
{ \ { \
unsigned long val; \ unsigned long val; \
if (!strict_strtoul(buf, 0, &val)) \ if (!kstrtoul(buf, 0, &val)) \
(*codecp)->name = val; \ (*codecp)->name = val; \
} }

View File

@ -1160,7 +1160,7 @@ static int azx_reset(struct azx *chip, int full_reset)
goto __skip; goto __skip;
/* clear STATESTS */ /* clear STATESTS */
azx_writeb(chip, STATESTS, STATESTS_INT_MASK); azx_writew(chip, STATESTS, STATESTS_INT_MASK);
/* reset controller */ /* reset controller */
azx_enter_link_reset(chip); azx_enter_link_reset(chip);
@ -1242,7 +1242,7 @@ static void azx_int_clear(struct azx *chip)
} }
/* clear STATESTS */ /* clear STATESTS */
azx_writeb(chip, STATESTS, STATESTS_INT_MASK); azx_writew(chip, STATESTS, STATESTS_INT_MASK);
/* clear rirb status */ /* clear rirb status */
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK); azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
@ -1451,8 +1451,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
#if 0 #if 0
/* clear state status int */ /* clear state status int */
if (azx_readb(chip, STATESTS) & 0x04) if (azx_readw(chip, STATESTS) & 0x04)
azx_writeb(chip, STATESTS, 0x04); azx_writew(chip, STATESTS, 0x04);
#endif #endif
spin_unlock(&chip->reg_lock); spin_unlock(&chip->reg_lock);
@ -2971,6 +2971,10 @@ static int azx_runtime_suspend(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev); struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data; struct azx *chip = card->private_data;
/* enable controller wake up event */
azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
STATESTS_INT_MASK);
azx_stop_chip(chip); azx_stop_chip(chip);
azx_enter_link_reset(chip); azx_enter_link_reset(chip);
azx_clear_irq_pending(chip); azx_clear_irq_pending(chip);
@ -2983,11 +2987,31 @@ static int azx_runtime_resume(struct device *dev)
{ {
struct snd_card *card = dev_get_drvdata(dev); struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip = card->private_data; struct azx *chip = card->private_data;
struct hda_bus *bus;
struct hda_codec *codec;
int status;
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
hda_display_power(true); hda_display_power(true);
/* Read STATESTS before controller reset */
status = azx_readw(chip, STATESTS);
azx_init_pci(chip); azx_init_pci(chip);
azx_init_chip(chip, 1); azx_init_chip(chip, 1);
bus = chip->bus;
if (status && bus) {
list_for_each_entry(codec, &bus->codec_list, list)
if (status & (1 << codec->addr))
queue_delayed_work(codec->bus->workq,
&codec->jackpoll_work, codec->jackpoll_interval);
}
/* disable controller Wake Up event*/
azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
~STATESTS_INT_MASK);
return 0; return 0;
} }
@ -3831,11 +3855,13 @@ static int azx_probe_continue(struct azx *chip)
/* Request power well for Haswell HDA controller and codec */ /* Request power well for Haswell HDA controller and codec */
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) { if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
#ifdef CONFIG_SND_HDA_I915
err = hda_i915_init(); err = hda_i915_init();
if (err < 0) { if (err < 0) {
snd_printk(KERN_ERR SFX "Error request power-well from i915\n"); snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
goto out_free; goto out_free;
} }
#endif
hda_display_power(true); hda_display_power(true);
} }

View File

@ -194,18 +194,24 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
EXPORT_SYMBOL_HDA(snd_hda_pin_sense); EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
/** /**
* snd_hda_jack_detect - query pin Presence Detect status * snd_hda_jack_detect_state - query pin Presence Detect status
* @codec: the CODEC to sense * @codec: the CODEC to sense
* @nid: the pin NID to sense * @nid: the pin NID to sense
* *
* Query and return the pin's Presence Detect status. * Query and return the pin's Presence Detect status, as either
* HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
*/ */
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid) int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
{ {
u32 sense = snd_hda_pin_sense(codec, nid); struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
return get_jack_plug_state(sense); if (jack && jack->phantom_jack)
return HDA_JACK_PHANTOM;
else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
return HDA_JACK_PRESENT;
else
return HDA_JACK_NOT_PRESENT;
} }
EXPORT_SYMBOL_HDA(snd_hda_jack_detect); EXPORT_SYMBOL_HDA(snd_hda_jack_detect_state);
/** /**
* snd_hda_jack_detect_enable - enable the jack-detection * snd_hda_jack_detect_enable - enable the jack-detection
@ -247,8 +253,8 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid, int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid) hda_nid_t gating_nid)
{ {
struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid); struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid); struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
if (!gated || !gating) if (!gated || !gating)
return -EINVAL; return -EINVAL;

View File

@ -75,7 +75,18 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
hda_nid_t gating_nid); hda_nid_t gating_nid);
u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid); u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
/* the jack state returned from snd_hda_jack_detect_state() */
enum {
HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
};
int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid);
static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
{
return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT;
}
bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid); bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);

View File

@ -582,6 +582,36 @@ static void print_gpio(struct snd_info_buffer *buffer,
print_nid_array(buffer, codec, nid, &codec->nids); print_nid_array(buffer, codec, nid, &codec->nids);
} }
static void print_device_list(struct snd_info_buffer *buffer,
struct hda_codec *codec, hda_nid_t nid)
{
int i, curr = -1;
u8 dev_list[AC_MAX_DEV_LIST_LEN];
int devlist_len;
devlist_len = snd_hda_get_devices(codec, nid, dev_list,
AC_MAX_DEV_LIST_LEN);
snd_iprintf(buffer, " Devices: %d\n", devlist_len);
if (devlist_len <= 0)
return;
curr = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_DEVICE_SEL, 0);
for (i = 0; i < devlist_len; i++) {
if (i == curr)
snd_iprintf(buffer, " *");
else
snd_iprintf(buffer, " ");
snd_iprintf(buffer,
"Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i,
!!(dev_list[i] & AC_DE_PD),
!!(dev_list[i] & AC_DE_ELDV),
!!(dev_list[i] & AC_DE_IA));
}
}
static void print_codec_info(struct snd_info_entry *entry, static void print_codec_info(struct snd_info_entry *entry,
struct snd_info_buffer *buffer) struct snd_info_buffer *buffer)
{ {
@ -751,6 +781,9 @@ static void print_codec_info(struct snd_info_entry *entry,
(wid_caps & AC_WCAP_DELAY) >> (wid_caps & AC_WCAP_DELAY) >>
AC_WCAP_DELAY_SHIFT); AC_WCAP_DELAY_SHIFT);
if (wid_type == AC_WID_PIN && codec->dp_mst)
print_device_list(buffer, codec, nid);
if (wid_caps & AC_WCAP_CONN_LIST) if (wid_caps & AC_WCAP_CONN_LIST)
print_conn_list(buffer, codec, nid, wid_type, print_conn_list(buffer, codec, nid, wid_type,
conn, conn_len); conn, conn_len);

File diff suppressed because it is too large Load Diff

View File

@ -66,6 +66,8 @@ struct conexant_spec {
hda_nid_t eapds[4]; hda_nid_t eapds[4];
bool dynamic_eapd; bool dynamic_eapd;
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
#ifdef ENABLE_CXT_STATIC_QUIRKS #ifdef ENABLE_CXT_STATIC_QUIRKS
const struct snd_kcontrol_new *mixers[5]; const struct snd_kcontrol_new *mixers[5];
int num_mixers; int num_mixers;
@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec)
snd_hda_gen_init(codec); snd_hda_gen_init(codec);
if (!spec->dynamic_eapd) if (!spec->dynamic_eapd)
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true); cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
return 0; return 0;
} }
@ -3224,6 +3229,8 @@ enum {
CXT_PINCFG_LEMOTE_A1205, CXT_PINCFG_LEMOTE_A1205,
CXT_FIXUP_STEREO_DMIC, CXT_FIXUP_STEREO_DMIC,
CXT_FIXUP_INC_MIC_BOOST, CXT_FIXUP_INC_MIC_BOOST,
CXT_FIXUP_HEADPHONE_MIC_PIN,
CXT_FIXUP_HEADPHONE_MIC,
}; };
static void cxt_fixup_stereo_dmic(struct hda_codec *codec, static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec,
(0 << AC_AMPCAP_MUTE_SHIFT)); (0 << AC_AMPCAP_MUTE_SHIFT));
} }
static void cxt_update_headset_mode(struct hda_codec *codec)
{
/* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
int i;
bool mic_mode = false;
struct conexant_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->gen.autocfg;
hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
for (i = 0; i < cfg->num_inputs; i++)
if (cfg->inputs[i].pin == mux_pin) {
mic_mode = !!cfg->inputs[i].is_headphone_mic;
break;
}
if (mic_mode) {
snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
spec->gen.hp_jack_present = false;
} else {
snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
}
snd_hda_gen_update_outputs(codec);
}
static void cxt_update_headset_mode_hook(struct hda_codec *codec,
struct snd_ctl_elem_value *ucontrol)
{
cxt_update_headset_mode(codec);
}
static void cxt_fixup_headphone_mic(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct conexant_spec *spec = codec->spec;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
break;
case HDA_FIXUP_ACT_PROBE:
spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
spec->gen.automute_hook = cxt_update_headset_mode;
break;
case HDA_FIXUP_ACT_INIT:
cxt_update_headset_mode(codec);
break;
}
}
/* ThinkPad X200 & co with cxt5051 */ /* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = { static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */ { 0x16, 0x042140ff }, /* HP (seq# overridden) */
@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC, .type = HDA_FIXUP_FUNC,
.v.func = cxt5066_increase_mic_boost, .v.func = cxt5066_increase_mic_boost,
}, },
[CXT_FIXUP_HEADPHONE_MIC_PIN] = {
.type = HDA_FIXUP_PINS,
.chained = true,
.chain_id = CXT_FIXUP_HEADPHONE_MIC,
.v.pins = (const struct hda_pintbl[]) {
{ 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
{ }
}
},
[CXT_FIXUP_HEADPHONE_MIC] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_headphone_mic,
},
}; };
static const struct snd_pci_quirk cxt5051_fixups[] = { static const struct snd_pci_quirk cxt5051_fixups[] = {
@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
static const struct snd_pci_quirk cxt5066_fixups[] = { static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0); err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
spec->parse_flags);
if (err < 0) if (err < 0)
goto error; goto error;
@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
codec->bus->allow_bus_reset = 1; codec->bus->allow_bus_reset = 1;
} }
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
return 0; return 0;
error: error:

View File

@ -959,6 +959,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
int pin_nid; int pin_nid;
int pin_idx; int pin_idx;
struct hda_jack_tbl *jack; struct hda_jack_tbl *jack;
int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
jack = snd_hda_jack_tbl_get_from_tag(codec, tag); jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
if (!jack) if (!jack)
@ -967,8 +968,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
jack->jack_dirty = 1; jack->jack_dirty = 1;
_snd_printd(SND_PR_VERBOSE, _snd_printd(SND_PR_VERBOSE,
"HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n", "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
codec->addr, pin_nid, codec->addr, pin_nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
!!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV)); !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
pin_idx = pin_nid_to_pin_index(spec, pin_nid); pin_idx = pin_nid_to_pin_index(spec, pin_nid);
@ -1992,8 +1993,10 @@ static int patch_generic_hdmi(struct hda_codec *codec)
return -EINVAL; return -EINVAL;
} }
codec->patch_ops = generic_hdmi_patch_ops; codec->patch_ops = generic_hdmi_patch_ops;
if (codec->vendor_id == 0x80862807) if (codec->vendor_id == 0x80862807) {
codec->patch_ops.set_power_state = haswell_set_power_state; codec->patch_ops.set_power_state = haswell_set_power_state;
codec->dp_mst = true;
}
generic_hdmi_init_per_pins(codec); generic_hdmi_init_per_pins(codec);

View File

@ -282,6 +282,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
{ {
alc_auto_setup_eapd(codec, false); alc_auto_setup_eapd(codec, false);
msleep(200); msleep(200);
snd_hda_shutup_pins(codec);
} }
/* generic EAPD initialization */ /* generic EAPD initialization */
@ -826,7 +827,8 @@ static inline void alc_shutup(struct hda_codec *codec)
if (spec && spec->shutup) if (spec && spec->shutup)
spec->shutup(codec); spec->shutup(codec);
snd_hda_shutup_pins(codec); else
snd_hda_shutup_pins(codec);
} }
#define alc_free snd_hda_gen_free #define alc_free snd_hda_gen_free
@ -1853,8 +1855,10 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
const struct hda_fixup *fix, int action) const struct hda_fixup *fix, int action)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PRE_PROBE) if (action == HDA_FIXUP_ACT_PRE_PROBE) {
spec->gen.no_primary_hp = 1; spec->gen.no_primary_hp = 1;
spec->gen.no_multi_io = 1;
}
} }
static const struct hda_fixup alc882_fixups[] = { static const struct hda_fixup alc882_fixups[] = {
@ -2533,6 +2537,7 @@ enum {
ALC269_TYPE_ALC269VD, ALC269_TYPE_ALC269VD,
ALC269_TYPE_ALC280, ALC269_TYPE_ALC280,
ALC269_TYPE_ALC282, ALC269_TYPE_ALC282,
ALC269_TYPE_ALC283,
ALC269_TYPE_ALC284, ALC269_TYPE_ALC284,
ALC269_TYPE_ALC286, ALC269_TYPE_ALC286,
}; };
@ -2558,6 +2563,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC269VB: case ALC269_TYPE_ALC269VB:
case ALC269_TYPE_ALC269VD: case ALC269_TYPE_ALC269VD:
case ALC269_TYPE_ALC282: case ALC269_TYPE_ALC282:
case ALC269_TYPE_ALC283:
case ALC269_TYPE_ALC286: case ALC269_TYPE_ALC286:
ssids = alc269_ssids; ssids = alc269_ssids;
break; break;
@ -2583,15 +2589,81 @@ static void alc269_shutup(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
if (spec->codec_variant != ALC269_TYPE_ALC269VB)
return;
if (spec->codec_variant == ALC269_TYPE_ALC269VB) if (spec->codec_variant == ALC269_TYPE_ALC269VB)
alc269vb_toggle_power_output(codec, 0); alc269vb_toggle_power_output(codec, 0);
if (spec->codec_variant == ALC269_TYPE_ALC269VB && if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
(alc_get_coef0(codec) & 0x00ff) == 0x018) { (alc_get_coef0(codec) & 0x00ff) == 0x018) {
msleep(150); msleep(150);
} }
snd_hda_shutup_pins(codec);
}
static void alc283_init(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
bool hp_pin_sense;
int val;
if (!hp_pin)
return;
hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
/* Index 0x43 Direct Drive HP AMP LPM Control 1 */
/* Headphone capless set to high power mode */
alc_write_coef_idx(codec, 0x43, 0x9004);
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp_pin_sense)
msleep(85);
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
if (hp_pin_sense)
msleep(85);
/* Index 0x46 Combo jack auto switch control 2 */
/* 3k pull low control for Headset jack. */
val = alc_read_coef_idx(codec, 0x46);
alc_write_coef_idx(codec, 0x46, val & ~(3 << 12));
/* Headphone capless set to normal mode */
alc_write_coef_idx(codec, 0x43, 0x9614);
}
static void alc283_shutup(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
bool hp_pin_sense;
int val;
if (!hp_pin) {
alc269_shutup(codec);
return;
}
hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
alc_write_coef_idx(codec, 0x43, 0x9004);
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
if (hp_pin_sense)
msleep(85);
snd_hda_codec_write(codec, hp_pin, 0,
AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
val = alc_read_coef_idx(codec, 0x46);
alc_write_coef_idx(codec, 0x46, val | (3 << 12));
if (hp_pin_sense)
msleep(85);
snd_hda_shutup_pins(codec);
alc_write_coef_idx(codec, 0x43, 0x9614);
} }
static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg, static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
@ -2722,6 +2794,7 @@ static int alc269_resume(struct hda_codec *codec)
hda_call_check_power_status(codec, 0x01); hda_call_check_power_status(codec, 0x01);
if (spec->has_alc5505_dsp) if (spec->has_alc5505_dsp)
alc5505_dsp_resume(codec); alc5505_dsp_resume(codec);
return 0; return 0;
} }
#endif /* CONFIG_PM */ #endif /* CONFIG_PM */
@ -3261,6 +3334,28 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
alc_fixup_headset_mode(codec, fix, action); alc_fixup_headset_mode(codec, fix, action);
} }
/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
static int find_ext_mic_pin(struct hda_codec *codec)
{
struct alc_spec *spec = codec->spec;
struct auto_pin_cfg *cfg = &spec->gen.autocfg;
hda_nid_t nid;
unsigned int defcfg;
int i;
for (i = 0; i < cfg->num_inputs; i++) {
if (cfg->inputs[i].type != AUTO_PIN_MIC)
continue;
nid = cfg->inputs[i].pin;
defcfg = snd_hda_codec_get_pincfg(codec, nid);
if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
continue;
return nid;
}
return 0;
}
static void alc271_hp_gate_mic_jack(struct hda_codec *codec, static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
const struct hda_fixup *fix, const struct hda_fixup *fix,
int action) int action)
@ -3268,11 +3363,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
if (action == HDA_FIXUP_ACT_PROBE) { if (action == HDA_FIXUP_ACT_PROBE) {
if (snd_BUG_ON(!spec->gen.am_entry[1].pin || int mic_pin = find_ext_mic_pin(codec);
!spec->gen.autocfg.hp_pins[0])) int hp_pin = spec->gen.autocfg.hp_pins[0];
if (snd_BUG_ON(!mic_pin || !hp_pin))
return; return;
snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin, snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
spec->gen.autocfg.hp_pins[0]);
} }
} }
@ -3308,6 +3404,45 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
} }
} }
static void alc283_hp_automute_hook(struct hda_codec *codec,
struct hda_jack_tbl *jack)
{
struct alc_spec *spec = codec->spec;
int vref;
msleep(200);
snd_hda_gen_hp_automute(codec, jack);
vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
msleep(600);
snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
vref);
}
static void alc283_chromebook_caps(struct hda_codec *codec)
{
snd_hda_override_wcaps(codec, 0x03, 0);
}
static void alc283_fixup_chromebook(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
struct alc_spec *spec = codec->spec;
int val;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
alc283_chromebook_caps(codec);
spec->gen.hp_automute_hook = alc283_hp_automute_hook;
/* MIC2-VREF control */
/* Set to manual mode */
val = alc_read_coef_idx(codec, 0x06);
alc_write_coef_idx(codec, 0x06, val & ~0x000c);
break;
}
}
enum { enum {
ALC269_FIXUP_SONY_VAIO, ALC269_FIXUP_SONY_VAIO,
ALC275_FIXUP_SONY_VAIO_GPIO2, ALC275_FIXUP_SONY_VAIO_GPIO2,
@ -3344,6 +3479,7 @@ enum {
ALC269_FIXUP_ACER_AC700, ALC269_FIXUP_ACER_AC700,
ALC269_FIXUP_LIMIT_INT_MIC_BOOST, ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
ALC269VB_FIXUP_ORDISSIMO_EVE2, ALC269VB_FIXUP_ORDISSIMO_EVE2,
ALC283_FIXUP_CHROME_BOOK,
}; };
static const struct hda_fixup alc269_fixups[] = { static const struct hda_fixup alc269_fixups[] = {
@ -3595,11 +3731,20 @@ static const struct hda_fixup alc269_fixups[] = {
{ } { }
}, },
}, },
[ALC283_FIXUP_CHROME_BOOK] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc283_fixup_chromebook,
},
}; };
static const struct snd_pci_quirk alc269_fixup_tbl[] = { static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC), SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@ -3637,6 +3782,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED), SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1), SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED), SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@ -3655,11 +3801,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ), SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO), SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK), SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE), SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
@ -3670,8 +3811,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK), SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K), SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD), SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */ SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
@ -3840,11 +3989,15 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0290: case 0x10ec0290:
spec->codec_variant = ALC269_TYPE_ALC280; spec->codec_variant = ALC269_TYPE_ALC280;
break; break;
case 0x10ec0233:
case 0x10ec0282: case 0x10ec0282:
case 0x10ec0283:
spec->codec_variant = ALC269_TYPE_ALC282; spec->codec_variant = ALC269_TYPE_ALC282;
break; break;
case 0x10ec0233:
case 0x10ec0283:
spec->codec_variant = ALC269_TYPE_ALC283;
spec->shutup = alc283_shutup;
spec->init_hook = alc283_init;
break;
case 0x10ec0284: case 0x10ec0284:
case 0x10ec0292: case 0x10ec0292:
spec->codec_variant = ALC269_TYPE_ALC284; spec->codec_variant = ALC269_TYPE_ALC284;
@ -3872,7 +4025,8 @@ static int patch_alc269(struct hda_codec *codec)
codec->patch_ops.suspend = alc269_suspend; codec->patch_ops.suspend = alc269_suspend;
codec->patch_ops.resume = alc269_resume; codec->patch_ops.resume = alc269_resume;
#endif #endif
spec->shutup = alc269_shutup; if (!spec->shutup)
spec->shutup = alc269_shutup;
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE); snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);

View File

@ -158,6 +158,7 @@ enum {
STAC_D965_VERBS, STAC_D965_VERBS,
STAC_DELL_3ST, STAC_DELL_3ST,
STAC_DELL_BIOS, STAC_DELL_BIOS,
STAC_DELL_BIOS_AMIC,
STAC_DELL_BIOS_SPDIF, STAC_DELL_BIOS_SPDIF,
STAC_927X_DELL_DMIC, STAC_927X_DELL_DMIC,
STAC_927X_VOLKNOB, STAC_927X_VOLKNOB,
@ -3231,8 +3232,6 @@ static const struct hda_fixup stac927x_fixups[] = {
[STAC_DELL_BIOS] = { [STAC_DELL_BIOS] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) { .v.pins = (const struct hda_pintbl[]) {
/* configure the analog microphone on some laptops */
{ 0x0c, 0x90a79130 },
/* correct the front output jack as a hp out */ /* correct the front output jack as a hp out */
{ 0x0f, 0x0221101f }, { 0x0f, 0x0221101f },
/* correct the front input jack as a mic */ /* correct the front input jack as a mic */
@ -3242,6 +3241,16 @@ static const struct hda_fixup stac927x_fixups[] = {
.chained = true, .chained = true,
.chain_id = STAC_927X_DELL_DMIC, .chain_id = STAC_927X_DELL_DMIC,
}, },
[STAC_DELL_BIOS_AMIC] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
/* configure the analog microphone on some laptops */
{ 0x0c, 0x90a79130 },
{}
},
.chained = true,
.chain_id = STAC_DELL_BIOS,
},
[STAC_DELL_BIOS_SPDIF] = { [STAC_DELL_BIOS_SPDIF] = {
.type = HDA_FIXUP_PINS, .type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) { .v.pins = (const struct hda_pintbl[]) {
@ -3270,6 +3279,7 @@ static const struct hda_model_fixup stac927x_models[] = {
{ .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" }, { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
{ .id = STAC_DELL_3ST, .name = "dell-3stack" }, { .id = STAC_DELL_3ST, .name = "dell-3stack" },
{ .id = STAC_DELL_BIOS, .name = "dell-bios" }, { .id = STAC_DELL_BIOS, .name = "dell-bios" },
{ .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
{ .id = STAC_927X_VOLKNOB, .name = "volknob" }, { .id = STAC_927X_VOLKNOB, .name = "volknob" },
{} {}
}; };

View File

@ -207,9 +207,9 @@ static void vt1708_stop_hp_work(struct hda_codec *codec)
return; return;
if (spec->hp_work_active) { if (spec->hp_work_active) {
snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1); snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
codec->jackpoll_interval = 0;
cancel_delayed_work_sync(&codec->jackpoll_work); cancel_delayed_work_sync(&codec->jackpoll_work);
spec->hp_work_active = false; spec->hp_work_active = false;
codec->jackpoll_interval = 0;
} }
} }

View File

@ -28,6 +28,7 @@
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/vmalloc.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/info.h> #include <sound/info.h>
@ -198,6 +199,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
#define RME96_AD1852_VOL_BITS 14 #define RME96_AD1852_VOL_BITS 14
#define RME96_AD1855_VOL_BITS 10 #define RME96_AD1855_VOL_BITS 10
/* Defines for snd_rme96_trigger */
#define RME96_TB_START_PLAYBACK 1
#define RME96_TB_START_CAPTURE 2
#define RME96_TB_STOP_PLAYBACK 4
#define RME96_TB_STOP_CAPTURE 8
#define RME96_TB_RESET_PLAYPOS 16
#define RME96_TB_RESET_CAPTUREPOS 32
#define RME96_TB_CLEAR_PLAYBACK_IRQ 64
#define RME96_TB_CLEAR_CAPTURE_IRQ 128
#define RME96_RESUME_PLAYBACK (RME96_TB_START_PLAYBACK)
#define RME96_RESUME_CAPTURE (RME96_TB_START_CAPTURE)
#define RME96_RESUME_BOTH (RME96_RESUME_PLAYBACK \
| RME96_RESUME_CAPTURE)
#define RME96_START_PLAYBACK (RME96_TB_START_PLAYBACK \
| RME96_TB_RESET_PLAYPOS)
#define RME96_START_CAPTURE (RME96_TB_START_CAPTURE \
| RME96_TB_RESET_CAPTUREPOS)
#define RME96_START_BOTH (RME96_START_PLAYBACK \
| RME96_START_CAPTURE)
#define RME96_STOP_PLAYBACK (RME96_TB_STOP_PLAYBACK \
| RME96_TB_CLEAR_PLAYBACK_IRQ)
#define RME96_STOP_CAPTURE (RME96_TB_STOP_CAPTURE \
| RME96_TB_CLEAR_CAPTURE_IRQ)
#define RME96_STOP_BOTH (RME96_STOP_PLAYBACK \
| RME96_STOP_CAPTURE)
struct rme96 { struct rme96 {
spinlock_t lock; spinlock_t lock;
@ -214,6 +240,13 @@ struct rme96 {
u8 rev; /* card revision number */ u8 rev; /* card revision number */
#ifdef CONFIG_PM
u32 playback_pointer;
u32 capture_pointer;
void *playback_suspend_buffer;
void *capture_suspend_buffer;
#endif
struct snd_pcm_substream *playback_substream; struct snd_pcm_substream *playback_substream;
struct snd_pcm_substream *capture_substream; struct snd_pcm_substream *capture_substream;
@ -344,6 +377,8 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
{ {
.info = (SNDRV_PCM_INFO_MMAP_IOMEM | .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE), SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | .formats = (SNDRV_PCM_FMTBIT_S16_LE |
@ -373,6 +408,8 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info =
{ {
.info = (SNDRV_PCM_INFO_MMAP_IOMEM | .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE), SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | .formats = (SNDRV_PCM_FMTBIT_S16_LE |
@ -402,6 +439,8 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info =
{ {
.info = (SNDRV_PCM_INFO_MMAP_IOMEM | .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE), SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | .formats = (SNDRV_PCM_FMTBIT_S16_LE |
@ -427,6 +466,8 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info =
{ {
.info = (SNDRV_PCM_INFO_MMAP_IOMEM | .info = (SNDRV_PCM_INFO_MMAP_IOMEM |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_SYNC_START |
SNDRV_PCM_INFO_RESUME |
SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_PAUSE), SNDRV_PCM_INFO_PAUSE),
.formats = (SNDRV_PCM_FMTBIT_S16_LE | .formats = (SNDRV_PCM_FMTBIT_S16_LE |
@ -1045,54 +1086,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
} }
static void static void
snd_rme96_playback_start(struct rme96 *rme96, snd_rme96_trigger(struct rme96 *rme96,
int from_pause) int op)
{ {
if (!from_pause) { if (op & RME96_TB_RESET_PLAYPOS)
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
} if (op & RME96_TB_RESET_CAPTUREPOS)
rme96->wcreg |= RME96_WCR_START;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
static void
snd_rme96_capture_start(struct rme96 *rme96,
int from_pause)
{
if (!from_pause) {
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) {
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
if (rme96->rcreg & RME96_RCR_IRQ)
writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
} }
if (op & RME96_TB_CLEAR_CAPTURE_IRQ) {
rme96->wcreg |= RME96_WCR_START_2; rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
if (rme96->rcreg & RME96_RCR_IRQ_2)
writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
}
if (op & RME96_TB_START_PLAYBACK)
rme96->wcreg |= RME96_WCR_START;
if (op & RME96_TB_STOP_PLAYBACK)
rme96->wcreg &= ~RME96_WCR_START;
if (op & RME96_TB_START_CAPTURE)
rme96->wcreg |= RME96_WCR_START_2;
if (op & RME96_TB_STOP_CAPTURE)
rme96->wcreg &= ~RME96_WCR_START_2;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER); writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
} }
static void
snd_rme96_playback_stop(struct rme96 *rme96)
{
/*
* Check if there is an unconfirmed IRQ, if so confirm it, or else
* the hardware will not stop generating interrupts
*/
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
if (rme96->rcreg & RME96_RCR_IRQ) {
writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
}
rme96->wcreg &= ~RME96_WCR_START;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
static void
snd_rme96_capture_stop(struct rme96 *rme96)
{
rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
if (rme96->rcreg & RME96_RCR_IRQ_2) {
writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
}
rme96->wcreg &= ~RME96_WCR_START_2;
writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
}
static irqreturn_t static irqreturn_t
snd_rme96_interrupt(int irq, snd_rme96_interrupt(int irq,
@ -1155,6 +1177,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_set_sync(substream);
spin_lock_irq(&rme96->lock); spin_lock_irq(&rme96->lock);
if (rme96->playback_substream != NULL) { if (rme96->playback_substream != NULL) {
spin_unlock_irq(&rme96->lock); spin_unlock_irq(&rme96->lock);
@ -1191,6 +1214,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_set_sync(substream);
runtime->hw = snd_rme96_capture_spdif_info; runtime->hw = snd_rme96_capture_spdif_info;
if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG && if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
(rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0) (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0)
@ -1222,6 +1246,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_set_sync(substream);
spin_lock_irq(&rme96->lock); spin_lock_irq(&rme96->lock);
if (rme96->playback_substream != NULL) { if (rme96->playback_substream != NULL) {
spin_unlock_irq(&rme96->lock); spin_unlock_irq(&rme96->lock);
@ -1253,6 +1278,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
snd_pcm_set_sync(substream);
runtime->hw = snd_rme96_capture_adat_info; runtime->hw = snd_rme96_capture_adat_info;
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) { if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
/* makes no sense to use analog input. Note that analog /* makes no sense to use analog input. Note that analog
@ -1288,7 +1314,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock); spin_lock_irq(&rme96->lock);
if (RME96_ISPLAYING(rme96)) { if (RME96_ISPLAYING(rme96)) {
snd_rme96_playback_stop(rme96); snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
} }
rme96->playback_substream = NULL; rme96->playback_substream = NULL;
rme96->playback_periodsize = 0; rme96->playback_periodsize = 0;
@ -1309,7 +1335,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock); spin_lock_irq(&rme96->lock);
if (RME96_ISRECORDING(rme96)) { if (RME96_ISRECORDING(rme96)) {
snd_rme96_capture_stop(rme96); snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
} }
rme96->capture_substream = NULL; rme96->capture_substream = NULL;
rme96->capture_periodsize = 0; rme96->capture_periodsize = 0;
@ -1324,7 +1350,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock); spin_lock_irq(&rme96->lock);
if (RME96_ISPLAYING(rme96)) { if (RME96_ISPLAYING(rme96)) {
snd_rme96_playback_stop(rme96); snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
} }
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS); writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
spin_unlock_irq(&rme96->lock); spin_unlock_irq(&rme96->lock);
@ -1338,7 +1364,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
spin_lock_irq(&rme96->lock); spin_lock_irq(&rme96->lock);
if (RME96_ISRECORDING(rme96)) { if (RME96_ISRECORDING(rme96)) {
snd_rme96_capture_stop(rme96); snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
} }
writel(0, rme96->iobase + RME96_IO_RESET_REC_POS); writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
spin_unlock_irq(&rme96->lock); spin_unlock_irq(&rme96->lock);
@ -1350,41 +1376,55 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
int cmd) int cmd)
{ {
struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
bool sync;
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) == rme96)
snd_pcm_trigger_done(s, substream);
}
sync = (rme96->playback_substream && rme96->capture_substream) &&
(rme96->playback_substream->group ==
rme96->capture_substream->group);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
if (!RME96_ISPLAYING(rme96)) { if (!RME96_ISPLAYING(rme96)) {
if (substream != rme96->playback_substream) { if (substream != rme96->playback_substream)
return -EBUSY; return -EBUSY;
} snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
snd_rme96_playback_start(rme96, 0); : RME96_START_PLAYBACK);
} }
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
if (RME96_ISPLAYING(rme96)) { if (RME96_ISPLAYING(rme96)) {
if (substream != rme96->playback_substream) { if (substream != rme96->playback_substream)
return -EBUSY; return -EBUSY;
} snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
snd_rme96_playback_stop(rme96); : RME96_STOP_PLAYBACK);
} }
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (RME96_ISPLAYING(rme96)) { if (RME96_ISPLAYING(rme96))
snd_rme96_playback_stop(rme96); snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
} : RME96_STOP_PLAYBACK);
break; break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!RME96_ISPLAYING(rme96)) { if (!RME96_ISPLAYING(rme96))
snd_rme96_playback_start(rme96, 1); snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
} : RME96_RESUME_PLAYBACK);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
return 0; return 0;
} }
@ -1393,38 +1433,51 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
int cmd) int cmd)
{ {
struct rme96 *rme96 = snd_pcm_substream_chip(substream); struct rme96 *rme96 = snd_pcm_substream_chip(substream);
struct snd_pcm_substream *s;
bool sync;
snd_pcm_group_for_each_entry(s, substream) {
if (snd_pcm_substream_chip(s) == rme96)
snd_pcm_trigger_done(s, substream);
}
sync = (rme96->playback_substream && rme96->capture_substream) &&
(rme96->playback_substream->group ==
rme96->capture_substream->group);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
if (!RME96_ISRECORDING(rme96)) { if (!RME96_ISRECORDING(rme96)) {
if (substream != rme96->capture_substream) { if (substream != rme96->capture_substream)
return -EBUSY; return -EBUSY;
} snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
snd_rme96_capture_start(rme96, 0); : RME96_START_CAPTURE);
} }
break; break;
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
if (RME96_ISRECORDING(rme96)) { if (RME96_ISRECORDING(rme96)) {
if (substream != rme96->capture_substream) { if (substream != rme96->capture_substream)
return -EBUSY; return -EBUSY;
} snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
snd_rme96_capture_stop(rme96); : RME96_STOP_CAPTURE);
} }
break; break;
case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
if (RME96_ISRECORDING(rme96)) { if (RME96_ISRECORDING(rme96))
snd_rme96_capture_stop(rme96); snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
} : RME96_STOP_CAPTURE);
break; break;
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
if (!RME96_ISRECORDING(rme96)) { if (!RME96_ISRECORDING(rme96))
snd_rme96_capture_start(rme96, 1); snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
} : RME96_RESUME_CAPTURE);
break; break;
default: default:
return -EINVAL; return -EINVAL;
} }
@ -1505,8 +1558,7 @@ snd_rme96_free(void *private_data)
return; return;
} }
if (rme96->irq >= 0) { if (rme96->irq >= 0) {
snd_rme96_playback_stop(rme96); snd_rme96_trigger(rme96, RME96_STOP_BOTH);
snd_rme96_capture_stop(rme96);
rme96->areg &= ~RME96_AR_DAC_EN; rme96->areg &= ~RME96_AR_DAC_EN;
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG); writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
free_irq(rme96->irq, (void *)rme96); free_irq(rme96->irq, (void *)rme96);
@ -1520,6 +1572,10 @@ snd_rme96_free(void *private_data)
pci_release_regions(rme96->pci); pci_release_regions(rme96->pci);
rme96->port = 0; rme96->port = 0;
} }
#ifdef CONFIG_PM
vfree(rme96->playback_suspend_buffer);
vfree(rme96->capture_suspend_buffer);
#endif
pci_disable_device(rme96->pci); pci_disable_device(rme96->pci);
} }
@ -1606,8 +1662,7 @@ snd_rme96_create(struct rme96 *rme96)
rme96->capture_periodsize = 0; rme96->capture_periodsize = 0;
/* make sure playback/capture is stopped, if by some reason active */ /* make sure playback/capture is stopped, if by some reason active */
snd_rme96_playback_stop(rme96); snd_rme96_trigger(rme96, RME96_STOP_BOTH);
snd_rme96_capture_stop(rme96);
/* set default values in registers */ /* set default values in registers */
rme96->wcreg = rme96->wcreg =
@ -2319,6 +2374,87 @@ snd_rme96_create_switches(struct snd_card *card,
* Card initialisation * Card initialisation
*/ */
#ifdef CONFIG_PM
static int
snd_rme96_suspend(struct pci_dev *pci,
pm_message_t state)
{
struct snd_card *card = pci_get_drvdata(pci);
struct rme96 *rme96 = card->private_data;
snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
snd_pcm_suspend(rme96->playback_substream);
snd_pcm_suspend(rme96->capture_substream);
/* save capture & playback pointers */
rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
& RME96_RCR_AUDIO_ADDR_MASK;
rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS)
& RME96_RCR_AUDIO_ADDR_MASK;
/* save playback and capture buffers */
memcpy_fromio(rme96->playback_suspend_buffer,
rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE);
memcpy_fromio(rme96->capture_suspend_buffer,
rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE);
/* disable the DAC */
rme96->areg &= ~RME96_AR_DAC_EN;
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
pci_disable_device(pci);
pci_save_state(pci);
return 0;
}
static int
snd_rme96_resume(struct pci_dev *pci)
{
struct snd_card *card = pci_get_drvdata(pci);
struct rme96 *rme96 = card->private_data;
pci_restore_state(pci);
if (pci_enable_device(pci) < 0) {
printk(KERN_ERR "rme96: pci_enable_device failed, disabling device\n");
snd_card_disconnect(card);
return -EIO;
}
/* reset playback and record buffer pointers */
writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS
+ rme96->playback_pointer);
writel(0, rme96->iobase + RME96_IO_SET_REC_POS
+ rme96->capture_pointer);
/* restore playback and capture buffers */
memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER,
rme96->playback_suspend_buffer, RME96_BUFFER_SIZE);
memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER,
rme96->capture_suspend_buffer, RME96_BUFFER_SIZE);
/* reset the ADC */
writel(rme96->areg | RME96_AR_PD2,
rme96->iobase + RME96_IO_ADDITIONAL_REG);
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
/* reset and enable DAC, restore analog volume */
snd_rme96_reset_dac(rme96);
rme96->areg |= RME96_AR_DAC_EN;
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
if (RME96_HAS_ANALOG_OUT(rme96)) {
usleep_range(3000, 10000);
snd_rme96_apply_dac_volume(rme96);
}
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
return 0;
}
#endif
static void snd_rme96_card_free(struct snd_card *card) static void snd_rme96_card_free(struct snd_card *card)
{ {
snd_rme96_free(card->private_data); snd_rme96_free(card->private_data);
@ -2355,6 +2491,23 @@ snd_rme96_probe(struct pci_dev *pci,
return err; return err;
} }
#ifdef CONFIG_PM
rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
if (!rme96->playback_suspend_buffer) {
snd_printk(KERN_ERR
"Failed to allocate playback suspend buffer!\n");
snd_card_free(card);
return -ENOMEM;
}
rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
if (!rme96->capture_suspend_buffer) {
snd_printk(KERN_ERR
"Failed to allocate capture suspend buffer!\n");
snd_card_free(card);
return -ENOMEM;
}
#endif
strcpy(card->driver, "Digi96"); strcpy(card->driver, "Digi96");
switch (rme96->pci->device) { switch (rme96->pci->device) {
case PCI_DEVICE_ID_RME_DIGI96: case PCI_DEVICE_ID_RME_DIGI96:
@ -2397,6 +2550,10 @@ static struct pci_driver rme96_driver = {
.id_table = snd_rme96_ids, .id_table = snd_rme96_ids,
.probe = snd_rme96_probe, .probe = snd_rme96_probe,
.remove = snd_rme96_remove, .remove = snd_rme96_remove,
#ifdef CONFIG_PM
.suspend = snd_rme96_suspend,
.resume = snd_rme96_resume,
#endif
}; };
module_pci_driver(rme96_driver); module_pci_driver(rme96_driver);

File diff suppressed because it is too large Load Diff

View File

@ -408,7 +408,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
return 0; return 0;
fail_put_lrclk: fail_put_lrclk:
dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk); clk_put(info->lrclk);
fail_put_sclk: fail_put_sclk:
clk_put(info->sclk); clk_put(info->sclk);
@ -423,7 +422,6 @@ static int ep93xx_i2s_remove(struct platform_device *pdev)
struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev); struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
clk_put(info->lrclk); clk_put(info->lrclk);
clk_put(info->sclk); clk_put(info->sclk);
clk_put(info->mclk); clk_put(info->mclk);

View File

@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = {
{"DMIC AIF", NULL, "DMic"}, {"DMIC AIF", NULL, "DMic"},
}; };
static int dmic_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = &codec->dapm;
snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
ARRAY_SIZE(dmic_dapm_widgets));
snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
snd_soc_dapm_new_widgets(dapm);
return 0;
}
static struct snd_soc_codec_driver soc_dmic = { static struct snd_soc_codec_driver soc_dmic = {
.probe = dmic_probe, .dapm_widgets = dmic_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
.dapm_routes = intercon,
.num_dapm_routes = ARRAY_SIZE(intercon),
}; };
static int dmic_dev_probe(struct platform_device *pdev) static int dmic_dev_probe(struct platform_device *pdev)

View File

@ -50,8 +50,6 @@ static const struct regmap_range_cfg rt5640_ranges[] = {
static struct reg_default init_list[] = { static struct reg_default init_list[] = {
{RT5640_PR_BASE + 0x3d, 0x3600}, {RT5640_PR_BASE + 0x3d, 0x3600},
{RT5640_PR_BASE + 0x1c, 0x0D21},
{RT5640_PR_BASE + 0x1b, 0x0000},
{RT5640_PR_BASE + 0x12, 0x0aa8}, {RT5640_PR_BASE + 0x12, 0x0aa8},
{RT5640_PR_BASE + 0x14, 0x0aaa}, {RT5640_PR_BASE + 0x14, 0x0aaa},
{RT5640_PR_BASE + 0x20, 0x6110}, {RT5640_PR_BASE + 0x20, 0x6110},
@ -384,15 +382,11 @@ static const SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5640_snd_controls[] = { static const struct snd_kcontrol_new rt5640_snd_controls[] = {
/* Speaker Output Volume */ /* Speaker Output Volume */
SOC_DOUBLE("Speaker Playback Switch", RT5640_SPK_VOL,
RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL, SOC_DOUBLE("Speaker Channel Switch", RT5640_SPK_VOL,
RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL, SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv), RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
/* Headphone Output Volume */ /* Headphone Output Volume */
SOC_DOUBLE("HP Playback Switch", RT5640_HP_VOL,
RT5640_L_MUTE_SFT, RT5640_R_MUTE_SFT, 1, 1),
SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL, SOC_DOUBLE("HP Channel Switch", RT5640_HP_VOL,
RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1), RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL, SOC_DOUBLE_TLV("HP Playback Volume", RT5640_HP_VOL,
@ -737,6 +731,22 @@ static const struct snd_kcontrol_new rt5640_mono_mix[] = {
RT5640_M_BST1_MM_SFT, 1, 1), RT5640_M_BST1_MM_SFT, 1, 1),
}; };
static const struct snd_kcontrol_new spk_l_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
RT5640_L_MUTE_SFT, 1, 1);
static const struct snd_kcontrol_new spk_r_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_SPK_VOL,
RT5640_R_MUTE_SFT, 1, 1);
static const struct snd_kcontrol_new hp_l_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
RT5640_L_MUTE_SFT, 1, 1);
static const struct snd_kcontrol_new hp_r_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5640_HP_VOL,
RT5640_R_MUTE_SFT, 1, 1);
/* Stereo ADC source */ /* Stereo ADC source */
static const char * const rt5640_stereo_adc1_src[] = { static const char * const rt5640_stereo_adc1_src[] = {
"DIG MIX", "ADC" "DIG MIX", "ADC"
@ -868,33 +878,6 @@ static const SOC_ENUM_SINGLE_DECL(
static const struct snd_kcontrol_new rt5640_sdi_mux = static const struct snd_kcontrol_new rt5640_sdi_mux =
SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum); SOC_DAPM_ENUM("SDI select", rt5640_sdi_sel_enum);
static int spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1,
0x0001, 0x0001);
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c,
0xf000, 0xf000);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE + 0x1c,
0xf000, 0x0000);
regmap_update_bits(rt5640->regmap, RT5640_PWR_DIG1,
0x0001, 0x0000);
break;
default:
return 0;
}
return 0;
}
static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w, static int rt5640_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event) struct snd_kcontrol *kcontrol, int event)
{ {
@ -943,6 +926,117 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w,
return 0; return 0;
} }
void hp_amp_power_on(struct snd_soc_codec *codec)
{
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
/* depop parameters */
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
RT5640_CHPUMP_INT_REG1, 0x0700, 0x0200);
regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
RT5640_DEPOP_MASK, RT5640_DEPOP_MAN);
regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
RT5640_HP_CP_MASK | RT5640_HP_SG_MASK | RT5640_HP_CB_MASK,
RT5640_HP_CP_PU | RT5640_HP_SG_DIS | RT5640_HP_CB_PU);
regmap_write(rt5640->regmap, RT5640_PR_BASE + RT5640_HP_DCC_INT1,
0x9f00);
/* headphone amp power on */
regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2, 0);
regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
RT5640_PWR_HA,
RT5640_PWR_HA);
usleep_range(10000, 15000);
regmap_update_bits(rt5640->regmap, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2 ,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
}
static void rt5640_pmu_depop(struct snd_soc_codec *codec)
{
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M2,
RT5640_DEPOP_MASK | RT5640_DIG_DP_MASK,
RT5640_DEPOP_AUTO | RT5640_DIG_DP_EN);
regmap_update_bits(rt5640->regmap, RT5640_CHARGE_PUMP,
RT5640_PM_HP_MASK, RT5640_PM_HP_HV);
regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M3,
RT5640_CP_FQ1_MASK | RT5640_CP_FQ2_MASK | RT5640_CP_FQ3_MASK,
(RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ1_SFT) |
(RT5640_CP_FQ_12_KHZ << RT5640_CP_FQ2_SFT) |
(RT5640_CP_FQ_192_KHZ << RT5640_CP_FQ3_SFT));
regmap_write(rt5640->regmap, RT5640_PR_BASE +
RT5640_MAMP_INT_REG2, 0x1c00);
regmap_update_bits(rt5640->regmap, RT5640_DEPOP_M1,
RT5640_HP_CP_MASK | RT5640_HP_SG_MASK,
RT5640_HP_CP_PD | RT5640_HP_SG_EN);
regmap_update_bits(rt5640->regmap, RT5640_PR_BASE +
RT5640_CHPUMP_INT_REG1, 0x0700, 0x0400);
}
static int rt5640_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
rt5640_pmu_depop(codec);
rt5640->hp_mute = 0;
break;
case SND_SOC_DAPM_PRE_PMD:
rt5640->hp_mute = 1;
usleep_range(70000, 75000);
break;
default:
return 0;
}
return 0;
}
static int rt5640_hp_power_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
hp_amp_power_on(codec);
break;
default:
return 0;
}
return 0;
}
static int rt5640_hp_post_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
switch (event) {
case SND_SOC_DAPM_POST_PMU:
if (!rt5640->hp_mute)
usleep_range(80000, 85000);
break;
default:
return 0;
}
return 0;
}
static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = { static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2, SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
RT5640_PWR_PLL_BIT, 0, NULL, 0), RT5640_PWR_PLL_BIT, 0, NULL, 0),
@ -1132,15 +1226,28 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)), rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1, SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
RT5640_PWR_MA_BIT, 0, NULL, 0), RT5640_PWR_MA_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Improve HP Amp Drv", RT5640_PWR_ANLG1, SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM,
SND_SOC_NOPM, 0, NULL, 0), 0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA("HP L Amp", RT5640_PWR_ANLG1, SND_SOC_DAPM_PGA_S("HP Amp", 1, SND_SOC_NOPM, 0, 0,
rt5640_hp_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SUPPLY("HP L Amp", RT5640_PWR_ANLG1,
RT5640_PWR_HP_L_BIT, 0, NULL, 0), RT5640_PWR_HP_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("HP R Amp", RT5640_PWR_ANLG1, SND_SOC_DAPM_SUPPLY("HP R Amp", RT5640_PWR_ANLG1,
RT5640_PWR_HP_R_BIT, 0, NULL, 0), RT5640_PWR_HP_R_BIT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1, SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
SND_SOC_NOPM, 0, spk_event, RT5640_PWR_CLS_D_BIT, 0, NULL, 0),
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
/* Output Switch */
SND_SOC_DAPM_SWITCH("Speaker L Playback", SND_SOC_NOPM, 0, 0,
&spk_l_enable_control),
SND_SOC_DAPM_SWITCH("Speaker R Playback", SND_SOC_NOPM, 0, 0,
&spk_r_enable_control),
SND_SOC_DAPM_SWITCH("HP L Playback", SND_SOC_NOPM, 0, 0,
&hp_l_enable_control),
SND_SOC_DAPM_SWITCH("HP R Playback", SND_SOC_NOPM, 0, 0,
&hp_r_enable_control),
SND_SOC_DAPM_POST("HP Post", rt5640_hp_post_event),
/* Output Lines */ /* Output Lines */
SND_SOC_DAPM_OUTPUT("SPOLP"), SND_SOC_DAPM_OUTPUT("SPOLP"),
SND_SOC_DAPM_OUTPUT("SPOLN"), SND_SOC_DAPM_OUTPUT("SPOLN"),
@ -1381,9 +1488,11 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"}, {"HPO MIX L", "HPO MIX DAC2 Switch", "DAC L2"},
{"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"}, {"HPO MIX L", "HPO MIX DAC1 Switch", "DAC L1"},
{"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"}, {"HPO MIX L", "HPO MIX HPVOL Switch", "HPOVOL L"},
{"HPO MIX L", NULL, "HP L Amp"},
{"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"}, {"HPO MIX R", "HPO MIX DAC2 Switch", "DAC R2"},
{"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"}, {"HPO MIX R", "HPO MIX DAC1 Switch", "DAC R1"},
{"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"}, {"HPO MIX R", "HPO MIX HPVOL Switch", "HPOVOL R"},
{"HPO MIX R", NULL, "HP R Amp"},
{"LOUT MIX", "DAC L1 Switch", "DAC L1"}, {"LOUT MIX", "DAC L1 Switch", "DAC L1"},
{"LOUT MIX", "DAC R1 Switch", "DAC R1"}, {"LOUT MIX", "DAC R1 Switch", "DAC R1"},
@ -1396,13 +1505,15 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"Mono MIX", "OUTVOL L Switch", "OUTVOL L"}, {"Mono MIX", "OUTVOL L Switch", "OUTVOL L"},
{"Mono MIX", "BST1 Switch", "BST1"}, {"Mono MIX", "BST1 Switch", "BST1"},
{"HP L Amp", NULL, "HPO MIX L"}, {"HP Amp", NULL, "HPO MIX L"},
{"HP R Amp", NULL, "HPO MIX R"}, {"HP Amp", NULL, "HPO MIX R"},
{"SPOLP", NULL, "SPOL MIX"}, {"Speaker L Playback", "Switch", "SPOL MIX"},
{"SPOLN", NULL, "SPOL MIX"}, {"Speaker R Playback", "Switch", "SPOR MIX"},
{"SPORP", NULL, "SPOR MIX"}, {"SPOLP", NULL, "Speaker L Playback"},
{"SPORN", NULL, "SPOR MIX"}, {"SPOLN", NULL, "Speaker L Playback"},
{"SPORP", NULL, "Speaker R Playback"},
{"SPORN", NULL, "Speaker R Playback"},
{"SPOLP", NULL, "Improve SPK Amp Drv"}, {"SPOLP", NULL, "Improve SPK Amp Drv"},
{"SPOLN", NULL, "Improve SPK Amp Drv"}, {"SPOLN", NULL, "Improve SPK Amp Drv"},
@ -1412,8 +1523,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
{"HPOL", NULL, "Improve HP Amp Drv"}, {"HPOL", NULL, "Improve HP Amp Drv"},
{"HPOR", NULL, "Improve HP Amp Drv"}, {"HPOR", NULL, "Improve HP Amp Drv"},
{"HPOL", NULL, "HP L Amp"}, {"HP L Playback", "Switch", "HP Amp"},
{"HPOR", NULL, "HP R Amp"}, {"HP R Playback", "Switch", "HP Amp"},
{"HPOL", NULL, "HP L Playback"},
{"HPOR", NULL, "HP R Playback"},
{"LOUTL", NULL, "LOUT MIX"}, {"LOUTL", NULL, "LOUT MIX"},
{"LOUTR", NULL, "LOUT MIX"}, {"LOUTR", NULL, "LOUT MIX"},
{"MONOP", NULL, "Mono MIX"}, {"MONOP", NULL, "Mono MIX"},
@ -1792,17 +1905,13 @@ static int rt5640_set_bias_level(struct snd_soc_codec *codec,
RT5640_PWR_BG | RT5640_PWR_VREF2, RT5640_PWR_BG | RT5640_PWR_VREF2,
RT5640_PWR_VREF1 | RT5640_PWR_MB | RT5640_PWR_VREF1 | RT5640_PWR_MB |
RT5640_PWR_BG | RT5640_PWR_VREF2); RT5640_PWR_BG | RT5640_PWR_VREF2);
mdelay(10); usleep_range(10000, 15000);
snd_soc_update_bits(codec, RT5640_PWR_ANLG1, snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2, RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2); RT5640_PWR_FV1 | RT5640_PWR_FV2);
regcache_sync(rt5640->regmap); regcache_sync(rt5640->regmap);
snd_soc_update_bits(codec, RT5640_DUMMY1, snd_soc_update_bits(codec, RT5640_DUMMY1,
0x0301, 0x0301); 0x0301, 0x0301);
snd_soc_update_bits(codec, RT5640_DEPOP_M1,
0x001d, 0x0019);
snd_soc_update_bits(codec, RT5640_DEPOP_M2,
0x2000, 0x2000);
snd_soc_update_bits(codec, RT5640_MICBIAS, snd_soc_update_bits(codec, RT5640_MICBIAS,
0x0030, 0x0030); 0x0030, 0x0030);
} }
@ -1846,8 +1955,6 @@ static int rt5640_probe(struct snd_soc_codec *codec)
rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF); rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301); snd_soc_update_bits(codec, RT5640_DUMMY1, 0x0301, 0x0301);
snd_soc_update_bits(codec, RT5640_DEPOP_M1, 0x001d, 0x0019);
snd_soc_update_bits(codec, RT5640_DEPOP_M2, 0x2000, 0x2000);
snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030); snd_soc_update_bits(codec, RT5640_MICBIAS, 0x0030, 0x0030);
snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00); snd_soc_update_bits(codec, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
@ -2069,6 +2176,8 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4, regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
RT5640_IN_DF2, RT5640_IN_DF2); RT5640_IN_DF2, RT5640_IN_DF2);
rt5640->hp_mute = 1;
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640, ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
rt5640_dai, ARRAY_SIZE(rt5640_dai)); rt5640_dai, ARRAY_SIZE(rt5640_dai));
if (ret < 0) if (ret < 0)

View File

@ -145,6 +145,8 @@
/* Index of Codec Private Register definition */ /* Index of Codec Private Register definition */
#define RT5640_CHPUMP_INT_REG1 0x24
#define RT5640_MAMP_INT_REG2 0x37
#define RT5640_3D_SPK 0x63 #define RT5640_3D_SPK 0x63
#define RT5640_WND_1 0x6c #define RT5640_WND_1 0x6c
#define RT5640_WND_2 0x6d #define RT5640_WND_2 0x6d
@ -153,6 +155,7 @@
#define RT5640_WND_5 0x70 #define RT5640_WND_5 0x70
#define RT5640_WND_8 0x73 #define RT5640_WND_8 0x73
#define RT5640_DIP_SPK_INF 0x75 #define RT5640_DIP_SPK_INF 0x75
#define RT5640_HP_DCC_INT1 0x77
#define RT5640_EQ_BW_LOP 0xa0 #define RT5640_EQ_BW_LOP 0xa0
#define RT5640_EQ_GN_LOP 0xa1 #define RT5640_EQ_GN_LOP 0xa1
#define RT5640_EQ_FC_BP1 0xa2 #define RT5640_EQ_FC_BP1 0xa2
@ -1201,6 +1204,14 @@
#define RT5640_CP_FQ2_SFT 4 #define RT5640_CP_FQ2_SFT 4
#define RT5640_CP_FQ3_MASK (0x7) #define RT5640_CP_FQ3_MASK (0x7)
#define RT5640_CP_FQ3_SFT 0 #define RT5640_CP_FQ3_SFT 0
#define RT5640_CP_FQ_1_5_KHZ 0
#define RT5640_CP_FQ_3_KHZ 1
#define RT5640_CP_FQ_6_KHZ 2
#define RT5640_CP_FQ_12_KHZ 3
#define RT5640_CP_FQ_24_KHZ 4
#define RT5640_CP_FQ_48_KHZ 5
#define RT5640_CP_FQ_96_KHZ 6
#define RT5640_CP_FQ_192_KHZ 7
/* HPOUT charge pump (0x91) */ /* HPOUT charge pump (0x91) */
#define RT5640_OSW_L_MASK (0x1 << 11) #define RT5640_OSW_L_MASK (0x1 << 11)
@ -2087,6 +2098,7 @@ struct rt5640_priv {
int pll_out; int pll_out;
int dmic_en; int dmic_en;
bool hp_mute;
}; };
#endif #endif

View File

@ -561,8 +561,9 @@ static int ssm2602_suspend(struct snd_soc_codec *codec)
static int ssm2602_resume(struct snd_soc_codec *codec) static int ssm2602_resume(struct snd_soc_codec *codec)
{ {
snd_soc_cache_sync(codec); struct ssm2602_priv *ssm2602 = snd_soc_codec_get_drvdata(codec);
regcache_sync(ssm2602->regmap);
ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY); ssm2602_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
return 0; return 0;

View File

@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate)
return -EINVAL; return -EINVAL;
} }
static int aic32x4_add_widgets(struct snd_soc_codec *codec)
{
snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
ARRAY_SIZE(aic32x4_dapm_widgets));
snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
ARRAY_SIZE(aic32x4_dapm_routes));
snd_soc_dapm_new_widgets(&codec->dapm);
return 0;
}
static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai, static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir) int clk_id, unsigned int freq, int dir)
{ {
@ -683,9 +671,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
} }
aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY); aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
snd_soc_add_codec_controls(codec, aic32x4_snd_controls,
ARRAY_SIZE(aic32x4_snd_controls));
aic32x4_add_widgets(codec);
/* /*
* Workaround: for an unknown reason, the ADC needs to be powered up * Workaround: for an unknown reason, the ADC needs to be powered up
@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
.suspend = aic32x4_suspend, .suspend = aic32x4_suspend,
.resume = aic32x4_resume, .resume = aic32x4_resume,
.set_bias_level = aic32x4_set_bias_level, .set_bias_level = aic32x4_set_bias_level,
.controls = aic32x4_snd_controls,
.num_controls = ARRAY_SIZE(aic32x4_snd_controls),
.dapm_widgets = aic32x4_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
.dapm_routes = aic32x4_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
}; };
static int aic32x4_i2c_probe(struct i2c_client *i2c, static int aic32x4_i2c_probe(struct i2c_client *i2c,

View File

@ -1202,7 +1202,6 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec)
break; break;
} }
snd_soc_dapm_new_widgets(dapm);
return 0; return 0;
} }

View File

@ -3174,7 +3174,7 @@ static ssize_t wm8962_beep_set(struct device *dev,
long int time; long int time;
int ret; int ret;
ret = strict_strtol(buf, 10, &time); ret = kstrtol(buf, 10, &time);
if (ret != 0) if (ret != 0)
return ret; return ret;

View File

@ -421,13 +421,11 @@ static int dw_i2s_probe(struct platform_device *pdev)
dw_i2s_dai, 1); dw_i2s_dai, 1);
if (ret != 0) { if (ret != 0) {
dev_err(&pdev->dev, "not able to register dai\n"); dev_err(&pdev->dev, "not able to register dai\n");
goto err_set_drvdata; goto err_clk_disable;
} }
return 0; return 0;
err_set_drvdata:
dev_set_drvdata(&pdev->dev, NULL);
err_clk_disable: err_clk_disable:
clk_disable(dev->clk); clk_disable(dev->clk);
err_clk_put: err_clk_put:
@ -440,7 +438,6 @@ static int dw_i2s_remove(struct platform_device *pdev)
struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev); struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
clk_put(dev->clk); clk_put(dev->clk);

View File

@ -193,6 +193,17 @@ config SND_SOC_IMX_SGTL5000
Say Y if you want to add support for SoC audio on an i.MX board with Say Y if you want to add support for SoC audio on an i.MX board with
a sgtl5000 codec. a sgtl5000 codec.
config SND_SOC_IMX_SPDIF
tristate "SoC Audio support for i.MX boards with S/PDIF"
select SND_SOC_IMX_PCM_DMA
select SND_SOC_FSL_SPDIF
select SND_SOC_SPDIF
select REGMAP_MMIO
help
SoC Audio support for i.MX boards with S/PDIF
Say Y if you want to add support for SoC audio on an i.MX board with
a S/DPDIF.
config SND_SOC_IMX_MC13783 config SND_SOC_IMX_MC13783
tristate "SoC Audio support for I.MX boards with mc13783" tristate "SoC Audio support for I.MX boards with mc13783"
depends on MFD_MC13783 && ARM depends on MFD_MC13783 && ARM

View File

@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
snd-soc-wm1133-ev1-objs := wm1133-ev1.o snd-soc-wm1133-ev1-objs := wm1133-ev1.o
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o snd-soc-imx-sgtl5000-objs := imx-sgtl5000.o
snd-soc-imx-wm8962-objs := imx-wm8962.o snd-soc-imx-wm8962-objs := imx-wm8962.o
snd-soc-imx-spdif-objs := imx-spdif.o
snd-soc-imx-mc13783-objs := imx-mc13783.o snd-soc-imx-mc13783-objs := imx-mc13783.o
obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o obj-$(CONFIG_SND_SOC_EUKREA_TLV320) += snd-soc-eukrea-tlv320.o
@ -53,4 +54,5 @@ obj-$(CONFIG_SND_SOC_MX27VIS_AIC32X4) += snd-soc-mx27vis-aic32x4.o
obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o obj-$(CONFIG_SND_MXC_SOC_WM1133_EV1) += snd-soc-wm1133-ev1.o
obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o obj-$(CONFIG_SND_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.o
obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o obj-$(CONFIG_SND_SOC_IMX_WM8962) += snd-soc-imx-wm8962.o
obj-$(CONFIG_SND_SOC_IMX_SPDIF) += snd-soc-imx-spdif.o
obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o obj-$(CONFIG_SND_SOC_IMX_MC13783) += snd-soc-imx-mc13783.o

View File

@ -411,8 +411,8 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
return 0; return 0;
} }
int fsl_spdif_startup(struct snd_pcm_substream *substream, static int fsl_spdif_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai) struct snd_soc_dai *cpu_dai)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai); struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
@ -546,7 +546,7 @@ static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
return 0; return 0;
} }
struct snd_soc_dai_ops fsl_spdif_dai_ops = { static struct snd_soc_dai_ops fsl_spdif_dai_ops = {
.startup = fsl_spdif_startup, .startup = fsl_spdif_startup,
.hw_params = fsl_spdif_hw_params, .hw_params = fsl_spdif_hw_params,
.trigger = fsl_spdif_trigger, .trigger = fsl_spdif_trigger,
@ -555,7 +555,6 @@ struct snd_soc_dai_ops fsl_spdif_dai_ops = {
/* /*
* ============================================
* FSL SPDIF IEC958 controller(mixer) functions * FSL SPDIF IEC958 controller(mixer) functions
* *
* Channel status get/put control * Channel status get/put control
@ -563,7 +562,6 @@ struct snd_soc_dai_ops fsl_spdif_dai_ops = {
* Valid bit value get control * Valid bit value get control
* DPLL lock status get control * DPLL lock status get control
* User bit sync mode selection control * User bit sync mode selection control
* ============================================
*/ */
static int fsl_spdif_info(struct snd_kcontrol *kcontrol, static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
@ -921,7 +919,7 @@ static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
return 0; return 0;
} }
struct snd_soc_dai_driver fsl_spdif_dai = { static struct snd_soc_dai_driver fsl_spdif_dai = {
.probe = &fsl_spdif_dai_probe, .probe = &fsl_spdif_dai_probe,
.playback = { .playback = {
.channels_min = 2, .channels_min = 2,
@ -942,11 +940,7 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
.name = "fsl-spdif", .name = "fsl-spdif",
}; };
/* /* FSL SPDIF REGMAP */
* ================
* FSL SPDIF REGMAP
* ================
*/
static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg) static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
{ {
@ -1077,9 +1071,9 @@ static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
break; break;
} }
dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate", dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate\n",
spdif_priv->txclk_src[index], rate[index]); spdif_priv->txclk_src[index], rate[index]);
dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate", dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate\n",
spdif_priv->txclk_div[index], rate[index]); spdif_priv->txclk_div[index], rate[index]);
return 0; return 0;
@ -1119,10 +1113,8 @@ static int fsl_spdif_probe(struct platform_device *pdev)
} }
regs = devm_ioremap_resource(&pdev->dev, res); regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(regs)) { if (IS_ERR(regs))
dev_err(&pdev->dev, "could not map device resources\n");
return PTR_ERR(regs); return PTR_ERR(regs);
}
spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev, spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
"core", regs, &fsl_spdif_regmap_config); "core", regs, &fsl_spdif_regmap_config);
@ -1184,7 +1176,7 @@ static int fsl_spdif_probe(struct platform_device *pdev)
&spdif_priv->cpu_dai_drv, 1); &spdif_priv->cpu_dai_drv, 1);
if (ret) { if (ret) {
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret); dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
goto error_dev; return ret;
} }
ret = imx_pcm_dma_init(pdev); ret = imx_pcm_dma_init(pdev);
@ -1197,8 +1189,6 @@ static int fsl_spdif_probe(struct platform_device *pdev)
error_component: error_component:
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
error_dev:
dev_set_drvdata(&pdev->dev, NULL);
return ret; return ret;
} }
@ -1207,7 +1197,6 @@ static int fsl_spdif_remove(struct platform_device *pdev)
{ {
imx_pcm_dma_exit(pdev); imx_pcm_dma_exit(pdev);
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
dev_set_drvdata(&pdev->dev, NULL);
return 0; return 0;
} }

View File

@ -1114,7 +1114,6 @@ error_dai:
snd_soc_unregister_component(&pdev->dev); snd_soc_unregister_component(&pdev->dev);
error_dev: error_dev:
dev_set_drvdata(&pdev->dev, NULL);
device_remove_file(&pdev->dev, dev_attr); device_remove_file(&pdev->dev, dev_attr);
error_clk: error_clk:

View File

@ -335,7 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev)
if (audmux_type == IMX31_AUDMUX) if (audmux_type == IMX31_AUDMUX)
audmux_debugfs_init(); audmux_debugfs_init();
imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node); if (of_id)
imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
return 0; return 0;
} }

148
sound/soc/fsl/imx-spdif.c Normal file
View File

@ -0,0 +1,148 @@
/*
* Copyright (C) 2013 Freescale Semiconductor, Inc.
*
* The code contained herein is licensed under the GNU General Public
* License. You may obtain a copy of the GNU General Public License
* Version 2 or later at the following locations:
*
* http://www.opensource.org/licenses/gpl-license.html
* http://www.gnu.org/copyleft/gpl.html
*/
#include <linux/module.h>
#include <linux/of_platform.h>
#include <sound/soc.h>
struct imx_spdif_data {
struct snd_soc_dai_link dai[2];
struct snd_soc_card card;
struct platform_device *txdev;
struct platform_device *rxdev;
};
static int imx_spdif_audio_probe(struct platform_device *pdev)
{
struct device_node *spdif_np, *np = pdev->dev.of_node;
struct imx_spdif_data *data;
int ret = 0, num_links = 0;
spdif_np = of_parse_phandle(np, "spdif-controller", 0);
if (!spdif_np) {
dev_err(&pdev->dev, "failed to find spdif-controller\n");
ret = -EINVAL;
goto end;
}
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data) {
dev_err(&pdev->dev, "failed to allocate memory\n");
ret = -ENOMEM;
goto end;
}
if (of_property_read_bool(np, "spdif-out")) {
data->dai[num_links].name = "S/PDIF TX";
data->dai[num_links].stream_name = "S/PDIF PCM Playback";
data->dai[num_links].codec_dai_name = "dit-hifi";
data->dai[num_links].codec_name = "spdif-dit";
data->dai[num_links].cpu_of_node = spdif_np;
data->dai[num_links].platform_of_node = spdif_np;
num_links++;
data->txdev = platform_device_register_simple("spdif-dit", -1, NULL, 0);
if (IS_ERR(data->txdev)) {
ret = PTR_ERR(data->txdev);
dev_err(&pdev->dev, "register dit failed: %d\n", ret);
goto end;
}
}
if (of_property_read_bool(np, "spdif-in")) {
data->dai[num_links].name = "S/PDIF RX";
data->dai[num_links].stream_name = "S/PDIF PCM Capture";
data->dai[num_links].codec_dai_name = "dir-hifi";
data->dai[num_links].codec_name = "spdif-dir";
data->dai[num_links].cpu_of_node = spdif_np;
data->dai[num_links].platform_of_node = spdif_np;
num_links++;
data->rxdev = platform_device_register_simple("spdif-dir", -1, NULL, 0);
if (IS_ERR(data->rxdev)) {
ret = PTR_ERR(data->rxdev);
dev_err(&pdev->dev, "register dir failed: %d\n", ret);
goto error_dit;
}
}
if (!num_links) {
dev_err(&pdev->dev, "no enabled S/PDIF DAI link\n");
goto error_dir;
}
data->card.dev = &pdev->dev;
data->card.num_links = num_links;
data->card.dai_link = data->dai;
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret)
goto error_dir;
ret = snd_soc_register_card(&data->card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed: %d\n", ret);
goto error_dir;
}
platform_set_drvdata(pdev, data);
goto end;
error_dir:
if (data->rxdev)
platform_device_unregister(data->rxdev);
error_dit:
if (data->txdev)
platform_device_unregister(data->txdev);
end:
if (spdif_np)
of_node_put(spdif_np);
return ret;
}
static int imx_spdif_audio_remove(struct platform_device *pdev)
{
struct imx_spdif_data *data = platform_get_drvdata(pdev);
if (data->rxdev)
platform_device_unregister(data->rxdev);
if (data->txdev)
platform_device_unregister(data->txdev);
snd_soc_unregister_card(&data->card);
return 0;
}
static const struct of_device_id imx_spdif_dt_ids[] = {
{ .compatible = "fsl,imx-audio-spdif", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
static struct platform_driver imx_spdif_driver = {
.driver = {
.name = "imx-spdif",
.owner = THIS_MODULE,
.of_match_table = imx_spdif_dt_ids,
},
.probe = imx_spdif_audio_probe,
.remove = imx_spdif_audio_remove,
};
module_platform_driver(imx_spdif_driver);
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
MODULE_DESCRIPTION("Freescale i.MX S/PDIF machine driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:imx-spdif");

View File

@ -105,6 +105,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev)
static struct platform_driver asoc_simple_card = { static struct platform_driver asoc_simple_card = {
.driver = { .driver = {
.name = "asoc-simple-card", .name = "asoc-simple-card",
.owner = THIS_MODULE,
}, },
.probe = asoc_simple_card_probe, .probe = asoc_simple_card_probe,
.remove = asoc_simple_card_remove, .remove = asoc_simple_card_remove,
@ -112,6 +113,7 @@ static struct platform_driver asoc_simple_card = {
module_platform_driver(asoc_simple_card); module_platform_driver(asoc_simple_card);
MODULE_ALIAS("platform:asoc-simple-card");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ASoC Simple Sound Card"); MODULE_DESCRIPTION("ASoC Simple Sound Card");
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");

View File

@ -1,6 +1,6 @@
config SND_KIRKWOOD_SOC config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood chip" tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
depends on ARCH_KIRKWOOD || COMPILE_TEST depends on ARCH_KIRKWOOD || ARCH_DOVE || COMPILE_TEST
help help
Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the the Kirkwood I2S interface. You will also need to select the

View File

@ -22,6 +22,8 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/platform_data/asoc-kirkwood.h> #include <linux/platform_data/asoc-kirkwood.h>
#include <linux/of.h>
#include "kirkwood.h" #include "kirkwood.h"
#define DRV_NAME "mvebu-audio" #define DRV_NAME "mvebu-audio"
@ -453,6 +455,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai; struct snd_soc_dai_driver *soc_dai = &kirkwood_i2s_dai;
struct kirkwood_dma_data *priv; struct kirkwood_dma_data *priv;
struct resource *mem; struct resource *mem;
struct device_node *np = pdev->dev.of_node;
int err; int err;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@ -473,14 +476,16 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
return -ENXIO; return -ENXIO;
} }
if (!data) { if (np) {
dev_err(&pdev->dev, "no platform data ?!\n"); priv->burst = 128; /* might be 32 or 128 */
} else if (data) {
priv->burst = data->burst;
} else {
dev_err(&pdev->dev, "no DT nor platform data ?!\n");
return -EINVAL; return -EINVAL;
} }
priv->burst = data->burst; priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
priv->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(priv->clk)) { if (IS_ERR(priv->clk)) {
dev_err(&pdev->dev, "no clock\n"); dev_err(&pdev->dev, "no clock\n");
return PTR_ERR(priv->clk); return PTR_ERR(priv->clk);
@ -507,7 +512,7 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24; priv->ctl_rec = KIRKWOOD_RECCTL_SIZE_24;
/* Select the burst size */ /* Select the burst size */
if (data->burst == 32) { if (priv->burst == 32) {
priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32; priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32; priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
} else { } else {
@ -552,12 +557,21 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_OF
static struct of_device_id mvebu_audio_of_match[] = {
{ .compatible = "marvell,mvebu-audio" },
{ }
};
MODULE_DEVICE_TABLE(of, mvebu_audio_of_match);
#endif
static struct platform_driver kirkwood_i2s_driver = { static struct platform_driver kirkwood_i2s_driver = {
.probe = kirkwood_i2s_dev_probe, .probe = kirkwood_i2s_dev_probe,
.remove = kirkwood_i2s_dev_remove, .remove = kirkwood_i2s_dev_remove,
.driver = { .driver = {
.name = DRV_NAME, .name = DRV_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(mvebu_audio_of_match),
}, },
}; };

View File

@ -105,11 +105,13 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
.stream_name = "HiFi Playback", .stream_name = "HiFi Playback",
.codec_dai_name = "sgtl5000", .codec_dai_name = "sgtl5000",
.ops = &mxs_sgtl5000_hifi_ops, .ops = &mxs_sgtl5000_hifi_ops,
.playback_only = true,
}, { }, {
.name = "HiFi Rx", .name = "HiFi Rx",
.stream_name = "HiFi Capture", .stream_name = "HiFi Capture",
.codec_dai_name = "sgtl5000", .codec_dai_name = "sgtl5000",
.ops = &mxs_sgtl5000_hifi_ops, .ops = &mxs_sgtl5000_hifi_ops,
.capture_only = true,
}, },
}; };

View File

@ -781,7 +781,7 @@ static ssize_t prop##_store(struct device *dev, \
unsigned long val; \ unsigned long val; \
int status; \ int status; \
\ \
status = strict_strtoul(buf, 0, &val); \ status = kstrtoul(buf, 0, &val); \
if (status) \ if (status) \
return status; \ return status; \
\ \

View File

@ -90,6 +90,13 @@ static void dma_enqueue(struct snd_pcm_substream *substream)
dma_info.period = prtd->dma_period; dma_info.period = prtd->dma_period;
dma_info.len = prtd->dma_period*limit; dma_info.len = prtd->dma_period*limit;
if (dma_info.cap == DMA_CYCLIC) {
dma_info.buf = pos;
prtd->params->ops->prepare(prtd->params->ch, &dma_info);
prtd->dma_loaded += limit;
return;
}
while (prtd->dma_loaded < limit) { while (prtd->dma_loaded < limit) {
pr_debug("dma_loaded: %d\n", prtd->dma_loaded); pr_debug("dma_loaded: %d\n", prtd->dma_loaded);

View File

@ -235,6 +235,8 @@ struct fsi_stream {
struct sh_dmae_slave slave; /* see fsi_handler_init() */ struct sh_dmae_slave slave; /* see fsi_handler_init() */
struct work_struct work; struct work_struct work;
dma_addr_t dma; dma_addr_t dma;
int loop_cnt;
int additional_pos;
}; };
struct fsi_clk { struct fsi_clk {
@ -1289,6 +1291,8 @@ static int fsi_dma_init(struct fsi_priv *fsi, struct fsi_stream *io)
io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) | io->bus_option = BUSOP_SET(24, PACKAGE_24BITBUS_BACK) |
BUSOP_SET(16, PACKAGE_16BITBUS_STREAM); BUSOP_SET(16, PACKAGE_16BITBUS_STREAM);
io->loop_cnt = 2; /* push 1st, 2nd period first, then 3rd, 4th... */
io->additional_pos = 0;
io->dma = dma_map_single(dai->dev, runtime->dma_area, io->dma = dma_map_single(dai->dev, runtime->dma_area,
snd_pcm_lib_buffer_bytes(io->substream), dir); snd_pcm_lib_buffer_bytes(io->substream), dir);
return 0; return 0;
@ -1305,11 +1309,15 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
return 0; return 0;
} }
static dma_addr_t fsi_dma_get_area(struct fsi_stream *io) static dma_addr_t fsi_dma_get_area(struct fsi_stream *io, int additional)
{ {
struct snd_pcm_runtime *runtime = io->substream->runtime; struct snd_pcm_runtime *runtime = io->substream->runtime;
int period = io->period_pos + additional;
return io->dma + samples_to_bytes(runtime, io->buff_sample_pos); if (period >= runtime->periods)
period = 0;
return io->dma + samples_to_bytes(runtime, period * io->period_samples);
} }
static void fsi_dma_complete(void *data) static void fsi_dma_complete(void *data)
@ -1321,7 +1329,7 @@ static void fsi_dma_complete(void *data)
enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ? enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
DMA_TO_DEVICE : DMA_FROM_DEVICE; DMA_TO_DEVICE : DMA_FROM_DEVICE;
dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io), dma_sync_single_for_cpu(dai->dev, fsi_dma_get_area(io, 0),
samples_to_bytes(runtime, io->period_samples), dir); samples_to_bytes(runtime, io->period_samples), dir);
io->buff_sample_pos += io->period_samples; io->buff_sample_pos += io->period_samples;
@ -1347,7 +1355,7 @@ static void fsi_dma_do_work(struct work_struct *work)
struct snd_pcm_runtime *runtime; struct snd_pcm_runtime *runtime;
enum dma_data_direction dir; enum dma_data_direction dir;
int is_play = fsi_stream_is_play(fsi, io); int is_play = fsi_stream_is_play(fsi, io);
int len; int len, i;
dma_addr_t buf; dma_addr_t buf;
if (!fsi_stream_is_working(fsi, io)) if (!fsi_stream_is_working(fsi, io))
@ -1357,26 +1365,33 @@ static void fsi_dma_do_work(struct work_struct *work)
runtime = io->substream->runtime; runtime = io->substream->runtime;
dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE; dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
len = samples_to_bytes(runtime, io->period_samples); len = samples_to_bytes(runtime, io->period_samples);
buf = fsi_dma_get_area(io);
dma_sync_single_for_device(dai->dev, buf, len, dir); for (i = 0; i < io->loop_cnt; i++) {
buf = fsi_dma_get_area(io, io->additional_pos);
desc = dmaengine_prep_slave_single(io->chan, buf, len, dir, dma_sync_single_for_device(dai->dev, buf, len, dir);
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
if (!desc) { desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n"); DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
return; if (!desc) {
dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
return;
}
desc->callback = fsi_dma_complete;
desc->callback_param = io;
if (dmaengine_submit(desc) < 0) {
dev_err(dai->dev, "tx_submit() fail\n");
return;
}
dma_async_issue_pending(io->chan);
io->additional_pos = 1;
} }
desc->callback = fsi_dma_complete; io->loop_cnt = 1;
desc->callback_param = io;
if (dmaengine_submit(desc) < 0) {
dev_err(dai->dev, "tx_submit() fail\n");
return;
}
dma_async_issue_pending(io->chan);
/* /*
* FIXME * FIXME

View File

@ -203,7 +203,7 @@ static ssize_t pmdown_time_set(struct device *dev,
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev); struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
int ret; int ret;
ret = strict_strtol(buf, 10, &rtd->pmdown_time); ret = kstrtol(buf, 10, &rtd->pmdown_time);
if (ret) if (ret)
return ret; return ret;
@ -248,6 +248,7 @@ static ssize_t codec_reg_write_file(struct file *file,
char *start = buf; char *start = buf;
unsigned long reg, value; unsigned long reg, value;
struct snd_soc_codec *codec = file->private_data; struct snd_soc_codec *codec = file->private_data;
int ret;
buf_size = min(count, (sizeof(buf)-1)); buf_size = min(count, (sizeof(buf)-1));
if (copy_from_user(buf, user_buf, buf_size)) if (copy_from_user(buf, user_buf, buf_size))
@ -259,8 +260,9 @@ static ssize_t codec_reg_write_file(struct file *file,
reg = simple_strtoul(start, &start, 16); reg = simple_strtoul(start, &start, 16);
while (*start == ' ') while (*start == ' ')
start++; start++;
if (strict_strtoul(start, 16, &value)) ret = kstrtoul(start, 16, &value);
return -EINVAL; if (ret)
return ret;
/* Userspace has been fiddling around behind the kernel's back */ /* Userspace has been fiddling around behind the kernel's back */
add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE); add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE);
@ -1243,9 +1245,6 @@ static int soc_post_component_init(struct snd_soc_card *card,
} }
rtd->card = card; rtd->card = card;
/* Make sure all DAPM widgets are instantiated */
snd_soc_dapm_new_widgets(&codec->dapm);
/* machine controls, routes and widgets are not prefixed */ /* machine controls, routes and widgets are not prefixed */
temp = codec->name_prefix; temp = codec->name_prefix;
codec->name_prefix = NULL; codec->name_prefix = NULL;
@ -1741,8 +1740,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes, snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
card->num_dapm_routes); card->num_dapm_routes);
snd_soc_dapm_new_widgets(&card->dapm);
for (i = 0; i < card->num_links; i++) { for (i = 0; i < card->num_links; i++) {
dai_link = &card->dai_link[i]; dai_link = &card->dai_link[i];
dai_fmt = dai_link->dai_fmt; dai_fmt = dai_link->dai_fmt;
@ -1821,12 +1818,12 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
} }
} }
snd_soc_dapm_new_widgets(&card->dapm);
if (card->fully_routed) if (card->fully_routed)
list_for_each_entry(codec, &card->codec_dev_list, card_list) list_for_each_entry(codec, &card->codec_dev_list, card_list)
snd_soc_dapm_auto_nc_codec_pins(codec); snd_soc_dapm_auto_nc_codec_pins(codec);
snd_soc_dapm_new_widgets(card);
ret = snd_card_register(card->snd_card); ret = snd_card_register(card->snd_card);
if (ret < 0) { if (ret < 0) {
dev_err(card->dev, "ASoC: failed to register soundcard %d\n", dev_err(card->dev, "ASoC: failed to register soundcard %d\n",

View File

@ -229,6 +229,8 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
template.id = snd_soc_dapm_kcontrol; template.id = snd_soc_dapm_kcontrol;
template.name = kcontrol->id.name; template.name = kcontrol->id.name;
data->value = template.on_val;
data->widget = snd_soc_dapm_new_control(widget->dapm, data->widget = snd_soc_dapm_new_control(widget->dapm,
&template); &template);
if (!data->widget) { if (!data->widget) {
@ -2374,6 +2376,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
wsource->ext = 1; wsource->ext = 1;
} }
dapm_mark_dirty(wsource, "Route added");
dapm_mark_dirty(wsink, "Route added");
/* connect static paths */ /* connect static paths */
if (control == NULL) { if (control == NULL) {
list_add(&path->list, &dapm->card->paths); list_add(&path->list, &dapm->card->paths);
@ -2436,9 +2441,6 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
return 0; return 0;
} }
dapm_mark_dirty(wsource, "Route added");
dapm_mark_dirty(wsink, "Route added");
return 0; return 0;
err: err:
kfree(path); kfree(path);
@ -2712,9 +2714,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
* *
* Returns 0 for success. * Returns 0 for success.
*/ */
int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm) int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
{ {
struct snd_soc_card *card = dapm->card;
struct snd_soc_dapm_widget *w; struct snd_soc_dapm_widget *w;
unsigned int val; unsigned int val;

View File

@ -183,8 +183,6 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
list_add(&(pins[i].list), &jack->pins); list_add(&(pins[i].list), &jack->pins);
} }
snd_soc_dapm_new_widgets(&jack->codec->card->dapm);
/* Update to reflect the last reported status; canned jack /* Update to reflect the last reported status; canned jack
* implementations are likely to set their state before the * implementations are likely to set their state before the
* card has an opportunity to associate pins. * card has an opportunity to associate pins.

View File

@ -2020,6 +2020,16 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
capture = 1; capture = 1;
} }
if (rtd->dai_link->playback_only) {
playback = 1;
capture = 0;
}
if (rtd->dai_link->capture_only) {
playback = 0;
capture = 1;
}
/* create the PCM */ /* create the PCM */
if (rtd->dai_link->no_pcm) { if (rtd->dai_link->no_pcm) {
snprintf(new_name, sizeof(new_name), "(%s)", snprintf(new_name, sizeof(new_name), "(%s)",

View File

@ -346,10 +346,10 @@ static int usb6fire_fw_check(u8 *version)
if (!memcmp(version, known_fw_versions + i, 2)) if (!memcmp(version, known_fw_versions + i, 2))
return 0; return 0;
snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. " snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %4ph. "
"please reconnect to power. if this failure " "please reconnect to power. if this failure "
"still happens, check your firmware installation.", "still happens, check your firmware installation.",
4, version); version);
return -EINVAL; return -EINVAL;
} }

View File

@ -418,6 +418,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
struct snd_usb_endpoint *ep; struct snd_usb_endpoint *ep;
int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK; int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
if (WARN_ON(!alts))
return NULL;
mutex_lock(&chip->mutex); mutex_lock(&chip->mutex);
list_for_each_entry(ep, &chip->ep_list, list) { list_for_each_entry(ep, &chip->ep_list, list) {

View File

@ -327,6 +327,137 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
return 0; return 0;
} }
static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
struct usb_device *dev,
struct usb_interface_descriptor *altsd,
unsigned int attr)
{
struct usb_host_interface *alts;
struct usb_interface *iface;
unsigned int ep;
/* Implicit feedback sync EPs consumers are always playback EPs */
if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK)
return 0;
switch (subs->stream->chip->usb_id) {
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
ep = 0x81;
iface = usb_ifnum_to_if(dev, 3);
if (!iface || iface->num_altsetting == 0)
return -EINVAL;
alts = &iface->altsetting[1];
goto add_sync_ep;
break;
case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
case USB_ID(0x0763, 0x2081):
ep = 0x81;
iface = usb_ifnum_to_if(dev, 2);
if (!iface || iface->num_altsetting == 0)
return -EINVAL;
alts = &iface->altsetting[1];
goto add_sync_ep;
}
if (attr == USB_ENDPOINT_SYNC_ASYNC &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
altsd->bInterfaceProtocol == 2 &&
altsd->bNumEndpoints == 1 &&
USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
altsd->bAlternateSetting,
&alts, &ep) >= 0) {
goto add_sync_ep;
}
/* No quirk */
return 0;
add_sync_ep:
subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, ep, !subs->direction,
SND_USB_ENDPOINT_TYPE_DATA);
if (!subs->sync_endpoint)
return -EINVAL;
subs->data_endpoint->sync_master = subs->sync_endpoint;
return 0;
}
static int set_sync_endpoint(struct snd_usb_substream *subs,
struct audioformat *fmt,
struct usb_device *dev,
struct usb_host_interface *alts,
struct usb_interface_descriptor *altsd)
{
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
unsigned int ep, attr;
bool implicit_fb;
int err;
/* we need a sync pipe in async OUT or adaptive IN mode */
/* check the number of EP, since some devices have broken
* descriptors which fool us. if it has only one EP,
* assume it as adaptive-out or sync-in.
*/
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr);
if (err < 0)
return err;
if (altsd->bNumEndpoints < 2)
return 0;
if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) ||
(!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE))
return 0;
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
because there is a version of the SB Audigy 2 NX firmware lacking
the audio fields in the endpoint descriptors */
if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
get_endpoint(alts, 1)->bSynchAddress != 0)) {
snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
dev->devnum, fmt->iface, fmt->altsetting,
get_endpoint(alts, 1)->bmAttributes,
get_endpoint(alts, 1)->bLength,
get_endpoint(alts, 1)->bSynchAddress);
return -EINVAL;
}
ep = get_endpoint(alts, 1)->bEndpointAddress;
if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
(!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
dev->devnum, fmt->iface, fmt->altsetting,
is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
return -EINVAL;
}
implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
== USB_ENDPOINT_USAGE_IMPLICIT_FB;
subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, ep, !subs->direction,
implicit_fb ?
SND_USB_ENDPOINT_TYPE_DATA :
SND_USB_ENDPOINT_TYPE_SYNC);
if (!subs->sync_endpoint)
return -EINVAL;
subs->data_endpoint->sync_master = subs->sync_endpoint;
return 0;
}
/* /*
* find a matching format and set up the interface * find a matching format and set up the interface
*/ */
@ -336,9 +467,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
struct usb_host_interface *alts; struct usb_host_interface *alts;
struct usb_interface_descriptor *altsd; struct usb_interface_descriptor *altsd;
struct usb_interface *iface; struct usb_interface *iface;
unsigned int ep, attr; int err;
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
int err, implicit_fb = 0;
iface = usb_ifnum_to_if(dev, fmt->iface); iface = usb_ifnum_to_if(dev, fmt->iface);
if (WARN_ON(!iface)) if (WARN_ON(!iface))
@ -383,118 +512,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip, subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, fmt->endpoint, subs->direction, alts, fmt->endpoint, subs->direction,
SND_USB_ENDPOINT_TYPE_DATA); SND_USB_ENDPOINT_TYPE_DATA);
if (!subs->data_endpoint) if (!subs->data_endpoint)
return -EINVAL; return -EINVAL;
/* we need a sync pipe in async OUT or adaptive IN mode */ err = set_sync_endpoint(subs, fmt, dev, alts, altsd);
/* check the number of EP, since some devices have broken if (err < 0)
* descriptors which fool us. if it has only one EP, return err;
* assume it as adaptive-out or sync-in.
*/
attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
switch (subs->stream->chip->usb_id) { err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt);
case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */ if (err < 0)
case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
if (is_playback) {
implicit_fb = 1;
ep = 0x81;
iface = usb_ifnum_to_if(dev, 3);
if (!iface || iface->num_altsetting == 0)
return -EINVAL;
alts = &iface->altsetting[1];
goto add_sync_ep;
}
break;
case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
case USB_ID(0x0763, 0x2081):
if (is_playback) {
implicit_fb = 1;
ep = 0x81;
iface = usb_ifnum_to_if(dev, 2);
if (!iface || iface->num_altsetting == 0)
return -EINVAL;
alts = &iface->altsetting[1];
goto add_sync_ep;
}
}
if (is_playback &&
attr == USB_ENDPOINT_SYNC_ASYNC &&
altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
altsd->bInterfaceProtocol == 2 &&
altsd->bNumEndpoints == 1 &&
USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
altsd->bAlternateSetting,
&alts, &ep) >= 0) {
implicit_fb = 1;
goto add_sync_ep;
}
if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
(!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
altsd->bNumEndpoints >= 2) {
/* check sync-pipe endpoint */
/* ... and check descriptor size before accessing bSynchAddress
because there is a version of the SB Audigy 2 NX firmware lacking
the audio fields in the endpoint descriptors */
if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
(get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
get_endpoint(alts, 1)->bSynchAddress != 0 &&
!implicit_fb)) {
snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
dev->devnum, fmt->iface, fmt->altsetting,
get_endpoint(alts, 1)->bmAttributes,
get_endpoint(alts, 1)->bLength,
get_endpoint(alts, 1)->bSynchAddress);
return -EINVAL;
}
ep = get_endpoint(alts, 1)->bEndpointAddress;
if (!implicit_fb &&
get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
(( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
(!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
dev->devnum, fmt->iface, fmt->altsetting,
is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
return -EINVAL;
}
implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
== USB_ENDPOINT_USAGE_IMPLICIT_FB;
add_sync_ep:
subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
alts, ep, !subs->direction,
implicit_fb ?
SND_USB_ENDPOINT_TYPE_DATA :
SND_USB_ENDPOINT_TYPE_SYNC);
if (!subs->sync_endpoint)
return -EINVAL;
subs->data_endpoint->sync_master = subs->sync_endpoint;
}
if ((err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt)) < 0)
return err; return err;
subs->cur_audiofmt = fmt; subs->cur_audiofmt = fmt;
snd_usb_set_format_quirk(subs, fmt); snd_usb_set_format_quirk(subs, fmt);
#if 0
printk(KERN_DEBUG
"setting done: format = %d, rate = %d..%d, channels = %d\n",
fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
printk(KERN_DEBUG
" datapipe = 0x%0x, syncpipe = 0x%0x\n",
subs->datapipe, subs->syncpipe);
#endif
return 0; return 0;
} }

View File

@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S)
{ {
int i; int i;
for (i = 0; i < URBS_AsyncSeq; ++i) { for (i = 0; i < URBS_AsyncSeq; ++i) {
if (S[i].urb) { usb_kill_urb(S->urb[i]);
usb_kill_urb(S->urb[i]); usb_free_urb(S->urb[i]);
usb_free_urb(S->urb[i]); S->urb[i] = NULL;
S->urb[i] = NULL;
}
} }
kfree(S->buffer); kfree(S->buffer);
} }