mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Neil Armstrong <neil.armstrong@linaro.org>
To: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>,
	Andrzej Hajda <andrzej.hajda@intel.com>,
	Robert Foss <rfoss@kernel.org>,
	Laurent Pinchart <Laurent.pinchart@ideasonboard.com>,
	Jonas Karlman <jonas@kwiboo.se>,
	Jernej Skrabec <jernej.skrabec@gmail.com>,
	Luca Ceresoli <luca.ceresoli@bootlin.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>,
	Jonathan Corbet <corbet@lwn.net>,
	Shuah Khan <skhan@linuxfoundation.org>,
	Randy Dunlap <rdunlap@infradead.org>,
	Jessica Zhang <jesszhan0024@gmail.com>,
	Konrad Dybcio <konradybcio@kernel.org>,
	Val Packett <val@packett.cool>
Cc: dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org,
	linux-doc@vger.kernel.org
Subject: Re: [PATCH RFC 9/9] drm/panel: novatek-nt35950: use the atomic state to select the mode
Date: Tue, 13 Oct 2026 14:51:29 +0200	[thread overview]
Message-ID: <f05474f8-89a0-4ad7-bf00-58b9a12770f8@linaro.org> (raw)
In-Reply-To: <20261008-panel-atomic-state-v1-9-b157fddb8de1@oss.qualcomm.com>

