ALSA: x86: intel_hdmi: add definitions and logic for DP audio
authorPierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Tue, 31 Jan 2017 20:16:52 +0000 (14:16 -0600)
committerTakashi Iwai <tiwai@suse.de>
Wed, 1 Feb 2017 15:22:24 +0000 (16:22 +0100)
Imported from legacy patches

Note: the new code doesn't assume a modified ELD but
an explicit notification that DP is present. It appears
that the i915 code does change the ELD so we could use
the ELD-based tests to check for DP audio

Signed-off-by: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/x86/intel_hdmi_audio.c
sound/x86/intel_hdmi_audio.h
sound/x86/intel_hdmi_lpe_audio.c
sound/x86/intel_hdmi_lpe_audio.h

index f30155446117d9832b5eaaaef9b0117a5d9a74df..5ce139c1b21dbdb5b92d38cbc7857dbebc4e92c9 100644 (file)
@@ -396,6 +396,7 @@ static int snd_intelhad_prog_audio_ctrl_v2(struct snd_pcm_substream *substream,
        else
                cfg_val.cfg_regx_v2.layout = LAYOUT1;
 
+       cfg_val.cfg_regx_v2.val_bit = 1;
        had_write_register(AUD_CONFIG, cfg_val.cfg_regval);
        return 0;
 }
@@ -447,6 +448,7 @@ static int snd_intelhad_prog_audio_ctrl_v1(struct snd_pcm_substream *substream,
 
        }
 
+       cfg_val.cfg_regx.val_bit = 1;
        had_write_register(AUD_CONFIG, cfg_val.cfg_regval);
        return 0;
 }
@@ -548,6 +550,7 @@ void had_build_channel_allocation_map(struct snd_intelhad *intelhaddata)
        }
 
        had_get_caps(HAD_GET_ELD, &intelhaddata->eeld);
+       had_get_caps(HAD_GET_DP_OUTPUT, &intelhaddata->dp_output);
 
        pr_debug("eeld.speaker_allocation_block = %x\n",
                        intelhaddata->eeld.speaker_allocation_block);
@@ -685,7 +688,7 @@ static void snd_intelhad_prog_dip_v1(struct snd_pcm_substream *substream,
 
        /*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) & MASK_BYTE0;
+               checksum += (HDMI_INFO_FRAME_WORD1 >> i*BITS_PER_BYTE) & MASK_BYTE0;
        for (i = 0; i < BYTES_PER_WORD; i++)
                checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0;
        for (i = 0; i < BYTES_PER_WORD; i++)
@@ -693,7 +696,7 @@ static void snd_intelhad_prog_dip_v1(struct snd_pcm_substream *substream,
 
        frame2.fr2_regx.chksum = -(checksum);
 
-       had_write_register(AUD_HDMIW_INFOFR, INFO_FRAME_WORD1);
+       had_write_register(AUD_HDMIW_INFOFR, HDMI_INFO_FRAME_WORD1);
        had_write_register(AUD_HDMIW_INFOFR, frame2.fr2_val);
        had_write_register(AUD_HDMIW_INFOFR, frame3.fr3_val);
 
@@ -722,28 +725,35 @@ static void snd_intelhad_prog_dip_v2(struct snd_pcm_substream *substream,
        union aud_info_frame2 frame2 = {.fr2_val = 0};
        union aud_info_frame3 frame3 = {.fr3_val = 0};
        u8 checksum = 0;
+       u32 info_frame;
        int channels;
 
        channels = substream->runtime->channels;
 
        had_write_register(AUD_CNTL_ST, ctrl_state.ctrl_val);
 
-       frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1;
+       if (intelhaddata->dp_output) {
+               info_frame = DP_INFO_FRAME_WORD1;
+               frame2.fr2_val = 1;
+       } else {
+               info_frame = HDMI_INFO_FRAME_WORD1;
+               frame2.fr2_regx.chnl_cnt = substream->runtime->channels - 1;
 
-       frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation(
-                                       intelhaddata, channels);
+               frame3.fr3_regx.chnl_alloc = snd_intelhad_channel_allocation(
+                       intelhaddata, channels);
 
-       /*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) & MASK_BYTE0;
-       for (i = 0; i < BYTES_PER_WORD; i++)
-               checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0;
-       for (i = 0; i < BYTES_PER_WORD; i++)
-               checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0;
+               /*Calculte the byte wide checksum for all valid DIP words*/
+               for (i = 0; i < BYTES_PER_WORD; i++)
+                       checksum += (info_frame >> i*BITS_PER_BYTE) & MASK_BYTE0;
+               for (i = 0; i < BYTES_PER_WORD; i++)
+                       checksum += (frame2.fr2_val >> i*BITS_PER_BYTE) & MASK_BYTE0;
+               for (i = 0; i < BYTES_PER_WORD; i++)
+                       checksum += (frame3.fr3_val >> i*BITS_PER_BYTE) & MASK_BYTE0;
 
-       frame2.fr2_regx.chksum = -(checksum);
+               frame2.fr2_regx.chksum = -(checksum);
+       }
 
