From 02435edfc3cbead9678712bc590a89b98da81fed Mon Sep 17 00:00:00 2001 From: Sugar Zhang Date: Mon, 5 Dec 2022 18:46:22 +0800 Subject: [PATCH] drm/bridge: synopsys: dw-hdmi-qp: Fix register access fail This patch wrap register access by lock which guarantee the clk enabled first. and remove the unused CLK status from CMN. Signed-off-by: Sugar Zhang Change-Id: Iba058ad08c71de0b118913216baec1886d8f3819 --- .../bridge/synopsys/dw-hdmi-qp-i2s-audio.c | 86 +----------------- drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 88 +++++++++++++++++-- include/drm/bridge/dw_hdmi.h | 3 + 3 files changed, 88 insertions(+), 89 deletions(-) diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c index 82d2d5a1cb60..5ece777c1afa 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp-i2s-audio.c @@ -42,85 +42,23 @@ static inline void hdmi_mod(struct dw_hdmi_qp_i2s_audio_data *audio, return audio->mod(hdmi, data, mask, reg); } -static inline bool is_dw_hdmi_qp_clk_off(struct dw_hdmi_qp_i2s_audio_data *audio) -{ - u32 sta = hdmi_read(audio, CMU_STATUS); - - return (sta & (AUDCLK_OFF | LINKQPCLK_OFF | VIDQPCLK_OFF)); -} - static int dw_hdmi_qp_i2s_hw_params(struct device *dev, void *data, struct hdmi_codec_daifmt *fmt, struct hdmi_codec_params *hparms) { struct dw_hdmi_qp_i2s_audio_data *audio = data; struct dw_hdmi_qp *hdmi = audio->hdmi; - u32 conf0 = 0; bool ref2stream = false; - if (is_dw_hdmi_qp_clk_off(audio)) - return 0; - if (fmt->bit_clk_master | fmt->frame_clk_master) { dev_err(dev, "unsupported clock settings\n"); return -EINVAL; } - /* Reset the audio data path of the AVP */ - hdmi_write(audio, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST); - - /* Disable AUDS, ACR, AUDI */ - hdmi_mod(audio, 0, - PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | PKTSCHED_AUDI_TX_EN, - PKTSCHED_PKT_EN); - - /* Clear the audio FIFO */ - hdmi_write(audio, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0); - - /* Select I2S interface as the audio source */ - hdmi_mod(audio, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0); - - /* Enable the active i2s lanes */ - switch (hparms->channels) { - case 7 ... 8: - conf0 |= I2S_LINES_EN(3); - fallthrough; - case 5 ... 6: - conf0 |= I2S_LINES_EN(2); - fallthrough; - case 3 ... 4: - conf0 |= I2S_LINES_EN(1); - fallthrough; - default: - conf0 |= I2S_LINES_EN(0); - break; - } - - hdmi_mod(audio, conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0); - - /* - * Enable bpcuv generated internally for L-PCM, or received - * from stream for NLPCM/HBR. - */ - switch (fmt->bit_fmt) { - case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: - conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP; - conf0 |= I2S_BPCUV_RCV_EN; + if (fmt->bit_fmt == SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE) ref2stream = true; - break; - default: - conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS; - ref2stream = false; - break; - } - - hdmi_mod(audio, conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK, - AUDIO_INTERFACE_CONFIG0); - - /* Enable audio FIFO auto clear when overflow */ - hdmi_mod(audio, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK, - AUDIO_INTERFACE_CONFIG0); + dw_hdmi_qp_set_audio_interface(hdmi, fmt, hparms); dw_hdmi_qp_set_sample_rate(hdmi, hparms->sample_rate); dw_hdmi_qp_set_channel_status(hdmi, hparms->iec.status, ref2stream); dw_hdmi_qp_set_channel_count(hdmi, hparms->channels); @@ -135,9 +73,6 @@ static int dw_hdmi_qp_i2s_audio_startup(struct device *dev, void *data) struct dw_hdmi_qp_i2s_audio_data *audio = data; struct dw_hdmi_qp *hdmi = audio->hdmi; - if (is_dw_hdmi_qp_clk_off(audio)) - return 0; - dw_hdmi_qp_audio_enable(hdmi); return 0; @@ -146,22 +81,9 @@ static int dw_hdmi_qp_i2s_audio_startup(struct device *dev, void *data) static void dw_hdmi_qp_i2s_audio_shutdown(struct device *dev, void *data) { struct dw_hdmi_qp_i2s_audio_data *audio = data; + struct dw_hdmi_qp *hdmi = audio->hdmi; - if (is_dw_hdmi_qp_clk_off(audio)) - return; - - /* - * Keep ACR, AUDI, AUDS packet always on to make SINK device - * active for better compatibility and user experience. - * - * This also fix POP sound on some SINK devices which wakeup - * from suspend to active. - */ - hdmi_mod(audio, I2S_BPCUV_RCV_DIS, I2S_BPCUV_RCV_MSK, - AUDIO_INTERFACE_CONFIG0); - hdmi_mod(audio, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, - AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, - AUDPKT_CONTROL0); + dw_hdmi_qp_audio_disable(hdmi); } static int dw_hdmi_qp_i2s_get_eld(struct device *dev, void *data, uint8_t *buf, diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c index 38d9065ef073..262b9e1d45da 100644 --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c @@ -523,6 +523,75 @@ static unsigned int hdmi_find_n(struct dw_hdmi_qp *hdmi, unsigned long pixel_clk return hdmi_compute_n(hdmi, pixel_clk, sample_rate); } +void dw_hdmi_qp_set_audio_interface(struct dw_hdmi_qp *hdmi, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms) +{ + u32 conf0 = 0; + + mutex_lock(&hdmi->audio_mutex); + if (!hdmi->dclk_en) { + mutex_unlock(&hdmi->audio_mutex); + return; + } + + /* Reset the audio data path of the AVP */ + hdmi_writel(hdmi, AVP_DATAPATH_PACKET_AUDIO_SWINIT_P, GLOBAL_SWRESET_REQUEST); + + /* Disable AUDS, ACR, AUDI */ + hdmi_modb(hdmi, 0, + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | PKTSCHED_AUDI_TX_EN, + PKTSCHED_PKT_EN); + + /* Clear the audio FIFO */ + hdmi_writel(hdmi, AUDIO_FIFO_CLR_P, AUDIO_INTERFACE_CONTROL0); + + /* Select I2S interface as the audio source */ + hdmi_modb(hdmi, AUD_IF_I2S, AUD_IF_SEL_MSK, AUDIO_INTERFACE_CONFIG0); + + /* Enable the active i2s lanes */ + switch (hparms->channels) { + case 7 ... 8: + conf0 |= I2S_LINES_EN(3); + fallthrough; + case 5 ... 6: + conf0 |= I2S_LINES_EN(2); + fallthrough; + case 3 ... 4: + conf0 |= I2S_LINES_EN(1); + fallthrough; + default: + conf0 |= I2S_LINES_EN(0); + break; + } + + hdmi_modb(hdmi, conf0, I2S_LINES_EN_MSK, AUDIO_INTERFACE_CONFIG0); + + /* + * Enable bpcuv generated internally for L-PCM, or received + * from stream for NLPCM/HBR. + */ + switch (fmt->bit_fmt) { + case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE: + conf0 = (hparms->channels == 8) ? AUD_HBR : AUD_ASP; + conf0 |= I2S_BPCUV_RCV_EN; + break; + default: + conf0 = AUD_ASP | I2S_BPCUV_RCV_DIS; + break; + } + + hdmi_modb(hdmi, conf0, I2S_BPCUV_RCV_MSK | AUD_FORMAT_MSK, + AUDIO_INTERFACE_CONFIG0); + + /* Enable audio FIFO auto clear when overflow */ + hdmi_modb(hdmi, AUD_FIFO_INIT_ON_OVF_EN, AUD_FIFO_INIT_ON_OVF_MSK, + AUDIO_INTERFACE_CONFIG0); + + mutex_unlock(&hdmi->audio_mutex); +} +EXPORT_SYMBOL_GPL(dw_hdmi_qp_set_audio_interface); + /* * When transmitting IEC60958 linear PCM audio, these registers allow to * configure the channel status information of all the channel status @@ -735,13 +804,18 @@ static void dw_hdmi_i2s_audio_enable(struct dw_hdmi_qp *hdmi) static void dw_hdmi_i2s_audio_disable(struct dw_hdmi_qp *hdmi) { - /* Disable AUDS, ACR, AUDI, AMD */ - hdmi_modb(hdmi, 0, - PKTSCHED_ACR_TX_EN | PKTSCHED_AUDS_TX_EN | - PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, - PKTSCHED_PKT_EN); - - hdmi_enable_audio_clk(hdmi, false); + /* + * Keep ACR, AUDI, AUDS packet always on to make SINK device + * active for better compatibility and user experience. + * + * This also fix POP sound on some SINK devices which wakeup + * from suspend to active. + */ + hdmi_modb(hdmi, I2S_BPCUV_RCV_DIS, I2S_BPCUV_RCV_MSK, + AUDIO_INTERFACE_CONFIG0); + hdmi_modb(hdmi, AUDPKT_PBIT_FORCE_EN | AUDPKT_CHSTATUS_OVR_EN, + AUDPKT_PBIT_FORCE_EN_MASK | AUDPKT_CHSTATUS_OVR_EN_MASK, + AUDPKT_CONTROL0); } void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi) diff --git a/include/drm/bridge/dw_hdmi.h b/include/drm/bridge/dw_hdmi.h index a58b7dc9844a..d4eaa0bf2e79 100644 --- a/include/drm/bridge/dw_hdmi.h +++ b/include/drm/bridge/dw_hdmi.h @@ -317,6 +317,9 @@ void dw_hdmi_qp_set_channel_count(struct dw_hdmi_qp *hdmi, unsigned int cnt); void dw_hdmi_qp_set_channel_status(struct dw_hdmi_qp *hdmi, u8 *channel_status, bool ref2stream); void dw_hdmi_qp_set_channel_allocation(struct dw_hdmi_qp *hdmi, unsigned int ca); +void dw_hdmi_qp_set_audio_interface(struct dw_hdmi_qp *hdmi, + struct hdmi_codec_daifmt *fmt, + struct hdmi_codec_params *hparms); void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, struct hdmi_codec_params *hparms); void dw_hdmi_qp_audio_enable(struct dw_hdmi_qp *hdmi);