From nobody Mon Jun 8 04:24:37 2026 Received: from mail-01.1984.is (mail-01.1984.is [185.112.145.69]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 94C3A202997 for ; Tue, 2 Jun 2026 12:45:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.112.145.69 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780404335; cv=none; b=rkTgosRzQQdCOQxQc5siw9nVUBzRNDTSxIvC0qbk44Kqwj7aL7aIu22i9C3bSB3SZqdBfHVnxnwfKd761xt0px4Bx/ZLNQonI4OzYvsUwemseYHkHULZR9ZmmbQLCJpLz6OxXAbro3ZMzM3+0w4UtBil8p85PZyrtuFk7Lv/ARk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1780404335; c=relaxed/simple; bh=Y+O5bsQRQ+m2MSj5yoMlXOJXue/45ulnPOHP6E35psA=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version:Content-Type; b=bCYg6R4hzFQMcPP5ONQabcmGIf1iL6VSa1P8q11W8E6il7mu34OajnK4u/fKD84p+xLAtyq/BrQ/iTfrX4INlrapyetWnxgEojBCH5nGNOTzBk7n93pUpu1HdjSA7LwOWYPA9eXs+2JL6nEA8piw7wRgr1qazb6fhdPLZ1fFrtM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=berkoc.com; spf=pass smtp.mailfrom=berkoc.com; dkim=pass (2048-bit key) header.d=berkoc.com header.i=@berkoc.com header.b=QxOZ/v80; dkim=pass (2048-bit key) header.d=berkoc.com header.i=@berkoc.com header.b=UmxQNmXB; arc=none smtp.client-ip=185.112.145.69 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=quarantine dis=none) header.from=berkoc.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=berkoc.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=berkoc.com header.i=@berkoc.com header.b="QxOZ/v80"; dkim=pass (2048-bit key) header.d=berkoc.com header.i=@berkoc.com header.b="UmxQNmXB" DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=berkoc.com; s=1984; h=Content-Transfer-Encoding:Content-Type:MIME-Version:Message-ID:Date :Subject:Cc:To:From:Sender:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: In-Reply-To:References:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=MkohThdmCBghVJ6kKfUL75hAjcPs15At7pwTQ+W5scU=; b=QxOZ/v80h2FY3gjZYxe4/leLgq /GOQxTfc74VDpo8uzy593GCn1jzZKXViBRg6dkOTOFhk9+XBb7WZCCMdwYe/YgWa4FyDTg2yzbMYk X9Y+JGnfzaFLFamghF3AjKnBrdyhSeFcCXeZDwCX4cCj1r3wSy8JCsx4g0SlJ89nPcIPJgLmfPZtQ aXtCrn3regzWFAxF7XkjWHEImH2IYMSb+6OC4DmybJ869pT5r/g1LuO51h3fxeqNCVBd39EniD6AC aEGYSIUmTuEuyFTBN87gnHxvZfURMwqrApWAgB/4BMp6xd5MTcK1Z1goo2IhGtfNUVY8gGzDQQIb0 +Nm2zsHg==; Received: from localhost by mail-01.1984.is with utf8esmtp (Exim 4.96) (envelope-from ) id 1wUNxR-000OaN-0T; Tue, 02 Jun 2026 12:11:05 +0000 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=berkoc.com; i=@berkoc.com; q=dns/txt; s=me; t=1780402238; h=message-id : date : subject : cc : to : from : sender : reply-to; bh=MkohThdmCBghVJ6kKfUL75hAjcPs15At7pwTQ+W5scU=; b=UmxQNmXBXDWXcQ/lmer7OU43IVz9ispEClvSVvsDyBL3f3mYbcb23iFBZWmA7VYX6bnH1 5CPuPMigJ20uktlwqprAm3mke74YF1ORXeyAW+Yw9PaKryLx/NDlmazsHbZDOxFhsoQ/uy+ oVWwzrj/IZilKUM96bHBBClX2oUlX7oxsa3mYciOn0O7YivehbetAr0ysTBeUbpj12THmov 2xRhniOqriyVqf4wMuCHIRa0k3Y26gBxT9XwS4G+5GY7kLRHQMYKyJlhHduw91NLxoIq3Ok 3ROnt2eT+MpCMv9iS4D6r3xLKJVvox5TQ/hZ6wQnV9uKNYn3WUwjCqemckhw== From: Berkant Koc To: airlied@redhat.com Cc: tzimmermann@suse.de, jfalempe@redhat.com, maarten.lankhorst@linux.intel.com, mripard@kernel.org, airlied@gmail.com, simona@ffwll.ch, sam@ravnborg.org, dri-devel@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH v2] drm/mgag200: validate pixel clock against PLL range in mode_valid Date: Tue, 02 Jun 2026 14:10:38 +0200 Message-ID: <1780402238.22b24b6f590c@berkoc.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Mailer: git-format-patch via Skynet X-Spam-Score: -0.2 (/) X-Authenticated-User: me@berkoc.com X-Sender-Address: me@berkoc.com Content-Type: text/plain; charset="utf-8" The per-model pixpllc_atomic_check helpers for G200WB, G200EH, G200ER, G200EV, G200EH3 and G200EW3 initialise m, n, p and s to zero and only update them when the inner search loop finds a candidate whose delta to the requested clock improves on the previous best. They return 0 unconditionally afterwards. If the requested pixel clock falls outside the model's VCO window so that the outer guard skips every iteration, no candidate is recorded and atomic_update later programs pixpllc->m - 1 (unsigned underflow) into PIXPLLCM/N/P. The result is a corrupted output clock on those models. Following Thomas Zimmermann's suggestion, reject such modes earlier in the pipeline rather than inside atomic_check. Add an optional pixpllc_mode_valid callback to struct mgag200_device_funcs and dispatch it from the shared mgag200_crtc_helper_mode_valid. Each affected helper provides an implementation that walks the same divider space as its atomic_check counterpart and returns MODE_CLOCK_RANGE when no candidate falls within the same 0.5% (5/1000) tolerance that g200se has always enforced. Models without the callback (G200, G200SE, G200EH5) keep their current behaviour. Filtering at mode_valid time means the userspace mode list never contains modes the PLL cannot synthesise, so atomic_check no longer has to defend against an empty search loop and atomic_update is never reached with zeroed pixpllc values. Tested with static analysis against the linux-7.1-rc4 tree; runtime confirmation on physical Matrox hardware welcome. checkpatch.pl --strict clean. Suggested-by: Thomas Zimmermann Signed-off-by: Berkant Koc Assisted-by: Claude:claude-opus-4-7 berkoc-pipeline --- drivers/gpu/drm/mgag200/mgag200_drv.h | 8 ++++ drivers/gpu/drm/mgag200/mgag200_g200eh.c | 41 +++++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_g200eh3.c | 38 +++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_g200er.c | 50 +++++++++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_g200ev.c | 42 +++++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_g200ew3.c | 45 ++++++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_g200wb.c | 41 +++++++++++++++++++ drivers/gpu/drm/mgag200/mgag200_mode.c | 13 ++++++ 8 files changed, 278 insertions(+) diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag20= 0/mgag200_drv.h index a875c4bf8c..3eae8756cc 100644 --- a/drivers/gpu/drm/mgag200/mgag200_drv.h +++ b/drivers/gpu/drm/mgag200/mgag200_drv.h @@ -248,6 +248,14 @@ struct mgag200_device_info { } =20 struct mgag200_device_funcs { + /* + * Optional. Validate that @mode's pixel clock falls within the + * model's PIXPLLC window. Called from mgag200_crtc_helper_mode_valid + * so that out-of-range modes are filtered before atomic_check. + */ + enum drm_mode_status (*pixpllc_mode_valid)(struct drm_crtc *crtc, + const struct drm_display_mode *mode); + /* * Validate that the given state can be programmed into PIXPLLC. On * success, the calculated parameters should be stored in the CRTC's diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh.c b/drivers/gpu/drm/mga= g200/mgag200_g200eh.c index d2aa931f57..189bb1671e 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200eh.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200eh.c @@ -40,6 +40,46 @@ void mgag200_g200eh_init_registers(struct mga_device *md= ev) * PIXPLLC */ =20 +static enum drm_mode_status mgag200_g200eh_pixpllc_mode_valid(struct drm_c= rtc *crtc, + const struct drm_display_mode *mode) +{ + static const unsigned int vcomax =3D 800000; + static const unsigned int vcomin =3D 400000; + static const unsigned int pllreffreq =3D 33333; + + long clock =3D mode->clock; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int computed; + + if (clock <=3D 0) + return MODE_CLOCK_LOW; + + delta =3D 0xffffffff; + permitteddelta =3D clock * 5 / 1000; + + for (testp =3D 16; testp > 0; testp >>=3D 1) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testm =3D 1; testm < 33; testm++) { + for (testn =3D 17; testn < 257; testn++) { + computed =3D (pllreffreq * testn) / (testm * testp); + if (computed > clock) + tmpdelta =3D computed - clock; + else + tmpdelta =3D clock - computed; + if (tmpdelta < delta) + delta =3D tmpdelta; + } + } + } + + return (delta > permitteddelta) ? MODE_CLOCK_RANGE : MODE_OK; +} + static int mgag200_g200eh_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { @@ -230,6 +270,7 @@ static const struct mgag200_device_info mgag200_g200eh_= device_info =3D MGAG200_DEVICE_INFO_INIT(2048, 2048, 37500, false, 1, 0, false); =20 static const struct mgag200_device_funcs mgag200_g200eh_device_funcs =3D { + .pixpllc_mode_valid =3D mgag200_g200eh_pixpllc_mode_valid, .pixpllc_atomic_check =3D mgag200_g200eh_pixpllc_atomic_check, .pixpllc_atomic_update =3D mgag200_g200eh_pixpllc_atomic_update, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c b/drivers/gpu/drm/mg= ag200/mgag200_g200eh3.c index 7bea7a728f..621e4efb47 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200eh3.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200eh3.c @@ -15,6 +15,43 @@ * PIXPLLC */ =20 +static enum drm_mode_status mgag200_g200eh3_pixpllc_mode_valid(struct drm_= crtc *crtc, + const struct drm_display_mode *mode) +{ + static const unsigned int vcomax =3D 3000000; + static const unsigned int vcomin =3D 1500000; + static const unsigned int pllreffreq =3D 25000; + + long clock =3D mode->clock; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testm, testn; + unsigned int computed; + + if (clock <=3D 0) + return MODE_CLOCK_LOW; + + delta =3D 0xffffffff; + permitteddelta =3D clock * 5 / 1000; + + for (testm =3D 150; testm >=3D 6; testm--) { + if (clock * testm > vcomax) + continue; + if (clock * testm < vcomin) + continue; + for (testn =3D 120; testn >=3D 60; testn--) { + computed =3D (pllreffreq * testn) / testm; + if (computed > clock) + tmpdelta =3D computed - clock; + else + tmpdelta =3D clock - computed; + if (tmpdelta < delta) + delta =3D tmpdelta; + } + } + + return (delta > permitteddelta) ? MODE_CLOCK_RANGE : MODE_OK; +} + static int mgag200_g200eh3_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { @@ -134,6 +171,7 @@ static const struct mgag200_device_info mgag200_g200eh3= _device_info =3D MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, false, 1, 0, false); =20 static const struct mgag200_device_funcs mgag200_g200eh3_device_funcs =3D { + .pixpllc_mode_valid =3D mgag200_g200eh3_pixpllc_mode_valid, .pixpllc_atomic_check =3D mgag200_g200eh3_pixpllc_atomic_check, .pixpllc_atomic_update =3D mgag200_g200eh_pixpllc_atomic_update, // same = as G200EH }; diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mga= g200/mgag200_g200er.c index 8fa8fe943a..d030094ae4 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200er.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c @@ -57,6 +57,55 @@ static void mgag200_g200er_reset_tagfifo(struct mga_devi= ce *mdev) * PIXPLLC */ =20 +static enum drm_mode_status mgag200_g200er_pixpllc_mode_valid(struct drm_c= rtc *crtc, + const struct drm_display_mode *mode) +{ + static const unsigned int vcomax =3D 1488000; + static const unsigned int vcomin =3D 1056000; + static const unsigned int pllreffreq =3D 48000; + static const unsigned int m_div_val[] =3D { 1, 2, 4, 8 }; + + long clock =3D mode->clock; + unsigned int delta, tmpdelta, permitteddelta; + int testr, testn, testm, testo; + unsigned int computed, vco; + + if (clock <=3D 0) + return MODE_CLOCK_LOW; + + delta =3D 0xffffffff; + permitteddelta =3D clock * 5 / 1000; + + for (testr =3D 0; testr < 4; testr++) { + if (delta =3D=3D 0) + break; + for (testn =3D 5; testn < 129; testn++) { + if (delta =3D=3D 0) + break; + for (testm =3D 3; testm >=3D 0; testm--) { + if (delta =3D=3D 0) + break; + for (testo =3D 5; testo < 33; testo++) { + vco =3D pllreffreq * (testn + 1) / (testr + 1); + if (vco < vcomin) + continue; + if (vco > vcomax) + continue; + computed =3D vco / (m_div_val[testm] * (testo + 1)); + if (computed > clock) + tmpdelta =3D computed - clock; + else + tmpdelta =3D clock - computed; + if (tmpdelta < delta) + delta =3D tmpdelta; + } + } + } + } + + return (delta > permitteddelta) ? MODE_CLOCK_RANGE : MODE_OK; +} + static int mgag200_g200er_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { @@ -267,6 +316,7 @@ static const struct mgag200_device_info mgag200_g200er_= device_info =3D MGAG200_DEVICE_INFO_INIT(2048, 2048, 55000, false, 1, 0, false); =20 static const struct mgag200_device_funcs mgag200_g200er_device_funcs =3D { + .pixpllc_mode_valid =3D mgag200_g200er_pixpllc_mode_valid, .pixpllc_atomic_check =3D mgag200_g200er_pixpllc_atomic_check, .pixpllc_atomic_update =3D mgag200_g200er_pixpllc_atomic_update, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mga= g200/mgag200_g200ev.c index 3fadbeb10a..8286e4d6ad 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200ev.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c @@ -46,6 +46,47 @@ static void mgag200_g200ev_set_hiprilvl(struct mga_devic= e *mdev) * PIXPLLC */ =20 +static enum drm_mode_status mgag200_g200ev_pixpllc_mode_valid(struct drm_c= rtc *crtc, + const struct drm_display_mode *mode) +{ + static const unsigned int vcomax =3D 550000; + static const unsigned int vcomin =3D 150000; + static const unsigned int pllreffreq =3D 50000; + + long clock =3D mode->clock; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int computed; + + if (clock <=3D 0) + return MODE_CLOCK_LOW; + + delta =3D 0xffffffff; + permitteddelta =3D clock * 5 / 1000; + + for (testp =3D 16; testp > 0; testp--) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testn =3D 1; testn < 257; testn++) { + for (testm =3D 1; testm < 17; testm++) { + computed =3D (pllreffreq * testn) / + (testm * testp); + if (computed > clock) + tmpdelta =3D computed - clock; + else + tmpdelta =3D clock - computed; + if (tmpdelta < delta) + delta =3D tmpdelta; + } + } + } + + return (delta > permitteddelta) ? MODE_CLOCK_RANGE : MODE_OK; +} + static int mgag200_g200ev_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { @@ -268,6 +309,7 @@ static const struct mgag200_device_info mgag200_g200ev_= device_info =3D MGAG200_DEVICE_INFO_INIT(2048, 2048, 32700, false, 0, 1, false); =20 static const struct mgag200_device_funcs mgag200_g200ev_device_funcs =3D { + .pixpllc_mode_valid =3D mgag200_g200ev_pixpllc_mode_valid, .pixpllc_atomic_check =3D mgag200_g200ev_pixpllc_atomic_check, .pixpllc_atomic_update =3D mgag200_g200ev_pixpllc_atomic_update, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c b/drivers/gpu/drm/mg= ag200/mgag200_g200ew3.c index e387a455ea..8db95f4d13 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200ew3.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200ew3.c @@ -22,6 +22,50 @@ static void mgag200_g200ew3_init_registers(struct mga_de= vice *mdev) * PIXPLLC */ =20 +static enum drm_mode_status mgag200_g200ew3_pixpllc_mode_valid(struct drm_= crtc *crtc, + const struct drm_display_mode *mode) +{ + static const unsigned int vcomax =3D 800000; + static const unsigned int vcomin =3D 400000; + static const unsigned int pllreffreq =3D 25000; + + long clock =3D mode->clock; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn, testp2; + unsigned int computed; + + if (clock <=3D 0) + return MODE_CLOCK_LOW; + + delta =3D 0xffffffff; + permitteddelta =3D clock * 5 / 1000; + + for (testp =3D 1; testp < 8; testp++) { + for (testp2 =3D 1; testp2 < 8; testp2++) { + if (testp < testp2) + continue; + if ((clock * testp * testp2) > vcomax) + continue; + if ((clock * testp * testp2) < vcomin) + continue; + for (testm =3D 1; testm < 26; testm++) { + for (testn =3D 32; testn < 2048; testn++) { + computed =3D (pllreffreq * testn) / + (testm * testp * testp2); + if (computed > clock) + tmpdelta =3D computed - clock; + else + tmpdelta =3D clock - computed; + if (tmpdelta < delta) + delta =3D tmpdelta; + } + } + } + } + + return (delta > permitteddelta) ? MODE_CLOCK_RANGE : MODE_OK; +} + static int mgag200_g200ew3_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { @@ -143,6 +187,7 @@ static const struct mgag200_device_info mgag200_g200ew3= _device_info =3D MGAG200_DEVICE_INFO_INIT(2048, 2048, 0, true, 0, 1, false); =20 static const struct mgag200_device_funcs mgag200_g200ew3_device_funcs =3D { + .pixpllc_mode_valid =3D mgag200_g200ew3_pixpllc_mode_valid, .pixpllc_atomic_check =3D mgag200_g200ew3_pixpllc_atomic_check, .pixpllc_atomic_update =3D mgag200_g200wb_pixpllc_atomic_update, // same = as G200WB }; diff --git a/drivers/gpu/drm/mgag200/mgag200_g200wb.c b/drivers/gpu/drm/mga= g200/mgag200_g200wb.c index d847fa8ded..63f9909a99 100644 --- a/drivers/gpu/drm/mgag200/mgag200_g200wb.c +++ b/drivers/gpu/drm/mgag200/mgag200_g200wb.c @@ -38,6 +38,46 @@ void mgag200_g200wb_init_registers(struct mga_device *md= ev) * PIXPLLC */ =20 +static enum drm_mode_status mgag200_g200wb_pixpllc_mode_valid(struct drm_c= rtc *crtc, + const struct drm_display_mode *mode) +{ + static const unsigned int vcomax =3D 550000; + static const unsigned int vcomin =3D 150000; + static const unsigned int pllreffreq =3D 48000; + + long clock =3D mode->clock; + unsigned int delta, tmpdelta, permitteddelta; + unsigned int testp, testm, testn; + unsigned int computed; + + if (clock <=3D 0) + return MODE_CLOCK_LOW; + + delta =3D 0xffffffff; + permitteddelta =3D clock * 5 / 1000; + + for (testp =3D 1; testp < 9; testp++) { + if (clock * testp > vcomax) + continue; + if (clock * testp < vcomin) + continue; + + for (testm =3D 1; testm < 17; testm++) { + for (testn =3D 1; testn < 151; testn++) { + computed =3D (pllreffreq * testn) / (testm * testp); + if (computed > clock) + tmpdelta =3D computed - clock; + else + tmpdelta =3D clock - computed; + if (tmpdelta < delta) + delta =3D tmpdelta; + } + } + } + + return (delta > permitteddelta) ? MODE_CLOCK_RANGE : MODE_OK; +} + static int mgag200_g200wb_pixpllc_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state) { @@ -277,6 +317,7 @@ static const struct mgag200_device_info mgag200_g200wb_= device_info =3D MGAG200_DEVICE_INFO_INIT(1280, 1024, 31877, true, 0, 1, false); =20 static const struct mgag200_device_funcs mgag200_g200wb_device_funcs =3D { + .pixpllc_mode_valid =3D mgag200_g200wb_pixpllc_mode_valid, .pixpllc_atomic_check =3D mgag200_g200wb_pixpllc_atomic_check, .pixpllc_atomic_update =3D mgag200_g200wb_pixpllc_atomic_update, }; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag2= 00/mgag200_mode.c index 8894a063b1..ed82171a84 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -583,6 +583,8 @@ enum drm_mode_status mgag200_crtc_helper_mode_valid(str= uct drm_crtc *crtc, { struct mga_device *mdev =3D to_mga_device(crtc->dev); const struct mgag200_device_info *info =3D mdev->info; + const struct mgag200_device_funcs *funcs =3D mdev->funcs; + enum drm_mode_status status; =20 /* * Some devices have additional limits on the size of the @@ -605,6 +607,17 @@ enum drm_mode_status mgag200_crtc_helper_mode_valid(st= ruct drm_crtc *crtc, return MODE_BAD; } =20 + /* + * Reject modes whose pixel clock falls outside the per-model PIXPLLC + * window. Without this filter, pixpllc_atomic_check can return success + * with zeroed PLL parameters and atomic_update would underflow. + */ + if (funcs->pixpllc_mode_valid) { + status =3D funcs->pixpllc_mode_valid(crtc, mode); + if (status !=3D MODE_OK) + return status; + } + return MODE_OK; } =20 --=20 2.47.3