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 <sugar.zhang@rock-chips.com>
Change-Id: Iba058ad08c71de0b118913216baec1886d8f3819
This commit is contained in:
Sugar Zhang
2022-12-05 18:46:22 +08:00
committed by Tao Huang
parent d6e8547727
commit 02435edfc3
3 changed files with 88 additions and 89 deletions
@@ -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,
+81 -7
View File
@@ -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)
+3
View File
@@ -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);