Message ID | 20220727045035.32225-1-rex-bc.chen@mediatek.com |
---|---|
Headers | show |
Series | drm/mediatek: Add MT8195 DisplayPort driver | expand |
Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > From: Guillaume Ranquet <granquet@baylibre.com> > > This patch adds two helper functions that extract the frequency and word > length from a struct cea_sad. > > For these helper functions new defines are added that help translate the > 'freq' and 'byte2' fields into real numbers. > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > From: Markus Schneider-Pargmann <msp@baylibre.com> > > Similar to HDMI, DP uses audio infoframes as well which are structured > very similar to the HDMI ones. > > This patch adds a helper function to pack the HDMI audio infoframe for > DP, called hdmi_audio_infoframe_pack_for_dp(). > hdmi_audio_infoframe_pack_only() is split into two parts. One of them > packs the payload only and can be used for HDMI and DP. > > Also constify the frame parameter in hdmi_audio_infoframe_check() as > it is passed to hdmi_audio_infoframe_check_only() which expects a const. > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > From: Markus Schneider-Pargmann <msp@baylibre.com> > > This patch adds a embedded displayport driver for the MediaTek mt8195 SoC. > > It supports the MT8195, the embedded DisplayPort units. It offers > DisplayPort 1.4 with up to 4 lanes. > > The driver creates a child device for the phy. The child device will > never exist without the parent being active. As they are sharing a > register range, the parent passes a regmap pointer to the child so that > both can work with the same register range. The phy driver sets device > data that is read by the parent to get the phy device that can be used > to control the phy properties. > > This driver is based on an initial version by > Jitao shi <jitao.shi@mediatek.com> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> Tested-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > From: Guillaume Ranquet <granquet@baylibre.com> > > This patch adds External DisplayPort support to the mt8195 eDP driver. > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > For some DP dungles, we need to train more than onece to confirm that we > don't misjudge the status of sink device. Please fix the typos in your commit title and description. title: misjudgment -> misjudgement desc: dungles->dongles; onece->once > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > --- > drivers/gpu/drm/mediatek/mtk_dp.c | 21 ++++++++++++++++++--- > 1 file changed, 18 insertions(+), 3 deletions(-) > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c b/drivers/gpu/drm/mediatek/mtk_dp.c > index ce817cb59445..80d7d6488105 100644 > --- a/drivers/gpu/drm/mediatek/mtk_dp.c > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c > @@ -42,6 +42,7 @@ > #define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3 > #define MTK_DP_TBC_BUF_READ_START_ADDR 0x08 > #define MTK_DP_TRAIN_DOWNSCALE_RETRY 8 > +#define MTK_DP_TRAIN_CLEAR_RETRY 50 > > struct mtk_dp_train_info { > bool tps3; > @@ -1431,11 +1432,25 @@ static int mtk_dp_video_config(struct mtk_dp *mtk_dp) > > static int mtk_dp_training(struct mtk_dp *mtk_dp) > { > + short max_retry = MTK_DP_TRAIN_CLEAR_RETRY; > int ret; > > - ret = mtk_dp_train_start(mtk_dp); > - if (ret) > - return ret; > + /* > + * We do retry to confirm that we don't misjudge the sink status. > + * If it is still failed, we can confirm there are some issues for the > + * sink device. > + */ > + do { > + ret = mtk_dp_train_start(mtk_dp); > + if (!ret) > + break; > + } while (--max_retry); > + > + dev_info(mtk_dp->dev, "dp training clear retry times: %d\n", > + MTK_DP_TRAIN_CLEAR_RETRY - max_retry); dev_dbg() here. ...after which, Reviewed-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com> > + > + if (!max_retry) > + return -ETIMEDOUT; > > ret = mtk_dp_video_config(mtk_dp); > if (ret)
On Wed, 2022-07-27 at 11:40 +0200, AngeloGioacchino Del Regno wrote: > Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > > For some DP dungles, we need to train more than onece to confirm > > that we > > don't misjudge the status of sink device. > > Please fix the typos in your commit title and description. > title: misjudgment -> misjudgement > desc: dungles->dongles; onece->once > > > > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > > --- > > drivers/gpu/drm/mediatek/mtk_dp.c | 21 ++++++++++++++++++--- > > 1 file changed, 18 insertions(+), 3 deletions(-) > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c > > b/drivers/gpu/drm/mediatek/mtk_dp.c > > index ce817cb59445..80d7d6488105 100644 > > --- a/drivers/gpu/drm/mediatek/mtk_dp.c > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c > > @@ -42,6 +42,7 @@ > > #define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3 > > #define MTK_DP_TBC_BUF_READ_START_ADDR 0x08 > > #define MTK_DP_TRAIN_DOWNSCALE_RETRY 8 > > +#define MTK_DP_TRAIN_CLEAR_RETRY 50 > > > > struct mtk_dp_train_info { > > bool tps3; > > @@ -1431,11 +1432,25 @@ static int mtk_dp_video_config(struct > > mtk_dp *mtk_dp) > > > > static int mtk_dp_training(struct mtk_dp *mtk_dp) > > { > > + short max_retry = MTK_DP_TRAIN_CLEAR_RETRY; > > int ret; > > > > - ret = mtk_dp_train_start(mtk_dp); > > - if (ret) > > - return ret; > > + /* > > + * We do retry to confirm that we don't misjudge the sink > > status. > > + * If it is still failed, we can confirm there are some issues > > for the > > + * sink device. > > + */ > > + do { > > + ret = mtk_dp_train_start(mtk_dp); > > + if (!ret) > > + break; > > + } while (--max_retry); > > + > > + dev_info(mtk_dp->dev, "dp training clear retry times: %d\n", > > + MTK_DP_TRAIN_CLEAR_RETRY - max_retry); > > dev_dbg() here. > > ...after which, > > Reviewed-by: AngeloGioacchino Del Regno < > angelogioacchino.delregno@collabora.com> > Hello Angelo, Thanks for your review. I will modify these in next version. BRs, Bo-Chen > > + > > + if (!max_retry) > > + return -ETIMEDOUT; > > > > ret = mtk_dp_video_config(mtk_dp); > > if (ret) > >
Hi, Bo-Chen: On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote: > From: Guillaume Ranquet <granquet@baylibre.com> > > This patch adds External DisplayPort support to the mt8195 eDP > driver. > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > --- > drivers/gpu/drm/mediatek/mtk_dp.c | 181 +++++++++++++++++++++--- > -- > drivers/gpu/drm/mediatek/mtk_dp_reg.h | 1 + > 2 files changed, 146 insertions(+), 36 deletions(-) > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c > b/drivers/gpu/drm/mediatek/mtk_dp.c > index 06eeecedd49e..ce817cb59445 100644 > --- a/drivers/gpu/drm/mediatek/mtk_dp.c > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c > @@ -103,6 +103,11 @@ static struct regmap_config mtk_dp_regmap_config > = { > .name = "mtk-dp-registers", > }; > > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) > +{ > + return mtk_dp->next_bridge; For external DP, it could also connect to a bridge IC which transfer DP to HDMI or some interface else. Use the compatible to judge this edp or dp. > +} > + > static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) > { > return container_of(b, struct mtk_dp, bridge); > @@ -341,6 +346,19 @@ static void mtk_dp_pg_enable(struct mtk_dp > *mtk_dp, bool enable) > 4 << PGEN_PATTERN_SEL_SHIFT, > PGEN_PATTERN_SEL_MASK); > } > > +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp) > +{ > + return mtk_dp->train_info.cable_plugged_in; > +} > + > +static bool mtk_dp_plug_state_avoid_pulse(struct mtk_dp *mtk_dp) > +{ > + bool ret; > + > + return !(readx_poll_timeout(mtk_dp_plug_state, mtk_dp, ret, > ret, > + 4000, 7 * 4000)); > +} > + > static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp) > { > mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, > @@ -778,35 +796,67 @@ static void mtk_dp_get_calibration_data(struct > mtk_dp *mtk_dp) > return; > } > > - cal_data->glb_bias_trim = > - check_cal_data_valid(mtk_dp, 1, 0x1e, > - (buf[3] >> 27) & 0x1f, 0xf); > - cal_data->clktx_impse = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[0] >> 9) & 0xf, 0x8); > - cal_data->ln_tx_impsel_pmos[0] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 28) & 0xf, 0x8); > - cal_data->ln_tx_impsel_nmos[0] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 24) & 0xf, 0x8); > - cal_data->ln_tx_impsel_pmos[1] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 20) & 0xf, 0x8); > - cal_data->ln_tx_impsel_nmos[1] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 16) & 0xf, 0x8); > - cal_data->ln_tx_impsel_pmos[2] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 12) & 0xf, 0x8); > - cal_data->ln_tx_impsel_nmos[2] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 8) & 0xf, 0x8); > - cal_data->ln_tx_impsel_pmos[3] = > - check_cal_data_valid(mtk_dp, 1, 0xe, > - (buf[2] >> 4) & 0xf, 0x8); > - cal_data->ln_tx_impsel_nmos[3] = > - check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & 0xf, > 0x8); > + if (mtk_dp_is_edp(mtk_dp)) { > + cal_data->glb_bias_trim = > + check_cal_data_valid(mtk_dp, 1, 0x1e, > + (buf[3] >> 27) & 0x1f, > 0xf); > + cal_data->clktx_impse = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[0] >> 9) & 0xf, 0x8); > + cal_data->ln_tx_impsel_pmos[0] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 28) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_nmos[0] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 24) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_pmos[1] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 20) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_nmos[1] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 16) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_pmos[2] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 12) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_nmos[2] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 8) & 0xf, 0x8); > + cal_data->ln_tx_impsel_pmos[3] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[2] >> 4) & 0xf, 0x8); > + cal_data->ln_tx_impsel_nmos[3] = > + check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & > 0xf, 0x8); > + } else { > + cal_data->glb_bias_trim = > + check_cal_data_valid(mtk_dp, 1, 0x1e, > + (buf[0] >> 27) & 0x1f, > 0xf); Why not place glb_bias_trim value in nvmem buf[3] for both edp and dp case? Place in the same nvmem position, the driver code would be simple. > + cal_data->clktx_impse = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[0] >> 13) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_pmos[0] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 28) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_nmos[0] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 24) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_pmos[1] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 20) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_nmos[1] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 16) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_pmos[2] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 12) & 0xf, > 0x8); > + cal_data->ln_tx_impsel_nmos[2] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 8) & 0xf, 0x8); > + cal_data->ln_tx_impsel_pmos[3] = > + check_cal_data_valid(mtk_dp, 1, 0xe, > + (buf[1] >> 4) & 0xf, 0x8); > + cal_data->ln_tx_impsel_nmos[3] = > + check_cal_data_valid(mtk_dp, 1, 0xe, buf[1] & > 0xf, 0x8); Why not place ln_tx_impsel_nmos and ln_tx_impsel_pmos value in nvmem buf[2] for both edp and dp case? Place in the same nvmem position, the driver code would be simple. > + } > > kfree(buf); > } > @@ -926,7 +976,10 @@ static void mtk_dp_video_mute(struct mtk_dp > *mtk_dp, bool enable) > VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | > VIDEO_MUTE_SW_DP_ENC0_P0_MASK); > > - mtk_dp_sip_atf_call(mtk_dp, MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, > enable); > + mtk_dp_sip_atf_call(mtk_dp, > + mtk_dp_is_edp(mtk_dp) ? > + MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE : > + MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); Use of_device_get_match_data to select MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE or MTK_DP_SIP_ATF_VIDEO_UNMUTE. > } > > static void mtk_dp_power_enable(struct mtk_dp *mtk_dp) > @@ -1226,6 +1279,9 @@ static int mtk_dp_parse_capabilities(struct > mtk_dp *mtk_dp) > drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, > DP_SET_POWER_D0); > usleep_range(2000, 5000); > > + if (!mtk_dp_plug_state(mtk_dp)) > + return -ENODEV; > + > drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap); > > train_info->link_rate = min_t(int, mtk_dp->max_linkrate, > @@ -1277,6 +1333,9 @@ static int mtk_dp_train_start(struct mtk_dp > *mtk_dp) > u8 train_limit; > u8 max_link_rate; > > + if (!mtk_dp_plug_state_avoid_pulse(mtk_dp)) > + return -ENODEV; > + > link_rate = mtk_dp->rx_cap[1]; > lane_count = mtk_dp->rx_cap[2] & 0x1F; > > @@ -1412,6 +1471,9 @@ static irqreturn_t mtk_dp_hpd_event_thread(int > hpd, void *dev) > { > struct mtk_dp *mtk_dp = dev; > > + dev_dbg(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); > + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); Because edp also have hpd interrupt, so I would like both edp and dp have the same control flow for hdp. So move this to edp patch. > + > if (mtk_dp->train_info.hpd_inerrupt) { > dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); > mtk_dp->train_info.hpd_inerrupt = false; > @@ -1452,9 +1514,20 @@ static irqreturn_t mtk_dp_hpd_event(int hpd, > void *dev) > else > train_info->cable_plugged_in = false; > > - mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, > - DP_PWR_STATE_BANDGAP_TPLL_LANE, > - DP_PWR_STATE_MASK); > + if (!train_info->cable_plugged_in) { > + mtk_dp_video_mute(mtk_dp, true); > + > + mtk_dp_initialize_priv_data(mtk_dp); > + mtk_dp_set_idle_pattern(mtk_dp, true); > + > + mtk_dp_update_bits(mtk_dp, > MTK_DP_TOP_PWR_STATE, > + DP_PWR_STATE_BANDGAP_TPLL, > + DP_PWR_STATE_MASK); > + } else { > + mtk_dp_update_bits(mtk_dp, > MTK_DP_TOP_PWR_STATE, > + DP_PWR_STATE_BANDGAP_TPLL_LA > NE, > + DP_PWR_STATE_MASK); > + } > } > > return IRQ_HANDLED; > @@ -1499,6 +1572,24 @@ static int mtk_dp_dt_parse(struct mtk_dp > *mtk_dp, > return 0; > } > > +static enum drm_connector_status mtk_dp_bdg_detect(struct drm_bridge > *bridge) > +{ > + struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); > + enum drm_connector_status ret = connector_status_disconnected; > + u8 sink_count = 0; > + > + if (mtk_dp_is_edp(mtk_dp)) > + return connector_status_connected; Because edp also have hpd interrupt, so I would like both edp and dp have the same control flow for hdp. So remove this. > + > + if (mtk_dp_plug_state_avoid_pulse(mtk_dp)) { > + drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, > &sink_count); > + if (DP_GET_SINK_COUNT(sink_count)) > + ret = connector_status_connected; > + } > + > + return ret; > +} > + > static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge, > struct drm_connector *connector) > { > @@ -1512,7 +1603,8 @@ static struct edid *mtk_dp_get_edid(struct > drm_bridge *bridge, > drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, > DP_SET_POWER_D0); > usleep_range(2000, 5000); > > - new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc); > + if (mtk_dp_plug_state(mtk_dp)) > + new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc); Please explain why only dp has this problem. If edp also has this problem, move this to edp patch. Regards, CK > > if (!enabled) > drm_bridge_chain_post_disable(bridge); > @@ -1852,6 +1944,7 @@ static const struct drm_bridge_funcs > mtk_dp_bridge_funcs = { > .atomic_disable = mtk_dp_bridge_atomic_disable, > .mode_valid = mtk_dp_bridge_mode_valid, > .get_edid = mtk_dp_get_edid, > + .detect = mtk_dp_bdg_detect, > }; > > static int mtk_dp_probe(struct platform_device *pdev) > @@ -1873,9 +1966,15 @@ static int mtk_dp_probe(struct platform_device > *pdev) > "failed to request dp irq > resource\n"); > > mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev->of_node, > 1, 0); > - if (IS_ERR(mtk_dp->next_bridge)) > + if (IS_ERR(mtk_dp->next_bridge) && > + PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { > + dev_info(dev, > + "No panel connected in devicetree, continue as > external DP\n"); > + mtk_dp->next_bridge = NULL; > + } else if (IS_ERR(mtk_dp->next_bridge)) { > return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), > "Failed to get bridge\n"); > + } > > ret = mtk_dp_dt_parse(mtk_dp, pdev); > if (ret) > @@ -1918,7 +2017,10 @@ static int mtk_dp_probe(struct platform_device > *pdev) > > mtk_dp->bridge.ops = > DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | > DRM_BRIDGE_OP_HPD; > - mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; > + if (mtk_dp_is_edp(mtk_dp)) > + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; > + else > + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; > > drm_bridge_add(&mtk_dp->bridge); > > @@ -1945,6 +2047,12 @@ static int mtk_dp_suspend(struct device *dev) > { > struct mtk_dp *mtk_dp = dev_get_drvdata(dev); > > + if (mtk_dp_plug_state(mtk_dp)) { > + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, > DP_SET_POWER_D3); > + /* Ensure the sink is off before shutting down power */ > + usleep_range(2000, 3000); > + } > + > mtk_dp_power_disable(mtk_dp); > > mtk_dp_hwirq_enable(mtk_dp, false); > @@ -1978,6 +2086,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, > mtk_dp_suspend, mtk_dp_resume); > > static const struct of_device_id mtk_dp_of_match[] = { > { .compatible = "mediatek,mt8195-edp-tx" }, > + { .compatible = "mediatek,mt8195-dp-tx" }, > {}, > }; > MODULE_DEVICE_TABLE(of, mtk_dp_of_match); > diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h > b/drivers/gpu/drm/mediatek/mtk_dp_reg.h > index 4ff8e9880dc9..59086eb82868 100644 > --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h > +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h > @@ -14,6 +14,7 @@ > #define SEC_OFFSET 0x4000 > > #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5)) > +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE BIT(5) > > #define DP_PHY_GLB_BIAS_GEN_00 0 > #define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(20, 16)
Hi, Bo-Chen: On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote: > From: Markus Schneider-Pargmann <msp@baylibre.com> > > This patch adds a embedded displayport driver for the MediaTek mt8195 > SoC. > > It supports the MT8195, the embedded DisplayPort units. It offers > DisplayPort 1.4 with up to 4 lanes. > > The driver creates a child device for the phy. The child device will > never exist without the parent being active. As they are sharing a > register range, the parent passes a regmap pointer to the child so > that > both can work with the same register range. The phy driver sets > device > data that is read by the parent to get the phy device that can be > used > to control the phy properties. > > This driver is based on an initial version by > Jitao shi <jitao.shi@mediatek.com> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > --- [snip] > + > +#define MTK_DP_ENC0_P0_31D0 (ENC0_OFFSET + 0x1D0) MTK_DP_ENC0_P0_31D0 is uselese, so remove it. > +#define VSC_EXT_CEA_HB0_DP_ENC0_P0_MASK GENMASK(7, 0) > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_MASK GENMASK(15, 8) > +#define VSC_EXT_CEA_HB1_DP_ENC0_P0_SHIFT BIT(3) > + > +#define MTK_DP_ENC0_P0_31D4 (ENC0_OFFSET + 0x1D4) Ditto. > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_MASK GENMASK(7, 0) > +#define VSC_EXT_CEA_HB2_DP_ENC0_P0_SHIFT 0 > +#define VSC_EXT_CEA_HB3_DP_ENC0_P0_MASK GENMASK(15, 8) > + > +#define MTK_DP_ENC0_P0_31D8 (ENC0_OFFSET + 0x1D8) Ditto. > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_MASK GENMASK(5, 0) > +#define VSC_EXT_VESA_NUM_DP_ENC0_P0_SHIFT 0 > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_MASK GENMASK(13, 8) > +#define VSC_EXT_CEA_NUM_DP_ENC0_P0_SHIFT BIT(3) > + > +#define MTK_DP_ENC0_P0_31DC (ENC0_OFFSET + 0x1DC) Ditto. Regards, CK > +#define HDR0_CFG_DP_ENC0_P0_MASK GENMASK(7, 0) > +#define HDR0_CFG_DP_ENC0_P0_SHIFT 0 > +#define MTK_DP_ENC0_P0_31E8 (ENC0_OFFSET + 0x1E8) > +#define MTK_DP_ENC0_P0_31EC (ENC0_OFFSET + 0x1EC) > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_MASK BIT(4) > +#define AUDIO_CH_SRC_SEL_DP_ENC0_P0_SHIFT BIT(2) > +#define ISRC1_HB3_DP_ENC0_P0_MASK GENMASK(15, 8) > +#define ISRC1_HB3_DP_ENC0_P0_SHIFT BIT(3) > +
Hi, Bo-Chen: On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote: > From: Markus Schneider-Pargmann <msp@baylibre.com> > > This patch adds a embedded displayport driver for the MediaTek mt8195 > SoC. > > It supports the MT8195, the embedded DisplayPort units. It offers > DisplayPort 1.4 with up to 4 lanes. > > The driver creates a child device for the phy. The child device will > never exist without the parent being active. As they are sharing a > register range, the parent passes a regmap pointer to the child so > that > both can work with the same register range. The phy driver sets > device > data that is read by the parent to get the phy device that can be > used > to control the phy properties. > > This driver is based on an initial version by > Jitao shi <jitao.shi@mediatek.com> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > --- [snip] > + > +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) > +{ > + struct mtk_dp *mtk_dp = dev; > + > + if (mtk_dp->train_info.hpd_inerrupt) { When the thread is running, mtk_dp->train_info.hpd_inerrupt would be true. So this checking is redundant. > + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); > + mtk_dp->train_info.hpd_inerrupt = false; > + mtk_dp_hpd_sink_event(mtk_dp); > + } > + > + return IRQ_HANDLED; > +} > + > +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) > +{ > + struct mtk_dp *mtk_dp = dev; > + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; > + u32 irq_status; > + > + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); > + > + if (!irq_status) > + return IRQ_HANDLED; > + > + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) { Combine this if-checking with previous if-checking, it would be: if (!(irq_status & RGS_IRQ_STATUS_TRANSMITTER)) return IRQ_HANDLED; > + irq_status = mtk_dp_swirq_get_clear(mtk_dp) | > + mtk_dp_hwirq_get_clear(mtk_dp); > + > + if (!irq_status) > + return IRQ_HANDLED; > + > + if (irq_status & MTK_DP_HPD_INTERRUPT) Does this interrupt MTK_DP_HPD_INTERRUPT have any relation with MTK_DP_HPD_CONNECT and MTK_DP_HPD_CONNECT? From the naming, I guess that when MTK_DP_HPD_CONNECT happen, MTK_DP_HPD_INTERRUPT would also happen. Either for MTK_DP_HPD_DISCONNECT. When would MTK_DP_HPD_INTERRUPT happen but MTK_DP_HPD_CONNECT or MTK_DP_HPD_DISCONNECT does not happen. Regards, CK > + train_info->hpd_inerrupt = true; > + > + if (!(irq_status & MTK_DP_HPD_CONNECT || > + irq_status & MTK_DP_HPD_DISCONNECT)) > + return IRQ_WAKE_THREAD; > + > + if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) & > + HPD_DB_DP_TRANS_P0_MASK)) > + train_info->cable_plugged_in = true; > + else > + train_info->cable_plugged_in = false; > + > + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, > + DP_PWR_STATE_BANDGAP_TPLL_LANE, > + DP_PWR_STATE_MASK); > + } > + > + return IRQ_HANDLED; > +} > +
Hi, Bo-Chen: On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote: > From: Markus Schneider-Pargmann <msp@baylibre.com> > > This patch adds a embedded displayport driver for the MediaTek mt8195 > SoC. > > It supports the MT8195, the embedded DisplayPort units. It offers > DisplayPort 1.4 with up to 4 lanes. > > The driver creates a child device for the phy. The child device will > never exist without the parent being active. As they are sharing a > register range, the parent passes a regmap pointer to the child so > that > both can work with the same register range. The phy driver sets > device > data that is read by the parent to get the phy device that can be > used > to control the phy properties. > > This driver is based on an initial version by > Jitao shi <jitao.shi@mediatek.com> > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > --- [snip] > + > +static int mtk_dp_train_tps_2_3(struct mtk_dp *mtk_dp, u8 > target_linkrate, > + u8 target_lane_count, u8 *lane_adjust, > + int *status_control, u8 > *prev_lane_adjust) > +{ > + u8 val; > + u8 link_status[DP_LINK_STATUS_SIZE] = {}; > + > + if (*status_control == 1) { > + if (mtk_dp->train_info.tps4) { > + mtk_dp_train_set_pattern(mtk_dp, 4); > + val = DP_TRAINING_PATTERN_4; > + } else if (mtk_dp->train_info.tps3) { > + mtk_dp_train_set_pattern(mtk_dp, 3); > + val = DP_LINK_SCRAMBLING_DISABLE | > + DP_TRAINING_PATTERN_3; > + } else { > + mtk_dp_train_set_pattern(mtk_dp, 2); > + val = DP_LINK_SCRAMBLING_DISABLE | > + DP_TRAINING_PATTERN_2; > + } Only one of tps2, tps3, tps4 would be process, and the priority is tps4 > tps3 > tps2, so I would like use one variable for this. if support tps4, train_info.tps = 4. else if support tps3, train_info.tps = 3. else train_info.tps = 2. And this part would be almost the same as the part in mtk_dp_train_tps_1(), so separate this part to a function. Regards, CK > + drm_dp_dpcd_writeb(&mtk_dp->aux, > + DP_TRAINING_PATTERN_SET, val); > + drm_dp_dpcd_read(&mtk_dp->aux, > + DP_ADJUST_REQUEST_LANE0_1, > lane_adjust, > + sizeof(*lane_adjust) * 2); > + > + mtk_dp_train_update_swing_pre(mtk_dp, > + target_lane_count, > lane_adjust); > + *status_control = 2; > + } > + > + drm_dp_link_train_channel_eq_delay(&mtk_dp->aux, mtk_dp- > >rx_cap); > + > + drm_dp_dpcd_read_link_status(&mtk_dp->aux, link_status); > + > + if (drm_dp_channel_eq_ok(link_status, target_lane_count)) { > + mtk_dp->train_info.eq_done = true; > + dev_dbg(mtk_dp->dev, "Link train EQ pass\n"); > + return 0; > + } > + > + dev_dbg(mtk_dp->dev, "Link train EQ fail\n"); > + > + if (*prev_lane_adjust != link_status[4]) > + *prev_lane_adjust = link_status[4]; > + > + return -EAGAIN; > +} > +
On Wed, 27 Jul 2022, Bo-Chen Chen <rex-bc.chen@mediatek.com> wrote: > From: Guillaume Ranquet <granquet@baylibre.com> > > This patch adds two helper functions that extract the frequency and word > length from a struct cea_sad. > > For these helper functions new defines are added that help translate the > 'freq' and 'byte2' fields into real numbers. I think we should stop adding a plethora of functions that deal with sads like this. There should probably be *one* struct that contains all the details you and everyone need extracted. There should be *one* function that fills in all the details. The struct should be placed in connector->display_info, and the function should be called *once* from update_display_info(). Ideally, the function shouldn't even be exposed from drm_edid.c, but if you still need to deal with situations where you don't call connector update, maybe it needs to be exposed. BR, Jani. > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > --- > drivers/gpu/drm/drm_edid.c | 63 ++++++++++++++++++++++++++++++++++++++ > include/drm/drm_edid.h | 14 +++++++++ > 2 files changed, 77 insertions(+) > > diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c > index bc43e1b32092..2a6f92da5ff3 100644 > --- a/drivers/gpu/drm/drm_edid.c > +++ b/drivers/gpu/drm/drm_edid.c > @@ -4916,6 +4916,69 @@ int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb) > } > EXPORT_SYMBOL(drm_edid_to_speaker_allocation); > > +/** > + * drm_cea_sad_get_sample_rate - Extract the sample rate from cea_sad > + * @sad: Pointer to the cea_sad struct > + * > + * Extracts the cea_sad frequency field and returns the sample rate in Hz. > + * > + * Return: Sample rate in Hz or a negative errno if parsing failed. > + */ > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad) > +{ > + switch (sad->freq) { > + case DRM_CEA_SAD_FREQ_32KHZ: > + return 32000; > + case DRM_CEA_SAD_FREQ_44KHZ: > + return 44100; > + case DRM_CEA_SAD_FREQ_48KHZ: > + return 48000; > + case DRM_CEA_SAD_FREQ_88KHZ: > + return 88200; > + case DRM_CEA_SAD_FREQ_96KHZ: > + return 96000; > + case DRM_CEA_SAD_FREQ_176KHZ: > + return 176400; > + case DRM_CEA_SAD_FREQ_192KHZ: > + return 192000; > + default: > + return -EINVAL; > + } > +} > +EXPORT_SYMBOL(drm_cea_sad_get_sample_rate); > + > +/** > + * drm_cea_sad_get_uncompressed_word_length - Extract word length > + * @sad: Pointer to the cea_sad struct > + * > + * Extracts the cea_sad byte2 field and returns the word length for an > + * uncompressed stream. > + * > + * Note: This function may only be called for uncompressed audio. > + * > + * Return: Word length in bits or a negative errno if parsing failed. > + */ > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad) > +{ > + if (sad->format != HDMI_AUDIO_CODING_TYPE_PCM) { > + DRM_WARN("Unable to get the uncompressed word length for format: %u\n", > + sad->format); > + return -EINVAL; > + } > + > + switch (sad->byte2) { > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT: > + return 16; > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT: > + return 20; > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT: > + return 24; > + default: > + return -EINVAL; > + } > +} > +EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length); > + > /** > * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync delay > * @connector: connector associated with the HDMI/DP sink > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > index c2c43a4af681..779b710aed40 100644 > --- a/include/drm/drm_edid.h > +++ b/include/drm/drm_edid.h > @@ -373,6 +373,18 @@ struct cea_sad { > u8 byte2; > }; > > +#define DRM_CEA_SAD_FREQ_32KHZ BIT(0) > +#define DRM_CEA_SAD_FREQ_44KHZ BIT(1) > +#define DRM_CEA_SAD_FREQ_48KHZ BIT(2) > +#define DRM_CEA_SAD_FREQ_88KHZ BIT(3) > +#define DRM_CEA_SAD_FREQ_96KHZ BIT(4) > +#define DRM_CEA_SAD_FREQ_176KHZ BIT(5) > +#define DRM_CEA_SAD_FREQ_192KHZ BIT(6) > + > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0) > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1) > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2) > + > struct drm_encoder; > struct drm_connector; > struct drm_connector_state; > @@ -380,6 +392,8 @@ struct drm_display_mode; > > int drm_edid_to_sad(const struct edid *edid, struct cea_sad **sads); > int drm_edid_to_speaker_allocation(const struct edid *edid, u8 **sadb); > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad); > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad *sad); > int drm_av_sync_delay(struct drm_connector *connector, > const struct drm_display_mode *mode);
On Tue, 2022-08-02 at 10:37 +0800, CK Hu wrote: > Hi, Bo-Chen: > > On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote: > > From: Guillaume Ranquet <granquet@baylibre.com> > > > > This patch adds External DisplayPort support to the mt8195 eDP > > driver. > > > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > > --- > > drivers/gpu/drm/mediatek/mtk_dp.c | 181 +++++++++++++++++++++- > > -- > > -- > > drivers/gpu/drm/mediatek/mtk_dp_reg.h | 1 + > > 2 files changed, 146 insertions(+), 36 deletions(-) > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c > > b/drivers/gpu/drm/mediatek/mtk_dp.c > > index 06eeecedd49e..ce817cb59445 100644 > > --- a/drivers/gpu/drm/mediatek/mtk_dp.c > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c > > @@ -103,6 +103,11 @@ static struct regmap_config > > mtk_dp_regmap_config > > = { > > .name = "mtk-dp-registers", > > }; > > > > +static bool mtk_dp_is_edp(struct mtk_dp *mtk_dp) > > +{ > > + return mtk_dp->next_bridge; > > For external DP, it could also connect to a bridge IC which transfer > DP > to HDMI or some interface else. Use the compatible to judge this edp > or > dp. > Hello CK, ok, I will use of device data to do this. > > +} > > + > > static struct mtk_dp *mtk_dp_from_bridge(struct drm_bridge *b) > > { > > return container_of(b, struct mtk_dp, bridge); > > @@ -341,6 +346,19 @@ static void mtk_dp_pg_enable(struct mtk_dp > > *mtk_dp, bool enable) > > 4 << PGEN_PATTERN_SEL_SHIFT, > > PGEN_PATTERN_SEL_MASK); > > } > > > > +static bool mtk_dp_plug_state(struct mtk_dp *mtk_dp) > > +{ > > + return mtk_dp->train_info.cable_plugged_in; > > +} > > + > > +static bool mtk_dp_plug_state_avoid_pulse(struct mtk_dp *mtk_dp) > > +{ > > + bool ret; > > + > > + return !(readx_poll_timeout(mtk_dp_plug_state, mtk_dp, ret, > > ret, > > + 4000, 7 * 4000)); > > +} > > + > > static void mtk_dp_aux_irq_clear(struct mtk_dp *mtk_dp) > > { > > mtk_dp_write(mtk_dp, MTK_DP_AUX_P0_3640, > > @@ -778,35 +796,67 @@ static void > > mtk_dp_get_calibration_data(struct > > mtk_dp *mtk_dp) > > return; > > } > > > > - cal_data->glb_bias_trim = > > - check_cal_data_valid(mtk_dp, 1, 0x1e, > > - (buf[3] >> 27) & 0x1f, 0xf); > > - cal_data->clktx_impse = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[0] >> 9) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_pmos[0] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 28) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_nmos[0] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 24) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_pmos[1] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 20) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_nmos[1] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 16) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_pmos[2] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 12) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_nmos[2] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 8) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_pmos[3] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, > > - (buf[2] >> 4) & 0xf, 0x8); > > - cal_data->ln_tx_impsel_nmos[3] = > > - check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & 0xf, > > 0x8); > > + if (mtk_dp_is_edp(mtk_dp)) { > > + cal_data->glb_bias_trim = > > + check_cal_data_valid(mtk_dp, 1, 0x1e, > > + (buf[3] >> 27) & 0x1f, > > 0xf); > > + cal_data->clktx_impse = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[0] >> 9) & 0xf, 0x8); > > + cal_data->ln_tx_impsel_pmos[0] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 28) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_nmos[0] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 24) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_pmos[1] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 20) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_nmos[1] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 16) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_pmos[2] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 12) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_nmos[2] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 8) & 0xf, 0x8); > > + cal_data->ln_tx_impsel_pmos[3] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[2] >> 4) & 0xf, 0x8); > > + cal_data->ln_tx_impsel_nmos[3] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, buf[2] & > > 0xf, 0x8); > > + } else { > > + cal_data->glb_bias_trim = > > + check_cal_data_valid(mtk_dp, 1, 0x1e, > > + (buf[0] >> 27) & 0x1f, > > 0xf); > > Why not place glb_bias_trim value in nvmem buf[3] for both edp and dp > case? Place in the same nvmem position, the driver code would be > simple. > These value are read from efuse data. do you mean change the order in this dp driver? Is so, I think it's not appropriate. It will be hard to debug if any issue occurs. > > + cal_data->clktx_impse = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[0] >> 13) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_pmos[0] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 28) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_nmos[0] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 24) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_pmos[1] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 20) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_nmos[1] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 16) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_pmos[2] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 12) & 0xf, > > 0x8); > > + cal_data->ln_tx_impsel_nmos[2] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 8) & 0xf, 0x8); > > + cal_data->ln_tx_impsel_pmos[3] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, > > + (buf[1] >> 4) & 0xf, 0x8); > > + cal_data->ln_tx_impsel_nmos[3] = > > + check_cal_data_valid(mtk_dp, 1, 0xe, buf[1] & > > 0xf, 0x8); > > Why not place ln_tx_impsel_nmos and ln_tx_impsel_pmos value in nvmem > buf[2] for both edp and dp case? Place in the same nvmem position, > the > driver code would be simple. > > > + } > > > > kfree(buf); > > } > > @@ -926,7 +976,10 @@ static void mtk_dp_video_mute(struct mtk_dp > > *mtk_dp, bool enable) > > VIDEO_MUTE_SEL_DP_ENC0_P0_MASK | > > VIDEO_MUTE_SW_DP_ENC0_P0_MASK); > > > > - mtk_dp_sip_atf_call(mtk_dp, MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE, > > enable); > > + mtk_dp_sip_atf_call(mtk_dp, > > + mtk_dp_is_edp(mtk_dp) ? > > + MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE : > > + MTK_DP_SIP_ATF_VIDEO_UNMUTE, enable); > > Use of_device_get_match_data to select > MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE > or MTK_DP_SIP_ATF_VIDEO_UNMUTE. > Do you mean add a new variable to control this in of device match data? BRs, Bo-Chen > > } > > > > static void mtk_dp_power_enable(struct mtk_dp *mtk_dp) > > @@ -1226,6 +1279,9 @@ static int mtk_dp_parse_capabilities(struct > > mtk_dp *mtk_dp) > > drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, > > DP_SET_POWER_D0); > > usleep_range(2000, 5000); > > > > + if (!mtk_dp_plug_state(mtk_dp)) > > + return -ENODEV; > > + > > drm_dp_read_dpcd_caps(&mtk_dp->aux, mtk_dp->rx_cap); > > > > train_info->link_rate = min_t(int, mtk_dp->max_linkrate, > > @@ -1277,6 +1333,9 @@ static int mtk_dp_train_start(struct mtk_dp > > *mtk_dp) > > u8 train_limit; > > u8 max_link_rate; > > > > + if (!mtk_dp_plug_state_avoid_pulse(mtk_dp)) > > + return -ENODEV; > > + > > link_rate = mtk_dp->rx_cap[1]; > > lane_count = mtk_dp->rx_cap[2] & 0x1F; > > > > @@ -1412,6 +1471,9 @@ static irqreturn_t > > mtk_dp_hpd_event_thread(int > > hpd, void *dev) > > { > > struct mtk_dp *mtk_dp = dev; > > > > + dev_dbg(mtk_dp->dev, "drm_helper_hpd_irq_event\n"); > > + drm_helper_hpd_irq_event(mtk_dp->bridge.dev); > > Because edp also have hpd interrupt, so I would like both edp and dp > have the same control flow for hdp. So move this to edp patch. > > > + > > if (mtk_dp->train_info.hpd_inerrupt) { > > dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); > > mtk_dp->train_info.hpd_inerrupt = false; > > @@ -1452,9 +1514,20 @@ static irqreturn_t mtk_dp_hpd_event(int hpd, > > void *dev) > > else > > train_info->cable_plugged_in = false; > > > > - mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, > > - DP_PWR_STATE_BANDGAP_TPLL_LANE, > > - DP_PWR_STATE_MASK); > > + if (!train_info->cable_plugged_in) { > > + mtk_dp_video_mute(mtk_dp, true); > > + > > + mtk_dp_initialize_priv_data(mtk_dp); > > + mtk_dp_set_idle_pattern(mtk_dp, true); > > + > > + mtk_dp_update_bits(mtk_dp, > > MTK_DP_TOP_PWR_STATE, > > + DP_PWR_STATE_BANDGAP_TPLL, > > + DP_PWR_STATE_MASK); > > + } else { > > + mtk_dp_update_bits(mtk_dp, > > MTK_DP_TOP_PWR_STATE, > > + DP_PWR_STATE_BANDGAP_TPLL_LA > > NE, > > + DP_PWR_STATE_MASK); > > + } > > } > > > > return IRQ_HANDLED; > > @@ -1499,6 +1572,24 @@ static int mtk_dp_dt_parse(struct mtk_dp > > *mtk_dp, > > return 0; > > } > > > > +static enum drm_connector_status mtk_dp_bdg_detect(struct > > drm_bridge > > *bridge) > > +{ > > + struct mtk_dp *mtk_dp = mtk_dp_from_bridge(bridge); > > + enum drm_connector_status ret = connector_status_disconnected; > > + u8 sink_count = 0; > > + > > + if (mtk_dp_is_edp(mtk_dp)) > > + return connector_status_connected; > > Because edp also have hpd interrupt, so I would like both edp and dp > have the same control flow for hdp. So remove this. > > > + > > + if (mtk_dp_plug_state_avoid_pulse(mtk_dp)) { > > + drm_dp_dpcd_readb(&mtk_dp->aux, DP_SINK_COUNT, > > &sink_count); > > + if (DP_GET_SINK_COUNT(sink_count)) > > + ret = connector_status_connected; > > + } > > + > > + return ret; > > +} > > + > > static struct edid *mtk_dp_get_edid(struct drm_bridge *bridge, > > struct drm_connector *connector) > > { > > @@ -1512,7 +1603,8 @@ static struct edid *mtk_dp_get_edid(struct > > drm_bridge *bridge, > > drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, > > DP_SET_POWER_D0); > > usleep_range(2000, 5000); > > > > - new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc); > > + if (mtk_dp_plug_state(mtk_dp)) > > + new_edid = drm_get_edid(connector, &mtk_dp->aux.ddc); > > Please explain why only dp has this problem. If edp also has this > problem, move this to edp patch. > > Regards, > CK > > > > > if (!enabled) > > drm_bridge_chain_post_disable(bridge); > > @@ -1852,6 +1944,7 @@ static const struct drm_bridge_funcs > > mtk_dp_bridge_funcs = { > > .atomic_disable = mtk_dp_bridge_atomic_disable, > > .mode_valid = mtk_dp_bridge_mode_valid, > > .get_edid = mtk_dp_get_edid, > > + .detect = mtk_dp_bdg_detect, > > }; > > > > static int mtk_dp_probe(struct platform_device *pdev) > > @@ -1873,9 +1966,15 @@ static int mtk_dp_probe(struct > > platform_device > > *pdev) > > "failed to request dp irq > > resource\n"); > > > > mtk_dp->next_bridge = devm_drm_of_get_bridge(dev, dev- > > >of_node, > > 1, 0); > > - if (IS_ERR(mtk_dp->next_bridge)) > > + if (IS_ERR(mtk_dp->next_bridge) && > > + PTR_ERR(mtk_dp->next_bridge) == -ENODEV) { > > + dev_info(dev, > > + "No panel connected in devicetree, continue as > > external DP\n"); > > + mtk_dp->next_bridge = NULL; > > + } else if (IS_ERR(mtk_dp->next_bridge)) { > > return dev_err_probe(dev, PTR_ERR(mtk_dp->next_bridge), > > "Failed to get bridge\n"); > > + } > > > > ret = mtk_dp_dt_parse(mtk_dp, pdev); > > if (ret) > > @@ -1918,7 +2017,10 @@ static int mtk_dp_probe(struct > > platform_device > > *pdev) > > > > mtk_dp->bridge.ops = > > DRM_BRIDGE_OP_DETECT | DRM_BRIDGE_OP_EDID | > > DRM_BRIDGE_OP_HPD; > > - mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; > > + if (mtk_dp_is_edp(mtk_dp)) > > + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_eDP; > > + else > > + mtk_dp->bridge.type = DRM_MODE_CONNECTOR_DisplayPort; > > > > drm_bridge_add(&mtk_dp->bridge); > > > > @@ -1945,6 +2047,12 @@ static int mtk_dp_suspend(struct device > > *dev) > > { > > struct mtk_dp *mtk_dp = dev_get_drvdata(dev); > > > > + if (mtk_dp_plug_state(mtk_dp)) { > > + drm_dp_dpcd_writeb(&mtk_dp->aux, DP_SET_POWER, > > DP_SET_POWER_D3); > > + /* Ensure the sink is off before shutting down power */ > > + usleep_range(2000, 3000); > > + } > > + > > mtk_dp_power_disable(mtk_dp); > > > > mtk_dp_hwirq_enable(mtk_dp, false); > > @@ -1978,6 +2086,7 @@ static SIMPLE_DEV_PM_OPS(mtk_dp_pm_ops, > > mtk_dp_suspend, mtk_dp_resume); > > > > static const struct of_device_id mtk_dp_of_match[] = { > > { .compatible = "mediatek,mt8195-edp-tx" }, > > + { .compatible = "mediatek,mt8195-dp-tx" }, > > {}, > > }; > > MODULE_DEVICE_TABLE(of, mtk_dp_of_match); > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp_reg.h > > b/drivers/gpu/drm/mediatek/mtk_dp_reg.h > > index 4ff8e9880dc9..59086eb82868 100644 > > --- a/drivers/gpu/drm/mediatek/mtk_dp_reg.h > > +++ b/drivers/gpu/drm/mediatek/mtk_dp_reg.h > > @@ -14,6 +14,7 @@ > > #define SEC_OFFSET 0x4000 > > > > #define MTK_DP_SIP_ATF_EDP_VIDEO_UNMUTE (BIT(0) | BIT(5)) > > +#define MTK_DP_SIP_ATF_VIDEO_UNMUTE BIT(5) > > > > #define DP_PHY_GLB_BIAS_GEN_00 0 > > #define RG_XTP_GLB_BIAS_INTR_CTRL GENMASK(20, 16) > >
On Tue, 2022-08-02 at 17:11 +0300, Jani Nikula wrote: > On Wed, 27 Jul 2022, Bo-Chen Chen <rex-bc.chen@mediatek.com> wrote: > > From: Guillaume Ranquet <granquet@baylibre.com> > > > > This patch adds two helper functions that extract the frequency and > > word > > length from a struct cea_sad. > > > > For these helper functions new defines are added that help > > translate the > > 'freq' and 'byte2' fields into real numbers. > > I think we should stop adding a plethora of functions that deal with > sads like this. > > There should probably be *one* struct that contains all the details > you > and everyone need extracted. There should be *one* function that > fills > in all the details. The struct should be placed in > connector->display_info, and the function should be called *once* > from > update_display_info(). > > Ideally, the function shouldn't even be exposed from drm_edid.c, but > if > you still need to deal with situations where you don't call connector > update, maybe it needs to be exposed. > > BR, > Jani. > > Hello Jani, Thanks for your review. After checking our patches, we found we will not use these two function because we remove them in patch [11/11] drm/mediatek: Use cached audio config when changing resolution. I will drop [02/11] and [03/11]. Thanks! BRs, Bo-Chen > > > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > > --- > > drivers/gpu/drm/drm_edid.c | 63 > > ++++++++++++++++++++++++++++++++++++++ > > include/drm/drm_edid.h | 14 +++++++++ > > 2 files changed, 77 insertions(+) > > > > diff --git a/drivers/gpu/drm/drm_edid.c > > b/drivers/gpu/drm/drm_edid.c > > index bc43e1b32092..2a6f92da5ff3 100644 > > --- a/drivers/gpu/drm/drm_edid.c > > +++ b/drivers/gpu/drm/drm_edid.c > > @@ -4916,6 +4916,69 @@ int drm_edid_to_speaker_allocation(const > > struct edid *edid, u8 **sadb) > > } > > EXPORT_SYMBOL(drm_edid_to_speaker_allocation); > > > > +/** > > + * drm_cea_sad_get_sample_rate - Extract the sample rate from > > cea_sad > > + * @sad: Pointer to the cea_sad struct > > + * > > + * Extracts the cea_sad frequency field and returns the sample > > rate in Hz. > > + * > > + * Return: Sample rate in Hz or a negative errno if parsing > > failed. > > + */ > > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad) > > +{ > > + switch (sad->freq) { > > + case DRM_CEA_SAD_FREQ_32KHZ: > > + return 32000; > > + case DRM_CEA_SAD_FREQ_44KHZ: > > + return 44100; > > + case DRM_CEA_SAD_FREQ_48KHZ: > > + return 48000; > > + case DRM_CEA_SAD_FREQ_88KHZ: > > + return 88200; > > + case DRM_CEA_SAD_FREQ_96KHZ: > > + return 96000; > > + case DRM_CEA_SAD_FREQ_176KHZ: > > + return 176400; > > + case DRM_CEA_SAD_FREQ_192KHZ: > > + return 192000; > > + default: > > + return -EINVAL; > > + } > > +} > > +EXPORT_SYMBOL(drm_cea_sad_get_sample_rate); > > + > > +/** > > + * drm_cea_sad_get_uncompressed_word_length - Extract word length > > + * @sad: Pointer to the cea_sad struct > > + * > > + * Extracts the cea_sad byte2 field and returns the word length > > for an > > + * uncompressed stream. > > + * > > + * Note: This function may only be called for uncompressed audio. > > + * > > + * Return: Word length in bits or a negative errno if parsing > > failed. > > + */ > > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad > > *sad) > > +{ > > + if (sad->format != HDMI_AUDIO_CODING_TYPE_PCM) { > > + DRM_WARN("Unable to get the uncompressed word length > > for format: %u\n", > > + sad->format); > > + return -EINVAL; > > + } > > + > > + switch (sad->byte2) { > > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT: > > + return 16; > > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT: > > + return 20; > > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT: > > + return 24; > > + default: > > + return -EINVAL; > > + } > > +} > > +EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length); > > + > > /** > > * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync > > delay > > * @connector: connector associated with the HDMI/DP sink > > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h > > index c2c43a4af681..779b710aed40 100644 > > --- a/include/drm/drm_edid.h > > +++ b/include/drm/drm_edid.h > > @@ -373,6 +373,18 @@ struct cea_sad { > > u8 byte2; > > }; > > > > +#define DRM_CEA_SAD_FREQ_32KHZ BIT(0) > > +#define DRM_CEA_SAD_FREQ_44KHZ BIT(1) > > +#define DRM_CEA_SAD_FREQ_48KHZ BIT(2) > > +#define DRM_CEA_SAD_FREQ_88KHZ BIT(3) > > +#define DRM_CEA_SAD_FREQ_96KHZ BIT(4) > > +#define DRM_CEA_SAD_FREQ_176KHZ BIT(5) > > +#define DRM_CEA_SAD_FREQ_192KHZ BIT(6) > > + > > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0) > > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1) > > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2) > > + > > struct drm_encoder; > > struct drm_connector; > > struct drm_connector_state; > > @@ -380,6 +392,8 @@ struct drm_display_mode; > > > > int drm_edid_to_sad(const struct edid *edid, struct cea_sad > > **sads); > > int drm_edid_to_speaker_allocation(const struct edid *edid, u8 > > **sadb); > > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad); > > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad > > *sad); > > int drm_av_sync_delay(struct drm_connector *connector, > > const struct drm_display_mode *mode); > >
Hello all, I will drop this modification becaus after we testing current drivers, the issue described in commit message does not occur. BRs, Bo-Chen On Thu, 2022-07-28 at 17:40 +0800, Rex-BC Chen wrote: > On Wed, 2022-07-27 at 11:40 +0200, AngeloGioacchino Del Regno wrote: > > Il 27/07/22 06:50, Bo-Chen Chen ha scritto: > > > For some DP dungles, we need to train more than onece to confirm > > > that we > > > don't misjudge the status of sink device. > > > > Please fix the typos in your commit title and description. > > title: misjudgment -> misjudgement > > desc: dungles->dongles; onece->once > > > > > > > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > > > --- > > > drivers/gpu/drm/mediatek/mtk_dp.c | 21 ++++++++++++++++++--- > > > 1 file changed, 18 insertions(+), 3 deletions(-) > > > > > > diff --git a/drivers/gpu/drm/mediatek/mtk_dp.c > > > b/drivers/gpu/drm/mediatek/mtk_dp.c > > > index ce817cb59445..80d7d6488105 100644 > > > --- a/drivers/gpu/drm/mediatek/mtk_dp.c > > > +++ b/drivers/gpu/drm/mediatek/mtk_dp.c > > > @@ -42,6 +42,7 @@ > > > #define MTK_DP_CHECK_SINK_CAP_TIMEOUT_COUNT 3 > > > #define MTK_DP_TBC_BUF_READ_START_ADDR 0x08 > > > #define MTK_DP_TRAIN_DOWNSCALE_RETRY 8 > > > +#define MTK_DP_TRAIN_CLEAR_RETRY 50 > > > > > > struct mtk_dp_train_info { > > > bool tps3; > > > @@ -1431,11 +1432,25 @@ static int mtk_dp_video_config(struct > > > mtk_dp *mtk_dp) > > > > > > static int mtk_dp_training(struct mtk_dp *mtk_dp) > > > { > > > + short max_retry = MTK_DP_TRAIN_CLEAR_RETRY; > > > int ret; > > > > > > - ret = mtk_dp_train_start(mtk_dp); > > > - if (ret) > > > - return ret; > > > + /* > > > + * We do retry to confirm that we don't misjudge the sink > > > status. > > > + * If it is still failed, we can confirm there are some issues > > > for the > > > + * sink device. > > > + */ > > > + do { > > > + ret = mtk_dp_train_start(mtk_dp); > > > + if (!ret) > > > + break; > > > + } while (--max_retry); > > > + > > > + dev_info(mtk_dp->dev, "dp training clear retry times: %d\n", > > > + MTK_DP_TRAIN_CLEAR_RETRY - max_retry); > > > > dev_dbg() here. > > > > ...after which, > > > > Reviewed-by: AngeloGioacchino Del Regno < > > angelogioacchino.delregno@collabora.com> > > > > Hello Angelo, > > Thanks for your review. > I will modify these in next version. > > BRs, > Bo-Chen > > > > + > > > + if (!max_retry) > > > + return -ETIMEDOUT; > > > > > > ret = mtk_dp_video_config(mtk_dp); > > > if (ret) > > > > > >
On Fri, 05 Aug 2022, Rex-BC Chen <rex-bc.chen@mediatek.com> wrote: > On Tue, 2022-08-02 at 17:11 +0300, Jani Nikula wrote: >> On Wed, 27 Jul 2022, Bo-Chen Chen <rex-bc.chen@mediatek.com> wrote: >> > From: Guillaume Ranquet <granquet@baylibre.com> >> > >> > This patch adds two helper functions that extract the frequency and >> > word >> > length from a struct cea_sad. >> > >> > For these helper functions new defines are added that help >> > translate the >> > 'freq' and 'byte2' fields into real numbers. >> >> I think we should stop adding a plethora of functions that deal with >> sads like this. >> >> There should probably be *one* struct that contains all the details >> you >> and everyone need extracted. There should be *one* function that >> fills >> in all the details. The struct should be placed in >> connector->display_info, and the function should be called *once* >> from >> update_display_info(). >> >> Ideally, the function shouldn't even be exposed from drm_edid.c, but >> if >> you still need to deal with situations where you don't call connector >> update, maybe it needs to be exposed. >> >> BR, >> Jani. >> >> > > Hello Jani, > > Thanks for your review. > After checking our patches, we found we will not use these two function > because we remove them in patch [11/11] drm/mediatek: Use cached audio > config when changing resolution. > > I will drop [02/11] and [03/11]. > > Thanks! Thank you too! :) BR, Jani. > > BRs, > Bo-Chen > >> > >> > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> >> > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> >> > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> >> > --- >> > drivers/gpu/drm/drm_edid.c | 63 >> > ++++++++++++++++++++++++++++++++++++++ >> > include/drm/drm_edid.h | 14 +++++++++ >> > 2 files changed, 77 insertions(+) >> > >> > diff --git a/drivers/gpu/drm/drm_edid.c >> > b/drivers/gpu/drm/drm_edid.c >> > index bc43e1b32092..2a6f92da5ff3 100644 >> > --- a/drivers/gpu/drm/drm_edid.c >> > +++ b/drivers/gpu/drm/drm_edid.c >> > @@ -4916,6 +4916,69 @@ int drm_edid_to_speaker_allocation(const >> > struct edid *edid, u8 **sadb) >> > } >> > EXPORT_SYMBOL(drm_edid_to_speaker_allocation); >> > >> > +/** >> > + * drm_cea_sad_get_sample_rate - Extract the sample rate from >> > cea_sad >> > + * @sad: Pointer to the cea_sad struct >> > + * >> > + * Extracts the cea_sad frequency field and returns the sample >> > rate in Hz. >> > + * >> > + * Return: Sample rate in Hz or a negative errno if parsing >> > failed. >> > + */ >> > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad) >> > +{ >> > + switch (sad->freq) { >> > + case DRM_CEA_SAD_FREQ_32KHZ: >> > + return 32000; >> > + case DRM_CEA_SAD_FREQ_44KHZ: >> > + return 44100; >> > + case DRM_CEA_SAD_FREQ_48KHZ: >> > + return 48000; >> > + case DRM_CEA_SAD_FREQ_88KHZ: >> > + return 88200; >> > + case DRM_CEA_SAD_FREQ_96KHZ: >> > + return 96000; >> > + case DRM_CEA_SAD_FREQ_176KHZ: >> > + return 176400; >> > + case DRM_CEA_SAD_FREQ_192KHZ: >> > + return 192000; >> > + default: >> > + return -EINVAL; >> > + } >> > +} >> > +EXPORT_SYMBOL(drm_cea_sad_get_sample_rate); >> > + >> > +/** >> > + * drm_cea_sad_get_uncompressed_word_length - Extract word length >> > + * @sad: Pointer to the cea_sad struct >> > + * >> > + * Extracts the cea_sad byte2 field and returns the word length >> > for an >> > + * uncompressed stream. >> > + * >> > + * Note: This function may only be called for uncompressed audio. >> > + * >> > + * Return: Word length in bits or a negative errno if parsing >> > failed. >> > + */ >> > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad >> > *sad) >> > +{ >> > + if (sad->format != HDMI_AUDIO_CODING_TYPE_PCM) { >> > + DRM_WARN("Unable to get the uncompressed word length >> > for format: %u\n", >> > + sad->format); >> > + return -EINVAL; >> > + } >> > + >> > + switch (sad->byte2) { >> > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT: >> > + return 16; >> > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT: >> > + return 20; >> > + case DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT: >> > + return 24; >> > + default: >> > + return -EINVAL; >> > + } >> > +} >> > +EXPORT_SYMBOL(drm_cea_sad_get_uncompressed_word_length); >> > + >> > /** >> > * drm_av_sync_delay - compute the HDMI/DP sink audio-video sync >> > delay >> > * @connector: connector associated with the HDMI/DP sink >> > diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h >> > index c2c43a4af681..779b710aed40 100644 >> > --- a/include/drm/drm_edid.h >> > +++ b/include/drm/drm_edid.h >> > @@ -373,6 +373,18 @@ struct cea_sad { >> > u8 byte2; >> > }; >> > >> > +#define DRM_CEA_SAD_FREQ_32KHZ BIT(0) >> > +#define DRM_CEA_SAD_FREQ_44KHZ BIT(1) >> > +#define DRM_CEA_SAD_FREQ_48KHZ BIT(2) >> > +#define DRM_CEA_SAD_FREQ_88KHZ BIT(3) >> > +#define DRM_CEA_SAD_FREQ_96KHZ BIT(4) >> > +#define DRM_CEA_SAD_FREQ_176KHZ BIT(5) >> > +#define DRM_CEA_SAD_FREQ_192KHZ BIT(6) >> > + >> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_16BIT BIT(0) >> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_20BIT BIT(1) >> > +#define DRM_CEA_SAD_UNCOMPRESSED_WORD_24BIT BIT(2) >> > + >> > struct drm_encoder; >> > struct drm_connector; >> > struct drm_connector_state; >> > @@ -380,6 +392,8 @@ struct drm_display_mode; >> > >> > int drm_edid_to_sad(const struct edid *edid, struct cea_sad >> > **sads); >> > int drm_edid_to_speaker_allocation(const struct edid *edid, u8 >> > **sadb); >> > +int drm_cea_sad_get_sample_rate(const struct cea_sad *sad); >> > +int drm_cea_sad_get_uncompressed_word_length(const struct cea_sad >> > *sad); >> > int drm_av_sync_delay(struct drm_connector *connector, >> > const struct drm_display_mode *mode); >> >> >
On Tue, 2022-08-02 at 13:09 +0800, CK Hu wrote: > Hi, Bo-Chen: > > On Wed, 2022-07-27 at 12:50 +0800, Bo-Chen Chen wrote: > > From: Markus Schneider-Pargmann <msp@baylibre.com> > > > > This patch adds a embedded displayport driver for the MediaTek > > mt8195 > > SoC. > > > > It supports the MT8195, the embedded DisplayPort units. It offers > > DisplayPort 1.4 with up to 4 lanes. > > > > The driver creates a child device for the phy. The child device > > will > > never exist without the parent being active. As they are sharing a > > register range, the parent passes a regmap pointer to the child so > > that > > both can work with the same register range. The phy driver sets > > device > > data that is read by the parent to get the phy device that can be > > used > > to control the phy properties. > > > > This driver is based on an initial version by > > Jitao shi <jitao.shi@mediatek.com> > > > > Signed-off-by: Markus Schneider-Pargmann <msp@baylibre.com> > > Signed-off-by: Guillaume Ranquet <granquet@baylibre.com> > > Signed-off-by: Bo-Chen Chen <rex-bc.chen@mediatek.com> > > --- > > [snip] > > > + > > +static irqreturn_t mtk_dp_hpd_event_thread(int hpd, void *dev) > > +{ > > + struct mtk_dp *mtk_dp = dev; > > + > > + if (mtk_dp->train_info.hpd_inerrupt) { > > When the thread is running, mtk_dp->train_info.hpd_inerrupt would be > true. So this checking is redundant. > Hello CK, ok, I will remove it. > > + dev_dbg(mtk_dp->dev, "MTK_DP_HPD_INTERRUPT\n"); > > + mtk_dp->train_info.hpd_inerrupt = false; > > + mtk_dp_hpd_sink_event(mtk_dp); > > + } > > + > > + return IRQ_HANDLED; > > +} > > + > > +static irqreturn_t mtk_dp_hpd_event(int hpd, void *dev) > > +{ > > + struct mtk_dp *mtk_dp = dev; > > + struct mtk_dp_train_info *train_info = &mtk_dp->train_info; > > + u32 irq_status; > > + > > + irq_status = mtk_dp_read(mtk_dp, MTK_DP_TOP_IRQ_STATUS); > > + > > + if (!irq_status) > > + return IRQ_HANDLED; > > + > > + if (irq_status & RGS_IRQ_STATUS_TRANSMITTER) { > > Combine this if-checking with previous if-checking, it would be: > > if (!(irq_status & RGS_IRQ_STATUS_TRANSMITTER)) > return IRQ_HANDLED; > ok. > > + irq_status = mtk_dp_swirq_get_clear(mtk_dp) | > > + mtk_dp_hwirq_get_clear(mtk_dp); > > + > > + if (!irq_status) > > + return IRQ_HANDLED; > > + > > + if (irq_status & MTK_DP_HPD_INTERRUPT) > > Does this interrupt MTK_DP_HPD_INTERRUPT have any relation with > MTK_DP_HPD_CONNECT and MTK_DP_HPD_CONNECT? From the naming, I guess > that when MTK_DP_HPD_CONNECT happen, MTK_DP_HPD_INTERRUPT would also > happen. Either for MTK_DP_HPD_DISCONNECT. When would > MTK_DP_HPD_INTERRUPT happen but MTK_DP_HPD_CONNECT or > MTK_DP_HPD_DISCONNECT does not happen. > There is no relation for these status. They are individual. MTK_DP_HPD_INTERRUPT is for interrupt from sink device. After receiving this source device should do some actions. MTK_DP_HPD_CONNECT and MTK_DP_HPD_DISCONNECT are for HPD status. BRs, Bo-Chen > Regards, > CK > > > + train_info->hpd_inerrupt = true; > > + > > + if (!(irq_status & MTK_DP_HPD_CONNECT || > > + irq_status & MTK_DP_HPD_DISCONNECT)) > > + return IRQ_WAKE_THREAD; > > + > > + if (!!(mtk_dp_read(mtk_dp, MTK_DP_TRANS_P0_3414) & > > + HPD_DB_DP_TRANS_P0_MASK)) > > + train_info->cable_plugged_in = true; > > + else > > + train_info->cable_plugged_in = false; > > + > > + mtk_dp_update_bits(mtk_dp, MTK_DP_TOP_PWR_STATE, > > + DP_PWR_STATE_BANDGAP_TPLL_LANE, > > + DP_PWR_STATE_MASK); > > + } > > + > > + return IRQ_HANDLED; > > +} > > + > >