On Fri, Jan 17, 2025 at 11:46:57AM -0500, Detlev Casanova wrote: > From: Sugar Zhang <sugar.zhang@xxxxxxxxxxxxxx> > > Register the dw-hdmi-qp bridge driver as an HDMI audio codec. > > The register values computation functions (for n) are based on the > downstream driver, as well as the register writing functions. > > The driver uses the generic HDMI Codec framework in order to implement > the HDMI audio support. > > Signed-off-by: Sugar Zhang <sugar.zhang@xxxxxxxxxxxxxx> > Signed-off-by: Detlev Casanova <detlev.casanova@xxxxxxxxxxxxx> > --- > drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c | 472 +++++++++++++++++++ > 1 file changed, 472 insertions(+) > > diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c > index b281cabfe992..55ceeb180bc6 100644 > --- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c > +++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-qp.c > @@ -36,6 +36,66 @@ > > #define SCRAMB_POLL_DELAY_MS 3000 > > +/* > + * Unless otherwise noted, entries in this table are 100% optimization. > + * Values can be obtained from hdmi_compute_n() but that function is > + * slow so we pre-compute values we expect to see. > + * > + * All 32k and 48k values are expected to be the same (due to the way > + * the math works) for any rate that's an exact kHz. > + */ > +static const struct dw_hdmi_audio_tmds_n { > + unsigned long tmds; > + unsigned int n_32k; > + unsigned int n_44k1; > + unsigned int n_48k; > +} common_tmds_n_table[] = { > + { .tmds = 25175000, .n_32k = 4096, .n_44k1 = 12854, .n_48k = 6144, }, > + { .tmds = 25200000, .n_32k = 4096, .n_44k1 = 5656, .n_48k = 6144, }, > + { .tmds = 27000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, > + { .tmds = 28320000, .n_32k = 4096, .n_44k1 = 5586, .n_48k = 6144, }, > + { .tmds = 30240000, .n_32k = 4096, .n_44k1 = 5642, .n_48k = 6144, }, > + { .tmds = 31500000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, > + { .tmds = 32000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, > + { .tmds = 33750000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, > + { .tmds = 36000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, > + { .tmds = 40000000, .n_32k = 4096, .n_44k1 = 5733, .n_48k = 6144, }, > + { .tmds = 49500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, > + { .tmds = 50000000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, > + { .tmds = 54000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, > + { .tmds = 65000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, > + { .tmds = 68250000, .n_32k = 4096, .n_44k1 = 5376, .n_48k = 6144, }, > + { .tmds = 71000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, > + { .tmds = 72000000, .n_32k = 4096, .n_44k1 = 5635, .n_48k = 6144, }, > + { .tmds = 73250000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, > + { .tmds = 74250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, > + { .tmds = 75000000, .n_32k = 4096, .n_44k1 = 5880, .n_48k = 6144, }, > + { .tmds = 78750000, .n_32k = 4096, .n_44k1 = 5600, .n_48k = 6144, }, > + { .tmds = 78800000, .n_32k = 4096, .n_44k1 = 5292, .n_48k = 6144, }, > + { .tmds = 79500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, > + { .tmds = 83500000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, > + { .tmds = 85500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, > + { .tmds = 88750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, > + { .tmds = 97750000, .n_32k = 4096, .n_44k1 = 14112, .n_48k = 6144, }, > + { .tmds = 101000000, .n_32k = 4096, .n_44k1 = 7056, .n_48k = 6144, }, > + { .tmds = 106500000, .n_32k = 4096, .n_44k1 = 4704, .n_48k = 6144, }, > + { .tmds = 108000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, > + { .tmds = 115500000, .n_32k = 4096, .n_44k1 = 5712, .n_48k = 6144, }, > + { .tmds = 119000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, > + { .tmds = 135000000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, > + { .tmds = 146250000, .n_32k = 4096, .n_44k1 = 6272, .n_48k = 6144, }, > + { .tmds = 148500000, .n_32k = 4096, .n_44k1 = 5488, .n_48k = 6144, }, > + { .tmds = 154000000, .n_32k = 4096, .n_44k1 = 5544, .n_48k = 6144, }, > + { .tmds = 162000000, .n_32k = 4096, .n_44k1 = 5684, .n_48k = 6144, }, > + > + /* For 297 MHz+ HDMI spec have some other rule for setting N */ > + { .tmds = 297000000, .n_32k = 3073, .n_44k1 = 4704, .n_48k = 5120, }, > + { .tmds = 594000000, .n_32k = 3073, .n_44k1 = 9408, .n_48k = 10240, }, > + > + /* End of table */ > + { .tmds = 0, .n_32k = 0, .n_44k1 = 0, .n_48k = 0, }, > +}; > + > struct dw_hdmi_qp_i2c { > struct i2c_adapter adap; > > @@ -59,6 +119,8 @@ struct dw_hdmi_qp { > void *data; > } phy; > > + struct mutex audio_mutex; There should be a comment, what is being protected by this mutex. > + > struct regmap *regm; > }; > [...] > +static int dw_hdmi_qp_init_audio_infoframe(struct dw_hdmi_qp *hdmi) > +{ > + struct hdmi_audio_infoframe frame; > + u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; > + int ret = 0; > + > + hdmi_audio_infoframe_init(&frame); > + > + frame.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; > + frame.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; > + frame.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; > + frame.channels = 2; > + > + ret = hdmi_audio_infoframe_pack(&frame, infoframe_buf, > + sizeof(infoframe_buf)); > + if (ret < 0) { > + dev_err(hdmi->dev, "%s: Failed to pack audio infoframe: %d\n", > + __func__, ret); > + return ret; > + } > + > + regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &infoframe_buf[3], 2); > + dw_hdmi_qp_mod(hdmi, > + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN, > + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN, > + PKTSCHED_PKT_EN); > + > + return 0; > +} > + > +static void dw_hdmi_qp_set_audio_infoframe(struct dw_hdmi_qp *hdmi, > + struct hdmi_codec_params *hparms) > +{ > + u8 infoframe_buf[HDMI_INFOFRAME_SIZE(AUDIO)]; > + int ret = 0; > + > + ret = hdmi_audio_infoframe_pack(&hparms->cea, infoframe_buf, > + sizeof(infoframe_buf)); Please use existing functions, drm_atomic_helper_connector_hdmi_update_audio_infoframe() and drm_atomic_helper_connector_hdmi_clear_audio_infoframe() to manage the infoframe. The drm_atomic_helper_connector_hdmi_update_infoframes() function will take care of updating the audio infoframe. > + if (!ret) { > + dev_err(hdmi->dev, "%s: Failed to pack audio infoframe: %d\n", > + __func__, ret); > + return; > + } > + > + /* > + * AUDI_CONTENTS0: { RSV, HB2, HB1, RSV } > + * AUDI_CONTENTS1: { PB3, PB2, PB1, PB0 } > + * AUDI_CONTENTS2: { PB7, PB6, PB5, PB4 } > + * > + * PB0: CheckSum > + * PB1: | CT3 | CT2 | CT1 | CT0 | F13 | CC2 | CC1 | CC0 | > + * PB2: | F27 | F26 | F25 | SF2 | SF1 | SF0 | SS1 | SS0 | > + * PB3: | F37 | F36 | F35 | F34 | F33 | F32 | F31 | F30 | > + * PB4: | CA7 | CA6 | CA5 | CA4 | CA3 | CA2 | CA1 | CA0 | > + * PB5: | DM_INH | LSV3 | LSV2 | LSV1 | LSV0 | F52 | F51 | F50 | > + * PB6~PB10: Reserved > + * > + * AUDI_CONTENTS0 default value defined by HDMI specification, > + * and shall only be changed for debug purposes. > + * So, we only configure payload byte from PB0~PB7(2 word total). > + */ > + regmap_bulk_write(hdmi->regm, PKT_AUDI_CONTENTS1, &infoframe_buf[3], 2); This should go to your dw_hdmi_qp_bridge_write_infoframe(). > + > + /* Enable ACR, AUDI, AMD */ > + dw_hdmi_qp_mod(hdmi, > + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, > + PKTSCHED_ACR_TX_EN | PKTSCHED_AUDI_TX_EN | PKTSCHED_AMD_TX_EN, > + PKTSCHED_PKT_EN); > + > + /* Enable AUDS */ > + dw_hdmi_qp_mod(hdmi, PKTSCHED_AUDS_TX_EN, PKTSCHED_AUDS_TX_EN, PKTSCHED_PKT_EN); > + mutex_unlock(&hdmi->audio_mutex); > +} > + -- With best wishes Dmitry