On 10/8/26 15:14, Dmitry Baryshkov wrote:
> The compression, scaler and display output settings of the NT35950
> depend on the mode being set. As the panel has no access to the
> configuration being committed, the driver saves the connector pointer in
> get_modes() and then dereferences connector->state->crtc->state from the
> prepare() callback. This is done without holding the modeset locks and
> it reads the current state of the objects rather than the state being
> committed. Moreover, if none of the panel modes match, the driver
> silently programs the panel for the first mode instead of rejecting the
> configuration.
> 
> Subclass the panel state to store the parameters of the selected mode.
> Look the mode up in atomic_check(), failing the commit if the panel
> doesn't support it, and use the stored parameters in atomic_prepare().
> Switch unprepare() to its atomic variant too, so that the driver uses a
> single set of callbacks.
> 
> Assisted-by: LLM
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
>   drivers/gpu/drm/panel/panel-novatek-nt35950.c | 159 +++++++++++++++++++-------
>   1 file changed, 117 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/gpu/drm/panel/panel-novatek-nt35950.c b/drivers/gpu/drm/panel/panel-novatek-nt35950.c
> index 94aa6489d99f..f5161f11b1f5 100644
> --- a/drivers/gpu/drm/panel/panel-novatek-nt35950.c
> +++ b/drivers/gpu/drm/panel/panel-novatek-nt35950.c
> @@ -11,12 +11,15 @@
>   #include <linux/of.h>
>   #include <linux/of_graph.h>
>   #include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
>   
> +#include <drm/drm_atomic.h>
>   #include <drm/drm_connector.h>
>   #include <drm/drm_crtc.h>
>   #include <drm/drm_mipi_dsi.h>
>   #include <drm/drm_modes.h>
>   #include <drm/drm_panel.h>
> +#include <drm/drm_print.h>
>   
>   #define MCS_CMD_MAUCCTR			0xf0 /* Manufacturer command enable */
>   #define MCS_PARAM_SCALER_FUNCTION	0x58 /* Scale-up function */
> @@ -51,16 +54,19 @@
>   
>   struct nt35950 {
>   	struct drm_panel panel;
> -	struct drm_connector *connector;
>   	struct mipi_dsi_device *dsi[2];
>   	struct regulator_bulk_data vregs[NT35950_VREG_MAX];
>   	struct gpio_desc *reset_gpio;
>   	const struct nt35950_panel_desc *desc;
>   
> -	int cur_mode;
>   	u8 last_page;
>   };
>   
> +struct nt35950_state {
> +	struct drm_panel_state base;
> +	const struct nt35950_panel_mode *mode_data;
> +};
> +
>   struct nt35950_panel_mode {
>   	const struct drm_display_mode mode;
>   
> @@ -88,6 +94,9 @@ static inline struct nt35950 *to_nt35950(struct drm_panel *panel)
>   	return container_of(panel, struct nt35950, panel);
>   }
>   
> +#define to_nt35950_state(panel_state) \
> +	container_of_const(panel_state, struct nt35950_state, base)
> +
>   static void nt35950_reset(struct nt35950 *nt)
>   {
>   	gpiod_set_value_cansleep(nt->reset_gpio, 1);
> @@ -212,61 +221,37 @@ static void nt35950_inject_black_image(struct mipi_dsi_multi_context *dsi_ctx)
>   
>   /*
>    * nt35950_set_dispout - Set Display Output register parameters
> - * @nt:    Main driver structure
>    * @dsi_ctx: context for mipi_dsi functions
> + * @mode_data: Parameters of the mode being set
>    */
>   static void nt35950_set_dispout(struct mipi_dsi_multi_context *dsi_ctx,
> -				struct nt35950 *nt)
> +				const struct nt35950_panel_mode *mode_data)
>   {
>   	u8 cmd_dispout[] = { MCS_PARAM_DISP_OUTPUT_CTRL, 0x00 };
> -	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
>   
> -	if (mode_data[nt->cur_mode].is_video_mode)
> +	if (mode_data->is_video_mode)
>   		cmd_dispout[1] |= MCS_DISP_OUT_VIDEO_MODE;
> -	if (mode_data[nt->cur_mode].enable_sram)
> +	if (mode_data->enable_sram)
>   		cmd_dispout[1] |= MCS_DISP_OUT_SRAM_EN;
>   
>   	mipi_dsi_dcs_write_buffer_multi(dsi_ctx, cmd_dispout,
>   					ARRAY_SIZE(cmd_dispout));
>   }
>   
> -static int nt35950_get_current_mode(struct nt35950 *nt)
> -{
> -	struct drm_connector *connector = nt->connector;
> -	struct drm_crtc_state *crtc_state;
> -	int i;
> -
> -	/* Return the default (first) mode if no info available yet */
> -	if (!connector->state || !connector->state->crtc)
> -		return 0;
> -
> -	crtc_state = connector->state->crtc->state;
> -
> -	for (i = 0; i < nt->desc->num_modes; i++) {
> -		if (drm_mode_match(&crtc_state->mode,
> -				   &nt->desc->mode_data[i].mode,
> -				   DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK))
> -			return i;
> -	}
> -
> -	return 0;
> -}
> -
> -static int nt35950_on(struct nt35950 *nt)
> +static int nt35950_on(struct nt35950 *nt,
> +		      const struct nt35950_panel_mode *mode_data)
>   {
> -	const struct nt35950_panel_mode *mode_data = nt->desc->mode_data;
>   	struct mipi_dsi_device *dsi = nt->dsi[0];
>   	struct mipi_dsi_multi_context dsi_ctx = { .dsi = dsi };
>   
> -	nt->cur_mode = nt35950_get_current_mode(nt);
>   	nt->dsi[0]->mode_flags |= MIPI_DSI_MODE_LPM;
>   	nt->dsi[1]->mode_flags |= MIPI_DSI_MODE_LPM;
>   
>   	nt35950_set_cmd2_page(&dsi_ctx, nt, 0);
> -	nt35950_set_data_compression(&dsi_ctx, nt, mode_data[nt->cur_mode].compression);
> -	nt35950_set_scale_mode(&dsi_ctx, mode_data[nt->cur_mode].scaler_mode);
> -	nt35950_set_scaler(&dsi_ctx, mode_data[nt->cur_mode].scaler_on);
> -	nt35950_set_dispout(&dsi_ctx, nt);
> +	nt35950_set_data_compression(&dsi_ctx, nt, mode_data->compression);
> +	nt35950_set_scale_mode(&dsi_ctx, mode_data->scaler_mode);
> +	nt35950_set_scaler(&dsi_ctx, mode_data->scaler_on);
> +	nt35950_set_dispout(&dsi_ctx, mode_data);
>   
>   	mipi_dsi_dcs_set_tear_on_multi(&dsi_ctx, MIPI_DSI_DCS_TEAR_MODE_VBLANK);
>   	mipi_dsi_dcs_set_tear_scanline_multi(&dsi_ctx, 0);
> @@ -354,11 +339,43 @@ static int nt35950_sharp_init_vregs(struct nt35950 *nt, struct device *dev)
>   	return 0;
>   }
>   
> -static int nt35950_prepare(struct drm_panel *panel)
> +static int nt35950_atomic_check(struct drm_panel *panel,
> +				struct drm_atomic_commit *commit)
> +{
> +	struct nt35950 *nt = to_nt35950(panel);
> +	struct drm_panel_state *panel_state;
> +	struct drm_crtc_state *crtc_state;
> +	int i;
> +
> +	panel_state = drm_atomic_get_new_panel_state(commit, panel);
> +	crtc_state = drm_atomic_get_new_crtc_state_for_panel(commit, panel);
> +	if (!panel_state || !crtc_state)
> +		return -EINVAL;
> +
> +	for (i = 0; i < nt->desc->num_modes; i++) {
> +		if (drm_mode_match(&crtc_state->mode,
> +				   &nt->desc->mode_data[i].mode,
> +				   DRM_MODE_MATCH_TIMINGS | DRM_MODE_MATCH_CLOCK)) {
> +			to_nt35950_state(panel_state)->mode_data =
> +				&nt->desc->mode_data[i];
> +			return 0;
> +		}
> +	}


This looks like a boilerplate which could be moved to the drm_panel if atomic_check is
not defined.

Neil

> +
> +	return -EINVAL;
> +}
> +
> +static int nt35950_atomic_prepare(struct drm_panel *panel,
> +				  struct drm_atomic_commit *commit)
>   {
>   	struct nt35950 *nt = to_nt35950(panel);
> +	struct drm_panel_state *panel_state;
>   	int ret;
>   
> +	panel_state = drm_atomic_get_new_panel_state(commit, panel);
> +	if (!panel_state || !to_nt35950_state(panel_state)->mode_data)
> +		return -EINVAL;
> +
>   	ret = regulator_enable(nt->vregs[0].consumer);
>   	if (ret)
>   		return ret;
> @@ -380,7 +397,7 @@ static int nt35950_prepare(struct drm_panel *panel)
>   
>   	nt35950_reset(nt);
>   
> -	ret = nt35950_on(nt);
> +	ret = nt35950_on(nt, to_nt35950_state(panel_state)->mode_data);
>   
>   end:
>   	if (ret < 0) {
> @@ -391,7 +408,8 @@ static int nt35950_prepare(struct drm_panel *panel)
>   	return 0;
>   }
>   
> -static int nt35950_unprepare(struct drm_panel *panel)
> +static int nt35950_atomic_unprepare(struct drm_panel *panel,
> +				    struct drm_atomic_commit *commit)
>   {
>   	struct nt35950 *nt = to_nt35950(panel);
>   
> @@ -429,15 +447,72 @@ static int nt35950_get_modes(struct drm_panel *panel,
>   	connector->display_info.bpc = 8;
>   	connector->display_info.height_mm = nt->desc->mode_data[0].mode.height_mm;
>   	connector->display_info.width_mm = nt->desc->mode_data[0].mode.width_mm;
> -	nt->connector = connector;
>   
>   	return nt->desc->num_modes;
>   }
>   
> +static struct drm_panel_state *
> +nt35950_atomic_create_state(struct drm_panel *panel)
> +{
> +	struct nt35950_state *state;
> +
> +	state = kzalloc_obj(*state);
> +	if (!state)
> +		return ERR_PTR(-ENOMEM);
> +
> +	__drm_atomic_helper_panel_state_init(&state->base, panel);
> +
> +	return &state->base;
> +}
> +
> +static struct drm_panel_state *
> +nt35950_atomic_duplicate_state(struct drm_panel *panel)
> +{
> +	struct drm_panel_state *old_state = drm_panel_get_current_state(panel);
> +	struct nt35950_state *state;
> +
> +	if (!old_state)
> +		return NULL;
> +
> +	state = kzalloc_obj(*state);
> +	if (!state)
> +		return NULL;
> +
> +	__drm_atomic_helper_panel_duplicate_state(panel, &state->base);
> +	state->mode_data = to_nt35950_state(old_state)->mode_data;
> +
> +	return &state->base;
> +}
> +
> +static void nt35950_atomic_destroy_state(struct drm_panel *panel,
> +					 struct drm_panel_state *state)
> +{
> +	kfree(to_nt35950_state(state));
> +}
> +
> +static void nt35950_atomic_print_state(struct drm_printer *p,
> +				       const struct drm_panel_state *state)
> +{
> +	const struct nt35950_panel_mode *mode_data =
> +		to_nt35950_state(state)->mode_data;
> +
> +	if (!mode_data)
> +		return;
> +
> +	drm_printf_indent(p, 1, "compression: %02x\n", mode_data->compression);
> +	drm_printf_indent(p, 1, "scaler: %u, mode %02x\n",
> +			  mode_data->scaler_on, mode_data->scaler_mode);
> +}
> +
>   static const struct drm_panel_funcs nt35950_panel_funcs = {
> -	.prepare = nt35950_prepare,
> -	.unprepare = nt35950_unprepare,
> +	.atomic_check = nt35950_atomic_check,
> +	.atomic_prepare = nt35950_atomic_prepare,
> +	.atomic_unprepare = nt35950_atomic_unprepare,
>   	.get_modes = nt35950_get_modes,
> +	.atomic_create_state = nt35950_atomic_create_state,
> +	.atomic_duplicate_state = nt35950_atomic_duplicate_state,
> +	.atomic_destroy_state = nt35950_atomic_destroy_state,
> +	.atomic_print_state = nt35950_atomic_print_state,
>   };
>   
>   static int nt35950_probe(struct mipi_dsi_device *dsi)
> 


  reply	other threads:[~2026-10-13 12:51 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-08 13:14 [PATCH RFC 0/9] drm/panel: add atomic state Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 1/9] drm/bridge: terminate the lines of the bridge state dump Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 2/9] drm/bridge: add atomic_print_state callback Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 3/9] drm/bridge: drop the bridge pointer from drm_bridge_state Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 4/9] drm/panel: add atomic state Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 5/9] drm/panel: add atomic_check callback Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 6/9] drm/panel: factor out the panel follower notifications Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 7/9] drm/panel: add atomic variants of the enable and disable callbacks Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 8/9] drm/panel: add atomic_print_state callback Dmitry Baryshkov
