diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c index 3b7e5c59a5e9..c981ff9dc358 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-i2s-audio.c @@ -32,6 +32,18 @@ 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, int reg) +{ + struct dw_hdmi *hdmi = audio->hdmi; + u8 val; + + val = audio->read(hdmi, reg); + val &= ~mask; + val |= data & mask; + audio->write(hdmi, val, 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 +53,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) || @@ -50,7 +63,6 @@ static int dw_hdmi_i2s_hw_params(struct device *dev, void *data, } inputclkfs = HDMI_AUD_INPUTCLKFS_64FS; - conf0 = HDMI_AUD_CONF0_I2S_ALL_ENABLE; switch (hparms->sample_width) { case 16: @@ -60,14 +72,115 @@ 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 1: + case 2: + conf0 = HDMI_AUD_CONF0_I2S_2CHANNEL_ENABLE; + break; + case 3: + case 4: + conf0 = HDMI_AUD_CONF0_I2S_4CHANNEL_ENABLE; + break; + case 5: + case 6: + conf0 = HDMI_AUD_CONF0_I2S_6CHANNEL_ENABLE; + break; + case 7: + case 8: + conf0 = HDMI_AUD_CONF0_I2S_8CHANNEL_ENABLE; + break; + default: + dev_err(dev, "unsupported channels [%d]\n", hparms->channels); + return -EINVAL; } + /*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); + 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; @@ -118,7 +231,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)); diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h index 9d90eb9c46e5..23f476e0632a 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.h +++ b/drivers/gpu/drm/bridge/synopsys/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 @@ -709,6 +718,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, @@ -716,6 +727,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, @@ -868,8 +888,16 @@ enum { /* AUD_CONF0 field values */ HDMI_AUD_CONF0_SW_RESET = 0x80, + 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, @@ -1042,6 +1070,9 @@ enum { HDMI_I2CM_CTLINT_NAC_MASK = 0x40, HDMI_I2CM_CTLINT_ARB_POL = 0x8, HDMI_I2CM_CTLINT_ARB_MASK = 0x4, + +/* HDMI_MC_SWRSTZ filed values */ + HDMI_MC_SWRSTZ_I2S_RESET_MSK = BIT(3), }; /* diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c index 7826fb3523c1..4a14f6b35630 100644 --- a/sound/soc/sunxi/sun4i-i2s.c +++ b/sound/soc/sunxi/sun4i-i2s.c @@ -27,7 +27,7 @@ #define SUN4I_I2S_CTRL_REG 0x00 #define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8) -#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 + (sdo)) +#define SUN4I_I2S_CTRL_SDO_EN(line) (((1 << line) - 1) << 8) #define SUN4I_I2S_CTRL_MODE_MASK BIT(5) #define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5) #define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5) @@ -353,10 +353,17 @@ static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream, struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); int sr, wss, channels; u32 width; + int lines; channels = params_channels(params); - if (channels != 2) - return -EINVAL; + + lines = (channels + 1) / 2; + printk("I2S: Number of lines enabled: %d\n", lines); + /* Enable the output lines */ + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, + SUN4I_I2S_CTRL_SDO_EN_MASK, + SUN4I_I2S_CTRL_SDO_EN(lines)); + printk("I2S: SDO enable value: %x\n", SUN4I_I2S_CTRL_SDO_EN(lines)); if (i2s->variant->has_chcfg) { regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, @@ -635,12 +642,6 @@ static int sun4i_i2s_startup(struct snd_pcm_substream *substream, regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, SUN4I_I2S_CTRL_GL_EN, SUN4I_I2S_CTRL_GL_EN); - /* Enable the first output line */ - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, - SUN4I_I2S_CTRL_SDO_EN_MASK, - SUN4I_I2S_CTRL_SDO_EN(0)); - - return clk_prepare_enable(i2s->mod_clk); } @@ -707,7 +708,7 @@ static struct snd_soc_dai_driver sun4i_i2s_dai = { .playback = { .stream_name = "Playback", .channels_min = 2, - .channels_max = 2, + .channels_max = 8, .rates = SNDRV_PCM_RATE_8000_192000, .formats = SNDRV_PCM_FMTBIT_S16_LE, },