mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-06 10:58:48 +09:00
drm: bridge/dw-hdmi: add support for hdmi bitstream audio
Change-Id: Ib1f6c5dba6451f3fbb029b5472dbfbf5694cff68 Signed-off-by: Sugar Zhang <sugar.zhang@rock-chips.com>
This commit is contained in:
@@ -17,6 +17,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
|
||||
|
||||
@@ -32,6 +32,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)
|
||||
@@ -41,6 +49,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) ||
|
||||
@@ -49,8 +58,7 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
inputclkfs = HDMI_AUD_INPUTCLKFS_64FS;
|
||||
conf0 = HDMI_AUD_CONF0_I2S_ALL_ENABLE;
|
||||
inputclkfs = HDMI_AUD_INPUTCLKFS_128FS;
|
||||
|
||||
switch (hparms->sample_width) {
|
||||
case 16:
|
||||
@@ -60,6 +68,62 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
|
||||
case 32:
|
||||
conf1 = HDMI_AUD_CONF1_WIDTH_24;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unsupported sample width [%d]\n", hparms->sample_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (hparms->channels) {
|
||||
case 2:
|
||||
conf0 = HDMI_AUD_CONF0_I2S_2CHANNEL_ENABLE;
|
||||
break;
|
||||
case 4:
|
||||
conf0 = HDMI_AUD_CONF0_I2S_4CHANNEL_ENABLE;
|
||||
break;
|
||||
case 6:
|
||||
conf0 = HDMI_AUD_CONF0_I2S_6CHANNEL_ENABLE;
|
||||
break;
|
||||
case 8:
|
||||
conf0 = HDMI_AUD_CONF0_I2S_8CHANNEL_ENABLE;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev, "unsupported channels [%d]\n", hparms->channels);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
*/
|
||||
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);
|
||||
@@ -68,8 +132,73 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data,
|
||||
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,
|
||||
(((u8)~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);
|
||||
|
||||
dw_hdmi_audio_enable(hdmi);
|
||||
|
||||
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);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -118,7 +247,7 @@ static int snd_dw_hdmi_probe(struct platform_device *pdev)
|
||||
|
||||
pdata.ops = &dw_hdmi_i2s_ops;
|
||||
pdata.i2s = 1;
|
||||
pdata.max_i2s_channels = 6;
|
||||
pdata.max_i2s_channels = 8;
|
||||
pdata.data = audio;
|
||||
|
||||
memset(&pdevinfo, 0, sizeof(pdevinfo));
|
||||
|
||||
@@ -3816,6 +3816,7 @@ __dw_hdmi_probe(struct platform_device *pdev,
|
||||
audio.hdmi = hdmi;
|
||||
audio.write = hdmi_writeb;
|
||||
audio.read = hdmi_readb;
|
||||
audio.mod = hdmi_modb;
|
||||
hdmi->enable_audio = dw_hdmi_i2s_audio_enable;
|
||||
hdmi->disable_audio = dw_hdmi_i2s_audio_disable;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -788,6 +797,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,
|
||||
@@ -795,6 +806,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,
|
||||
@@ -956,15 +976,30 @@ enum {
|
||||
|
||||
/* AUD_CONF0 field values */
|
||||
HDMI_AUD_CONF0_SW_RESET = 0x80,
|
||||
HDMI_AUD_CONF0_I2S_SELECT_MASK = 0x20,
|
||||
HDMI_AUD_CONF0_I2S_2CHANNEL_ENABLE = 0x21,
|
||||
HDMI_AUD_CONF0_I2S_4CHANNEL_ENABLE = 0x23,
|
||||
HDMI_AUD_CONF0_I2S_6CHANNEL_ENABLE = 0x27,
|
||||
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 */
|
||||
HDMI_AUD_CONF2_HBR = 0x1,
|
||||
HDMI_AUD_CONF2_NLPCM = 0x2,
|
||||
HDMI_AUD_CONF2_INSERT_PCUV = 0x04,
|
||||
|
||||
/* AUD_CTS3 field values */
|
||||
HDMI_AUD_CTS3_N_SHIFT_OFFSET = 5,
|
||||
HDMI_AUD_CTS3_N_SHIFT_MASK = 0xe0,
|
||||
@@ -1135,6 +1170,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),
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user