2026-10-08 13:14 ` [PATCH RFC 9/9] drm/panel: novatek-nt35950: use the atomic state to select the mode Dmitry Baryshkov
2026-10-13 12:51   ` Neil Armstrong [this message]
2026-10-11 20:43 ` [PATCH RFC 0/9] drm/panel: add atomic state Val Packett
2026-10-12  6:40 ` Maxime Ripard
2026-10-13  7:51   ` Dmitry Baryshkov
2026-10-13  8:37     ` Maxime Ripard
2026-10-13 11:57       ` Dmitry Baryshkov
2026-10-13 13:47         ` Luca Ceresoli

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=f05474f8-89a0-4ad7-bf00-58b9a12770f8@linaro.org \
    --to=neil.armstrong@linaro.org \
    --cc=Laurent.pinchart@ideasonboard.com \
    --cc=airlied@gmail.com \
    --cc=andrzej.hajda@intel.com \
    --cc=corbet@lwn.net \
    --cc=dmitry.baryshkov@oss.qualcomm.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=jernej.skrabec@gmail.com \
    --cc=jesszhan0024@gmail.com \
    --cc=jonas@kwiboo.se \
    --cc=konradybcio@kernel.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=luca.ceresoli@bootlin.com \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=rdunlap@infradead.org \
    --cc=rfoss@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=skhan@linuxfoundation.org \
    --cc=tzimmermann@suse.de \
    --cc=val@packett.cool \
    /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

all inboxes | Powered by JetHome®