Add initial support for vc4 HDMI Audio
This commit is contained in:
		
							parent
							
								
									88fc0e2f65
								
							
						
					
					
						commit
						580d60f07a
					
				
							
								
								
									
										836
									
								
								bcm283x-hdmi-audio.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										836
									
								
								bcm283x-hdmi-audio.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,836 @@ | ||||
| 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 | ||||
| Subject: [PATCH 3/3] ARM: dts: bcm283x: Add HDMI audio related properties | ||||
| 
 | ||||
| Add the dmas and dma-names properties to support HDMI audio. | ||||
| 
 | ||||
| Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com> | ||||
| Signed-off-by: Eric Anholt <eric@anholt.net> | ||||
| ---
 | ||||
|  arch/arm/boot/dts/bcm283x.dtsi | 2 ++ | ||||
|  1 file changed, 2 insertions(+) | ||||
| 
 | ||||
| diff --git a/arch/arm/boot/dts/bcm283x.dtsi b/arch/arm/boot/dts/bcm283x.dtsi
 | ||||
| index a3106aa446c6..a31b0b303ddc 100644
 | ||||
| --- a/arch/arm/boot/dts/bcm283x.dtsi
 | ||||
| +++ b/arch/arm/boot/dts/bcm283x.dtsi
 | ||||
| @@ -499,6 +499,8 @@
 | ||||
|  			clocks = <&clocks BCM2835_PLLH_PIX>, | ||||
|  				 <&clocks BCM2835_CLOCK_HSM>; | ||||
|  			clock-names = "pixel", "hdmi"; | ||||
| +			dmas = <&dma 17>;
 | ||||
| +			dma-names = "audio-rx";
 | ||||
|  			status = "disabled"; | ||||
|  		}; | ||||
|   | ||||
| -- 
 | ||||
| 2.12.0 | ||||
| 
 | ||||
| @ -13,4 +13,4 @@ driverdirs="atm auxdisplay bcma bluetooth firewire fmc infiniband isdn leds medi | ||||
| 
 | ||||
| ethdrvs="3com adaptec arc alteon atheros broadcom cadence calxeda chelsio cisco dec dlink emulex icplus marvell micrel myricom neterion nvidia oki-semi packetengines qlogic rdc renesas sfc silan sis smsc stmicro sun tehuti ti via wiznet xircom" | ||||
| 
 | ||||
| singlemods="ntb_netdev iscsi_ibft iscsi_boot_sysfs megaraid pmcraid qedi qla1280 9pnet_rdma rpcrdma nvmet-rdma nvme-rdma hid-picolcd hid-prodikeys hwa-hc hwpoison-inject target_core_user sbp_target cxgbit iw_cxgb3 iw_cxgb4 cxgb3i cxgb3i cxgb3i_ddp cxgb4i chcr" | ||||
| singlemods="ntb_netdev iscsi_ibft iscsi_boot_sysfs megaraid pmcraid qedi qla1280 9pnet_rdma rpcrdma nvmet-rdma nvme-rdma hid-picolcd hid-prodikeys hwa-hc hwpoison-inject target_core_user sbp_target cxgbit iw_cxgb3 iw_cxgb4 cxgb3i cxgb3i cxgb3i_ddp cxgb4i chcr vc4" | ||||
|  | ||||
| @ -15,4 +15,4 @@ ethdrvs="3com adaptec alteon altera amd atheros broadcom cadence chelsio cisco d | ||||
| 
 | ||||
| drmdrvs="amd armada bridge ast exynos i2c imx mgag200 msm omapdrm panel nouveau radeon rockchip tegra tilcdc via" | ||||
| 
 | ||||
| singlemods="ntb_netdev iscsi_ibft iscsi_boot_sysfs megaraid pmcraid qedi qla1280 9pnet_rdma rpcrdma nvmet-rdma nvme-rdma hid-picolcd hid-prodikeys hwa-hc hwpoison-inject target_core_user sbp_target cxgbit iw_cxgb3 iw_cxgb4 cxgb3i cxgb3i cxgb3i_ddp cxgb4i chcr" | ||||
| singlemods="ntb_netdev iscsi_ibft iscsi_boot_sysfs megaraid pmcraid qedi qla1280 9pnet_rdma rpcrdma nvmet-rdma nvme-rdma hid-picolcd hid-prodikeys hwa-hc hwpoison-inject target_core_user sbp_target cxgbit iw_cxgb3 iw_cxgb4 cxgb3i cxgb3i cxgb3i_ddp cxgb4i chcr vc4" | ||||
|  | ||||
| @ -543,6 +543,8 @@ Patch435: bcm283x-fixes.patch | ||||
| # https://lists.freedesktop.org/archives/dri-devel/2017-February/133823.html | ||||
| Patch436: vc4-fix-vblank-cursor-update-issue.patch | ||||
| 
 | ||||
| Patch437: bcm283x-hdmi-audio.patch | ||||
| 
 | ||||
| # http://www.spinics.net/lists/arm-kernel/msg552554.html | ||||
| Patch438: arm-imx6-hummingboard2.patch | ||||
| 
 | ||||
| @ -2167,6 +2169,9 @@ fi | ||||
| # | ||||
| # | ||||
| %changelog | ||||
| * Tue Mar 21 2017 Peter Robinson <pbrobinson@fedoraproject.org> | ||||
| - Add initial support for vc4 HDMI Audio | ||||
| 
 | ||||
| * Mon Mar 20 2017 Laura Abbott <labbott@fedoraproject.org> - 4.11.0-0.rc3.git0.1 | ||||
| - Linux v4.11-rc3 | ||||
| - Fix for debuginfo conflicts (rhbz 1431296) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user