drm: bridge/dw-hdmi: add support for hdmi bitstream audio
authorSugar Zhang <sugar.zhang@rock-chips.com>
Thu, 13 Apr 2017 07:37:21 +0000 (15:37 +0800)
committerHuang, Tao <huangtao@rock-chips.com>
Thu, 13 Apr 2017 11:20:03 +0000 (19:20 +0800)
Change-Id: Ib1f6c5dba6451f3fbb029b5472dbfbf5694cff68
Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
drivers/gpu/drm/bridge/dw-hdmi-audio.h
drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c
drivers/gpu/drm/bridge/dw-hdmi.c
drivers/gpu/drm/bridge/dw-hdmi.h

index fd1f745..b9e839f 100644 (file)
@@ -16,6 +16,7 @@ struct dw_hdmi_i2s_audio_data {
 
        void (*write)(struct dw_hdmi *hdmi, u8 val, int offset);
        u8 (*read)(struct dw_hdmi *hdmi, int offset);
+       void (*mod)(struct dw_hdmi *hdmi, u8 data, u8 mask, unsigned int reg);
 };
 
 #endif
index 84c2a31..d4ad3f0 100644 (file)
@@ -30,6 +30,14 @@ static inline u8 hdmi_read(struct dw_hdmi_i2s_audio_data *audio, int offset)
        return audio->read(hdmi, offset);
 }
 
+static inline void hdmi_update_bits(struct dw_hdmi_i2s_audio_data *audio,
+                                   u8 data, u8 mask, unsigned int reg)
+{
+       struct dw_hdmi *hdmi = audio->hdmi;
+
+       audio->mod(hdmi, data, mask, reg);
+}
+
 static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
                                 struct hdmi_codec_daifmt *fmt,
                                 struct hdmi_codec_params *hparms)
@@ -39,6 +47,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
        u8 conf0 = 0;
        u8 conf1 = 0;
        u8 inputclkfs = 0;
+       u8 val;
 
        /* it cares I2S only */
        if ((fmt->fmt != HDMI_I2S) ||
@@ -47,7 +56,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
                return -EINVAL;
        }
 
-       inputclkfs      = HDMI_AUD_INPUTCLKFS_64FS;
+       inputclkfs = HDMI_AUD_INPUTCLKFS_128FS;
 
        switch (hparms->sample_width) {
        case 16:
@@ -80,19 +89,112 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
                return -EINVAL;
        }
 
-       dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
-
-       hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
-       hdmi_write(audio, conf0, HDMI_AUD_CONF0);
-       hdmi_write(audio, conf1, HDMI_AUD_CONF1);
        /*
         * dw-hdmi introduced insert_pcuv bit in version 2.10a.
         * When set (1'b1), this bit enables the insertion of the PCUV
         * (Parity, Channel Status, User bit and Validity) bits on the
         * incoming audio stream (support limited to Linear PCM audio)
         */
