Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f2594933 authored by Christian Koenig's avatar Christian Koenig Committed by Dave Airlie
Browse files

drm/radeon/kms: HDMI irq support



Implements irq support for HDMI audio output. Now the polling timer
is only enabled if irq support isn't available.

Signed-off-by: default avatarChristian König <deathsimple@vodafone.de>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
parent 58bd0863
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -2527,6 +2527,7 @@ int r600_irq_set(struct radeon_device *rdev)
	u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
	u32 mode_int = 0;
	u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
	u32 hdmi1, hdmi2;

	if (!rdev->irq.installed) {
		WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
@@ -2540,7 +2541,9 @@ int r600_irq_set(struct radeon_device *rdev)
		return 0;
	}

	hdmi1 = RREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
	if (ASIC_IS_DCE3(rdev)) {
		hdmi2 = RREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
		hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
		hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
		hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
@@ -2550,6 +2553,7 @@ int r600_irq_set(struct radeon_device *rdev)
			hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
		}
	} else {
		hdmi2 = RREG32(R600_HDMI_BLOCK2 + R600_HDMI_CNTL) & ~R600_HDMI_INT_EN;
		hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN;
		hpd2 = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & ~DC_HPDx_INT_EN;
		hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN;
@@ -2591,10 +2595,20 @@ int r600_irq_set(struct radeon_device *rdev)
		DRM_DEBUG("r600_irq_set: hpd 6\n");
		hpd6 |= DC_HPDx_INT_EN;
	}
	if (rdev->irq.hdmi[0]) {
		DRM_DEBUG("r600_irq_set: hdmi 1\n");
		hdmi1 |= R600_HDMI_INT_EN;
	}
	if (rdev->irq.hdmi[1]) {
		DRM_DEBUG("r600_irq_set: hdmi 2\n");
		hdmi2 |= R600_HDMI_INT_EN;
	}

	WREG32(CP_INT_CNTL, cp_int_cntl);
	WREG32(DxMODE_INT_MASK, mode_int);
	WREG32(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, hdmi1);
	if (ASIC_IS_DCE3(rdev)) {
		WREG32(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, hdmi2);
		WREG32(DC_HPD1_INT_CONTROL, hpd1);
		WREG32(DC_HPD2_INT_CONTROL, hpd2);
		WREG32(DC_HPD3_INT_CONTROL, hpd3);
@@ -2604,6 +2618,7 @@ int r600_irq_set(struct radeon_device *rdev)
			WREG32(DC_HPD6_INT_CONTROL, hpd6);
		}
	} else {
		WREG32(R600_HDMI_BLOCK2 + R600_HDMI_CNTL, hdmi2);
		WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
		WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
		WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3);
@@ -2687,6 +2702,18 @@ static inline void r600_irq_ack(struct radeon_device *rdev,
			WREG32(DC_HPD6_INT_CONTROL, tmp);
		}
	}
	if (RREG32(R600_HDMI_BLOCK1 + R600_HDMI_STATUS) & R600_HDMI_INT_PENDING) {
		WREG32_P(R600_HDMI_BLOCK1 + R600_HDMI_CNTL, R600_HDMI_INT_ACK, ~R600_HDMI_INT_ACK);
	}
	if (ASIC_IS_DCE3(rdev)) {
		if (RREG32(R600_HDMI_BLOCK3 + R600_HDMI_STATUS) & R600_HDMI_INT_PENDING) {
			WREG32_P(R600_HDMI_BLOCK3 + R600_HDMI_CNTL, R600_HDMI_INT_ACK, ~R600_HDMI_INT_ACK);
		}
	} else {
		if (RREG32(R600_HDMI_BLOCK2 + R600_HDMI_STATUS) & R600_HDMI_INT_PENDING) {
			WREG32_P(R600_HDMI_BLOCK2 + R600_HDMI_CNTL, R600_HDMI_INT_ACK, ~R600_HDMI_INT_ACK);
		}
	}
}

