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.
Signed-off-by: Eric Anholt
---
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..56c285253ee5 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-