-       had_write_register(AUD_HDMIW_INFOFR_v2, INFO_FRAME_WORD1);
+       had_write_register(AUD_HDMIW_INFOFR_v2, info_frame);
        had_write_register(AUD_HDMIW_INFOFR_v2, frame2.fr2_val);
        had_write_register(AUD_HDMIW_INFOFR_v2, frame3.fr3_val);
 
@@ -839,6 +849,85 @@ int snd_intelhad_read_len(struct snd_intelhad *intelhaddata)
        return retval;
 }
 
+static int had_calculate_maud_value(u32 aud_samp_freq, u32 link_rate)
+{
+       u32 maud_val;
+
+       /* Select maud according to DP 1.2 spec*/
+       if (link_rate == DP_2_7_GHZ) {
+               switch (aud_samp_freq) {
+               case AUD_SAMPLE_RATE_32:
+                       maud_val = AUD_SAMPLE_RATE_32_DP_2_7_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_44_1:
+                       maud_val = AUD_SAMPLE_RATE_44_1_DP_2_7_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_48:
+                       maud_val = AUD_SAMPLE_RATE_48_DP_2_7_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_88_2:
+                       maud_val = AUD_SAMPLE_RATE_88_2_DP_2_7_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_96:
+                       maud_val = AUD_SAMPLE_RATE_96_DP_2_7_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_176_4:
+                       maud_val = AUD_SAMPLE_RATE_176_4_DP_2_7_MAUD_VAL;
+                       break;
+
+               case HAD_MAX_RATE:
+                       maud_val = HAD_MAX_RATE_DP_2_7_MAUD_VAL;
+                       break;
+
+               default:
+                       maud_val = -EINVAL;
+                       break;
+               }
+       } else if (link_rate == DP_1_62_GHZ) {
+               switch (aud_samp_freq) {
+               case AUD_SAMPLE_RATE_32:
+                       maud_val = AUD_SAMPLE_RATE_32_DP_1_62_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_44_1:
+                       maud_val = AUD_SAMPLE_RATE_44_1_DP_1_62_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_48:
+                       maud_val = AUD_SAMPLE_RATE_48_DP_1_62_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_88_2:
+                       maud_val = AUD_SAMPLE_RATE_88_2_DP_1_62_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_96:
+                       maud_val = AUD_SAMPLE_RATE_96_DP_1_62_MAUD_VAL;
+                       break;
+
+               case AUD_SAMPLE_RATE_176_4:
+                       maud_val = AUD_SAMPLE_RATE_176_4_DP_1_62_MAUD_VAL;
+                       break;
+
+               case HAD_MAX_RATE:
+                       maud_val = HAD_MAX_RATE_DP_1_62_MAUD_VAL;
+                       break;
+
+               default:
+                       maud_val = -EINVAL;
+                       break;
+               }
+       } else
+               maud_val = -EINVAL;
+
+       return maud_val;
+}
+
 /**
  * snd_intelhad_prog_cts_v1 - Program HDMI audio CTS value
  *
@@ -849,8 +938,9 @@ int snd_intelhad_read_len(struct snd_intelhad *intelhaddata)
  *
  * Program CTS register based on the audio and display sampling frequency
  */
