summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-11-06 22:04:07 +0300
committerLinus Torvalds <torvalds@linux-foundation.org>2015-11-06 22:04:07 +0300
commit0280d1a099da1d211e76ec47cc0944c993a36316 (patch)
tree7a2961ded372ca6b6fa88d83a46a5bb5d40abbe4 /drivers/gpu
parent5bc23a0cdee4a6757fcc2919eb26827fe11e3bee (diff)
parent5cf92c8b3dc5da59e05dc81bdc069cedf6f38313 (diff)
downloadlinux-0280d1a099da1d211e76ec47cc0944c993a36316.tar.xz
Merge tag 'sound-4.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai: "Here is the first batch of updates for sound system on 4.4-rc1. Again at this time, the update looks fairly calm; no big changes in either ALSA core or ASoC infrastructures, rather all small cleanups, in addition to the new stuff as usual. The biggest changes are about Firewire sound devices. It gained lots of new device support, and MIDI functionality. Also there are updates for a few still working-in-progress stuff (topology API and ASoC skylake), too. But overall, this update should give no big surprise. Some highlights are below: Core: - A few more Kconfig items for tinification; it's marked as EXPERT, so normal user should't be bothered :) - Refactoring with a new PCM hw_constraint helper - Removal of unused transfer_ack_{begin,end} PCM callbacks Firewire: - Restructuring of code subtree, lots of refactoring - Support AMDTP variants - New driver for Digidesign 002/003 family - Adds support for TASCAM FireOne to ALSA OXFW driver - Add MIDI support to TASCAM and Digi00x devices HD-Audio: - Automated modalias generation for codec drivers, finally - Improvement on heuristics for setting mixer name - A few fixes for longstanding bugs on Creative CA0132 cards - Addition of audio rate callback with i915 communication - Fix suspend issue on recent Dell XPS - Intel Lewisburg controller support ASoC: - Updates to the topology userspace interface - Big updates to the Renesas support (rcar) - More updates for supporting Intel Sky Lake systems - New drivers for Asahi Kasei Microdevices AK4613, Allwinnner A10, Cirrus Logic WM8998, Dialog DA7219, Nuvoton NAU8825, Rockchip S/PDIF, and Atmel class D amplifier USB-Audio: - A fix for newer Roland MIDI devices - Quirks and workarounds for Zoom R16/24 device Misc: - A few fixes for some old Cirrus CS46xx PCI sound boards - Yet another fixes for some old ESS Maestro3 PCI sound boards" * tag 'sound-4.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (330 commits) ALSA: hda - Add Intel Lewisburg device IDs Audio ALSA: hda - Apply pin fixup for HP ProBook 6550b ALSA: hda - Fix lost 4k BDL boundary workaround ALSA: maestro3: Fix Allegro mute until master volume/mute is touched ALSA: maestro3: Enable docking support for Dell Latitude C810 ALSA: firewire-digi00x: add another rawmidi character device for MIDI control ports ALSA: firewire-digi00x: add MIDI operations for MIDI control port ALSA: firewire-digi00x: rename identifiers of MIDI operation for physical ports ALSA: cs46xx: Fix suspend for all channels ALSA: cs46xx: Fix Duplicate front for CS4294 and CS4298 codecs ALSA: DocBook: Add soc-ops.c and soc-compress.c ALSA: hda - Add / fix kernel doc comments ALSA: Constify ratden/ratnum constraints ALSA: hda - Disable 64bit address for Creative HDA controllers ALSA: hda/realtek - Dell XPS one ALC3260 speaker no sound after resume back ALSA: hda/ca0132 - Convert leftover pr_info() and pr_err() ASoC: fsl: Use #ifdef instead of #if for CONFIG_PM_SLEEP ASoC: rt5645: Sort the order for register bit defines ASoC: dwc: add check for master/slave format ASoC: rt5645: Add the HWEQ for the speaker output ...
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c1
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h5
-rw-r--r--drivers/gpu/drm/i915/intel_audio.c179
3 files changed, 184 insertions, 1 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index ab37d1121be8..990f656e6ab0 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -832,6 +832,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
mutex_init(&dev_priv->sb_lock);
mutex_init(&dev_priv->modeset_restore_lock);
mutex_init(&dev_priv->csr_lock);
+ mutex_init(&dev_priv->av_mutex);
intel_pm_setup(dev);
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index e1db8de52851..22dd7043c9ef 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -1885,6 +1885,11 @@ struct drm_i915_private {
/* hda/i915 audio component */
struct i915_audio_component *audio_component;
bool audio_component_registered;
+ /**
+ * av_mutex - mutex for audio/video sync
+ *
+ */
+ struct mutex av_mutex;
uint32_t hw_context_size;
struct list_head context_list;
diff --git a/drivers/gpu/drm/i915/intel_audio.c b/drivers/gpu/drm/i915/intel_audio.c
index 2a5c76faf9f8..ae8df0a43de6 100644
--- a/drivers/gpu/drm/i915/intel_audio.c
+++ b/drivers/gpu/drm/i915/intel_audio.c
@@ -68,6 +68,31 @@ static const struct {
{ 148500, AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 },
};
+/* HDMI N/CTS table */
+#define TMDS_297M 297000
+#define TMDS_296M DIV_ROUND_UP(297000 * 1000, 1001)
+static const struct {
+ int sample_rate;
+ int clock;
+ int n;
+ int cts;
+} aud_ncts[] = {
+ { 44100, TMDS_296M, 4459, 234375 },
+ { 44100, TMDS_297M, 4704, 247500 },
+ { 48000, TMDS_296M, 5824, 281250 },
+ { 48000, TMDS_297M, 5120, 247500 },
+ { 32000, TMDS_296M, 5824, 421875 },
+ { 32000, TMDS_297M, 3072, 222750 },
+ { 88200, TMDS_296M, 8918, 234375 },
+ { 88200, TMDS_297M, 9408, 247500 },
+ { 96000, TMDS_296M, 11648, 281250 },
+ { 96000, TMDS_297M, 10240, 247500 },
+ { 176400, TMDS_296M, 17836, 234375 },
+ { 176400, TMDS_297M, 18816, 247500 },
+ { 192000, TMDS_296M, 23296, 281250 },
+ { 192000, TMDS_297M, 20480, 247500 },
+};
+
/* get AUD_CONFIG_PIXEL_CLOCK_HDMI_* value for mode */
static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
{
@@ -90,6 +115,45 @@ static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
return hdmi_audio_clock[i].config;
}
+static int audio_config_get_n(const struct drm_display_mode *mode, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(aud_ncts); i++) {
+ if ((rate == aud_ncts[i].sample_rate) &&
+ (mode->clock == aud_ncts[i].clock)) {
+ return aud_ncts[i].n;
+ }
+ }
+ return 0;
+}
+
+static uint32_t audio_config_setup_n_reg(int n, uint32_t val)
+{
+ int n_low, n_up;
+ uint32_t tmp = val;
+
+ n_low = n & 0xfff;
+ n_up = (n >> 12) & 0xff;
+ tmp &= ~(AUD_CONFIG_UPPER_N_MASK | AUD_CONFIG_LOWER_N_MASK);
+ tmp |= ((n_up << AUD_CONFIG_UPPER_N_SHIFT) |
+ (n_low << AUD_CONFIG_LOWER_N_SHIFT) |
+ AUD_CONFIG_N_PROG_ENABLE);
+ return tmp;
+}
+
+/* check whether N/CTS/M need be set manually */
+static bool audio_rate_need_prog(struct intel_crtc *crtc,
+ const struct drm_display_mode *mode)
+{
+ if (((mode->clock == TMDS_297M) ||
+ (mode->clock == TMDS_296M)) &&
+ intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI))
+ return true;
+ else
+ return false;
+}
+
static bool intel_eld_uptodate(struct drm_connector *connector,
int reg_eldv, uint32_t bits_eldv,
int reg_elda, uint32_t bits_elda,
@@ -184,6 +248,8 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder)
DRM_DEBUG_KMS("Disable audio codec on pipe %c\n", pipe_name(pipe));
+ mutex_lock(&dev_priv->av_mutex);
+
/* Disable timestamps */
tmp = I915_READ(HSW_AUD_CFG(pipe));
tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
@@ -199,6 +265,8 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder)
tmp &= ~AUDIO_ELD_VALID(pipe);
tmp &= ~AUDIO_OUTPUT_ENABLE(pipe);
I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+
+ mutex_unlock(&dev_priv->av_mutex);
}
static void hsw_audio_codec_enable(struct drm_connector *connector,
@@ -208,13 +276,20 @@ static void hsw_audio_codec_enable(struct drm_connector *connector,
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
enum pipe pipe = intel_crtc->pipe;
+ struct i915_audio_component *acomp = dev_priv->audio_component;
const uint8_t *eld = connector->eld;
+ struct intel_digital_port *intel_dig_port =
+ enc_to_dig_port(&encoder->base);
+ enum port port = intel_dig_port->port;
uint32_t tmp;
int len, i;
+ int n, rate;
DRM_DEBUG_KMS("Enable audio codec on pipe %c, %u bytes ELD\n",
pipe_name(pipe), drm_eld_size(eld));
+ mutex_lock(&dev_priv->av_mutex);
+
/* Enable audio presence detect, invalidate ELD */
tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
tmp |= AUDIO_OUTPUT_ENABLE(pipe);
@@ -246,13 +321,32 @@ static void hsw_audio_codec_enable(struct drm_connector *connector,
/* Enable timestamps */
tmp = I915_READ(HSW_AUD_CFG(pipe));
tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
- tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
tmp &= ~AUD_CONFIG_PIXEL_CLOCK_HDMI_MASK;
if (intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DISPLAYPORT))
tmp |= AUD_CONFIG_N_VALUE_INDEX;
else
tmp |= audio_config_hdmi_pixel_clock(mode);
+
+ tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+ if (audio_rate_need_prog(intel_crtc, mode)) {
+ if (!acomp)
+ rate = 0;
+ else if (port >= PORT_A && port <= PORT_E)
+ rate = acomp->aud_sample_rate[port];
+ else {
+ DRM_ERROR("invalid port: %d\n", port);
+ rate = 0;
+ }
+ n = audio_config_get_n(mode, rate);
+ if (n != 0)
+ tmp = audio_config_setup_n_reg(n, tmp);
+ else
+ DRM_DEBUG_KMS("no suitable N value is found\n");
+ }
+
I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+ mutex_unlock(&dev_priv->av_mutex);
}
static void ilk_audio_codec_disable(struct intel_encoder *encoder)
@@ -527,12 +621,91 @@ static int i915_audio_component_get_cdclk_freq(struct device *dev)
return ret;
}
+static int i915_audio_component_sync_audio_rate(struct device *dev,
+ int port, int rate)
+{
+ struct drm_i915_private *dev_priv = dev_to_i915(dev);
+ struct drm_device *drm_dev = dev_priv->dev;
+ struct intel_encoder *intel_encoder;
+ struct intel_digital_port *intel_dig_port;
+ struct intel_crtc *crtc;
+ struct drm_display_mode *mode;
+ struct i915_audio_component *acomp = dev_priv->audio_component;
+ enum pipe pipe = -1;
+ u32 tmp;
+ int n;
+
+ /* HSW, BDW SKL need this fix */
+ if (!IS_SKYLAKE(dev_priv) &&
+ !IS_BROADWELL(dev_priv) &&
+ !IS_HASWELL(dev_priv))
+ return 0;
+
+ mutex_lock(&dev_priv->av_mutex);
+ /* 1. get the pipe */
+ for_each_intel_encoder(drm_dev, intel_encoder) {
+ if (intel_encoder->type != INTEL_OUTPUT_HDMI)
+ continue;
+ intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+ if (port == intel_dig_port->port) {
+ crtc = to_intel_crtc(intel_encoder->base.crtc);
+ if (!crtc) {
+ DRM_DEBUG_KMS("%s: crtc is NULL\n", __func__);
+ continue;
+ }
+ pipe = crtc->pipe;
+ break;
+ }
+ }
+
+ if (pipe == INVALID_PIPE) {
+ DRM_DEBUG_KMS("no pipe for the port %c\n", port_name(port));
+ mutex_unlock(&dev_priv->av_mutex);
+ return -ENODEV;
+ }
+ DRM_DEBUG_KMS("pipe %c connects port %c\n",
+ pipe_name(pipe), port_name(port));
+ mode = &crtc->config->base.adjusted_mode;
+
+ /* port must be valid now, otherwise the pipe will be invalid */
+ acomp->aud_sample_rate[port] = rate;
+
+ /* 2. check whether to set the N/CTS/M manually or not */
+ if (!audio_rate_need_prog(crtc, mode)) {
+ tmp = I915_READ(HSW_AUD_CFG(pipe));
+ tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+ I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+ mutex_unlock(&dev_priv->av_mutex);
+ return 0;
+ }
+
+ n = audio_config_get_n(mode, rate);
+ if (n == 0) {
+ DRM_DEBUG_KMS("Using automatic mode for N value on port %c\n",
+ port_name(port));
+ tmp = I915_READ(HSW_AUD_CFG(pipe));
+ tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+ I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+ mutex_unlock(&dev_priv->av_mutex);
+ return 0;
+ }
+
+ /* 3. set the N/CTS/M */
+ tmp = I915_READ(HSW_AUD_CFG(pipe));
+ tmp = audio_config_setup_n_reg(n, tmp);
+ I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+ mutex_unlock(&dev_priv->av_mutex);
+ return 0;
+}
+
static const struct i915_audio_component_ops i915_audio_component_ops = {
.owner = THIS_MODULE,
.get_power = i915_audio_component_get_power,
.put_power = i915_audio_component_put_power,
.codec_wake_override = i915_audio_component_codec_wake_override,
.get_cdclk_freq = i915_audio_component_get_cdclk_freq,
+ .sync_audio_rate = i915_audio_component_sync_audio_rate,
};
static int i915_audio_component_bind(struct device *i915_dev,
@@ -540,6 +713,7 @@ static int i915_audio_component_bind(struct device *i915_dev,
{
struct i915_audio_component *acomp = data;
struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
+ int i;
if (WARN_ON(acomp->ops || acomp->dev))
return -EEXIST;
@@ -547,6 +721,9 @@ static int i915_audio_component_bind(struct device *i915_dev,
drm_modeset_lock_all(dev_priv->dev);
acomp->ops = &i915_audio_component_ops;
acomp->dev = i915_dev;
+ BUILD_BUG_ON(MAX_PORTS != I915_MAX_PORTS);
+ for (i = 0; i < ARRAY_SIZE(acomp->aud_sample_rate); i++)
+ acomp->aud_sample_rate[i] = 0;
dev_priv->audio_component = acomp;
drm_modeset_unlock_all(dev_priv->dev);