Linux-Rockchip Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Chaoyi Chen <chaoyi.chen@rock-chips.com>
To: "Rok Markovič" <rok@kanardia.eu>
Cc: Heiko Stuebner <heiko@sntech.de>,
	Sandy Huang <hjc@rock-chips.com>,
	Andy Yan <andy.yan@rock-chips.com>,
	Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	Maxime Ripard <mripard@kernel.org>,
	Thomas Zimmermann <tzimmermann@suse.de>,
	David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>,
	dri-devel@lists.freedesktop.org, devicetree@vger.kernel.org,
	linux-arm-kernel@lists.infradead.org,
	linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org,
	Alibek Omarov <a1ba.omarov@gmail.com>
Subject: Re: [PATCH 3/4] drm/rockchip: lvds: add RK3568 support
Date: Tue, 21 Jul 2026 16:48:09 +0800	[thread overview]
Message-ID: <e2eb4217-e4d3-4650-915c-e36f4355168e@rock-chips.com> (raw)
In-Reply-To: <3a5685992b5e335f2ebf50ee740b8a36@kanardia.eu>

On 7/21/2026 4:31 PM, Rok Markovič wrote:
> 20.7.2026 3:54, je Chaoyi Chen napisal
>> Hello Rok,
>>
>> Thank you for your patch. Please see the comment below:
>>
>> On 7/17/2026 8:00 PM, Rok Markovic wrote:
>>> The RK3568 LVDS transmitter has no register block of its own. It is
>>> driven entirely through the GRF and re-uses the MIPI DSI0 D-PHY in
>>> PHY_MODE_LVDS, which phy-rockchip-inno-dsidphy already supports.
>>>
>>> Based on Alibek Omarov's earlier posting [1], with the changes below.
>>>
>>> Power the D-PHY from the encoder enable path rather than from probe.
>>> phy_power_on() runs the phy driver's whole LVDS bring-up: PLL and
>>> bandgap power-on, a settle, PLL mode select, then a reset pulse of the
>>> serializer and the lane enables. None of that is safe at probe time -
>>> the GRF has not yet switched the block to LVDS mode (LVDS0_MODE_EN is
>>> set from rk3568_lvds_poweron(), i.e. the enable path) and the VOP is
>>> not driving dclk. The serializer is clocked from dclk and latches its
>>> state coming out of that reset, so it comes up dead and stays dead.
>>> The failure is silent: every register in the phy, the GRF and the VOP
>>> reads back exactly as on a working system while the lanes sit at
>>> common mode and never toggle.
>>>
>>
>> Could you please confirm this? Based on my earlier tests, after PHY
>> poweron, re-disabling and re-enabling the GRF did not reproduce the
>> issue you described.
> 
> I can confirm that this is not working but I testedwith writing registers
> manually with devmem and I could make something wrong.
> 
> Should I leave it this way or should I move this back to probe and try to
> make it work in enable by some trick (disabel/enable GRF)?
> 

Just out of curiosity. I think it's fine as it is now :)


