Merge remote-tracking branch 'asoc/fix/fsl' into asoc-linus
This commit is contained in:
commit
c34c0d7684
63 changed files with 2174 additions and 5017 deletions
34
Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
Normal file
34
Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
Normal 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;
|
||||
};
|
29
Documentation/devicetree/bindings/sound/mvebu-audio.txt
Normal file
29
Documentation/devicetree/bindings/sound/mvebu-audio.txt
Normal 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";
|
||||
};
|
|
@ -244,6 +244,7 @@ STAC9227/9228/9229/927x
|
|||
5stack-no-fp D965 5stack without front panel
|
||||
dell-3stack Dell Dimension E520
|
||||
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
|
||||
auto BIOS setup (default)
|
||||
|
||||
|
|
|
@ -454,6 +454,8 @@ The generic parser supports the following hints:
|
|||
- need_dac_fix (bool): limits the DACs depending on the channel count
|
||||
- primary_hp (bool): probe headphone jacks as the primary outputs;
|
||||
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
|
||||
- inv_dmic_split (bool): provide split internal mic volume/switch for
|
||||
phase-inverted digital mics
|
||||
|
|
11
MAINTAINERS
11
MAINTAINERS
|
@ -7676,6 +7676,17 @@ F: include/sound/
|
|||
F: include/uapi/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)
|
||||
M: Liam Girdwood <lgirdwood@gmail.com>
|
||||
M: Mark Brown <broonie@kernel.org>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include <linux/rwsem.h> /* struct rw_semaphore */
|
||||
#include <linux/pm.h> /* pm_message_t */
|
||||
#include <linux/stringify.h>
|
||||
#include <linux/printk.h>
|
||||
|
||||
/* number of supported soundcards */
|
||||
#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")
|
||||
|
||||
/**
|
||||
* Suppress high rates of output when CONFIG_SND_DEBUG is enabled.
|
||||
*/
|
||||
#define snd_printd_ratelimit() printk_ratelimit()
|
||||
|
||||
/**
|
||||
* snd_BUG_ON - debugging check macro
|
||||
* @cond: condition to evaluate
|
||||
|
@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {}
|
|||
unlikely(__ret_warn_on); \
|
||||
})
|
||||
|
||||
static inline bool snd_printd_ratelimit(void) { return false; }
|
||||
|
||||
#endif /* CONFIG_SND_DEBUG */
|
||||
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
|
|
|
@ -413,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
|
|||
struct snd_soc_dapm_widget *sink);
|
||||
|
||||
/* 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);
|
||||
int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
|
||||
const struct snd_soc_dapm_route *route, int num);
|
||||
|
|
|
@ -697,7 +697,6 @@ struct snd_soc_codec {
|
|||
unsigned int probed:1; /* Codec has been probed */
|
||||
unsigned int ac97_registered:1; /* Codec has been AC97 registered */
|
||||
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 using_regmap:1; /* using regmap access */
|
||||
u32 cache_only; /* Suppress writes to hardware */
|
||||
|
@ -705,7 +704,6 @@ struct snd_soc_codec {
|
|||
|
||||
/* codec IO */
|
||||
void *control_data; /* codec control (i2c/3wire) data */
|
||||
enum snd_soc_control_type control_type;
|
||||
hw_write_t hw_write;
|
||||
unsigned int (*hw_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
|
||||
struct dentry *debugfs_codec_root;
|
||||
struct dentry *debugfs_reg;
|
||||
struct dentry *debugfs_dapm;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -849,7 +846,6 @@ struct snd_soc_platform {
|
|||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
struct dentry *debugfs_platform_root;
|
||||
struct dentry *debugfs_dapm;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -934,6 +930,10 @@ struct snd_soc_dai_link {
|
|||
/* machine stream operations */
|
||||
const struct snd_soc_ops *ops;
|
||||
const struct snd_soc_compr_ops *compr_ops;
|
||||
|
||||
/* For unidirectional dai links */
|
||||
bool playback_only;
|
||||
bool capture_only;
|
||||
};
|
||||
|
||||
struct snd_soc_codec_conf {
|
||||
|
|
|
@ -111,7 +111,7 @@ struct hdspm_ltc {
|
|||
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
|
||||
|
|
|
@ -184,7 +184,7 @@ static void xrun(struct snd_pcm_substream *substream)
|
|||
do { \
|
||||
if (xrun_debug(substream, XRUN_DEBUG_BASIC)) { \
|
||||
xrun_log_show(substream); \
|
||||
if (printk_ratelimit()) { \
|
||||
if (snd_printd_ratelimit()) { \
|
||||
snd_printd("PCM: " fmt, ##args); \
|
||||
} \
|
||||
dump_stack_on_xrun(substream); \
|
||||
|
@ -342,7 +342,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
|
|||
return -EPIPE;
|
||||
}
|
||||
if (pos >= runtime->buffer_size) {
|
||||
if (printk_ratelimit()) {
|
||||
if (snd_printd_ratelimit()) {
|
||||
char name[16];
|
||||
snd_pcm_debug_name(substream, name, sizeof(name));
|
||||
xrun_log_show(substream);
|
||||
|
|
|
@ -1022,7 +1022,7 @@ static void dummy_proc_write(struct snd_info_entry *entry,
|
|||
if (i >= ARRAY_SIZE(fields))
|
||||
continue;
|
||||
snd_info_get_str(item, ptr, sizeof(item));
|
||||
if (strict_strtoull(item, 0, &val))
|
||||
if (kstrtoull(item, 0, &val))
|
||||
continue;
|
||||
if (fields[i].size == sizeof(int))
|
||||
*get_dummy_int_ptr(dummy, fields[i].offset) = val;
|
||||
|
|
|
@ -49,7 +49,6 @@ struct fwspk {
|
|||
struct snd_card *card;
|
||||
struct fw_unit *unit;
|
||||
const struct device_info *device_info;
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct mutex mutex;
|
||||
struct cmp_connection connection;
|
||||
struct amdtp_out_stream stream;
|
||||
|
@ -363,8 +362,7 @@ static int fwspk_create_pcm(struct fwspk *fwspk)
|
|||
return err;
|
||||
pcm->private_data = fwspk;
|
||||
strcpy(pcm->name, fwspk->device_info->short_name);
|
||||
fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
|
||||
fwspk->pcm->ops = &ops;
|
||||
snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -443,8 +443,7 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
|
|||
for (i = 0; i < 8; ++i)
|
||||
iwave[i] = snd_gf1_peek(gus, bank_pos + i);
|
||||
#ifdef CONFIG_SND_DEBUG_ROM
|
||||
printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos,
|
||||
8, iwave);
|
||||
printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave);
|
||||
#endif
|
||||
if (strncmp(iwave, "INTRWAVE", 8))
|
||||
continue; /* first check */
|
||||
|
|
|
@ -557,7 +557,6 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
|
|||
unsigned long flags;
|
||||
int err = 0, n = 0;
|
||||
struct dma_buffparms *dmap = adev->dmap_in;
|
||||
int go;
|
||||
|
||||
if (!(adev->open_mode & OPEN_READ))
|
||||
return -EIO;
|
||||
|
@ -584,7 +583,7 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
|
|||
spin_unlock_irqrestore(&dmap->lock,flags);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if ((go = adev->go))
|
||||
if (adev->go)
|
||||
timeout = dmabuf_timeout(dmap);
|
||||
|
||||
spin_unlock_irqrestore(&dmap->lock,flags);
|
||||
|
|
|
@ -152,14 +152,9 @@ config SND_HDA_CODEC_HDMI
|
|||
This module is automatically loaded at probing.
|
||||
|
||||
config SND_HDA_I915
|
||||
bool "Build Display HD-audio controller/codec power well support for i915 cards"
|
||||
bool
|
||||
default y
|
||||
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
|
||||
bool "Build Cirrus Logic codec support"
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
||||
/* 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
|
||||
* @bus: the BUS
|
||||
|
@ -1216,11 +1274,13 @@ static void hda_jackpoll_work(struct work_struct *work)
|
|||
{
|
||||
struct hda_codec *codec =
|
||||
container_of(work, struct hda_codec, jackpoll_work.work);
|
||||
if (!codec->jackpoll_interval)
|
||||
return;
|
||||
|
||||
snd_hda_jack_set_dirty_all(codec);
|
||||
snd_hda_jack_poll_all(codec);
|
||||
|
||||
if (!codec->jackpoll_interval)
|
||||
return;
|
||||
|
||||
queue_delayed_work(codec->bus->workq, &codec->jackpoll_work,
|
||||
codec->jackpoll_interval);
|
||||
}
|
||||
|
|
|
@ -94,6 +94,8 @@ enum {
|
|||
#define AC_VERB_GET_HDMI_DIP_XMIT 0x0f32
|
||||
#define AC_VERB_GET_HDMI_CP_CTRL 0x0f33
|
||||
#define AC_VERB_GET_HDMI_CHAN_SLOT 0x0f34
|
||||
#define AC_VERB_GET_DEVICE_SEL 0xf35
|
||||
#define AC_VERB_GET_DEVICE_LIST 0xf36
|
||||
|
||||
/*
|
||||
* SET verbs
|
||||
|
@ -133,6 +135,7 @@ enum {
|
|||
#define AC_VERB_SET_HDMI_DIP_XMIT 0x732
|
||||
#define AC_VERB_SET_HDMI_CP_CTRL 0x733
|
||||
#define AC_VERB_SET_HDMI_CHAN_SLOT 0x734
|
||||
#define AC_VERB_SET_DEVICE_SEL 0x735
|
||||
|
||||
/*
|
||||
* Parameter IDs
|
||||
|
@ -154,6 +157,7 @@ enum {
|
|||
#define AC_PAR_GPIO_CAP 0x11
|
||||
#define AC_PAR_AMP_OUT_CAP 0x12
|
||||
#define AC_PAR_VOL_KNB_CAP 0x13
|
||||
#define AC_PAR_DEVLIST_LEN 0x15
|
||||
#define AC_PAR_HDMI_LPCM_CAP 0x20
|
||||
|
||||
/*
|
||||
|
@ -251,6 +255,11 @@ enum {
|
|||
#define AC_UNSOL_RES_TAG_SHIFT 26
|
||||
#define AC_UNSOL_RES_SUBTAG (0x1f<<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_PD (1<<0) /* pinsense detect */
|
||||
#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_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
|
||||
*/
|
||||
|
@ -460,6 +473,11 @@ enum {
|
|||
#define AC_DEFCFG_PORT_CONN (0x3<<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) */
|
||||
enum {
|
||||
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 epss:1; /* supporting EPSS? */
|
||||
unsigned int cached_write:1; /* write only to caches */
|
||||
unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
|
||||
#ifdef CONFIG_PM
|
||||
unsigned int power_on :1; /* current (global) power-state */
|
||||
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);
|
||||
int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
|
||||
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,
|
||||
u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
|
||||
|
||||
|
|
|
@ -142,6 +142,9 @@ static void parse_user_hints(struct hda_codec *codec)
|
|||
val = snd_hda_get_bool_hint(codec, "primary_hp");
|
||||
if (val >= 0)
|
||||
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");
|
||||
if (val >= 0)
|
||||
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,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol);
|
||||
|
||||
enum {
|
||||
HDA_CTL_WIDGET_VOL,
|
||||
|
@ -830,7 +835,13 @@ static const struct snd_kcontrol_new control_templates[] = {
|
|||
.put = hda_gen_mixer_mute_put, /* replaced */
|
||||
.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 */
|
||||
|
@ -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 */
|
||||
static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
|
||||
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[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);
|
||||
}
|
||||
|
||||
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? */
|
||||
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,
|
||||
spec->multiout.extra_out_nid,
|
||||
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) {
|
||||
err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
|
||||
if (!err)
|
||||
|
@ -1554,7 +1578,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
|
|||
spec->private_dac_nids, spec->out_paths,
|
||||
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) {
|
||||
/* try to fill multi-io first */
|
||||
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;
|
||||
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);
|
||||
if (err < 0)
|
||||
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]);
|
||||
}
|
||||
|
||||
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)
|
||||
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 */
|
||||
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++) {
|
||||
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 */
|
||||
if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
|
||||
continue;
|
||||
present |= snd_hda_jack_detect(codec, nid);
|
||||
if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
|
||||
present = true;
|
||||
}
|
||||
return present;
|
||||
}
|
||||
|
||||
/* standard HP/line-out auto-mute helper */
|
||||
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;
|
||||
int i;
|
||||
|
@ -3752,10 +3780,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
|
|||
break;
|
||||
|
||||
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)
|
||||
spec->mute_bits |= (1ULL << nid);
|
||||
spec->mute_bits |= (1ULL << mute_nid);
|
||||
else
|
||||
spec->mute_bits &= ~(1ULL << nid);
|
||||
spec->mute_bits &= ~(1ULL << mute_nid);
|
||||
set_pin_eapd(codec, nid, !mute);
|
||||
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)
|
||||
{
|
||||
struct hda_gen_spec *spec = codec->spec;
|
||||
int *paths;
|
||||
int on;
|
||||
|
||||
/* Control HP pins/amps depending on master_mute state;
|
||||
* in general, HP pins/amps control should be enabled in all cases,
|
||||
* 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),
|
||||
spec->autocfg.hp_pins, spec->master_mute);
|
||||
spec->autocfg.hp_pins, paths, spec->master_mute);
|
||||
|
||||
if (!spec->automute_speaker)
|
||||
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->master_mute;
|
||||
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),
|
||||
spec->autocfg.speaker_pins, on);
|
||||
spec->autocfg.speaker_pins, paths, on);
|
||||
|
||||
/* toggle line-out mutes if needed, too */
|
||||
/* 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->master_mute;
|
||||
spec->line_out_muted = on;
|
||||
paths = spec->out_paths;
|
||||
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);
|
||||
|
||||
|
@ -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 */
|
||||
if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
|
||||
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);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -220,6 +220,7 @@ struct hda_gen_spec {
|
|||
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 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 inv_dmic_split:1; /* inverted dmic w/a for conexant */
|
||||
unsigned int own_eapd_ctl:1; /* set EAPD by own function */
|
||||
|
|
|
@ -295,7 +295,7 @@ static ssize_t type##_store(struct device *dev, \
|
|||
struct snd_hwdep *hwdep = dev_get_drvdata(dev); \
|
||||
struct hda_codec *codec = hwdep->private_data; \
|
||||
unsigned long val; \
|
||||
int err = strict_strtoul(buf, 0, &val); \
|
||||
int err = kstrtoul(buf, 0, &val); \
|
||||
if (err < 0) \
|
||||
return err; \
|
||||
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);
|
||||
if (!p)
|
||||
ret = -ENOENT;
|
||||
else if (strict_strtoul(p, 0, &val))
|
||||
else if (kstrtoul(p, 0, &val))
|
||||
ret = -EINVAL;
|
||||
else {
|
||||
*valp = val;
|
||||
|
@ -751,7 +751,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
|
|||
struct hda_codec **codecp) \
|
||||
{ \
|
||||
unsigned long val; \
|
||||
if (!strict_strtoul(buf, 0, &val)) \
|
||||
if (!kstrtoul(buf, 0, &val)) \
|
||||
(*codecp)->name = val; \
|
||||
}
|
||||
|
||||
|
|
|
@ -1160,7 +1160,7 @@ static int azx_reset(struct azx *chip, int full_reset)
|
|||
goto __skip;
|
||||
|
||||
/* clear STATESTS */
|
||||
azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
|
||||
azx_writew(chip, STATESTS, STATESTS_INT_MASK);
|
||||
|
||||
/* reset controller */
|
||||
azx_enter_link_reset(chip);
|
||||
|
@ -1242,7 +1242,7 @@ static void azx_int_clear(struct azx *chip)
|
|||
}
|
||||
|
||||
/* clear STATESTS */
|
||||
azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
|
||||
azx_writew(chip, STATESTS, STATESTS_INT_MASK);
|
||||
|
||||
/* clear rirb status */
|
||||
azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
|
||||
|
@ -1451,8 +1451,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
|
|||
|
||||
#if 0
|
||||
/* clear state status int */
|
||||
if (azx_readb(chip, STATESTS) & 0x04)
|
||||
azx_writeb(chip, STATESTS, 0x04);
|
||||
if (azx_readw(chip, STATESTS) & 0x04)
|
||||
azx_writew(chip, STATESTS, 0x04);
|
||||
#endif
|
||||
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 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_enter_link_reset(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 azx *chip = card->private_data;
|
||||
struct hda_bus *bus;
|
||||
struct hda_codec *codec;
|
||||
int status;
|
||||
|
||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
|
||||
hda_display_power(true);
|
||||
|
||||
/* Read STATESTS before controller reset */
|
||||
status = azx_readw(chip, STATESTS);
|
||||
|
||||
azx_init_pci(chip);
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -3831,11 +3855,13 @@ static int azx_probe_continue(struct azx *chip)
|
|||
|
||||
/* Request power well for Haswell HDA controller and codec */
|
||||
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
|
||||
#ifdef CONFIG_SND_HDA_I915
|
||||
err = hda_i915_init();
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
|
||||
goto out_free;
|
||||
}
|
||||
#endif
|
||||
hda_display_power(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -194,18 +194,24 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
|
|||
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
|
||||
* @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);
|
||||
return get_jack_plug_state(sense);
|
||||
struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
|
||||
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
|
||||
|
@ -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,
|
||||
hda_nid_t gating_nid)
|
||||
{
|
||||
struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid);
|
||||
struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid);
|
||||
struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
|
||||
struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
|
||||
|
||||
if (!gated || !gating)
|
||||
return -EINVAL;
|
||||
|
|
|
@ -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);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -582,6 +582,36 @@ static void print_gpio(struct snd_info_buffer *buffer,
|
|||
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,
|
||||
struct snd_info_buffer *buffer)
|
||||
{
|
||||
|
@ -751,6 +781,9 @@ static void print_codec_info(struct snd_info_entry *entry,
|
|||
(wid_caps & AC_WCAP_DELAY) >>
|
||||
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)
|
||||
print_conn_list(buffer, codec, nid, wid_type,
|
||||
conn, conn_len);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -66,6 +66,8 @@ struct conexant_spec {
|
|||
hda_nid_t eapds[4];
|
||||
bool dynamic_eapd;
|
||||
|
||||
unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
|
||||
|
||||
#ifdef ENABLE_CXT_STATIC_QUIRKS
|
||||
const struct snd_kcontrol_new *mixers[5];
|
||||
int num_mixers;
|
||||
|
@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec)
|
|||
snd_hda_gen_init(codec);
|
||||
if (!spec->dynamic_eapd)
|
||||
cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
|
||||
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -3224,6 +3229,8 @@ enum {
|
|||
CXT_PINCFG_LEMOTE_A1205,
|
||||
CXT_FIXUP_STEREO_DMIC,
|
||||
CXT_FIXUP_INC_MIC_BOOST,
|
||||
CXT_FIXUP_HEADPHONE_MIC_PIN,
|
||||
CXT_FIXUP_HEADPHONE_MIC,
|
||||
};
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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 */
|
||||
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
|
||||
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
|
||||
|
@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = {
|
|||
.type = HDA_FIXUP_FUNC,
|
||||
.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[] = {
|
||||
|
@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_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(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
|
||||
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, 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);
|
||||
|
||||
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)
|
||||
goto error;
|
||||
|
||||
|
@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
|
|||
codec->bus->allow_bus_reset = 1;
|
||||
}
|
||||
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
|
|
|
@ -959,6 +959,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|||
int pin_nid;
|
||||
int pin_idx;
|
||||
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);
|
||||
if (!jack)
|
||||
|
@ -967,8 +968,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
|
|||
jack->jack_dirty = 1;
|
||||
|
||||
_snd_printd(SND_PR_VERBOSE,
|
||||
"HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
|
||||
codec->addr, pin_nid,
|
||||
"HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
|
||||
codec->addr, pin_nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
|
||||
!!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
|
||||
|
||||
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;
|
||||
}
|
||||
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->dp_mst = true;
|
||||
}
|
||||
|
||||
generic_hdmi_init_per_pins(codec);
|
||||
|
||||
|
|
|
@ -282,6 +282,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
|
|||
{
|
||||
alc_auto_setup_eapd(codec, false);
|
||||
msleep(200);
|
||||
snd_hda_shutup_pins(codec);
|
||||
}
|
||||
|
||||
/* generic EAPD initialization */
|
||||
|
@ -826,7 +827,8 @@ static inline void alc_shutup(struct hda_codec *codec)
|
|||
|
||||
if (spec && spec->shutup)
|
||||
spec->shutup(codec);
|
||||
snd_hda_shutup_pins(codec);
|
||||
else
|
||||
snd_hda_shutup_pins(codec);
|
||||
}
|
||||
|
||||
#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)
|
||||
{
|
||||
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_multi_io = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static const struct hda_fixup alc882_fixups[] = {
|
||||
|
@ -2533,6 +2537,7 @@ enum {
|
|||
ALC269_TYPE_ALC269VD,
|
||||
ALC269_TYPE_ALC280,
|
||||
ALC269_TYPE_ALC282,
|
||||
ALC269_TYPE_ALC283,
|
||||
ALC269_TYPE_ALC284,
|
||||
ALC269_TYPE_ALC286,
|
||||
};
|
||||
|
@ -2558,6 +2563,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
|
|||
case ALC269_TYPE_ALC269VB:
|
||||
case ALC269_TYPE_ALC269VD:
|
||||
case ALC269_TYPE_ALC282:
|
||||
case ALC269_TYPE_ALC283:
|
||||
case ALC269_TYPE_ALC286:
|
||||
ssids = alc269_ssids;
|
||||
break;
|
||||
|
@ -2583,15 +2589,81 @@ static void alc269_shutup(struct hda_codec *codec)
|
|||
{
|
||||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (spec->codec_variant != ALC269_TYPE_ALC269VB)
|
||||
return;
|
||||
|
||||
if (spec->codec_variant == ALC269_TYPE_ALC269VB)
|
||||
alc269vb_toggle_power_output(codec, 0);
|
||||
if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
|
||||
(alc_get_coef0(codec) & 0x00ff) == 0x018) {
|
||||
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,
|
||||
|
@ -2722,6 +2794,7 @@ static int alc269_resume(struct hda_codec *codec)
|
|||
hda_call_check_power_status(codec, 0x01);
|
||||
if (spec->has_alc5505_dsp)
|
||||
alc5505_dsp_resume(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#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);
|
||||
}
|
||||
|
||||
/* 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,
|
||||
const struct hda_fixup *fix,
|
||||
int action)
|
||||
|
@ -3268,11 +3363,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
|
|||
struct alc_spec *spec = codec->spec;
|
||||
|
||||
if (action == HDA_FIXUP_ACT_PROBE) {
|
||||
if (snd_BUG_ON(!spec->gen.am_entry[1].pin ||
|
||||
!spec->gen.autocfg.hp_pins[0]))
|
||||
int mic_pin = find_ext_mic_pin(codec);
|
||||
int hp_pin = spec->gen.autocfg.hp_pins[0];
|
||||
|
||||
if (snd_BUG_ON(!mic_pin || !hp_pin))
|
||||
return;
|
||||
snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin,
|
||||
spec->gen.autocfg.hp_pins[0]);
|
||||
snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
ALC269_FIXUP_SONY_VAIO,
|
||||
ALC275_FIXUP_SONY_VAIO_GPIO2,
|
||||
|
@ -3344,6 +3479,7 @@ enum {
|
|||
ALC269_FIXUP_ACER_AC700,
|
||||
ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
|
||||
ALC269VB_FIXUP_ORDISSIMO_EVE2,
|
||||
ALC283_FIXUP_CHROME_BOOK,
|
||||
};
|
||||
|
||||
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[] = {
|
||||
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, 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, 0x05be, "Dell", ALC269_FIXUP_DELL2_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, 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, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
|
||||
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, 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, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
|
||||
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(0x17aa, 0x20f2, "Thinkpad SL410/510", 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, 0x21f3, "Thinkpad T430", 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, 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, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
|
||||
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:
|
||||
spec->codec_variant = ALC269_TYPE_ALC280;
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0282:
|
||||
case 0x10ec0283:
|
||||
spec->codec_variant = ALC269_TYPE_ALC282;
|
||||
break;
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0283:
|
||||
spec->codec_variant = ALC269_TYPE_ALC283;
|
||||
spec->shutup = alc283_shutup;
|
||||
spec->init_hook = alc283_init;
|
||||
break;
|
||||
case 0x10ec0284:
|
||||
case 0x10ec0292:
|
||||
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.resume = alc269_resume;
|
||||
#endif
|
||||
spec->shutup = alc269_shutup;
|
||||
if (!spec->shutup)
|
||||
spec->shutup = alc269_shutup;
|
||||
|
||||
snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
|
||||
|
||||
|
|
|
@ -158,6 +158,7 @@ enum {
|
|||
STAC_D965_VERBS,
|
||||
STAC_DELL_3ST,
|
||||
STAC_DELL_BIOS,
|
||||
STAC_DELL_BIOS_AMIC,
|
||||
STAC_DELL_BIOS_SPDIF,
|
||||
STAC_927X_DELL_DMIC,
|
||||
STAC_927X_VOLKNOB,
|
||||
|
@ -3231,8 +3232,6 @@ static const struct hda_fixup stac927x_fixups[] = {
|
|||
[STAC_DELL_BIOS] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.v.pins = (const struct hda_pintbl[]) {
|
||||
/* configure the analog microphone on some laptops */
|
||||
{ 0x0c, 0x90a79130 },
|
||||
/* correct the front output jack as a hp out */
|
||||
{ 0x0f, 0x0221101f },
|
||||
/* correct the front input jack as a mic */
|
||||
|
@ -3242,6 +3241,16 @@ static const struct hda_fixup stac927x_fixups[] = {
|
|||
.chained = true,
|
||||
.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] = {
|
||||
.type = HDA_FIXUP_PINS,
|
||||
.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_DELL_3ST, .name = "dell-3stack" },
|
||||
{ .id = STAC_DELL_BIOS, .name = "dell-bios" },
|
||||
{ .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
|
||||
{ .id = STAC_927X_VOLKNOB, .name = "volknob" },
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -207,9 +207,9 @@ static void vt1708_stop_hp_work(struct hda_codec *codec)
|
|||
return;
|
||||
if (spec->hp_work_active) {
|
||||
snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
|
||||
codec->jackpoll_interval = 0;
|
||||
cancel_delayed_work_sync(&codec->jackpoll_work);
|
||||
spec->hp_work_active = false;
|
||||
codec->jackpoll_interval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/vmalloc.h>
|
||||
|
||||
#include <sound/core.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_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 {
|
||||
spinlock_t lock;
|
||||
|
@ -214,6 +240,13 @@ struct rme96 {
|
|||
|
||||
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 *capture_substream;
|
||||
|
||||
|
@ -344,6 +377,8 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
|
|||
{
|
||||
.info = (SNDRV_PCM_INFO_MMAP_IOMEM |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.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 |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.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 |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.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 |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
SNDRV_PCM_INFO_SYNC_START |
|
||||
SNDRV_PCM_INFO_RESUME |
|
||||
SNDRV_PCM_INFO_INTERLEAVED |
|
||||
SNDRV_PCM_INFO_PAUSE),
|
||||
.formats = (SNDRV_PCM_FMTBIT_S16_LE |
|
||||
|
@ -1045,54 +1086,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
|
|||
}
|
||||
|
||||
static void
|
||||
snd_rme96_playback_start(struct rme96 *rme96,
|
||||
int from_pause)
|
||||
snd_rme96_trigger(struct rme96 *rme96,
|
||||
int op)
|
||||
{
|
||||
if (!from_pause) {
|
||||
if (op & RME96_TB_RESET_PLAYPOS)
|
||||
writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
|
||||
}
|
||||
|
||||
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) {
|
||||
if (op & RME96_TB_RESET_CAPTUREPOS)
|
||||
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);
|
||||
}
|
||||
|
||||
rme96->wcreg |= RME96_WCR_START_2;
|
||||
if (op & RME96_TB_CLEAR_CAPTURE_IRQ) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
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 snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
spin_lock_irq(&rme96->lock);
|
||||
if (rme96->playback_substream != NULL) {
|
||||
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 snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
runtime->hw = snd_rme96_capture_spdif_info;
|
||||
if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
|
||||
(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 snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
spin_lock_irq(&rme96->lock);
|
||||
if (rme96->playback_substream != NULL) {
|
||||
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 snd_pcm_runtime *runtime = substream->runtime;
|
||||
|
||||
snd_pcm_set_sync(substream);
|
||||
runtime->hw = snd_rme96_capture_adat_info;
|
||||
if (snd_rme96_getinputtype(rme96) == RME96_INPUT_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);
|
||||
if (RME96_ISPLAYING(rme96)) {
|
||||
snd_rme96_playback_stop(rme96);
|
||||
snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
|
||||
}
|
||||
rme96->playback_substream = NULL;
|
||||
rme96->playback_periodsize = 0;
|
||||
|
@ -1309,7 +1335,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)
|
|||
|
||||
spin_lock_irq(&rme96->lock);
|
||||
if (RME96_ISRECORDING(rme96)) {
|
||||
snd_rme96_capture_stop(rme96);
|
||||
snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
|
||||
}
|
||||
rme96->capture_substream = NULL;
|
||||
rme96->capture_periodsize = 0;
|
||||
|
@ -1324,7 +1350,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
spin_lock_irq(&rme96->lock);
|
||||
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);
|
||||
spin_unlock_irq(&rme96->lock);
|
||||
|
@ -1338,7 +1364,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
|
|||
|
||||
spin_lock_irq(&rme96->lock);
|
||||
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);
|
||||
spin_unlock_irq(&rme96->lock);
|
||||
|
@ -1350,41 +1376,55 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
|
|||
int cmd)
|
||||
{
|
||||
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) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (!RME96_ISPLAYING(rme96)) {
|
||||
if (substream != rme96->playback_substream) {
|
||||
if (substream != rme96->playback_substream)
|
||||
return -EBUSY;
|
||||
}
|
||||
snd_rme96_playback_start(rme96, 0);
|
||||
snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
|
||||
: RME96_START_PLAYBACK);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (RME96_ISPLAYING(rme96)) {
|
||||
if (substream != rme96->playback_substream) {
|
||||
if (substream != rme96->playback_substream)
|
||||
return -EBUSY;
|
||||
}
|
||||
snd_rme96_playback_stop(rme96);
|
||||
snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
|
||||
: RME96_STOP_PLAYBACK);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (RME96_ISPLAYING(rme96)) {
|
||||
snd_rme96_playback_stop(rme96);
|
||||
}
|
||||
if (RME96_ISPLAYING(rme96))
|
||||
snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
|
||||
: RME96_STOP_PLAYBACK);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (!RME96_ISPLAYING(rme96)) {
|
||||
snd_rme96_playback_start(rme96, 1);
|
||||
}
|
||||
if (!RME96_ISPLAYING(rme96))
|
||||
snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
|
||||
: RME96_RESUME_PLAYBACK);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1393,38 +1433,51 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
|
|||
int cmd)
|
||||
{
|
||||
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) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
if (!RME96_ISRECORDING(rme96)) {
|
||||
if (substream != rme96->capture_substream) {
|
||||
if (substream != rme96->capture_substream)
|
||||
return -EBUSY;
|
||||
}
|
||||
snd_rme96_capture_start(rme96, 0);
|
||||
snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
|
||||
: RME96_START_CAPTURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_SUSPEND:
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
if (RME96_ISRECORDING(rme96)) {
|
||||
if (substream != rme96->capture_substream) {
|
||||
if (substream != rme96->capture_substream)
|
||||
return -EBUSY;
|
||||
}
|
||||
snd_rme96_capture_stop(rme96);
|
||||
snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
|
||||
: RME96_STOP_CAPTURE);
|
||||
}
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
if (RME96_ISRECORDING(rme96)) {
|
||||
snd_rme96_capture_stop(rme96);
|
||||
}
|
||||
if (RME96_ISRECORDING(rme96))
|
||||
snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
|
||||
: RME96_STOP_CAPTURE);
|
||||
break;
|
||||
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
if (!RME96_ISRECORDING(rme96)) {
|
||||
snd_rme96_capture_start(rme96, 1);
|
||||
}
|
||||
if (!RME96_ISRECORDING(rme96))
|
||||
snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
|
||||
: RME96_RESUME_CAPTURE);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -1505,8 +1558,7 @@ snd_rme96_free(void *private_data)
|
|||
return;
|
||||
}
|
||||
if (rme96->irq >= 0) {
|
||||
snd_rme96_playback_stop(rme96);
|
||||
snd_rme96_capture_stop(rme96);
|
||||
snd_rme96_trigger(rme96, RME96_STOP_BOTH);
|
||||
rme96->areg &= ~RME96_AR_DAC_EN;
|
||||
writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
|
||||
free_irq(rme96->irq, (void *)rme96);
|
||||
|
@ -1520,6 +1572,10 @@ snd_rme96_free(void *private_data)
|
|||
pci_release_regions(rme96->pci);
|
||||
rme96->port = 0;
|
||||
}
|
||||
#ifdef CONFIG_PM
|
||||
vfree(rme96->playback_suspend_buffer);
|
||||
vfree(rme96->capture_suspend_buffer);
|
||||
#endif
|
||||
pci_disable_device(rme96->pci);
|
||||
}
|
||||
|
||||
|
@ -1606,8 +1662,7 @@ snd_rme96_create(struct rme96 *rme96)
|
|||
rme96->capture_periodsize = 0;
|
||||
|
||||
/* make sure playback/capture is stopped, if by some reason active */
|
||||
snd_rme96_playback_stop(rme96);
|
||||
snd_rme96_capture_stop(rme96);
|
||||
snd_rme96_trigger(rme96, RME96_STOP_BOTH);
|
||||
|
||||
/* set default values in registers */
|
||||
rme96->wcreg =
|
||||
|
@ -2319,6 +2374,87 @@ snd_rme96_create_switches(struct snd_card *card,
|
|||
* 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)
|
||||
{
|
||||
snd_rme96_free(card->private_data);
|
||||
|
@ -2355,6 +2491,23 @@ snd_rme96_probe(struct pci_dev *pci,
|
|||
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");
|
||||
switch (rme96->pci->device) {
|
||||
case PCI_DEVICE_ID_RME_DIGI96:
|
||||
|
@ -2397,6 +2550,10 @@ static struct pci_driver rme96_driver = {
|
|||
.id_table = snd_rme96_ids,
|
||||
.probe = snd_rme96_probe,
|
||||
.remove = snd_rme96_remove,
|
||||
#ifdef CONFIG_PM
|
||||
.suspend = snd_rme96_suspend,
|
||||
.resume = snd_rme96_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
module_pci_driver(rme96_driver);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -408,7 +408,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
|
|||
return 0;
|
||||
|
||||
fail_put_lrclk:
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
clk_put(info->lrclk);
|
||||
fail_put_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);
|
||||
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
clk_put(info->lrclk);
|
||||
clk_put(info->sclk);
|
||||
clk_put(info->mclk);
|
||||
|
|
|
@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = {
|
|||
{"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 = {
|
||||
.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)
|
||||
|
|
|
@ -50,8 +50,6 @@ static const struct regmap_range_cfg rt5640_ranges[] = {
|
|||
|
||||
static struct reg_default init_list[] = {
|
||||
{RT5640_PR_BASE + 0x3d, 0x3600},
|
||||
{RT5640_PR_BASE + 0x1c, 0x0D21},
|
||||
{RT5640_PR_BASE + 0x1b, 0x0000},
|
||||
{RT5640_PR_BASE + 0x12, 0x0aa8},
|
||||
{RT5640_PR_BASE + 0x14, 0x0aaa},
|
||||
{RT5640_PR_BASE + 0x20, 0x6110},
|
||||
|
@ -384,15 +382,11 @@ static const SOC_ENUM_SINGLE_DECL(
|
|||
|
||||
static const struct snd_kcontrol_new rt5640_snd_controls[] = {
|
||||
/* 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,
|
||||
RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
|
||||
SOC_DOUBLE_TLV("Speaker Playback Volume", RT5640_SPK_VOL,
|
||||
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT, 39, 1, out_vol_tlv),
|
||||
/* 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,
|
||||
RT5640_VOL_L_SFT, RT5640_VOL_R_SFT, 1, 1),
|
||||
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),
|
||||
};
|
||||
|
||||
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 */
|
||||
static const char * const rt5640_stereo_adc1_src[] = {
|
||||
"DIG MIX", "ADC"
|
||||
|
@ -868,33 +878,6 @@ static const SOC_ENUM_SINGLE_DECL(
|
|||
static const struct snd_kcontrol_new rt5640_sdi_mux =
|
||||
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,
|
||||
struct snd_kcontrol *kcontrol, int event)
|
||||
{
|
||||
|
@ -943,6 +926,117 @@ static int rt5640_set_dmic2_event(struct snd_soc_dapm_widget *w,
|
|||
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[] = {
|
||||
SND_SOC_DAPM_SUPPLY("PLL1", RT5640_PWR_ANLG2,
|
||||
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)),
|
||||
SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
|
||||
RT5640_PWR_MA_BIT, 0, NULL, 0),
|
||||
SND_SOC_DAPM_SUPPLY("Improve HP Amp Drv", RT5640_PWR_ANLG1,
|
||||
SND_SOC_NOPM, 0, NULL, 0),
|
||||
SND_SOC_DAPM_PGA("HP L Amp", RT5640_PWR_ANLG1,
|
||||
SND_SOC_DAPM_SUPPLY_S("Improve HP Amp Drv", 1, SND_SOC_NOPM,
|
||||
0, 0, rt5640_hp_power_event, SND_SOC_DAPM_POST_PMU),
|
||||
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),
|
||||
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),
|
||||
SND_SOC_DAPM_SUPPLY("Improve SPK Amp Drv", RT5640_PWR_DIG1,
|
||||
SND_SOC_NOPM, 0, spk_event,
|
||||
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
|
||||
RT5640_PWR_CLS_D_BIT, 0, NULL, 0),
|
||||
|
||||
/* 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 */
|
||||
SND_SOC_DAPM_OUTPUT("SPOLP"),
|
||||
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 DAC1 Switch", "DAC L1"},
|
||||
{"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 DAC1 Switch", "DAC R1"},
|
||||
{"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 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", "BST1 Switch", "BST1"},
|
||||
|
||||
{"HP L Amp", NULL, "HPO MIX L"},
|
||||
{"HP R Amp", NULL, "HPO MIX R"},
|
||||
{"HP Amp", NULL, "HPO MIX L"},
|
||||
{"HP Amp", NULL, "HPO MIX R"},
|
||||
|
||||
{"SPOLP", NULL, "SPOL MIX"},
|
||||
{"SPOLN", NULL, "SPOL MIX"},
|
||||
{"SPORP", NULL, "SPOR MIX"},
|
||||
{"SPORN", NULL, "SPOR MIX"},
|
||||
{"Speaker L Playback", "Switch", "SPOL MIX"},
|
||||
{"Speaker R Playback", "Switch", "SPOR MIX"},
|
||||
{"SPOLP", NULL, "Speaker L Playback"},
|
||||
{"SPOLN", NULL, "Speaker L Playback"},
|
||||
{"SPORP", NULL, "Speaker R Playback"},
|
||||
{"SPORN", NULL, "Speaker R Playback"},
|
||||
|
||||
{"SPOLP", 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"},
|
||||
{"HPOR", NULL, "Improve HP Amp Drv"},
|
||||
|
||||
{"HPOL", NULL, "HP L Amp"},
|
||||
{"HPOR", NULL, "HP R Amp"},
|
||||
{"HP L Playback", "Switch", "HP Amp"},
|
||||
{"HP R Playback", "Switch", "HP Amp"},
|
||||
{"HPOL", NULL, "HP L Playback"},
|
||||
{"HPOR", NULL, "HP R Playback"},
|
||||
{"LOUTL", NULL, "LOUT MIX"},
|
||||
{"LOUTR", NULL, "LOUT 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_VREF1 | RT5640_PWR_MB |
|
||||
RT5640_PWR_BG | RT5640_PWR_VREF2);
|
||||
mdelay(10);
|
||||
usleep_range(10000, 15000);
|
||||
snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
|
||||
RT5640_PWR_FV1 | RT5640_PWR_FV2,
|
||||
RT5640_PWR_FV1 | RT5640_PWR_FV2);
|
||||
regcache_sync(rt5640->regmap);
|
||||
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);
|
||||
}
|
||||
|
@ -1846,8 +1955,6 @@ static int rt5640_probe(struct snd_soc_codec *codec)
|
|||
rt5640_set_bias_level(codec, SND_SOC_BIAS_OFF);
|
||||
|
||||
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_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,
|
||||
RT5640_IN_DF2, RT5640_IN_DF2);
|
||||
|
||||
rt5640->hp_mute = 1;
|
||||
|
||||
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
|
||||
rt5640_dai, ARRAY_SIZE(rt5640_dai));
|
||||
if (ret < 0)
|
||||
|
|
|
@ -145,6 +145,8 @@
|
|||
|
||||
|
||||
/* 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_WND_1 0x6c
|
||||
#define RT5640_WND_2 0x6d
|
||||
|
@ -153,6 +155,7 @@
|
|||
#define RT5640_WND_5 0x70
|
||||
#define RT5640_WND_8 0x73
|
||||
#define RT5640_DIP_SPK_INF 0x75
|
||||
#define RT5640_HP_DCC_INT1 0x77
|
||||
#define RT5640_EQ_BW_LOP 0xa0
|
||||
#define RT5640_EQ_GN_LOP 0xa1
|
||||
#define RT5640_EQ_FC_BP1 0xa2
|
||||
|
@ -1201,6 +1204,14 @@
|
|||
#define RT5640_CP_FQ2_SFT 4
|
||||
#define RT5640_CP_FQ3_MASK (0x7)
|
||||
#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) */
|
||||
#define RT5640_OSW_L_MASK (0x1 << 11)
|
||||
|
@ -2087,6 +2098,7 @@ struct rt5640_priv {
|
|||
int pll_out;
|
||||
|
||||
int dmic_en;
|
||||
bool hp_mute;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -561,8 +561,9 @@ static int ssm2602_suspend(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);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate)
|
|||
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,
|
||||
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);
|
||||
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
|
||||
|
@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
|
|||
.suspend = aic32x4_suspend,
|
||||
.resume = aic32x4_resume,
|
||||
.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,
|
||||
|
|
|
@ -1202,7 +1202,6 @@ static int wm8904_add_widgets(struct snd_soc_codec *codec)
|
|||
break;
|
||||
}
|
||||
|
||||
snd_soc_dapm_new_widgets(dapm);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -3174,7 +3174,7 @@ static ssize_t wm8962_beep_set(struct device *dev,
|
|||
long int time;
|
||||
int ret;
|
||||
|
||||
ret = strict_strtol(buf, 10, &time);
|
||||
ret = kstrtol(buf, 10, &time);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -421,13 +421,11 @@ static int dw_i2s_probe(struct platform_device *pdev)
|
|||
dw_i2s_dai, 1);
|
||||
if (ret != 0) {
|
||||
dev_err(&pdev->dev, "not able to register dai\n");
|
||||
goto err_set_drvdata;
|
||||
goto err_clk_disable;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_set_drvdata:
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
err_clk_disable:
|
||||
clk_disable(dev->clk);
|
||||
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);
|
||||
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
clk_put(dev->clk);
|
||||
|
||||
|
|
|
@ -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
|
||||
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
|
||||
tristate "SoC Audio support for I.MX boards with mc13783"
|
||||
depends on MFD_MC13783 && ARM
|
||||
|
|
|
@ -45,6 +45,7 @@ snd-soc-mx27vis-aic32x4-objs := mx27vis-aic32x4.o
|
|||
snd-soc-wm1133-ev1-objs := wm1133-ev1.o
|
||||
snd-soc-imx-sgtl5000-objs := imx-sgtl5000.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
|
||||
|
||||
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_SOC_IMX_SGTL5000) += snd-soc-imx-sgtl5000.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
|
||||
|
|
|
@ -411,8 +411,8 @@ static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
|
|||
return 0;
|
||||
}
|
||||
|
||||
int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
static int fsl_spdif_startup(struct snd_pcm_substream *substream,
|
||||
struct snd_soc_dai *cpu_dai)
|
||||
{
|
||||
struct snd_soc_pcm_runtime *rtd = substream->private_data;
|
||||
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;
|
||||
}
|
||||
|
||||
struct snd_soc_dai_ops fsl_spdif_dai_ops = {
|
||||
static struct snd_soc_dai_ops fsl_spdif_dai_ops = {
|
||||
.startup = fsl_spdif_startup,
|
||||
.hw_params = fsl_spdif_hw_params,
|
||||
.trigger = fsl_spdif_trigger,
|
||||
|
@ -555,7 +555,6 @@ struct snd_soc_dai_ops fsl_spdif_dai_ops = {
|
|||
|
||||
|
||||
/*
|
||||
* ============================================
|
||||
* FSL SPDIF IEC958 controller(mixer) functions
|
||||
*
|
||||
* Channel status get/put control
|
||||
|
@ -563,7 +562,6 @@ struct snd_soc_dai_ops fsl_spdif_dai_ops = {
|
|||
* Valid bit value get control
|
||||
* DPLL lock status get control
|
||||
* User bit sync mode selection control
|
||||
* ============================================
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
struct snd_soc_dai_driver fsl_spdif_dai = {
|
||||
static struct snd_soc_dai_driver fsl_spdif_dai = {
|
||||
.probe = &fsl_spdif_dai_probe,
|
||||
.playback = {
|
||||
.channels_min = 2,
|
||||
|
@ -942,11 +940,7 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
|
|||
.name = "fsl-spdif",
|
||||
};
|
||||
|
||||
/*
|
||||
* ================
|
||||
* FSL SPDIF REGMAP
|
||||
* ================
|
||||
*/
|
||||
/* FSL SPDIF REGMAP */
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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]);
|
||||
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]);
|
||||
|
||||
return 0;
|
||||
|
@ -1119,10 +1113,8 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
regs = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(regs)) {
|
||||
dev_err(&pdev->dev, "could not map device resources\n");
|
||||
if (IS_ERR(regs))
|
||||
return PTR_ERR(regs);
|
||||
}
|
||||
|
||||
spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
|
||||
"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);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
|
||||
goto error_dev;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = imx_pcm_dma_init(pdev);
|
||||
|
@ -1197,8 +1189,6 @@ static int fsl_spdif_probe(struct platform_device *pdev)
|
|||
|
||||
error_component:
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
error_dev:
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1207,7 +1197,6 @@ static int fsl_spdif_remove(struct platform_device *pdev)
|
|||
{
|
||||
imx_pcm_dma_exit(pdev);
|
||||
snd_soc_unregister_component(&pdev->dev);
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1114,7 +1114,6 @@ static int fsl_ssi_probe(struct platform_device *pdev)
|
|||
snd_soc_unregister_component(&pdev->dev);
|
||||
|
||||
error_dev:
|
||||
dev_set_drvdata(&pdev->dev, NULL);
|
||||
device_remove_file(&pdev->dev, dev_attr);
|
||||
|
||||
error_clk:
|
||||
|
|
|
@ -335,7 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev)
|
|||
if (audmux_type == IMX31_AUDMUX)
|
||||
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;
|
||||
}
|
||||
|
|
148
sound/soc/fsl/imx-spdif.c
Normal file
148
sound/soc/fsl/imx-spdif.c
Normal 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");
|
|
@ -105,6 +105,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev)
|
|||
static struct platform_driver asoc_simple_card = {
|
||||
.driver = {
|
||||
.name = "asoc-simple-card",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = asoc_simple_card_probe,
|
||||
.remove = asoc_simple_card_remove,
|
||||
|
@ -112,6 +113,7 @@ static struct platform_driver asoc_simple_card = {
|
|||
|
||||
module_platform_driver(asoc_simple_card);
|
||||
|
||||
MODULE_ALIAS("platform:asoc-simple-card");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_DESCRIPTION("ASoC Simple Sound Card");
|
||||
MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
config SND_KIRKWOOD_SOC
|
||||
tristate "SoC Audio for the Marvell Kirkwood chip"
|
||||
depends on ARCH_KIRKWOOD || COMPILE_TEST
|
||||
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
|
||||
depends on ARCH_KIRKWOOD || ARCH_DOVE || COMPILE_TEST
|
||||
help
|
||||
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
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include <sound/pcm_params.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/platform_data/asoc-kirkwood.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "kirkwood.h"
|
||||
|
||||
#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 kirkwood_dma_data *priv;
|
||||
struct resource *mem;
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
int err;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
dev_err(&pdev->dev, "no platform data ?!\n");
|
||||
if (np) {
|
||||
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;
|
||||
}
|
||||
|
||||
priv->burst = data->burst;
|
||||
|
||||
priv->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
priv->clk = devm_clk_get(&pdev->dev, np ? "internal" : NULL);
|
||||
if (IS_ERR(priv->clk)) {
|
||||
dev_err(&pdev->dev, "no clock\n");
|
||||
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;
|
||||
|
||||
/* Select the burst size */
|
||||
if (data->burst == 32) {
|
||||
if (priv->burst == 32) {
|
||||
priv->ctl_play |= KIRKWOOD_PLAYCTL_BURST_32;
|
||||
priv->ctl_rec |= KIRKWOOD_RECCTL_BURST_32;
|
||||
} else {
|
||||
|
@ -552,12 +557,21 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
|
|||
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 = {
|
||||
.probe = kirkwood_i2s_dev_probe,
|
||||
.remove = kirkwood_i2s_dev_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(mvebu_audio_of_match),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -105,11 +105,13 @@ static struct snd_soc_dai_link mxs_sgtl5000_dai[] = {
|
|||
.stream_name = "HiFi Playback",
|
||||
.codec_dai_name = "sgtl5000",
|
||||
.ops = &mxs_sgtl5000_hifi_ops,
|
||||
.playback_only = true,
|
||||
}, {
|
||||
.name = "HiFi Rx",
|
||||
.stream_name = "HiFi Capture",
|
||||
.codec_dai_name = "sgtl5000",
|
||||
.ops = &mxs_sgtl5000_hifi_ops,
|
||||
.capture_only = true,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -781,7 +781,7 @@ static ssize_t prop##_store(struct device *dev, \
|
|||
unsigned long val; \
|
||||
int status; \
|
||||
\
|
||||
status = strict_strtoul(buf, 0, &val); \
|
||||
status = kstrtoul(buf, 0, &val); \
|
||||
if (status) \
|
||||
return status; \
|
||||
\
|
||||
|
|
|
@ -90,6 +90,13 @@ static void dma_enqueue(struct snd_pcm_substream *substream)
|
|||
dma_info.period = prtd->dma_period;
|
||||
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) {
|
||||
pr_debug("dma_loaded: %d\n", prtd->dma_loaded);
|
||||
|
||||
|
|
|
@ -235,6 +235,8 @@ struct fsi_stream {
|
|||
struct sh_dmae_slave slave; /* see fsi_handler_init() */
|
||||
struct work_struct work;
|
||||
dma_addr_t dma;
|
||||
int loop_cnt;
|
||||
int additional_pos;
|
||||
};
|
||||
|
||||
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) |
|
||||
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,
|
||||
snd_pcm_lib_buffer_bytes(io->substream), dir);
|
||||
return 0;
|
||||
|
@ -1305,11 +1309,15 @@ static int fsi_dma_quit(struct fsi_priv *fsi, struct fsi_stream *io)
|
|||
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;
|
||||
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)
|
||||
|
@ -1321,7 +1329,7 @@ static void fsi_dma_complete(void *data)
|
|||
enum dma_data_direction dir = fsi_stream_is_play(fsi, io) ?
|
||||
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);
|
||||
|
||||
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;
|
||||
enum dma_data_direction dir;
|
||||
int is_play = fsi_stream_is_play(fsi, io);
|
||||
int len;
|
||||
int len, i;
|
||||
dma_addr_t buf;
|
||||
|
||||
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;
|
||||
dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
|
||||
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_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
if (!desc) {
|
||||
dev_err(dai->dev, "dmaengine_prep_slave_sg() fail\n");
|
||||
return;
|
||||
dma_sync_single_for_device(dai->dev, buf, len, dir);
|
||||
|
||||
desc = dmaengine_prep_slave_single(io->chan, buf, len, dir,
|
||||
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||
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;
|
||||
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->loop_cnt = 1;
|
||||
|
||||
/*
|
||||
* FIXME
|
||||
|
|
|
@ -203,7 +203,7 @@ static ssize_t pmdown_time_set(struct device *dev,
|
|||
struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
ret = strict_strtol(buf, 10, &rtd->pmdown_time);
|
||||
ret = kstrtol(buf, 10, &rtd->pmdown_time);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -248,6 +248,7 @@ static ssize_t codec_reg_write_file(struct file *file,
|
|||
char *start = buf;
|
||||
unsigned long reg, value;
|
||||
struct snd_soc_codec *codec = file->private_data;
|
||||
int ret;
|
||||
|
||||
buf_size = min(count, (sizeof(buf)-1));
|
||||
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);
|
||||
while (*start == ' ')
|
||||
start++;
|
||||
if (strict_strtoul(start, 16, &value))
|
||||
return -EINVAL;
|
||||
ret = kstrtoul(start, 16, &value);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Userspace has been fiddling around behind the kernel's back */
|
||||
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;
|
||||
|
||||
/* Make sure all DAPM widgets are instantiated */
|
||||
snd_soc_dapm_new_widgets(&codec->dapm);
|
||||
|
||||
/* machine controls, routes and widgets are not prefixed */
|
||||
temp = codec->name_prefix;
|
||||
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,
|
||||
card->num_dapm_routes);
|
||||
|
||||
snd_soc_dapm_new_widgets(&card->dapm);
|
||||
|
||||
for (i = 0; i < card->num_links; i++) {
|
||||
dai_link = &card->dai_link[i];
|
||||
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)
|
||||
list_for_each_entry(codec, &card->codec_dev_list, card_list)
|
||||
snd_soc_dapm_auto_nc_codec_pins(codec);
|
||||
|
||||
snd_soc_dapm_new_widgets(card);
|
||||
|
||||
ret = snd_card_register(card->snd_card);
|
||||
if (ret < 0) {
|
||||
dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
|
||||
|
|
|
@ -229,6 +229,8 @@ static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
|
|||
template.id = snd_soc_dapm_kcontrol;
|
||||
template.name = kcontrol->id.name;
|
||||
|
||||
data->value = template.on_val;
|
||||
|
||||
data->widget = snd_soc_dapm_new_control(widget->dapm,
|
||||
&template);
|
||||
if (!data->widget) {
|
||||
|
@ -2374,6 +2376,9 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
|
|||
wsource->ext = 1;
|
||||
}
|
||||
|
||||
dapm_mark_dirty(wsource, "Route added");
|
||||
dapm_mark_dirty(wsink, "Route added");
|
||||
|
||||
/* connect static paths */
|
||||
if (control == NULL) {
|
||||
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;
|
||||
}
|
||||
|
||||
dapm_mark_dirty(wsource, "Route added");
|
||||
dapm_mark_dirty(wsink, "Route added");
|
||||
|
||||
return 0;
|
||||
err:
|
||||
kfree(path);
|
||||
|
@ -2712,9 +2714,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
|
|||
*
|
||||
* 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;
|
||||
unsigned int val;
|
||||
|
||||
|
|
|
@ -183,8 +183,6 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
|
|||
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
|
||||
* implementations are likely to set their state before the
|
||||
* card has an opportunity to associate pins.
|
||||
|
|
|
@ -2020,6 +2020,16 @@ int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num)
|
|||
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 */
|
||||
if (rtd->dai_link->no_pcm) {
|
||||
snprintf(new_name, sizeof(new_name), "(%s)",
|
||||
|
|
|
@ -346,10 +346,10 @@ static int usb6fire_fw_check(u8 *version)
|
|||
if (!memcmp(version, known_fw_versions + i, 2))
|
||||
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 "
|
||||
"still happens, check your firmware installation.",
|
||||
4, version);
|
||||
version);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -418,6 +418,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
|
|||
struct snd_usb_endpoint *ep;
|
||||
int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
|
||||
if (WARN_ON(!alts))
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&chip->mutex);
|
||||
|
||||
list_for_each_entry(ep, &chip->ep_list, list) {
|
||||
|
|
243
sound/usb/pcm.c
243
sound/usb/pcm.c
|
@ -327,6 +327,137 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
|
|||
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
|
||||
*/
|
||||
|
@ -336,9 +467,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
|
|||
struct usb_host_interface *alts;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
struct usb_interface *iface;
|
||||
unsigned int ep, attr;
|
||||
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
int err, implicit_fb = 0;
|
||||
int err;
|
||||
|
||||
iface = usb_ifnum_to_if(dev, fmt->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,
|
||||
alts, fmt->endpoint, subs->direction,
|
||||
SND_USB_ENDPOINT_TYPE_DATA);
|
||||
|
||||
if (!subs->data_endpoint)
|
||||
return -EINVAL;
|
||||
|
||||
/* 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_endpoint(subs, fmt, dev, alts, altsd);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
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 */
|
||||
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)
|
||||
err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
subs->cur_audiofmt = 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S)
|
|||
{
|
||||
int i;
|
||||
for (i = 0; i < URBS_AsyncSeq; ++i) {
|
||||
if (S[i].urb) {
|
||||
usb_kill_urb(S->urb[i]);
|
||||
usb_free_urb(S->urb[i]);
|
||||
S->urb[i] = NULL;
|
||||
}
|
||||
usb_kill_urb(S->urb[i]);
|
||||
usb_free_urb(S->urb[i]);
|
||||
S->urb[i] = NULL;
|
||||
}
|
||||
kfree(S->buffer);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue