18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2008 Advanced Micro Devices, Inc.
38c2ecf20Sopenharmony_ci * Copyright 2008 Red Hat Inc.
48c2ecf20Sopenharmony_ci * Copyright 2009 Christian König.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
78c2ecf20Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
88c2ecf20Sopenharmony_ci * to deal in the Software without restriction, including without limitation
98c2ecf20Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
108c2ecf20Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
118c2ecf20Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
128c2ecf20Sopenharmony_ci *
138c2ecf20Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
148c2ecf20Sopenharmony_ci * all copies or substantial portions of the Software.
158c2ecf20Sopenharmony_ci *
168c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
178c2ecf20Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
188c2ecf20Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
198c2ecf20Sopenharmony_ci * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
208c2ecf20Sopenharmony_ci * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
218c2ecf20Sopenharmony_ci * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
228c2ecf20Sopenharmony_ci * OTHER DEALINGS IN THE SOFTWARE.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * Authors: Christian König
258c2ecf20Sopenharmony_ci */
268c2ecf20Sopenharmony_ci#include <linux/hdmi.h>
278c2ecf20Sopenharmony_ci#include <linux/gcd.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#include <drm/radeon_drm.h>
308c2ecf20Sopenharmony_ci#include "radeon.h"
318c2ecf20Sopenharmony_ci#include "radeon_asic.h"
328c2ecf20Sopenharmony_ci#include "radeon_audio.h"
338c2ecf20Sopenharmony_ci#include "r600d.h"
348c2ecf20Sopenharmony_ci#include "atom.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci/*
378c2ecf20Sopenharmony_ci * HDMI color format
388c2ecf20Sopenharmony_ci */
398c2ecf20Sopenharmony_cienum r600_hdmi_color_format {
408c2ecf20Sopenharmony_ci	RGB = 0,
418c2ecf20Sopenharmony_ci	YCC_422 = 1,
428c2ecf20Sopenharmony_ci	YCC_444 = 2
438c2ecf20Sopenharmony_ci};
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/*
468c2ecf20Sopenharmony_ci * IEC60958 status bits
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cienum r600_hdmi_iec_status_bits {
498c2ecf20Sopenharmony_ci	AUDIO_STATUS_DIG_ENABLE   = 0x01,
508c2ecf20Sopenharmony_ci	AUDIO_STATUS_V            = 0x02,
518c2ecf20Sopenharmony_ci	AUDIO_STATUS_VCFG         = 0x04,
528c2ecf20Sopenharmony_ci	AUDIO_STATUS_EMPHASIS     = 0x08,
538c2ecf20Sopenharmony_ci	AUDIO_STATUS_COPYRIGHT    = 0x10,
548c2ecf20Sopenharmony_ci	AUDIO_STATUS_NONAUDIO     = 0x20,
558c2ecf20Sopenharmony_ci	AUDIO_STATUS_PROFESSIONAL = 0x40,
568c2ecf20Sopenharmony_ci	AUDIO_STATUS_LEVEL        = 0x80
578c2ecf20Sopenharmony_ci};
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_cistatic struct r600_audio_pin r600_audio_status(struct radeon_device *rdev)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct r600_audio_pin status = {};
628c2ecf20Sopenharmony_ci	uint32_t value;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* number of channels */
678c2ecf20Sopenharmony_ci	status.channels = (value & 0x7) + 1;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/* bits per sample */
708c2ecf20Sopenharmony_ci	switch ((value & 0xF0) >> 4) {
718c2ecf20Sopenharmony_ci	case 0x0:
728c2ecf20Sopenharmony_ci		status.bits_per_sample = 8;
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	case 0x1:
758c2ecf20Sopenharmony_ci		status.bits_per_sample = 16;
768c2ecf20Sopenharmony_ci		break;
778c2ecf20Sopenharmony_ci	case 0x2:
788c2ecf20Sopenharmony_ci		status.bits_per_sample = 20;
798c2ecf20Sopenharmony_ci		break;
808c2ecf20Sopenharmony_ci	case 0x3:
818c2ecf20Sopenharmony_ci		status.bits_per_sample = 24;
828c2ecf20Sopenharmony_ci		break;
838c2ecf20Sopenharmony_ci	case 0x4:
848c2ecf20Sopenharmony_ci		status.bits_per_sample = 32;
858c2ecf20Sopenharmony_ci		break;
868c2ecf20Sopenharmony_ci	default:
878c2ecf20Sopenharmony_ci		dev_err(rdev->dev, "Unknown bits per sample 0x%x, using 16\n",
888c2ecf20Sopenharmony_ci			(int)value);
898c2ecf20Sopenharmony_ci		status.bits_per_sample = 16;
908c2ecf20Sopenharmony_ci	}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	/* current sampling rate in HZ */
938c2ecf20Sopenharmony_ci	if (value & 0x4000)
948c2ecf20Sopenharmony_ci		status.rate = 44100;
958c2ecf20Sopenharmony_ci	else
968c2ecf20Sopenharmony_ci		status.rate = 48000;
978c2ecf20Sopenharmony_ci	status.rate *= ((value >> 11) & 0x7) + 1;
988c2ecf20Sopenharmony_ci	status.rate /= ((value >> 8) & 0x7) + 1;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	value = RREG32(R600_AUDIO_STATUS_BITS);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/* iec 60958 status bits */
1038c2ecf20Sopenharmony_ci	status.status_bits = value & 0xff;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* iec 60958 category code */
1068c2ecf20Sopenharmony_ci	status.category_code = (value >> 8) & 0xff;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	return status;
1098c2ecf20Sopenharmony_ci}
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/*
1128c2ecf20Sopenharmony_ci * update all hdmi interfaces with current audio parameters
1138c2ecf20Sopenharmony_ci */
1148c2ecf20Sopenharmony_civoid r600_audio_update_hdmi(struct work_struct *work)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	struct radeon_device *rdev = container_of(work, struct radeon_device,
1178c2ecf20Sopenharmony_ci						  audio_work);
1188c2ecf20Sopenharmony_ci	struct drm_device *dev = rdev->ddev;
1198c2ecf20Sopenharmony_ci	struct r600_audio_pin audio_status = r600_audio_status(rdev);
1208c2ecf20Sopenharmony_ci	struct drm_encoder *encoder;
1218c2ecf20Sopenharmony_ci	bool changed = false;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (rdev->audio.pin[0].channels != audio_status.channels ||
1248c2ecf20Sopenharmony_ci	    rdev->audio.pin[0].rate != audio_status.rate ||
1258c2ecf20Sopenharmony_ci	    rdev->audio.pin[0].bits_per_sample != audio_status.bits_per_sample ||
1268c2ecf20Sopenharmony_ci	    rdev->audio.pin[0].status_bits != audio_status.status_bits ||
1278c2ecf20Sopenharmony_ci	    rdev->audio.pin[0].category_code != audio_status.category_code) {
1288c2ecf20Sopenharmony_ci		rdev->audio.pin[0] = audio_status;
1298c2ecf20Sopenharmony_ci		changed = true;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
1338c2ecf20Sopenharmony_ci		if (!radeon_encoder_is_digital(encoder))
1348c2ecf20Sopenharmony_ci			continue;
1358c2ecf20Sopenharmony_ci		if (changed || r600_hdmi_buffer_status_changed(encoder))
1368c2ecf20Sopenharmony_ci			r600_hdmi_update_audio_settings(encoder);
1378c2ecf20Sopenharmony_ci	}
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci/* enable the audio stream */
1418c2ecf20Sopenharmony_civoid r600_audio_enable(struct radeon_device *rdev,
1428c2ecf20Sopenharmony_ci		       struct r600_audio_pin *pin,
1438c2ecf20Sopenharmony_ci		       u8 enable_mask)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	u32 tmp = RREG32(AZ_HOT_PLUG_CONTROL);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	if (!pin)
1488c2ecf20Sopenharmony_ci		return;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (enable_mask) {
1518c2ecf20Sopenharmony_ci		tmp |= AUDIO_ENABLED;
1528c2ecf20Sopenharmony_ci		if (enable_mask & 1)
1538c2ecf20Sopenharmony_ci			tmp |= PIN0_AUDIO_ENABLED;
1548c2ecf20Sopenharmony_ci		if (enable_mask & 2)
1558c2ecf20Sopenharmony_ci			tmp |= PIN1_AUDIO_ENABLED;
1568c2ecf20Sopenharmony_ci		if (enable_mask & 4)
1578c2ecf20Sopenharmony_ci			tmp |= PIN2_AUDIO_ENABLED;
1588c2ecf20Sopenharmony_ci		if (enable_mask & 8)
1598c2ecf20Sopenharmony_ci			tmp |= PIN3_AUDIO_ENABLED;
1608c2ecf20Sopenharmony_ci	} else {
1618c2ecf20Sopenharmony_ci		tmp &= ~(AUDIO_ENABLED |
1628c2ecf20Sopenharmony_ci			 PIN0_AUDIO_ENABLED |
1638c2ecf20Sopenharmony_ci			 PIN1_AUDIO_ENABLED |
1648c2ecf20Sopenharmony_ci			 PIN2_AUDIO_ENABLED |
1658c2ecf20Sopenharmony_ci			 PIN3_AUDIO_ENABLED);
1668c2ecf20Sopenharmony_ci	}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	WREG32(AZ_HOT_PLUG_CONTROL, tmp);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistruct r600_audio_pin *r600_audio_get_pin(struct radeon_device *rdev)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	/* only one pin on 6xx-NI */
1748c2ecf20Sopenharmony_ci	return &rdev->audio.pin[0];
1758c2ecf20Sopenharmony_ci}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_civoid r600_hdmi_update_acr(struct drm_encoder *encoder, long offset,
1788c2ecf20Sopenharmony_ci	const struct radeon_hdmi_acr *acr)
1798c2ecf20Sopenharmony_ci{
1808c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
1818c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	/* DCE 3.0 uses register that's normally for CRC_CONTROL */
1848c2ecf20Sopenharmony_ci	uint32_t acr_ctl = ASIC_IS_DCE3(rdev) ? DCE3_HDMI0_ACR_PACKET_CONTROL :
1858c2ecf20Sopenharmony_ci				       HDMI0_ACR_PACKET_CONTROL;
1868c2ecf20Sopenharmony_ci	WREG32_P(acr_ctl + offset,
1878c2ecf20Sopenharmony_ci		HDMI0_ACR_SOURCE |		/* select SW CTS value */
1888c2ecf20Sopenharmony_ci		HDMI0_ACR_AUTO_SEND,	/* allow hw to sent ACR packets when required */
1898c2ecf20Sopenharmony_ci		~(HDMI0_ACR_SOURCE |
1908c2ecf20Sopenharmony_ci		HDMI0_ACR_AUTO_SEND));
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_ACR_32_0 + offset,
1938c2ecf20Sopenharmony_ci		HDMI0_ACR_CTS_32(acr->cts_32khz),
1948c2ecf20Sopenharmony_ci		~HDMI0_ACR_CTS_32_MASK);
1958c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_ACR_32_1 + offset,
1968c2ecf20Sopenharmony_ci		HDMI0_ACR_N_32(acr->n_32khz),
1978c2ecf20Sopenharmony_ci		~HDMI0_ACR_N_32_MASK);
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_ACR_44_0 + offset,
2008c2ecf20Sopenharmony_ci		HDMI0_ACR_CTS_44(acr->cts_44_1khz),
2018c2ecf20Sopenharmony_ci		~HDMI0_ACR_CTS_44_MASK);
2028c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_ACR_44_1 + offset,
2038c2ecf20Sopenharmony_ci		HDMI0_ACR_N_44(acr->n_44_1khz),
2048c2ecf20Sopenharmony_ci		~HDMI0_ACR_N_44_MASK);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_ACR_48_0 + offset,
2078c2ecf20Sopenharmony_ci		HDMI0_ACR_CTS_48(acr->cts_48khz),
2088c2ecf20Sopenharmony_ci		~HDMI0_ACR_CTS_48_MASK);
2098c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_ACR_48_1 + offset,
2108c2ecf20Sopenharmony_ci		HDMI0_ACR_N_48(acr->n_48khz),
2118c2ecf20Sopenharmony_ci		~HDMI0_ACR_N_48_MASK);
2128c2ecf20Sopenharmony_ci}
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci/*
2158c2ecf20Sopenharmony_ci * build a HDMI Video Info Frame
2168c2ecf20Sopenharmony_ci */
2178c2ecf20Sopenharmony_civoid r600_set_avi_packet(struct radeon_device *rdev, u32 offset,
2188c2ecf20Sopenharmony_ci			 unsigned char *buffer, size_t size)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	uint8_t *frame = buffer + 3;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	WREG32(HDMI0_AVI_INFO0 + offset,
2238c2ecf20Sopenharmony_ci		frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
2248c2ecf20Sopenharmony_ci	WREG32(HDMI0_AVI_INFO1 + offset,
2258c2ecf20Sopenharmony_ci		frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24));
2268c2ecf20Sopenharmony_ci	WREG32(HDMI0_AVI_INFO2 + offset,
2278c2ecf20Sopenharmony_ci		frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24));
2288c2ecf20Sopenharmony_ci	WREG32(HDMI0_AVI_INFO3 + offset,
2298c2ecf20Sopenharmony_ci		frame[0xC] | (frame[0xD] << 8) | (buffer[1] << 24));
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	WREG32_OR(HDMI0_INFOFRAME_CONTROL1 + offset,
2328c2ecf20Sopenharmony_ci		  HDMI0_AVI_INFO_LINE(2));	/* anything other than 0 */
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
2358c2ecf20Sopenharmony_ci		  HDMI0_AVI_INFO_SEND |	/* enable AVI info frames */
2368c2ecf20Sopenharmony_ci		  HDMI0_AVI_INFO_CONT);	/* send AVI info frames every frame/field */
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/*
2418c2ecf20Sopenharmony_ci * build a Audio Info Frame
2428c2ecf20Sopenharmony_ci */
2438c2ecf20Sopenharmony_cistatic void r600_hdmi_update_audio_infoframe(struct drm_encoder *encoder,
2448c2ecf20Sopenharmony_ci					     const void *buffer, size_t size)
2458c2ecf20Sopenharmony_ci{
2468c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
2478c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
2488c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
2498c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
2508c2ecf20Sopenharmony_ci	uint32_t offset = dig->afmt->offset;
2518c2ecf20Sopenharmony_ci	const u8 *frame = buffer + 3;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	WREG32(HDMI0_AUDIO_INFO0 + offset,
2548c2ecf20Sopenharmony_ci		frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
2558c2ecf20Sopenharmony_ci	WREG32(HDMI0_AUDIO_INFO1 + offset,
2568c2ecf20Sopenharmony_ci		frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24));
2578c2ecf20Sopenharmony_ci}
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/*
2608c2ecf20Sopenharmony_ci * test if audio buffer is filled enough to start playing
2618c2ecf20Sopenharmony_ci */
2628c2ecf20Sopenharmony_cistatic bool r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder)
2638c2ecf20Sopenharmony_ci{
2648c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
2658c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
2668c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
2678c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
2688c2ecf20Sopenharmony_ci	uint32_t offset = dig->afmt->offset;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	return (RREG32(HDMI0_STATUS + offset) & 0x10) != 0;
2718c2ecf20Sopenharmony_ci}
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci/*
2748c2ecf20Sopenharmony_ci * have buffer status changed since last call?
2758c2ecf20Sopenharmony_ci */
2768c2ecf20Sopenharmony_ciint r600_hdmi_buffer_status_changed(struct drm_encoder *encoder)
2778c2ecf20Sopenharmony_ci{
2788c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
2798c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
2808c2ecf20Sopenharmony_ci	int status, result;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	if (!dig->afmt || !dig->afmt->enabled)
2838c2ecf20Sopenharmony_ci		return 0;
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	status = r600_hdmi_is_audio_buffer_filled(encoder);
2868c2ecf20Sopenharmony_ci	result = dig->afmt->last_buffer_filled_status != status;
2878c2ecf20Sopenharmony_ci	dig->afmt->last_buffer_filled_status = status;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	return result;
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci/*
2938c2ecf20Sopenharmony_ci * write the audio workaround status to the hardware
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_civoid r600_hdmi_audio_workaround(struct drm_encoder *encoder)
2968c2ecf20Sopenharmony_ci{
2978c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
2988c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
2998c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
3008c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
3018c2ecf20Sopenharmony_ci	uint32_t offset = dig->afmt->offset;
3028c2ecf20Sopenharmony_ci	bool hdmi_audio_workaround = false; /* FIXME */
3038c2ecf20Sopenharmony_ci	u32 value;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	if (!hdmi_audio_workaround ||
3068c2ecf20Sopenharmony_ci	    r600_hdmi_is_audio_buffer_filled(encoder))
3078c2ecf20Sopenharmony_ci		value = 0; /* disable workaround */
3088c2ecf20Sopenharmony_ci	else
3098c2ecf20Sopenharmony_ci		value = HDMI0_AUDIO_TEST_EN; /* enable workaround */
3108c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset,
3118c2ecf20Sopenharmony_ci		 value, ~HDMI0_AUDIO_TEST_EN);
3128c2ecf20Sopenharmony_ci}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_civoid r600_hdmi_audio_set_dto(struct radeon_device *rdev,
3158c2ecf20Sopenharmony_ci			     struct radeon_crtc *crtc, unsigned int clock)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder;
3188c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig;
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci	if (!crtc)
3218c2ecf20Sopenharmony_ci		return;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	radeon_encoder = to_radeon_encoder(crtc->encoder);
3248c2ecf20Sopenharmony_ci	dig = radeon_encoder->enc_priv;
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_ci	if (!dig)
3278c2ecf20Sopenharmony_ci		return;
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	if (dig->dig_encoder == 0) {
3308c2ecf20Sopenharmony_ci		WREG32(DCCG_AUDIO_DTO0_PHASE, 24000 * 100);
3318c2ecf20Sopenharmony_ci		WREG32(DCCG_AUDIO_DTO0_MODULE, clock * 100);
3328c2ecf20Sopenharmony_ci		WREG32(DCCG_AUDIO_DTO_SELECT, 0); /* select DTO0 */
3338c2ecf20Sopenharmony_ci	} else {
3348c2ecf20Sopenharmony_ci		WREG32(DCCG_AUDIO_DTO1_PHASE, 24000 * 100);
3358c2ecf20Sopenharmony_ci		WREG32(DCCG_AUDIO_DTO1_MODULE, clock * 100);
3368c2ecf20Sopenharmony_ci		WREG32(DCCG_AUDIO_DTO_SELECT, 1); /* select DTO1 */
3378c2ecf20Sopenharmony_ci	}
3388c2ecf20Sopenharmony_ci}
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_civoid r600_set_vbi_packet(struct drm_encoder *encoder, u32 offset)
3418c2ecf20Sopenharmony_ci{
3428c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
3438c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	WREG32_OR(HDMI0_VBI_PACKET_CONTROL + offset,
3468c2ecf20Sopenharmony_ci		HDMI0_NULL_SEND |	/* send null packets when required */
3478c2ecf20Sopenharmony_ci		HDMI0_GC_SEND |		/* send general control packets */
3488c2ecf20Sopenharmony_ci		HDMI0_GC_CONT);		/* send general control packets every frame */
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_civoid r600_set_audio_packet(struct drm_encoder *encoder, u32 offset)
3528c2ecf20Sopenharmony_ci{
3538c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
3548c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_AUDIO_PACKET_CONTROL + offset,
3578c2ecf20Sopenharmony_ci		HDMI0_AUDIO_SAMPLE_SEND |			/* send audio packets */
3588c2ecf20Sopenharmony_ci		HDMI0_AUDIO_DELAY_EN(1) |			/* default audio delay */
3598c2ecf20Sopenharmony_ci		HDMI0_AUDIO_PACKETS_PER_LINE(3) |	/* should be suffient for all audio modes and small enough for all hblanks */
3608c2ecf20Sopenharmony_ci		HDMI0_60958_CS_UPDATE,				/* allow 60958 channel status fields to be updated */
3618c2ecf20Sopenharmony_ci		~(HDMI0_AUDIO_SAMPLE_SEND |
3628c2ecf20Sopenharmony_ci		HDMI0_AUDIO_DELAY_EN_MASK |
3638c2ecf20Sopenharmony_ci		HDMI0_AUDIO_PACKETS_PER_LINE_MASK |
3648c2ecf20Sopenharmony_ci		HDMI0_60958_CS_UPDATE));
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci	WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
3678c2ecf20Sopenharmony_ci		HDMI0_AUDIO_INFO_SEND |		/* enable audio info frames (frames won't be set until audio is enabled) */
3688c2ecf20Sopenharmony_ci		HDMI0_AUDIO_INFO_UPDATE);	/* required for audio info values to be updated */
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_INFOFRAME_CONTROL1 + offset,
3718c2ecf20Sopenharmony_ci		HDMI0_AUDIO_INFO_LINE(2),	/* anything other than 0 */
3728c2ecf20Sopenharmony_ci		~HDMI0_AUDIO_INFO_LINE_MASK);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	WREG32_AND(HDMI0_GENERIC_PACKET_CONTROL + offset,
3758c2ecf20Sopenharmony_ci		~(HDMI0_GENERIC0_SEND |
3768c2ecf20Sopenharmony_ci		HDMI0_GENERIC0_CONT |
3778c2ecf20Sopenharmony_ci		HDMI0_GENERIC0_UPDATE |
3788c2ecf20Sopenharmony_ci		HDMI0_GENERIC1_SEND |
3798c2ecf20Sopenharmony_ci		HDMI0_GENERIC1_CONT |
3808c2ecf20Sopenharmony_ci		HDMI0_GENERIC0_LINE_MASK |
3818c2ecf20Sopenharmony_ci		HDMI0_GENERIC1_LINE_MASK));
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_60958_0 + offset,
3848c2ecf20Sopenharmony_ci		HDMI0_60958_CS_CHANNEL_NUMBER_L(1),
3858c2ecf20Sopenharmony_ci		~(HDMI0_60958_CS_CHANNEL_NUMBER_L_MASK |
3868c2ecf20Sopenharmony_ci		HDMI0_60958_CS_CLOCK_ACCURACY_MASK));
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	WREG32_P(HDMI0_60958_1 + offset,
3898c2ecf20Sopenharmony_ci		HDMI0_60958_CS_CHANNEL_NUMBER_R(2),
3908c2ecf20Sopenharmony_ci		~HDMI0_60958_CS_CHANNEL_NUMBER_R_MASK);
3918c2ecf20Sopenharmony_ci}
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_civoid r600_set_mute(struct drm_encoder *encoder, u32 offset, bool mute)
3948c2ecf20Sopenharmony_ci{
3958c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
3968c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	if (mute)
3998c2ecf20Sopenharmony_ci		WREG32_OR(HDMI0_GC + offset, HDMI0_GC_AVMUTE);
4008c2ecf20Sopenharmony_ci	else
4018c2ecf20Sopenharmony_ci		WREG32_AND(HDMI0_GC + offset, ~HDMI0_GC_AVMUTE);
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/**
4058c2ecf20Sopenharmony_ci * r600_hdmi_update_audio_settings - Update audio infoframe
4068c2ecf20Sopenharmony_ci *
4078c2ecf20Sopenharmony_ci * @encoder: drm encoder
4088c2ecf20Sopenharmony_ci *
4098c2ecf20Sopenharmony_ci * Gets info about current audio stream and updates audio infoframe.
4108c2ecf20Sopenharmony_ci */
4118c2ecf20Sopenharmony_civoid r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
4128c2ecf20Sopenharmony_ci{
4138c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
4148c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
4158c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
4168c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
4178c2ecf20Sopenharmony_ci	struct r600_audio_pin audio = r600_audio_status(rdev);
4188c2ecf20Sopenharmony_ci	uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
4198c2ecf20Sopenharmony_ci	struct hdmi_audio_infoframe frame;
4208c2ecf20Sopenharmony_ci	uint32_t offset;
4218c2ecf20Sopenharmony_ci	uint32_t value;
4228c2ecf20Sopenharmony_ci	ssize_t err;
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci	if (!dig->afmt || !dig->afmt->enabled)
4258c2ecf20Sopenharmony_ci		return;
4268c2ecf20Sopenharmony_ci	offset = dig->afmt->offset;
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n",
4298c2ecf20Sopenharmony_ci		 r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped",
4308c2ecf20Sopenharmony_ci		  audio.channels, audio.rate, audio.bits_per_sample);
4318c2ecf20Sopenharmony_ci	DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n",
4328c2ecf20Sopenharmony_ci		  (int)audio.status_bits, (int)audio.category_code);
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	err = hdmi_audio_infoframe_init(&frame);
4358c2ecf20Sopenharmony_ci	if (err < 0) {
4368c2ecf20Sopenharmony_ci		DRM_ERROR("failed to setup audio infoframe\n");
4378c2ecf20Sopenharmony_ci		return;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	frame.channels = audio.channels;
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	err = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
4438c2ecf20Sopenharmony_ci	if (err < 0) {
4448c2ecf20Sopenharmony_ci		DRM_ERROR("failed to pack audio infoframe\n");
4458c2ecf20Sopenharmony_ci		return;
4468c2ecf20Sopenharmony_ci	}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	value = RREG32(HDMI0_AUDIO_PACKET_CONTROL + offset);
4498c2ecf20Sopenharmony_ci	if (value & HDMI0_AUDIO_TEST_EN)
4508c2ecf20Sopenharmony_ci		WREG32(HDMI0_AUDIO_PACKET_CONTROL + offset,
4518c2ecf20Sopenharmony_ci		       value & ~HDMI0_AUDIO_TEST_EN);
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	WREG32_OR(HDMI0_CONTROL + offset,
4548c2ecf20Sopenharmony_ci		  HDMI0_ERROR_ACK);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	WREG32_AND(HDMI0_INFOFRAME_CONTROL0 + offset,
4578c2ecf20Sopenharmony_ci		   ~HDMI0_AUDIO_INFO_SOURCE);
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	r600_hdmi_update_audio_infoframe(encoder, buffer, sizeof(buffer));
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	WREG32_OR(HDMI0_INFOFRAME_CONTROL0 + offset,
4628c2ecf20Sopenharmony_ci		  HDMI0_AUDIO_INFO_CONT |
4638c2ecf20Sopenharmony_ci		  HDMI0_AUDIO_INFO_UPDATE);
4648c2ecf20Sopenharmony_ci}
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci/*
4678c2ecf20Sopenharmony_ci * enable the HDMI engine
4688c2ecf20Sopenharmony_ci */
4698c2ecf20Sopenharmony_civoid r600_hdmi_enable(struct drm_encoder *encoder, bool enable)
4708c2ecf20Sopenharmony_ci{
4718c2ecf20Sopenharmony_ci	struct drm_device *dev = encoder->dev;
4728c2ecf20Sopenharmony_ci	struct radeon_device *rdev = dev->dev_private;
4738c2ecf20Sopenharmony_ci	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
4748c2ecf20Sopenharmony_ci	struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
4758c2ecf20Sopenharmony_ci	u32 hdmi = HDMI0_ERROR_ACK;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if (!dig || !dig->afmt)
4788c2ecf20Sopenharmony_ci		return;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	/* Older chipsets require setting HDMI and routing manually */
4818c2ecf20Sopenharmony_ci	if (!ASIC_IS_DCE3(rdev)) {
4828c2ecf20Sopenharmony_ci		if (enable)
4838c2ecf20Sopenharmony_ci			hdmi |= HDMI0_ENABLE;
4848c2ecf20Sopenharmony_ci		switch (radeon_encoder->encoder_id) {
4858c2ecf20Sopenharmony_ci		case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
4868c2ecf20Sopenharmony_ci			if (enable) {
4878c2ecf20Sopenharmony_ci				WREG32_OR(AVIVO_TMDSA_CNTL, AVIVO_TMDSA_CNTL_HDMI_EN);
4888c2ecf20Sopenharmony_ci				hdmi |= HDMI0_STREAM(HDMI0_STREAM_TMDSA);
4898c2ecf20Sopenharmony_ci			} else {
4908c2ecf20Sopenharmony_ci				WREG32_AND(AVIVO_TMDSA_CNTL, ~AVIVO_TMDSA_CNTL_HDMI_EN);
4918c2ecf20Sopenharmony_ci			}
4928c2ecf20Sopenharmony_ci			break;
4938c2ecf20Sopenharmony_ci		case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
4948c2ecf20Sopenharmony_ci			if (enable) {
4958c2ecf20Sopenharmony_ci				WREG32_OR(AVIVO_LVTMA_CNTL, AVIVO_LVTMA_CNTL_HDMI_EN);
4968c2ecf20Sopenharmony_ci				hdmi |= HDMI0_STREAM(HDMI0_STREAM_LVTMA);
4978c2ecf20Sopenharmony_ci			} else {
4988c2ecf20Sopenharmony_ci				WREG32_AND(AVIVO_LVTMA_CNTL, ~AVIVO_LVTMA_CNTL_HDMI_EN);
4998c2ecf20Sopenharmony_ci			}
5008c2ecf20Sopenharmony_ci			break;
5018c2ecf20Sopenharmony_ci		case ENCODER_OBJECT_ID_INTERNAL_DDI:
5028c2ecf20Sopenharmony_ci			if (enable) {
5038c2ecf20Sopenharmony_ci				WREG32_OR(DDIA_CNTL, DDIA_HDMI_EN);
5048c2ecf20Sopenharmony_ci				hdmi |= HDMI0_STREAM(HDMI0_STREAM_DDIA);
5058c2ecf20Sopenharmony_ci			} else {
5068c2ecf20Sopenharmony_ci				WREG32_AND(DDIA_CNTL, ~DDIA_HDMI_EN);
5078c2ecf20Sopenharmony_ci			}
5088c2ecf20Sopenharmony_ci			break;
5098c2ecf20Sopenharmony_ci		case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
5108c2ecf20Sopenharmony_ci			if (enable)
5118c2ecf20Sopenharmony_ci				hdmi |= HDMI0_STREAM(HDMI0_STREAM_DVOA);
5128c2ecf20Sopenharmony_ci			break;
5138c2ecf20Sopenharmony_ci		default:
5148c2ecf20Sopenharmony_ci			dev_err(rdev->dev, "Invalid encoder for HDMI: 0x%X\n",
5158c2ecf20Sopenharmony_ci				radeon_encoder->encoder_id);
5168c2ecf20Sopenharmony_ci			break;
5178c2ecf20Sopenharmony_ci		}
5188c2ecf20Sopenharmony_ci		WREG32(HDMI0_CONTROL + dig->afmt->offset, hdmi);
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci
5218c2ecf20Sopenharmony_ci	if (rdev->irq.installed) {
5228c2ecf20Sopenharmony_ci		/* if irq is available use it */
5238c2ecf20Sopenharmony_ci		/* XXX: shouldn't need this on any asics.  Double check DCE2/3 */
5248c2ecf20Sopenharmony_ci		if (enable)
5258c2ecf20Sopenharmony_ci			radeon_irq_kms_enable_afmt(rdev, dig->afmt->id);
5268c2ecf20Sopenharmony_ci		else
5278c2ecf20Sopenharmony_ci			radeon_irq_kms_disable_afmt(rdev, dig->afmt->id);
5288c2ecf20Sopenharmony_ci	}
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	dig->afmt->enabled = enable;
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci	DRM_DEBUG("%sabling HDMI interface @ 0x%04X for encoder 0x%x\n",
5338c2ecf20Sopenharmony_ci		  enable ? "En" : "Dis", dig->afmt->offset, radeon_encoder->encoder_id);
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
536