Message ID | 20231213195125.212923-8-knaerzche@gmail.com |
---|---|
State | New |
Headers |
Return-Path: <linux-kernel-owner@vger.kernel.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:7300:3b04:b0:fb:cd0c:d3e with SMTP id c4csp8030494dys; Wed, 13 Dec 2023 11:52:08 -0800 (PST) X-Google-Smtp-Source: AGHT+IEqdcaMfxrs3tB/eu36uVOJazlX31xAM8YPmIERNUm2zGQaol9T7rcJxiQoo2EEmXjVt47z X-Received: by 2002:a05:6a00:cd3:b0:6ce:2e39:97d5 with SMTP id b19-20020a056a000cd300b006ce2e3997d5mr11109820pfv.38.1702497128605; Wed, 13 Dec 2023 11:52:08 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1702497128; cv=none; d=google.com; s=arc-20160816; b=Hrpue1BG7YaQIPAoYFwPrH2krxWTk19lcloi2q6x257MCMWuOHTWZFPGo1Yh0fvbv5 37h21SPeQ4I/YB+83MJnRTo+JviOQvD6yaN6Kce2tbm0B0McovmTwz5EcqCX09tLhQbo eqIPUa0myaw0MyZLT9h6F0+k2x1HI7nroejotGeHL3WbEsmasqNauKkMSoq2JR94P9Zo /FxgxyhBQH9q4RTFLEHssF4lwpqcXTVM+d5JvgpW2jMt26VVAA0GV1R3/qj8cTwWcJMH XSpG4uKWCo7kEDGaC//4eJSmjAHAr6RYniv3r6qB8h/arSorAqZVPZ7JpvwvrpzKqrhn 00DA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=eBLnvjeKj80rmtJP//IlHjPdLy+lYfLLyByJrZj8/N8=; fh=nzMX5Jolb3GculGR3f1Z1ReHOqdNjcdazDY5R3X+7M0=; b=wfGS55RnZ4s+ejgLY9xsyVEO6brFCwTqEmqtHUjyaFOAMay/9elgVyaeNACAzBZOei ealbpscYSwY/lOGgAj2y9KIJXkAJM51vzzZAE8shwUTeC5XinRUfO2rqgi5LrBoRZ27F /0USgg/niBuNZ+0Py0VN+mYmO0NEnYMMP2Wx3gJ+PhPkJhC7kEox+4DSg4RqSXufCHPI MukyeG/n50Zr8Zd/0SpbKASv+LfSX2o2AgBxjUl+pz3xX7iSDdXaH4qDfzIDwb6LCZmS Uy2IbJLC9c2olt4YW7fFQE78h80dB4N3FxRXtIgZMEoubCBfgkcc7OmzpFDgNnRkWSlx lcYg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=bxCQ3H14; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:1 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from morse.vger.email (morse.vger.email. [2620:137:e000::3:1]) by mx.google.com with ESMTPS id s63-20020a632c42000000b005c6eb3502bcsi9760767pgs.408.2023.12.13.11.52.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 11:52:08 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:1 as permitted sender) client-ip=2620:137:e000::3:1; Authentication-Results: mx.google.com; dkim=pass header.i=@gmail.com header.s=20230601 header.b=bxCQ3H14; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:1 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by morse.vger.email (Postfix) with ESMTP id E4730826EE8A; Wed, 13 Dec 2023 11:52:04 -0800 (PST) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.11 at morse.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1442315AbjLMTvv (ORCPT <rfc822;dexuan.linux@gmail.com> + 99 others); Wed, 13 Dec 2023 14:51:51 -0500 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:54554 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1379033AbjLMTv2 (ORCPT <rfc822;linux-kernel@vger.kernel.org>); Wed, 13 Dec 2023 14:51:28 -0500 Received: from mail-wm1-x329.google.com (mail-wm1-x329.google.com [IPv6:2a00:1450:4864:20::329]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 0AE62E0; Wed, 13 Dec 2023 11:51:33 -0800 (PST) Received: by mail-wm1-x329.google.com with SMTP id 5b1f17b1804b1-40c3984f0cdso59661675e9.1; Wed, 13 Dec 2023 11:51:33 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1702497092; x=1703101892; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=eBLnvjeKj80rmtJP//IlHjPdLy+lYfLLyByJrZj8/N8=; b=bxCQ3H14MTiM05nMM/WiVrgsXSWtYx1RAvZZC2hq/x+gIFZf0Vr4RclKkeb+rXrYpU JYXiGXaIDobNSSxBvJl+2rX+PWQ5SBTLSrgxIDFdKl1W0IifoJcSSvPXDJuBDttjvB5I q9ohyknymFvMoceBIfUPlpib3Z1lIljS3ukpZ695iL3gscXhrRyEZuXhWaBAtg84SiXM MluQ3h5psi5zOjVhqFYskmmE/XVA1BscapXac2OikKxmlKLVdXGj+Fkgv9SMK9vLY3n+ 0i7SvX7CY4boa748dvyrXZqzOyrrIjaTl0S0AQWJMPqQoURsdWZR4GyRKQhAoTgqq8kQ nXTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1702497092; x=1703101892; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=eBLnvjeKj80rmtJP//IlHjPdLy+lYfLLyByJrZj8/N8=; b=uQE7sS6eDAWRDb686PJqdcsKHNiGe8cDJCo6Wpdwi4gU5y7+BAZXDcT5yySs8II/2S HAUa0v0ccYzdwf867wDxBUWwFwTsyi/zPTf6dlnU9g3yehEqBRK/npCHc3wXcJm+ByWP f6AbktpNPDl9JZJEEMQr73JI2bqpT6mGKYjuyYiiIuB5dUbQhheMjJlEyGvdRdf/zpVx o6zR/x+O4cAgStDxt60Ylxr5HJbQr1A0CdzmhH23XjppBCuLcvIc9sXYer+9p1HVnZLp Ol7acq1uYGBcJe7b2iYpAyUgdv6G977KhWUf2CRUh5a5QZo7a0lFWc/kNFQtHD+ngEgc MQaQ== X-Gm-Message-State: AOJu0Yx9633+t1wmiUsIlGzjXKeUQ833xc8IMKSh30KDVR4jLq9UvIDF dmBGmOIwvWtXKXQXXHNYmw== X-Received: by 2002:a05:600c:46c6:b0:40c:2ba6:809 with SMTP id q6-20020a05600c46c600b0040c2ba60809mr4457221wmo.157.1702497091606; Wed, 13 Dec 2023 11:51:31 -0800 (PST) Received: from U4.lan ([2a02:810b:f40:4300:92dc:8b1c:e01c:b93c]) by smtp.gmail.com with ESMTPSA id fm14-20020a05600c0c0e00b00407b93d8085sm24050698wmb.27.2023.12.13.11.51.31 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 13 Dec 2023 11:51:31 -0800 (PST) From: Alex Bee <knaerzche@gmail.com> To: Sandy Huang <hjc@rock-chips.com>, =?utf-8?q?Heiko_St=C3=BCbner?= <heiko@sntech.de>, Andy Yan <andyshrk@163.com>, Rob Herring <robh+dt@kernel.org>, Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>, Conor Dooley <conor+dt@kernel.org>, Maarten Lankhorst <maarten.lankhorst@linux.intel.com>, Maxime Ripard <mripard@kernel.org>, Thomas Zimmermann <tzimmermann@suse.de> Cc: David Airlie <airlied@gmail.com>, Daniel Vetter <daniel@ffwll.ch>, devicetree@vger.kernel.org, linux-arm-kernel@lists.infradead.org, linux-rockchip@lists.infradead.org, linux-kernel@vger.kernel.org, Alex Bee <knaerzche@gmail.com> Subject: [PATCH 07/11] drm/rockchip: inno_hdmi: Add basic mode validation Date: Wed, 13 Dec 2023 20:51:21 +0100 Message-ID: <20231213195125.212923-8-knaerzche@gmail.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20231213195125.212923-1-knaerzche@gmail.com> References: <20231213195125.212923-1-knaerzche@gmail.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-0.6 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,SPF_HELO_NONE, SPF_PASS,T_SCC_BODY_TEXT_LINE autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on morse.vger.email Precedence: bulk List-ID: <linux-kernel.vger.kernel.org> X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (morse.vger.email [0.0.0.0]); Wed, 13 Dec 2023 11:52:05 -0800 (PST) X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1785197629458529688 X-GMAIL-MSGID: 1785197629458529688 |
Series |
Add HDMI support for RK3128
|
|
Commit Message
Alex Bee
Dec. 13, 2023, 7:51 p.m. UTC
As per TRM this controller supports pixelclocks starting from 25 MHz. The
maximum supported pixelclocks are defined by the phy configurations we
have. Also it can't support modes that require doubled clocks.
If there is a phy reference clock we can additionally validate against
VESA DMT's recommendations.
Those checks are added to the mode_valid hook of the connector and
encoder's mode_fixup hook.
Signed-off-by: Alex Bee <knaerzche@gmail.com>
---
drivers/gpu/drm/rockchip/inno_hdmi.c | 38 ++++++++++++++++++++++++++--
1 file changed, 36 insertions(+), 2 deletions(-)
Comments
Hi, On Wed, Dec 13, 2023 at 08:51:21PM +0100, Alex Bee wrote: > As per TRM this controller supports pixelclocks starting from 25 MHz. The > maximum supported pixelclocks are defined by the phy configurations we > have. Also it can't support modes that require doubled clocks. > If there is a phy reference clock we can additionally validate against > VESA DMT's recommendations. > Those checks are added to the mode_valid hook of the connector and > encoder's mode_fixup hook. > > Signed-off-by: Alex Bee <knaerzche@gmail.com> > --- > drivers/gpu/drm/rockchip/inno_hdmi.c | 38 ++++++++++++++++++++++++++-- > 1 file changed, 36 insertions(+), 2 deletions(-) > > diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c > index f7f0bec725f9..2f839ff31c1c 100644 > --- a/drivers/gpu/drm/rockchip/inno_hdmi.c > +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c > @@ -38,6 +38,8 @@ struct inno_hdmi_variant { > struct inno_hdmi_phy_config *default_phy_config; > }; > > +#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U > + > struct hdmi_data_info { > int vic; > bool sink_has_audio; > @@ -572,6 +574,34 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, > return 0; > } > > +static enum drm_mode_status inno_hdmi_mode_valid(struct inno_hdmi *hdmi, > + struct drm_display_mode *mode) > +{ So, mode_valid is only called to filter out the modes retrieved by get_modes, but it won't be called when userspace programs a mode. That's atomic_check's job. So you probably want to create a shared function between atomic_check and mode_valid, and call it from both places (or call mode_valid from atomic_check). > + /* No support for double-clock modes */ > + if (mode->flags & DRM_MODE_FLAG_DBLCLK) > + return MODE_BAD; > + > + unsigned int mpixelclk = mode->clock * 1000; Variables should be declared at the top of the function. > + if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK) > + return MODE_CLOCK_LOW; You probably want to check the max TMDS clock too? > + if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0) > + return MODE_CLOCK_HIGH; > + > + if (hdmi->refclk) { > + long refclk = clk_round_rate(hdmi->refclk, mpixelclk); > + unsigned int max_tolerance = mpixelclk / 5000; > + > + /* Vesa DMT standard mentions +/- 0.5% max tolerance */ > + if (abs(refclk - mpixelclk) > max_tolerance || > + mpixelclk - refclk > max_tolerance; > + return MODE_NOCLOCK; You should use abs_diff here. abs() will get confused by the unsigned vs signed comparison. > + } > + > + return MODE_OK; > +} > + > static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder, > struct drm_display_mode *mode, > struct drm_display_mode *adj_mode) > @@ -602,7 +632,9 @@ static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, > const struct drm_display_mode *mode, > struct drm_display_mode *adj_mode) > { > - return true; > + struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); > + > + return inno_hdmi_mode_valid(hdmi, adj_mode) == MODE_OK; > } Why do you call mode_valid in mode_fixup? Maxime
Hi Maxime, Am 14.12.23 um 09:05 schrieb Maxime Ripard: > Hi, > > On Wed, Dec 13, 2023 at 08:51:21PM +0100, Alex Bee wrote: >> As per TRM this controller supports pixelclocks starting from 25 MHz. The >> maximum supported pixelclocks are defined by the phy configurations we >> have. Also it can't support modes that require doubled clocks. >> If there is a phy reference clock we can additionally validate against >> VESA DMT's recommendations. >> Those checks are added to the mode_valid hook of the connector and >> encoder's mode_fixup hook. >> >> Signed-off-by: Alex Bee <knaerzche@gmail.com> >> --- >> drivers/gpu/drm/rockchip/inno_hdmi.c | 38 ++++++++++++++++++++++++++-- >> 1 file changed, 36 insertions(+), 2 deletions(-) >> >> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c >> index f7f0bec725f9..2f839ff31c1c 100644 >> --- a/drivers/gpu/drm/rockchip/inno_hdmi.c >> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c >> @@ -38,6 +38,8 @@ struct inno_hdmi_variant { >> struct inno_hdmi_phy_config *default_phy_config; >> }; >> >> +#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U >> + >> struct hdmi_data_info { >> int vic; >> bool sink_has_audio; >> @@ -572,6 +574,34 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, >> return 0; >> } >> >> +static enum drm_mode_status inno_hdmi_mode_valid(struct inno_hdmi *hdmi, >> + struct drm_display_mode *mode) >> +{ > So, mode_valid is only called to filter out the modes retrieved by > get_modes, but it won't be called when userspace programs a mode. That's > atomic_check's job. > > So you probably want to create a shared function between atomic_check > and mode_valid, and call it from both places (or call mode_valid from > atomic_check). This actually _is_ a shared function called in inno_hdmi_connector_mode_valid and inno_hdmi_encoder_mode_fixup. Yes, I probably should use it in atomic_check _also_. > >> + /* No support for double-clock modes */ >> + if (mode->flags & DRM_MODE_FLAG_DBLCLK) >> + return MODE_BAD; >> + >> + unsigned int mpixelclk = mode->clock * 1000; > Variables should be declared at the top of the function. Oookay ... can move them. >> + if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK) >> + return MODE_CLOCK_LOW; > You probably want to check the max TMDS clock too? Not sure what you mean here. For the currently supported formats of this driver (rgb only) tmds clock and pixel clock are always the same. > >> + if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0) >> + return MODE_CLOCK_HIGH; >> + >> + if (hdmi->refclk) { >> + long refclk = clk_round_rate(hdmi->refclk, mpixelclk); >> + unsigned int max_tolerance = mpixelclk / 5000; >> + >> + /* Vesa DMT standard mentions +/- 0.5% max tolerance */ >> + if (abs(refclk - mpixelclk) > max_tolerance || >> + mpixelclk - refclk > max_tolerance; >> + return MODE_NOCLOCK; > You should use abs_diff here. abs() will get confused by the unsigned vs > signed comparison. Ack. > >> + } >> + >> + return MODE_OK; >> +} >> + >> static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder, >> struct drm_display_mode *mode, >> struct drm_display_mode *adj_mode) >> @@ -602,7 +632,9 @@ static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, >> const struct drm_display_mode *mode, >> struct drm_display_mode *adj_mode) >> { >> - return true; >> + struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); >> + >> + return inno_hdmi_mode_valid(hdmi, adj_mode) == MODE_OK; >> } > Why do you call mode_valid in mode_fixup? I'm calling the shared function you asked me to introduce (inno_hdmi_connector_mode_valid != inno_mode_valid) Alex > > Maxime
On Thu, Dec 14, 2023 at 12:17:39PM +0100, Alex Bee wrote: > Hi Maxime, > > Am 14.12.23 um 09:05 schrieb Maxime Ripard: > > Hi, > > > > On Wed, Dec 13, 2023 at 08:51:21PM +0100, Alex Bee wrote: > > > As per TRM this controller supports pixelclocks starting from 25 MHz. The > > > maximum supported pixelclocks are defined by the phy configurations we > > > have. Also it can't support modes that require doubled clocks. > > > If there is a phy reference clock we can additionally validate against > > > VESA DMT's recommendations. > > > Those checks are added to the mode_valid hook of the connector and > > > encoder's mode_fixup hook. > > > > > > Signed-off-by: Alex Bee <knaerzche@gmail.com> > > > --- > > > drivers/gpu/drm/rockchip/inno_hdmi.c | 38 ++++++++++++++++++++++++++-- > > > 1 file changed, 36 insertions(+), 2 deletions(-) > > > > > > diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c > > > index f7f0bec725f9..2f839ff31c1c 100644 > > > --- a/drivers/gpu/drm/rockchip/inno_hdmi.c > > > +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c > > > @@ -38,6 +38,8 @@ struct inno_hdmi_variant { > > > struct inno_hdmi_phy_config *default_phy_config; > > > }; > > > +#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U > > > + > > > struct hdmi_data_info { > > > int vic; > > > bool sink_has_audio; > > > @@ -572,6 +574,34 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, > > > return 0; > > > } > > > +static enum drm_mode_status inno_hdmi_mode_valid(struct inno_hdmi *hdmi, > > > + struct drm_display_mode *mode) > > > +{ > > So, mode_valid is only called to filter out the modes retrieved by > > get_modes, but it won't be called when userspace programs a mode. That's > > atomic_check's job. > > > > So you probably want to create a shared function between atomic_check > > and mode_valid, and call it from both places (or call mode_valid from > > atomic_check). > > This actually _is_ a shared function called in > inno_hdmi_connector_mode_valid and inno_hdmi_encoder_mode_fixup. Yes, I > probably should use it in atomic_check _also_. Yeah, I saw that later and forgot to rephrase. > > > + /* No support for double-clock modes */ > > > + if (mode->flags & DRM_MODE_FLAG_DBLCLK) > > > + return MODE_BAD; > > > + > > > + unsigned int mpixelclk = mode->clock * 1000; > > Variables should be declared at the top of the function. > Oookay ... can move them. > > > + if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK) > > > + return MODE_CLOCK_LOW; > > You probably want to check the max TMDS clock too? > > Not sure what you mean here. For the currently supported formats of this > driver (rgb only) tmds clock and pixel clock are always the same. I mean that your controller has a maximum TMDS rate it supports too (probably something like 340MHz). You should also filter out the modes that have a pixel clock higher than the one you can reach. > > > @@ -602,7 +632,9 @@ static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, > > > const struct drm_display_mode *mode, > > > struct drm_display_mode *adj_mode) > > > { > > > - return true; > > > + struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); > > > + > > > + return inno_hdmi_mode_valid(hdmi, adj_mode) == MODE_OK; > > > } > > Why do you call mode_valid in mode_fixup? > > I'm calling the shared function you asked me to introduce > (inno_hdmi_connector_mode_valid != inno_mode_valid) That's the mode_fixup part that I'm focused on :) mode_fixup is the legacy function to adjust the mode to the controller capabilities. It's optional, and you're not adjusting anything here, just testing the same thing mode_valid did. mode_valid has been superseeded by atomic_check anyway, so just drop mode_valid and use your function in atomic_check like we discussed. Maxime
Am 14.12.23 um 12:40 schrieb Maxime Ripard: > On Thu, Dec 14, 2023 at 12:17:39PM +0100, Alex Bee wrote: >> Hi Maxime, >> >> Am 14.12.23 um 09:05 schrieb Maxime Ripard: >>> Hi, >>> >>> On Wed, Dec 13, 2023 at 08:51:21PM +0100, Alex Bee wrote: >>>> As per TRM this controller supports pixelclocks starting from 25 MHz. The >>>> maximum supported pixelclocks are defined by the phy configurations we >>>> have. Also it can't support modes that require doubled clocks. >>>> If there is a phy reference clock we can additionally validate against >>>> VESA DMT's recommendations. >>>> Those checks are added to the mode_valid hook of the connector and >>>> encoder's mode_fixup hook. >>>> >>>> Signed-off-by: Alex Bee <knaerzche@gmail.com> >>>> --- >>>> drivers/gpu/drm/rockchip/inno_hdmi.c | 38 ++++++++++++++++++++++++++-- >>>> 1 file changed, 36 insertions(+), 2 deletions(-) >>>> >>>> diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c >>>> index f7f0bec725f9..2f839ff31c1c 100644 >>>> --- a/drivers/gpu/drm/rockchip/inno_hdmi.c >>>> +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c >>>> @@ -38,6 +38,8 @@ struct inno_hdmi_variant { >>>> struct inno_hdmi_phy_config *default_phy_config; >>>> }; >>>> +#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U >>>> + >>>> struct hdmi_data_info { >>>> int vic; >>>> bool sink_has_audio; >>>> @@ -572,6 +574,34 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, >>>> return 0; >>>> } >>>> +static enum drm_mode_status inno_hdmi_mode_valid(struct inno_hdmi *hdmi, >>>> + struct drm_display_mode *mode) >>>> +{ >>> So, mode_valid is only called to filter out the modes retrieved by >>> get_modes, but it won't be called when userspace programs a mode. That's >>> atomic_check's job. >>> >>> So you probably want to create a shared function between atomic_check >>> and mode_valid, and call it from both places (or call mode_valid from >>> atomic_check). >> This actually _is_ a shared function called in >> inno_hdmi_connector_mode_valid and inno_hdmi_encoder_mode_fixup. Yes, I >> probably should use it in atomic_check _also_. > Yeah, I saw that later and forgot to rephrase. > >>>> + /* No support for double-clock modes */ >>>> + if (mode->flags & DRM_MODE_FLAG_DBLCLK) >>>> + return MODE_BAD; >>>> + >>>> + unsigned int mpixelclk = mode->clock * 1000; >>> Variables should be declared at the top of the function. >> Oookay ... can move them. >>>> + if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK) >>>> + return MODE_CLOCK_LOW; >>> You probably want to check the max TMDS clock too? >> Not sure what you mean here. For the currently supported formats of this >> driver (rgb only) tmds clock and pixel clock are always the same. > I mean that your controller has a maximum TMDS rate it supports too > (probably something like 340MHz). You should also filter out the modes > that have a pixel clock higher than the one you can reach. Yes it does have it and it is defined by the phy configurations that do exist. The mode is validated against those exactly below this statement. (See commit message, btw.) > >>>> @@ -602,7 +632,9 @@ static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, >>>> const struct drm_display_mode *mode, >>>> struct drm_display_mode *adj_mode) >>>> { >>>> - return true; >>>> + struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); >>>> + >>>> + return inno_hdmi_mode_valid(hdmi, adj_mode) == MODE_OK; >>>> } >>> Why do you call mode_valid in mode_fixup? >> I'm calling the shared function you asked me to introduce >> (inno_hdmi_connector_mode_valid != inno_mode_valid) > That's the mode_fixup part that I'm focused on :) > > mode_fixup is the legacy function to adjust the mode to the controller > capabilities. It's optional, and you're not adjusting anything here, > just testing the same thing mode_valid did. > > mode_valid has been superseeded by atomic_check anyway, so just drop > mode_valid and use your function in atomic_check like we discussed. OK. I just read that mode_fixup won't be called at all if atomic_check exists. I will drop here and call in atomic_check only. Alex > Maxime
diff --git a/drivers/gpu/drm/rockchip/inno_hdmi.c b/drivers/gpu/drm/rockchip/inno_hdmi.c index f7f0bec725f9..2f839ff31c1c 100644 --- a/drivers/gpu/drm/rockchip/inno_hdmi.c +++ b/drivers/gpu/drm/rockchip/inno_hdmi.c @@ -38,6 +38,8 @@ struct inno_hdmi_variant { struct inno_hdmi_phy_config *default_phy_config; }; +#define INNO_HDMI_MIN_TMDS_CLOCK 25000000U + struct hdmi_data_info { int vic; bool sink_has_audio; @@ -572,6 +574,34 @@ static int inno_hdmi_setup(struct inno_hdmi *hdmi, return 0; } +static enum drm_mode_status inno_hdmi_mode_valid(struct inno_hdmi *hdmi, + struct drm_display_mode *mode) +{ + /* No support for double-clock modes */ + if (mode->flags & DRM_MODE_FLAG_DBLCLK) + return MODE_BAD; + + unsigned int mpixelclk = mode->clock * 1000; + + if (mpixelclk < INNO_HDMI_MIN_TMDS_CLOCK) + return MODE_CLOCK_LOW; + + if (inno_hdmi_find_phy_config(hdmi, mpixelclk) < 0) + return MODE_CLOCK_HIGH; + + if (hdmi->refclk) { + long refclk = clk_round_rate(hdmi->refclk, mpixelclk); + unsigned int max_tolerance = mpixelclk / 5000; + + /* Vesa DMT standard mentions +/- 0.5% max tolerance */ + if (abs(refclk - mpixelclk) > max_tolerance || + mpixelclk - refclk > max_tolerance) + return MODE_NOCLOCK; + } + + return MODE_OK; +} + static void inno_hdmi_encoder_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adj_mode) @@ -602,7 +632,9 @@ static bool inno_hdmi_encoder_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adj_mode) { - return true; + struct inno_hdmi *hdmi = encoder_to_inno_hdmi(encoder); + + return inno_hdmi_mode_valid(hdmi, adj_mode) == MODE_OK; } static int @@ -659,7 +691,9 @@ static enum drm_mode_status inno_hdmi_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - return MODE_OK; + struct inno_hdmi *hdmi = connector_to_inno_hdmi(connector); + + return inno_hdmi_mode_valid(hdmi, mode); } static int