-       if (hdmi_read(audio, HDMI_DESIGN_ID) > 0x21)
-               hdmi_write(audio, HDMI_AUD_CONF2_INSERT_PCUV, HDMI_AUD_CONF2);
+       val = 0;
+       if (hdmi_read(audio, HDMI_DESIGN_ID) >= 0x21)
+               val = HDMI_AUD_CONF2_INSERT_PCUV;
+
+       /*Mask fifo empty and full int and reset fifo*/
+       hdmi_update_bits(audio,
+                        HDMI_AUD_INT_FIFO_EMPTY_MSK |
+                        HDMI_AUD_INT_FIFO_FULL_MSK,
+                        HDMI_AUD_INT_FIFO_EMPTY_MSK |
+                        HDMI_AUD_INT_FIFO_FULL_MSK, HDMI_AUD_INT);
+       hdmi_update_bits(audio, HDMI_AUD_CONF0_SW_RESET,
+                        HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
+       hdmi_update_bits(audio, HDMI_MC_SWRSTZ_I2S_RESET_MSK,
+                        HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ);
+
+       switch (hparms->mode) {
+       case NLPCM:
+               hdmi_write(audio, HDMI_AUD_CONF2_NLPCM, HDMI_AUD_CONF2);
+               conf1 = HDMI_AUD_CONF1_WIDTH_21;
+               break;
+       case HBR:
+               hdmi_write(audio, HDMI_AUD_CONF2_HBR, HDMI_AUD_CONF2);
+               conf1 = HDMI_AUD_CONF1_WIDTH_21;
+               break;
+       default:
+               hdmi_write(audio, val, HDMI_AUD_CONF2);
+               break;
+       }
+
+       dw_hdmi_set_sample_rate(hdmi, hparms->sample_rate);
+
+       hdmi_write(audio, inputclkfs, HDMI_AUD_INPUTCLKFS);
+       hdmi_write(audio, conf0, HDMI_AUD_CONF0);
+       hdmi_write(audio, conf1, HDMI_AUD_CONF1);
+
+       val = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT0;
+       if (hparms->channels > 2)
+               val = HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_LAYOUT1;
+       hdmi_update_bits(audio, val, HDMI_FC_AUDSCONF_AUD_PACKET_LAYOUT_MASK,
+                        HDMI_FC_AUDSCONF);
+
+       switch (hparms->sample_rate) {
+       case 32000:
+               val = HDMI_FC_AUDSCHNLS_32K;
+               break;
+       case 44100:
+               val = HDMI_FC_AUDSCHNLS_441K;
+               break;
+       case 48000:
+               val = HDMI_FC_AUDSCHNLS_48K;
+               break;
+       case 88200:
+               val = HDMI_FC_AUDSCHNLS_882K;
+               break;
+       case 96000:
+               val = HDMI_FC_AUDSCHNLS_96K;
+               break;
+       case 176400:
+               val = HDMI_FC_AUDSCHNLS_1764K;
+               break;
+       case 192000:
+               val = HDMI_FC_AUDSCHNLS_192K;
+               break;
+       default:
+               val = HDMI_FC_AUDSCHNLS_441K;
+               break;
+       }
+
+       /* set channel status register */
+       hdmi_update_bits(audio, val,
+                        HDMI_FC_AUDSCHNLS7_SAMPFREQ_MASK,
+                        HDMI_FC_AUDSCHNLS7);
+       hdmi_write(audio,
+                  ((~val) << HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_OFFSET),
+                  HDMI_FC_AUDSCHNLS8);
+
+       /* Refer to CEA861-E Audio infoFrame
+        * Set both Audio Channel Count and Audio Coding
+        * Type Refer to Stream Head for HDMI
+        */
+       hdmi_update_bits(audio,
+                        (hparms->channels - 1) << HDMI_FC_AUDICONF0_CC_OFFSET,
+                        HDMI_FC_AUDICONF0_CC_MASK, HDMI_FC_AUDICONF0);
+
+       /* Set both Audio Sample Size and Sample Frequency
+        * Refer to Stream Head for HDMI
+        */
+       hdmi_write(audio, 0x00, HDMI_FC_AUDICONF1);
+
+       /* Set Channel Allocation */
+       hdmi_write(audio, 0x00, HDMI_FC_AUDICONF2);
+
+       /* Set LFEPBLDOWN-MIX INH and LSV */
+       hdmi_write(audio, 0x00, HDMI_FC_AUDICONF3);
+
+       hdmi_update_bits(audio, HDMI_AUD_CONF0_SW_RESET,
+                        HDMI_AUD_CONF0_SW_RESET, HDMI_AUD_CONF0);
+       hdmi_update_bits(audio, HDMI_MC_SWRSTZ_I2S_RESET_MSK,
+                        HDMI_MC_SWRSTZ_I2S_RESET_MSK, HDMI_MC_SWRSTZ);
+
        dw_hdmi_audio_enable(hdmi);
 
        return 0;
index aacccec..a251177 100644 (file)
@@ -2614,6 +2614,7 @@ int dw_hdmi_bind(struct device *dev, struct device *master,
                audio.hdmi      = hdmi;
                audio.write     = hdmi_writeb;
                audio.read      = hdmi_readb;
+               audio.mod       = hdmi_modb;
 
                pdevinfo.name = "dw-hdmi-i2s-audio";
                pdevinfo.data = &audio;
index 70db498..2517cb3 100644 (file)
 #define HDMI_FC_SPDDEVICEINF                    0x1062
 #define HDMI_FC_AUDSCONF                        0x1063
 #define HDMI_FC_AUDSSTAT                        0x1064
+#define HDMI_FC_AUDSCHNLS0                      0x1067
+#define HDMI_FC_AUDSCHNLS1                      0x1068
+#define HDMI_FC_AUDSCHNLS2                      0x1069
+#define HDMI_FC_AUDSCHNLS3                      0x106a
+#define HDMI_FC_AUDSCHNLS4                      0x106b
+#define HDMI_FC_AUDSCHNLS5                      0x106c
+#define HDMI_FC_AUDSCHNLS6                      0x106d
+#define HDMI_FC_AUDSCHNLS7                      0x106e
+#define HDMI_FC_AUDSCHNLS8                      0x106f
 #define HDMI_FC_DATACH0FILL                     0x1070
 #define HDMI_FC_DATACH1FILL                     0x1071
 #define HDMI_FC_DATACH2FILL                     0x1072
@@ -744,6 +753,8 @@ enum {
 /* HDMI_FC_AUDSCHNLS7 field values */
        HDMI_FC_AUDSCHNLS7_ACCURACY_OFFSET = 4,
        HDMI_FC_AUDSCHNLS7_ACCURACY_MASK = 0x30,
+       HDMI_FC_AUDSCHNLS7_SAMPFREQ_OFFSET = 0,
+       HDMI_FC_AUDSCHNLS7_SAMPFREQ_MASK = 0x0f,
 
 /* HDMI_FC_AUDSCHNLS8 field values */
        HDMI_FC_AUDSCHNLS8_ORIGSAMPFREQ_MASK = 0xf0,
@@ -751,6 +762,15 @@ enum {
        HDMI_FC_AUDSCHNLS8_WORDLEGNTH_MASK = 0x0f,
        HDMI_FC_AUDSCHNLS8_WORDLEGNTH_OFFSET = 0,
 
+/* HDMI_FC_AUDSCHNLS Sample Rate */
+       HDMI_FC_AUDSCHNLS_32K = 0x3,
+       HDMI_FC_AUDSCHNLS_441K = 0x0,
+       HDMI_FC_AUDSCHNLS_48K = 0x2,
+       HDMI_FC_AUDSCHNLS_882K = 0x8,
+       HDMI_FC_AUDSCHNLS_96K = 0xa,
+       HDMI_FC_AUDSCHNLS_1764K = 0xc,
+       HDMI_FC_AUDSCHNLS_192K = 0xe,
+
 /* FC_AUDSCONF field values */
        HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_MASK = 0xF0,
        HDMI_FC_AUDSCONF_AUD_PACKET_SAMPFIT_OFFSET = 4,
@@ -909,11 +929,16 @@ enum {
        HDMI_AUD_CONF0_I2S_8CHANNEL_ENABLE = 0x2F,
        HDMI_AUD_CONF0_I2S_ALL_ENABLE = 0x2F,
 
+/* AUD_INT field values */
+       HDMI_AUD_INT_FIFO_EMPTY_MSK = BIT(3),
+       HDMI_AUD_INT_FIFO_FULL_MSK = BIT(2),
+
 /* AUD_CONF1 field values */
        HDMI_AUD_CONF1_MODE_I2S = 0x00,
        HDMI_AUD_CONF1_MODE_RIGHT_J = 0x02,
        HDMI_AUD_CONF1_MODE_LEFT_J = 0x04,
        HDMI_AUD_CONF1_WIDTH_16 = 0x10,
+       HDMI_AUD_CONF1_WIDTH_21 = 0x15,
        HDMI_AUD_CONF1_WIDTH_24 = 0x18,
 
 /* AUD_CONF2 filed values */
@@ -1092,6 +1117,9 @@ enum {
        HDMI_I2CM_DIV_FAST_STD_MODE = 0x8,
        HDMI_I2CM_DIV_FAST_MODE = 0x8,
        HDMI_I2CM_DIV_STD_MODE = 0,
+
+/* HDMI_MC_SWRSTZ filed values */
+       HDMI_MC_SWRSTZ_I2S_RESET_MSK = BIT(3),
 };
 
 #endif /* __DW_HDMI_H__ */