void r600_irq_disable(struct radeon_device *rdev)
@@ -2740,6 +2767,8 @@ static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
 *     19         1  FP Hot plug detection B
 *     19         2  DAC A auto-detection
 *     19         3  DAC B auto-detection
 *     21         4  HDMI block A
 *     21         5  HDMI block B
 *    176         -  CP_INT RB
 *    177         -  CP_INT IB1
 *    178         -  CP_INT IB2
@@ -2879,6 +2908,10 @@ int r600_irq_process(struct radeon_device *rdev)
				break;
			}
			break;
		case 21: /* HDMI */
			DRM_DEBUG("IH: HDMI: 0x%x\n", src_data);
			r600_audio_schedule_polling(rdev);
			break;
		case 176: /* CP_INT in ring buffer */
		case 177: /* CP_INT in IB1 */
		case 178: /* CP_INT in IB2 */
+13 −8
Original line number Diff line number Diff line
@@ -103,6 +103,15 @@ uint8_t r600_audio_category_code(struct radeon_device *rdev)
	return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff;
}

/*
 * schedule next audio update event
 */
void r600_audio_schedule_polling(struct radeon_device *rdev)
{
	mod_timer(&rdev->audio_timer,
		jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
}

/*
 * update all hdmi interfaces with current audio parameters
 */
@@ -136,16 +145,12 @@ static void r600_audio_update_hdmi(unsigned long param)

	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
		struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
		if (radeon_encoder->audio_polling_active) {
			still_going = 1;
		still_going |= radeon_encoder->audio_polling_active;
		if (changes || r600_hdmi_buffer_status_changed(encoder))
			r600_hdmi_update_audio_settings(encoder);
	}
	}

	if(still_going)
		mod_timer(&rdev->audio_timer,
			jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
	if(still_going) r600_audio_schedule_polling(rdev);
}

/*
+33 −21
Original line number Diff line number Diff line
@@ -290,17 +290,15 @@ void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
	if (!offset)
		return;

	if (r600_hdmi_is_audio_buffer_filled(encoder)) {
		/* disable audio workaround and start delivering of audio frames */
		WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
	if (!radeon_encoder->hdmi_audio_workaround ||
		r600_hdmi_is_audio_buffer_filled(encoder)) {

	} else if (radeon_encoder->hdmi_audio_workaround) {
		/* enable audio workaround and start delivering of audio frames */
		WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
		/* disable audio workaround */
		WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);

	} else {
		/* disable audio workaround and stop delivering of audio frames */
		WREG32_P(offset+R600_HDMI_CNTL, 0x00000000, ~0x00001001);
		/* enable audio workaround */
		WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
	}
}

@@ -345,9 +343,6 @@ void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mod

	/* audio packets per line, does anyone know how to calc this ? */
	WREG32_P(offset+R600_HDMI_CNTL, 0x00040000, ~0x001F0000);

	/* update? reset? don't realy know */
	WREG32_P(offset+R600_HDMI_CNTL, 0x14000000, ~0x14000000);
}

/*
@@ -416,9 +411,6 @@ void r600_hdmi_update_audio_settings(struct drm_encoder *encoder)
	r600_hdmi_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0);

	r600_hdmi_audio_workaround(encoder);

	/* update? reset? don't realy know */
	WREG32_P(offset+R600_HDMI_CNTL, 0x04000000, ~0x04000000);
}

static int r600_hdmi_find_free_block(struct drm_device *dev)
@@ -487,6 +479,7 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
	struct drm_device *dev = encoder->dev;
	struct radeon_device *rdev = dev->dev_private;
	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
	uint32_t offset;

	if (ASIC_IS_DCE4(rdev))
		return;
