From: Sugar Zhang Date: Thu, 13 Apr 2017 07:37:21 +0000 (+0800) Subject: drm: bridge/dw-hdmi: add support for hdmi bitstream audio X-Git-Tag: firefly_0821_release~33 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=firefly-linux-kernel-4.4.55.git;a=commitdiff_plain;h=924f480383c982da9908fb96d6bbb580b25545a5;ds=sidebyside drm: bridge/dw-hdmi: add support for hdmi bitstream audio Change-Id: Ib1f6c5dba6451f3fbb029b5472dbfbf5694cff68 Signed-off-by: Sugar Zhang --- diff --git a/drivers/gpu/drm/bridge/dw-hdmi-audio.h b/drivers/gpu/drm/bridge/dw-hdmi-audio.h index fd1f745c6073..b9e839f4151a 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi-audio.h +++ b/drivers/gpu/drm/bridge/dw-hdmi-audio.h @@ -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 diff --git a/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c index 84c2a316aac3..d4ad3f0014e7 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/dw-hdmi-i2s-audio.c @@ -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; diff --git a/drivers/gpu/drm/bridge/dw-hdmi.c b/drivers/gpu/drm/bridge/dw-hdmi.c index aacccec78d48..a25117765af4 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.c +++ b/drivers/gpu/drm/bridge/dw-hdmi.c @@ -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; diff --git a/drivers/gpu/drm/bridge/dw-hdmi.h b/drivers/gpu/drm/bridge/dw-hdmi.h index 70db49815fb6..2517cb38c0ba 100644 --- a/drivers/gpu/drm/bridge/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/dw-hdmi.h @@ -162,6 +162,15 @@ #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__ */