diff options
Diffstat (limited to 'bcm283x-hdmi-audio.patch')
-rw-r--r-- | bcm283x-hdmi-audio.patch | 807 |
1 files changed, 0 insertions, 807 deletions
diff --git a/bcm283x-hdmi-audio.patch b/bcm283x-hdmi-audio.patch index 3ed3d2d34..8c962442f 100644 --- a/bcm283x-hdmi-audio.patch +++ b/bcm283x-hdmi-audio.patch @@ -1,810 +1,3 @@ -From bbcb8aacb871edf0360e808180162591b11c6a35 Mon Sep 17 00:00:00 2001 -From: Boris Brezillon <boris.brezillon@free-electrons.com> -Date: Mon, 27 Feb 2017 12:28:01 -0800 -Subject: [PATCH 1/3] dt-bindings: Document the dmas and dma-names properties - for VC4 HDMI - -These are optional, but necessary for HDMI audio support. - -Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> -Signed-off-by: Eric Anholt <eric@anholt.net> -Acked-by: Rob Herring <robh@kernel.org> -Link: http://patchwork.freedesktop.org/patch/msgid/20170227202803.12855-1-eric@anholt.net ---- - Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt -index 34c7fddcea39..ca02d3e4db91 100644 ---- a/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt -+++ b/Documentation/devicetree/bindings/display/brcm,bcm-vc4.txt -@@ -34,6 +34,9 @@ Optional properties for HDMI: - - hpd-gpios: The GPIO pin for HDMI hotplug detect (if it doesn't appear - as an interrupt/status bit in the HDMI controller - itself). See bindings/pinctrl/brcm,bcm2835-gpio.txt -+- dmas: Should contain one entry pointing to the DMA channel used to -+ transfer audio data -+- dma-names: Should contain "audio-rx" - - Required properties for DPI: - - compatible: Should be "brcm,bcm2835-dpi" --- -2.12.0 - -From 8e13e0d8ecf2202c707225a612d10c9534d849f7 Mon Sep 17 00:00:00 2001 -From: Eric Anholt <eric@anholt.net> -Date: Mon, 27 Feb 2017 12:28:02 -0800 -Subject: [PATCH 2/3] drm/vc4: Add HDMI audio support - -The HDMI encoder IP embeds all needed blocks to output audio, with a -custom DAI called MAI moving audio between the two parts of the HDMI -core. This driver now exposes a sound card to let users stream audio -to their display. - -Using the hdmi-codec driver has been considered here, but MAI meant -having to significantly rework hdmi-codec, and it would have left -little shared code with the I2S mode anyway. - -The encoder requires that the audio be SPDIF-formatted frames only, -which alsalib will format-convert for us. - -This patch is the combined work of Eric Anholt (initial register setup -with a separate dmaengine driver and using simple-audio-card) and -Boris Brezillon (moving it all into HDMI, massive debug to get it -actually working), and which Eric has the permission to release. - -v2: Drop "-audio" from sound card name, since that's already implied - (suggestion by Boris) - -Signed-off-by: Eric Anholt <eric@anholt.net> -Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com> -Link: http://patchwork.freedesktop.org/patch/msgid/20170227202803.12855-2-eric@anholt.net ---- - drivers/gpu/drm/vc4/Kconfig | 4 + - drivers/gpu/drm/vc4/vc4_hdmi.c | 494 ++++++++++++++++++++++++++++++++++++++++- - drivers/gpu/drm/vc4/vc4_regs.h | 107 ++++++++- - 3 files changed, 603 insertions(+), 2 deletions(-) - -diff --git a/drivers/gpu/drm/vc4/Kconfig b/drivers/gpu/drm/vc4/Kconfig -index e1517d07cb7d..973b4203c0b2 100644 ---- a/drivers/gpu/drm/vc4/Kconfig -+++ b/drivers/gpu/drm/vc4/Kconfig -@@ -2,11 +2,15 @@ config DRM_VC4 - tristate "Broadcom VC4 Graphics" - depends on ARCH_BCM2835 || COMPILE_TEST - depends on DRM -+ depends on SND && SND_SOC - depends on COMMON_CLK - select DRM_KMS_HELPER - select DRM_KMS_CMA_HELPER - select DRM_GEM_CMA_HELPER - select DRM_PANEL -+ select SND_PCM -+ select SND_PCM_ELD -+ select SND_SOC_GENERIC_DMAENGINE_PCM - select DRM_MIPI_DSI - help - Choose this option if you have a system that has a Broadcom -diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c -index 93d5994f3a04..e4abf4bfc464 100644 ---- a/drivers/gpu/drm/vc4/vc4_hdmi.c -+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c -@@ -31,11 +31,27 @@ - #include "linux/clk.h" - #include "linux/component.h" - #include "linux/i2c.h" -+#include "linux/of_address.h" - #include "linux/of_gpio.h" - #include "linux/of_platform.h" -+#include "linux/rational.h" -+#include "sound/dmaengine_pcm.h" -+#include "sound/pcm_drm_eld.h" -+#include "sound/pcm_params.h" -+#include "sound/soc.h" - #include "vc4_drv.h" - #include "vc4_regs.h" - -+/* HDMI audio information */ -+struct vc4_hdmi_audio { -+ struct snd_soc_card card; -+ struct snd_soc_dai_link link; -+ int samplerate; -+ int channels; -+ struct snd_dmaengine_dai_dma_data dma_data; -+ struct snd_pcm_substream *substream; -+}; -+ - /* General HDMI hardware state. */ - struct vc4_hdmi { - struct platform_device *pdev; -@@ -43,6 +59,8 @@ struct vc4_hdmi { - struct drm_encoder *encoder; - struct drm_connector *connector; - -+ struct vc4_hdmi_audio audio; -+ - struct i2c_adapter *ddc; - void __iomem *hdmicore_regs; - void __iomem *hd_regs; -@@ -98,6 +116,10 @@ static const struct { - HDMI_REG(VC4_HDMI_SW_RESET_CONTROL), - HDMI_REG(VC4_HDMI_HOTPLUG_INT), - HDMI_REG(VC4_HDMI_HOTPLUG), -+ HDMI_REG(VC4_HDMI_MAI_CHANNEL_MAP), -+ HDMI_REG(VC4_HDMI_MAI_CONFIG), -+ HDMI_REG(VC4_HDMI_MAI_FORMAT), -+ HDMI_REG(VC4_HDMI_AUDIO_PACKET_CONFIG), - HDMI_REG(VC4_HDMI_RAM_PACKET_CONFIG), - HDMI_REG(VC4_HDMI_HORZA), - HDMI_REG(VC4_HDMI_HORZB), -@@ -108,6 +130,7 @@ static const struct { - HDMI_REG(VC4_HDMI_VERTB0), - HDMI_REG(VC4_HDMI_VERTB1), - HDMI_REG(VC4_HDMI_TX_PHY_RESET_CTL), -+ HDMI_REG(VC4_HDMI_TX_PHY_CTL0), - }; - - static const struct { -@@ -116,6 +139,9 @@ static const struct { - } hd_regs[] = { - HDMI_REG(VC4_HD_M_CTL), - HDMI_REG(VC4_HD_MAI_CTL), -+ HDMI_REG(VC4_HD_MAI_THR), -+ HDMI_REG(VC4_HD_MAI_FMT), -+ HDMI_REG(VC4_HD_MAI_SMP), - HDMI_REG(VC4_HD_VID_CTL), - HDMI_REG(VC4_HD_CSC_CTL), - HDMI_REG(VC4_HD_FRAME_COUNT), -@@ -215,6 +241,7 @@ static int vc4_hdmi_connector_get_modes(struct drm_connector *connector) - - drm_mode_connector_update_edid_property(connector, edid); - ret = drm_add_edid_modes(connector, edid); -+ drm_edid_to_eld(connector, edid); - - return ret; - } -@@ -300,7 +327,7 @@ static void vc4_hdmi_write_infoframe(struct drm_encoder *encoder, - struct drm_device *dev = encoder->dev; - struct vc4_dev *vc4 = to_vc4_dev(dev); - u32 packet_id = frame->any.type - 0x80; -- u32 packet_reg = VC4_HDMI_GCP_0 + VC4_HDMI_PACKET_STRIDE * packet_id; -+ u32 packet_reg = VC4_HDMI_RAM_PACKET(packet_id); - uint8_t buffer[VC4_HDMI_PACKET_STRIDE]; - ssize_t len, i; - int ret; -@@ -381,6 +408,24 @@ static void vc4_hdmi_set_spd_infoframe(struct drm_encoder *encoder) - vc4_hdmi_write_infoframe(encoder, &frame); - } - -+static void vc4_hdmi_set_audio_infoframe(struct drm_encoder *encoder) -+{ -+ struct drm_device *drm = encoder->dev; -+ struct vc4_dev *vc4 = drm->dev_private; -+ struct vc4_hdmi *hdmi = vc4->hdmi; -+ union hdmi_infoframe frame; -+ int ret; -+ -+ ret = hdmi_audio_infoframe_init(&frame.audio); -+ -+ frame.audio.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; -+ frame.audio.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; -+ frame.audio.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; -+ frame.audio.channels = hdmi->audio.channels; -+ -+ vc4_hdmi_write_infoframe(encoder, &frame); -+} -+ - static void vc4_hdmi_set_infoframes(struct drm_encoder *encoder) - { - vc4_hdmi_set_avi_infoframe(encoder); -@@ -589,6 +634,447 @@ static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = { - .enable = vc4_hdmi_encoder_enable, - }; - -+/* HDMI audio codec callbacks */ -+static void vc4_hdmi_audio_set_mai_clock(struct vc4_hdmi *hdmi) -+{ -+ struct drm_device *drm = hdmi->encoder->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ u32 hsm_clock = clk_get_rate(hdmi->hsm_clock); -+ unsigned long n, m; -+ -+ rational_best_approximation(hsm_clock, hdmi->audio.samplerate, -+ VC4_HD_MAI_SMP_N_MASK >> -+ VC4_HD_MAI_SMP_N_SHIFT, -+ (VC4_HD_MAI_SMP_M_MASK >> -+ VC4_HD_MAI_SMP_M_SHIFT) + 1, -+ &n, &m); -+ -+ HD_WRITE(VC4_HD_MAI_SMP, -+ VC4_SET_FIELD(n, VC4_HD_MAI_SMP_N) | -+ VC4_SET_FIELD(m - 1, VC4_HD_MAI_SMP_M)); -+} -+ -+static void vc4_hdmi_set_n_cts(struct vc4_hdmi *hdmi) -+{ -+ struct drm_encoder *encoder = hdmi->encoder; -+ struct drm_crtc *crtc = encoder->crtc; -+ struct drm_device *drm = encoder->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ const struct drm_display_mode *mode = &crtc->state->adjusted_mode; -+ u32 samplerate = hdmi->audio.samplerate; -+ u32 n, cts; -+ u64 tmp; -+ -+ n = 128 * samplerate / 1000; -+ tmp = (u64)(mode->clock * 1000) * n; -+ do_div(tmp, 128 * samplerate); -+ cts = tmp; -+ -+ HDMI_WRITE(VC4_HDMI_CRP_CFG, -+ VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN | -+ VC4_SET_FIELD(n, VC4_HDMI_CRP_CFG_N)); -+ -+ /* -+ * We could get slightly more accurate clocks in some cases by -+ * providing a CTS_1 value. The two CTS values are alternated -+ * between based on the period fields -+ */ -+ HDMI_WRITE(VC4_HDMI_CTS_0, cts); -+ HDMI_WRITE(VC4_HDMI_CTS_1, cts); -+} -+ -+static inline struct vc4_hdmi *dai_to_hdmi(struct snd_soc_dai *dai) -+{ -+ struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai); -+ -+ return snd_soc_card_get_drvdata(card); -+} -+ -+static int vc4_hdmi_audio_startup(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai); -+ struct drm_encoder *encoder = hdmi->encoder; -+ struct vc4_dev *vc4 = to_vc4_dev(encoder->dev); -+ int ret; -+ -+ if (hdmi->audio.substream && hdmi->audio.substream != substream) -+ return -EINVAL; -+ -+ hdmi->audio.substream = substream; -+ -+ /* -+ * If the HDMI encoder hasn't probed, or the encoder is -+ * currently in DVI mode, treat the codec dai as missing. -+ */ -+ if (!encoder->crtc || !(HDMI_READ(VC4_HDMI_RAM_PACKET_CONFIG) & -+ VC4_HDMI_RAM_PACKET_ENABLE)) -+ return -ENODEV; -+ -+ ret = snd_pcm_hw_constraint_eld(substream->runtime, -+ hdmi->connector->eld); -+ if (ret) -+ return ret; -+ -+ return 0; -+} -+ -+static int vc4_hdmi_audio_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) -+{ -+ return 0; -+} -+ -+static void vc4_hdmi_audio_reset(struct vc4_hdmi *hdmi) -+{ -+ struct drm_encoder *encoder = hdmi->encoder; -+ struct drm_device *drm = encoder->dev; -+ struct device *dev = &hdmi->pdev->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ int ret; -+ -+ ret = vc4_hdmi_stop_packet(encoder, HDMI_INFOFRAME_TYPE_AUDIO); -+ if (ret) -+ dev_err(dev, "Failed to stop audio infoframe: %d\n", ret); -+ -+ HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_RESET); -+ HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_ERRORF); -+ HD_WRITE(VC4_HD_MAI_CTL, VC4_HD_MAI_CTL_FLUSH); -+} -+ -+static void vc4_hdmi_audio_shutdown(struct snd_pcm_substream *substream, -+ struct snd_soc_dai *dai) -+{ -+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai); -+ -+ if (substream != hdmi->audio.substream) -+ return; -+ -+ vc4_hdmi_audio_reset(hdmi); -+ -+ hdmi->audio.substream = NULL; -+} -+ -+/* HDMI audio codec callbacks */ -+static int vc4_hdmi_audio_hw_params(struct snd_pcm_substream *substream, -+ struct snd_pcm_hw_params *params, -+ struct snd_soc_dai *dai) -+{ -+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai); -+ struct drm_encoder *encoder = hdmi->encoder; -+ struct drm_device *drm = encoder->dev; -+ struct device *dev = &hdmi->pdev->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ u32 audio_packet_config, channel_mask; -+ u32 channel_map, i; -+ -+ if (substream != hdmi->audio.substream) -+ return -EINVAL; -+ -+ dev_dbg(dev, "%s: %u Hz, %d bit, %d channels\n", __func__, -+ params_rate(params), params_width(params), -+ params_channels(params)); -+ -+ hdmi->audio.channels = params_channels(params); -+ hdmi->audio.samplerate = params_rate(params); -+ -+ HD_WRITE(VC4_HD_MAI_CTL, -+ VC4_HD_MAI_CTL_RESET | -+ VC4_HD_MAI_CTL_FLUSH | -+ VC4_HD_MAI_CTL_DLATE | -+ VC4_HD_MAI_CTL_ERRORE | -+ VC4_HD_MAI_CTL_ERRORF); -+ -+ vc4_hdmi_audio_set_mai_clock(hdmi); -+ -+ audio_packet_config = -+ VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT | -+ VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS | -+ VC4_SET_FIELD(0xf, VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER); -+ -+ channel_mask = GENMASK(hdmi->audio.channels - 1, 0); -+ audio_packet_config |= VC4_SET_FIELD(channel_mask, -+ VC4_HDMI_AUDIO_PACKET_CEA_MASK); -+ -+ /* Set the MAI threshold. This logic mimics the firmware's. */ -+ if (hdmi->audio.samplerate > 96000) { -+ HD_WRITE(VC4_HD_MAI_THR, -+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQHIGH) | -+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); -+ } else if (hdmi->audio.samplerate > 48000) { -+ HD_WRITE(VC4_HD_MAI_THR, -+ VC4_SET_FIELD(0x14, VC4_HD_MAI_THR_DREQHIGH) | -+ VC4_SET_FIELD(0x12, VC4_HD_MAI_THR_DREQLOW)); -+ } else { -+ HD_WRITE(VC4_HD_MAI_THR, -+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICHIGH) | -+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_PANICLOW) | -+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQHIGH) | -+ VC4_SET_FIELD(0x10, VC4_HD_MAI_THR_DREQLOW)); -+ } -+ -+ HDMI_WRITE(VC4_HDMI_MAI_CONFIG, -+ VC4_HDMI_MAI_CONFIG_BIT_REVERSE | -+ VC4_SET_FIELD(channel_mask, VC4_HDMI_MAI_CHANNEL_MASK)); -+ -+ channel_map = 0; -+ for (i = 0; i < 8; i++) { -+ if (channel_mask & BIT(i)) -+ channel_map |= i << (3 * i); -+ } -+ -+ HDMI_WRITE(VC4_HDMI_MAI_CHANNEL_MAP, channel_map); -+ HDMI_WRITE(VC4_HDMI_AUDIO_PACKET_CONFIG, audio_packet_config); -+ vc4_hdmi_set_n_cts(hdmi); -+ -+ return 0; -+} -+ -+static int vc4_hdmi_audio_trigger(struct snd_pcm_substream *substream, int cmd, -+ struct snd_soc_dai *dai) -+{ -+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai); -+ struct drm_encoder *encoder = hdmi->encoder; -+ struct drm_device *drm = encoder->dev; -+ struct vc4_dev *vc4 = to_vc4_dev(drm); -+ -+ switch (cmd) { -+ case SNDRV_PCM_TRIGGER_START: -+ vc4_hdmi_set_audio_infoframe(encoder); -+ HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, -+ HDMI_READ(VC4_HDMI_TX_PHY_CTL0) & -+ ~VC4_HDMI_TX_PHY_RNG_PWRDN); -+ HD_WRITE(VC4_HD_MAI_CTL, -+ VC4_SET_FIELD(hdmi->audio.channels, -+ VC4_HD_MAI_CTL_CHNUM) | -+ VC4_HD_MAI_CTL_ENABLE); -+ break; -+ case SNDRV_PCM_TRIGGER_STOP: -+ HD_WRITE(VC4_HD_MAI_CTL, -+ VC4_HD_MAI_CTL_DLATE | -+ VC4_HD_MAI_CTL_ERRORE | -+ VC4_HD_MAI_CTL_ERRORF); -+ HDMI_WRITE(VC4_HDMI_TX_PHY_CTL0, -+ HDMI_READ(VC4_HDMI_TX_PHY_CTL0) | -+ VC4_HDMI_TX_PHY_RNG_PWRDN); -+ break; -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+static inline struct vc4_hdmi * -+snd_component_to_hdmi(struct snd_soc_component *component) -+{ -+ struct snd_soc_card *card = snd_soc_component_get_drvdata(component); -+ -+ return snd_soc_card_get_drvdata(card); -+} -+ -+static int vc4_hdmi_audio_eld_ctl_info(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_info *uinfo) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); -+ -+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES; -+ uinfo->count = sizeof(hdmi->connector->eld); -+ -+ return 0; -+} -+ -+static int vc4_hdmi_audio_eld_ctl_get(struct snd_kcontrol *kcontrol, -+ struct snd_ctl_elem_value *ucontrol) -+{ -+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); -+ struct vc4_hdmi *hdmi = snd_component_to_hdmi(component); -+ -+ memcpy(ucontrol->value.bytes.data, hdmi->connector->eld, -+ sizeof(hdmi->connector->eld)); -+ -+ return 0; -+} -+ -+static const struct snd_kcontrol_new vc4_hdmi_audio_controls[] = { -+ { -+ .access = SNDRV_CTL_ELEM_ACCESS_READ | -+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, -+ .iface = SNDRV_CTL_ELEM_IFACE_PCM, -+ .name = "ELD", -+ .info = vc4_hdmi_audio_eld_ctl_info, -+ .get = vc4_hdmi_audio_eld_ctl_get, -+ }, -+}; -+ -+static const struct snd_soc_dapm_widget vc4_hdmi_audio_widgets[] = { -+ SND_SOC_DAPM_OUTPUT("TX"), -+}; -+ -+static const struct snd_soc_dapm_route vc4_hdmi_audio_routes[] = { -+ { "TX", NULL, "Playback" }, -+}; -+ -+static const struct snd_soc_codec_driver vc4_hdmi_audio_codec_drv = { -+ .component_driver = { -+ .controls = vc4_hdmi_audio_controls, -+ .num_controls = ARRAY_SIZE(vc4_hdmi_audio_controls), -+ .dapm_widgets = vc4_hdmi_audio_widgets, -+ .num_dapm_widgets = ARRAY_SIZE(vc4_hdmi_audio_widgets), -+ .dapm_routes = vc4_hdmi_audio_routes, -+ .num_dapm_routes = ARRAY_SIZE(vc4_hdmi_audio_routes), -+ }, -+}; -+ -+static const struct snd_soc_dai_ops vc4_hdmi_audio_dai_ops = { -+ .startup = vc4_hdmi_audio_startup, -+ .shutdown = vc4_hdmi_audio_shutdown, -+ .hw_params = vc4_hdmi_audio_hw_params, -+ .set_fmt = vc4_hdmi_audio_set_fmt, -+ .trigger = vc4_hdmi_audio_trigger, -+}; -+ -+static struct snd_soc_dai_driver vc4_hdmi_audio_codec_dai_drv = { -+ .name = "vc4-hdmi-hifi", -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 2, -+ .channels_max = 8, -+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | -+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | -+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | -+ SNDRV_PCM_RATE_192000, -+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, -+ }, -+}; -+ -+static const struct snd_soc_component_driver vc4_hdmi_audio_cpu_dai_comp = { -+ .name = "vc4-hdmi-cpu-dai-component", -+}; -+ -+static int vc4_hdmi_audio_cpu_dai_probe(struct snd_soc_dai *dai) -+{ -+ struct vc4_hdmi *hdmi = dai_to_hdmi(dai); -+ -+ snd_soc_dai_init_dma_data(dai, &hdmi->audio.dma_data, NULL); -+ -+ return 0; -+} -+ -+static struct snd_soc_dai_driver vc4_hdmi_audio_cpu_dai_drv = { -+ .name = "vc4-hdmi-cpu-dai", -+ .probe = vc4_hdmi_audio_cpu_dai_probe, -+ .playback = { -+ .stream_name = "Playback", -+ .channels_min = 1, -+ .channels_max = 8, -+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | -+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | -+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | -+ SNDRV_PCM_RATE_192000, -+ .formats = SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE, -+ }, -+ .ops = &vc4_hdmi_audio_dai_ops, -+}; -+ -+static const struct snd_dmaengine_pcm_config pcm_conf = { -+ .chan_names[SNDRV_PCM_STREAM_PLAYBACK] = "audio-rx", -+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, -+}; -+ -+static int vc4_hdmi_audio_init(struct vc4_hdmi *hdmi) -+{ -+ struct snd_soc_dai_link *dai_link = &hdmi->audio.link; -+ struct snd_soc_card *card = &hdmi->audio.card; -+ struct device *dev = &hdmi->pdev->dev; -+ const __be32 *addr; -+ int ret; -+ -+ if (!of_find_property(dev->of_node, "dmas", NULL)) { -+ dev_warn(dev, -+ "'dmas' DT property is missing, no HDMI audio\n"); -+ return 0; -+ } -+ -+ /* -+ * Get the physical address of VC4_HD_MAI_DATA. We need to retrieve -+ * the bus address specified in the DT, because the physical address -+ * (the one returned by platform_get_resource()) is not appropriate -+ * for DMA transfers. -+ * This VC/MMU should probably be exposed to avoid this kind of hacks. -+ */ -+ addr = of_get_address(dev->of_node, 1, NULL, NULL); -+ hdmi->audio.dma_data.addr = be32_to_cpup(addr) + VC4_HD_MAI_DATA; -+ hdmi->audio.dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; -+ hdmi->audio.dma_data.maxburst = 2; -+ -+ ret = devm_snd_dmaengine_pcm_register(dev, &pcm_conf, 0); -+ if (ret) { -+ dev_err(dev, "Could not register PCM component: %d\n", ret); -+ return ret; -+ } -+ -+ ret = devm_snd_soc_register_component(dev, &vc4_hdmi_audio_cpu_dai_comp, -+ &vc4_hdmi_audio_cpu_dai_drv, 1); -+ if (ret) { -+ dev_err(dev, "Could not register CPU DAI: %d\n", ret); -+ return ret; -+ } -+ -+ /* register codec and codec dai */ -+ ret = snd_soc_register_codec(dev, &vc4_hdmi_audio_codec_drv, -+ &vc4_hdmi_audio_codec_dai_drv, 1); -+ if (ret) { -+ dev_err(dev, "Could not register codec: %d\n", ret); -+ return ret; -+ } -+ -+ dai_link->name = "MAI"; -+ dai_link->stream_name = "MAI PCM"; -+ dai_link->codec_dai_name = vc4_hdmi_audio_codec_dai_drv.name; -+ dai_link->cpu_dai_name = dev_name(dev); -+ dai_link->codec_name = dev_name(dev); -+ dai_link->platform_name = dev_name(dev); -+ -+ card->dai_link = dai_link; -+ card->num_links = 1; -+ card->name = "vc4-hdmi"; -+ card->dev = dev; -+ -+ /* -+ * Be careful, snd_soc_register_card() calls dev_set_drvdata() and -+ * stores a pointer to the snd card object in dev->driver_data. This -+ * means we cannot use it for something else. The hdmi back-pointer is -+ * now stored in card->drvdata and should be retrieved with -+ * snd_soc_card_get_drvdata() if needed. -+ */ -+ snd_soc_card_set_drvdata(card, hdmi); -+ ret = devm_snd_soc_register_card(dev, card); -+ if (ret) { -+ dev_err(dev, "Could not register sound card: %d\n", ret); -+ goto unregister_codec; -+ } -+ -+ return 0; -+ -+unregister_codec: -+ snd_soc_unregister_codec(dev); -+ -+ return ret; -+} -+ -+static void vc4_hdmi_audio_cleanup(struct vc4_hdmi *hdmi) -+{ -+ struct device *dev = &hdmi->pdev->dev; -+ -+ /* -+ * If drvdata is not set this means the audio card was not -+ * registered, just skip codec unregistration in this case. -+ */ -+ if (dev_get_drvdata(dev)) -+ snd_soc_unregister_codec(dev); -+} -+ - static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) - { - struct platform_device *pdev = to_platform_device(dev); -@@ -720,6 +1206,10 @@ static int vc4_hdmi_bind(struct device *dev, struct device *master, void *data) - goto err_destroy_encoder; - } - -+ ret = vc4_hdmi_audio_init(hdmi); -+ if (ret) -+ goto err_destroy_encoder; -+ - return 0; - - err_destroy_encoder: -@@ -741,6 +1231,8 @@ static void vc4_hdmi_unbind(struct device *dev, struct device *master, - struct vc4_dev *vc4 = drm->dev_private; - struct vc4_hdmi *hdmi = vc4->hdmi; - -+ vc4_hdmi_audio_cleanup(hdmi); -+ - vc4_hdmi_connector_destroy(hdmi->connector); - vc4_hdmi_encoder_destroy(hdmi->encoder); - -diff --git a/drivers/gpu/drm/vc4/vc4_regs.h b/drivers/gpu/drm/vc4/vc4_regs.h -index 385405a2df05..932093936178 100644 ---- a/drivers/gpu/drm/vc4/vc4_regs.h -+++ b/drivers/gpu/drm/vc4/vc4_regs.h -@@ -446,11 +446,62 @@ - #define VC4_HDMI_HOTPLUG 0x00c - # define VC4_HDMI_HOTPLUG_CONNECTED BIT(0) - -+/* 3 bits per field, where each field maps from that corresponding MAI -+ * bus channel to the given HDMI channel. -+ */ -+#define VC4_HDMI_MAI_CHANNEL_MAP 0x090 -+ -+#define VC4_HDMI_MAI_CONFIG 0x094 -+# define VC4_HDMI_MAI_CONFIG_FORMAT_REVERSE BIT(27) -+# define VC4_HDMI_MAI_CONFIG_BIT_REVERSE BIT(26) -+# define VC4_HDMI_MAI_CHANNEL_MASK_MASK VC4_MASK(15, 0) -+# define VC4_HDMI_MAI_CHANNEL_MASK_SHIFT 0 -+ -+/* Last received format word on the MAI bus. */ -+#define VC4_HDMI_MAI_FORMAT 0x098 -+ -+#define VC4_HDMI_AUDIO_PACKET_CONFIG 0x09c -+# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_SAMPLE_FLAT BIT(29) -+# define VC4_HDMI_AUDIO_PACKET_ZERO_DATA_ON_INACTIVE_CHANNELS BIT(24) -+# define VC4_HDMI_AUDIO_PACKET_FORCE_SAMPLE_PRESENT BIT(19) -+# define VC4_HDMI_AUDIO_PACKET_FORCE_B_FRAME BIT(18) -+# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_MASK VC4_MASK(13, 10) -+# define VC4_HDMI_AUDIO_PACKET_B_FRAME_IDENTIFIER_SHIFT 10 -+/* If set, then multichannel, otherwise 2 channel. */ -+# define VC4_HDMI_AUDIO_PACKET_AUDIO_LAYOUT BIT(9) -+/* If set, then AUDIO_LAYOUT overrides audio_cea_mask */ -+# define VC4_HDMI_AUDIO_PACKET_FORCE_AUDIO_LAYOUT BIT(8) -+# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_MASK VC4_MASK(7, 0) -+# define VC4_HDMI_AUDIO_PACKET_CEA_MASK_SHIFT 0 -+ - #define VC4_HDMI_RAM_PACKET_CONFIG 0x0a0 - # define VC4_HDMI_RAM_PACKET_ENABLE BIT(16) - - #define VC4_HDMI_RAM_PACKET_STATUS 0x0a4 - -+#define VC4_HDMI_CRP_CFG 0x0a8 -+/* When set, the CTS_PERIOD counts based on MAI bus sync pulse instead -+ * of pixel clock. -+ */ -+# define VC4_HDMI_CRP_USE_MAI_BUS_SYNC_FOR_CTS BIT(26) -+/* When set, no CRP packets will be sent. */ -+# define VC4_HDMI_CRP_CFG_DISABLE BIT(25) -+/* If set, generates CTS values based on N, audio clock, and video -+ * clock. N must be divisible by 128. -+ */ -+# define VC4_HDMI_CRP_CFG_EXTERNAL_CTS_EN BIT(24) -+# define VC4_HDMI_CRP_CFG_N_MASK VC4_MASK(19, 0) -+# define VC4_HDMI_CRP_CFG_N_SHIFT 0 -+ -+/* 20-bit fields containing CTS values to be transmitted if !EXTERNAL_CTS_EN */ -+#define VC4_HDMI_CTS_0 0x0ac -+#define VC4_HDMI_CTS_1 0x0b0 -+/* 20-bit fields containing number of clocks to send CTS0/1 before -+ * switching to the other one. -+ */ -+#define VC4_HDMI_CTS_PERIOD_0 0x0b4 -+#define VC4_HDMI_CTS_PERIOD_1 0x0b8 -+ - #define VC4_HDMI_HORZA 0x0c4 - # define VC4_HDMI_HORZA_VPOS BIT(14) - # define VC4_HDMI_HORZA_HPOS BIT(13) -@@ -512,7 +563,11 @@ - - #define VC4_HDMI_TX_PHY_RESET_CTL 0x2c0 - --#define VC4_HDMI_GCP_0 0x400 -+#define VC4_HDMI_TX_PHY_CTL0 0x2c4 -+# define VC4_HDMI_TX_PHY_RNG_PWRDN BIT(25) -+ -+#define VC4_HDMI_GCP(x) (0x400 + ((x) * 0x4)) -+#define VC4_HDMI_RAM_PACKET(x) (0x400 + ((x) * 0x24)) - #define VC4_HDMI_PACKET_STRIDE 0x24 - - #define VC4_HD_M_CTL 0x00c -@@ -522,6 +577,56 @@ - # define VC4_HD_M_ENABLE BIT(0) - - #define VC4_HD_MAI_CTL 0x014 -+/* Set when audio stream is received at a slower rate than the -+ * sampling period, so MAI fifo goes empty. Write 1 to clear. -+ */ -+# define VC4_HD_MAI_CTL_DLATE BIT(15) -+# define VC4_HD_MAI_CTL_BUSY BIT(14) -+# define VC4_HD_MAI_CTL_CHALIGN BIT(13) -+# define VC4_HD_MAI_CTL_WHOLSMP BIT(12) -+# define VC4_HD_MAI_CTL_FULL BIT(11) -+# define VC4_HD_MAI_CTL_EMPTY BIT(10) -+# define VC4_HD_MAI_CTL_FLUSH BIT(9) -+/* If set, MAI bus generates SPDIF (bit 31) parity instead of passing -+ * through. -+ */ -+# define VC4_HD_MAI_CTL_PAREN BIT(8) -+# define VC4_HD_MAI_CTL_CHNUM_MASK VC4_MASK(7, 4) -+# define VC4_HD_MAI_CTL_CHNUM_SHIFT 4 -+# define VC4_HD_MAI_CTL_ENABLE BIT(3) -+/* Underflow error status bit, write 1 to clear. */ -+# define VC4_HD_MAI_CTL_ERRORE BIT(2) -+/* Overflow error status bit, write 1 to clear. */ -+# define VC4_HD_MAI_CTL_ERRORF BIT(1) -+/* Single-shot reset bit. Read value is undefined. */ -+# define VC4_HD_MAI_CTL_RESET BIT(0) -+ -+#define VC4_HD_MAI_THR 0x018 -+# define VC4_HD_MAI_THR_PANICHIGH_MASK VC4_MASK(29, 24) -+# define VC4_HD_MAI_THR_PANICHIGH_SHIFT 24 -+# define VC4_HD_MAI_THR_PANICLOW_MASK VC4_MASK(21, 16) -+# define VC4_HD_MAI_THR_PANICLOW_SHIFT 16 -+# define VC4_HD_MAI_THR_DREQHIGH_MASK VC4_MASK(13, 8) -+# define VC4_HD_MAI_THR_DREQHIGH_SHIFT 8 -+# define VC4_HD_MAI_THR_DREQLOW_MASK VC4_MASK(5, 0) -+# define VC4_HD_MAI_THR_DREQLOW_SHIFT 0 -+ -+/* Format header to be placed on the MAI data. Unused. */ -+#define VC4_HD_MAI_FMT 0x01c -+ -+/* Register for DMAing in audio data to be transported over the MAI -+ * bus to the Falcon core. -+ */ -+#define VC4_HD_MAI_DATA 0x020 -+ -+/* Divider from HDMI HSM clock to MAI serial clock. Sampling period -+ * converges to N / (M + 1) cycles. -+ */ -+#define VC4_HD_MAI_SMP 0x02c -+# define VC4_HD_MAI_SMP_N_MASK VC4_MASK(31, 8) -+# define VC4_HD_MAI_SMP_N_SHIFT 8 -+# define VC4_HD_MAI_SMP_M_MASK VC4_MASK(7, 0) -+# define VC4_HD_MAI_SMP_M_SHIFT 0 - - #define VC4_HD_VID_CTL 0x038 - # define VC4_HD_VID_CTL_ENABLE BIT(31) --- -2.12.0 - From 25ea82d7f7c869ff81ff8e64d24c5c4a896239fe Mon Sep 17 00:00:00 2001 From: Boris Brezillon <boris.brezillon@free-electrons.com> Date: Mon, 27 Feb 2017 12:28:03 -0800 |