-static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds, u32 n_param,
-                               struct snd_intelhad *intelhaddata)
+static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds,
+                                    u32 link_rate, u32 n_param,
+                                    struct snd_intelhad *intelhaddata)
 {
        u32 cts_val;
        u64 dividend, divisor;
@@ -874,18 +964,24 @@ static void snd_intelhad_prog_cts_v1(u32 aud_samp_freq, u32 tmds, u32 n_param,
  *
  * Program CTS register based on the audio and display sampling frequency
  */
-static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds, u32 n_param,
-                               struct snd_intelhad *intelhaddata)
+static void snd_intelhad_prog_cts_v2(u32 aud_samp_freq, u32 tmds,
+                                    u32 link_rate, u32 n_param,
+                                    struct snd_intelhad *intelhaddata)
 {
        u32 cts_val;
        u64 dividend, divisor;
 
-       /* Calculate CTS according to HDMI 1.3a spec*/
-       dividend = (u64)tmds * n_param*1000;
-       divisor = 128 * aud_samp_freq;
-       cts_val = div64_u64(dividend, divisor);
+       if (intelhaddata->dp_output) {
+               /* Substitute cts_val with Maud according to DP 1.2 spec*/
+               cts_val = had_calculate_maud_value(aud_samp_freq, link_rate);
+       } else {
+               /* Calculate CTS according to HDMI 1.3a spec*/
+               dividend = (u64)tmds * n_param*1000;
+               divisor = 128 * aud_samp_freq;
+               cts_val = div64_u64(dividend, divisor);
+       }
        pr_debug("TMDS value=%d, N value=%d, CTS Value=%d\n",
-                       tmds, n_param, cts_val);
+                tmds, n_param, cts_val);
        had_write_register(AUD_HDMI_CTS, (BIT(24) | cts_val));
 }
 
@@ -970,7 +1066,18 @@ static int snd_intelhad_prog_n_v2(u32 aud_samp_freq, u32 *n_param,
 {
        s32 n_val;
 
-       n_val = had_calculate_n_value(aud_samp_freq);
+       if (intelhaddata->dp_output) {
+               /*
+                * According to DP specs, Maud and Naud values hold
+                * a relationship, which is stated as:
+                * Maud/Naud = 512 * fs / f_LS_Clk
+                * where, fs is the sampling frequency of the audio stream
+                * and Naud is 32768 for Async clock.
+                */
+
+               n_val = DP_NAUD_VAL;
+       } else
+               n_val = had_calculate_n_value(aud_samp_freq);
 
        if (n_val < 0)
                return n_val;
@@ -1343,6 +1450,7 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
 {
        int retval;
        u32 disp_samp_freq, n_param;
+       u32 link_rate = 0;
        struct snd_intelhad *intelhaddata;
        struct snd_pcm_runtime *runtime;
        struct had_pvt_data *had_stream;
@@ -1387,6 +1495,7 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
        }
 
        had_get_caps(HAD_GET_ELD, &intelhaddata->eeld);
+       had_get_caps(HAD_GET_DP_OUTPUT, &intelhaddata->dp_output);
 
        retval = intelhaddata->ops->prog_n(substream->runtime->rate, &n_param,
                                                                intelhaddata);
@@ -1394,8 +1503,14 @@ static int snd_intelhad_pcm_prepare(struct snd_pcm_substream *substream)
                pr_err("programming N value failed %#x\n", retval);
                goto prep_end;
        }
+
+       if (intelhaddata->dp_output)
+               had_get_caps(HAD_GET_LINK_RATE, &link_rate);
+
+
        intelhaddata->ops->prog_cts(substream->runtime->rate,
-                                       disp_samp_freq, n_param, intelhaddata);
+                                   disp_samp_freq, link_rate,
+                                   n_param, intelhaddata);
 
        intelhaddata->ops->prog_dip(substream, intelhaddata);
 
@@ -1503,6 +1618,7 @@ int hdmi_audio_mode_change(struct snd_pcm_substream *substream)
 {
        int retval = 0;
        u32 disp_samp_freq, n_param;
+       u32 link_rate = 0;
        struct snd_intelhad *intelhaddata;
 
        intelhaddata = snd_pcm_substream_chip(substream);
@@ -1523,8 +1639,13 @@ int hdmi_audio_mode_change(struct snd_pcm_substream *substream)
                pr_err("programming N value failed %#x\n", retval);
                goto out;
        }
+
+       if (intelhaddata->dp_output)
+               had_get_caps(HAD_GET_LINK_RATE, &link_rate);
+
        intelhaddata->ops->prog_cts(substream->runtime->rate,
-                                       disp_samp_freq, n_param, intelhaddata);
+                                   disp_samp_freq, link_rate,
+                                   n_param, intelhaddata);
 
        /* Enable Audio */
        intelhaddata->ops->enable_audio(substream, 1);