@@ -500,10 +493,10 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
		}
	}

	offset = radeon_encoder->hdmi_offset;
	if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) {
		WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0x1, ~0x1);
	} else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
		int offset = radeon_encoder->hdmi_offset;
		switch (radeon_encoder->encoder_id) {
		case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
			WREG32_P(AVIVO_TMDSA_CNTL, 0x4, ~0x4);
@@ -519,7 +512,20 @@ void r600_hdmi_enable(struct drm_encoder *encoder)
		}
	}

	if (rdev->irq.installed
	    && rdev->family != CHIP_RS600
	    && rdev->family != CHIP_RS690
	    && rdev->family != CHIP_RS740) {

		/* if irq is available use it */
		rdev->irq.hdmi[offset == R600_HDMI_BLOCK1 ? 0 : 1] = true;
		radeon_irq_set(rdev);

		r600_audio_disable_polling(encoder);
	} else {
		/* if not fallback to polling */
		r600_audio_enable_polling(encoder);
	}

	DRM_DEBUG("Enabling HDMI interface @ 0x%04X for encoder 0x%x\n",
		radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
@@ -533,24 +539,30 @@ void r600_hdmi_disable(struct drm_encoder *encoder)
	struct drm_device *dev = encoder->dev;
	struct radeon_device *rdev = dev->dev_private;
	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
	uint8_t offset;

	if (ASIC_IS_DCE4(rdev))
		return;

	if (!radeon_encoder->hdmi_offset) {
	offset = radeon_encoder->hdmi_offset;
	if (!offset) {
		dev_err(rdev->dev, "Disabling not enabled HDMI\n");
		return;
	}

	r600_audio_disable_polling(encoder);

	DRM_DEBUG("Disabling HDMI interface @ 0x%04X for encoder 0x%x\n",
		radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
		offset, radeon_encoder->encoder_id);

	/* disable irq */
	rdev->irq.hdmi[offset == R600_HDMI_BLOCK1 ? 0 : 1] = false;
	radeon_irq_set(rdev);

	/* disable polling */
	r600_audio_disable_polling(encoder);

	if (ASIC_IS_DCE32(rdev) && !ASIC_IS_DCE4(rdev)) {
		WREG32_P(radeon_encoder->hdmi_config_offset + 0x4, 0, ~0x1);
	} else if (rdev->family >= CHIP_R600 && !ASIC_IS_DCE3(rdev)) {
		int offset = radeon_encoder->hdmi_offset;
		switch (radeon_encoder->encoder_id) {
		case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
			WREG32_P(AVIVO_TMDSA_CNTL, 0, ~0x4);
+30 −27
Original line number Diff line number Diff line
@@ -159,7 +159,10 @@
/* HDMI registers */
#define R600_HDMI_ENABLE                0x00
#define R600_HDMI_STATUS                0x04
#       define R600_HDMI_INT_PENDING    (1 << 29)
#define R600_HDMI_CNTL                  0x08
#       define R600_HDMI_INT_EN         (1 << 28)
#       define R600_HDMI_INT_ACK        (1 << 29)
#define R600_HDMI_UNKNOWN_0             0x0C
#define R600_HDMI_AUDIOCNTL             0x10
#define R600_HDMI_VIDEOCNTL             0x14
+3 −0
Original line number Diff line number Diff line
@@ -376,6 +376,8 @@ struct radeon_irq {
	wait_queue_head_t	vblank_queue;
	/* FIXME: use defines for max hpd/dacs */
	bool            hpd[6];
	/* FIXME: use defines for max HDMI blocks */
	bool		hdmi[2];
	spinlock_t sw_lock;
	int sw_refcount;
};
@@ -1332,6 +1334,7 @@ extern int r600_audio_bits_per_sample(struct radeon_device *rdev);
extern int r600_audio_rate(struct radeon_device *rdev);
extern uint8_t r600_audio_status_bits(struct radeon_device *rdev);
extern uint8_t r600_audio_category_code(struct radeon_device *rdev);
extern void r600_audio_schedule_polling(struct radeon_device *rdev);
extern void r600_audio_enable_polling(struct drm_encoder *encoder);
extern void r600_audio_disable_polling(struct drm_encoder *encoder);
extern void r600_audio_fini(struct radeon_device *rdev);