[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