index d2015ec848439172c1a344aa469484a8e1d68d67..034b3873ffa1eb60e71a38accbb15e2f78a83496 100644 (file)
@@ -44,7 +44,8 @@
 #define MAX_CAP_STREAMS                0
 #define HDMI_AUDIO_DRIVER      "hdmi-audio"
 
-#define INFO_FRAME_WORD1       0x000a0184
+#define HDMI_INFO_FRAME_WORD1  0x000a0184
+#define DP_INFO_FRAME_WORD1    0x00441b84
 #define FIFO_THRESHOLD         0xFE
 #define DMA_FIFO_THRESHOLD     0x7
 #define BYTES_PER_WORD         0x4
@@ -134,6 +135,7 @@ struct snd_intelhad {
        struct          ring_buf_info buf_info[HAD_NUM_OF_RING_BUFS];
        struct          pcm_stream_info stream_info;
        union otm_hdmi_eld_t    eeld;
+       bool dp_output;
        enum            intel_had_aud_buf_type curr_buf;
        int             valid_buf_cnt;
        unsigned int    aes_bits;
@@ -156,8 +158,8 @@ struct had_ops {
        void (*reset_audio)(u8 reset);
        int (*prog_n)(u32 aud_samp_freq, u32 *n_param,
                        struct snd_intelhad *intelhaddata);
-       void (*prog_cts)(u32 aud_samp_freq, u32 tmds, u32 n_param,
-                       struct snd_intelhad *intelhaddata);
+       void (*prog_cts)(u32 aud_samp_freq, u32 tmds, u32 link_rate,
+                        u32 n_param, struct snd_intelhad *intelhaddata);
        int (*audio_ctrl)(struct snd_pcm_substream *substream,
                                struct snd_intelhad *intelhaddata);
        void (*prog_dip)(struct snd_pcm_substream *substream,
index ead2d3af168cd09f0f788c5d7df3fb577c9b8a75..cea05dfc081ac0d468266ba8235dddb7162f1662 100644 (file)
@@ -48,6 +48,8 @@ struct hdmi_lpe_audio_ctx {
        struct snd_intel_had_interface *had_interface;
        void *had_pvt_data;
        int tmds_clock_speed;
+       bool dp_output;
+       int link_rate;
        unsigned int had_config_offset;
        int hdmi_audio_interrupt_mask;
        struct work_struct hdmi_audio_wq;
@@ -187,6 +189,15 @@ static int hdmi_audio_write(u32 reg, u32 val)
 
        dev_dbg(&hlpe_pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__, reg, val);
 
+       if (ctx->dp_output) {
+               if ((reg == AUDIO_HDMI_CONFIG_A) ||
+                   (reg == AUDIO_HDMI_CONFIG_B) ||
+                   (reg == AUDIO_HDMI_CONFIG_C)) {
+                       if (val & AUD_CONFIG_VALID_BIT)
+                               val = val | AUD_CONFIG_DP_MODE |
+                                       AUD_CONFIG_BLOCK_BIT;
+               }
+       }
        iowrite32(val, (ctx->mmio_start+reg));
 
        return 0;
@@ -220,6 +231,16 @@ static int hdmi_audio_rmw(u32 reg, u32 val, u32 mask)
        val_tmp = (val & mask) |
                        ((ioread32(ctx->mmio_start + reg)) & ~mask);
 
+       if (ctx->dp_output) {
+               if ((reg == AUDIO_HDMI_CONFIG_A) ||
+                   (reg == AUDIO_HDMI_CONFIG_B) ||
+                   (reg == AUDIO_HDMI_CONFIG_C)) {
+                       if (val_tmp & AUD_CONFIG_VALID_BIT)
+                               val_tmp = val_tmp | AUD_CONFIG_DP_MODE |
+                                       AUD_CONFIG_BLOCK_BIT;
+               }
+       }
+
        iowrite32(val_tmp, (ctx->mmio_start+reg));
        dev_dbg(&hlpe_pdev->dev, "%s: reg[0x%x] = 0x%x\n", __func__,
                                reg, val_tmp);
@@ -249,7 +270,18 @@ static int hdmi_audio_get_caps(enum had_caps_list get_element,
                /* ToDo: Verify if sampling freq logic is correct */
                *(u32 *)capabilities = ctx->tmds_clock_speed;
                dev_dbg(&hlpe_pdev->dev, "%s: tmds_clock_speed = 0x%x\n",
-                               __func__, ctx->tmds_clock_speed);
+                       __func__, ctx->tmds_clock_speed);
+               break;
+       case HAD_GET_LINK_RATE:
+               /* ToDo: Verify if sampling freq logic is correct */
+               *(u32 *)capabilities = ctx->link_rate;
+               dev_dbg(&hlpe_pdev->dev, "%s: link rate = 0x%x\n",
+                       __func__, ctx->link_rate);
+               break;
+       case HAD_GET_DP_OUTPUT:
+               *(u32 *)capabilities = ctx->dp_output;
+               dev_dbg(&hlpe_pdev->dev, "%s: dp_output = %d\n",
+                       __func__, ctx->dp_output);
                break;
        default:
                break;
@@ -442,6 +474,8 @@ static void notify_audio_lpe(void *audio_ptr)
 
                if (pdata->tmds_clock_speed) {
                        ctx->tmds_clock_speed = pdata->tmds_clock_speed;
+                       ctx->dp_output = pdata->dp_output;
+                       ctx->link_rate = pdata->link_rate;
                        mid_hdmi_audio_signal_event(HAD_EVENT_MODE_CHANGING);
                }
        } else
index ec4bde50dba79933f99a84edfdf37be0db708f3c..3aed89af5b45b3ef46965c7a37d2b522c3ba6dd0 100644 (file)
 #include <sound/control.h>
 #include <sound/pcm.h>
 
+#define AUD_CONFIG_VALID_BIT                   (1<<9)
+#define AUD_CONFIG_DP_MODE                     (1<<15)
+#define AUD_CONFIG_BLOCK_BIT                   (1<<7)
+
 #define HMDI_LPE_AUDIO_DRIVER_NAME             "intel-hdmi-lpe-audio"
 #define HAD_MAX_DEVICES                1
 #define HAD_MIN_CHANNEL                2
 #define HAD_MAX_DIP_WORDS              16
 #define INTEL_HAD              "IntelHdmiLpeAudio"
 
+/* DP Link Rates */
+#define DP_2_7_GHZ                     270000
+#define DP_1_62_GHZ                    162000
+
+/* Maud Values */
+#define AUD_SAMPLE_RATE_32_DP_2_7_MAUD_VAL             1988
+#define AUD_SAMPLE_RATE_44_1_DP_2_7_MAUD_VAL           2740
+#define AUD_SAMPLE_RATE_48_DP_2_7_MAUD_VAL             2982
+#define AUD_SAMPLE_RATE_88_2_DP_2_7_MAUD_VAL           5480
+#define AUD_SAMPLE_RATE_96_DP_2_7_MAUD_VAL             5965
+#define AUD_SAMPLE_RATE_176_4_DP_2_7_MAUD_VAL          10961
+#define HAD_MAX_RATE_DP_2_7_MAUD_VAL                   11930
+#define AUD_SAMPLE_RATE_32_DP_1_62_MAUD_VAL            3314
+#define AUD_SAMPLE_RATE_44_1_DP_1_62_MAUD_VAL          4567
+#define AUD_SAMPLE_RATE_48_DP_1_62_MAUD_VAL            4971
+#define AUD_SAMPLE_RATE_88_2_DP_1_62_MAUD_VAL          9134
+#define AUD_SAMPLE_RATE_96_DP_1_62_MAUD_VAL            9942
+#define AUD_SAMPLE_RATE_176_4_DP_1_62_MAUD_VAL         18268
+#define HAD_MAX_RATE_DP_1_62_MAUD_VAL                  19884
+
+/* Naud Value */
+#define DP_NAUD_VAL                                    32768
+
 /* _AUD_CONFIG register MASK */
 #define AUD_CONFIG_MASK_UNDERRUN       0xC0000000
 #define AUD_CONFIG_MASK_SRDBG          0x00000002
@@ -618,6 +645,8 @@ enum hdmi_connector_status {
 enum had_caps_list {
        HAD_GET_ELD = 1,
        HAD_GET_DISPLAY_RATE,
+       HAD_GET_DP_OUTPUT,
+       HAD_GET_LINK_RATE,
        HAD_SET_ENABLE_AUDIO,
        HAD_SET_DISABLE_AUDIO,
        HAD_SET_ENABLE_AUDIO_INT,