>>
>>> Program RK3568_LVDS0_DCLK_INV_SEL from the CRTC state's bus_flags so
>>> the panel's declared pixelclk-active is honoured on the LVDS block as
>>> well as on the VOP pin polarity. Both have to agree with the edge the
>>> panel samples on.
>>>
>>> Re-assert RK3568_LVDS0_P2S_EN in rk3568_lvds_poweron().
>>> rk3568_lvds_poweroff() clears MODE_EN and P2S_EN together, so setting
>>> P2S_EN once at probe would leave the parallel-to-serial converter off
>>> after the first disable/enable cycle.
>>>
>>> Use regmap_write() rather than regmap_update_bits() for the GRF. These
>>> registers are write-masked - the upper 16 bits select which of the
>>> lower 16 a write may change - so there is nothing to preserve and no
>>> reason to read first. Passing a FIELD_PREP_WM16() value as both the
>>> mask and the value of an update_bits() applies the masking twice and
>>> only works by accident.
>>>
>>> Between the two, nothing needs programming at probe at all: the GRF is
>>> written entirely from the enable path, so px30_lvds_probe() is left
>>> alone rather than being refactored into a shared phy helper.
>>>
>>> [1] https://lore.kernel.org/all/20230119184807.171132-1-a1ba.omarov@gmail.com/
>>>
>>> Co-developed-by: Alibek Omarov <a1ba.omarov@gmail.com>
>>> Signed-off-by: Alibek Omarov <a1ba.omarov@gmail.com>
>>> Signed-off-by: Rok Markovic <rok@kanardia.eu>
>>> Assisted-by: Claude:claude-opus-4-8
>>> ---
>>>  drivers/gpu/drm/rockchip/rockchip_lvds.c | 161 +++++++++++++++++++++++
>>>  drivers/gpu/drm/rockchip/rockchip_lvds.h |  10 ++
>>>  2 files changed, 171 insertions(+)
>>>
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.c b/drivers/gpu/drm/rockchip/rockchip_lvds.c
>>> index 95fa0a9..f45d04a 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_lvds.c
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.c
>>> @@ -435,6 +435,133 @@ static void px30_lvds_encoder_disable(struct drm_encoder *encoder)
>>>      drm_panel_unprepare(lvds->panel);
>>>  }
>>>
>>> +static int rk3568_lvds_poweron(struct rockchip_lvds *lvds)
>>> +{
>>> +    int ret;
>>> +
>>> +    ret = clk_enable(lvds->pclk);
>>> +    if (ret < 0) {
>>> +        DRM_DEV_ERROR(lvds->dev, "failed to enable lvds pclk %d\n", ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = pm_runtime_get_sync(lvds->dev);
>>> +    if (ret < 0) {
>>> +        DRM_DEV_ERROR(lvds->dev, "failed to get pm runtime: %d\n", ret);
>>> +        clk_disable(lvds->pclk);
>>> +        return ret;
>>> +    }
>>> +
>>> +    /*
>>> +     * Enable LVDS mode and the parallel-to-serial converter. These are
>>> +     * write-masked registers, so a plain write only touches the bits named
>>> +     * here; there is nothing to preserve and no need to read first.
>>> +     */
>>
>> I think this comment is redundant. We all know this is a common design
>> on Rockchip platform, right? :)
>>
> 
> Done
> 
>>> +    return regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
>>> +                RK3568_LVDS0_MODE_EN(1) |
>>> +                RK3568_LVDS0_P2S_EN(1));
>>> +}
>>> +
>>> +static void rk3568_lvds_poweroff(struct rockchip_lvds *lvds)
>>> +{
>>> +    regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
>>> +             RK3568_LVDS0_MODE_EN(0) | RK3568_LVDS0_P2S_EN(0));
>>> +
>>> +    pm_runtime_put(lvds->dev);
>>> +    clk_disable(lvds->pclk);
>>> +}
>>> +
>>> +static int rk3568_lvds_grf_config(struct drm_encoder *encoder,
>>> +                  struct drm_display_mode *mode)
>>> +{
>>> +    struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
>>> +    struct rockchip_crtc_state *s =
>>> +        to_rockchip_crtc_state(encoder->crtc->state);
>>> +    bool negedge = !!(s->bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE);
>>> +
>>> +    if (lvds->output != DISPLAY_OUTPUT_LVDS) {
>>> +        DRM_DEV_ERROR(lvds->dev, "Unsupported display output %d\n",
>>> +                  lvds->output);
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    /*
>>> +     * The LVDS block has its own dclk inversion select, separate from the
>>> +     * VOP's pin polarity. Both have to agree with what the panel samples on.
>>> +     */
>>> +    regmap_write(lvds->grf, RK3568_GRF_VO_CON2,
>>> +             RK3568_LVDS0_DCLK_INV_SEL(negedge));
>>> +
>>> +    /* Set format */
>>> +    return regmap_write(lvds->grf, RK3568_GRF_VO_CON0,
>>> +                RK3568_LVDS0_SELECT(lvds->format) |
>>> +                RK3568_LVDS0_MSBSEL(1));
>>> +}
>>
>> I think rk3568_lvds_poweron() and rk3568_lvds_grf_config() can be merged
>> to reduce extra register operations.
> 
> Done
> 
>>> +
>>> +static void rk3568_lvds_encoder_enable(struct drm_encoder *encoder)
>>> +{
>>> +    struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
>>> +    struct drm_display_mode *mode = &encoder->crtc->state->adjusted_mode;
>>> +    int ret;
>>> +
>>> +    drm_panel_prepare(lvds->panel);
>>> +
>>> +    ret = rk3568_lvds_poweron(lvds);
>>> +    if (ret) {
>>> +        DRM_DEV_ERROR(lvds->dev, "failed to power on LVDS: %d\n", ret);
>>> +        drm_panel_unprepare(lvds->panel);
>>> +        return;
>>> +    }
>>> +
>>> +    ret = rk3568_lvds_grf_config(encoder, mode);
>>> +    if (ret) {
>>> +        DRM_DEV_ERROR(lvds->dev, "failed to configure LVDS: %d\n", ret);
>>> +        drm_panel_unprepare(lvds->panel);
>>> +        return;
>>> +    }
>>> +
>>> +    /*
>>> +     * Only now bring the D-PHY up. phy_power_on() runs the whole
>>> +     * inno_dsidphy_lvds_mode_enable() sequence - PLL and bandgap power-on,
>>> +     * a settle, PLL mode select, then a reset pulse of the serializer and
>>> +     * the lane enables. All of that has to happen with the block already
>>> +     * switched to LVDS mode in the GRF (above) and with the VOP's dclk
>>> +     * running, because the serializer is clocked from dclk and latches its
>>> +     * state out of that reset.
>>> +     *
>>> +     * Doing it at probe instead - as this driver used to - resets and
>>> +     * enables the serializer against a block that is not in LVDS mode yet
>>> +     * and has no input clock. Every register then reads back correct while
>>> +     * the lanes sit at common mode forever. Rockchip's BSP orders it this
>>> +     * way (GRF writes, then phy_set_mode + phy_power_on).
>>> +     */
>>
>> I think this comment is redundant. These are internal details of
>> phy_set_mode() and don't need to be explained here. Also, the
>> ordering requirements are quite common. You can describe them in the
>> commit message.
>>
> 
> Done
> 
>>> +    ret = phy_set_mode(lvds->dphy, PHY_MODE_LVDS);
>>> +    if (ret) {
>>> +        DRM_DEV_ERROR(lvds->dev, "failed to set phy mode: %d\n", ret);
>>> +        drm_panel_unprepare(lvds->panel);
>>> +        return;
>>> +    }
>>> +
>>> +    ret = phy_power_on(lvds->dphy);
>>> +    if (ret) {
>>> +        DRM_DEV_ERROR(lvds->dev, "failed to power on phy: %d\n", ret);
>>> +        drm_panel_unprepare(lvds->panel);
>>> +        return;
>>> +    }
>>> +
>>> +    drm_panel_enable(lvds->panel);
>>> +}
>>> +
>>> +static void rk3568_lvds_encoder_disable(struct drm_encoder *encoder)
>>> +{
>>> +    struct rockchip_lvds *lvds = encoder_to_lvds(encoder);
>>> +
>>> +    drm_panel_disable(lvds->panel);
>>> +    phy_power_off(lvds->dphy);
>>> +    rk3568_lvds_poweroff(lvds);
>>> +    drm_panel_unprepare(lvds->panel);
>>> +}
>>> +
>>>  static const
>>>  struct drm_encoder_helper_funcs rk3288_lvds_encoder_helper_funcs = {
>>>      .enable = rk3288_lvds_encoder_enable,
>>> @@ -449,6 +576,13 @@ struct drm_encoder_helper_funcs px30_lvds_encoder_helper_funcs = {
>>>      .atomic_check = rockchip_lvds_encoder_atomic_check,
>>>  };
>>>
>>> +static const
>>> +struct drm_encoder_helper_funcs rk3568_lvds_encoder_helper_funcs = {
>>> +    .enable = rk3568_lvds_encoder_enable,
>>> +    .disable = rk3568_lvds_encoder_disable,
>>> +    .atomic_check = rockchip_lvds_encoder_atomic_check,
>>> +};
>>> +
>>>  static int rk3288_lvds_probe(struct platform_device *pdev,
>>>                   struct rockchip_lvds *lvds)
>>>  {
>>> @@ -512,6 +646,22 @@ static int px30_lvds_probe(struct platform_device *pdev,
>>>      return phy_power_on(lvds->dphy);
>>>  }
>>>
>>> +static int rk3568_lvds_probe(struct platform_device *pdev,
>>> +                 struct rockchip_lvds *lvds)
>>> +{
>>> +    /*
>>> +     * Grab and init the phy, but do NOT power it on here - that is done in
>>> +     * rk3568_lvds_encoder_enable() once the GRF is in LVDS mode and dclk is
>>> +     * running. See the comment there. The GRF is not touched at probe
>>> +     * either: every bit of it is programmed from the enable path.
>>> +     */
>>
>> Please see the comments above.
> 
> Done
> 
>>
>>> +    lvds->dphy = devm_phy_get(&pdev->dev, "dphy");
>>> +    if (IS_ERR(lvds->dphy))
>>> +        return PTR_ERR(lvds->dphy);
>>> +
>>> +    return phy_init(lvds->dphy);
>>> +}
>>> +
>>>  static const struct rockchip_lvds_soc_data rk3288_lvds_data = {
>>>      .probe = rk3288_lvds_probe,
>>>      .helper_funcs = &rk3288_lvds_encoder_helper_funcs,
>>> @@ -522,6 +672,11 @@ static const struct rockchip_lvds_soc_data px30_lvds_data = {
>>>      .helper_funcs = &px30_lvds_encoder_helper_funcs,
>>>  };
>>>
>>> +static const struct rockchip_lvds_soc_data rk3568_lvds_data = {
>>> +    .probe = rk3568_lvds_probe,
>>> +    .helper_funcs = &rk3568_lvds_encoder_helper_funcs,
>>> +};
>>> +
>>>  static const struct of_device_id rockchip_lvds_dt_ids[] = {
>>>      {
>>>          .compatible = "rockchip,rk3288-lvds",
>>> @@ -531,6 +686,10 @@ static const struct of_device_id rockchip_lvds_dt_ids[] = {
>>>          .compatible = "rockchip,px30-lvds",
>>>          .data = &px30_lvds_data
>>>      },
>>> +    {
>>> +        .compatible = "rockchip,rk3568-lvds",
>>> +        .data = &rk3568_lvds_data
>>> +    },
>>>      {}
>>>  };
>>>  MODULE_DEVICE_TABLE(of, rockchip_lvds_dt_ids);
>>> @@ -601,6 +760,8 @@ static int rockchip_lvds_bind(struct device *dev, struct device *master,
>>>      encoder = &lvds->encoder.encoder;
>>>      encoder->possible_crtcs = drm_of_find_possible_crtcs(drm_dev,
>>>                                   dev->of_node);
>>> +    rockchip_drm_encoder_set_crtc_endpoint_id(&lvds->encoder,
>>> +                          dev->of_node, 0, 0);
>>>
>>>      ret = drm_simple_encoder_init(drm_dev, encoder, DRM_MODE_ENCODER_LVDS);
>>>      if (ret < 0) {
>>> diff --git a/drivers/gpu/drm/rockchip/rockchip_lvds.h b/drivers/gpu/drm/rockchip/rockchip_lvds.h
>>> index 2d92447..93d3415 100644
>>> --- a/drivers/gpu/drm/rockchip/rockchip_lvds.h
>>> +++ b/drivers/gpu/drm/rockchip/rockchip_lvds.h
>>> @@ -121,4 +121,14 @@
>>>  #define   PX30_LVDS_P2S_EN(val)            FIELD_PREP_WM16(BIT(6), (val))
>>>  #define   PX30_LVDS_VOP_SEL(val)        FIELD_PREP_WM16(BIT(1), (val))
>>>
>>> +#define RK3568_GRF_VO_CON0            0x0360
>>> +#define   RK3568_LVDS0_SELECT(val)        FIELD_PREP_WM16(GENMASK(5, 4), (val))
>>> +#define   RK3568_LVDS0_MSBSEL(val)        FIELD_PREP_WM16(BIT(3), (val))
>>> +
>>> +#define RK3568_GRF_VO_CON2            0x0368
>>> +#define   RK3568_LVDS0_DCLK_INV_SEL(val)    FIELD_PREP_WM16(BIT(9), (val))
>>> +#define   RK3568_LVDS0_DCLK_DIV2_SEL(val)    FIELD_PREP_WM16(BIT(8), (val))
>>> +#define   RK3568_LVDS0_MODE_EN(val)        FIELD_PREP_WM16(BIT(1), (val))
>>> +#define   RK3568_LVDS0_P2S_EN(val)        FIELD_PREP_WM16(BIT(0), (val))
>>> +
>>>  #endif /* _ROCKCHIP_LVDS_ */
> 
> 

-- 
Best, 
Chaoyi


_______________________________________________
Linux-rockchip mailing list
Linux-rockchip@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-rockchip

  reply	other threads:[~2026-07-21  8:48 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-17 12:00 [PATCH 0/4] drm/rockchip: add RK3568 LVDS support Rok Markovic
2026-07-17 12:00 ` [PATCH 1/4] drm/rockchip: lvds: propagate bus_flags to the CRTC state Rok Markovic
2026-07-20  1:27   ` Chaoyi Chen
2026-07-17 12:00 ` [PATCH 2/4] dt-bindings: display: rockchip,lvds: add RK3568 Rok Markovic
2026-07-17 12:00 ` [PATCH 3/4] drm/rockchip: lvds: add RK3568 support Rok Markovic
2026-07-20  1:54   ` Chaoyi Chen
2026-07-21  8:31     ` Rok Markovič
2026-07-21  8:48       ` Chaoyi Chen [this message]
2026-07-20  5:39   ` Alibek Omarov
2026-07-20 12:20     ` AW: " Jakob Loe
2026-07-17 12:00 ` [PATCH 4/4] arm64: dts: rockchip: rk356x: add LVDS node Rok Markovic

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e2eb4217-e4d3-4650-915c-e36f4355168e@rock-chips.com \
    --to=chaoyi.chen@rock-chips.com \
    --cc=a1ba.omarov@gmail.com \
    --cc=airlied@gmail.com \
    --cc=andy.yan@rock-chips.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=heiko@sntech.de \
    --cc=hjc@rock-chips.com \
    --cc=krzk+dt@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-rockchip@lists.infradead.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=robh@kernel.org \
    --cc=rok@kanardia.eu \
    --cc=simona@ffwll.ch \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox