[alsa-devel] [PATCH 4/5] [RFC]intel_hdmi_audio:driver module-hdmi hw interface

ramesh.babu at intel.com ramesh.babu at intel.com
Mon Nov 22 14:39:45 CET 2010


From: Ramesh Babu K V <ramesh.babu at intel.com>

This patch programs hdmi audio hw registers whenever
ALSA _prepare function is invoked.  It calculates N and CTS
values according HDMI 1.3a spec and programs it.
It also programs hw buffer pointer and length registers.

Signed-off-by: Ramesh Babu K V <ramesh.babu at intel.com>
Signed-off-by: Sailaja Bandarupalli <sailaja.bandarupalli at intel.com>
---
 .../drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c  |  281 ++++++++++++++++++++
 1 files changed, 281 insertions(+), 0 deletions(-)

diff --git a/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c b/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c
index 3ef4d48..6701a88 100644
--- a/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c
+++ b/sound/drivers/intel_mid_hdmi/intel_mid_hdmi_audio.c
@@ -99,6 +99,287 @@ static const struct snd_pcm_hardware snd_intel_hadstream = {
 struct snd_intelhad *intelhaddata;
 
 /**
+ * snd_intelhad_init_audio_ctrl - to initialize audio channel status
+ * registers and confgiuration registers
+ *
+ * @substream:substream for which the prepare function is called
+ *
+ * This function is called in the prepare callback
+ */
+static int snd_intelhad_init_audio_ctrl(struct snd_pcm_substream *substream)
+{
+	int retval;
+	union aud_cfg cfg_val = {.cfg_regval = 0};
+	union aud_ch_status_0 ch_stat0 = {.status_0_regval = 0};
+	union aud_ch_status_1 ch_stat1 = {.status_1_regval = 0};
+	union aud_buf_config buf_cfg = {.buf_cfgval = 0};
+	u8 channels;
+
+	/* Currently only PCM stream is supported */
+	ch_stat0.status_0_regx.lpcm_id = 0;
+
+	switch (substream->runtime->rate) {
+	case AUD_SAMPLE_RATE_32:
+		ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_32KHZ;
+	break;
+	case AUD_SAMPLE_RATE_44_1:
+	case AUD_SAMPLE_RATE_88_2:
+	case AUD_SAMPLE_RATE_176_4:
+		ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_44KHZ;
+	break;
+	case AUD_SAMPLE_RATE_48:
+	case AUD_SAMPLE_RATE_96:
+	case HAD_MAX_RATE:
+		ch_stat0.status_0_regx.samp_freq = CH_STATUS_MAP_48KHZ;
+	break;
+	default:
+		retval = -EINVAL;
+	break;
+	}
+	ch_stat0.status_0_regx.clk_acc = 0;
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+				AUD_CH_STATUS_0, ch_stat0.status_0_regval);
+	if (retval)
+		return retval;
+
+	if (substream->runtime->format == SNDRV_PCM_FORMAT_S16_LE) {
+		ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_20;
+		ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_16BITS;
+	} else if (substream->runtime->format == SNDRV_PCM_FORMAT_S24_LE) {
+		ch_stat1.status_1_regx.max_wrd_len = MAX_SMPL_WIDTH_24;
+		ch_stat1.status_1_regx.wrd_len = SMPL_WIDTH_24BITS;
+	} else {
+		ch_stat1.status_1_regx.max_wrd_len = 0;
+		ch_stat1.status_1_regx.wrd_len = 0;
+	}
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+				AUD_CH_STATUS_1, ch_stat1.status_1_regval);
+	if (retval)
+		return retval;
+
+	buf_cfg.buf_cfg_regx.fifo_width	 = FIFO_THRESHOLD;
+	buf_cfg.buf_cfg_regx.aud_delay	 = 0; /* TODO: Read from EDID */
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_BUF_CONFIG, buf_cfg.buf_cfgval);
+	if (retval)
+		return retval;
+
+	channels = substream->runtime->channels;
+	switch (channels) {
+	case 1:
+	case 2:
+		cfg_val.cfg_regx.num_ch = CH_STEREO;
+		cfg_val.cfg_regx.layout = LAYOUT0;
+		break;
+	case 3:
+	case 4:
+		cfg_val.cfg_regx.num_ch = CH_THREE_FOUR;
+		cfg_val.cfg_regx.layout = LAYOUT1;
+		break;
+	case 5:
+	case 6:
+		cfg_val.cfg_regx.num_ch = CH_FIVE_SIX;
+		cfg_val.cfg_regx.layout = LAYOUT1;
+		break;
+	case 7:
+	case 8:
+		cfg_val.cfg_regx.num_ch = CH_SEVEN_EIGHT;
+		cfg_val.cfg_regx.layout = LAYOUT1;
+		break;
+	}
+	cfg_val.cfg_regx.val_bit = 1;
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+						AUD_CONFIG, cfg_val.cfg_regval);
+	if (retval)
+		return retval;
+	return 0;
+}
+
+/**
+ * snd_intelhad_prog_DIP - to initialize Data Island Packets registers
+ *
+ * @substream:substream for which the prepare function is called
+ *
+ * This function is called in the prepare callback
+ */
+static int snd_intelhad_prog_DIP(struct snd_pcm_substream *substream)
+{
+	int retval, i;
+	union aud_ctrl_st ctrl_state = {.ctrl_val = 0};
+	union aud_info_frame2 frame2 = {.fr2_val = 0};
+	union aud_info_frame3 frame3 = {.fr3_val = 0};
+	u8 checksum = 0;
+
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_CNTL_ST, ctrl_state.ctrl_val);
+	if (retval)
+		return retval;
+
+	frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1;
+
+	/*TODO: Read from intelhaddata->eeld.speaker_allocation_block;*/
+	frame3.fr3_regx.chnl_alloc = CHANNEL_ALLOCATION;
+
+	/*Calculte the byte wide checksum for all valid DIP words*/
+	for (i = 0; i < BYTES_PER_WORD; i++)
+		checksum += (INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & SET_BYTE0;
+	for (i = 0; i < BYTES_PER_WORD; i++)
+		checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & SET_BYTE0;
+	for (i = 0; i < BYTES_PER_WORD; i++)
+		checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & SET_BYTE0;
+
+	frame2.fr2_regx.chksum = ~(checksum)+1;
+
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_HDMIW_INFOFR, INFO_FRAME_WORD1);
+	if (retval)
+		return retval;
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_HDMIW_INFOFR, frame2.fr2_val);
+	if (retval)
+		return retval;
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_HDMIW_INFOFR, frame3.fr3_val);
+	/* program remaining DIP words with zero */
+	for (i = 0; i < MAX_DIP_WORDS-VALID_DIP_WORDS; i++) {
+		retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_HDMIW_INFOFR, 0x0);
+		if (retval)
+			return retval;
+	}
+
+	ctrl_state.ctrl_regx.dip_freq = SET_BIT0;
+	ctrl_state.ctrl_regx.dip_en_sta = SET_BIT0;
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_CNTL_ST, ctrl_state.ctrl_val);
+	return retval;
+}
+
+/**
+ * snd_intelhad_prog_ring_buf - programs A , B, C and D
+ * address and length registers
+ *
+ * @substream:substream for which the prepare function is called
+ *
+ * This function programs ring buffer address and length into registers.
+ */
+static int snd_intelhad_prog_ring_buf(struct snd_pcm_substream *substream)
+{
+	int retval;
+	u32 ring_buf_addr, ring_buf_size, period_bytes;
+	u8 i, num_periods;
+	struct snd_intelhad *intelhaddata =  snd_pcm_substream_chip(substream);
+
+	ring_buf_addr = virt_to_phys(substream->runtime->dma_area);
+	pr_debug("had: Ring buffer address = %#x\n", ring_buf_addr);
+	ring_buf_size = snd_pcm_lib_buffer_bytes(substream);
+	pr_debug("had: Ring buffer size = %#x\n", ring_buf_size);
+	intelhaddata->stream_info.ring_buf_size = ring_buf_size;
+
+	period_bytes = frames_to_bytes(substream->runtime,
+				substream->runtime->period_size);
+	pr_debug("had: period size in bytes = %d\n", period_bytes);
+	num_periods = substream->runtime->periods;
+
+	/* Hardware supports MAX_PERIODS buffers */
+	if (num_periods > MAX_PERIODS)
+		return -EINVAL;
+
+	for (i = 0; i < num_periods; i++) {
+		/* Program the buf registers with addr and len */
+		intelhaddata->buf_info[i].buf_addr = ring_buf_addr  +
+							 (i * period_bytes);
+		intelhaddata->buf_info[i].buf_size = period_bytes;
+		retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_BUF_A_ADDR + (i * REG_WIDTH),
+					intelhaddata->buf_info[i].buf_addr |
+					REG_BIT_0 | REG_BIT_1);
+		if (retval)
+			return retval;
+		retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+					AUD_BUF_A_LENGTH + (i * REG_WIDTH),
+					period_bytes);
+		if (retval)
+			return retval;
+		intelhaddata->buf_info[i].is_valid = true;
+	}
+	intelhaddata->valid_buf_cnt = num_periods;
+	intelhaddata->curr_buf = HAD_BUF_TYPE_A;
+	return 0;
+}
+
+/**
+ * snd_intelhad_prog_cts - Program HDMI audio CTS value
+ *
+ * @aud_samp_freq: sampling frequency of audio data
+ * @tmds: sampling frequency of the display data
+ * @n_param: N value, depends on aud_samp_freq
+ *
+ * Program CTS register based on the audio and display sampling frequency
+ */
+static int snd_intelhad_prog_cts(u32 aud_samp_freq, u32 tmds, u32 n_param)
+{
+	u32 cts_val;
+	int retval = 0;
+
+	/* Calculate CTS according to HDMI 1.3a spec*/
+	cts_val = (tmds * n_param)/(128 * aud_samp_freq);
+	pr_debug("had:CTS Value=%d\n", cts_val);
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+				AUD_HDMI_CTS, (REG_BIT_20 | cts_val));
+	return retval;
+}
+
+/**
+ * snd_intelhad_prog_n - Program HDMI audio N value
+ *
+ * @aud_samp_freq: sampling frequency of audio data
+ * @n_param: N value, depends on aud_samp_freq
+ *
+ * This function is called in the prepare callback.
+ * It programs based on the audio and display sampling frequency
+ */
+static int snd_intelhad_prog_n(u32 aud_samp_freq, u32 *n_param)
+{
+	u32 n_val;
+	int retval = 0;
+
+	/* Select N according to HDMI 1.3a spec*/
+	switch (aud_samp_freq) {
+	case AUD_SAMPLE_RATE_32:
+		n_val = 4096;
+	break;
+	case AUD_SAMPLE_RATE_44_1:
+		n_val = 6272;
+	break;
+	case AUD_SAMPLE_RATE_48:
+		n_val = 6144;
+	break;
+	case AUD_SAMPLE_RATE_88_2:
+		n_val = 12544;
+	break;
+	case AUD_SAMPLE_RATE_96:
+		n_val = 12288;
+	break;
+	case AUD_SAMPLE_RATE_176_4:
+		n_val = 25088;
+	break;
+	case HAD_MAX_RATE:
+		n_val = 24576;
+	break;
+	default:
+		retval = -EINVAL;
+	break;
+	}
+	if (retval)
+		return retval;
+	retval = intelhaddata->reg_ops->hdmi_audio_write_register(
+				AUD_N_ENABLE, (REG_BIT_20 | n_val));
+	*n_param = n_val;
+	return retval;
+}
+
+/**
 * snd_intelhad_open - stream initializations are done here
 * @substream:substream for which the stream function is called
 *
-- 
1.6.2.5



More information about the Alsa-devel mailing list