diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-25 17:15:18 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-25 17:15:18 -0700 |
commit | 4570a37169d4b44d316f40b2ccc681dc93fedc7b (patch) | |
tree | cafffb586c60dddfb04b8619fa1ae0e859600de7 /sound/hda/hdac_device.c | |
parent | f7b08217c755e88a29b5bd53b4a1d10cd8b3c5f8 (diff) | |
parent | 60b93030b44a8c2cd015cebe5624fd7552ec67ec (diff) |
Merge tag 'sound-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"It was a busy development cycle at this time, as you can see a wide
range of changes in diffstat. There are no big changes but many
refactoring and improvements. Here we go some highlights:
ALSA core:
- Procfs codes were cleaned up to use seq_file
- Procfs can be opt out via Kconfig (only for EXPERT)
- Two types of jack API were unified finally; now both kctl and input
jack devs are handled via a single function call.
HD-audio:
- Continued code restructuring for the future ASoC driver; now HDA
controller driver is split to a core helper module.
- Preliminary codes for Skylake audio support in HDA core.
- Proper i915 gfx power well management for SKL & co
- Enabled runtime PM as default for Intel HDMI/DP codecs
- Newer Tegra chip supports
- More quirks for Dell headsets, Alienware (with CA0132), etc.
- A couple of DRM ELD helper API functions
ASoC:
- Support for loading ASoC topology maps from firmware, intended to
be used to allow self-describing DSP firmware images to be built
which can map controls added by the DSP to userspace without the
kernel needing to know about individual DSP firmwares
- Lots of refactoring to avoid direct access to snd_soc_codec where
it's not needed supporting future refactoring
- Big refactoring, cleanup and enhancement for the Wolfson ADSP
driver
- Cleanup series for TI TAS2552 and R-CAR drivers
- Fixes and improvements on RT56xx codecs
- Support for TI TAS571x power amplifiers
- Support for Qualcomm APQ8016 and ZTE ZX296702 SoCs
- Support for x86 systems with RT5650 and Qualcomm Storm
- Support for Mediatek AFE (Audio Front End) unit
- Other various small fixes to ASoC codec drivers
Firewire:
- Enhanced to allow non-blocking streams to use timestamp
synchronization
- Improve support for DM1500 and BeBoBv3
Misc:
- Cleanup of old pci API functions over all PCI sound drivers
- Fix long-standing regression of the old powermac i2c setup"
* tag 'sound-4.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (533 commits)
ALSA: pcm: Fix pcm_class sysfs output
ALSA: hda-beep: Update authors dead email address
ASoC: wm_adsp: Move DSP Rate controls into the codec
ASoC: wm8995: Fix setting sysclk for WM8995_SYSCLK_MCLK2 case
ALSA: hda: provide default bus io ops extended hdac
ALSA: hda: add hda link cleanup routine
ALSA: hda: add hdac_ext stream creation and cleanup routines
ASoC: rsrc-card: remove unused ret
ALSA: HDAC: move SND_HDA_PREALLOC_SIZE to core
ASoC: mediatek: Add machine driver for rt5650 rt5676 codec
ASoC: mediatek: Add machine driver for MAX98090 codec
ASoC: mediatek: Add AFE platform driver
ASoC: rsnd: remove io from rsnd_mod
ASoC: rsnd: move rsnd_mod_is_working() to rsnd_io_is_working()
ASoC: rsnd: don't use rsnd_mod_to_io() on snd_kcontrol
ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_src_xxx()
ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_ssi_xxx()
ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_dma_xxx()
ASoC: rsnd: don't use rsnd_mod_to_io() on rsnd_get_adinr()
ASoC: rsnd: add common interrupt handler for SSI/SRC/DMA
...
Diffstat (limited to 'sound/hda/hdac_device.c')
-rw-r--r-- | sound/hda/hdac_device.c | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/sound/hda/hdac_device.c b/sound/hda/hdac_device.c index f75bf5622687..cdee7103f649 100644 --- a/sound/hda/hdac_device.c +++ b/sound/hda/hdac_device.c @@ -10,6 +10,7 @@ #include <linux/pm_runtime.h> #include <sound/hdaudio.h> #include <sound/hda_regmap.h> +#include <sound/pcm.h> #include "local.h" static void setup_fg_nodes(struct hdac_device *codec); @@ -551,6 +552,21 @@ void snd_hdac_power_down_pm(struct hdac_device *codec) EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm); #endif +/* + * Enable/disable the link power for a codec. + */ +int snd_hdac_link_power(struct hdac_device *codec, bool enable) +{ + if (!codec->link_power_control) + return 0; + + if (codec->bus->ops->link_power) + return codec->bus->ops->link_power(codec->bus, enable); + else + return -EINVAL; +} +EXPORT_SYMBOL_GPL(snd_hdac_link_power); + /* codec vendor labels */ struct hda_vendor_id { unsigned int id; @@ -597,3 +613,302 @@ static int get_codec_vendor_name(struct hdac_device *codec) codec->vendor_name = kasprintf(GFP_KERNEL, "Generic %04x", vendor_id); return codec->vendor_name ? 0 : -ENOMEM; } + +/* + * stream formats + */ +struct hda_rate_tbl { + unsigned int hz; + unsigned int alsa_bits; + unsigned int hda_fmt; +}; + +/* rate = base * mult / div */ +#define HDA_RATE(base, mult, div) \ + (AC_FMT_BASE_##base##K | (((mult) - 1) << AC_FMT_MULT_SHIFT) | \ + (((div) - 1) << AC_FMT_DIV_SHIFT)) + +static struct hda_rate_tbl rate_bits[] = { + /* rate in Hz, ALSA rate bitmask, HDA format value */ + + /* autodetected value used in snd_hda_query_supported_pcm */ + { 8000, SNDRV_PCM_RATE_8000, HDA_RATE(48, 1, 6) }, + { 11025, SNDRV_PCM_RATE_11025, HDA_RATE(44, 1, 4) }, + { 16000, SNDRV_PCM_RATE_16000, HDA_RATE(48, 1, 3) }, + { 22050, SNDRV_PCM_RATE_22050, HDA_RATE(44, 1, 2) }, + { 32000, SNDRV_PCM_RATE_32000, HDA_RATE(48, 2, 3) }, + { 44100, SNDRV_PCM_RATE_44100, HDA_RATE(44, 1, 1) }, + { 48000, SNDRV_PCM_RATE_48000, HDA_RATE(48, 1, 1) }, + { 88200, SNDRV_PCM_RATE_88200, HDA_RATE(44, 2, 1) }, + { 96000, SNDRV_PCM_RATE_96000, HDA_RATE(48, 2, 1) }, + { 176400, SNDRV_PCM_RATE_176400, HDA_RATE(44, 4, 1) }, + { 192000, SNDRV_PCM_RATE_192000, HDA_RATE(48, 4, 1) }, +#define AC_PAR_PCM_RATE_BITS 11 + /* up to bits 10, 384kHZ isn't supported properly */ + + /* not autodetected value */ + { 9600, SNDRV_PCM_RATE_KNOT, HDA_RATE(48, 1, 5) }, + + { 0 } /* terminator */ +}; + +/** + * snd_hdac_calc_stream_format - calculate the format bitset + * @rate: the sample rate + * @channels: the number of channels + * @format: the PCM format (SNDRV_PCM_FORMAT_XXX) + * @maxbps: the max. bps + * @spdif_ctls: HD-audio SPDIF status bits (0 if irrelevant) + * + * Calculate the format bitset from the given rate, channels and th PCM format. + * + * Return zero if invalid. + */ +unsigned int snd_hdac_calc_stream_format(unsigned int rate, + unsigned int channels, + unsigned int format, + unsigned int maxbps, + unsigned short spdif_ctls) +{ + int i; + unsigned int val = 0; + + for (i = 0; rate_bits[i].hz; i++) + if (rate_bits[i].hz == rate) { + val = rate_bits[i].hda_fmt; + break; + } + if (!rate_bits[i].hz) + return 0; + + if (channels == 0 || channels > 8) + return 0; + val |= channels - 1; + + switch (snd_pcm_format_width(format)) { + case 8: + val |= AC_FMT_BITS_8; + break; + case 16: + val |= AC_FMT_BITS_16; + break; + case 20: + case 24: + case 32: + if (maxbps >= 32 || format == SNDRV_PCM_FORMAT_FLOAT_LE) + val |= AC_FMT_BITS_32; + else if (maxbps >= 24) + val |= AC_FMT_BITS_24; + else + val |= AC_FMT_BITS_20; + break; + default: + return 0; + } + + if (spdif_ctls & AC_DIG1_NONAUDIO) + val |= AC_FMT_TYPE_NON_PCM; + + return val; +} +EXPORT_SYMBOL_GPL(snd_hdac_calc_stream_format); + +static unsigned int query_pcm_param(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int val = 0; + + if (nid != codec->afg && + (get_wcaps(codec, nid) & AC_WCAP_FORMAT_OVRD)) + val = snd_hdac_read_parm(codec, nid, AC_PAR_PCM); + if (!val || val == -1) + val = snd_hdac_read_parm(codec, codec->afg, AC_PAR_PCM); + if (!val || val == -1) + return 0; + return val; +} + +static unsigned int query_stream_param(struct hdac_device *codec, hda_nid_t nid) +{ + unsigned int streams = snd_hdac_read_parm(codec, nid, AC_PAR_STREAM); + + if (!streams || streams == -1) + streams = snd_hdac_read_parm(codec, codec->afg, AC_PAR_STREAM); + if (!streams || streams == -1) + return 0; + return streams; +} + +/** + * snd_hdac_query_supported_pcm - query the supported PCM rates and formats + * @codec: the codec object + * @nid: NID to query + * @ratesp: the pointer to store the detected rate bitflags + * @formatsp: the pointer to store the detected formats + * @bpsp: the pointer to store the detected format widths + * + * Queries the supported PCM rates and formats. The NULL @ratesp, @formatsp + * or @bsps argument is ignored. + * + * Returns 0 if successful, otherwise a negative error code. + */ +int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid, + u32 *ratesp, u64 *formatsp, unsigned int *bpsp) +{ + unsigned int i, val, wcaps; + + wcaps = get_wcaps(codec, nid); + val = query_pcm_param(codec, nid); + + if (ratesp) { + u32 rates = 0; + for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) { + if (val & (1 << i)) + rates |= rate_bits[i].alsa_bits; + } + if (rates == 0) { + dev_err(&codec->dev, + "rates == 0 (nid=0x%x, val=0x%x, ovrd=%i)\n", + nid, val, + (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0); + return -EIO; + } + *ratesp = rates; + } + + if (formatsp || bpsp) { + u64 formats = 0; + unsigned int streams, bps; + + streams = query_stream_param(codec, nid); + if (!streams) + return -EIO; + + bps = 0; + if (streams & AC_SUPFMT_PCM) { + if (val & AC_SUPPCM_BITS_8) { + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (val & AC_SUPPCM_BITS_16) { + formats |= SNDRV_PCM_FMTBIT_S16_LE; + bps = 16; + } + if (wcaps & AC_WCAP_DIGITAL) { + if (val & AC_SUPPCM_BITS_32) + formats |= SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE; + if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24)) + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } else if (val & (AC_SUPPCM_BITS_20|AC_SUPPCM_BITS_24| + AC_SUPPCM_BITS_32)) { + formats |= SNDRV_PCM_FMTBIT_S32_LE; + if (val & AC_SUPPCM_BITS_32) + bps = 32; + else if (val & AC_SUPPCM_BITS_24) + bps = 24; + else if (val & AC_SUPPCM_BITS_20) + bps = 20; + } + } +#if 0 /* FIXME: CS4206 doesn't work, which is the only codec supporting float */ + if (streams & AC_SUPFMT_FLOAT32) { + formats |= SNDRV_PCM_FMTBIT_FLOAT_LE; + if (!bps) + bps = 32; + } +#endif + if (streams == AC_SUPFMT_AC3) { + /* should be exclusive */ + /* temporary hack: we have still no proper support + * for the direct AC3 stream... + */ + formats |= SNDRV_PCM_FMTBIT_U8; + bps = 8; + } + if (formats == 0) { + dev_err(&codec->dev, + "formats == 0 (nid=0x%x, val=0x%x, ovrd=%i, streams=0x%x)\n", + nid, val, + (wcaps & AC_WCAP_FORMAT_OVRD) ? 1 : 0, + streams); + return -EIO; + } + if (formatsp) + *formatsp = formats; + if (bpsp) + *bpsp = bps; + } + + return 0; +} +EXPORT_SYMBOL_GPL(snd_hdac_query_supported_pcm); + +/** + * snd_hdac_is_supported_format - Check the validity of the format + * @codec: the codec object + * @nid: NID to check + * @format: the HD-audio format value to check + * + * Check whether the given node supports the format value. + * + * Returns true if supported, false if not. + */ +bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid, + unsigned int format) +{ + int i; + unsigned int val = 0, rate, stream; + + val = query_pcm_param(codec, nid); + if (!val) + return false; + + rate = format & 0xff00; + for (i = 0; i < AC_PAR_PCM_RATE_BITS; i++) + if (rate_bits[i].hda_fmt == rate) { + if (val & (1 << i)) + break; + return false; + } + if (i >= AC_PAR_PCM_RATE_BITS) + return false; + + stream = query_stream_param(codec, nid); + if (!stream) + return false; + + if (stream & AC_SUPFMT_PCM) { + switch (format & 0xf0) { + case 0x00: + if (!(val & AC_SUPPCM_BITS_8)) + return false; + break; + case 0x10: + if (!(val & AC_SUPPCM_BITS_16)) + return false; + break; + case 0x20: + if (!(val & AC_SUPPCM_BITS_20)) + return false; + break; + case 0x30: + if (!(val & AC_SUPPCM_BITS_24)) + return false; + break; + case 0x40: + if (!(val & AC_SUPPCM_BITS_32)) + return false; + break; + default: + return false; + } + } else { + /* FIXME: check for float32 and AC3? */ + } + + return true; +} +EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format); |