Re: [PATCH v2 08/10] drm/simpledrm: Acquire clocks from DT device node

2021-04-15 Thread maxime
On Thu, Apr 15, 2021 at 01:02:44PM +0200, Thomas Zimmermann wrote:
> Hi
> 
> Am 15.04.21 um 11:21 schrieb Maxime Ripard:
> > Hi,
> > 
> > On Thu, Apr 15, 2021 at 09:31:01AM +0200, Thomas Zimmermann wrote:
> > > Am 08.04.21 um 10:13 schrieb Maxime Ripard:
> > > > Hi,
> > > > 
> > > > On Thu, Mar 18, 2021 at 11:29:19AM +0100, Thomas Zimmermann wrote:
> > > > > Make sure required hardware clocks are enabled while the firmware
> > > > > framebuffer is in use.
> > > > > 
> > > > > The basic code has been taken from the simplefb driver and adapted
> > > > > to DRM. Clocks are released automatically via devres helpers.
> > > > > 
> > > > > Signed-off-by: Thomas Zimmermann 
> > > > > Tested-by: nerdopolis 
> > > > 
> > > > Even though it's definitely simpler to review, merging the driver first
> > > > and then the clocks and regulators will break bisection on the platforms
> > > > that rely on them
> > > 
> > > I'd like to keep the patches separate for now, but can squash patches 6 
> > > to 8
> > > them into one before pushing them. OK?
> > 
> > Yep, that works for me :)
> > 
> > > > 
> > > > Another thing worth considering is also that both drivers will probe if
> > > > they are enabled (which is pretty likely), which is not great :)
> > > > 
> > > > I guess we should make them mutually exclusive through Kconfig
> > > 
> > > We already have several drivers in fbdev and DRM that handle the same
> > > hardware. We don't do this for any other pair, why bother now?
> > 
> > Yeah, but simplefb/simpledrm are going to be enabled pretty much
> > everywhere, as opposed to the other drivers that are more specialized.
> 
> Well, OK. But I'd like to give simpledrm preference over simplefb. There
> should be an incentive to switch to DRM.

Yeah that makes total sense :)

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v4 03/10] dt-bindings: display: Add ingenic,jz4780-dw-hdmi DT Schema

2021-09-27 Thread maxime
Hi,

On Mon, Sep 27, 2021 at 06:44:21PM +0200, H. Nikolaus Schaller wrote:
> From: Sam Ravnborg 
> 
> Add DT bindings for the hdmi driver for the Ingenic JZ4780 SoC.
> Based on .txt binding from Zubair Lutfullah Kakakhel
> 
> Signed-off-by: Sam Ravnborg 
> Signed-off-by: H. Nikolaus Schaller 
> Cc: Rob Herring 
> Cc: devicet...@vger.kernel.org
> ---
>  .../bindings/display/ingenic-jz4780-hdmi.yaml | 85 +++
>  1 file changed, 85 insertions(+)
>  create mode 100644 
> Documentation/devicetree/bindings/display/ingenic-jz4780-hdmi.yaml
> 
> diff --git 
> a/Documentation/devicetree/bindings/display/ingenic-jz4780-hdmi.yaml 
> b/Documentation/devicetree/bindings/display/ingenic-jz4780-hdmi.yaml
> new file mode 100644
> index ..5e60cdac4f63
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/display/ingenic-jz4780-hdmi.yaml
> @@ -0,0 +1,85 @@
> +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/display/ingenic-jz4780-hdmi.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Bindings for Ingenic JZ4780 HDMI Transmitter
> +
> +maintainers:
> +  - H. Nikolaus Schaller 
> +
> +description: |
> +  The HDMI Transmitter in the Ingenic JZ4780 is a Synopsys DesignWare HDMI 
> 1.4
> +  TX controller IP with accompanying PHY IP.
> +
> +allOf:
> +  - $ref: panel/panel-common.yaml#

Is it a panel though?

> +properties:
> +  compatible:
> +items:
> +  - const: ingenic,jz4780-dw-hdmi

This can just be a const, there's no need for the items

> +
> +  reg:
> +maxItems: 1
> +description: the address & size of the LCD controller registers

There's no need for that description, it's obvious enough

> +  reg-io-width:
> +const: 4

If it's fixed, why do you need it in the first place?

> +  interrupts:
> +maxItems: 1
> +description: Specifies the interrupt provided by parent

There's no need for that description, it's obvious enough

> +  clocks:
> +maxItems: 2
> +description: Clock specifiers for isrf and iahb clocks

This can be defined as

clocks:
  items:
- description: isrf
- description: iahb

A better description about what these clocks are would be nice as well

> +  clock-names:
> +items:
> +  - const: isfr

Is it isfr or isrf?

> +  - const: iahb
> +
> +  hdmi-regulator: true
> +description: Optional regulator to provide +5V at the connector

regulators need to be suffixed by -supply

You also can just provide the description, you don't need the true there

> +  ddc-i2c-bus: true

ditto

> +description: An I2C interface if the internal DDC I2C driver is not to 
> be used
> +  ports: true

If there's a single port, you don't need ports

You should also include /schemas/graph.yaml#/$defs/port-base

Maxime



signature.asc
Description: PGP signature


[PATCH v4 0/7] drm/vc4: Fix the core clock behaviour

2022-10-20 Thread maxime
Hi,

Those patches used to be part of a larger clock fixes series:
https://lore.kernel.org/linux-clk/20220715160014.2623107-1-max...@cerno.tech/

However, that series doesn't seem to be getting anywhere, so I've split out
these patches that fix a regression that has been there since 5.18 and that
prevents the 4k output from working on the RaspberryPi4.

Hopefully, we will be able to merge those patches through the DRM tree to avoid
any further disruption.

Let me know what you think,
Maxime

To: Florian Fainelli 
To: Broadcom internal kernel review list 
To: Ray Jui 
To: Scott Branden 
To: Michael Turquette 
To: Stephen Boyd 
To: Emma Anholt 
To: Maxime Ripard 
To: David Airlie 
To: Daniel Vetter 
Cc: Stefan Wahren 
Cc: linux-rpi-ker...@lists.infradead.org
Cc: linux-arm-ker...@lists.infradead.org
Cc: linux-ker...@vger.kernel.org
Cc: linux-...@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: Dom Cobley 
Signed-off-by: Maxime Ripard 

---
Changes in v4:
- Move the rpi_firmware_of_match to avoid confusion
- Link to v3: 
https://lore.kernel.org/r/20220815-rpi-fix-4k-60-v3-0-fc56729d1...@cerno.tech

Changes in v3:
- Return UINT_MAX when the firmware call fails in the _get_max_rate function
- Link to v2: 
https://lore.kernel.org/r/20220815-rpi-fix-4k-60-v2-0-983276b83...@cerno.tech

Changes in v2:
- Dropped the clock patches, made an ad-hoc function in the firmware driver
- Link to v1: 
https://lore.kernel.org/r/20220815-rpi-fix-4k-60-v1-0-c52bd642f...@cerno.tech

---
Dom Cobley (1):
  drm/vc4: hdmi: Add more checks for 4k resolutions

Maxime Ripard (6):
  firmware: raspberrypi: Introduce rpi_firmware_find_node()
  firmware: raspberrypi: Move the clock IDs to the firmware header
  firmware: raspberrypi: Provide a helper to query a clock max rate
  drm/vc4: hdmi: Fix hdmi_enable_4kp60 detection
  drm/vc4: hdmi: Rework hdmi_enable_4kp60 detection code
  drm/vc4: Make sure we don't end up with a core clock too high

 drivers/clk/bcm/clk-raspberrypi.c  | 19 ---
 drivers/firmware/raspberrypi.c | 38 ++
 drivers/gpu/drm/vc4/vc4_drv.h  | 16 +
 drivers/gpu/drm/vc4/vc4_hdmi.c | 25 +++---
 drivers/gpu/drm/vc4/vc4_hdmi.h |  8 -
 drivers/gpu/drm/vc4/vc4_hvs.c  | 26 +++
 drivers/gpu/drm/vc4/vc4_kms.c  | 13 +---
 include/soc/bcm2835/raspberrypi-firmware.h | 52 ++
 8 files changed, 147 insertions(+), 50 deletions(-)
---
base-commit: 9abf2313adc1ca1b6180c508c25f22f9395cc780
change-id: 20220815-rpi-fix-4k-60-17273650429d

Best regards,
-- 
Maxime Ripard 


[PATCH v4 2/7] firmware: raspberrypi: Move the clock IDs to the firmware header

2022-10-20 Thread maxime
We'll need the clock IDs in more drivers than just the clock driver from
now on, so let's move them in the firmware header.

Reviewed-by: Florian Fainelli 
Acked-by: Stephen Boyd 
Signed-off-by: Maxime Ripard 
---
 drivers/clk/bcm/clk-raspberrypi.c  | 19 ---
 include/soc/bcm2835/raspberrypi-firmware.h | 19 +++
 2 files changed, 19 insertions(+), 19 deletions(-)

diff --git a/drivers/clk/bcm/clk-raspberrypi.c 
b/drivers/clk/bcm/clk-raspberrypi.c
index 679f4649a7ef..ce2f93479736 100644
--- a/drivers/clk/bcm/clk-raspberrypi.c
+++ b/drivers/clk/bcm/clk-raspberrypi.c
@@ -18,25 +18,6 @@
 
 #include 
 
-enum rpi_firmware_clk_id {
-   RPI_FIRMWARE_EMMC_CLK_ID = 1,
-   RPI_FIRMWARE_UART_CLK_ID,
-   RPI_FIRMWARE_ARM_CLK_ID,
-   RPI_FIRMWARE_CORE_CLK_ID,
-   RPI_FIRMWARE_V3D_CLK_ID,
-   RPI_FIRMWARE_H264_CLK_ID,
-   RPI_FIRMWARE_ISP_CLK_ID,
-   RPI_FIRMWARE_SDRAM_CLK_ID,
-   RPI_FIRMWARE_PIXEL_CLK_ID,
-   RPI_FIRMWARE_PWM_CLK_ID,
-   RPI_FIRMWARE_HEVC_CLK_ID,
-   RPI_FIRMWARE_EMMC2_CLK_ID,
-   RPI_FIRMWARE_M2MC_CLK_ID,
-   RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
-   RPI_FIRMWARE_VEC_CLK_ID,
-   RPI_FIRMWARE_NUM_CLK_ID,
-};
-
 static char *rpi_firmware_clk_names[] = {
[RPI_FIRMWARE_EMMC_CLK_ID]  = "emmc",
[RPI_FIRMWARE_UART_CLK_ID]  = "uart",
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h 
b/include/soc/bcm2835/raspberrypi-firmware.h
index 63426082bcb9..9b1db12d013f 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -136,6 +136,25 @@ enum rpi_firmware_property_tag {
RPI_FIRMWARE_GET_DMA_CHANNELS =   0x00060001,
 };
 
+enum rpi_firmware_clk_id {
+   RPI_FIRMWARE_EMMC_CLK_ID = 1,
+   RPI_FIRMWARE_UART_CLK_ID,
+   RPI_FIRMWARE_ARM_CLK_ID,
+   RPI_FIRMWARE_CORE_CLK_ID,
+   RPI_FIRMWARE_V3D_CLK_ID,
+   RPI_FIRMWARE_H264_CLK_ID,
+   RPI_FIRMWARE_ISP_CLK_ID,
+   RPI_FIRMWARE_SDRAM_CLK_ID,
+   RPI_FIRMWARE_PIXEL_CLK_ID,
+   RPI_FIRMWARE_PWM_CLK_ID,
+   RPI_FIRMWARE_HEVC_CLK_ID,
+   RPI_FIRMWARE_EMMC2_CLK_ID,
+   RPI_FIRMWARE_M2MC_CLK_ID,
+   RPI_FIRMWARE_PIXEL_BVB_CLK_ID,
+   RPI_FIRMWARE_VEC_CLK_ID,
+   RPI_FIRMWARE_NUM_CLK_ID,
+};
+
 #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
 int rpi_firmware_property(struct rpi_firmware *fw,
  u32 tag, void *data, size_t len);

-- 
b4 0.10.1


[PATCH v4 3/7] firmware: raspberrypi: Provide a helper to query a clock max rate

2022-10-20 Thread maxime
The firmware allows to query for its clocks the operating range of a
given clock. We'll need this for some drivers (KMS, in particular) to
infer the state of some configuration options, so let's create a
function to do so.

Acked-by: Stephen Boyd 
Reviewed-by: Florian Fainelli 
Signed-off-by: Maxime Ripard 
---
 drivers/firmware/raspberrypi.c | 20 
 include/soc/bcm2835/raspberrypi-firmware.h | 26 ++
 2 files changed, 46 insertions(+)

diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
index 932a8bef22fb..b15aa6fce0e9 100644
--- a/drivers/firmware/raspberrypi.c
+++ b/drivers/firmware/raspberrypi.c
@@ -228,6 +228,26 @@ static void rpi_register_clk_driver(struct device *dev)
-1, NULL, 0);
 }
 
+unsigned int rpi_firmware_clk_get_max_rate(struct rpi_firmware *fw, unsigned 
int id)
+{
+   struct rpi_firmware_clk_rate_request msg =
+   RPI_FIRMWARE_CLK_RATE_REQUEST(id);
+   int ret;
+
+   ret = rpi_firmware_property(fw, RPI_FIRMWARE_GET_MAX_CLOCK_RATE,
+   &msg, sizeof(msg));
+   if (ret)
+   /*
+* If our firmware doesn't support that operation, or fails, we
+* assume the maximum clock rate is absolute maximum we can
+* store over our type.
+*/
+return UINT_MAX;
+
+   return le32_to_cpu(msg.rate);
+}
+EXPORT_SYMBOL_GPL(rpi_firmware_clk_get_max_rate);
+
 static void rpi_firmware_delete(struct kref *kref)
 {
struct rpi_firmware *fw = container_of(kref, struct rpi_firmware,
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h 
b/include/soc/bcm2835/raspberrypi-firmware.h
index 9b1db12d013f..ab955591cb72 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -155,12 +155,32 @@ enum rpi_firmware_clk_id {
RPI_FIRMWARE_NUM_CLK_ID,
 };
 
+/**
+ * struct rpi_firmware_clk_rate_request - Firmware Request for a rate
+ * @id:ID of the clock being queried
+ * @rate: Rate in Hertz. Set by the firmware.
+ *
+ * Used by @RPI_FIRMWARE_GET_CLOCK_RATE, @RPI_FIRMWARE_GET_CLOCK_MEASURED,
+ * @RPI_FIRMWARE_GET_MAX_CLOCK_RATE and @RPI_FIRMWARE_GET_MIN_CLOCK_RATE.
+ */
+struct rpi_firmware_clk_rate_request {
+   __le32 id;
+   __le32 rate;
+} __packed;
+
+#define RPI_FIRMWARE_CLK_RATE_REQUEST(_id) \
+   {   \
+   .id = _id,  \
+   }
+
 #if IS_ENABLED(CONFIG_RASPBERRYPI_FIRMWARE)
 int rpi_firmware_property(struct rpi_firmware *fw,
  u32 tag, void *data, size_t len);
 int rpi_firmware_property_list(struct rpi_firmware *fw,
   void *data, size_t tag_size);
 void rpi_firmware_put(struct rpi_firmware *fw);
+unsigned int rpi_firmware_clk_get_max_rate(struct rpi_firmware *fw,
+  unsigned int id);
 struct device_node *rpi_firmware_find_node(void);
 struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node);
 struct rpi_firmware *devm_rpi_firmware_get(struct device *dev,
@@ -180,6 +200,12 @@ static inline int rpi_firmware_property_list(struct 
rpi_firmware *fw,
 
 static inline void rpi_firmware_put(struct rpi_firmware *fw) { }
 
+static inline unsigned int rpi_firmware_clk_get_max_rate(struct rpi_firmware 
*fw,
+unsigned int id)
+{
+   return UINT_MAX;
+}
+
 static inline struct device_node *rpi_firmware_find_node(void)
 {
return NULL;

-- 
b4 0.10.1


[PATCH v4 1/7] firmware: raspberrypi: Introduce rpi_firmware_find_node()

2022-10-20 Thread maxime
A significant number of RaspberryPi drivers using the firmware don't
have a phandle to it, so end up scanning the device tree to find a node
with the firmware compatible.

That code is duplicated everywhere, so let's introduce a helper instead.

Signed-off-by: Maxime Ripard 
---
 drivers/firmware/raspberrypi.c | 18 --
 include/soc/bcm2835/raspberrypi-firmware.h |  7 +++
 2 files changed, 19 insertions(+), 6 deletions(-)

diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c
index 4b8978b254f9..932a8bef22fb 100644
--- a/drivers/firmware/raspberrypi.c
+++ b/drivers/firmware/raspberrypi.c
@@ -311,6 +311,18 @@ static int rpi_firmware_remove(struct platform_device 
*pdev)
return 0;
 }
 
+static const struct of_device_id rpi_firmware_of_match[] = {
+   { .compatible = "raspberrypi,bcm2835-firmware", },
+   {},
+};
+MODULE_DEVICE_TABLE(of, rpi_firmware_of_match);
+
+struct device_node *rpi_firmware_find_node(void)
+{
+   return of_find_matching_node(NULL, rpi_firmware_of_match);
+}
+EXPORT_SYMBOL_GPL(rpi_firmware_find_node);
+
 /**
  * rpi_firmware_get - Get pointer to rpi_firmware structure.
  * @firmware_node:Pointer to the firmware Device Tree node.
@@ -366,12 +378,6 @@ struct rpi_firmware *devm_rpi_firmware_get(struct device 
*dev,
 }
 EXPORT_SYMBOL_GPL(devm_rpi_firmware_get);
 
-static const struct of_device_id rpi_firmware_of_match[] = {
-   { .compatible = "raspberrypi,bcm2835-firmware", },
-   {},
-};
-MODULE_DEVICE_TABLE(of, rpi_firmware_of_match);
-
 static struct platform_driver rpi_firmware_driver = {
.driver = {
.name = "raspberrypi-firmware",
diff --git a/include/soc/bcm2835/raspberrypi-firmware.h 
b/include/soc/bcm2835/raspberrypi-firmware.h
index 811ea668c4a1..63426082bcb9 100644
--- a/include/soc/bcm2835/raspberrypi-firmware.h
+++ b/include/soc/bcm2835/raspberrypi-firmware.h
@@ -142,6 +142,7 @@ int rpi_firmware_property(struct rpi_firmware *fw,
 int rpi_firmware_property_list(struct rpi_firmware *fw,
   void *data, size_t tag_size);
 void rpi_firmware_put(struct rpi_firmware *fw);
+struct device_node *rpi_firmware_find_node(void);
 struct rpi_firmware *rpi_firmware_get(struct device_node *firmware_node);
 struct rpi_firmware *devm_rpi_firmware_get(struct device *dev,
   struct device_node *firmware_node);
@@ -159,6 +160,12 @@ static inline int rpi_firmware_property_list(struct 
rpi_firmware *fw,
 }
 
 static inline void rpi_firmware_put(struct rpi_firmware *fw) { }
+
+static inline struct device_node *rpi_firmware_find_node(void)
+{
+   return NULL;
+}
+
 static inline struct rpi_firmware *rpi_firmware_get(struct device_node 
*firmware_node)
 {
return NULL;

-- 
b4 0.10.1


[PATCH v4 4/7] drm/vc4: hdmi: Fix hdmi_enable_4kp60 detection

2022-10-20 Thread maxime
In order to support higher HDMI frequencies, users have to set the
hdmi_enable_4kp60 parameter in their config.txt file.

We were detecting this so far by calling clk_round_rate() on the core
clock with the frequency we're supposed to run at when one of those
modes is enabled. Whether or not the parameter was enabled could then be
inferred by the returned rate since the maximum clock rate reported by
the firmware was one of the side effect of setting that parameter.

However, the recent clock rework we did changed what clk_round_rate()
was returning to always return the minimum allowed, and thus this test
wasn't reliable anymore.

Let's use the new clk_get_max_rate() function to reliably determine the
maximum rate allowed on that clock and fix the 4k@60Hz output.

Fixes: e9d6cea2af1c ("clk: bcm: rpi: Run some clocks at the minimum rate 
allowed")
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 64f9feabf43e..87961d4de5aa 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -46,6 +46,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -3429,7 +3430,7 @@ static int vc4_hdmi_bind(struct device *dev, struct 
device *master, void *data)
 
if (variant->max_pixel_clock == 6) {
struct vc4_dev *vc4 = to_vc4_dev(drm);
-   long max_rate = clk_round_rate(vc4->hvs->core_clk, 55000);
+   unsigned long max_rate = 
rpi_firmware_clk_get_max_rate(vc4->hvs->core_clk);
 
if (max_rate < 55000)
vc4_hdmi->disable_4kp60 = true;

-- 
b4 0.10.1


[PATCH v4 6/7] drm/vc4: hdmi: Add more checks for 4k resolutions

2022-10-20 Thread maxime
From: Dom Cobley 

At least the 4096x2160@60Hz mode requires some overclocking that isn't
available by default, even if hdmi_enable_4kp60 is enabled.

Let's add some logic to detect whether we can satisfy the core clock
requirements for that mode, and prevent it from being used otherwise.

Signed-off-by: Dom Cobley 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_drv.h  |  6 ++
 drivers/gpu/drm/vc4/vc4_hdmi.c | 11 +--
 drivers/gpu/drm/vc4/vc4_hvs.c  |  3 +++
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 8b2b1af565f9..72a6b7151d23 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -347,6 +347,12 @@ struct vc4_hvs {
 * available.
 */
bool vc5_hdmi_enable_scrambling;
+
+   /*
+* 4096x2160@60 requires a core overclock to work, so register
+* whether that is sufficient.
+*/
+   bool vc5_hdmi_enable_4096by2160;
 };
 
 struct vc4_plane {
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index afe3daa2173e..fd3730ea976f 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -1753,6 +1753,7 @@ vc4_hdmi_sink_supports_format_bpc(const struct vc4_hdmi 
*vc4_hdmi,
 
 static enum drm_mode_status
 vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi *vc4_hdmi,
+const struct drm_display_mode *mode,
 unsigned long long clock)
 {
const struct drm_connector *connector = &vc4_hdmi->connector;
@@ -1765,6 +1766,12 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi 
*vc4_hdmi,
if (!vc4->hvs->vc5_hdmi_enable_scrambling && clock > 
HDMI_14_MAX_TMDS_CLK)
return MODE_CLOCK_HIGH;
 
+   /* 4096x2160@60 is not reliable without overclocking core */
+   if (!vc4->hvs->vc5_hdmi_enable_4096by2160 &&
+   mode->hdisplay > 3840 && mode->vdisplay >= 2160 &&
+   drm_mode_vrefresh(mode) >= 50)
+   return MODE_CLOCK_HIGH;
+
if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000))
return MODE_CLOCK_HIGH;
 
@@ -1799,7 +1806,7 @@ vc4_hdmi_encoder_compute_clock(const struct vc4_hdmi 
*vc4_hdmi,
unsigned long long clock;
 
clock = vc4_hdmi_encoder_compute_mode_clock(mode, bpc, fmt);
-   if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, clock) != MODE_OK)
+   if (vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, clock) != MODE_OK)
return -EINVAL;
 
vc4_state->tmds_char_rate = clock;
@@ -1962,7 +1969,7 @@ vc4_hdmi_encoder_mode_valid(struct drm_encoder *encoder,
 (mode->hsync_end % 2) || (mode->htotal % 2)))
return MODE_H_ILLEGAL;
 
-   return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode->clock * 1000);
+   return vc4_hdmi_encoder_clock_valid(vc4_hdmi, mode, mode->clock * 1000);
 }
 
 static const struct drm_encoder_helper_funcs vc4_hdmi_encoder_helper_funcs = {
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 300ac0b57571..a68913f76687 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -818,6 +818,9 @@ static int vc4_hvs_bind(struct device *dev, struct device 
*master, void *data)
if (max_rate >= 55000)
hvs->vc5_hdmi_enable_scrambling = true;
 
+   if (max_rate >= 6)
+   hvs->vc5_hdmi_enable_4096by2160 = true;
+
hvs->max_core_rate = max_rate;
 
ret = clk_prepare_enable(hvs->core_clk);

-- 
b4 0.10.1


[PATCH v4 7/7] drm/vc4: Make sure we don't end up with a core clock too high

2022-10-20 Thread maxime
Following the clock rate range improvements to the clock framework,
trying to set a disjoint range on a clock will now result in an error.

Thus, we can't set a minimum rate higher than the maximum reported by
the firmware, or clk_set_min_rate() will fail.

Thus we need to clamp the rate we are about to ask for to the maximum
rate possible on that clock.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_kms.c | 13 -
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c
index 4419e810103d..5c97642ed66a 100644
--- a/drivers/gpu/drm/vc4/vc4_kms.c
+++ b/drivers/gpu/drm/vc4/vc4_kms.c
@@ -396,8 +396,8 @@ static void vc4_atomic_commit_tail(struct drm_atomic_state 
*state)
if (vc4->is_vc5) {
unsigned long state_rate = max(old_hvs_state->core_clock_rate,
   new_hvs_state->core_clock_rate);
-   unsigned long core_rate = max_t(unsigned long,
-   5, state_rate);
+   unsigned long core_rate = clamp_t(unsigned long, state_rate,
+ 5, 
hvs->max_core_rate);
 
drm_dbg(dev, "Raising the core clock at %lu Hz\n", core_rate);
 
@@ -431,14 +431,17 @@ static void vc4_atomic_commit_tail(struct 
drm_atomic_state *state)
drm_atomic_helper_cleanup_planes(dev, state);
 
if (vc4->is_vc5) {
-   drm_dbg(dev, "Running the core clock at %lu Hz\n",
-   new_hvs_state->core_clock_rate);
+   unsigned long core_rate = min_t(unsigned long,
+   hvs->max_core_rate,
+   new_hvs_state->core_clock_rate);
+
+   drm_dbg(dev, "Running the core clock at %lu Hz\n", core_rate);
 
/*
 * Request a clock rate based on the current HVS
 * requirements.
 */
-   WARN_ON(clk_set_min_rate(hvs->core_clk, 
new_hvs_state->core_clock_rate));
+   WARN_ON(clk_set_min_rate(hvs->core_clk, core_rate));
 
drm_dbg(dev, "Core clock actual rate: %lu Hz\n",
clk_get_rate(hvs->core_clk));

-- 
b4 0.10.1


[PATCH v4 5/7] drm/vc4: hdmi: Rework hdmi_enable_4kp60 detection code

2022-10-20 Thread maxime
In order to support higher HDMI frequencies, users have to set the
hdmi_enable_4kp60 parameter in their config.txt file.

This will have the side-effect of raising the maximum of the core clock,
tied to the HVS, and managed by the HVS driver.

However, we are querying this in the HDMI driver by poking into the HVS
structure to get our struct clk handle.

Let's make this part of the HVS bind implementation to have all the core
clock related setup in the same place.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_drv.h  | 10 ++
 drivers/gpu/drm/vc4/vc4_hdmi.c | 15 ---
 drivers/gpu/drm/vc4/vc4_hdmi.h |  8 
 drivers/gpu/drm/vc4/vc4_hvs.c  | 23 +++
 4 files changed, 37 insertions(+), 19 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.h b/drivers/gpu/drm/vc4/vc4_drv.h
index 418a8242691f..8b2b1af565f9 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.h
+++ b/drivers/gpu/drm/vc4/vc4_drv.h
@@ -326,6 +326,8 @@ struct vc4_hvs {
 
struct clk *core_clk;
 
+   unsigned long max_core_rate;
+
/* Memory manager for CRTCs to allocate space in the display
 * list.  Units are dwords.
 */
@@ -337,6 +339,14 @@ struct vc4_hvs {
struct drm_mm_node mitchell_netravali_filter;
 
struct debugfs_regset32 regset;
+
+   /*
+* Even if HDMI0 on the RPi4 can output modes requiring a pixel
+* rate higher than 297MHz, it needs some adjustments in the
+* config.txt file to be able to do so and thus won't always be
+* available.
+*/
+   bool vc5_hdmi_enable_scrambling;
 };
 
 struct vc4_plane {
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 87961d4de5aa..afe3daa2173e 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -46,7 +46,6 @@
 #include 
 #include 
 #include 
-#include 
 #include 
 #include 
 #include 
@@ -460,6 +459,7 @@ static int vc4_hdmi_connector_detect_ctx(struct 
drm_connector *connector,
 static int vc4_hdmi_connector_get_modes(struct drm_connector *connector)
 {
struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
+   struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
int ret = 0;
struct edid *edid;
 
@@ -483,7 +483,7 @@ static int vc4_hdmi_connector_get_modes(struct 
drm_connector *connector)
ret = drm_add_edid_modes(connector, edid);
kfree(edid);
 
-   if (vc4_hdmi->disable_4kp60) {
+   if (!vc4->hvs->vc5_hdmi_enable_scrambling) {
struct drm_device *drm = connector->dev;
const struct drm_display_mode *mode;
 
@@ -1757,11 +1757,12 @@ vc4_hdmi_encoder_clock_valid(const struct vc4_hdmi 
*vc4_hdmi,
 {
const struct drm_connector *connector = &vc4_hdmi->connector;
const struct drm_display_info *info = &connector->display_info;
+   struct vc4_dev *vc4 = to_vc4_dev(connector->dev);
 
if (clock > vc4_hdmi->variant->max_pixel_clock)
return MODE_CLOCK_HIGH;
 
-   if (vc4_hdmi->disable_4kp60 && clock > HDMI_14_MAX_TMDS_CLK)
+   if (!vc4->hvs->vc5_hdmi_enable_scrambling && clock > 
HDMI_14_MAX_TMDS_CLK)
return MODE_CLOCK_HIGH;
 
if (info->max_tmds_clock && clock > (info->max_tmds_clock * 1000))
@@ -3428,14 +3429,6 @@ static int vc4_hdmi_bind(struct device *dev, struct 
device *master, void *data)
vc4_hdmi->disable_wifi_frequencies =
of_property_read_bool(dev->of_node, "wifi-2.4ghz-coexistence");
 
-   if (variant->max_pixel_clock == 6) {
-   struct vc4_dev *vc4 = to_vc4_dev(drm);
-   unsigned long max_rate = 
rpi_firmware_clk_get_max_rate(vc4->hvs->core_clk);
-
-   if (max_rate < 55000)
-   vc4_hdmi->disable_4kp60 = true;
-   }
-
ret = devm_pm_runtime_enable(dev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index db823efb2563..e3619836ca17 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -156,14 +156,6 @@ struct vc4_hdmi {
 */
bool disable_wifi_frequencies;
 
-   /*
-* Even if HDMI0 on the RPi4 can output modes requiring a pixel
-* rate higher than 297MHz, it needs some adjustments in the
-* config.txt file to be able to do so and thus won't always be
-* available.
-*/
-   bool disable_4kp60;
-
struct cec_adapter *cec_adap;
struct cec_msg cec_rx_msg;
bool cec_tx_ok;
diff --git a/drivers/gpu/drm/vc4/vc4_hvs.c b/drivers/gpu/drm/vc4/vc4_hvs.c
index 4ac9f5a2d5f9..300ac0b57571 100644
--- a/drivers/gpu/drm/vc4/vc4_hvs.c
+++ b/drivers/gpu/drm/vc4/vc4_hvs.c
@@ -28,6 +28,8 @@
 #include 
 #include 
 
+#include 
+
 #include &q

Re: [PATCH v5 12/22] drm/connector: Add a function to lookup a TV mode by its name

2022-10-20 Thread maxime
Hi Noralf,

On Wed, Oct 19, 2022 at 12:43:19PM +0200, Noralf Trønnes wrote:
> 
> 
> Den 19.10.2022 10.48, skrev Maxime Ripard:
> > On Tue, Oct 18, 2022 at 02:29:00PM +0200, Noralf Trønnes wrote:
> >>
> >>
> >> Den 18.10.2022 11.33, skrev Maxime Ripard:
> >>> On Mon, Oct 17, 2022 at 12:44:45PM +0200, Noralf Trønnes wrote:
> >>>> Den 13.10.2022 15.18, skrev Maxime Ripard:
> >>>>> As part of the command line parsing rework coming in the next patches,
> >>>>> we'll need to lookup drm_connector_tv_mode values by their name, already
> >>>>> defined in drm_tv_mode_enum_list.
> >>>>>
> >>>>> In order to avoid any code duplication, let's do a function that will
> >>>>> perform a lookup of a TV mode name and return its value.
> >>>>>
> >>>>> Signed-off-by: Maxime Ripard 
> >>>>> ---
> >>>>>  drivers/gpu/drm/drm_connector.c | 24 
> >>>>>  include/drm/drm_connector.h |  2 ++
> >>>>>  2 files changed, 26 insertions(+)
> >>>>>
> >>>>> diff --git a/drivers/gpu/drm/drm_connector.c 
> >>>>> b/drivers/gpu/drm/drm_connector.c
> >>>>> index 820f4c730b38..30611c616435 100644
> >>>>> --- a/drivers/gpu/drm/drm_connector.c
> >>>>> +++ b/drivers/gpu/drm/drm_connector.c
> >>>>> @@ -991,6 +991,30 @@ static const struct drm_prop_enum_list 
> >>>>> drm_tv_mode_enum_list[] = {
> >>>>>  };
> >>>>>  DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list)
> >>>>>  
> >>>>> +/**
> >>>>> + * drm_get_tv_mode_from_name - Translates a TV mode name into its enum 
> >>>>> value
> >>>>> + * @name: TV Mode name we want to convert
> >>>>> + * @len: Length of @name
> >>>>> + *
> >>>>> + * Translates @name into an enum drm_connector_tv_mode.
> >>>>> + *
> >>>>> + * Returns: the enum value on success, a negative errno otherwise.
> >>>>> + */
> >>>>> +int drm_get_tv_mode_from_name(const char *name, size_t len)
> >>>>
> >>>> Do we really need to pass in length here? item->name has to always be
> >>>> NUL terminated otherwise things would break elsewhere, so it shouldn't
> >>>> be necessary AFAICS.
> >>>
> >>> The only user so far is the command-line parsing code, and we might very
> >>> well have an option after the tv_mode, something like
> >>> 720x480i,tv_mode=NTSC,rotate=180
> >>>
> >>> In this case, we won't get a NULL-terminated name.
> >>
> >> My point is that item->name will always be NUL terminated so strcmp()
> >> will never look past that.
> > 
> > Right, but we don't have the guarantee that strlen(item->name) <
> > strlen(name), and we could thus just access after the end of our name
> > 
> 
> Ok, using the length limiting str funtions is the safe thing to do, so
> len needs to stay. But I don't get the 'strlen(item->name) == len'
> check. strncmp() will stop when it reaches a NUL in either string so no
> need for the length check?

Yeah, but if the cmdline is truncated, we'll pass a shorter len than
strlen(item->name), and it will consider the string as equal.

For example strncmp("NTS", "NTSC", strlen("NTS"))) == 0, while it obviously
isn't for us.

> Anyways:
> 
> Reviewed-by: Noralf Trønnes 

Thanks!
Maxime


signature.asc
Description: PGP signature


Re: [PATCH v5 20/22] drm/vc4: vec: Convert to the new TV mode property

2022-10-20 Thread maxime
Hi,

On Tue, Oct 18, 2022 at 11:28:35PM +0200, Mateusz Kwiatkowski wrote:
> W dniu 18.10.2022 o 12:00, Maxime Ripard pisze:
> > On Mon, Oct 17, 2022 at 12:31:31PM +0200, Noralf Trønnes wrote:
> >> Den 16.10.2022 20.52, skrev Mateusz Kwiatkowski:
> >>>>  static int vc4_vec_connector_get_modes(struct drm_connector *connector)
> >>>>  {
> >>>> -struct drm_connector_state *state = connector->state;
> >>>>  struct drm_display_mode *mode;
> >>>>  
> >>>> -mode = drm_mode_duplicate(connector->dev,
> >>>> -  
> >>>> vc4_vec_tv_modes[state->tv.legacy_mode].mode);
> >>>> +mode = drm_mode_analog_ntsc_480i(connector->dev);
> >>>>  if (!mode) {
> >>>>  DRM_ERROR("Failed to create a new display mode\n");
> >>>>  return -ENOMEM;
> >>>>  }
> >>>>  
> >>>> +mode->type |= DRM_MODE_TYPE_PREFERRED;
> >>>>  drm_mode_probed_add(connector, mode);
> >>>>  
> >>>> -return 1;
> >>>> +mode = drm_mode_analog_pal_576i(connector->dev);
> >>>> +if (!mode) {
> >>>> +DRM_ERROR("Failed to create a new display mode\n");
> >>>> +return -ENOMEM;
> >>>> +}
> >>>> +
> >>>> +drm_mode_probed_add(connector, mode);
> >>>> +
> >>>> +return 2;
> >>>> +}
> >>>
> >>> Referencing those previous discussions:
> >>> - 
> >>> https://lore.kernel.org/dri-devel/0255f7c6-0484-6456-350d-cf24f3fee...@tronnes.org/
> >>> - 
> >>> https://lore.kernel.org/dri-devel/c8f8015a-75da-afa8-ca7f-b2b134cac...@gmail.com/
> >>>
> >>> Unconditionally setting the 480i mode as DRM_MODE_TYPE_PREFERRED causes 
> >>> Xorg
> >>> (at least on current Raspberry Pi OS) to display garbage when
> >>> video=Composite1:PAL is specified on the command line, so I'm afraid this 
> >>> won't
> >>> do.
> >>>
> >>> As I see it, there are three viable solutions for this issue:
> >>>
> >>> a) Somehow query the video= command line option from this function, and 
> >>> set
> >>>DRM_MODE_TYPE_PREFERRED appropriately. This would break the abstraction
> >>>provided by global DRM code, but should work fine.
> >>>
> >>> b) Modify drm_helper_probe_add_cmdline_mode() so that it sets
> >>>    DRM_MODE_TYPE_PREFERRED in addition to DRM_MODE_TYPE_USERDEF. This 
> >>> seems
> >>>pretty robust, but affects the entire DRM subsystem, which may break
> >>>userspace in different ways.
> >>>
> >>>- Maybe this could be mitigated by adding some additional conditions, 
> >>> e.g.
> >>>  setting the PREFERRED flag only if no modes are already flagged as 
> >>> such
> >>>  and/or only if the cmdline mode is a named one (~= analog TV mode)
> >>>
> >>> c) Forcing userspace (Xorg / Raspberry Pi OS) to get fixed and honor the 
> >>> USERDEF
> >>>flag.
> >>>
> >>> Either way, hardcoding 480i as PREFERRED does not seem right.
> >>>
> >>
> >> My solution for this is to look at tv.mode to know which mode to mark as
> >> preferred. Maxime didn't like this since it changes things behind
> >> userspace's back. I don't see how that can cause any problems for 
> >> userspace.
> >>
> >> If userspace uses atomic and sets tv_mode, it has to know which mode to
> >> use before hand, so it doesn't look at the preferreded flag.
> >>
> >> If it uses legacy and sets tv_mode, it can end up with a stale preferred
> >> flag, but no worse than not having the flag or that ntsc is always
> >> preferred.
> >>
> >> If it doesn't change tv_mode, there's no problem, the preferred flag
> >> doesn't change.
> >
> > I don't like it because I just see no way to make this reliable. When we
> > set tv_mode, we're not only going to change the preferred flag, but also
> > the order of the modes to make the preferred mode first.
> >
> > Since we just changed the mode list

Re: [PATCH] drm/vc4: vec: Add support for PAL-60

2022-10-20 Thread maxime
On Tue, Oct 18, 2022 at 10:57:04PM +0200, Mateusz Kwiatkowski wrote:
> Hi Maxime,
> 
> W dniu 18.10.2022 o 10:31, Maxime Ripard pisze:
> > Hi,
> >
> > On Sun, Oct 16, 2022 at 09:46:49PM +0200, Mateusz Kwiatkowski wrote:
> >> @@ -308,14 +324,15 @@ static const struct vc4_vec_tv_mode 
> >> vc4_vec_tv_modes[] = {
> >>  };
> >>  
> >>  static inline const struct vc4_vec_tv_mode *
> >> -vc4_vec_tv_mode_lookup(unsigned int mode)
> >> +vc4_vec_tv_mode_lookup(unsigned int mode, u16 htotal)
> >>  {
> >>unsigned int i;
> >>  
> >>for (i = 0; i < ARRAY_SIZE(vc4_vec_tv_modes); i++) {
> >>const struct vc4_vec_tv_mode *tv_mode = &vc4_vec_tv_modes[i];
> >>  
> >> -  if (tv_mode->mode == mode)
> >> +  if (tv_mode->mode == mode &&
> >> +  tv_mode->expected_htotal == htotal)
> >>return tv_mode;
> >
> > Is there any reason we're not using the refresh rate to filter this? It
> > seems more natural to me.
> 
> Let me give you an example first.
> 
> There are actually two ways to configure PAL-60-ish mode on VC4/VEC:
> 
> a) Modeline 13.5 720 734 798 858 480 487 493 525 Interlace, standard registers
>set to VEC_CONFIG0_PAL_M_STD, custom frequency enabled and set to 
> 0x2a098acb;
>Setting the standard registers to "PAL-M" puts the VEC in true 59.94 Hz 
> mode,
>so the video timings are identical as for NTSC (or PAL-M), and the custom
>frequency makes the color subcarrier compatible with regular PAL receivers.
>This is the "true" PAL-60, thanks to the true System M timings.

That's the one I would expect, and I assume we could just do that by
selecting the 480i mode + PAL TV Mode property, right?

> a) Modeline 13.5 720 740 804 864 480 486 492 525 Interlace, standards 
> registers
>set to VEC_CONFIG0_PAL with standard frequency; This is a "fake" PAL-60 
> mode,
>the refresh rate is actually ~59.524 Hz. Most "NTSC" sets will be able to
>sync with this mode no problem, but the VEC is actually operating in its
>50 Hz mode - it's just the "premature" vertical sync signal causes it to
>output something that is similar to the 525-line system, however strictly
>speaking non-standard due to lower horizontal sync frequency.

But it's not really clear to me why we should support both.

> This comes down to the fact that:
> 
> - When VEC's standard registers are set to VEC_CONFIG0_NTSC_STD or
>   VEC_CONFIG0_PAL_M_STD, it operates in the "CCIR System M" mode, expects 
> htotal
>   to be exactly 858 pixels (and it will generate horizontal sync pulse every 
> 858
>   pixels on its own regardless of what comes out of the PV - so there will be
>   garbage on screen if you set it to anything else), and vtotal to be 525 
> lines.
>   It will not accept vtotal that's any higher (it will generate its own 
> vertical
>   sync as demanded by System M if not triggered by the PV), but it can be 
> lower
>   - resulting in modes that are non-standard, but otherwise valid.
> 
> - Likewise, when the registers are set to VEC_CONFIG0_PAL_BDGHI_STD,
>   VEC_CONFIG0_PAL_N_STD or VEC_CONFIG0_SECAM_STD (SECAM is a bit special, but
>   that's irrelevant here), it operates in the "CCIR System B/D/G/H/I/N" mode,
>   and likewise, expects htotal to be exactly 864 pixels (garbage on screen
>   otherwise), vtotal limit is 625 lines, etc.
> 
> Checking for the refresh rate would only work for standard-compliant modes and
> have the potential of completely breaking on any custom modes. Conversely,
> checking for htotal aligns perfectly with the limitations of the hardware, and
> allows the user to set any modeline that the hardware is able to output with
> any level of sanity.

OK

Thanks!
Maxime


signature.asc
Description: PGP signature


[PATCH] drm/vc4: hdmi: Fix HSM clock too low on Pi4

2022-10-21 Thread maxime
Commit ae71ab585c81 ("drm/vc4: hdmi: Enforce the minimum rate at
runtime_resume") reintroduced the call to clk_set_min_rate in an attempt
to fix the boot without a monitor connected on the RaspberryPi3.

However, that introduced a regression breaking the display output
entirely (black screen but no vblank timeout) on the Pi4.

This is due to the fact that we now have in a typical modeset at boot,
in vc4_hdmi_encoder_pre_crtc_configure(), we have a first call to
clk_set_min_rate() asking for the minimum rate of the HSM clock for our
given resolution, and then a call to pm_runtime_resume_and_get(). We
will thus execute vc4_hdmi_runtime_resume() which, since the commit
mentioned above, will call clk_set_min_rate() a second time with the
absolute minimum rate we want to enforce on the HSM clock.

We're thus effectively erasing the minimum mandated by the mode we're
trying to set. The fact that only the Pi4 is affected is due to the fact
that it uses a different clock driver that tries to minimize the HSM
clock at all time. It will thus lower the HSM clock rate to 120MHz on
the second clk_set_min_rate() call.

The Pi3 doesn't use the same driver and will not change the frequency on
the second clk_set_min_rate() call since it's still within the new
boundaries and it doesn't have the code to minimize the clock rate as
needed. So even though the boundaries are still off, the clock rate is
still the right one for our given mode, so everything works.

There is a lot of moving parts, so I couldn't find any obvious
solution:

  - Reverting the original is not an option, as that would break the Pi3
again.

  - We can't move the clk_set_min_rate() call in _pre_crtc_configure()
since because, on the Pi3, the HSM clock has the CLK_SET_RATE_GATE
flag which prevents the clock rate from being changed after it's
been enabled. Our calls to clk_set_min_rate() can change it, so they
need to be done before clk_prepare_enable().

  - We can't remove the call to clk_prepare_enable() from the
runtime_resume hook to put it into _pre_crtc_configure() either,
since we need that clock to be enabled to access the registers, and
we can't count on the fact that the display will be active in all
situations (doing any CEC operation, or listing the modes while
inactive are valid for example()).

  - We can't drop the call to clk_set_min_rate() in
_pre_crtc_configure() since we would need to still enforce the
minimum rate for a given resolution, and runtime_resume doesn't have
access to the current mode, if there's any.

  - We can't copy the TMDS character rate into vc4_hdmi and reuse it
since, because it's part of the KMS atomic state, it needs to be
protected by a mutex. Unfortunately, some functions (CEC operations,
mostly) can be reentrant (through the CEC framework) and still need
a pm_runtime_get.

However, we can work around this issue by leveraging the fact that the
clk_set_min_rate() calls set boundaries for its given struct clk, and
that each different clk_get() call will return a different instance of
struct clk. The clock framework will then aggregate the boundaries for
each struct clk instances linked to a given clock, plus its hardware
boundaries, and will use that.

We can thus get an extra HSM clock user for runtime_pm use only, and use
our different clock instances depending on the context: runtime_pm will
use its own to set the absolute minimum clock setup so that we never
lock the CPU waiting for a register access, and the modeset part will
set its requirement for the current resolution. And we let the CCF do
the coordination.

It's not an ideal solution, but it's fairly unintrusive and doesn't
really change any part of the logic so it looks like a rather safe fix.

Link: https://bugzilla.redhat.com/show_bug.cgi?id=2136234
Fixes: ae71ab585c81 ("drm/vc4: hdmi: Enforce the minimum rate at 
runtime_resume")
Reported-by: Peter Robinson 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 19 ---
 drivers/gpu/drm/vc4/vc4_hdmi.h |  5 +
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index eb3aaaca2b80..715f0fe3d8e0 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2732,9 +2732,16 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi 
*vc4_hdmi)
DRM_ERROR("Failed to get HDMI state machine clock\n");
return PTR_ERR(vc4_hdmi->hsm_clock);
}
+
vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock;
vc4_hdmi->cec_clock = vc4_hdmi->hsm_clock;
 
+   vc4_hdmi->hsm_rpm_clock = devm_clk_get(dev, "hdmi");
+   if (IS_ERR(vc4_hdmi->hsm_rpm_clock)) {
+   DRM_ERROR("Failed to get HDMI state machine clock\n&quo

[PATCH v2] drm/vc4: hdmi: Fix HSM clock too low on Pi4

2022-10-21 Thread maxime
Commit ae71ab585c81 ("drm/vc4: hdmi: Enforce the minimum rate at
runtime_resume") reintroduced the call to clk_set_min_rate in an attempt
to fix the boot without a monitor connected on the RaspberryPi3.

However, that introduced a regression breaking the display output
entirely (black screen but no vblank timeout) on the Pi4.

This is due to the fact that we now have in a typical modeset at boot,
in vc4_hdmi_encoder_pre_crtc_configure(), we have a first call to
clk_set_min_rate() asking for the minimum rate of the HSM clock for our
given resolution, and then a call to pm_runtime_resume_and_get(). We
will thus execute vc4_hdmi_runtime_resume() which, since the commit
mentioned above, will call clk_set_min_rate() a second time with the
absolute minimum rate we want to enforce on the HSM clock.

We're thus effectively erasing the minimum mandated by the mode we're
trying to set. The fact that only the Pi4 is affected is due to the fact
that it uses a different clock driver that tries to minimize the HSM
clock at all time. It will thus lower the HSM clock rate to 120MHz on
the second clk_set_min_rate() call.

The Pi3 doesn't use the same driver and will not change the frequency on
the second clk_set_min_rate() call since it's still within the new
boundaries and it doesn't have the code to minimize the clock rate as
needed. So even though the boundaries are still off, the clock rate is
still the right one for our given mode, so everything works.

There is a lot of moving parts, so I couldn't find any obvious
solution:

  - Reverting the original is not an option, as that would break the Pi3
again.

  - We can't move the clk_set_min_rate() call in _pre_crtc_configure()
since because, on the Pi3, the HSM clock has the CLK_SET_RATE_GATE
flag which prevents the clock rate from being changed after it's
been enabled. Our calls to clk_set_min_rate() can change it, so they
need to be done before clk_prepare_enable().

  - We can't remove the call to clk_prepare_enable() from the
runtime_resume hook to put it into _pre_crtc_configure() either,
since we need that clock to be enabled to access the registers, and
we can't count on the fact that the display will be active in all
situations (doing any CEC operation, or listing the modes while
inactive are valid for example()).

  - We can't drop the call to clk_set_min_rate() in
_pre_crtc_configure() since we would need to still enforce the
minimum rate for a given resolution, and runtime_resume doesn't have
access to the current mode, if there's any.

  - We can't copy the TMDS character rate into vc4_hdmi and reuse it
since, because it's part of the KMS atomic state, it needs to be
protected by a mutex. Unfortunately, some functions (CEC operations,
mostly) can be reentrant (through the CEC framework) and still need
a pm_runtime_get.

However, we can work around this issue by leveraging the fact that the
clk_set_min_rate() calls set boundaries for its given struct clk, and
that each different clk_get() call will return a different instance of
struct clk. The clock framework will then aggregate the boundaries for
each struct clk instances linked to a given clock, plus its hardware
boundaries, and will use that.

We can thus get an extra HSM clock user for runtime_pm use only, and use
our different clock instances depending on the context: runtime_pm will
use its own to set the absolute minimum clock setup so that we never
lock the CPU waiting for a register access, and the modeset part will
set its requirement for the current resolution. And we let the CCF do
the coordination.

It's not an ideal solution, but it's fairly unintrusive and doesn't
really change any part of the logic so it looks like a rather safe fix.

Link: https://bugzilla.redhat.com/show_bug.cgi?id=2136234
Fixes: ae71ab585c81 ("drm/vc4: hdmi: Enforce the minimum rate at 
runtime_resume")
Reported-by: Peter Robinson 
Signed-off-by: Maxime Ripard 

---
Changes in v2:
- Forgot one more clock to convert to the new one
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 21 +
 drivers/gpu/drm/vc4/vc4_hdmi.h |  1 +
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index eb3aaaca2b80..3519b0c23d3b 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -2732,9 +2732,16 @@ static int vc4_hdmi_init_resources(struct vc4_hdmi 
*vc4_hdmi)
DRM_ERROR("Failed to get HDMI state machine clock\n");
return PTR_ERR(vc4_hdmi->hsm_clock);
}
+
vc4_hdmi->audio_clock = vc4_hdmi->hsm_clock;
vc4_hdmi->cec_clock = vc4_hdmi->hsm_clock;
 
+   vc4_hdmi->hsm_rpm_clock = devm_clk_get(dev, "hdmi");
+   if (IS_ERR(vc4_hdmi->hsm_rpm_clock)) {
+

Re: (subset) [PATCH] drm/vc4: Fix spelling mistake "mmaping" -> "mmapping"

2022-10-24 Thread maxime
On Fri, 21 Oct 2022 09:40:35 +0100, Colin Ian King wrote:
> There are a couple of spelling mistakes in DRM_DEBUG messages. Fix them.
> 
> 

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime


Re: (subset) [PATCH] gpu/drm: fix repeated words in comments

2022-10-24 Thread maxime
On Sat, 22 Oct 2022 14:07:01 +0800, wangjianli wrote:
> Delete the redundant word 'the'.
> 
> 

Applied to drm/drm-misc (drm-misc-next).

Thanks!
Maxime


[PATCH 1/2] drm/vc4: hdmi: Take our lock to reset the link

2022-10-24 Thread maxime
We access some fields protected by our internal mutex in
vc4_hdmi_reset_link() (saved_adjusted_mode, output_bpc, output_format)
and are calling functions that need to have that lock taken
(vc4_hdmi_supports_scrambling()).

However, the current code doesn't lock that mutex. Let's make sure it
does.

Fixes: 6bed2ea3cb38 ("drm/vc4: hdmi: Reset link on hotplug")
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 21 +
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 596e311d6e58..9e549fa07467 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -349,27 +349,40 @@ static int vc4_hdmi_reset_link(struct drm_connector 
*connector,
if (!crtc_state->active)
return 0;
 
-   if (!vc4_hdmi_supports_scrambling(encoder))
+   mutex_lock(&vc4_hdmi->mutex);
+
+   if (!vc4_hdmi_supports_scrambling(encoder)) {
+   mutex_unlock(&vc4_hdmi->mutex);
return 0;
+   }
 
scrambling_needed = 
vc4_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
   vc4_hdmi->output_bpc,
   
vc4_hdmi->output_format);
-   if (!scrambling_needed)
+   if (!scrambling_needed) {
+   mutex_unlock(&vc4_hdmi->mutex);
return 0;
+   }
 
if (conn_state->commit &&
-   !try_wait_for_completion(&conn_state->commit->hw_done))
+   !try_wait_for_completion(&conn_state->commit->hw_done)) {
+   mutex_unlock(&vc4_hdmi->mutex);
return 0;
+   }
 
ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config);
if (ret < 0) {
drm_err(drm, "Failed to read TMDS config: %d\n", ret);
+   mutex_unlock(&vc4_hdmi->mutex);
return 0;
}
 
-   if (!!(config & SCDC_SCRAMBLING_ENABLE) == scrambling_needed)
+   if (!!(config & SCDC_SCRAMBLING_ENABLE) == scrambling_needed) {
+   mutex_unlock(&vc4_hdmi->mutex);
return 0;
+   }
+
+   mutex_unlock(&vc4_hdmi->mutex);
 
/*
 * HDMI 2.0 says that one should not send scrambled data
-- 
2.37.3



[PATCH 2/2] drm/vc4: hdmi: Fix outdated function name in comment

2022-10-24 Thread maxime
A comment introduced by commit 6bed2ea3cb38 ("drm/vc4: hdmi: Reset link
on hotplug") mentions a drm_atomic_helper_connector_hdmi_reset_link()
function that was part of the earlier versions but got moved internally
and is now named vc4_hdmi_reset_link(). Let's fix the function name.

Fixes: 6bed2ea3cb38 ("drm/vc4: hdmi: Reset link on hotplug")
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 9e549fa07467..79eda8f5fea0 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -410,9 +410,8 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi 
*vc4_hdmi,
 * .adap_enable, which leads to that funtion being called with
 * our mutex held.
 *
-* A similar situation occurs with
-* drm_atomic_helper_connector_hdmi_reset_link() that will call
-* into our KMS hooks if the scrambling was enabled.
+* A similar situation occurs with vc4_hdmi_reset_link() that
+* will call into our KMS hooks if the scrambling was enabled.
 *
 * Concurrency isn't an issue at the moment since we don't share
 * any state with any of the other frameworks so we can ignore
-- 
2.37.3



Must-Pass Test Suite for KMS drivers

2022-10-24 Thread maxime
Hi,

I've discussing the idea for the past year to add an IGT test suite that
all well-behaved KMS drivers must pass.

The main idea behind it comes from v4l2-compliance and cec-compliance,
that are being used to validate that the drivers are sane.

We should probably start building up the test list, and eventually
mandate that all tests pass for all the new KMS drivers we would merge
in the kernel, and be run by KCi or similar.

I did a first pass to create a draft of such a test-suite, which would
contain:

igt@core_auth@basic-auth
igt@core_auth@getclient-master-drop
igt@core_auth@getclient-simple
igt@core_auth@many-magics
igt@core_getclient
igt@core_getstats
igt@core_getversion
igt@core_hotunplug@hotrebind-lateclose
igt@core_hotunplug@hotunbind-rebind
igt@core_hotunplug@unbind-rebind
igt@core_setmaster
igt@core_setmaster_vs_auth
igt@device_reset@unbind-reset-rebind
igt@drm_read
igt@dumb_buffer
igt@fbdev
igt@feature_discovery@display
igt@kms_3d
igt@kms_addfb_basic
igt@kms_async_flips
igt@kms_color
igt@kms_concurrent
igt@kms_cursor_crc
igt@kms_cursor_edge_walk
igt@kms_cursor_legacy@basic-busy-flip-before-cursor
igt@kms_cursor_legacy@basic-flip-after-cursor
igt@kms_cursor_legacy@basic-flip-after-cursor
igt@kms_display_modes
igt@kms_dither
igt@kms_dp_aux_dev
igt@kms_flip@basic-flip-vs-dpms
igt@kms_flip@basic-flip-vs-modeset
igt@kms_flip@basic-flip-vs-wf_vblank
igt@kms_flip@basic-plain-flip
igt@kms_flip_event_leak@basic
igt@kms_force_connector_basic@force-connector-state
igt@kms_force_connector_basic@force-edid
igt@kms_force_connector_basic@force-load-detect
igt@kms_force_connector_basic@prune-stale-modes
igt@kms_getfb
igt@kms_hdmi_inject
igt@kms_hdr
igt@kms_invalid_mode
igt@kms_lease
igt@kms_panel_fitting
igt@kms_pipe_crc_basic
igt@kms_plane_alpha_blend
igt@kms_plane
igt@kms_plane_cursor
igt@kms_plane_lowres
igt@kms_plane_multiple
igt@kms_plane_scaling
igt@kms_prop_blob
igt@kms_properties
igt@kms_rmfb
igt@kms_scaling_modes
igt@kms_sequence
igt@kms_setmode
igt@kms_sysfs_edid_timing
igt@kms_tv_load_detect
igt@kms_universal_plane
igt@kms_vblank
igt@kms_vrr
igt@kms_writeback

Most of them are skipped on vc4 right now, but I could see that some of
them fail already (kms_rmfb, core_hotunplug), so it proves to be useful
already.

What do you think? Is there some more tests needed, or did I include
some tests that shouldn't have been there?

Thanks!
Maxime


signature.asc
Description: PGP signature


Re: Must-Pass Test Suite for KMS drivers

2022-10-26 Thread maxime
Hi Rob,

On Mon, Oct 24, 2022 at 08:48:15AM -0700, Rob Clark wrote:
> On Mon, Oct 24, 2022 at 5:43 AM  wrote:
> > I've discussing the idea for the past year to add an IGT test suite that
> > all well-behaved KMS drivers must pass.
> >
> > The main idea behind it comes from v4l2-compliance and cec-compliance,
> > that are being used to validate that the drivers are sane.
> >
> > We should probably start building up the test list, and eventually
> > mandate that all tests pass for all the new KMS drivers we would merge
> > in the kernel, and be run by KCi or similar.
> 
> Let's get https://patchwork.freedesktop.org/patch/502641/ merged
> first, that already gives us a mechanism similar to what we use in
> mesa to track pass/fail/flake

I'm not sure it's a dependency per-se, and I believe both can (and
should) happen separately.

AFAIU, the CI patches are here to track which tests are supposed to be
working and which aren't so that we can track regressions.

The list I was talking about is here to identify issues in the first
place. All tests must pass, and if one fails it should be considered a
hard failure.

This would be eligible for CI only for drivers which have been known to
pass them all already, but we wouldn't need to track which ones can fail
or not, all of them must.

> Beyond that, I think some of the igt tests need to get more stable
> before we could consider a "mustpass" list.

I agree that IGT tests could get more stable on ARM platforms, but it's
also a chicken-and-egg issue. If no-one is using them regularly on ARM,
then they'll never get fixed.

> The kms_lease tests seem to fail on msm due to bad assumptions in the
> test about which CRTCs primary planes can attach to. The legacy-cursor
> crc tests seem a bit racy (there was a patch posted for that, not sure
> if it landed yet), etc.

And this is fine, we can merge that list without them, and if and when
they get stable, we'll add them later.

> The best thing to do is actually start running CI and tracking xfails
> and flakes ;-)

Again, I wouldn't oppose them.

The issue I'm trying to solve is that there's just no way to know, at
the moment:

  - When you're running IGT, which tests are relevant for your platform
exactly.

  - If some of them fail, is it expected for them to fail or not. The
ci/ patch you mentioned help for that a bit, but only for platforms
where someone already did that work. When you want to do that work
in the first place, it's extremely tedious and obscure.

  - And if some of them fail, is it something that I should actually fix
or not.

The mustpass list addresses all those issues by providing a baseline.

Maxime


[PATCH v6 02/23] drm/connector: Rename legacy TV property

2022-10-26 Thread maxime
The current tv_mode has driver-specific values that don't allow to
easily share code using it, either at the userspace or kernel level.

Since we're going to introduce a new, generic, property that fit the
same purpose, let's rename this one to legacy_tv_mode to make it
obvious we should move away from it.

Acked-by: Thomas Zimmermann 
Reviewed-by: Lyude Paul  # nouveau
Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_atomic_uapi.c | 8 
 drivers/gpu/drm/drm_connector.c   | 6 +++---
 drivers/gpu/drm/gud/gud_connector.c   | 6 +++---
 drivers/gpu/drm/i2c/ch7006_drv.c  | 4 ++--
 drivers/gpu/drm/i915/display/intel_tv.c   | 3 ++-
 drivers/gpu/drm/nouveau/dispnv04/tvnv17.c | 4 ++--
 drivers/gpu/drm/vc4/vc4_vec.c | 8 
 include/drm/drm_connector.h   | 4 ++--
 include/drm/drm_mode_config.h | 6 --
 9 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index c06d0639d552..7f2b9a07fbdf 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -698,8 +698,8 @@ static int drm_atomic_connector_set_property(struct 
drm_connector *connector,
state->tv.margins.top = val;
} else if (property == config->tv_bottom_margin_property) {
state->tv.margins.bottom = val;
-   } else if (property == config->tv_mode_property) {
-   state->tv.mode = val;
+   } else if (property == config->legacy_tv_mode_property) {
+   state->tv.legacy_mode = val;
} else if (property == config->tv_brightness_property) {
state->tv.brightness = val;
} else if (property == config->tv_contrast_property) {
@@ -808,8 +808,8 @@ drm_atomic_connector_get_property(struct drm_connector 
*connector,
*val = state->tv.margins.top;
} else if (property == config->tv_bottom_margin_property) {
*val = state->tv.margins.bottom;
-   } else if (property == config->tv_mode_property) {
-   *val = state->tv.mode;
+   } else if (property == config->legacy_tv_mode_property) {
+   *val = state->tv.legacy_mode;
} else if (property == config->tv_brightness_property) {
*val = state->tv.brightness;
} else if (property == config->tv_contrast_property) {
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index e3142c8142b3..ede6025638d7 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -1686,14 +1686,14 @@ int drm_mode_create_tv_properties(struct drm_device 
*dev,
if (drm_mode_create_tv_margin_properties(dev))
goto nomem;
 
-   dev->mode_config.tv_mode_property =
+   dev->mode_config.legacy_tv_mode_property =
drm_property_create(dev, DRM_MODE_PROP_ENUM,
"mode", num_modes);
-   if (!dev->mode_config.tv_mode_property)
+   if (!dev->mode_config.legacy_tv_mode_property)
goto nomem;
 
for (i = 0; i < num_modes; i++)
-   drm_property_add_enum(dev->mode_config.tv_mode_property,
+   drm_property_add_enum(dev->mode_config.legacy_tv_mode_property,
  i, modes[i]);
 
dev->mode_config.tv_brightness_property =
diff --git a/drivers/gpu/drm/gud/gud_connector.c 
b/drivers/gpu/drm/gud/gud_connector.c
index fa636206f232..86e992b2108b 100644
--- a/drivers/gpu/drm/gud/gud_connector.c
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -303,7 +303,7 @@ static int gud_connector_atomic_check(struct drm_connector 
*connector,
old_state->tv.margins.right != new_state->tv.margins.right ||
old_state->tv.margins.top != new_state->tv.margins.top ||
old_state->tv.margins.bottom != new_state->tv.margins.bottom ||
-   old_state->tv.mode != new_state->tv.mode ||
+   old_state->tv.legacy_mode != new_state->tv.legacy_mode ||
old_state->tv.brightness != new_state->tv.brightness ||
old_state->tv.contrast != new_state->tv.contrast ||
old_state->tv.flicker_reduction != new_state->tv.flicker_reduction 
||
@@ -424,7 +424,7 @@ gud_connector_property_lookup(struct drm_connector 
*connector, u16 prop)
case GUD_PROPERTY_TV_BOTTOM_MARGIN:
return config->tv_bottom_margin_property;
case GUD_PROPERTY_TV_MODE:
-   return config->tv_mode_property;
+   return config->legacy_tv_mode_property;
case GUD_PROPERTY_TV_BRIGHTNESS:
return config->tv_brightness_property;
case GUD_PROPERTY_TV_CONTRAST:
@@ -454,7 +454,7 @@ static unsigned 

[PATCH v6 01/23] drm/tests: Add Kunit Helpers

2022-10-26 Thread maxime
As the number of kunit tests in KMS grows further, we start to have
multiple test suites that, for example, need to register a mock DRM
driver to interact with the KMS function they are supposed to test.

Let's add a file meant to provide those kind of helpers to avoid
duplication.

Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 

---
Changes in v4:
- Simplified the DRM device cleanup patch using devm_drm_dev_alloc()
---
 drivers/gpu/drm/tests/Makefile|  1 +
 drivers/gpu/drm/tests/drm_kunit_helpers.c | 61 +++
 drivers/gpu/drm/tests/drm_kunit_helpers.h |  9 +
 3 files changed, 71 insertions(+)

diff --git a/drivers/gpu/drm/tests/Makefile b/drivers/gpu/drm/tests/Makefile
index 2d9f49b62ecb..b29ef1085cad 100644
--- a/drivers/gpu/drm/tests/Makefile
+++ b/drivers/gpu/drm/tests/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_DRM_KUNIT_TEST) += \
drm_format_helper_test.o \
drm_format_test.o \
drm_framebuffer_test.o \
+   drm_kunit_helpers.o \
drm_mm_test.o \
drm_plane_helper_test.o \
drm_rect_test.o
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.c 
b/drivers/gpu/drm/tests/drm_kunit_helpers.c
new file mode 100644
index ..3524d6a1fa9a
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.c
@@ -0,0 +1,61 @@
+#include 
+#include 
+
+#include 
+
+#include 
+
+struct kunit_dev {
+   struct drm_device base;
+};
+
+static const struct drm_mode_config_funcs drm_mode_config_funcs = {
+};
+
+static const struct drm_driver drm_mode_driver = {
+};
+
+static int dev_init(struct kunit_resource *res, void *ptr)
+{
+   char *name = ptr;
+   struct device *dev;
+
+   dev = root_device_register(name);
+   if (IS_ERR(dev))
+   return PTR_ERR(dev);
+
+   res->data = dev;
+   return 0;
+}
+
+static void dev_free(struct kunit_resource *res)
+{
+   struct device *dev = res->data;
+
+   root_device_unregister(dev);
+}
+
+struct drm_device *drm_kunit_device_init(struct kunit *test, char *name)
+{
+   struct kunit_dev *kdev;
+   struct drm_device *drm;
+   struct device *dev;
+   int ret;
+
+   dev = kunit_alloc_resource(test, dev_init, dev_free, GFP_KERNEL, name);
+   if (!dev)
+   return ERR_PTR(-ENOMEM);
+
+   kdev = devm_drm_dev_alloc(dev, &drm_mode_driver, struct kunit_dev, 
base);
+   if (IS_ERR(kdev))
+   return ERR_CAST(kdev);
+
+   drm = &kdev->base;
+   drm->mode_config.funcs = &drm_mode_config_funcs;
+
+   ret = drmm_mode_config_init(drm);
+   if (ret)
+   return ERR_PTR(ret);
+
+   return drm;
+}
diff --git a/drivers/gpu/drm/tests/drm_kunit_helpers.h 
b/drivers/gpu/drm/tests/drm_kunit_helpers.h
new file mode 100644
index ..a9354f9bda4e
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_kunit_helpers.h
@@ -0,0 +1,9 @@
+#ifndef DRM_KUNIT_HELPERS_H_
+#define DRM_KUNIT_HELPERS_H_
+
+struct drm_device;
+struct kunit;
+
+struct drm_device *drm_kunit_device_init(struct kunit *test, char *name);
+
+#endif // DRM_KUNIT_HELPERS_H_

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 00/23] drm: Analog TV Improvements

2022-10-26 Thread maxime
Hi,

Here's a series aiming at improving the command line named modes support,
and more importantly how we deal with all the analog TV variants.

The named modes support were initially introduced to allow to specify the
analog TV mode to be used.

However, this was causing multiple issues:

  * The mode name parsed on the command line was passed directly to the
driver, which had to figure out which mode it was suppose to match;

  * Figuring that out wasn't really easy, since the video= argument or what
the userspace might not even have a name in the first place, but
instead could have passed a mode with the same timings;

  * The fallback to matching on the timings was mostly working as long as
we were supporting one 525 lines (most likely NSTC) and one 625 lines
(PAL), but couldn't differentiate between two modes with the same
timings (NTSC vs PAL-M vs NSTC-J for example);

  * There was also some overlap with the tv mode property registered by
drm_mode_create_tv_properties(), but named modes weren't interacting
with that property at all.

  * Even though that property was generic, its possible values were
specific to each drivers, which made some generic support difficult.

Thus, I chose to tackle in multiple steps:

  * A new TV mode property was introduced, with generic values, each driver
reporting through a bitmask what standard it supports to the userspace;

  * This option was added to the command line parsing code to be able to
specify it on the kernel command line, and new atomic_check and reset
helpers were created to integrate properly into atomic KMS;

  * The named mode parsing code is now creating a proper display mode for
the given named mode, and the TV standard will thus be part of the
connector state;

  * Two drivers were converted and tested for now (vc4 and sun4i), with
some backward compatibility code to translate the old TV mode to the
new TV mode;

Unit tests were created along the way.

One can switch from NTSC to PAL now using (on vc4)

modetest -M vc4  -s 53:720x480i -w 53:'TV mode':1 # NTSC
modetest -M vc4  -s 53:720x576i -w 53:'TV mode':4 # PAL

Let me know what you think,
Maxime

To: David Airlie 
To: Daniel Vetter 
To: Maarten Lankhorst 
To: Maxime Ripard 
To: Thomas Zimmermann 
To: Emma Anholt 
To: Jani Nikula 
To: Joonas Lahtinen 
To: Rodrigo Vivi 
To: Tvrtko Ursulin 
To: Ben Skeggs 
To: Karol Herbst 
To: Lyude Paul 
To: Chen-Yu Tsai 
To: Jernej Skrabec 
To: Samuel Holland 
Cc: Geert Uytterhoeven 
Cc: Mateusz Kwiatkowski 
Cc: "Noralf Trønnes" 
Cc: Dave Stevenson 
Cc: Dom Cobley 
Cc: Phil Elwell 
Cc: 
Cc: linux-ker...@vger.kernel.org
Cc: intel-...@lists.freedesktop.org
Cc: nouv...@lists.freedesktop.org
Cc: linux-arm-ker...@lists.infradead.org
Cc: linux-su...@lists.linux.dev
Cc: Hans de Goede 
Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Add and convert to a new get_modes helper to create the PAL and NTSC modes in
  the proper order, with the right preferred mode flag, depending on the driver
  capabilities and defaults.
- Support PAL60
- Renamed tests to be consistent with DRM tests naming convention
- Simplified a bit the named mode parsing code
- Add a tv_mode_specified field
- Return 0 in get_modes implementations instead of error codes
- Link to v5: 
https://lore.kernel.org/r/20220728-rpi-analog-tv-properties-v5-0-d841cc64f...@cerno.tech

Changes in v5:
- Dropped TV Standard documentation removal
- Switched the TV Mode documentation from CSV to actual documentation
- Switched to kunit assertions where possible
- Switched to KUNIT_ASSERT_NOT_NULL instead of KUNIT_ASSERT_PTR_NE(..., NULL)
- Shuffled a bit the introduction of drm_client_modeset_connector_get_modes 
between patches
- Renamed tv_mode_names to legacy_tv_mode_names
- Removed the count variable in sun4i_tv_comp_get_modes
- Rebased on top of current drm-misc-next
- Link to v4: 
https://lore.kernel.org/r/20220728-rpi-analog-tv-properties-v4-0-60d38873f...@cerno.tech

Changes in v4:
- Removed the unused TV Standard property documentation
- Added the TV Mode property documentation to kms-properties.csv
- Fixed the documentation of drm_mode_create_tv_properties()
- Removed DRM_MODE_TV_MODE_NONE
- Reworded the line length check comment in drm_mode_analog_tv tests
- Switched to HZ_PER_KHZ in drm_mode_analog_tv tests
- Reworked drm_mode_analog_tv to fill our mode using the previously computed
  timings
- Added the command-line option documentation to modedb.rst
- Improved the Kunit helpers cleanup
- Moved the subconnector documentation renaming to the proper patch
- Added the various review tags
- Removed the count variable in vc4_vec_connector_get_modes
- Rebased on drm-misc-next-2022-09-23 and fixed a merge conflict
- Folded all the named mode parsing improvements in a single patch
- Link to v3: 
https://lore.kernel.org/r/20220728-rpi-analog-tv-properties-v2-0-f733a0ed9...@cerno.tech

Changes i

[PATCH v6 03/23] drm/connector: Only register TV mode property if present

2022-10-26 Thread maxime
The drm_create_tv_properties() will create the TV mode property
unconditionally.

However, since we'll gradually phase it out, let's register it only if we
have a list passed as an argument. This will make the transition easier.

Acked-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_connector.c | 19 +++
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index ede6025638d7..17a5913cefe3 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -1686,15 +1686,18 @@ int drm_mode_create_tv_properties(struct drm_device 
*dev,
if (drm_mode_create_tv_margin_properties(dev))
goto nomem;
 
-   dev->mode_config.legacy_tv_mode_property =
-   drm_property_create(dev, DRM_MODE_PROP_ENUM,
-   "mode", num_modes);
-   if (!dev->mode_config.legacy_tv_mode_property)
-   goto nomem;
 
-   for (i = 0; i < num_modes; i++)
-   drm_property_add_enum(dev->mode_config.legacy_tv_mode_property,
- i, modes[i]);
+   if (num_modes) {
+   dev->mode_config.legacy_tv_mode_property =
+   drm_property_create(dev, DRM_MODE_PROP_ENUM,
+   "mode", num_modes);
+   if (!dev->mode_config.legacy_tv_mode_property)
+   goto nomem;
+
+   for (i = 0; i < num_modes; i++)
+   
drm_property_add_enum(dev->mode_config.legacy_tv_mode_property,
+ i, modes[i]);
+   }
 
dev->mode_config.tv_brightness_property =
drm_property_create_range(dev, 0, "brightness", 0, 100);

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 04/23] drm/connector: Rename drm_mode_create_tv_properties

2022-10-26 Thread maxime
drm_mode_create_tv_properties(), among other things, will create the
"mode" property that stores the analog TV mode that connector is
supposed to output.

However, that property is getting deprecated, so let's rename that
function to mention it's deprecated. We'll introduce a new variant of
that function creating the property superseeding it in a later patch.

Reviewed-by: Lyude Paul  # nouveau
Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_connector.c   | 12 ++--
 drivers/gpu/drm/gud/gud_connector.c   |  4 ++--
 drivers/gpu/drm/i2c/ch7006_drv.c  |  2 +-
 drivers/gpu/drm/i915/display/intel_tv.c   |  2 +-
 drivers/gpu/drm/nouveau/dispnv04/tvnv17.c |  2 +-
 drivers/gpu/drm/vc4/vc4_vec.c |  5 +++--
 include/drm/drm_connector.h   |  6 +++---
 7 files changed, 17 insertions(+), 16 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 17a5913cefe3..4e4fbc9e0049 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -1600,7 +1600,7 @@ EXPORT_SYMBOL(drm_connector_attach_tv_margin_properties);
  * Called by a driver's HDMI connector initialization routine, this function
  * creates the TV margin properties for a given device. No need to call this
  * function for an SDTV connector, it's already called from
- * drm_mode_create_tv_properties().
+ * drm_mode_create_tv_properties_legacy().
  *
  * Returns:
  * 0 on success or a negative error code on failure.
@@ -1635,7 +1635,7 @@ int drm_mode_create_tv_margin_properties(struct 
drm_device *dev)
 EXPORT_SYMBOL(drm_mode_create_tv_margin_properties);
 
 /**
- * drm_mode_create_tv_properties - create TV specific connector properties
+ * drm_mode_create_tv_properties_legacy - create TV specific connector 
properties
  * @dev: DRM device
  * @num_modes: number of different TV formats (modes) supported
  * @modes: array of pointers to strings containing name of each format
@@ -1648,9 +1648,9 @@ EXPORT_SYMBOL(drm_mode_create_tv_margin_properties);
  * Returns:
  * 0 on success or a negative error code on failure.
  */
-int drm_mode_create_tv_properties(struct drm_device *dev,
- unsigned int num_modes,
- const char * const modes[])
+int drm_mode_create_tv_properties_legacy(struct drm_device *dev,
+unsigned int num_modes,
+const char * const modes[])
 {
struct drm_property *tv_selector;
struct drm_property *tv_subconnector;
@@ -1733,7 +1733,7 @@ int drm_mode_create_tv_properties(struct drm_device *dev,
 nomem:
return -ENOMEM;
 }
-EXPORT_SYMBOL(drm_mode_create_tv_properties);
+EXPORT_SYMBOL(drm_mode_create_tv_properties_legacy);
 
 /**
  * drm_mode_create_scaling_mode_property - create scaling mode property
diff --git a/drivers/gpu/drm/gud/gud_connector.c 
b/drivers/gpu/drm/gud/gud_connector.c
index 86e992b2108b..034e78360d4f 100644
--- a/drivers/gpu/drm/gud/gud_connector.c
+++ b/drivers/gpu/drm/gud/gud_connector.c
@@ -400,7 +400,7 @@ static int gud_connector_add_tv_mode(struct gud_device 
*gdrm, struct drm_connect
for (i = 0; i < num_modes; i++)
modes[i] = &buf[i * GUD_CONNECTOR_TV_MODE_NAME_LEN];
 
-   ret = drm_mode_create_tv_properties(connector->dev, num_modes, modes);
+   ret = drm_mode_create_tv_properties_legacy(connector->dev, num_modes, 
modes);
 free:
kfree(buf);
if (ret < 0)
@@ -539,7 +539,7 @@ static int gud_connector_add_properties(struct gud_device 
*gdrm, struct gud_conn
fallthrough;
case GUD_PROPERTY_TV_HUE:
/* This is a no-op if already added. */
-   ret = drm_mode_create_tv_properties(drm, 0, NULL);
+   ret = drm_mode_create_tv_properties_legacy(drm, 0, 
NULL);
if (ret)
goto out;
break;
diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
index ef69f9bdeace..b63bad04b09d 100644
--- a/drivers/gpu/drm/i2c/ch7006_drv.c
+++ b/drivers/gpu/drm/i2c/ch7006_drv.c
@@ -250,7 +250,7 @@ static int ch7006_encoder_create_resources(struct 
drm_encoder *encoder,
struct drm_device *dev = encoder->dev;
struct drm_mode_config *conf = &dev->mode_config;
 
-   drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
+   drm_mode_create_tv_properties_legacy(dev, NUM_TV_NORMS, 
ch7006_tv_norm_names);
 
priv->scale_property = drm_property_create_range(dev, 0, "scale", 0, 2);
if (!priv->scale_property)
diff --git a/drivers/gpu/drm/i915/display/intel_tv.c 
b/drivers/gpu/drm/i915/display/intel_tv.c
index b2f42bf929e2..748821ebdf65 100644
--- a/drivers/gpu/drm/i915/displa

[PATCH v6 05/23] drm/connector: Add TV standard property

2022-10-26 Thread maxime
The TV mode property has been around for a while now to select and get the
current TV mode output on an analog TV connector.

Despite that property name being generic, its content isn't and has been
driver-specific which makes it hard to build any generic behaviour on top
of it, both in kernel and user-space.

Let's create a new enum tv norm property, that can contain any of the
analog TV standards currently supported by kernel drivers. Each driver can
then pass in a bitmask of the modes it supports, and the property
creation function will filter out the modes not supported.

We'll then be able to phase out the older tv mode property.

Signed-off-by: Maxime Ripard 

---
Changes in v5:
- Create an analog TV properties documentation section, and document TV
  Mode there instead of the csv file

Changes in v4:
- Add property documentation to kms-properties.csv
- Fix documentation
---
 Documentation/gpu/drm-kms.rst |   6 ++
 drivers/gpu/drm/drm_atomic_uapi.c |   4 ++
 drivers/gpu/drm/drm_connector.c   | 122 +-
 include/drm/drm_connector.h   |  64 
 include/drm/drm_mode_config.h |   8 +++
 5 files changed, 203 insertions(+), 1 deletion(-)

diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index b4377a545425..321f2f582c64 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -520,6 +520,12 @@ HDMI Specific Connector Properties
 .. kernel-doc:: drivers/gpu/drm/drm_connector.c
:doc: HDMI connector properties
 
+Analog TV Specific Connector Properties
+--
+
+.. kernel-doc:: drivers/gpu/drm/drm_connector.c
+   :doc: Analog TV Connector Properties
+
 Standard CRTC Properties
 
 
diff --git a/drivers/gpu/drm/drm_atomic_uapi.c 
b/drivers/gpu/drm/drm_atomic_uapi.c
index 7f2b9a07fbdf..d867e7f9f2cd 100644
--- a/drivers/gpu/drm/drm_atomic_uapi.c
+++ b/drivers/gpu/drm/drm_atomic_uapi.c
@@ -700,6 +700,8 @@ static int drm_atomic_connector_set_property(struct 
drm_connector *connector,
state->tv.margins.bottom = val;
} else if (property == config->legacy_tv_mode_property) {
state->tv.legacy_mode = val;
+   } else if (property == config->tv_mode_property) {
+   state->tv.mode = val;
} else if (property == config->tv_brightness_property) {
state->tv.brightness = val;
} else if (property == config->tv_contrast_property) {
@@ -810,6 +812,8 @@ drm_atomic_connector_get_property(struct drm_connector 
*connector,
*val = state->tv.margins.bottom;
} else if (property == config->legacy_tv_mode_property) {
*val = state->tv.legacy_mode;
+   } else if (property == config->tv_mode_property) {
+   *val = state->tv.mode;
} else if (property == config->tv_brightness_property) {
*val = state->tv.brightness;
} else if (property == config->tv_contrast_property) {
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 4e4fbc9e0049..820f4c730b38 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -980,6 +980,17 @@ static const struct drm_prop_enum_list 
drm_dvi_i_subconnector_enum_list[] = {
 DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name,
 drm_dvi_i_subconnector_enum_list)
 
+static const struct drm_prop_enum_list drm_tv_mode_enum_list[] = {
+   { DRM_MODE_TV_MODE_NTSC, "NTSC" },
+   { DRM_MODE_TV_MODE_NTSC_443, "NTSC-443" },
+   { DRM_MODE_TV_MODE_NTSC_J, "NTSC-J" },
+   { DRM_MODE_TV_MODE_PAL, "PAL" },
+   { DRM_MODE_TV_MODE_PAL_M, "PAL-M" },
+   { DRM_MODE_TV_MODE_PAL_N, "PAL-N" },
+   { DRM_MODE_TV_MODE_SECAM, "SECAM" },
+};
+DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list)
+
 static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
@@ -1548,6 +1559,71 @@ 
EXPORT_SYMBOL(drm_connector_attach_dp_subconnector_property);
  * infoframe values is done through drm_hdmi_avi_infoframe_content_type().
  */
 
+/*
+ * TODO: Document the properties:
+ *   - left margin
+ *   - right margin
+ *   - top margin
+ *   - bottom margin
+ *   - brightness
+ *   - contrast
+ *   - flicker reduction
+ *   - hue
+ *   - mode
+ *   - overscan
+ *   - saturation
+ *   - select subconnector
+ *   - subconnector
+ */
+/**
+ * DOC: Analog TV Connector Properties
+ *
+ * TV Mode:
+ * Indicates the TV Mode used on an analog TV connector. The value
+ * of this property can be one of the following:
+ *
+ * NTSC:
+ * TV Mode is CCIR System M (aka 525-lines

[PATCH v6 06/23] drm/modes: Add a function to generate analog display modes

2022-10-26 Thread maxime
Multiple drivers (meson, vc4, sun4i) define analog TV 525-lines and
625-lines modes in their drivers.

Since those modes are fairly standard, and that we'll need to use them
in more places in the future, it makes sense to move their definition
into the core framework.

However, analog display usually have fairly loose timings requirements,
the only discrete parameters being the total number of lines and pixel
clock frequency. Thus, we created a function that will create a display
mode from the standard, the pixel frequency and the active area.

Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Fix typo

Changes in v4:
- Reworded the line length check comment
- Switch to HZ_PER_KHZ in tests
- Use previous timing to fill our mode
- Move the number of lines check earlier
---
 drivers/gpu/drm/drm_modes.c| 474 +
 drivers/gpu/drm/tests/Makefile |   1 +
 drivers/gpu/drm/tests/drm_modes_test.c | 144 ++
 include/drm/drm_modes.h|  17 ++
 4 files changed, 636 insertions(+)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 5d4ac79381c4..71c050c3ee6b 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -116,6 +116,480 @@ void drm_mode_probed_add(struct drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_mode_probed_add);
 
+enum drm_mode_analog {
+   DRM_MODE_ANALOG_NTSC, /* 525 lines, 60Hz */
+   DRM_MODE_ANALOG_PAL, /* 625 lines, 50Hz */
+};
+
+/*
+ * The timings come from:
+ * - 
https://web.archive.org/web/20220406232708/http://www.kolumbus.fi/pami1/video/pal_ntsc.html
+ * - 
https://web.archive.org/web/20220406124914/http://martin.hinner.info/vga/pal.html
+ * - 
https://web.archive.org/web/20220609202433/http://www.batsocks.co.uk/readme/video_timing.htm
+ */
+#define NTSC_LINE_DURATION_NS  63556U
+#define NTSC_LINES_NUMBER  525
+
+#define NTSC_HBLK_DURATION_TYP_NS  10900U
+#define NTSC_HBLK_DURATION_MIN_NS  (NTSC_HBLK_DURATION_TYP_NS - 200)
+#define NTSC_HBLK_DURATION_MAX_NS  (NTSC_HBLK_DURATION_TYP_NS + 200)
+
+#define NTSC_HACT_DURATION_TYP_NS  (NTSC_LINE_DURATION_NS - 
NTSC_HBLK_DURATION_TYP_NS)
+#define NTSC_HACT_DURATION_MIN_NS  (NTSC_LINE_DURATION_NS - 
NTSC_HBLK_DURATION_MAX_NS)
+#define NTSC_HACT_DURATION_MAX_NS  (NTSC_LINE_DURATION_NS - 
NTSC_HBLK_DURATION_MIN_NS)
+
+#define NTSC_HFP_DURATION_TYP_NS   1500
+#define NTSC_HFP_DURATION_MIN_NS   1270
+#define NTSC_HFP_DURATION_MAX_NS   2220
+
+#define NTSC_HSLEN_DURATION_TYP_NS 4700
+#define NTSC_HSLEN_DURATION_MIN_NS (NTSC_HSLEN_DURATION_TYP_NS - 100)
+#define NTSC_HSLEN_DURATION_MAX_NS (NTSC_HSLEN_DURATION_TYP_NS + 100)
+
+#define NTSC_HBP_DURATION_TYP_NS   4700
+
+/*
+ * I couldn't find the actual tolerance for the back porch, so let's
+ * just reuse the sync length ones.
+ */
+#define NTSC_HBP_DURATION_MIN_NS   (NTSC_HBP_DURATION_TYP_NS - 100)
+#define NTSC_HBP_DURATION_MAX_NS   (NTSC_HBP_DURATION_TYP_NS + 100)
+
+#define PAL_LINE_DURATION_NS   64000U
+#define PAL_LINES_NUMBER   625
+
+#define PAL_HACT_DURATION_TYP_NS   51950U
+#define PAL_HACT_DURATION_MIN_NS   (PAL_HACT_DURATION_TYP_NS - 100)
+#define PAL_HACT_DURATION_MAX_NS   (PAL_HACT_DURATION_TYP_NS + 400)
+
+#define PAL_HBLK_DURATION_TYP_NS   (PAL_LINE_DURATION_NS - 
PAL_HACT_DURATION_TYP_NS)
+#define PAL_HBLK_DURATION_MIN_NS   (PAL_LINE_DURATION_NS - 
PAL_HACT_DURATION_MAX_NS)
+#define PAL_HBLK_DURATION_MAX_NS   (PAL_LINE_DURATION_NS - 
PAL_HACT_DURATION_MIN_NS)
+
+#define PAL_HFP_DURATION_TYP_NS1650
+#define PAL_HFP_DURATION_MIN_NS(PAL_HFP_DURATION_TYP_NS - 100)
+#define PAL_HFP_DURATION_MAX_NS(PAL_HFP_DURATION_TYP_NS + 400)
+
+#define PAL_HSLEN_DURATION_TYP_NS  4700
+#define PAL_HSLEN_DURATION_MIN_NS  (PAL_HSLEN_DURATION_TYP_NS - 200)
+#define PAL_HSLEN_DURATION_MAX_NS  (PAL_HSLEN_DURATION_TYP_NS + 200)
+
+#define PAL_HBP_DURATION_TYP_NS5700
+#define PAL_HBP_DURATION_MIN_NS(PAL_HBP_DURATION_TYP_NS - 200)
+#define PAL_HBP_DURATION_MAX_NS(PAL_HBP_DURATION_TYP_NS + 200)
+
+struct analog_param_field {
+   unsigned int even, odd;
+};
+
+#define PARAM_FIELD(_odd, _even)   \
+   { .even = _even, .odd = _odd }
+
+struct analog_param_range {
+   unsigned intmin, typ, max;
+};
+
+#define PARAM_RANGE(_min, _typ, _max)  \
+   { .min = _min, .typ = _typ, .max = _max }
+
+struct analog_parameters {
+   unsigned intnum_lines;
+   unsigned intline_duration_ns;
+
+   struct analog_param_range   hact_ns;
+   struct analog_param_range   hfp_ns;
+   struct analog_param_range   hslen_ns;
+   struct analog_param_range   hbp_ns;
+   struct analog_param_range   hblk_ns;
+
+   unsigned int  

[PATCH v6 08/23] drm/modes: Move named modes parsing to a separate function

2022-10-26 Thread maxime
The current construction of the named mode parsing doesn't allow to extend
it easily. Let's move it to a separate function so we can add more
parameters and modes.

In order for the tests to still pass, some extra checks are needed, so
it's not a 1:1 move.

Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Simplify the test for connection status extras
- Simplify the code path to call drm_mode_parse_cmdline_named_mode

Changes in v4:
- Fold down all the named mode patches that were split into a single
  patch again to maintain bisectability
---
 drivers/gpu/drm/drm_modes.c | 70 +
 1 file changed, 58 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 71c050c3ee6b..37542612912b 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2229,6 +2229,51 @@ static const char * const drm_named_modes_whitelist[] = {
"PAL",
 };
 
+static int drm_mode_parse_cmdline_named_mode(const char *name,
+unsigned int name_end,
+struct drm_cmdline_mode 
*cmdline_mode)
+{
+   unsigned int i;
+
+   if (!name_end)
+   return 0;
+
+   /* If the name starts with a digit, it's not a named mode */
+   if (isdigit(name[0]))
+   return 0;
+
+   /*
+* If there's an equal sign in the name, the command-line
+* contains only an option and no mode.
+*/
+   if (strnchr(name, name_end, '='))
+   return 0;
+
+   /* The connection status extras can be set without a mode. */
+   if (name_end == 1 &&
+   (name[0] == 'd' || name[0] == 'D' || name[0] == 'e'))
+   return 0;
+
+   /*
+* We're sure we're a named mode at this point, iterate over the
+* list of modes we're aware of.
+*/
+   for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
+   int ret;
+
+   ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
+   if (ret != name_end)
+   continue;
+
+   strcpy(cmdline_mode->name, drm_named_modes_whitelist[i]);
+   cmdline_mode->specified = true;
+
+   return 1;
+   }
+
+   return -EINVAL;
+}
+
 /**
  * drm_mode_parse_command_line_for_connector - parse command line modeline for 
connector
  * @mode_option: optional per connector mode option
@@ -2265,7 +2310,7 @@ bool drm_mode_parse_command_line_for_connector(const char 
*mode_option,
const char *bpp_ptr = NULL, *refresh_ptr = NULL, *extra_ptr = NULL;
const char *options_ptr = NULL;
char *bpp_end_ptr = NULL, *refresh_end_ptr = NULL;
-   int i, len, ret;
+   int len, ret;
 
memset(mode, 0, sizeof(*mode));
mode->panel_orientation = DRM_MODE_PANEL_ORIENTATION_UNKNOWN;
@@ -2306,18 +2351,19 @@ bool drm_mode_parse_command_line_for_connector(const 
char *mode_option,
parse_extras = true;
}
 
-   /* First check for a named mode */
-   for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
-   ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
-   if (ret == mode_end) {
-   if (refresh_ptr)
-   return false; /* named + refresh is invalid */
+   if (!mode_end)
+   return false;
 
-   strcpy(mode->name, drm_named_modes_whitelist[i]);
-   mode->specified = true;
-   break;
-   }
-   }
+   ret = drm_mode_parse_cmdline_named_mode(name, mode_end, mode);
+   if (ret < 0)
+   return false;
+
+   /*
+* Having a mode that starts by a letter (and thus is named) and
+* an at-sign (used to specify a refresh rate) is disallowed.
+*/
+   if (ret && refresh_ptr)
+   return false;
 
/* No named mode? Check for a normal mode argument, e.g. 1024x768 */
if (!mode->specified && isdigit(name[0])) {

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 11/23] drm/connector: Add pixel clock to cmdline mode

2022-10-26 Thread maxime
We'll need to get the pixel clock to generate proper display modes for
all the current named modes. Let's add it to struct drm_cmdline_mode and
fill it when parsing the named mode.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_modes.c | 9 ++---
 include/drm/drm_connector.h | 7 +++
 2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index acee23e1a8b7..c826f9583a1d 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2226,22 +2226,24 @@ static int drm_mode_parse_cmdline_options(const char 
*str,
 
 struct drm_named_mode {
const char *name;
+   unsigned int pixel_clock_khz;
unsigned int xres;
unsigned int yres;
unsigned int flags;
 };
 
-#define NAMED_MODE(_name, _x, _y, _flags)  \
+#define NAMED_MODE(_name, _pclk, _x, _y, _flags)   \
{   \
.name = _name,  \
+   .pixel_clock_khz = _pclk,   \
.xres = _x, \
.yres = _y, \
.flags = _flags,\
}
 
 static const struct drm_named_mode drm_named_modes[] = {
-   NAMED_MODE("NTSC", 720, 480, DRM_MODE_FLAG_INTERLACE),
-   NAMED_MODE("PAL", 720, 576, DRM_MODE_FLAG_INTERLACE),
+   NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE),
+   NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE),
 };
 
 static int drm_mode_parse_cmdline_named_mode(const char *name,
@@ -2282,6 +2284,7 @@ static int drm_mode_parse_cmdline_named_mode(const char 
*name,
continue;
 
strcpy(cmdline_mode->name, mode->name);
+   cmdline_mode->pixel_clock = mode->pixel_clock_khz;
cmdline_mode->xres = mode->xres;
cmdline_mode->yres = mode->yres;
cmdline_mode->interlace = !!(mode->flags & 
DRM_MODE_FLAG_INTERLACE);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 96b2e4e12334..5c5e67de2296 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1273,6 +1273,13 @@ struct drm_cmdline_mode {
 */
bool bpp_specified;
 
+   /**
+* @pixel_clock:
+*
+* Pixel Clock in kHz. Optional.
+*/
+   unsigned int pixel_clock;
+
/**
 * @xres:
 *

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 07/23] drm/client: Add some tests for drm_connector_pick_cmdline_mode()

2022-10-26 Thread maxime
drm_connector_pick_cmdline_mode() is in charge of finding a proper
drm_display_mode from the definition we got in the video= command line
argument.

Let's add some unit tests to make sure we're not getting any regressions
there.

Acked-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Rename tests to be consistent with DRM tests naming convention

Changes in v5:
- Removed useless (for now) count and modes intermediate variables in
  get_modes
- Switched to kunit assertions in test init, and to KUNIT_ASSERT_NOT_NULL
  instead of KUNIT_ASSERT_PTR_NE(..., NULL)

Changes in v4:
- Removed MODULE macros
---
 drivers/gpu/drm/drm_client_modeset.c|   4 +
 drivers/gpu/drm/tests/drm_client_modeset_test.c | 100 
 2 files changed, 104 insertions(+)

diff --git a/drivers/gpu/drm/drm_client_modeset.c 
b/drivers/gpu/drm/drm_client_modeset.c
index bbc535cc50dd..d553e793e673 100644
--- a/drivers/gpu/drm/drm_client_modeset.c
+++ b/drivers/gpu/drm/drm_client_modeset.c
@@ -1237,3 +1237,7 @@ int drm_client_modeset_dpms(struct drm_client_dev 
*client, int mode)
return ret;
 }
 EXPORT_SYMBOL(drm_client_modeset_dpms);
+
+#ifdef CONFIG_DRM_KUNIT_TEST
+#include "tests/drm_client_modeset_test.c"
+#endif
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c 
b/drivers/gpu/drm/tests/drm_client_modeset_test.c
new file mode 100644
index ..3aa1acfe75df
--- /dev/null
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Maxime Ripard 
+ */
+
+#include 
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include "drm_kunit_helpers.h"
+
+struct drm_client_modeset_test_priv {
+   struct drm_device *drm;
+   struct drm_connector connector;
+};
+
+static int drm_client_modeset_connector_get_modes(struct drm_connector 
*connector)
+{
+   return drm_add_modes_noedid(connector, 1920, 1200);
+}
+
+static const struct drm_connector_helper_funcs 
drm_client_modeset_connector_helper_funcs = {
+   .get_modes = drm_client_modeset_connector_get_modes,
+};
+
+static const struct drm_connector_funcs drm_client_modeset_connector_funcs = {
+};
+
+static int drm_client_modeset_test_init(struct kunit *test)
+{
+   struct drm_client_modeset_test_priv *priv;
+   int ret;
+
+   priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL);
+   KUNIT_ASSERT_NOT_NULL(test, priv);
+
+   test->priv = priv;
+
+   priv->drm = drm_kunit_device_init(test, "drm-client-modeset-test");
+   KUNIT_ASSERT_NOT_ERR_OR_NULL(test, priv->drm);
+
+   ret = drmm_connector_init(priv->drm, &priv->connector,
+ &drm_client_modeset_connector_funcs,
+ DRM_MODE_CONNECTOR_Unknown,
+ NULL);
+   KUNIT_ASSERT_EQ(test, ret, 0);
+
+   drm_connector_helper_add(&priv->connector, 
&drm_client_modeset_connector_helper_funcs);
+
+   return 0;
+
+}
+
+static void drm_test_pick_cmdline_res_1920_1080_60(struct kunit *test)
+{
+   struct drm_client_modeset_test_priv *priv = test->priv;
+   struct drm_device *drm = priv->drm;
+   struct drm_connector *connector = &priv->connector;
+   struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;
+   struct drm_display_mode *expected_mode, *mode;
+   const char *cmdline = "1920x1080@60";
+   int ret;
+
+   expected_mode = drm_mode_find_dmt(priv->drm, 1920, 1080, 60, false);
+   KUNIT_ASSERT_NOT_NULL(test, expected_mode);
+
+   KUNIT_ASSERT_TRUE(test,
+ drm_mode_parse_command_line_for_connector(cmdline,
+   connector,
+   
cmdline_mode));
+
+   mutex_lock(&drm->mode_config.mutex);
+   ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+   mutex_unlock(&drm->mode_config.mutex);
+   KUNIT_ASSERT_GT(test, ret, 0);
+
+   mode = drm_connector_pick_cmdline_mode(connector);
+   KUNIT_ASSERT_NOT_NULL(test, mode);
+
+   KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode));
+}
+
+
+static struct kunit_case drm_test_pick_cmdline_tests[] = {
+   KUNIT_CASE(drm_test_pick_cmdline_res_1920_1080_60),
+   {}
+};
+
+static struct kunit_suite drm_test_pick_cmdline_test_suite = {
+   .name = "drm_test_pick_cmdline",
+   .init = drm_client_modeset_test_init,
+   .test_cases = drm_test_pick_cmdline_tests
+};
+
+kunit_test_suite(drm_test_pick_cmdline_test_suite);

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 14/23] drm/modes: Properly generate a drm_display_mode from a named mode

2022-10-26 Thread maxime
The framework will get the drm_display_mode from the drm_cmdline_mode it
got by parsing the video command line argument by calling
drm_connector_pick_cmdline_mode().

The heavy lifting will then be done by the drm_mode_create_from_cmdline_mode()
function.

In the case of the named modes though, there's no real code to make that
translation and we rely on the drivers to guess which actual display mode
we meant.

Let's modify drm_mode_create_from_cmdline_mode() to properly generate the
drm_display_mode we mean when passing a named mode.

Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Fix get_modes to return 0 instead of an error code
- Rename the tests to follow the DRM test naming convention

Changes in v5:
- Switched to KUNIT_ASSERT_NOT_NULL
---
 drivers/gpu/drm/drm_modes.c | 34 ++-
 drivers/gpu/drm/tests/drm_client_modeset_test.c | 77 -
 2 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index dc037f7ceb37..85aa9898c229 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2497,6 +2497,36 @@ bool drm_mode_parse_command_line_for_connector(const 
char *mode_option,
 }
 EXPORT_SYMBOL(drm_mode_parse_command_line_for_connector);
 
+static struct drm_display_mode *drm_named_mode(struct drm_device *dev,
+  struct drm_cmdline_mode *cmd)
+{
+   struct drm_display_mode *mode;
+   unsigned int i;
+
+   for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) {
+   const struct drm_named_mode *named_mode = &drm_named_modes[i];
+
+   if (strcmp(cmd->name, named_mode->name))
+   continue;
+
+   if (!named_mode->tv_mode)
+   continue;
+
+   mode = drm_analog_tv_mode(dev,
+ named_mode->tv_mode,
+ named_mode->pixel_clock_khz * 1000,
+ named_mode->xres,
+ named_mode->yres,
+ named_mode->flags & 
DRM_MODE_FLAG_INTERLACE);
+   if (!mode)
+   return NULL;
+
+   return mode;
+   }
+
+   return NULL;
+}
+
 /**
  * drm_mode_create_from_cmdline_mode - convert a command line modeline into a 
DRM display mode
  * @dev: DRM device to create the new mode for
@@ -2514,7 +2544,9 @@ drm_mode_create_from_cmdline_mode(struct drm_device *dev,
if (cmd->xres == 0 || cmd->yres == 0)
return NULL;
 
-   if (cmd->cvt)
+   if (strlen(cmd->name))
+   mode = drm_named_mode(dev, cmd);
+   else if (cmd->cvt)
mode = drm_cvt_mode(dev,
cmd->xres, cmd->yres,
cmd->refresh_specified ? cmd->refresh : 60,
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c 
b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index 3aa1acfe75df..fdfe9e20702e 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -21,7 +21,26 @@ struct drm_client_modeset_test_priv {
 
 static int drm_client_modeset_connector_get_modes(struct drm_connector 
*connector)
 {
-   return drm_add_modes_noedid(connector, 1920, 1200);
+   struct drm_display_mode *mode;
+   int count;
+
+   count = drm_add_modes_noedid(connector, 1920, 1200);
+
+   mode = drm_mode_analog_ntsc_480i(connector->dev);
+   if (!mode)
+   return count;
+
+   drm_mode_probed_add(connector, mode);
+   count += 1;
+
+   mode = drm_mode_analog_pal_576i(connector->dev);
+   if (!mode)
+   return count;
+
+   drm_mode_probed_add(connector, mode);
+   count += 1;
+
+   return count;
 }
 
 static const struct drm_connector_helper_funcs 
drm_client_modeset_connector_helper_funcs = {
@@ -52,6 +71,9 @@ static int drm_client_modeset_test_init(struct kunit *test)
 
drm_connector_helper_add(&priv->connector, 
&drm_client_modeset_connector_helper_funcs);
 
+   priv->connector.interlace_allowed = true;
+   priv->connector.doublescan_allowed = true;
+
return 0;
 
 }
@@ -85,9 +107,62 @@ static void drm_test_pick_cmdline_res_1920_1080_60(struct 
kunit *test)
KUNIT_EXPECT_TRUE(test, drm_mode_equal(expected_mode, mode));
 }
 
+static void drm_test_pick_cmdline_named_ntsc(struct kunit *test)
+{
+   struct drm_client_modeset_test_priv *priv = test->priv;
+   struct drm_device *drm = priv->drm;
+   struct drm_connector *connector = &priv->connector;
+   struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;
+   str

[PATCH v6 10/23] drm/modes: Fill drm_cmdline mode from named modes

2022-10-26 Thread maxime
The current code to deal with named modes will only set the mode name, and
then it's up to drivers to try to match that name to whatever mode or
configuration they see fit.

The plan is to remove that need and move the named mode handling out of
drivers and into the core, and only rely on modes and properties. Let's
start by properly filling drm_cmdline_mode from a named mode.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_modes.c | 18 --
 1 file changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 7594b657f86a..acee23e1a8b7 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2226,11 +2226,22 @@ static int drm_mode_parse_cmdline_options(const char 
*str,
 
 struct drm_named_mode {
const char *name;
+   unsigned int xres;
+   unsigned int yres;
+   unsigned int flags;
 };
 
+#define NAMED_MODE(_name, _x, _y, _flags)  \
+   {   \
+   .name = _name,  \
+   .xres = _x, \
+   .yres = _y, \
+   .flags = _flags,\
+   }
+
 static const struct drm_named_mode drm_named_modes[] = {
-   { "NTSC", },
-   { "PAL", },
+   NAMED_MODE("NTSC", 720, 480, DRM_MODE_FLAG_INTERLACE),
+   NAMED_MODE("PAL", 720, 576, DRM_MODE_FLAG_INTERLACE),
 };
 
 static int drm_mode_parse_cmdline_named_mode(const char *name,
@@ -2271,6 +2282,9 @@ static int drm_mode_parse_cmdline_named_mode(const char 
*name,
continue;
 
strcpy(cmdline_mode->name, mode->name);
+   cmdline_mode->xres = mode->xres;
+   cmdline_mode->yres = mode->yres;
+   cmdline_mode->interlace = !!(mode->flags & 
DRM_MODE_FLAG_INTERLACE);
cmdline_mode->specified = true;
 
return 1;

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 15/23] drm/modes: Introduce more named modes

2022-10-26 Thread maxime
Now that we can easily extend the named modes list, let's add a few more
analog TV modes that were used in the wild, and some unit tests to make
sure it works as intended.

Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Renamed the tests to follow DRM test naming convention

Changes in v5:
- Switched to KUNIT_ASSERT_NOT_NULL
---
 drivers/gpu/drm/drm_modes.c |  2 +
 drivers/gpu/drm/tests/drm_client_modeset_test.c | 54 +
 2 files changed, 56 insertions(+)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 85aa9898c229..530516a166bc 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2272,7 +2272,9 @@ struct drm_named_mode {
 
 static const struct drm_named_mode drm_named_modes[] = {
NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, 
DRM_MODE_TV_MODE_NTSC),
+   NAMED_MODE("NTSC-J", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, 
DRM_MODE_TV_MODE_NTSC_J),
NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, 
DRM_MODE_TV_MODE_PAL),
+   NAMED_MODE("PAL-M", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, 
DRM_MODE_TV_MODE_PAL_M),
 };
 
 static int drm_mode_parse_cmdline_named_mode(const char *name,
diff --git a/drivers/gpu/drm/tests/drm_client_modeset_test.c 
b/drivers/gpu/drm/tests/drm_client_modeset_test.c
index fdfe9e20702e..b3820d25beca 100644
--- a/drivers/gpu/drm/tests/drm_client_modeset_test.c
+++ b/drivers/gpu/drm/tests/drm_client_modeset_test.c
@@ -133,6 +133,32 @@ static void drm_test_pick_cmdline_named_ntsc(struct kunit 
*test)
KUNIT_EXPECT_TRUE(test, drm_mode_equal(drm_mode_analog_ntsc_480i(drm), 
mode));
 }
 
+static void drm_test_pick_cmdline_named_ntsc_j(struct kunit *test)
+{
+   struct drm_client_modeset_test_priv *priv = test->priv;
+   struct drm_device *drm = priv->drm;
+   struct drm_connector *connector = &priv->connector;
+   struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;
+   struct drm_display_mode *mode;
+   const char *cmdline = "NTSC-J";
+   int ret;
+
+   KUNIT_ASSERT_TRUE(test,
+ drm_mode_parse_command_line_for_connector(cmdline,
+   connector,
+   
cmdline_mode));
+
+   mutex_lock(&drm->mode_config.mutex);
+   ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+   mutex_unlock(&drm->mode_config.mutex);
+   KUNIT_ASSERT_GT(test, ret, 0);
+
+   mode = drm_connector_pick_cmdline_mode(connector);
+   KUNIT_ASSERT_NOT_NULL(test, mode);
+
+   KUNIT_EXPECT_TRUE(test, drm_mode_equal(drm_mode_analog_ntsc_480i(drm), 
mode));
+}
+
 static void drm_test_pick_cmdline_named_pal(struct kunit *test)
 {
struct drm_client_modeset_test_priv *priv = test->priv;
@@ -159,10 +185,38 @@ static void drm_test_pick_cmdline_named_pal(struct kunit 
*test)
KUNIT_EXPECT_TRUE(test, drm_mode_equal(drm_mode_analog_pal_576i(drm), 
mode));
 }
 
+static void drm_test_pick_cmdline_named_pal_m(struct kunit *test)
+{
+   struct drm_client_modeset_test_priv *priv = test->priv;
+   struct drm_device *drm = priv->drm;
+   struct drm_connector *connector = &priv->connector;
+   struct drm_cmdline_mode *cmdline_mode = &connector->cmdline_mode;
+   struct drm_display_mode *mode;
+   const char *cmdline = "PAL-M";
+   int ret;
+
+   KUNIT_ASSERT_TRUE(test,
+ drm_mode_parse_command_line_for_connector(cmdline,
+   connector,
+   
cmdline_mode));
+
+   mutex_lock(&drm->mode_config.mutex);
+   ret = drm_helper_probe_single_connector_modes(connector, 1920, 1080);
+   mutex_unlock(&drm->mode_config.mutex);
+   KUNIT_ASSERT_GT(test, ret, 0);
+
+   mode = drm_connector_pick_cmdline_mode(connector);
+   KUNIT_ASSERT_NOT_NULL(test, mode);
+
+   KUNIT_EXPECT_TRUE(test, drm_mode_equal(drm_mode_analog_ntsc_480i(drm), 
mode));
+}
+
 static struct kunit_case drm_test_pick_cmdline_tests[] = {
KUNIT_CASE(drm_test_pick_cmdline_res_1920_1080_60),
KUNIT_CASE(drm_test_pick_cmdline_named_ntsc),
+   KUNIT_CASE(drm_test_pick_cmdline_named_ntsc_j),
KUNIT_CASE(drm_test_pick_cmdline_named_pal),
+   KUNIT_CASE(drm_test_pick_cmdline_named_pal_m),
{}
 };
 

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 13/23] drm/modes: Introduce the tv_mode property as a command-line option

2022-10-26 Thread maxime
Our new tv mode option allows to specify the TV mode from a property.
However, it can still be useful, for example to avoid any boot time
artifact, to set that property directly from the kernel command line.

Let's add some code to allow it, and some unit tests to exercise that code.

Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Add a tv_mode_specified field

Changes in v4:
- Add Documentation of the command-line option to modedb.rst
---
 Documentation/fb/modedb.rst |  2 +
 drivers/gpu/drm/drm_modes.c | 37 --
 drivers/gpu/drm/tests/drm_cmdline_parser_test.c | 67 +
 include/drm/drm_connector.h | 12 +
 4 files changed, 115 insertions(+), 3 deletions(-)

diff --git a/Documentation/fb/modedb.rst b/Documentation/fb/modedb.rst
index 4d2411e32ebb..5d6361a77f3c 100644
--- a/Documentation/fb/modedb.rst
+++ b/Documentation/fb/modedb.rst
@@ -65,6 +65,8 @@ Valid options are::
   - reflect_y (boolean): Perform an axial symmetry on the Y axis
   - rotate (integer): Rotate the initial framebuffer by x
 degrees. Valid values are 0, 90, 180 and 270.
+  - tv_mode: Analog TV mode. One of "NTSC", "NTSC-443", "NTSC-J", "PAL",
+"PAL-M", "PAL-N", or "SECAM".
   - panel_orientation, one of "normal", "upside_down", "left_side_up", or
 "right_side_up". For KMS drivers only, this sets the "panel orientation"
 property on the kms connector as hint for kms users.
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index c826f9583a1d..dc037f7ceb37 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2133,6 +2133,30 @@ static int drm_mode_parse_panel_orientation(const char 
*delim,
return 0;
 }
 
+static int drm_mode_parse_tv_mode(const char *delim,
+ struct drm_cmdline_mode *mode)
+{
+   const char *value;
+   int ret;
+
+   if (*delim != '=')
+   return -EINVAL;
+
+   value = delim + 1;
+   delim = strchr(value, ',');
+   if (!delim)
+   delim = value + strlen(value);
+
+   ret = drm_get_tv_mode_from_name(value, delim - value);
+   if (ret < 0)
+   return ret;
+
+   mode->tv_mode_specified = true;
+   mode->tv_mode = ret;
+
+   return 0;
+}
+
 static int drm_mode_parse_cmdline_options(const char *str,
  bool freestanding,
  const struct drm_connector *connector,
@@ -2202,6 +2226,9 @@ static int drm_mode_parse_cmdline_options(const char *str,
} else if (!strncmp(option, "panel_orientation", delim - 
option)) {
if (drm_mode_parse_panel_orientation(delim, mode))
return -EINVAL;
+   } else if (!strncmp(option, "tv_mode", delim - option)) {
+   if (drm_mode_parse_tv_mode(delim, mode))
+   return -EINVAL;
} else {
return -EINVAL;
}
@@ -2230,20 +2257,22 @@ struct drm_named_mode {
unsigned int xres;
unsigned int yres;
unsigned int flags;
+   unsigned int tv_mode;
 };
 
-#define NAMED_MODE(_name, _pclk, _x, _y, _flags)   \
+#define NAMED_MODE(_name, _pclk, _x, _y, _flags, _mode)\
{   \
.name = _name,  \
.pixel_clock_khz = _pclk,   \
.xres = _x, \
.yres = _y, \
.flags = _flags,\
+   .tv_mode = _mode,   \
}
 
 static const struct drm_named_mode drm_named_modes[] = {
-   NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE),
-   NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE),
+   NAMED_MODE("NTSC", 13500, 720, 480, DRM_MODE_FLAG_INTERLACE, 
DRM_MODE_TV_MODE_NTSC),
+   NAMED_MODE("PAL", 13500, 720, 576, DRM_MODE_FLAG_INTERLACE, 
DRM_MODE_TV_MODE_PAL),
 };
 
 static int drm_mode_parse_cmdline_named_mode(const char *name,
@@ -2288,6 +2317,8 @@ static int drm_mode_parse_cmdline_named_mode(const char 
*name,
cmdline_mode->xres = mode->xres;
cmdline_mode->yres = mode->yres;
cmdline_mode->interlace = !!(mode->flags & 
DRM_MODE_FLAG_INTERLACE);
+   cmdline_mode->tv_mode = mode->tv_mode;
+   cmdline_mode->tv_mode_specified = true;
cmdline_mode->specified = true;
 
return 1;
diff --git a/drivers/gpu/drm/

[PATCH v6 18/23] drm/atomic-helper: Add an analog TV atomic_check implementation

2022-10-26 Thread maxime
The analog TV connector drivers share some atomic_check logic, and the new
TV standard property have created some boilerplate that can be be shared
across drivers too.

Let's create an atomic_check helper for those use cases.

Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_atomic_state_helper.c | 49 +++
 include/drm/drm_atomic_state_helper.h |  3 ++
 2 files changed, 52 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c 
b/drivers/gpu/drm/drm_atomic_state_helper.c
index e1fc3f26340a..3a467013c656 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -556,6 +556,55 @@ void drm_atomic_helper_connector_tv_reset(struct 
drm_connector *connector)
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset);
 
+/**
+ * @drm_atomic_helper_connector_tv_check: Validate an analog TV connector state
+ * @connector: DRM Connector
+ * @state: the DRM State object
+ *
+ * Checks the state object to see if the requested state is valid for an
+ * analog TV connector.
+ *
+ * Returns:
+ * Zero for success, a negative error code on error.
+ */
+int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
+struct drm_atomic_state *state)
+{
+   struct drm_connector_state *old_conn_state =
+   drm_atomic_get_old_connector_state(state, connector);
+   struct drm_connector_state *new_conn_state =
+   drm_atomic_get_new_connector_state(state, connector);
+   struct drm_crtc_state *crtc_state;
+   struct drm_crtc *crtc;
+
+   crtc = new_conn_state->crtc;
+   if (!crtc)
+   return 0;
+
+   crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
+   if (!crtc_state)
+   return -EINVAL;
+
+   if (old_conn_state->tv.mode != new_conn_state->tv.mode)
+   crtc_state->mode_changed = true;
+
+   if ((old_conn_state->tv.margins.left != 
new_conn_state->tv.margins.left) ||
+   (old_conn_state->tv.margins.right != 
new_conn_state->tv.margins.right) ||
+   (old_conn_state->tv.margins.top != new_conn_state->tv.margins.top) 
||
+   (old_conn_state->tv.margins.bottom != 
new_conn_state->tv.margins.bottom) ||
+   (old_conn_state->tv.mode != new_conn_state->tv.mode) ||
+   (old_conn_state->tv.brightness != new_conn_state->tv.brightness) ||
+   (old_conn_state->tv.contrast != new_conn_state->tv.contrast) ||
+   (old_conn_state->tv.flicker_reduction != 
new_conn_state->tv.flicker_reduction) ||
+   (old_conn_state->tv.overscan != new_conn_state->tv.overscan) ||
+   (old_conn_state->tv.saturation != new_conn_state->tv.saturation) ||
+   (old_conn_state->tv.hue != new_conn_state->tv.hue))
+   crtc_state->connectors_changed = true;
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_tv_check);
+
 /**
  * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
  * @connector: connector object
diff --git a/include/drm/drm_atomic_state_helper.h 
b/include/drm/drm_atomic_state_helper.h
index c8fbce795ee7..b9740edb2658 100644
--- a/include/drm/drm_atomic_state_helper.h
+++ b/include/drm/drm_atomic_state_helper.h
@@ -26,6 +26,7 @@
 
 #include 
 
+struct drm_atomic_state;
 struct drm_bridge;
 struct drm_bridge_state;
 struct drm_crtc;
@@ -71,6 +72,8 @@ void __drm_atomic_helper_connector_reset(struct drm_connector 
*connector,
 struct drm_connector_state 
*conn_state);
 void drm_atomic_helper_connector_reset(struct drm_connector *connector);
 void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector);
+int drm_atomic_helper_connector_tv_check(struct drm_connector *connector,
+struct drm_atomic_state *state);
 void drm_atomic_helper_connector_tv_margins_reset(struct drm_connector 
*connector);
 void
 __drm_atomic_helper_connector_duplicate_state(struct drm_connector *connector,

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 16/23] drm/probe-helper: Provide a TV get_modes helper

2022-10-26 Thread maxime
Most of the TV connectors will need a similar get_modes implementation
that will, depending on the drivers' capabilities, register the 480i and
576i modes.

That implementation will also need to set the preferred flag and order
the modes based on the driver and users preferrence.

This is especially important to guarantee that a userspace stack such as
Xorg can start and pick up the preferred mode while maintaining a
working output.

Signed-off-by: Maxime Ripard 

---
Changes in v6:
- New patch
---
 drivers/gpu/drm/drm_probe_helper.c | 97 ++
 include/drm/drm_probe_helper.h |  1 +
 2 files changed, 98 insertions(+)

diff --git a/drivers/gpu/drm/drm_probe_helper.c 
b/drivers/gpu/drm/drm_probe_helper.c
index 69b0b2b9cc1c..4a60575f5c66 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -1147,3 +1147,100 @@ int drm_connector_helper_get_modes(struct drm_connector 
*connector)
return count;
 }
 EXPORT_SYMBOL(drm_connector_helper_get_modes);
+
+static bool tv_mode_supported(struct drm_connector *connector,
+ enum drm_connector_tv_mode mode)
+{
+   struct drm_device *dev = connector->dev;
+   struct drm_property *property = dev->mode_config.tv_mode_property;
+
+   unsigned int i;
+
+   for (i = 0; i < property->num_values; i++)
+   if (property->values[i] == mode)
+   return true;
+
+   return false;
+}
+
+/**
+ * drm_connector_helper_tv_get_modes - Fills the modes availables to a TV 
connector
+ * @connector: The connector
+ *
+ * Fills the available modes for a TV connector based on the supported
+ * TV modes, and the default mode expressed by the kernel command line.
+ *
+ * This can be used as the default TV connector helper .get_modes() hook
+ * if the driver does not need any special processing.
+ *
+ * Returns:
+ * The number of modes added to the connector.
+ */
+int drm_connector_helper_tv_get_modes(struct drm_connector *connector)
+{
+   struct drm_device *dev = connector->dev;
+   struct drm_cmdline_mode *cmdline = &connector->cmdline_mode;
+   struct drm_display_mode *tv_modes[2] = {};
+   struct drm_display_mode *mode;
+   unsigned int first_mode_idx;
+   unsigned int count = 0;
+   uint64_t default_mode;
+   int ret;
+
+   if (!dev->mode_config.tv_mode_property)
+   return 0;
+
+   if (tv_mode_supported(connector, DRM_MODE_TV_MODE_NTSC) ||
+   tv_mode_supported(connector, DRM_MODE_TV_MODE_NTSC_443) ||
+   tv_mode_supported(connector, DRM_MODE_TV_MODE_NTSC_J) ||
+   tv_mode_supported(connector, DRM_MODE_TV_MODE_PAL_M)) {
+   mode = drm_mode_analog_ntsc_480i(connector->dev);
+   if (!mode)
+   return 0;
+
+   tv_modes[count++] = mode;
+   }
+
+   if (tv_mode_supported(connector, DRM_MODE_TV_MODE_PAL) ||
+   tv_mode_supported(connector, DRM_MODE_TV_MODE_PAL_N) ||
+   tv_mode_supported(connector, DRM_MODE_TV_MODE_SECAM)) {
+   mode = drm_mode_analog_pal_576i(connector->dev);
+   if (!mode)
+   return 0;
+
+   tv_modes[count++] = mode;
+   }
+
+   if (count == 1) {
+   mode->type |= DRM_MODE_TYPE_PREFERRED;
+   drm_mode_probed_add(connector, mode);
+   return count;
+   }
+
+   ret = drm_object_property_get_default_value(&connector->base,
+   
dev->mode_config.tv_mode_property,
+   &default_mode);
+   if (ret)
+   return 0;
+
+   if (cmdline->tv_mode_specified)
+   default_mode = cmdline->tv_mode;
+
+   if ((default_mode == DRM_MODE_TV_MODE_NTSC) ||
+   (default_mode == DRM_MODE_TV_MODE_NTSC_443) ||
+   (default_mode == DRM_MODE_TV_MODE_NTSC_J) ||
+   (default_mode == DRM_MODE_TV_MODE_PAL_M))
+   first_mode_idx = 0;
+   else
+   first_mode_idx = 1;
+
+   mode = tv_modes[first_mode_idx];
+   mode->type |= DRM_MODE_TYPE_PREFERRED;
+   drm_mode_probed_add(connector, mode);
+
+   mode = first_mode_idx ? tv_modes[0] : tv_modes[1];
+   drm_mode_probed_add(connector, mode);
+
+   return count;
+}
+EXPORT_SYMBOL(drm_connector_helper_tv_get_modes);
diff --git a/include/drm/drm_probe_helper.h b/include/drm/drm_probe_helper.h
index 5880daa14624..4977e0ab72db 100644
--- a/include/drm/drm_probe_helper.h
+++ b/include/drm/drm_probe_helper.h
@@ -35,5 +35,6 @@ int drm_connector_helper_get_modes_from_ddc(struct 
drm_connector *connector);
 int drm_connector_helper_get_modes_fixed(struct drm_connector *connector,
 const struct drm_display_mode 
*fixed_mode);
 int drm_

[PATCH v6 17/23] drm/atomic-helper: Add a TV properties reset helper

2022-10-26 Thread maxime
The drm_tv_create_properties() function will create a bunch of properties,
but it's up to each and every driver using that function to properly reset
the state of these properties leading to inconsistent behaviours.

Let's create a helper that will take care of it.

Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Use tv_mode_specified instead of a !0 tv_mode to set the default
---
 drivers/gpu/drm/drm_atomic_state_helper.c | 75 +++
 include/drm/drm_atomic_state_helper.h |  1 +
 2 files changed, 76 insertions(+)

diff --git a/drivers/gpu/drm/drm_atomic_state_helper.c 
b/drivers/gpu/drm/drm_atomic_state_helper.c
index dfb57217253b..e1fc3f26340a 100644
--- a/drivers/gpu/drm/drm_atomic_state_helper.c
+++ b/drivers/gpu/drm/drm_atomic_state_helper.c
@@ -481,6 +481,81 @@ void drm_atomic_helper_connector_tv_margins_reset(struct 
drm_connector *connecto
 }
 EXPORT_SYMBOL(drm_atomic_helper_connector_tv_margins_reset);
 
+/**
+ * drm_atomic_helper_connector_tv_reset - Resets Analog TV connector properties
+ * @connector: DRM connector
+ *
+ * Resets the analog TV properties attached to a connector
+ */
+void drm_atomic_helper_connector_tv_reset(struct drm_connector *connector)
+{
+   struct drm_device *dev = connector->dev;
+   struct drm_cmdline_mode *cmdline = &connector->cmdline_mode;
+   struct drm_connector_state *state = connector->state;
+   struct drm_property *prop;
+   uint64_t val;
+
+   prop = dev->mode_config.tv_mode_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.mode = val;
+
+   if (cmdline->tv_mode_specified)
+   state->tv.mode = cmdline->tv_mode;
+
+   prop = dev->mode_config.tv_select_subconnector_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.select_subconnector = val;
+
+   prop = dev->mode_config.tv_subconnector_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.subconnector = val;
+
+   prop = dev->mode_config.tv_brightness_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.brightness = val;
+
+   prop = dev->mode_config.tv_contrast_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.contrast = val;
+
+   prop = dev->mode_config.tv_flicker_reduction_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.flicker_reduction = val;
+
+   prop = dev->mode_config.tv_overscan_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.overscan = val;
+
+   prop = dev->mode_config.tv_saturation_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.saturation = val;
+
+   prop = dev->mode_config.tv_hue_property;
+   if (prop)
+   if (!drm_object_property_get_default_value(&connector->base,
+  prop, &val))
+   state->tv.hue = val;
+
+   drm_atomic_helper_connector_tv_margins_reset(connector);
+}
+EXPORT_SYMBOL(drm_atomic_helper_connector_tv_reset);
+
 /**
  * __drm_atomic_helper_connector_duplicate_state - copy atomic connector state
  * @connector: connector object
diff --git a/include/drm/drm_atomic_state_helper.h 
b/include/drm/drm_atomic_state_helper.h
index 192766656b88..c8fbce795ee7 100644
--- a/include/drm/drm_atomic_state_helper.h
+++ b/include/drm/drm_atomic_state_helper.h
@@ -70,6 +70,7 @@ void __drm_atomic_helper_connector_state_reset(struct 
drm_connector_state *conn_
 void __drm_atomic_helper_connector_reset(struct drm_connector *connector,
 struct drm_co

[PATCH v6 12/23] drm/connector: Add a function to lookup a TV mode by its name

2022-10-26 Thread maxime
As part of the command line parsing rework coming in the next patches,
we'll need to lookup drm_connector_tv_mode values by their name, already
defined in drm_tv_mode_enum_list.

In order to avoid any code duplication, let's do a function that will
perform a lookup of a TV mode name and return its value.

Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_connector.c | 24 
 include/drm/drm_connector.h |  2 ++
 2 files changed, 26 insertions(+)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 820f4c730b38..30611c616435 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -991,6 +991,30 @@ static const struct drm_prop_enum_list 
drm_tv_mode_enum_list[] = {
 };
 DRM_ENUM_NAME_FN(drm_get_tv_mode_name, drm_tv_mode_enum_list)
 
+/**
+ * drm_get_tv_mode_from_name - Translates a TV mode name into its enum value
+ * @name: TV Mode name we want to convert
+ * @len: Length of @name
+ *
+ * Translates @name into an enum drm_connector_tv_mode.
+ *
+ * Returns: the enum value on success, a negative errno otherwise.
+ */
+int drm_get_tv_mode_from_name(const char *name, size_t len)
+{
+   unsigned int i;
+
+   for (i = 0; i < ARRAY_SIZE(drm_tv_mode_enum_list); i++) {
+   const struct drm_prop_enum_list *item = 
&drm_tv_mode_enum_list[i];
+
+   if (strlen(item->name) == len && !strncmp(item->name, name, 
len))
+   return item->type;
+   }
+
+   return -EINVAL;
+}
+EXPORT_SYMBOL(drm_get_tv_mode_from_name);
+
 static const struct drm_prop_enum_list drm_tv_select_enum_list[] = {
{ DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */
{ DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 5c5e67de2296..276f5cb0f351 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1864,6 +1864,8 @@ const char *drm_get_dp_subconnector_name(int val);
 const char *drm_get_content_protection_name(int val);
 const char *drm_get_hdcp_content_type_name(int val);
 
+int drm_get_tv_mode_from_name(const char *name, size_t len);
+
 int drm_mode_create_dvi_i_properties(struct drm_device *dev);
 void drm_connector_attach_dp_subconnector_property(struct drm_connector 
*connector);
 

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 21/23] drm/vc4: vec: Convert to the new TV mode property

2022-10-26 Thread maxime
Now that the core can deal fine with analog TV modes, let's convert the vc4
VEC driver to leverage those new features.

We've added some backward compatibility to support the old TV mode property
and translate it into the new TV norm property. We're also making use of
the new analog TV atomic_check helper to make sure we trigger a modeset
whenever the TV mode is updated.

Acked-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Use new get_modes helper

Changes in v5:
- Renamed tv_mode_names into legacy_tv_mode_names

Changes in v4:
- Removed the count variable in .get_modes
---
 drivers/gpu/drm/vc4/vc4_vec.c | 185 +-
 1 file changed, 129 insertions(+), 56 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index bfa8a58dba30..1dda451c8def 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -172,6 +172,8 @@ struct vc4_vec {
 
struct clk *clock;
 
+   struct drm_property *legacy_tv_mode_property;
+
struct debugfs_regset32 regset;
 };
 
@@ -184,6 +186,12 @@ encoder_to_vc4_vec(struct drm_encoder *encoder)
return container_of(encoder, struct vc4_vec, encoder.base);
 }
 
+static inline struct vc4_vec *
+connector_to_vc4_vec(struct drm_connector *connector)
+{
+   return container_of(connector, struct vc4_vec, connector);
+}
+
 enum vc4_vec_tv_mode_id {
VC4_VEC_TV_MODE_NTSC,
VC4_VEC_TV_MODE_NTSC_J,
@@ -192,7 +200,7 @@ enum vc4_vec_tv_mode_id {
 };
 
 struct vc4_vec_tv_mode {
-   const struct drm_display_mode *mode;
+   unsigned int mode;
u32 config0;
u32 config1;
u32 custom_freq;
@@ -225,43 +233,51 @@ static const struct debugfs_reg32 vec_regs[] = {
VC4_REG32(VEC_DAC_MISC),
 };
 
-static const struct drm_display_mode ntsc_mode = {
-   DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 13500,
-720, 720 + 14, 720 + 14 + 64, 720 + 14 + 64 + 60, 0,
-480, 480 + 7, 480 + 7 + 6, 525, 0,
-DRM_MODE_FLAG_INTERLACE)
-};
-
-static const struct drm_display_mode pal_mode = {
-   DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 13500,
-720, 720 + 20, 720 + 20 + 64, 720 + 20 + 64 + 60, 0,
-576, 576 + 4, 576 + 4 + 6, 625, 0,
-DRM_MODE_FLAG_INTERLACE)
-};
-
 static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
-   [VC4_VEC_TV_MODE_NTSC] = {
-   .mode = &ntsc_mode,
+   {
+   .mode = DRM_MODE_TV_MODE_NTSC,
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
-   [VC4_VEC_TV_MODE_NTSC_J] = {
-   .mode = &ntsc_mode,
+   {
+   .mode = DRM_MODE_TV_MODE_NTSC_J,
.config0 = VEC_CONFIG0_NTSC_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
-   [VC4_VEC_TV_MODE_PAL] = {
-   .mode = &pal_mode,
+   {
+   .mode = DRM_MODE_TV_MODE_PAL,
.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
-   [VC4_VEC_TV_MODE_PAL_M] = {
-   .mode = &ntsc_mode,
+   {
+   .mode = DRM_MODE_TV_MODE_PAL_M,
.config0 = VEC_CONFIG0_PAL_M_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
 };
 
+static inline const struct vc4_vec_tv_mode *
+vc4_vec_tv_mode_lookup(unsigned int mode)
+{
+   unsigned int i;
+
+   for (i = 0; i < ARRAY_SIZE(vc4_vec_tv_modes); i++) {
+   const struct vc4_vec_tv_mode *tv_mode = &vc4_vec_tv_modes[i];
+
+   if (tv_mode->mode == mode)
+   return tv_mode;
+   }
+
+   return NULL;
+}
+
+static const struct drm_prop_enum_list legacy_tv_mode_names[] = {
+   { VC4_VEC_TV_MODE_NTSC, "NTSC", },
+   { VC4_VEC_TV_MODE_NTSC_J, "NTSC-J", },
+   { VC4_VEC_TV_MODE_PAL, "PAL", },
+   { VC4_VEC_TV_MODE_PAL_M, "PAL-M", },
+};
+
 static enum drm_connector_status
 vc4_vec_connector_detect(struct drm_connector *connector, bool force)
 {
@@ -274,21 +290,74 @@ static void vc4_vec_connector_reset(struct drm_connector 
*connector)
drm_atomic_helper_connector_tv_reset(connector);
 }
 
-static int vc4_vec_connector_get_modes(struct drm_connector *connector)
+static int
+vc4_vec_connector_set_property(struct drm_connector *connector,
+  struct drm_connector_state *state,
+  struct drm_property *property,
+  uint64_t val)
 {
-   struct drm_connector_state *state = connector->state;
-   struct drm_display_mode *mode;
-
-   mode = drm_mode_duplicate(connector->dev,
- vc4_vec_tv_modes[state->tv.legacy_mode].mode);
-   i

[PATCH v6 19/23] drm/vc4: vec: Use TV Reset implementation

2022-10-26 Thread maxime
The analog TV properties created by the drm_mode_create_tv_properties() are
not properly initialised at reset. Let's switch our implementation to call
drm_atomic_helper_connector_tv_reset().

Reviewed-by: Noralf Trønnes 
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_vec.c | 8 +++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index adc9bf99e3fd..90e375a8a8f9 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -268,6 +268,12 @@ vc4_vec_connector_detect(struct drm_connector *connector, 
bool force)
return connector_status_unknown;
 }
 
+static void vc4_vec_connector_reset(struct drm_connector *connector)
+{
+   drm_atomic_helper_connector_reset(connector);
+   drm_atomic_helper_connector_tv_reset(connector);
+}
+
 static int vc4_vec_connector_get_modes(struct drm_connector *connector)
 {
struct drm_connector_state *state = connector->state;
@@ -288,7 +294,7 @@ static int vc4_vec_connector_get_modes(struct drm_connector 
*connector)
 static const struct drm_connector_funcs vc4_vec_connector_funcs = {
.detect = vc4_vec_connector_detect,
.fill_modes = drm_helper_probe_single_connector_modes,
-   .reset = drm_atomic_helper_connector_reset,
+   .reset = vc4_vec_connector_reset,
.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
 };

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 20/23] drm/vc4: vec: Check for VEC output constraints

2022-10-26 Thread maxime
From: Mateusz Kwiatkowski 

The VEC can accept pretty much any relatively reasonable mode, but still
has a bunch of constraints to meet.

Let's create an atomic_check() implementation that will make sure we
don't end up accepting a non-functional mode.

Acked-by: Noralf Trønnes 
Signed-off-by: Mateusz Kwiatkowski 
Signed-off-by: Maxime Ripard 

---

Changes in v6:
- Used htotal instead of vtotal to discriminate PAL against NTSC
---
 drivers/gpu/drm/vc4/vc4_vec.c | 50 +++
 1 file changed, 50 insertions(+)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 90e375a8a8f9..bfa8a58dba30 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -453,6 +453,7 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder 
*encoder,
struct drm_crtc_state *crtc_state,
struct drm_connector_state *conn_state)
 {
+   const struct drm_display_mode *mode = &crtc_state->adjusted_mode;
const struct vc4_vec_tv_mode *vec_mode;
 
vec_mode = &vc4_vec_tv_modes[conn_state->tv.legacy_mode];
@@ -461,6 +462,55 @@ static int vc4_vec_encoder_atomic_check(struct drm_encoder 
*encoder,
!drm_mode_equal(vec_mode->mode, &crtc_state->adjusted_mode))
return -EINVAL;
 
+   if (mode->crtc_hdisplay % 4)
+   return -EINVAL;
+
+   if (!(mode->crtc_hsync_end - mode->crtc_hsync_start))
+   return -EINVAL;
+
+   switch (mode->htotal) {
+   /* NTSC */
+   case 858:
+   if (mode->crtc_vtotal > 262)
+   return -EINVAL;
+
+   if (mode->crtc_vdisplay < 1 || mode->crtc_vdisplay > 253)
+   return -EINVAL;
+
+   if (!(mode->crtc_vsync_start - mode->crtc_vdisplay))
+   return -EINVAL;
+
+   if ((mode->crtc_vsync_end - mode->crtc_vsync_start) != 3)
+   return -EINVAL;
+
+   if ((mode->crtc_vtotal - mode->crtc_vsync_end) < 4)
+   return -EINVAL;
+
+   break;
+
+   /* PAL/SECAM */
+   case 864:
+   if (mode->crtc_vtotal > 312)
+   return -EINVAL;
+
+   if (mode->crtc_vdisplay < 1 || mode->crtc_vdisplay > 305)
+   return -EINVAL;
+
+   if (!(mode->crtc_vsync_start - mode->crtc_vdisplay))
+   return -EINVAL;
+
+   if ((mode->crtc_vsync_end - mode->crtc_vsync_start) != 3)
+   return -EINVAL;
+
+   if ((mode->crtc_vtotal - mode->crtc_vsync_end) < 2)
+   return -EINVAL;
+
+   break;
+
+   default:
+   return -EINVAL;
+   }
+
return 0;
 }
 

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 23/23] drm/sun4i: tv: Convert to the new TV mode property

2022-10-26 Thread maxime
Now that the core can deal fine with analog TV modes, let's convert the
sun4i TV driver to leverage those new features.

Acked-by: Noralf Trønnes 
Reviewed-by: Jernej Skrabec 
Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Convert to new get_modes helper

Changes in v5:
- Removed the count variable in get_modes
- Removed spurious vc4 change
---
 drivers/gpu/drm/sun4i/sun4i_tv.c | 141 ++-
 1 file changed, 34 insertions(+), 107 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_tv.c b/drivers/gpu/drm/sun4i/sun4i_tv.c
index c65f0a89b6b0..9625a00a48ba 100644
--- a/drivers/gpu/drm/sun4i/sun4i_tv.c
+++ b/drivers/gpu/drm/sun4i/sun4i_tv.c
@@ -141,23 +141,14 @@ struct resync_parameters {
 struct tv_mode {
char*name;
 
+   unsigned inttv_mode;
+
u32 mode;
u32 chroma_freq;
u16 back_porch;
u16 front_porch;
-   u16 line_number;
u16 vblank_level;
 
-   u32 hdisplay;
-   u16 hfront_porch;
-   u16 hsync_len;
-   u16 hback_porch;
-
-   u32 vdisplay;
-   u16 vfront_porch;
-   u16 vsync_len;
-   u16 vback_porch;
-
boolyc_en;
booldac3_en;
booldac_bit25_en;
@@ -213,7 +204,7 @@ static const struct resync_parameters pal_resync_parameters 
= {
 
 static const struct tv_mode tv_modes[] = {
{
-   .name   = "NTSC",
+   .tv_mode= DRM_MODE_TV_MODE_NTSC,
.mode   = SUN4I_TVE_CFG0_RES_480i,
.chroma_freq= 0x21f07c1f,
.yc_en  = true,
@@ -222,17 +213,6 @@ static const struct tv_mode tv_modes[] = {
 
.back_porch = 118,
.front_porch= 32,
-   .line_number= 525,
-
-   .hdisplay   = 720,
-   .hfront_porch   = 18,
-   .hsync_len  = 2,
-   .hback_porch= 118,
-
-   .vdisplay   = 480,
-   .vfront_porch   = 26,
-   .vsync_len  = 2,
-   .vback_porch= 17,
 
.vblank_level   = 240,
 
@@ -242,23 +222,12 @@ static const struct tv_mode tv_modes[] = {
.resync_params  = &ntsc_resync_parameters,
},
{
-   .name   = "PAL",
+   .tv_mode= DRM_MODE_TV_MODE_PAL,
.mode   = SUN4I_TVE_CFG0_RES_576i,
.chroma_freq= 0x2a098acb,
 
.back_porch = 138,
.front_porch= 24,
-   .line_number= 625,
-
-   .hdisplay   = 720,
-   .hfront_porch   = 3,
-   .hsync_len  = 2,
-   .hback_porch= 139,
-
-   .vdisplay   = 576,
-   .vfront_porch   = 28,
-   .vsync_len  = 2,
-   .vback_porch= 19,
 
.vblank_level   = 252,
 
@@ -276,63 +245,21 @@ drm_encoder_to_sun4i_tv(struct drm_encoder *encoder)
encoder);
 }
 
-/*
- * FIXME: If only the drm_display_mode private field was usable, this
- * could go away...
- *
- * So far, it doesn't seem to be preserved when the mode is passed by
- * to mode_set for some reason.
- */
-static const struct tv_mode *sun4i_tv_find_tv_by_mode(const struct 
drm_display_mode *mode)
+static const struct tv_mode *
+sun4i_tv_find_tv_by_mode(unsigned int mode)
 {
int i;
 
-   /* First try to identify the mode by name */
for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
const struct tv_mode *tv_mode = &tv_modes[i];
 
-   DRM_DEBUG_DRIVER("Comparing mode %s vs %s",
-mode->name, tv_mode->name);
-
-   if (!strcmp(mode->name, tv_mode->name))
-   return tv_mode;
-   }
-
-   /* Then by number of lines */
-   for (i = 0; i < ARRAY_SIZE(tv_modes); i++) {
-   const struct tv_mode *tv_mode = &tv_modes[i];
-
-   DRM_DEBUG_DRIVER("Comparing mode %s vs %s (X: %d vs %d)",
-mode->name, tv_mode->name,
-mode->vdisplay, tv_mode->vdisplay);
-
-   if (mode->vdisplay == tv_mode->vdisplay)
+   if (tv_mode->tv_mode == mode)
return tv_mode;
}
 
return NULL;
 }
 
-static void sun4i_tv_mode_to_drm_mode(const struct tv_mode *tv_mode,
- struct drm_display_mode *mode)
-{
-   DRM_DEBUG_DRIVER("Creating mode %s\n", mode->name);
-
-   mode->type = DRM_MODE_TYPE_DRIVER;
-  

[PATCH v6 09/23] drm/modes: Switch to named mode descriptors

2022-10-26 Thread maxime
The current named mode parsing relies only the mode name, and doesn't allow
to specify any other parameter.

Let's convert that string list to an array of a custom structure that will
hold the name and some additional parameters in the future.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_modes.c | 17 +++--
 1 file changed, 11 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 37542612912b..7594b657f86a 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -2224,9 +2224,13 @@ static int drm_mode_parse_cmdline_options(const char 
*str,
return 0;
 }
 
-static const char * const drm_named_modes_whitelist[] = {
-   "NTSC",
-   "PAL",
+struct drm_named_mode {
+   const char *name;
+};
+
+static const struct drm_named_mode drm_named_modes[] = {
+   { "NTSC", },
+   { "PAL", },
 };
 
 static int drm_mode_parse_cmdline_named_mode(const char *name,
@@ -2258,14 +2262,15 @@ static int drm_mode_parse_cmdline_named_mode(const char 
*name,
 * We're sure we're a named mode at this point, iterate over the
 * list of modes we're aware of.
 */
-   for (i = 0; i < ARRAY_SIZE(drm_named_modes_whitelist); i++) {
+   for (i = 0; i < ARRAY_SIZE(drm_named_modes); i++) {
+   const struct drm_named_mode *mode = &drm_named_modes[i];
int ret;
 
-   ret = str_has_prefix(name, drm_named_modes_whitelist[i]);
+   ret = str_has_prefix(name, mode->name);
if (ret != name_end)
continue;
 
-   strcpy(cmdline_mode->name, drm_named_modes_whitelist[i]);
+   strcpy(cmdline_mode->name, mode->name);
cmdline_mode->specified = true;
 
return 1;

-- 
b4 0.11.0-dev-99e3a


[PATCH v6 22/23] drm/vc4: vec: Add support for more analog TV standards

2022-10-26 Thread maxime
From: Mateusz Kwiatkowski 

Add support for the following composite output modes (all of them are
somewhat more obscure than the previously defined ones):

- NTSC_443 - NTSC-style signal with the chroma subcarrier shifted to
  4.43361875 MHz (the PAL subcarrier frequency). Never used for
  broadcasting, but sometimes used as a hack to play NTSC content in PAL
  regions (e.g. on VCRs).
- PAL_N - PAL with alternative chroma subcarrier frequency,
  3.58205625 MHz. Used as a broadcast standard in Argentina, Paraguay
  and Uruguay to fit 576i50 with colour in 6 MHz channel raster.
- PAL60 - 480i60 signal with PAL-style color at normal European PAL
  frequency. Another non-standard, non-broadcast mode, used in similar
  contexts as NTSC_443. Some displays support one but not the other.
- SECAM - French frequency-modulated analog color standard; also have
  been broadcast in Eastern Europe and various parts of Africa and Asia.
  Uses the same 576i50 timings as PAL.

Also added some comments explaining color subcarrier frequency
registers.

Acked-by: Noralf Trønnes 
Signed-off-by: Mateusz Kwiatkowski 
Signed-off-by: Maxime Ripard 

---
Changes in v6:
- Support PAL60 again
---
 drivers/gpu/drm/vc4/vc4_vec.c | 111 --
 1 file changed, 107 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_vec.c b/drivers/gpu/drm/vc4/vc4_vec.c
index 1dda451c8def..d82aef168075 100644
--- a/drivers/gpu/drm/vc4/vc4_vec.c
+++ b/drivers/gpu/drm/vc4/vc4_vec.c
@@ -46,6 +46,7 @@
 #define VEC_CONFIG0_YDEL(x)((x) << 26)
 #define VEC_CONFIG0_CDEL_MASK  GENMASK(25, 24)
 #define VEC_CONFIG0_CDEL(x)((x) << 24)
+#define VEC_CONFIG0_SECAM_STD  BIT(21)
 #define VEC_CONFIG0_PBPR_FIL   BIT(18)
 #define VEC_CONFIG0_CHROMA_GAIN_MASK   GENMASK(17, 16)
 #define VEC_CONFIG0_CHROMA_GAIN_UNITY  (0 << 16)
@@ -76,6 +77,27 @@
 #define VEC_SOFT_RESET 0x10c
 #define VEC_CLMP0_START0x144
 #define VEC_CLMP0_END  0x148
+
+/*
+ * These set the color subcarrier frequency
+ * if VEC_CONFIG1_CUSTOM_FREQ is enabled.
+ *
+ * VEC_FREQ1_0 contains the most significant 16-bit half-word,
+ * VEC_FREQ3_2 contains the least significant 16-bit half-word.
+ * 0x8000 seems to be equivalent to the pixel clock
+ * (which itself is the VEC clock divided by 8).
+ *
+ * Reference values (with the default pixel clock of 13.5 MHz):
+ *
+ * NTSC  (3579545.[45] Hz) - 0x21F07C1F
+ * PAL   (4433618.75 Hz)   - 0x2A098ACB
+ * PAL-M (3575611.[888111] Hz) - 0x21E6EFE3
+ * PAL-N (3582056.25 Hz)   - 0x21F69446
+ *
+ * NOTE: For SECAM, it is used as the Dr center frequency,
+ * regardless of whether VEC_CONFIG1_CUSTOM_FREQ is enabled or not;
+ * that is specified as 4406250 Hz, which corresponds to 0x29C71C72.
+ */
 #define VEC_FREQ3_20x180
 #define VEC_FREQ1_00x184
 
@@ -118,6 +140,14 @@
 
 #define VEC_INTERRUPT_CONTROL  0x190
 #define VEC_INTERRUPT_STATUS   0x194
+
+/*
+ * Db center frequency for SECAM; the clock for this is the same as for
+ * VEC_FREQ3_2/VEC_FREQ1_0, which is used for Dr center frequency.
+ *
+ * This is specified as 425 Hz, which corresponds to 0x284BDA13.
+ * That is also the default value, so no need to set it explicitly.
+ */
 #define VEC_FCW_SECAM_B0x198
 #define VEC_SECAM_GAIN_VAL 0x19c
 
@@ -197,10 +227,15 @@ enum vc4_vec_tv_mode_id {
VC4_VEC_TV_MODE_NTSC_J,
VC4_VEC_TV_MODE_PAL,
VC4_VEC_TV_MODE_PAL_M,
+   VC4_VEC_TV_MODE_NTSC_443,
+   VC4_VEC_TV_MODE_PAL_60,
+   VC4_VEC_TV_MODE_PAL_N,
+   VC4_VEC_TV_MODE_SECAM,
 };
 
 struct vc4_vec_tv_mode {
unsigned int mode;
+   u16 expected_htotal;
u32 config0;
u32 config1;
u32 custom_freq;
@@ -236,35 +271,68 @@ static const struct debugfs_reg32 vec_regs[] = {
 static const struct vc4_vec_tv_mode vc4_vec_tv_modes[] = {
{
.mode = DRM_MODE_TV_MODE_NTSC,
+   .expected_htotal = 858,
.config0 = VEC_CONFIG0_NTSC_STD | VEC_CONFIG0_PDEN,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
+   {
+   .mode = DRM_MODE_TV_MODE_NTSC_443,
+   .expected_htotal = 858,
+   .config0 = VEC_CONFIG0_NTSC_STD,
+   .config1 = VEC_CONFIG1_C_CVBS_CVBS | VEC_CONFIG1_CUSTOM_FREQ,
+   .custom_freq = 0x2a098acb,
+   },
{
.mode = DRM_MODE_TV_MODE_NTSC_J,
+   .expected_htotal = 858,
.config0 = VEC_CONFIG0_NTSC_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
{
.mode = DRM_MODE_TV_MODE_PAL,
+   .expected_htotal = 864,
.config0 = VEC_CONFIG0_PAL_BDGHI_STD,
.config1 = VEC_CONFIG1_C_CVBS_CVBS,
},
+   {
+  

Re: [PATCH v4 4/7] drm/vc4: hdmi: Fix hdmi_enable_4kp60 detection

2022-10-26 Thread maxime
Hi Dave,

Thanks for your review

On Wed, Oct 26, 2022 at 04:36:04PM +0100, Dave Stevenson wrote:
> On Wed, 26 Oct 2022 at 16:27, Dave Stevenson
>  wrote:
> >
> > On Thu, 20 Oct 2022 at 10:14,  wrote:
> > >
> > > In order to support higher HDMI frequencies, users have to set the
> > > hdmi_enable_4kp60 parameter in their config.txt file.
> > >
> > > We were detecting this so far by calling clk_round_rate() on the core
> > > clock with the frequency we're supposed to run at when one of those
> > > modes is enabled. Whether or not the parameter was enabled could then be
> > > inferred by the returned rate since the maximum clock rate reported by
> > > the firmware was one of the side effect of setting that parameter.
> > >
> > > However, the recent clock rework we did changed what clk_round_rate()
> > > was returning to always return the minimum allowed, and thus this test
> > > wasn't reliable anymore.
> > >
> > > Let's use the new clk_get_max_rate() function to reliably determine the
> > > maximum rate allowed on that clock and fix the 4k@60Hz output.
> > >
> > > Fixes: e9d6cea2af1c ("clk: bcm: rpi: Run some clocks at the minimum rate 
> > > allowed")
> > > Signed-off-by: Maxime Ripard 
> >
> > Reviewed-by: Dave Stevenson 
> >
> > > ---
> > >  drivers/gpu/drm/vc4/vc4_hdmi.c | 3 ++-
> > >  1 file changed, 2 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c 
> > > b/drivers/gpu/drm/vc4/vc4_hdmi.c
> > > index 64f9feabf43e..87961d4de5aa 100644
> > > --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> > > +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> > > @@ -46,6 +46,7 @@
> > >  #include 
> > >  #include 
> > >  #include 
> > > +#include 
> > >  #include 
> > >  #include 
> > >  #include 
> > > @@ -3429,7 +3430,7 @@ static int vc4_hdmi_bind(struct device *dev, struct 
> > > device *master, void *data)
> > >
> > > if (variant->max_pixel_clock == 6) {
> > > struct vc4_dev *vc4 = to_vc4_dev(drm);
> > > -   long max_rate = clk_round_rate(vc4->hvs->core_clk, 
> > > 55000);
> > > +   unsigned long max_rate = 
> > > rpi_firmware_clk_get_max_rate(vc4->hvs->core_clk);
> 
> Actually minor nit:
> rpi_firmware_clk_get_max_rate returns an unsigned int.
> AFAICT we don't need the range of unsigned long in any subsequent
> code, so I think it could just be unsigned int here.
> 
> clk_round_rate returned a long, and therefore previously it did have to be 
> that.

Yeah, I was actually two-minded about this.
rpi_firmware_clk_get_max_rate() indeed returns an unsigned long, because
that's what the firmware returns.

But the clock framework uses unsigned long to store all its frequencies,
and in our case here in clk_set_min_rate().

I don't mind changing it to unsigned int here if you prefer to, and if
you're fine with the rest of the patches I can fix it up while applying
the patches.

Maxime


Re: [PATCH v4 5/7] drm/vc4: hdmi: Rework hdmi_enable_4kp60 detection code

2022-10-26 Thread maxime
Hi Dave,

On Wed, Oct 26, 2022 at 05:00:25PM +0100, Dave Stevenson wrote:
> > +
> > +   node = rpi_firmware_find_node();
> > +   if (!node)
> > +   return -EINVAL;
> > +
> > +   firmware = rpi_firmware_get(node);
> > +   of_node_put(node);
> > +   if (!firmware)
> > +   return -EPROBE_DEFER;
> > +
> > hvs->core_clk = devm_clk_get(&pdev->dev, NULL);
> > if (IS_ERR(hvs->core_clk)) {
> > dev_err(&pdev->dev, "Couldn't get core clock\n");
> > return PTR_ERR(hvs->core_clk);
> > }
> >
> > +   max_rate = rpi_firmware_clk_get_max_rate(firmware,
> > +
> > RPI_FIRMWARE_CORE_CLK_ID);
> > +   rpi_firmware_put(firmware);
> > +   if (max_rate >= 55000)
> > +   hvs->vc5_hdmi_enable_scrambling = true;
> > +
> > +   hvs->max_core_rate = max_rate;
> 
> I was going to query the reason for storing this value, but it's used
> when we get to patch 7/7.
> I won't quibble about having it as an unused value for 2 patches.

Yeah, it felt natural to do it in that patch, even though it's indeed
only useful in a couple of patches.

Maxime


[PATCH 00/10] drm/bridge: Make panel and bridge probe order consistent

2021-07-20 Thread Maxime Ripard
Hi,

We've encountered an issue with the RaspberryPi DSI panel that prevented the
whole display driver from probing.

The issue is described in detail in the commit 7213246a803f ("drm/vc4: dsi:
Only register our component once a DSI device is attached"), but the basic idea
is that since the panel is probed through i2c, there's no synchronization
between its probe and the registration of the MIPI-DSI host it's attached to.

We initially moved the component framework registration to the MIPI-DSI Host
attach hook to make sure we register our component only when we have a DSI
device attached to our MIPI-DSI host, and then use lookup our DSI device in our
bind hook.

However, all the DSI bridges controlled through i2c are only registering their
associated DSI device in their bridge attach hook, meaning with our change
above, we never got that far, and therefore ended up in the same situation than
the one we were trying to fix for panels.

Since the RaspberryPi panel is the only driver in that situation, whereas it
seems like there's a consensus in bridge drivers, it makes more sense to try to
mimic the bridge pattern in the panel driver.

However, panels don't have an attach hook, and adding more panel hooks would
lead to more path to maintain in each and every driver, while the general push
is towards bridges. We also have to make sure that each and every DSI host and
device driver behaves the same in order to have expectations to rely on.

The solution I'm proposing is thus done in several steps:

  - We get rid of the initial patch to make sure we support the bridge case,
and not the odd-panel one.

  - Add a function that returns a bridge from a DT node, reducing the amount of
churn in each and every driver and making it a real incentive to not care
about panels in display drivers but only bridges.

  - Add an attach and detach hook into the panel operations, and make it called
automatically by the DRM panel bridge.

  - Convert the VC4 DSI host to this new bridge function, and the RaspberryPi
Panel to the new attach and detach hooks.

If the general approach is agreed upon, other drivers will obviously be
converted to drm_of_get_next.

Let me know what you think,
Maxime

Maxime Ripard (10):
  Revert "drm/vc4: dsi: Only register our component once a DSI device is
attached"
  drm/bridge: Add a function to abstract away panels
  drm/bridge: Add documentation sections
  drm/bridge: Document the probe issue with MIPI-DSI bridges
  drm/panel: Create attach and detach callbacks
  drm/bridge: panel: Call attach and detach for the panel
  drm/vc4: dsi: Switch to drm_of_get_next
  drm/panel: raspberrypi-touchscreen: Prevent double-free
  drm/panel: raspberrypi-touchscreen: Use the attach hook
  drm/panel: raspberrypi-touchscreen: Remove MIPI-DSI driver

 Documentation/gpu/drm-kms-helpers.rst |  12 ++
 drivers/gpu/drm/bridge/panel.c|   4 +
 drivers/gpu/drm/drm_bridge.c  | 134 ++-
 drivers/gpu/drm/drm_of.c  |   3 +
 drivers/gpu/drm/drm_panel.c   |  20 +++
 .../drm/panel/panel-raspberrypi-touchscreen.c | 159 +-
 drivers/gpu/drm/vc4/vc4_drv.c |   2 +
 drivers/gpu/drm/vc4/vc4_dsi.c |  53 +++---
 include/drm/drm_bridge.h  |   2 +
 include/drm/drm_panel.h   |   6 +
 10 files changed, 273 insertions(+), 122 deletions(-)

-- 
2.31.1



[PATCH 01/10] Revert "drm/vc4: dsi: Only register our component once a DSI device is attached"

2021-07-20 Thread Maxime Ripard
This reverts commit 7213246a803f9b8da0677adb9ae06a3d8b806d02.

The commit 7213246a803f ("drm/vc4: dsi: Only register our component once
a DSI device is attached") aimed at preventing an endless probe loop
between the DSI host driver and its panel/bridge where both would wait
for each other to probe.

The solution implemented in that commit however relies on the fact that
MIPI-DSI device will either be a MIPI-DSI device, or would call
mipi_dsi_device_register_full() at probe time.

This assumption isn't true for bridges though where most drivers will do
so in the bridge attach hook. However, the drm_bridge_attach is usually
called in the DSI host bind hook, and thus we never get this far,
resulting in a DSI bridge that will never have its attach run, and the
DSI host that will never be bound, effectively creating the same
situation we were trying to avoid for panels.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 25 +
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index a55256ed0955..6dfcbd9e234e 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1257,12 +1257,10 @@ static ssize_t vc4_dsi_host_transfer(struct 
mipi_dsi_host *host,
return ret;
 }
 
-static const struct component_ops vc4_dsi_ops;
 static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
   struct mipi_dsi_device *device)
 {
struct vc4_dsi *dsi = host_to_dsi(host);
-   int ret;
 
dsi->lanes = device->lanes;
dsi->channel = device->channel;
@@ -1297,12 +1295,6 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host 
*host,
return 0;
}
 
-   ret = component_add(&dsi->pdev->dev, &vc4_dsi_ops);
-   if (ret) {
-   mipi_dsi_host_unregister(&dsi->dsi_host);
-   return ret;
-   }
-
return 0;
 }
 
@@ -1689,6 +1681,7 @@ static int vc4_dsi_dev_probe(struct platform_device *pdev)
 {
struct device *dev = &pdev->dev;
struct vc4_dsi *dsi;
+   int ret;
 
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
@@ -1696,10 +1689,26 @@ static int vc4_dsi_dev_probe(struct platform_device 
*pdev)
dev_set_drvdata(dev, dsi);
 
dsi->pdev = pdev;
+
+   /* Note, the initialization sequence for DSI and panels is
+* tricky.  The component bind above won't get past its
+* -EPROBE_DEFER until the panel/bridge probes.  The
+* panel/bridge will return -EPROBE_DEFER until it has a
+* mipi_dsi_host to register its device to.  So, we register
+* the host during pdev probe time, so vc4 as a whole can then
+* -EPROBE_DEFER its component bind process until the panel
+* successfully attaches.
+*/
dsi->dsi_host.ops = &vc4_dsi_host_ops;
dsi->dsi_host.dev = dev;
mipi_dsi_host_register(&dsi->dsi_host);
 
+   ret = component_add(&pdev->dev, &vc4_dsi_ops);
+   if (ret) {
+   mipi_dsi_host_unregister(&dsi->dsi_host);
+   return ret;
+   }
+
return 0;
 }
 
-- 
2.31.1



[PATCH 02/10] drm/bridge: Add a function to abstract away panels

2021-07-20 Thread Maxime Ripard
Display drivers so far need to have a lot of boilerplate to first
retrieve either the panel or bridge that they are connected to using
drm_of_find_panel_or_bridge(), and then either deal with each with ad-hoc
functions or create a drm panel bridge through drm_panel_bridge_add.

In order to reduce the boilerplate and hopefully create a path of least
resistance towards using the DRM panel bridge layer, let's create the
function devm_drm_of_get_next to reduce that boilerplate.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_bridge.c | 62 +---
 drivers/gpu/drm/drm_of.c |  3 ++
 include/drm/drm_bridge.h |  2 ++
 3 files changed, 63 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 044acd07c153..aef8c9f4fb9f 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -24,10 +24,12 @@
 #include 
 #include 
 #include 
+#include 
 
 #include 
 #include 
 #include 
+#include 
 
 #include "drm_crtc_internal.h"
 
@@ -50,10 +52,8 @@
  *
  * Display drivers are responsible for linking encoders with the first bridge
  * in the chains. This is done by acquiring the appropriate bridge with
- * of_drm_find_bridge() or drm_of_find_panel_or_bridge(), or creating it for a
- * panel with drm_panel_bridge_add_typed() (or the managed version
- * devm_drm_panel_bridge_add_typed()). Once acquired, the bridge shall be
- * attached to the encoder with a call to drm_bridge_attach().
+ * drm_of_get_next(). Once acquired, the bridge shall be attached to the
+ * encoder with a call to drm_bridge_attach().
  *
  * Bridges are responsible for linking themselves with the next bridge in the
  * chain, if any. This is done the same way as for encoders, with the call to
@@ -1223,6 +1223,60 @@ struct drm_bridge *of_drm_find_bridge(struct device_node 
*np)
return NULL;
 }
 EXPORT_SYMBOL(of_drm_find_bridge);
+
+/**
+ * devm_drm_of_get_next - Return next bridge in the chain
+ * @dev: device to tie the bridge lifetime to
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ *
+ * Given a DT node's port and endpoint number, finds the connected node
+ * and returns the associated bridge if any, or creates and returns a
+ * drm panel bridge instance if a panel is connected.
+ *
+ * Returns a pointer to the bridge if successful, or an error pointer
+ * otherwise.
+ */
+struct drm_bridge *devm_drm_of_get_next(struct device *dev,
+   struct device_node *np,
+   unsigned int port,
+   unsigned int endpoint)
+{
+   struct device_node *remote;
+   struct drm_bridge *bridge;
+   struct drm_panel *panel;
+
+   /*
+* of_graph_get_remote_node() produces a noisy error message if port
+* node isn't found and the absence of the port is a legit case here,
+* so at first we silently check whether graph presents in the
+* device-tree node.
+*/
+   if (!of_graph_is_present(np))
+   return ERR_PTR(-ENODEV);
+
+   remote = of_graph_get_remote_node(np, port, endpoint);
+   if (!remote)
+   return ERR_PTR(-ENODEV);
+
+   bridge = of_drm_find_bridge(remote);
+   if (bridge) {
+   of_node_put(remote);
+   return bridge;
+   }
+
+   panel = of_drm_find_panel(remote);
+   if (IS_ERR(panel)) {
+   of_node_put(remote);
+   return ERR_CAST(panel);
+   }
+
+   of_node_put(remote);
+
+   return devm_drm_panel_bridge_add(dev, panel);
+}
+EXPORT_SYMBOL(devm_drm_of_get_next);
 #endif
 
 MODULE_AUTHOR("Ajay Kumar ");
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index 997b8827fed2..bbbdc4d17ac9 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -231,6 +231,9 @@ EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
  * return either the associated struct drm_panel or drm_bridge device. Either
  * @panel or @bridge must not be NULL.
  *
+ * This function is deprecated and should not be used in new drivers. Use
+ * drm_of_get_next() instead.
+ *
  * Returns zero if successful, or one of the standard error codes if it fails.
  */
 int drm_of_find_panel_or_bridge(const struct device_node *np,
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 46bdfa48c413..e16fafc6f37d 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -911,6 +911,8 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device 
*dev,
 struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
   struct drm_panel *panel,
   u32 connector_type);
+struct drm_bridge *devm_drm_of_get_next(struct dev

[PATCH 03/10] drm/bridge: Add documentation sections

2021-07-20 Thread Maxime Ripard
The bridge documentation overview is quite packed already, and we'll add
some more documentation that isn't part of an overview at all.

Let's add some sections to the documentation to separare each bits.

Signed-off-by: Maxime Ripard 
---
 Documentation/gpu/drm-kms-helpers.rst |  6 ++
 drivers/gpu/drm/drm_bridge.c  | 14 +-
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/Documentation/gpu/drm-kms-helpers.rst 
b/Documentation/gpu/drm-kms-helpers.rst
index 389892f36185..10f8df7aecc0 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -151,6 +151,12 @@ Overview
 .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: overview
 
+Display Driver Integration
+--
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :doc: display driver integration
+
 Bridge Operations
 -
 
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index aef8c9f4fb9f..c9a950bfdfe5 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -50,6 +50,15 @@
  * Chaining multiple bridges to the output of a bridge, or the same bridge to
  * the output of different bridges, is not supported.
  *
+ * &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes,
+ * CRTCs, encoders or connectors and hence are not visible to userspace. They
+ * just provide additional hooks to get the desired output at the end of the
+ * encoder chain.
+ */
+
+/**
+ * DOC:display driver integration
+ *
  * Display drivers are responsible for linking encoders with the first bridge
  * in the chains. This is done by acquiring the appropriate bridge with
  * drm_of_get_next(). Once acquired, the bridge shall be attached to the
@@ -84,11 +93,6 @@
  * helper to create the &drm_connector, or implement it manually on top of the
  * connector-related operations exposed by the bridge (see the overview
  * documentation of bridge operations for more details).
- *
- * &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes,
- * CRTCs, encoders or connectors and hence are not visible to userspace. They
- * just provide additional hooks to get the desired output at the end of the
- * encoder chain.
  */
 
 static DEFINE_MUTEX(bridge_lock);
-- 
2.31.1



[PATCH 04/10] drm/bridge: Document the probe issue with MIPI-DSI bridges

2021-07-20 Thread Maxime Ripard
Interactions between bridges, panels, MIPI-DSI host and the component
framework are not trivial and can lead to probing issues when
implementing a display driver. Let's document the various cases we need
too consider, and the solution to support all the cases.

Signed-off-by: Maxime Ripard 
---
 Documentation/gpu/drm-kms-helpers.rst |  6 +++
 drivers/gpu/drm/drm_bridge.c  | 60 +++
 2 files changed, 66 insertions(+)

diff --git a/Documentation/gpu/drm-kms-helpers.rst 
b/Documentation/gpu/drm-kms-helpers.rst
index 10f8df7aecc0..ec2f65b31930 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -157,6 +157,12 @@ Display Driver Integration
 .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: display driver integration
 
+Special Care with MIPI-DSI bridges
+--
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :doc: special care dsi
+
 Bridge Operations
 -
 
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index c9a950bfdfe5..81f8dac12367 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -95,6 +95,66 @@
  * documentation of bridge operations for more details).
  */
 
+/**
+ * DOC: special care dsi
+ *
+ * The interaction between the bridges and other frameworks involved in
+ * the probing of the display driver and the bridge driver can be
+ * challenging. Indeed, there's multiple cases that needs to be
+ * considered:
+ *
+ * - The display driver doesn't use the component framework and isn't a
+ *   MIPI-DSI host. In this case, the bridge driver will probe at some
+ *   point and the display driver should try to probe again by returning
+ *   EPROBE_DEFER as long as the bridge driver hasn't probed.
+ *
+ * - The display driver doesn't use the component framework, but is a
+ *   MIPI-DSI host. The bridge device uses the MIPI-DCS commands to be
+ *   controlled. In this case, the bridge device is a child of the
+ *   display device and when it will probe it's assured that the display
+ *   device (and MIPI-DSI host) is present. The display driver will be
+ *   assured that the bridge driver is connected between the
+ *   &mipi_dsi_host_ops.attach and &mipi_dsi_host_ops.detach operations.
+ *   Therefore, it must run mipi_dsi_host_register() in its probe
+ *   function, and then run drm_bridge_attach() in its
+ *   &mipi_dsi_host_ops.attach hook.
+ *
+ * - The display driver uses the component framework and is a MIPI-DSI
+ *   host. The bridge device uses the MIPI-DCS commands to be
+ *   controlled. This is the same situation than above, and can run
+ *   mipi_dsi_host_register() in either its probe or bind hooks.
+ *
+ * - The display driver uses the component framework and is a MIPI-DSI
+ *   host. The bridge device uses a separate bus (such as I2C) to be
+ *   controlled. In this case, there's no correlation between the probe
+ *   of the bridge and display drivers, so care must be taken to avoid
+ *   an endless EPROBE_DEFER loop, with each driver waiting for the
+ *   other to probe.
+ *
+ * The ideal pattern to cover the last item (and all the others in the
+ * display driver case) is to split the operations like this:
+ *
+ * - In the display driver must run mipi_dsi_host_register() and
+ *   component_add in its probe hook. It will make sure that the
+ *   MIPI-DSI host sticks around, and that the driver's bind can be
+ *   called.
+ *
+ * - In its probe hook, the bridge driver must not try to find its
+ *   MIPI-DSI host or register as a MIPI-DSI device. As far as the
+ *   framework is concerned, it must only call drm_bridge_add().
+ *
+ * - In its bind hook, the display driver must try to find the bridge
+ *   and return -EPROBE_DEFER if it doesn't find it. If it's there, it
+ *   must call drm_bridge_attach(). The MIPI-DSI host is now functional.
+ *
+ * - In its &drm_bridge_funcs.attach hook, the bridge driver can now try
+ *   to find its MIPI-DSI host and can register as a MIPI-DSI device.
+ *
+ * At this point, we're now certain that both the display driver and the
+ * bridge driver are functional and we can't have a deadlock-like
+ * situation when probing.
+ */
+
 static DEFINE_MUTEX(bridge_lock);
 static LIST_HEAD(bridge_list);
 
-- 
2.31.1



[PATCH 05/10] drm/panel: Create attach and detach callbacks

2021-07-20 Thread Maxime Ripard
In order to make the probe order expectation more consistent between
bridges, let's create attach and detach hooks for the panels as well to
match what is there for bridges.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_panel.c | 20 
 include/drm/drm_panel.h |  6 ++
 2 files changed, 26 insertions(+)

diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index f634371c717a..23bca798a2f3 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -223,6 +223,26 @@ int drm_panel_get_modes(struct drm_panel *panel,
 }
 EXPORT_SYMBOL(drm_panel_get_modes);
 
+int drm_panel_attach(struct drm_panel *panel)
+{
+   if (!panel)
+   return -EINVAL;
+
+   if (panel->funcs && panel->funcs->attach)
+   return panel->funcs->attach(panel);
+
+   return -EOPNOTSUPP;
+}
+
+void drm_panel_detach(struct drm_panel *panel)
+{
+   if (!panel)
+   return;
+
+   if (panel->funcs && panel->funcs->detach)
+   panel->funcs->detach(panel);
+}
+
 #ifdef CONFIG_OF
 /**
  * of_drm_find_panel - look up a panel using a device tree node
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 4602f833eb51..b9201d520754 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -68,6 +68,9 @@ enum drm_panel_orientation;
  * does not need to implement the functionality to enable/disable backlight.
  */
 struct drm_panel_funcs {
+   int (*attach)(struct drm_panel *panel);
+   void (*detach)(struct drm_panel *panel);
+
/**
 * @prepare:
 *
@@ -180,6 +183,9 @@ void drm_panel_init(struct drm_panel *panel, struct device 
*dev,
 void drm_panel_add(struct drm_panel *panel);
 void drm_panel_remove(struct drm_panel *panel);
 
+int drm_panel_attach(struct drm_panel *panel);
+void drm_panel_detach(struct drm_panel *panel);
+
 int drm_panel_prepare(struct drm_panel *panel);
 int drm_panel_unprepare(struct drm_panel *panel);
 
-- 
2.31.1



[PATCH 06/10] drm/bridge: panel: Call attach and detach for the panel

2021-07-20 Thread Maxime Ripard
Now that we have additional attach and detach hooks for panels, make
sure that the panel bridge driver calls them when relevant.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/bridge/panel.c | 4 
 1 file changed, 4 insertions(+)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index c916f4b8907e..c2249f3fd357 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -60,6 +60,8 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
struct drm_connector *connector = &panel_bridge->connector;
int ret;
 
+   drm_panel_attach(panel_bridge->panel);
+
if (flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)
return 0;
 
@@ -90,6 +92,8 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_connector *connector = &panel_bridge->connector;
 
+   drm_panel_detach(panel_bridge->panel);
+
/*
 * Cleanup the connector if we know it was initialized.
 *
-- 
2.31.1



[PATCH 07/10] drm/vc4: dsi: Switch to drm_of_get_next

2021-07-20 Thread Maxime Ripard
The new drm_of_get_next removes most of the boilerplate we have to deal
with. Let's switch to it.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_drv.c |  2 ++
 drivers/gpu/drm/vc4/vc4_dsi.c | 28 
 2 files changed, 6 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 73335feb712f..ff056ee8bc4b 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -25,7 +25,9 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 6dfcbd9e234e..f51ce8db0f4e 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1489,7 +1489,6 @@ static int vc4_dsi_bind(struct device *dev, struct device 
*master, void *data)
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dsi *dsi = dev_get_drvdata(dev);
struct vc4_dsi_encoder *vc4_dsi_encoder;
-   struct drm_panel *panel;
const struct of_device_id *match;
dma_cap_mask_t dma_mask;
int ret;
@@ -1601,27 +1600,9 @@ static int vc4_dsi_bind(struct device *dev, struct 
device *master, void *data)
return ret;
}
 
-   ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
- &panel, &dsi->bridge);
-   if (ret) {
-   /* If the bridge or panel pointed by dev->of_node is not
-* enabled, just return 0 here so that we don't prevent the DRM
-* dev from being registered. Of course that means the DSI
-* encoder won't be exposed, but that's not a problem since
-* nothing is connected to it.
-*/
-   if (ret == -ENODEV)
-   return 0;
-
-   return ret;
-   }
-
-   if (panel) {
-   dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
- 
DRM_MODE_CONNECTOR_DSI);
-   if (IS_ERR(dsi->bridge))
-   return PTR_ERR(dsi->bridge);
-   }
+   dsi->bridge = devm_drm_of_get_next(dev, dev->of_node, 0, 0);
+   if (IS_ERR(dsi->bridge))
+   return PTR_ERR(dsi->bridge);
 
/* The esc clock rate is supposed to always be 100Mhz. */
ret = clk_set_rate(dsi->escape_clock, 100 * 100);
@@ -1661,8 +1642,7 @@ static void vc4_dsi_unbind(struct device *dev, struct 
device *master,
 {
struct vc4_dsi *dsi = dev_get_drvdata(dev);
 
-   if (dsi->bridge)
-   pm_runtime_disable(dev);
+   pm_runtime_disable(dev);
 
/*
 * Restore the bridge_chain so the bridge detach procedure can happen
-- 
2.31.1



[PATCH 08/10] drm/panel: raspberrypi-touchscreen: Prevent double-free

2021-07-20 Thread Maxime Ripard
The mipi_dsi_device allocated by mipi_dsi_device_register_full() is
already free'd on release.

Fixes: 2f733d6194bd ("drm/panel: Add support for the Raspberry Pi 7" 
Touchscreen.")
Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c 
b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 6f2ce3b81f47..462faae0f446 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -447,7 +447,6 @@ static int rpi_touchscreen_remove(struct i2c_client *i2c)
drm_panel_remove(&ts->base);
 
mipi_dsi_device_unregister(ts->dsi);
-   kfree(ts->dsi);
 
return 0;
 }
-- 
2.31.1



[PATCH 09/10] drm/panel: raspberrypi-touchscreen: Use the attach hook

2021-07-20 Thread Maxime Ripard
Now that we have an attach hook available for panels as well, let's use
it for the RaspberryPi 7" DSI panel.

This now mimics what all the other bridges in a similar situation are
doing, and we avoid our probe order issue entirely.

Signed-off-by: Maxime Ripard 
---
 .../drm/panel/panel-raspberrypi-touchscreen.c | 135 ++
 1 file changed, 77 insertions(+), 58 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c 
b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 462faae0f446..995c5cafb970 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -346,7 +346,83 @@ static int rpi_touchscreen_get_modes(struct drm_panel 
*panel,
return num;
 }
 
+static int rpi_touchscreen_attach(struct drm_panel *panel)
+{
+   struct rpi_touchscreen *ts = panel_to_ts(panel);
+   struct device *dev = &ts->i2c->dev;
+   struct device_node *endpoint, *dsi_host_node;
+   struct mipi_dsi_device *dsi;
+   struct mipi_dsi_host *host;
+   int ret;
+
+   struct mipi_dsi_device_info info = {
+   .type = RPI_DSI_DRIVER_NAME,
+   .channel = 0,
+   .node = NULL,
+   };
+
+   /* Look up the DSI host.  It needs to probe before we do. */
+   endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+   if (!endpoint)
+   return -ENODEV;
+
+   dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+   if (!dsi_host_node) {
+   of_node_put(endpoint);
+   return -ENODEV;
+   }
+
+   host = of_find_mipi_dsi_host_by_node(dsi_host_node);
+   of_node_put(dsi_host_node);
+   if (!host) {
+   of_node_put(endpoint);
+   return -EPROBE_DEFER;
+   }
+
+   info.node = of_graph_get_remote_port(endpoint);
+   if (!info.node) {
+   of_node_put(endpoint);
+   return -ENODEV;
+   }
+
+   of_node_put(endpoint);
+
+   dsi = mipi_dsi_device_register_full(host, &info);
+   if (IS_ERR(dsi)) {
+   dev_err(dev, "DSI device registration failed: %ld\n",
+   PTR_ERR(dsi));
+   return PTR_ERR(dsi);
+   }
+
+   ts->dsi = dsi;
+
+   dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+  MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+  MIPI_DSI_MODE_LPM);
+   dsi->format = MIPI_DSI_FMT_RGB888;
+   dsi->lanes = 1;
+
+   ret = mipi_dsi_attach(dsi);
+   if (ret) {
+   dev_err(&dsi->dev, "failed to attach dsi to host: %d\n", ret);
+   return ret;
+   }
+
+   return 0;
+}
+
+static void rpi_touchscreen_detach(struct drm_panel *panel)
+{
+   struct rpi_touchscreen *ts = panel_to_ts(panel);
+
+   mipi_dsi_detach(ts->dsi);
+   mipi_dsi_device_unregister(ts->dsi);
+}
+
 static const struct drm_panel_funcs rpi_touchscreen_funcs = {
+   .attach = rpi_touchscreen_attach,
+   .detach = rpi_touchscreen_detach,
+
.disable = rpi_touchscreen_disable,
.unprepare = rpi_touchscreen_noop,
.prepare = rpi_touchscreen_noop,
@@ -359,14 +435,7 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
 {
struct device *dev = &i2c->dev;
struct rpi_touchscreen *ts;
-   struct device_node *endpoint, *dsi_host_node;
-   struct mipi_dsi_host *host;
int ver;
-   struct mipi_dsi_device_info info = {
-   .type = RPI_DSI_DRIVER_NAME,
-   .channel = 0,
-   .node = NULL,
-   };
 
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
@@ -394,35 +463,6 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
/* /\* Turn off at boot, so we can cleanly sequence powering on. *\/ */
/* rpi_touchscreen_i2c_write(ts, REG_POWERON, 0); */
 
-   /* Look up the DSI host.  It needs to probe before we do. */
-   endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
-   if (!endpoint)
-   return -ENODEV;
-
-   dsi_host_node = of_graph_get_remote_port_parent(endpoint);
-   if (!dsi_host_node)
-   goto error;
-
-   host = of_find_mipi_dsi_host_by_node(dsi_host_node);
-   of_node_put(dsi_host_node);
-   if (!host) {
-   of_node_put(endpoint);
-   return -EPROBE_DEFER;
-   }
-
-   info.node = of_graph_get_remote_port(endpoint);
-   if (!info.node)
-   goto error;
-
-   of_node_put(endpoint);
-
-   ts->dsi = mipi_dsi_device_register_full(host, &info);
-   if (IS_ERR(ts->dsi)) {
-   dev_err(dev, "DSI device registration failed: %ld\n",
-   PTR_ERR(ts->dsi));
-   return PTR_ERR(ts->dsi);
-   }
-
drm_panel_init(

[PATCH 10/10] drm/panel: raspberrypi-touchscreen: Remove MIPI-DSI driver

2021-07-20 Thread Maxime Ripard
The driver was using a two-steps initialisation when probing with the
i2c probe first registering the MIPI-DSI device, and then when that
device was probed the driver would attach the device to its host. This
resulted in a fairly non-standard probe logic.

The previous commit changed that logic entirely though, resulting in a
completely empty MIPI-DSI device probe. Let's simplify the driver by
removing it entirely and just behave as a normal i2c driver.

Signed-off-by: Maxime Ripard 
---
 .../drm/panel/panel-raspberrypi-touchscreen.c | 25 +--
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c 
b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 995c5cafb970..09937aa26c6a 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -483,16 +483,6 @@ static int rpi_touchscreen_remove(struct i2c_client *i2c)
return 0;
 }
 
-static int rpi_touchscreen_dsi_probe(struct mipi_dsi_device *dsi)
-{
-   return 0;
-}
-
-static struct mipi_dsi_driver rpi_touchscreen_dsi_driver = {
-   .driver.name = RPI_DSI_DRIVER_NAME,
-   .probe = rpi_touchscreen_dsi_probe,
-};
-
 static const struct of_device_id rpi_touchscreen_of_ids[] = {
{ .compatible = "raspberrypi,7inch-touchscreen-panel" },
{ } /* sentinel */
@@ -507,20 +497,7 @@ static struct i2c_driver rpi_touchscreen_driver = {
.probe = rpi_touchscreen_probe,
.remove = rpi_touchscreen_remove,
 };
-
-static int __init rpi_touchscreen_init(void)
-{
-   mipi_dsi_driver_register(&rpi_touchscreen_dsi_driver);
-   return i2c_add_driver(&rpi_touchscreen_driver);
-}
-module_init(rpi_touchscreen_init);
-
-static void __exit rpi_touchscreen_exit(void)
-{
-   i2c_del_driver(&rpi_touchscreen_driver);
-   mipi_dsi_driver_unregister(&rpi_touchscreen_dsi_driver);
-}
-module_exit(rpi_touchscreen_exit);
+module_i2c_driver(rpi_touchscreen_driver);
 
 MODULE_AUTHOR("Eric Anholt ");
 MODULE_DESCRIPTION("Raspberry Pi 7-inch touchscreen driver");
-- 
2.31.1



[PATCH v6] Documentation: gpu: Mention the requirements for new properties

2021-07-20 Thread Maxime Ripard
New KMS properties come with a bunch of requirements to avoid each
driver from running their own, inconsistent, set of properties,
eventually leading to issues like property conflicts, inconsistencies
between drivers and semantics, etc.

Let's document what we expect.

Cc: Alexandre Belloni 
Cc: Alexandre Torgue 
Cc: Alex Deucher 
Cc: Alison Wang 
Cc: Alyssa Rosenzweig 
Cc: Andrew Jeffery 
Cc: Andrzej Hajda 
Cc: Anitha Chrisanthus 
Cc: Benjamin Gaignard 
Cc: Ben Skeggs 
Cc: Boris Brezillon 
Cc: Brian Starkey 
Cc: Chen Feng 
Cc: Chen-Yu Tsai 
Cc: Christian Gmeiner 
Cc: "Christian König" 
Cc: Chun-Kuang Hu 
Cc: Edmund Dea 
Cc: Eric Anholt 
Cc: Fabio Estevam 
Cc: Gerd Hoffmann 
Cc: Haneen Mohammed 
Cc: Hans de Goede 
Cc: "Heiko Stübner" 
Cc: Huang Rui 
Cc: Hyun Kwon 
Cc: Inki Dae 
Cc: Jani Nikula 
Cc: Jernej Skrabec 
Cc: Jerome Brunet 
Cc: John Stultz 
Cc: Jonas Karlman 
Cc: Jonathan Hunter 
Cc: Joonas Lahtinen 
Cc: Joonyoung Shim 
Cc: Jyri Sarha 
Cc: Kevin Hilman 
Cc: Kieran Bingham 
Cc: Krzysztof Kozlowski 
Cc: Kyungmin Park 
Cc: Laurent Pinchart 
Cc: Linus Walleij 
Cc: Liviu Dudau 
Cc: Lucas Stach 
Cc: Ludovic Desroches 
Cc: Marek Vasut 
Cc: Martin Blumenstingl 
Cc: Matthias Brugger 
Cc: Maxime Coquelin 
Cc: Maxime Ripard 
Cc: Melissa Wen 
Cc: Neil Armstrong 
Cc: Nicolas Ferre 
Cc: "Noralf Trønnes" 
Cc: NXP Linux Team 
Cc: Oleksandr Andrushchenko 
Cc: Patrik Jakobsson 
Cc: Paul Cercueil 
Cc: Pekka Paalanen 
Cc: Pengutronix Kernel Team 
Cc: Philippe Cornu 
Cc: Philipp Zabel 
Cc: Qiang Yu 
Cc: Rob Clark 
Cc: Robert Foss 
Cc: Rob Herring 
Cc: Rodrigo Siqueira 
Cc: Rodrigo Vivi 
Cc: Roland Scheidegger 
Cc: Russell King 
Cc: Sam Ravnborg 
Cc: Sandy Huang 
Cc: Sascha Hauer 
Cc: Sean Paul 
Cc: Seung-Woo Kim 
Cc: Shawn Guo 
Cc: Simon Ser 
Cc: Stefan Agner 
Cc: Steven Price 
Cc: Sumit Semwal 
Cc: Thierry Reding 
Cc: Tian Tao 
Cc: Tomeu Vizoso 
Cc: Tomi Valkeinen 
Cc: VMware Graphics 
Cc: Xinliang Liu 
Cc: Xinwei Kong 
Cc: Yannick Fertre 
Cc: Zack Rusin 
Reviewed-by: Pekka Paalanen 
Reviewed-by: Daniel Vetter 
Signed-off-by: Maxime Ripard 

---

Changes from v5:
  - Typos fixes suggested by Pekka and Daniel
  - Added Pekka Reviewed-by

Changes from v4:
  - Changes suggested by Pekka

Changes from v3:
  - Roll back to the v2
  - Add Simon and Pekka in Cc

Changes from v2:
  - Take into account the feedback from Laurent and Lidiu to no longer
force generic properties, but prefix vendor-specific properties with
the vendor name

Changes from v1:
  - Typos and wording reported by Daniel and Alex
---
 Documentation/gpu/drm-kms.rst | 29 +
 1 file changed, 29 insertions(+)

diff --git a/Documentation/gpu/drm-kms.rst b/Documentation/gpu/drm-kms.rst
index 87e5023e3f55..12e25119e563 100644
--- a/Documentation/gpu/drm-kms.rst
+++ b/Documentation/gpu/drm-kms.rst
@@ -463,6 +463,35 @@ KMS Properties
 This section of the documentation is primarily aimed at user-space developers.
 For the driver APIs, see the other sections.
 
+Requirements
+
+
+KMS drivers might need to add extra properties to support new features. Each
+new property introduced in a driver needs to meet a few requirements, in
+addition to the one mentioned above:
+
+* It must be standardized, documenting:
+
+  * The full, exact, name string;
+  * If the property is an enum, all the valid value name strings;
+  * What values are accepted, and what these values mean;
+  * What the property does and how it can be used;
+  * How the property might interact with other, existing properties.
+
+* It must provide a generic helper in the core code to register that
+  property on the object it attaches to.
+
+* Its content must be decoded by the core and provided in the object's
+  associated state structure. That includes anything drivers might want
+  to precompute, like struct drm_clip_rect for planes.
+
+* Its initial state must match the behavior prior to the property
+  introduction. This might be a fixed value matching what the hardware
+  does, or it may be inherited from the state the firmware left the
+  system in during boot.
+
+* An IGT test must be submitted where reasonable.
+
 Property Types and Blob Property Support
 
 
-- 
2.31.1



[PATCH 10/54] dt-bindings: display: panel-lvds: Document panel compatibles

2021-07-21 Thread Maxime Ripard
The binding mentions that all the drivers using that driver must use a
vendor-specific compatible but never enforces it, nor documents the
vendor-specific compatibles.

Let's make we document all of them, and that the binding will create an
error if we add one that isn't.

Cc: dri-devel@lists.freedesktop.org
Cc: Laurent Pinchart 
Cc: Sam Ravnborg 
Cc: Thierry Reding 
Signed-off-by: Maxime Ripard 
---
 .../bindings/display/panel/lvds.yaml   | 18 --
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/panel/lvds.yaml 
b/Documentation/devicetree/bindings/display/panel/lvds.yaml
index 49460c9dceea..d1513111eb48 100644
--- a/Documentation/devicetree/bindings/display/panel/lvds.yaml
+++ b/Documentation/devicetree/bindings/display/panel/lvds.yaml
@@ -31,12 +31,18 @@ allOf:
 
 properties:
   compatible:
-contains:
-  const: panel-lvds
-description:
-  Shall contain "panel-lvds" in addition to a mandatory panel-specific
-  compatible string defined in individual panel bindings. The "panel-lvds"
-  value shall never be used on its own.
+items:
+  - enum:
+  - advantech,idk-1110wr
+  - advantech,idk-2121wr
+  - auo,b101ew05
+  - innolux,ee101ia-01d
+  - mitsubishi,aa104xd12
+  - mitsubishi,aa121td01
+  - sgd,gktw70sdae4se
+  - sharp,lq150x1lg11
+  - tbs,a711-panel
+  - const: panel-lvds
 
   data-mapping:
 enum:
-- 
2.31.1



[PATCH 11/54] dt-bindings: display: simple-bridge: Add corpro, gm7123 compatible

2021-07-21 Thread Maxime Ripard
The corpro,gm7123 was in use in a DT but was never properly documented,
let's add it.

Cc: dri-devel@lists.freedesktop.org
Reviewed-by: Laurent Pinchart 
Signed-off-by: Maxime Ripard 

---

Changes from v1:
  - Removed the dumb-vga-dac compatible from the list
---
 .../devicetree/bindings/display/bridge/simple-bridge.yaml  | 3 +++
 1 file changed, 3 insertions(+)

diff --git 
a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml 
b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
index 6c7b577fd471..43cf4df9811a 100644
--- a/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
+++ b/Documentation/devicetree/bindings/display/bridge/simple-bridge.yaml
@@ -22,6 +22,9 @@ properties:
   - ti,ths8134a
   - ti,ths8134b
   - const: ti,ths8134
+  - items:
+  - const: corpro,gm7123
+  - const: adi,adv7123
   - enum:
   - adi,adv7123
   - dumb-vga-dac
-- 
2.31.1



Re: [PATCH v1 1/7] drm/bridge: ps8640: Use atomic variants of drm_bridge_funcs

2021-07-22 Thread Maxime Ripard
On Thu, Jul 22, 2021 at 08:22:40AM +0200, Sam Ravnborg wrote:
> The atomic variants of enable/disable in drm_bridge_funcs are the
> preferred operations - introduce these.
> 
> The ps8640 driver used the non-atomic variants of the drm_bridge chain
> functions - convert these to the atomic variants.
> 
> Signed-off-by: Sam Ravnborg 
> Cc: Andrzej Hajda 
> Cc: Neil Armstrong 
> Cc: Robert Foss 
> Cc: Laurent Pinchart 
> Cc: Jonas Karlman 
> Cc: Jernej Skrabec 

Reviewed-by: Maxime Ripard 

Maxime


Re: [PATCH v1 2/7] drm/bridge: Drop unused drm_bridge_chain functions

2021-07-22 Thread Maxime Ripard
On Thu, Jul 22, 2021 at 08:22:41AM +0200, Sam Ravnborg wrote:
> The drm_bridge_chain_{pre_enable,enable,disable,post_disable} has no
> users left and we have atomic variants that should be used.
> Drop them so they do not gain new users.
> 
> Adjust a few comments to avoid references to the dropped functions.
> 
> Signed-off-by: Sam Ravnborg 
> Cc: Laurent Pinchart 
> Cc: Maarten Lankhorst 
> Cc: Maxime Ripard 
> Cc: Thomas Zimmermann 
> Cc: Andrzej Hajda 
> Cc: Neil Armstrong 
> Cc: Robert Foss 
> Cc: Daniel Vetter 

Reviewed-by: Maxime Ripard 

Maxime


Re: [PATCH v1 3/7] drm/bridge: Add drm_bridge_new_crtc_state() helper

2021-07-22 Thread Maxime Ripard
Hi,

On Thu, Jul 22, 2021 at 08:22:42AM +0200, Sam Ravnborg wrote:
> drm_bridge_new_crtc_state() will be used by bridge drivers to provide
> easy access to the mode from the drm_bridge_funcs operations.
> 
> The helper will be useful in the atomic operations of
> struct drm_bridge_funcs.
> 
> Signed-off-by: Sam Ravnborg 
> Suggested-by: Laurent Pinchart 
> Cc: Laurent Pinchart 
> Cc: Maarten Lankhorst 
> Cc: Maxime Ripard 
> Cc: Thomas Zimmermann 
> Cc: Andrzej Hajda 
> Cc: Neil Armstrong 
> Cc: Robert Foss 
> Cc: Daniel Vetter 
> ---
>  drivers/gpu/drm/drm_atomic.c | 34 ++
>  include/drm/drm_atomic.h |  3 +++
>  2 files changed, 37 insertions(+)
> 
> diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
> index a8bbb021684b..93d513078e9a 100644
> --- a/drivers/gpu/drm/drm_atomic.c
> +++ b/drivers/gpu/drm/drm_atomic.c
> @@ -1133,6 +1133,40 @@ drm_atomic_get_new_bridge_state(struct 
> drm_atomic_state *state,
>  }
>  EXPORT_SYMBOL(drm_atomic_get_new_bridge_state);
>  
> +/**
> + * drm_bridge_new_crtc_state - get crtc_state for the bridge
> + * @bridge: bridge object
> + * @old_bridge_state: state of the bridge
> + *
> + * This function returns the &struct drm_crtc_state for the given 
> bridge/state,
> + * or NULL if no crtc_state could be looked up. In case no crtc_state then 
> this is
> + * logged using WARN as the crtc_state is always expected to be present.
> + * This function is often used in the &struct drm_bridge_funcs operations.
> + */
> +const struct drm_crtc_state *
> +drm_bridge_new_crtc_state(struct drm_bridge *bridge,

Shouldn't we call this drm_atomic_bridge_get_new_crtc_state for consistency?

> +   struct drm_bridge_state *old_bridge_state)

It seems odd to me to name it old_bridge_state, I guess it would make
more sense to pass in the new bridge state?

> +{
> + struct drm_atomic_state *state = old_bridge_state->base.state;
> + const struct drm_connector_state *conn_state;
> + const struct drm_crtc_state *crtc_state;
> + struct drm_connector *connector;
> +
> + connector = drm_atomic_get_new_connector_for_encoder(state, 
> bridge->encoder);
> + if (WARN_ON(!connector))
> + return NULL;
> +
> + conn_state = drm_atomic_get_new_connector_state(state, connector);
> + if (WARN_ON(!conn_state || !conn_state->crtc))
> + return NULL;
> +
> + crtc_state = drm_atomic_get_new_crtc_state(state, conn_state->crtc);
> + if (WARN_ON(!crtc_state))
> + return NULL;
> +
> + return crtc_state;

You don't even seem to use the bridge state itself, so maybe we just
need to pass the drm_atomic_state? And thus we end up with something
like drm_atomic_get_new_connector_for_encoder, so maybe we should just
call it drm_atomic_get_new_crtc_for_bridge?

Also, can we end up with a commit that affects the bridge state but not
the crtc state (like a connector property change)? In such a case
drm_atomic_get_new_crtc_state would return NULL.

I'm not sure if it's a big deal or not, but we should make it clear in
the documentation and remove the WARN_ON.

Maxime


Re: [PATCH v1 5/7] drm/mediatek: Drop chain_mode_fixup call in mode_valid()

2021-07-22 Thread Maxime Ripard
On Thu, Jul 22, 2021 at 08:22:44AM +0200, Sam Ravnborg wrote:
> The mode_valid implementation had a call to
> drm_bridge_chain_mode_fixup() which would be wrong as the mode_valid is
> not allowed to change anything - only to validate the mode.
> 
> As the next bridge is often/always a connector the call had no effect
> anyway. So drop it.
> 
> From the git history I could see this call was included in the original
> version of the driver so there was no help there to find out why it was
> added in the first place. But a lot has changed since the initial driver
> were added and is seems safe to remove the call now.
> 
> Signed-off-by: Sam Ravnborg 
> Cc: Chun-Kuang Hu 
> Cc: Philipp Zabel 
> Cc: Matthias Brugger 
> Cc: Dafna Hirschfeld 
> Cc: linux-media...@lists.infradead.org
> Cc: linux-arm-ker...@lists.infradead.org

Reviewed-by: Maxime Ripard 

Maxime


Re: [PATCH v1 6/7] drm/bridge: Drop drm_bridge_chain_mode_fixup

2021-07-22 Thread Maxime Ripard
On Thu, Jul 22, 2021 at 08:22:45AM +0200, Sam Ravnborg wrote:
> There are no users left and we do not want to have this function
> available.
> 
> drm_atomic_bridge_check() is used to call the mode_fixup() operation for
> the chained bridges and there is no need for drm_atomic_bridge_check().
> Drop it.
> 
> Signed-off-by: Sam Ravnborg 
> Cc: Laurent Pinchart 
> Cc: Maarten Lankhorst 
> Cc: Maxime Ripard 
> Cc: Thomas Zimmermann 
> Cc: David Airlie 
> Cc: Daniel Vetter 

Reviewed-by: Maxime Ripard 

Maxime


Re: [PATCH v1 7/7] drm/todo: Add bridge related todo items

2021-07-22 Thread Maxime Ripard
On Thu, Jul 22, 2021 at 08:22:46AM +0200, Sam Ravnborg wrote:
> - deprecated callbacks in drm_bridge_funcs
> - move connector creation to display drivers
> 
> Signed-off-by: Sam Ravnborg 
> Cc: Laurent Pinchart 
> Cc: Maarten Lankhorst 
> Cc: Maxime Ripard 
> Cc: Thomas Zimmermann 
> Cc: David Airlie 
> Cc: Daniel Vetter 

Acked-by: Maxime Ripard 

Maxime


Re: [PATCH v1 4/7] drm/bridge: lontium-lt9611: Use atomic variants of drm_bridge_funcs

2021-07-22 Thread Maxime Ripard
Hi,

On Thu, Jul 22, 2021 at 08:22:43AM +0200, Sam Ravnborg wrote:
> The atomic variants of enable/disable in drm_bridge_funcs are the
> preferred operations - introduce these.
> 
> Use of mode_set is deprecated - merge the functionality with
> atomic_enable()
> 
> Signed-off-by: Sam Ravnborg 
> Cc: Andrzej Hajda 
> Cc: Neil Armstrong 
> Cc: Robert Foss 
> Cc: Laurent Pinchart 
> Cc: Jonas Karlman 
> Cc: Jernej Skrabec 
> ---
>  drivers/gpu/drm/bridge/lontium-lt9611.c | 69 ++---
>  1 file changed, 27 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/gpu/drm/bridge/lontium-lt9611.c 
> b/drivers/gpu/drm/bridge/lontium-lt9611.c
> index 29b1ce2140ab..dfa7baefe2ab 100644
> --- a/drivers/gpu/drm/bridge/lontium-lt9611.c
> +++ b/drivers/gpu/drm/bridge/lontium-lt9611.c
> @@ -700,9 +700,17 @@ lt9611_connector_mode_valid(struct drm_connector 
> *connector,
>  }
>  
>  /* bridge funcs */
> -static void lt9611_bridge_enable(struct drm_bridge *bridge)
> +static void lt9611_bridge_atomic_enable(struct drm_bridge *bridge,
> + struct drm_bridge_state 
> *old_bridge_state)
>  {
>   struct lt9611 *lt9611 = bridge_to_lt9611(bridge);
> + const struct drm_display_mode *mode;
> + const struct drm_crtc_state *crtc_state;
> + struct hdmi_avi_infoframe avi_frame;
> + int ret;
> +
> + crtc_state = drm_bridge_new_crtc_state(bridge, old_bridge_state);
> + mode = &crtc_state->mode;

So, yeah, it looks like you can't make the assumption that crtc_state is
going to be valid here.

I'm not entirely clear on how bridge states are allocated, but it looks
to me that they are through drm_atomic_add_encoder_bridges, which is
called for all the affected connectors in a commit here:

https://elixir.bootlin.com/linux/latest/source/drivers/gpu/drm/drm_atomic_helper.c#L744

Then, the atomic_enable call is made through
drm_atomic_bridge_chain_enable(), which is called in
drm_atomic_helper_commit_modeset_enables only if the CRTC is active and
needs a modeset.

I guess this means that we won't have a null pointer for crtc_state
there, but wouldn't that cause some issues? I can imagine a property
like the bpc count or output format where it wouldn't imply a modeset
but would definitely affect the bridges in the chain?

Maxime


Re: [PATCH 08/10] drm/panel: raspberrypi-touchscreen: Prevent double-free

2021-07-22 Thread Maxime Ripard
On Tue, Jul 20, 2021 at 07:19:40PM +0200, Sam Ravnborg wrote:
> Hi Maxime,
> On Tue, Jul 20, 2021 at 03:45:23PM +0200, Maxime Ripard wrote:
> > The mipi_dsi_device allocated by mipi_dsi_device_register_full() is
> > already free'd on release.
> > 
> > Fixes: 2f733d6194bd ("drm/panel: Add support for the Raspberry Pi 7" 
> > Touchscreen.")
> > Signed-off-by: Maxime Ripard 
>
> Reviewed-by: Sam Ravnborg 

Thanks, I applied it to drm-misc-fixes

> I did a quick audit (as using grep mostly) to see if other panels had
> the same bug, but did not find others.

Yeah, the RaspberryPi panel seems to be the only odd DSI panel not
controlled through DCS, and the other panels don't have to allocate the
mipi-dsi device anyway.

No bridge seems to have the issue though.

Maxime


signature.asc
Description: PGP signature


Re: [PATCH 11/54] dt-bindings: display: simple-bridge: Add corpro, gm7123 compatible

2021-07-22 Thread Maxime Ripard
On Wed, Jul 21, 2021 at 04:16:13PM +0200, Sam Ravnborg wrote:
> On Wed, Jul 21, 2021 at 04:03:41PM +0200, Maxime Ripard wrote:
> > The corpro,gm7123 was in use in a DT but was never properly documented,
> > let's add it.
> > 
> > Cc: dri-devel@lists.freedesktop.org
> > Reviewed-by: Laurent Pinchart 
> > Signed-off-by: Maxime Ripard 
> Acked-by: Sam Ravnborg 

Applied to drm-misc-next, thanks!
Maxime


signature.asc
Description: PGP signature


Re: [PATCH v6] Documentation: gpu: Mention the requirements for new properties

2021-07-23 Thread Maxime Ripard
On Tue, Jul 20, 2021 at 04:35:44PM +0200, Maxime Ripard wrote:
> New KMS properties come with a bunch of requirements to avoid each
> driver from running their own, inconsistent, set of properties,
> eventually leading to issues like property conflicts, inconsistencies
> between drivers and semantics, etc.
> 
> Let's document what we expect.
> 
> Cc: Alexandre Belloni 
> Cc: Alexandre Torgue 
> Cc: Alex Deucher 
> Cc: Alison Wang 
> Cc: Alyssa Rosenzweig 
> Cc: Andrew Jeffery 
> Cc: Andrzej Hajda 
> Cc: Anitha Chrisanthus 
> Cc: Benjamin Gaignard 
> Cc: Ben Skeggs 
> Cc: Boris Brezillon 
> Cc: Brian Starkey 
> Cc: Chen Feng 
> Cc: Chen-Yu Tsai 
> Cc: Christian Gmeiner 
> Cc: "Christian König" 
> Cc: Chun-Kuang Hu 
> Cc: Edmund Dea 
> Cc: Eric Anholt 
> Cc: Fabio Estevam 
> Cc: Gerd Hoffmann 
> Cc: Haneen Mohammed 
> Cc: Hans de Goede 
> Cc: "Heiko Stübner" 
> Cc: Huang Rui 
> Cc: Hyun Kwon 
> Cc: Inki Dae 
> Cc: Jani Nikula 
> Cc: Jernej Skrabec 
> Cc: Jerome Brunet 
> Cc: John Stultz 
> Cc: Jonas Karlman 
> Cc: Jonathan Hunter 
> Cc: Joonas Lahtinen 
> Cc: Joonyoung Shim 
> Cc: Jyri Sarha 
> Cc: Kevin Hilman 
> Cc: Kieran Bingham 
> Cc: Krzysztof Kozlowski 
> Cc: Kyungmin Park 
> Cc: Laurent Pinchart 
> Cc: Linus Walleij 
> Cc: Liviu Dudau 
> Cc: Lucas Stach 
> Cc: Ludovic Desroches 
> Cc: Marek Vasut 
> Cc: Martin Blumenstingl 
> Cc: Matthias Brugger 
> Cc: Maxime Coquelin 
> Cc: Maxime Ripard 
> Cc: Melissa Wen 
> Cc: Neil Armstrong 
> Cc: Nicolas Ferre 
> Cc: "Noralf Trønnes" 
> Cc: NXP Linux Team 
> Cc: Oleksandr Andrushchenko 
> Cc: Patrik Jakobsson 
> Cc: Paul Cercueil 
> Cc: Pekka Paalanen 
> Cc: Pengutronix Kernel Team 
> Cc: Philippe Cornu 
> Cc: Philipp Zabel 
> Cc: Qiang Yu 
> Cc: Rob Clark 
> Cc: Robert Foss 
> Cc: Rob Herring 
> Cc: Rodrigo Siqueira 
> Cc: Rodrigo Vivi 
> Cc: Roland Scheidegger 
> Cc: Russell King 
> Cc: Sam Ravnborg 
> Cc: Sandy Huang 
> Cc: Sascha Hauer 
> Cc: Sean Paul 
> Cc: Seung-Woo Kim 
> Cc: Shawn Guo 
> Cc: Simon Ser 
> Cc: Stefan Agner 
> Cc: Steven Price 
> Cc: Sumit Semwal 
> Cc: Thierry Reding 
> Cc: Tian Tao 
> Cc: Tomeu Vizoso 
> Cc: Tomi Valkeinen 
> Cc: VMware Graphics 
> Cc: Xinliang Liu 
> Cc: Xinwei Kong 
> Cc: Yannick Fertre 
> Cc: Zack Rusin 
> Reviewed-by: Pekka Paalanen 
> Reviewed-by: Daniel Vetter 
> Signed-off-by: Maxime Ripard 

Applied with Dave's Ack (on IRC)

Maxime


signature.asc
Description: PGP signature


Re: [PATCH 04/10] drm/bridge: Document the probe issue with MIPI-DSI bridges

2021-07-26 Thread Maxime Ripard
Hi Daniel,

On Wed, Jul 21, 2021 at 02:05:01PM +0200, Daniel Vetter wrote:
> On Tue, Jul 20, 2021 at 03:45:19PM +0200, Maxime Ripard wrote:
> > Interactions between bridges, panels, MIPI-DSI host and the component
> > framework are not trivial and can lead to probing issues when
> > implementing a display driver. Let's document the various cases we need
> > too consider, and the solution to support all the cases.
> > 
> > Signed-off-by: Maxime Ripard 
> 
> I still have this dream that eventually we resurrect a patch to add
> device_link to bridges/panels (ideally automatically), to help with some
> of the suspend/resume issues around here.
> 
> Will this make things worse?
> 
> I think it'd be really good to figure that out with some coding, since if
> we have incompatible solution to handle probe issues vs suspend/resume
> issues, we're screwed.
> 
> Atm the duct-tape is to carefully move things around between suspend and
> suspend_early hooks (and resume and resume_late) and hope it all works ...

My initial idea to fix this was indeed to use device links. I gave up
after a while since it doesn't look like there's a way to add a device
link before either the bridge or encoder probes.

Indeed the OF-Graph representation is device-specific, so it can't be
generic, and if you need to probe to add that link, well, it's already
too late for the probe ordering :)

Maxime


signature.asc
Description: PGP signature


Re: [PATCH 2/2] drm/vc4: hdmi: Remove unused struct

2021-07-28 Thread Maxime Ripard
On Wed, Jul 28, 2021 at 12:01:34PM +0100, Dave Stevenson wrote:
> On Wed, 7 Jul 2021 at 10:36, Maxime Ripard  wrote:
> >
> > Commit 91e99e113929 ("drm/vc4: hdmi: Register HDMI codec") removed the
> > references to the vc4_hdmi_audio_component_drv structure, but not the
> > structure itself resulting in a warning. Remove it.
> >
> > Fixes: 91e99e113929 ("drm/vc4: hdmi: Register HDMI codec")
> > Reported-by: kernel test robot 
> > Signed-off-by: Maxime Ripard 
> 
> Reviewed-by: Dave Stevenson 

Applied, thanks!
Maxime


signature.asc
Description: PGP signature


Re: [PATCH] drm/vc4: hdmi: Add debugfs prefix

2021-07-28 Thread Maxime Ripard
Hi,

On Fri, Jul 23, 2021 at 09:24:14AM +0200, Ivan T. Ivanov wrote:
> Without prefix debugfs can't properly create component
> debug information tree when driver register more than
> one component per device, in this case two. Fix this.
> 
> debugfs: Directory 'fef00700.hdmi' with parent 'vc4-hdmi-0' already present!
> 
> Signed-off-by: Ivan T. Ivanov 

Thanks for your patch.

However, that part changed fairly significantly recently so you'll need
to rebase it on top of the drm-misc-next (or linux-next)

Maxime


signature.asc
Description: PGP signature


Re: [PATCH 04/10] drm/bridge: Document the probe issue with MIPI-DSI bridges

2021-07-28 Thread Maxime Ripard
Hi,

On Tue, Jul 27, 2021 at 11:20:54AM +0200, Daniel Vetter wrote:
> On Mon, Jul 26, 2021 at 05:16:57PM +0200, Maxime Ripard wrote:
> > Hi Daniel,
> > 
> > On Wed, Jul 21, 2021 at 02:05:01PM +0200, Daniel Vetter wrote:
> > > On Tue, Jul 20, 2021 at 03:45:19PM +0200, Maxime Ripard wrote:
> > > > Interactions between bridges, panels, MIPI-DSI host and the component
> > > > framework are not trivial and can lead to probing issues when
> > > > implementing a display driver. Let's document the various cases we need
> > > > too consider, and the solution to support all the cases.
> > > > 
> > > > Signed-off-by: Maxime Ripard 
> > > 
> > > I still have this dream that eventually we resurrect a patch to add
> > > device_link to bridges/panels (ideally automatically), to help with some
> > > of the suspend/resume issues around here.
> > > 
> > > Will this make things worse?
> > > 
> > > I think it'd be really good to figure that out with some coding, since if
> > > we have incompatible solution to handle probe issues vs suspend/resume
> > > issues, we're screwed.
> > > 
> > > Atm the duct-tape is to carefully move things around between suspend and
> > > suspend_early hooks (and resume and resume_late) and hope it all works ...
> > 
> > My initial idea to fix this was indeed to use device links. I gave up
> > after a while since it doesn't look like there's a way to add a device
> > link before either the bridge or encoder probes.
> > 
> > Indeed the OF-Graph representation is device-specific, so it can't be
> > generic, and if you need to probe to add that link, well, it's already
> > too late for the probe ordering :)
> 
> But don't we still need the device_link for suspend/resume and module
> reload? All very annoying indeed anyway.

I guess we would still need it for proper suspend and resume ordering
(but I never really worked on that part, so I'm not sure), but it's a
bit orthogonal to the issue here since those can be added after probe

Maxime


signature.asc
Description: PGP signature


[PATCH v2 0/8] drm/bridge: Make panel and bridge probe order consistent

2021-07-28 Thread Maxime Ripard
Hi,

We've encountered an issue with the RaspberryPi DSI panel that prevented the
whole display driver from probing.

The issue is described in detail in the commit 7213246a803f ("drm/vc4: dsi:
Only register our component once a DSI device is attached"), but the basic idea
is that since the panel is probed through i2c, there's no synchronization
between its probe and the registration of the MIPI-DSI host it's attached to.

We initially moved the component framework registration to the MIPI-DSI Host
attach hook to make sure we register our component only when we have a DSI
device attached to our MIPI-DSI host, and then use lookup our DSI device in our
bind hook.

However, all the DSI bridges controlled through i2c are only registering their
associated DSI device in their bridge attach hook, meaning with our change
above, we never got that far, and therefore ended up in the same situation than
the one we were trying to fix for panels.

Since the RaspberryPi panel is the only driver in that situation, whereas it
seems like there's a consensus in bridge drivers, it makes more sense to try to
mimic the bridge pattern in the panel driver.

However, panels don't have an attach hook, and adding more panel hooks would
lead to more path to maintain in each and every driver, while the general push
is towards bridges. We also have to make sure that each and every DSI host and
device driver behaves the same in order to have expectations to rely on.

The solution I'm proposing is thus done in several steps:

  - We get rid of the initial patch to make sure we support the bridge case,
and not the odd-panel one.

  - Add a function that returns a bridge from a DT node, reducing the amount of
churn in each and every driver and making it a real incentive to not care
about panels in display drivers but only bridges.

  - Add an attach and detach hook into the panel operations, and make it called
automatically by the DRM panel bridge.

  - Convert the VC4 DSI host to this new bridge function, and the RaspberryPi
Panel to the new attach and detach hooks.

If the general approach is agreed upon, other drivers will obviously be
converted to drm_of_get_next.

Let me know what you think,
Maxime

---

Changes from v1:
  - Change the name of drm_of_get_next function to drm_of_get_bridge
  - Mention the revert of 87154ff86bf6 and squash the two patches that were
reverting that commit
  - Add some documentation
  - Make drm_panel_attach and _detach succeed when no callback is there

Maxime Ripard (8):
  Revert "drm/vc4: dsi: Only register our component once a DSI device is
attached"
  drm/bridge: Add a function to abstract away panels
  drm/bridge: Add documentation sections
  drm/bridge: Document the probe issue with MIPI-DSI bridges
  drm/panel: Create attach and detach callbacks
  drm/vc4: dsi: Switch to drm_of_get_bridge
  drm/panel: raspberrypi-touchscreen: Use the attach hook
  drm/panel: raspberrypi-touchscreen: Remove MIPI-DSI driver

 Documentation/gpu/drm-kms-helpers.rst |  12 ++
 drivers/gpu/drm/bridge/panel.c|   6 +
 drivers/gpu/drm/drm_bridge.c  | 114 -
 drivers/gpu/drm/drm_of.c  |   3 +
 drivers/gpu/drm/drm_panel.c   |  47 ++
 .../drm/panel/panel-raspberrypi-touchscreen.c | 158 +-
 drivers/gpu/drm/vc4/vc4_drv.c |   2 +
 drivers/gpu/drm/vc4/vc4_dsi.c |  53 +++---
 include/drm/drm_bridge.h  |   2 +
 include/drm/drm_panel.h   |  27 +++
 10 files changed, 303 insertions(+), 121 deletions(-)

-- 
2.31.1



[PATCH v2 1/8] Revert "drm/vc4: dsi: Only register our component once a DSI device is attached"

2021-07-28 Thread Maxime Ripard
This reverts commit 7213246a803f9b8da0677adb9ae06a3d8b806d02.

The commit 7213246a803f ("drm/vc4: dsi: Only register our component once
a DSI device is attached") aimed at preventing an endless probe loop
between the DSI host driver and its panel/bridge where both would wait
for each other to probe.

The solution implemented in that commit however relies on the fact that
MIPI-DSI device will either be a MIPI-DSI device, or would call
mipi_dsi_device_register_full() at probe time.

This assumption isn't true for bridges though where most drivers will do
so in the bridge attach hook. However, the drm_bridge_attach is usually
called in the DSI host bind hook, and thus we never get this far,
resulting in a DSI bridge that will never have its attach run, and the
DSI host that will never be bound, effectively creating the same
situation we were trying to avoid for panels.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_dsi.c | 25 +
 1 file changed, 17 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index a55256ed0955..6dfcbd9e234e 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1257,12 +1257,10 @@ static ssize_t vc4_dsi_host_transfer(struct 
mipi_dsi_host *host,
return ret;
 }
 
-static const struct component_ops vc4_dsi_ops;
 static int vc4_dsi_host_attach(struct mipi_dsi_host *host,
   struct mipi_dsi_device *device)
 {
struct vc4_dsi *dsi = host_to_dsi(host);
-   int ret;
 
dsi->lanes = device->lanes;
dsi->channel = device->channel;
@@ -1297,12 +1295,6 @@ static int vc4_dsi_host_attach(struct mipi_dsi_host 
*host,
return 0;
}
 
-   ret = component_add(&dsi->pdev->dev, &vc4_dsi_ops);
-   if (ret) {
-   mipi_dsi_host_unregister(&dsi->dsi_host);
-   return ret;
-   }
-
return 0;
 }
 
@@ -1689,6 +1681,7 @@ static int vc4_dsi_dev_probe(struct platform_device *pdev)
 {
struct device *dev = &pdev->dev;
struct vc4_dsi *dsi;
+   int ret;
 
dsi = devm_kzalloc(dev, sizeof(*dsi), GFP_KERNEL);
if (!dsi)
@@ -1696,10 +1689,26 @@ static int vc4_dsi_dev_probe(struct platform_device 
*pdev)
dev_set_drvdata(dev, dsi);
 
dsi->pdev = pdev;
+
+   /* Note, the initialization sequence for DSI and panels is
+* tricky.  The component bind above won't get past its
+* -EPROBE_DEFER until the panel/bridge probes.  The
+* panel/bridge will return -EPROBE_DEFER until it has a
+* mipi_dsi_host to register its device to.  So, we register
+* the host during pdev probe time, so vc4 as a whole can then
+* -EPROBE_DEFER its component bind process until the panel
+* successfully attaches.
+*/
dsi->dsi_host.ops = &vc4_dsi_host_ops;
dsi->dsi_host.dev = dev;
mipi_dsi_host_register(&dsi->dsi_host);
 
+   ret = component_add(&pdev->dev, &vc4_dsi_ops);
+   if (ret) {
+   mipi_dsi_host_unregister(&dsi->dsi_host);
+   return ret;
+   }
+
return 0;
 }
 
-- 
2.31.1



[PATCH v2 2/8] drm/bridge: Add a function to abstract away panels

2021-07-28 Thread Maxime Ripard
Display drivers so far need to have a lot of boilerplate to first
retrieve either the panel or bridge that they are connected to using
drm_of_find_panel_or_bridge(), and then either deal with each with ad-hoc
functions or create a drm panel bridge through drm_panel_bridge_add.

In order to reduce the boilerplate and hopefully create a path of least
resistance towards using the DRM panel bridge layer, let's create the
function devm_drm_of_get_next to reduce that boilerplate.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/drm_bridge.c | 42 
 drivers/gpu/drm/drm_of.c |  3 +++
 include/drm/drm_bridge.h |  2 ++
 3 files changed, 43 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 044acd07c153..426ce7442c86 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -28,6 +28,7 @@
 #include 
 #include 
 #include 
+#include 
 
 #include "drm_crtc_internal.h"
 
@@ -50,10 +51,8 @@
  *
  * Display drivers are responsible for linking encoders with the first bridge
  * in the chains. This is done by acquiring the appropriate bridge with
- * of_drm_find_bridge() or drm_of_find_panel_or_bridge(), or creating it for a
- * panel with drm_panel_bridge_add_typed() (or the managed version
- * devm_drm_panel_bridge_add_typed()). Once acquired, the bridge shall be
- * attached to the encoder with a call to drm_bridge_attach().
+ * devm_drm_of_get_bridge(). Once acquired, the bridge shall be attached to the
+ * encoder with a call to drm_bridge_attach().
  *
  * Bridges are responsible for linking themselves with the next bridge in the
  * chain, if any. This is done the same way as for encoders, with the call to
@@ -1223,6 +1222,41 @@ struct drm_bridge *of_drm_find_bridge(struct device_node 
*np)
return NULL;
 }
 EXPORT_SYMBOL(of_drm_find_bridge);
+
+/**
+ * devm_drm_of_get_bridge - Return next bridge in the chain
+ * @dev: device to tie the bridge lifetime to
+ * @np: device tree node containing encoder output ports
+ * @port: port in the device tree node
+ * @endpoint: endpoint in the device tree node
+ *
+ * Given a DT node's port and endpoint number, finds the connected node
+ * and returns the associated bridge if any, or creates and returns a
+ * drm panel bridge instance if a panel is connected.
+ *
+ * Returns a pointer to the bridge if successful, or an error pointer
+ * otherwise.
+ */
+struct drm_bridge *devm_drm_of_get_bridge(struct device *dev,
+ struct device_node *np,
+ unsigned int port,
+ unsigned int endpoint)
+{
+   struct drm_bridge *bridge;
+   struct drm_panel *panel;
+   int ret;
+
+   ret = drm_of_find_panel_or_bridge(np, port, endpoint,
+ &panel, &bridge);
+   if (ret)
+   return ERR_PTR(ret);
+
+   if (panel)
+   bridge = devm_drm_panel_bridge_add(dev, panel);
+
+   return bridge;
+}
+EXPORT_SYMBOL(devm_drm_of_get_bridge);
 #endif
 
 MODULE_AUTHOR("Ajay Kumar ");
diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c
index 997b8827fed2..37c34146eea8 100644
--- a/drivers/gpu/drm/drm_of.c
+++ b/drivers/gpu/drm/drm_of.c
@@ -231,6 +231,9 @@ EXPORT_SYMBOL_GPL(drm_of_encoder_active_endpoint);
  * return either the associated struct drm_panel or drm_bridge device. Either
  * @panel or @bridge must not be NULL.
  *
+ * This function is deprecated and should not be used in new drivers. Use
+ * devm_drm_of_get_bridge() instead.
+ *
  * Returns zero if successful, or one of the standard error codes if it fails.
  */
 int drm_of_find_panel_or_bridge(const struct device_node *np,
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 46bdfa48c413..f70c88ca96ef 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -911,6 +911,8 @@ struct drm_bridge *devm_drm_panel_bridge_add(struct device 
*dev,
 struct drm_bridge *devm_drm_panel_bridge_add_typed(struct device *dev,
   struct drm_panel *panel,
   u32 connector_type);
+struct drm_bridge *devm_drm_of_get_bridge(struct device *dev, struct 
device_node *node,
+   unsigned int port, unsigned int 
endpoint);
 struct drm_connector *drm_panel_bridge_connector(struct drm_bridge *bridge);
 #endif
 
-- 
2.31.1



[PATCH v2 3/8] drm/bridge: Add documentation sections

2021-07-28 Thread Maxime Ripard
The bridge documentation overview is quite packed already, and we'll add
some more documentation that isn't part of an overview at all.

Let's add some sections to the documentation to separate each bits.

Reviewed-by: Sam Ravnborg 
Signed-off-by: Maxime Ripard 
---
 Documentation/gpu/drm-kms-helpers.rst |  6 ++
 drivers/gpu/drm/drm_bridge.c  | 14 +-
 2 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/Documentation/gpu/drm-kms-helpers.rst 
b/Documentation/gpu/drm-kms-helpers.rst
index 389892f36185..10f8df7aecc0 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -151,6 +151,12 @@ Overview
 .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: overview
 
+Display Driver Integration
+--
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :doc: display driver integration
+
 Bridge Operations
 -
 
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 426ce7442c86..71d3370ce209 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -49,6 +49,15 @@
  * Chaining multiple bridges to the output of a bridge, or the same bridge to
  * the output of different bridges, is not supported.
  *
+ * &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes,
+ * CRTCs, encoders or connectors and hence are not visible to userspace. They
+ * just provide additional hooks to get the desired output at the end of the
+ * encoder chain.
+ */
+
+/**
+ * DOC:display driver integration
+ *
  * Display drivers are responsible for linking encoders with the first bridge
  * in the chains. This is done by acquiring the appropriate bridge with
  * devm_drm_of_get_bridge(). Once acquired, the bridge shall be attached to the
@@ -83,11 +92,6 @@
  * helper to create the &drm_connector, or implement it manually on top of the
  * connector-related operations exposed by the bridge (see the overview
  * documentation of bridge operations for more details).
- *
- * &drm_bridge, like &drm_panel, aren't &drm_mode_object entities like planes,
- * CRTCs, encoders or connectors and hence are not visible to userspace. They
- * just provide additional hooks to get the desired output at the end of the
- * encoder chain.
  */
 
 static DEFINE_MUTEX(bridge_lock);
-- 
2.31.1



[PATCH v2 4/8] drm/bridge: Document the probe issue with MIPI-DSI bridges

2021-07-28 Thread Maxime Ripard
Interactions between bridges, panels, MIPI-DSI host and the component
framework are not trivial and can lead to probing issues when
implementing a display driver. Let's document the various cases we need
too consider, and the solution to support all the cases.

Signed-off-by: Maxime Ripard 
---
 Documentation/gpu/drm-kms-helpers.rst |  6 +++
 drivers/gpu/drm/drm_bridge.c  | 60 +++
 2 files changed, 66 insertions(+)

diff --git a/Documentation/gpu/drm-kms-helpers.rst 
b/Documentation/gpu/drm-kms-helpers.rst
index 10f8df7aecc0..ec2f65b31930 100644
--- a/Documentation/gpu/drm-kms-helpers.rst
+++ b/Documentation/gpu/drm-kms-helpers.rst
@@ -157,6 +157,12 @@ Display Driver Integration
 .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
:doc: display driver integration
 
+Special Care with MIPI-DSI bridges
+--
+
+.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
+   :doc: special care dsi
+
 Bridge Operations
 -
 
diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
index 71d3370ce209..fd01f1ce7976 100644
--- a/drivers/gpu/drm/drm_bridge.c
+++ b/drivers/gpu/drm/drm_bridge.c
@@ -94,6 +94,66 @@
  * documentation of bridge operations for more details).
  */
 
+/**
+ * DOC: special care dsi
+ *
+ * The interaction between the bridges and other frameworks involved in
+ * the probing of the display driver and the bridge driver can be
+ * challenging. Indeed, there's multiple cases that needs to be
+ * considered:
+ *
+ * - The display driver doesn't use the component framework and isn't a
+ *   MIPI-DSI host. In this case, the bridge driver will probe at some
+ *   point and the display driver should try to probe again by returning
+ *   EPROBE_DEFER as long as the bridge driver hasn't probed.
+ *
+ * - The display driver doesn't use the component framework, but is a
+ *   MIPI-DSI host. The bridge device uses the MIPI-DCS commands to be
+ *   controlled. In this case, the bridge device is a child of the
+ *   display device and when it will probe it's assured that the display
+ *   device (and MIPI-DSI host) is present. The display driver will be
+ *   assured that the bridge driver is connected between the
+ *   &mipi_dsi_host_ops.attach and &mipi_dsi_host_ops.detach operations.
+ *   Therefore, it must run mipi_dsi_host_register() in its probe
+ *   function, and then run drm_bridge_attach() in its
+ *   &mipi_dsi_host_ops.attach hook.
+ *
+ * - The display driver uses the component framework and is a MIPI-DSI
+ *   host. The bridge device uses the MIPI-DCS commands to be
+ *   controlled. This is the same situation than above, and can run
+ *   mipi_dsi_host_register() in either its probe or bind hooks.
+ *
+ * - The display driver uses the component framework and is a MIPI-DSI
+ *   host. The bridge device uses a separate bus (such as I2C) to be
+ *   controlled. In this case, there's no correlation between the probe
+ *   of the bridge and display drivers, so care must be taken to avoid
+ *   an endless EPROBE_DEFER loop, with each driver waiting for the
+ *   other to probe.
+ *
+ * The ideal pattern to cover the last item (and all the others in the
+ * display driver case) is to split the operations like this:
+ *
+ * - In the display driver must run mipi_dsi_host_register() and
+ *   component_add in its probe hook. It will make sure that the
+ *   MIPI-DSI host sticks around, and that the driver's bind can be
+ *   called.
+ *
+ * - In its probe hook, the bridge driver must not try to find its
+ *   MIPI-DSI host or register as a MIPI-DSI device. As far as the
+ *   framework is concerned, it must only call drm_bridge_add().
+ *
+ * - In its bind hook, the display driver must try to find the bridge
+ *   and return -EPROBE_DEFER if it doesn't find it. If it's there, it
+ *   must call drm_bridge_attach(). The MIPI-DSI host is now functional.
+ *
+ * - In its &drm_bridge_funcs.attach hook, the bridge driver can now try
+ *   to find its MIPI-DSI host and can register as a MIPI-DSI device.
+ *
+ * At this point, we're now certain that both the display driver and the
+ * bridge driver are functional and we can't have a deadlock-like
+ * situation when probing.
+ */
+
 static DEFINE_MUTEX(bridge_lock);
 static LIST_HEAD(bridge_list);
 
-- 
2.31.1



[PATCH v2 6/8] drm/vc4: dsi: Switch to drm_of_get_bridge

2021-07-28 Thread Maxime Ripard
The new drm_of_get_bridge removes most of the boilerplate we have to deal
with. Let's switch to it.

Signed-off-by: Maxime Ripard 

fixup! drm/vc4: dsi: Switch to drm_of_get_bridge
---
 drivers/gpu/drm/vc4/vc4_drv.c |  2 ++
 drivers/gpu/drm/vc4/vc4_dsi.c | 28 
 2 files changed, 6 insertions(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_drv.c b/drivers/gpu/drm/vc4/vc4_drv.c
index 73335feb712f..ff056ee8bc4b 100644
--- a/drivers/gpu/drm/vc4/vc4_drv.c
+++ b/drivers/gpu/drm/vc4/vc4_drv.c
@@ -25,7 +25,9 @@
 #include 
 #include 
 #include 
+#include 
 #include 
+#include 
 #include 
 #include 
 #include 
diff --git a/drivers/gpu/drm/vc4/vc4_dsi.c b/drivers/gpu/drm/vc4/vc4_dsi.c
index 6dfcbd9e234e..3db03c95707f 100644
--- a/drivers/gpu/drm/vc4/vc4_dsi.c
+++ b/drivers/gpu/drm/vc4/vc4_dsi.c
@@ -1489,7 +1489,6 @@ static int vc4_dsi_bind(struct device *dev, struct device 
*master, void *data)
struct drm_device *drm = dev_get_drvdata(master);
struct vc4_dsi *dsi = dev_get_drvdata(dev);
struct vc4_dsi_encoder *vc4_dsi_encoder;
-   struct drm_panel *panel;
const struct of_device_id *match;
dma_cap_mask_t dma_mask;
int ret;
@@ -1601,27 +1600,9 @@ static int vc4_dsi_bind(struct device *dev, struct 
device *master, void *data)
return ret;
}
 
-   ret = drm_of_find_panel_or_bridge(dev->of_node, 0, 0,
- &panel, &dsi->bridge);
-   if (ret) {
-   /* If the bridge or panel pointed by dev->of_node is not
-* enabled, just return 0 here so that we don't prevent the DRM
-* dev from being registered. Of course that means the DSI
-* encoder won't be exposed, but that's not a problem since
-* nothing is connected to it.
-*/
-   if (ret == -ENODEV)
-   return 0;
-
-   return ret;
-   }
-
-   if (panel) {
-   dsi->bridge = devm_drm_panel_bridge_add_typed(dev, panel,
- 
DRM_MODE_CONNECTOR_DSI);
-   if (IS_ERR(dsi->bridge))
-   return PTR_ERR(dsi->bridge);
-   }
+   dsi->bridge = devm_drm_of_get_bridge(dev, dev->of_node, 0, 0);
+   if (IS_ERR(dsi->bridge))
+   return PTR_ERR(dsi->bridge);
 
/* The esc clock rate is supposed to always be 100Mhz. */
ret = clk_set_rate(dsi->escape_clock, 100 * 100);
@@ -1661,8 +1642,7 @@ static void vc4_dsi_unbind(struct device *dev, struct 
device *master,
 {
struct vc4_dsi *dsi = dev_get_drvdata(dev);
 
-   if (dsi->bridge)
-   pm_runtime_disable(dev);
+   pm_runtime_disable(dev);
 
/*
 * Restore the bridge_chain so the bridge detach procedure can happen
-- 
2.31.1



[PATCH v2 5/8] drm/panel: Create attach and detach callbacks

2021-07-28 Thread Maxime Ripard
This is a partial revert of 87154ff86bf6 ("drm: Remove unnecessary
drm_panel_attach and drm_panel_detach").

In order to make the probe order expectation more consistent between
bridges, let's create attach and detach hooks for the panels as well to
match what is there for bridges.

Most drivers have changed significantly since the reverted commit, so we
only brought back the calls in the panel bridge to lessen the risk of
regressions, and since panel bridge is what we're converging to these
days, it's not a big deal anyway.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/bridge/panel.c |  6 +
 drivers/gpu/drm/drm_panel.c| 47 ++
 include/drm/drm_panel.h| 27 +++
 3 files changed, 80 insertions(+)

diff --git a/drivers/gpu/drm/bridge/panel.c b/drivers/gpu/drm/bridge/panel.c
index c916f4b8907e..0b06e0dbad00 100644
--- a/drivers/gpu/drm/bridge/panel.c
+++ b/drivers/gpu/drm/bridge/panel.c
@@ -82,6 +82,10 @@ static int panel_bridge_attach(struct drm_bridge *bridge,
drm_connector_attach_encoder(&panel_bridge->connector,
  bridge->encoder);
 
+   ret = drm_panel_attach(panel_bridge->panel, &panel_bridge->connector);
+   if (ret < 0)
+   return ret;
+
return 0;
 }
 
@@ -90,6 +94,8 @@ static void panel_bridge_detach(struct drm_bridge *bridge)
struct panel_bridge *panel_bridge = drm_bridge_to_panel_bridge(bridge);
struct drm_connector *connector = &panel_bridge->connector;
 
+   drm_panel_detach(panel_bridge->panel);
+
/*
 * Cleanup the connector if we know it was initialized.
 *
diff --git a/drivers/gpu/drm/drm_panel.c b/drivers/gpu/drm/drm_panel.c
index f634371c717a..10716b740685 100644
--- a/drivers/gpu/drm/drm_panel.c
+++ b/drivers/gpu/drm/drm_panel.c
@@ -93,6 +93,53 @@ void drm_panel_remove(struct drm_panel *panel)
 }
 EXPORT_SYMBOL(drm_panel_remove);
 
+/**
+ * drm_panel_attach - attach a panel to a connector
+ * @panel: DRM panel
+ * @connector: DRM connector
+ *
+ * After obtaining a pointer to a DRM panel a display driver calls this
+ * function to attach a panel to a connector.
+ *
+ * An error is returned if the panel is already attached to another connector.
+ *
+ * When unloading, the driver should detach from the panel by calling
+ * drm_panel_detach().
+ *
+ * Return: 0 on success or a negative error code on failure.
+ */
+int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector)
+{
+   if (!panel)
+   return -EINVAL;
+
+   if (panel->funcs && panel->funcs->attach)
+   return panel->funcs->attach(panel);
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_panel_attach);
+
+/**
+ * drm_panel_detach - detach a panel from a connector
+ * @panel: DRM panel
+ *
+ * Detaches a panel from the connector it is attached to. If a panel is not
+ * attached to any connector this is effectively a no-op.
+ *
+ * This function should not be called by the panel device itself. It
+ * is only for the drm device that called drm_panel_attach().
+ */
+void drm_panel_detach(struct drm_panel *panel)
+{
+   if (!panel)
+   return;
+
+   if (panel->funcs && panel->funcs->detach)
+   panel->funcs->detach(panel);
+}
+EXPORT_SYMBOL(drm_panel_detach);
+
 /**
  * drm_panel_prepare - power on a panel
  * @panel: DRM panel
diff --git a/include/drm/drm_panel.h b/include/drm/drm_panel.h
index 4602f833eb51..33d139e98b19 100644
--- a/include/drm/drm_panel.h
+++ b/include/drm/drm_panel.h
@@ -68,6 +68,30 @@ enum drm_panel_orientation;
  * does not need to implement the functionality to enable/disable backlight.
  */
 struct drm_panel_funcs {
+   /**
+* @attach:
+*
+* This callback is invoked whenever our panel is being attached
+* to its DRM connector.
+*
+* The @attach callback is optional.
+*
+* RETURNS:
+*
+* Zero on success, error code on failure.
+*/
+   int (*attach)(struct drm_panel *panel);
+
+   /**
+* @detach:
+*
+* This callback is invoked whenever our panel is being detached
+* from its DRM connector.
+*
+* The @detach callback is optional.
+*/
+   void (*detach)(struct drm_panel *panel);
+
/**
 * @prepare:
 *
@@ -180,6 +204,9 @@ void drm_panel_init(struct drm_panel *panel, struct device 
*dev,
 void drm_panel_add(struct drm_panel *panel);
 void drm_panel_remove(struct drm_panel *panel);
 
+int drm_panel_attach(struct drm_panel *panel, struct drm_connector *connector);
+void drm_panel_detach(struct drm_panel *panel);
+
 int drm_panel_prepare(struct drm_panel *panel);
 int drm_panel_unprepare(struct drm_panel *panel);
 
-- 
2.31.1



[PATCH v2 8/8] drm/panel: raspberrypi-touchscreen: Remove MIPI-DSI driver

2021-07-28 Thread Maxime Ripard
The driver was using a two-steps initialisation when probing with the
i2c probe first registering the MIPI-DSI device, and then when that
device was probed the driver would attach the device to its host. This
resulted in a fairly non-standard probe logic.

The previous commit changed that logic entirely though, resulting in a
completely empty MIPI-DSI device probe. Let's simplify the driver by
removing it entirely and just behave as a normal i2c driver.

Signed-off-by: Maxime Ripard 
---
 .../drm/panel/panel-raspberrypi-touchscreen.c | 25 +--
 1 file changed, 1 insertion(+), 24 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c 
b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 995c5cafb970..09937aa26c6a 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -483,16 +483,6 @@ static int rpi_touchscreen_remove(struct i2c_client *i2c)
return 0;
 }
 
-static int rpi_touchscreen_dsi_probe(struct mipi_dsi_device *dsi)
-{
-   return 0;
-}
-
-static struct mipi_dsi_driver rpi_touchscreen_dsi_driver = {
-   .driver.name = RPI_DSI_DRIVER_NAME,
-   .probe = rpi_touchscreen_dsi_probe,
-};
-
 static const struct of_device_id rpi_touchscreen_of_ids[] = {
{ .compatible = "raspberrypi,7inch-touchscreen-panel" },
{ } /* sentinel */
@@ -507,20 +497,7 @@ static struct i2c_driver rpi_touchscreen_driver = {
.probe = rpi_touchscreen_probe,
.remove = rpi_touchscreen_remove,
 };
-
-static int __init rpi_touchscreen_init(void)
-{
-   mipi_dsi_driver_register(&rpi_touchscreen_dsi_driver);
-   return i2c_add_driver(&rpi_touchscreen_driver);
-}
-module_init(rpi_touchscreen_init);
-
-static void __exit rpi_touchscreen_exit(void)
-{
-   i2c_del_driver(&rpi_touchscreen_driver);
-   mipi_dsi_driver_unregister(&rpi_touchscreen_dsi_driver);
-}
-module_exit(rpi_touchscreen_exit);
+module_i2c_driver(rpi_touchscreen_driver);
 
 MODULE_AUTHOR("Eric Anholt ");
 MODULE_DESCRIPTION("Raspberry Pi 7-inch touchscreen driver");
-- 
2.31.1



[PATCH v2 7/8] drm/panel: raspberrypi-touchscreen: Use the attach hook

2021-07-28 Thread Maxime Ripard
Now that we have an attach hook available for panels as well, let's use
it for the RaspberryPi 7" DSI panel.

This now mimics what all the other bridges in a similar situation are
doing, and we avoid our probe order issue entirely.

Signed-off-by: Maxime Ripard 
---
 .../drm/panel/panel-raspberrypi-touchscreen.c | 135 ++
 1 file changed, 77 insertions(+), 58 deletions(-)

diff --git a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c 
b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
index 462faae0f446..995c5cafb970 100644
--- a/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
+++ b/drivers/gpu/drm/panel/panel-raspberrypi-touchscreen.c
@@ -346,7 +346,83 @@ static int rpi_touchscreen_get_modes(struct drm_panel 
*panel,
return num;
 }
 
+static int rpi_touchscreen_attach(struct drm_panel *panel)
+{
+   struct rpi_touchscreen *ts = panel_to_ts(panel);
+   struct device *dev = &ts->i2c->dev;
+   struct device_node *endpoint, *dsi_host_node;
+   struct mipi_dsi_device *dsi;
+   struct mipi_dsi_host *host;
+   int ret;
+
+   struct mipi_dsi_device_info info = {
+   .type = RPI_DSI_DRIVER_NAME,
+   .channel = 0,
+   .node = NULL,
+   };
+
+   /* Look up the DSI host.  It needs to probe before we do. */
+   endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
+   if (!endpoint)
+   return -ENODEV;
+
+   dsi_host_node = of_graph_get_remote_port_parent(endpoint);
+   if (!dsi_host_node) {
+   of_node_put(endpoint);
+   return -ENODEV;
+   }
+
+   host = of_find_mipi_dsi_host_by_node(dsi_host_node);
+   of_node_put(dsi_host_node);
+   if (!host) {
+   of_node_put(endpoint);
+   return -EPROBE_DEFER;
+   }
+
+   info.node = of_graph_get_remote_port(endpoint);
+   if (!info.node) {
+   of_node_put(endpoint);
+   return -ENODEV;
+   }
+
+   of_node_put(endpoint);
+
+   dsi = mipi_dsi_device_register_full(host, &info);
+   if (IS_ERR(dsi)) {
+   dev_err(dev, "DSI device registration failed: %ld\n",
+   PTR_ERR(dsi));
+   return PTR_ERR(dsi);
+   }
+
+   ts->dsi = dsi;
+
+   dsi->mode_flags = (MIPI_DSI_MODE_VIDEO |
+  MIPI_DSI_MODE_VIDEO_SYNC_PULSE |
+  MIPI_DSI_MODE_LPM);
+   dsi->format = MIPI_DSI_FMT_RGB888;
+   dsi->lanes = 1;
+
+   ret = mipi_dsi_attach(dsi);
+   if (ret) {
+   dev_err(&dsi->dev, "failed to attach dsi to host: %d\n", ret);
+   return ret;
+   }
+
+   return 0;
+}
+
+static void rpi_touchscreen_detach(struct drm_panel *panel)
+{
+   struct rpi_touchscreen *ts = panel_to_ts(panel);
+
+   mipi_dsi_detach(ts->dsi);
+   mipi_dsi_device_unregister(ts->dsi);
+}
+
 static const struct drm_panel_funcs rpi_touchscreen_funcs = {
+   .attach = rpi_touchscreen_attach,
+   .detach = rpi_touchscreen_detach,
+
.disable = rpi_touchscreen_disable,
.unprepare = rpi_touchscreen_noop,
.prepare = rpi_touchscreen_noop,
@@ -359,14 +435,7 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
 {
struct device *dev = &i2c->dev;
struct rpi_touchscreen *ts;
-   struct device_node *endpoint, *dsi_host_node;
-   struct mipi_dsi_host *host;
int ver;
-   struct mipi_dsi_device_info info = {
-   .type = RPI_DSI_DRIVER_NAME,
-   .channel = 0,
-   .node = NULL,
-   };
 
ts = devm_kzalloc(dev, sizeof(*ts), GFP_KERNEL);
if (!ts)
@@ -394,35 +463,6 @@ static int rpi_touchscreen_probe(struct i2c_client *i2c,
/* /\* Turn off at boot, so we can cleanly sequence powering on. *\/ */
/* rpi_touchscreen_i2c_write(ts, REG_POWERON, 0); */
 
-   /* Look up the DSI host.  It needs to probe before we do. */
-   endpoint = of_graph_get_next_endpoint(dev->of_node, NULL);
-   if (!endpoint)
-   return -ENODEV;
-
-   dsi_host_node = of_graph_get_remote_port_parent(endpoint);
-   if (!dsi_host_node)
-   goto error;
-
-   host = of_find_mipi_dsi_host_by_node(dsi_host_node);
-   of_node_put(dsi_host_node);
-   if (!host) {
-   of_node_put(endpoint);
-   return -EPROBE_DEFER;
-   }
-
-   info.node = of_graph_get_remote_port(endpoint);
-   if (!info.node)
-   goto error;
-
-   of_node_put(endpoint);
-
-   ts->dsi = mipi_dsi_device_register_full(host, &info);
-   if (IS_ERR(ts->dsi)) {
-   dev_err(dev, "DSI device registration failed: %ld\n",
-   PTR_ERR(ts->dsi));
-   return PTR_ERR(ts->dsi);
-   }
-
drm_panel_init(

Re: [PATCH 04/10] drm/bridge: Document the probe issue with MIPI-DSI bridges

2021-07-28 Thread Maxime Ripard
Hi Jagan,

On Tue, Jul 27, 2021 at 03:12:09PM +0530, Jagan Teki wrote:
> On Tue, Jul 20, 2021 at 7:15 PM Maxime Ripard  wrote:
> >
> > Interactions between bridges, panels, MIPI-DSI host and the component
> > framework are not trivial and can lead to probing issues when
> > implementing a display driver. Let's document the various cases we need
> > too consider, and the solution to support all the cases.
> >
> > Signed-off-by: Maxime Ripard 
> > ---
> >  Documentation/gpu/drm-kms-helpers.rst |  6 +++
> >  drivers/gpu/drm/drm_bridge.c  | 60 +++
> >  2 files changed, 66 insertions(+)
> >
> > diff --git a/Documentation/gpu/drm-kms-helpers.rst 
> > b/Documentation/gpu/drm-kms-helpers.rst
> > index 10f8df7aecc0..ec2f65b31930 100644
> > --- a/Documentation/gpu/drm-kms-helpers.rst
> > +++ b/Documentation/gpu/drm-kms-helpers.rst
> > @@ -157,6 +157,12 @@ Display Driver Integration
> >  .. kernel-doc:: drivers/gpu/drm/drm_bridge.c
> > :doc: display driver integration
> >
> > +Special Care with MIPI-DSI bridges
> > +--
> > +
> > +.. kernel-doc:: drivers/gpu/drm/drm_bridge.c
> > +   :doc: special care dsi
> > +
> >  Bridge Operations
> >  -
> >
> > diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c
> > index c9a950bfdfe5..81f8dac12367 100644
> > --- a/drivers/gpu/drm/drm_bridge.c
> > +++ b/drivers/gpu/drm/drm_bridge.c
> > @@ -95,6 +95,66 @@
> >   * documentation of bridge operations for more details).
> >   */
> >
> > +/**
> > + * DOC: special care dsi
> > + *
> > + * The interaction between the bridges and other frameworks involved in
> > + * the probing of the display driver and the bridge driver can be
> > + * challenging. Indeed, there's multiple cases that needs to be
> > + * considered:
> > + *
> > + * - The display driver doesn't use the component framework and isn't a
> > + *   MIPI-DSI host. In this case, the bridge driver will probe at some
> > + *   point and the display driver should try to probe again by returning
> > + *   EPROBE_DEFER as long as the bridge driver hasn't probed.
> > + *
> > + * - The display driver doesn't use the component framework, but is a
> > + *   MIPI-DSI host. The bridge device uses the MIPI-DCS commands to be
> > + *   controlled. In this case, the bridge device is a child of the
> > + *   display device and when it will probe it's assured that the display
> > + *   device (and MIPI-DSI host) is present. The display driver will be
> > + *   assured that the bridge driver is connected between the
> > + *   &mipi_dsi_host_ops.attach and &mipi_dsi_host_ops.detach operations.
> > + *   Therefore, it must run mipi_dsi_host_register() in its probe
> > + *   function, and then run drm_bridge_attach() in its
> > + *   &mipi_dsi_host_ops.attach hook.
> > + *
> > + * - The display driver uses the component framework and is a MIPI-DSI
> > + *   host. The bridge device uses the MIPI-DCS commands to be
> > + *   controlled. This is the same situation than above, and can run
> > + *   mipi_dsi_host_register() in either its probe or bind hooks.
> > + *
> > + * - The display driver uses the component framework and is a MIPI-DSI
> > + *   host. The bridge device uses a separate bus (such as I2C) to be
> > + *   controlled. In this case, there's no correlation between the probe
> > + *   of the bridge and display drivers, so care must be taken to avoid
> > + *   an endless EPROBE_DEFER loop, with each driver waiting for the
> > + *   other to probe.
> > + *
> > + * The ideal pattern to cover the last item (and all the others in the
> > + * display driver case) is to split the operations like this:
> > + *
> > + * - In the display driver must run mipi_dsi_host_register() and
> > + *   component_add in its probe hook. It will make sure that the
> > + *   MIPI-DSI host sticks around, and that the driver's bind can be
> > + *   called.
> > + *
> > + * - In its probe hook, the bridge driver must not try to find its
> > + *   MIPI-DSI host or register as a MIPI-DSI device. As far as the
> > + *   framework is concerned, it must only call drm_bridge_add().
> > + *
> > + * - In its bind hook, the display driver must try to find the bridge
> > + *   and return -EPROBE_DEFER if it doesn't find it. If it's there, it
> > + *   must call drm_bridge_attach(). The MIPI-DSI host is now functional.
> 
>

Re: [Regression] No framebuffer console on Rpi since 5.14-rc1

2021-07-29 Thread Maxime Ripard
Hi Stefan,

On Wed, Jul 28, 2021 at 05:14:38PM +0200, Stefan Wahren wrote:
> Hi,
> 
> Am 15.07.21 um 18:35 schrieb Stefan Wahren:
> > Hi guys,
> >
> > starting with Linux 5.14-rc1 the framebuffer console on Raspberry Pi 3/4
> > (no U-Boot, multi_v7_defconfig) isn't available anymore. The display
> > shows the rainbow screen from the bootloader and than the HDMI screen
> > goes black instead of kernel messages.
> >
> > I bisected the issue:
> >
> > 62fb9874f5da54fdb243003b386128037319b219 good
> > e73f0f0ee7541171d89f2e2491130c7771ba58d3 bad
> > e058a84bfddc42ba356a2316f2cf1141974625c9 bad
> > a6eaf3850cb171c328a8b0db6d3c79286a1eba9d good
> > 007b312c6f294770de01fbc0643610145012d244 good
> > 18703923a66aecf6f7ded0e16d22eb412ddae72f good
> > 334200bf52f0637a5ab8331c557dfcecbb9c30fa bad
> > c707b73f0cfb1acc94a20389aecde65e6385349b bad
> > caa18dd6dd9305d52943a6b59f410cbc960ad0a0 good
> > 691cf8cd7a531dbfcc29d09a23c509a86fd9b24f bad
> > 2fdcb55dfc86835e4845e3f422180b5596d23cb4 bad
> > 6c3f953381e526a1623d4575660afae8b19ffa20 bad
> > 5ea4dba68305d9648b9dba30036cc36d4e877bca bad
> > 4a888ba03fd97d1cb0253581973533965bf348c4 good
> > c5ef15ae09637fb51ae43e1d1d98329d67dd4fd6 good
> > f611b1e7624ccdbd495c19e9805629e22265aa16 bad
> > ff323d6d72e1e4971c8ba9e2f3cf8afc48f22383 good
> >
> > f611b1e7624ccdbd495c19e9805629e22265aa16 is the first bad commit
> > commit f611b1e7624ccdbd495c19e9805629e22265aa16
> > Author: Kees Cook 
> > Date:   Wed Jun 2 14:52:50 2021 -0700
> >
> >     drm: Avoid circular dependencies for CONFIG_FB
> >    
> >     When cleaning up other drm config dependencies, it is too easy to create
> >     larger problems. Instead, mark CONFIG_FB as a "depends":
> >    
> >     drivers/gpu/drm/Kconfig:74:error: recursive dependency detected!
> >
> > I compared the changes to the config (based on multi_v7_defconfig) with
> > and without this patch and it shows a lot of changes. Is this intended?
> 
> just one question: does the VC4 drm driver functionally depend on
> CONFIG_FB / FRAMEBUFFER_CONSOLE ?

No, just the fbdev emulation but it can work perfectly fine without it.

> Or with other words should we re-enable CONFIG_FB like this [1] but for
> Raspberry Pi related configs?
> 
> [1] -
> https://lore.kernel.org/linux-samsung-soc/20210611100204.6240-1-m.szyprow...@samsung.com/

It should work indeed

Maxime


signature.asc
Description: PGP signature


Re: [PATCH v4 4/6] drm/sprd: add Unisoc's drm display controller driver

2021-04-07 Thread Maxime Ripard
; > > +
> > > + drm_dbg(dpu->drm, "%s() mode: "DRM_MODE_FMT"\n", __func__,
> > DRM_MODE_ARG(mode));
> > > +
> > > + if (mode->type & DRM_MODE_TYPE_PREFERRED) {
> > > + drm_display_mode_to_videomode(mode, &dpu->ctx.vm);
> >
> > You don't seem to use that anywhere else? And that's a bit fragile,
> > nothing really guarantees that it's the mode you're going to use, and
> > even then it can be adjusted.
> >
>  drm_mode convert to video_mode is been use in "sprd_dpu_init" and
> "sprd_dpi_init "
>  Preferred mode should be fixed mode, we generally don’t adjust it.

That's not really the assumption DRM is built upon though. The userspace
is even allowed to setup its own mode and try to configure it, and your
driver should take that into account.

I'd just drop that mode_valid hook, and retrieve the videomode if you
need it in atomic_enable or mode_set_no_fb

> >
> > > +
> > > + if ((mode->hdisplay == mode->htotal) ||
> > > + (mode->vdisplay == mode->vtotal))
> > > + dpu->ctx.if_type = SPRD_DPU_IF_EDPI;
> > > + else
> > > + dpu->ctx.if_type = SPRD_DPU_IF_DPI;
> >
> > From an API PoV, you would want that to be in atomic_check. However, I'm
> > not even sure what it's doing in the first place?
> >
> dpi interface mode: DPI(dsi video mode panel) and EDPI(dsi cmd mode panel)
> dpi interface mode has been used on crtc atomic_enable foo, so we need
> check dpi interface
> mode before atomic_enable.
> 
> Must be put it in atomic_check? Here is the dpi interface mode selection,
> maybe here is better?

This doesn't have any relationship to the htotal and vtotal though? it's
something that is carried over by the MIPI-DSI functions and struct
mipi_dsi_device.

> >
> > > + }
> > > +
> > > + return MODE_OK;
> > > +}
> > > +
> > > +static void sprd_crtc_atomic_enable(struct drm_crtc *crtc,
> > > +struct drm_atomic_state *state)
> > > +{
> > > + struct sprd_dpu *dpu = to_sprd_crtc(crtc);
> > > +
> > > + sprd_dpu_init(dpu);
> > > +
> > > + sprd_dpi_init(dpu);
> > > +
> > > + enable_irq(dpu->ctx.irq);
> >
> > Shouldn't this be in enable_vblank? And I would assume that you would
> > have the interrupts enabled all the time, but disabled in your device?
> >
> It seems better to put in enable_vblank, i will try and test it... Thks
> 
>   And I would assume that you would
> have the interrupts enabled all the time, but disabled in your device?
> [kevin]I don’t quite understand this, can you help me explain it in
> detail?

You seem to have a register that enables and disables the interrupt in
that device. The way we usually deal with them in this case is just to
call request_irq in your bind/probe with the interrupts enabled at the
controller level, and mask them when needed at the device level by
clearing / setting that bit.

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v4 5/6] dt-bindings: display: add Unisoc's mipi dsi controller bindings

2021-04-07 Thread Maxime Ripard
On Wed, Mar 31, 2021 at 09:49:14AM +0800, Kevin Tang wrote:
> Hi Maxime,
> 
> Maxime Ripard  于2021年3月24日周三 下午7:13写道:
> 
> > On Mon, Feb 22, 2021 at 09:28:21PM +0800, Kevin Tang wrote:
> > > From: Kevin Tang 
> > >
> > > Adds MIPI DSI Controller
> > > support for Unisoc's display subsystem.
> > >
> > > Cc: Orson Zhai 
> > > Cc: Chunyan Zhang 
> > > Signed-off-by: Kevin Tang 
> > > Reviewed-by: Rob Herring 
> > > ---
> > >  .../display/sprd/sprd,sharkl3-dsi-host.yaml   | 102 ++
> > >  1 file changed, 102 insertions(+)
> > >  create mode 100644
> > Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
> > >
> > > diff --git
> > a/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
> > b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
> > > new file mode 100644
> > > index 0..d439f688f
> > > --- /dev/null
> > > +++
> > b/Documentation/devicetree/bindings/display/sprd/sprd,sharkl3-dsi-host.yaml
> > > @@ -0,0 +1,102 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id:
> > http://devicetree.org/schemas/display/sprd/sprd,sharkl3-dsi-host.yaml#
> > > +$schema: http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Unisoc MIPI DSI Controller
> > > +
> > > +maintainers:
> > > +  - Kevin Tang 
> > > +
> > > +properties:
> > > +  compatible:
> > > +const: sprd,sharkl3-dsi-host
> > > +
> > > +  reg:
> > > +maxItems: 1
> > > +
> > > +  interrupts:
> > > +maxItems: 2
> > > +
> > > +  clocks:
> > > +minItems: 1
> > > +
> > > +  clock-names:
> > > +items:
> > > +  - const: clk_src_96m
> > > +
> > > +  power-domains:
> > > +maxItems: 1
> > > +
> > > +  ports:
> > > +type: object
> > > +
> > > +properties:
> > > +  "#address-cells":
> > > +const: 1
> > > +
> > > +  "#size-cells":
> > > +const: 0
> > > +
> > > +  port@0:
> > > +type: object
> > > +description:
> > > +  A port node with endpoint definitions as defined in
> > > +  Documentation/devicetree/bindings/media/video-interfaces.txt.
> > > +  That port should be the input endpoint, usually coming from
> > > +  the associated DPU.
> > > +  port@1:
> > > +type: object
> > > +description:
> > > +  A port node with endpoint definitions as defined in
> > > +  Documentation/devicetree/bindings/media/video-interfaces.txt.
> > > +  That port should be the output endpoint, usually output to
> > > +  the associated panel.
> >
> > The DSI generic binding asks that peripherals that are controlled
> > through a DCS be a subnode of the MIPI-DSI bus, not through a port
> > endpoint.
> >
>  Our DSI controller don't support DCS now...

I'm not sure I follow you, you mentionned in the patch 4 that you were
testing for a device to be in command mode, how would that work without
DCS support?

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v4 6/6] drm/sprd: add Unisoc's drm mipi dsi&dphy driver

2021-04-07 Thread Maxime Ripard
On Wed, Mar 31, 2021 at 09:47:12AM +0800, Kevin Tang wrote:
> > > diff --git a/drivers/gpu/drm/sprd/Makefile
> > b/drivers/gpu/drm/sprd/Makefile
> > > index 6c25bfa99..d49f4977b 100644
> > > --- a/drivers/gpu/drm/sprd/Makefile
> > > +++ b/drivers/gpu/drm/sprd/Makefile
> > > @@ -1,5 +1,8 @@
> > >  # SPDX-License-Identifier: GPL-2.0
> > >
> > >  obj-y := sprd_drm.o \
> > > - sprd_dpu.o
> > > -
> > > + sprd_dpu.o \
> > > + sprd_dsi.o \
> > > + dw_dsi_ctrl.o \
> > > + dw_dsi_ctrl_ppi.o \
> >
> > So it's a designware IP? There's a driver for it already that seems
> > fairly similar:
> > drivers/gpu/drm/bridge/synopsys/dw-mipi-dsi.c
> >
> Our dw dsi controller is not a standard synopsys ip, we have updated a lot
> on the basic ip version,
> the entire control register is different, i have cc to drm/bridge reviewers
> and maintainers.

You should make it more obvious then in a comment or in the name of the
driver. If it's fairly different from the original IP from Synopsys,
maybe you should just drop the reference to the name?

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH 0/4] drm: Generic dumb_map_offset for TTM-based drivers

2021-04-08 Thread Maxime Ripard
On Tue, Apr 06, 2021 at 10:29:38AM +0200, Thomas Zimmermann wrote:
> The implementation of drm_driver.dumb_map_offset is the same for several
> TTM-based drivers. Provide a common function in GEM-TTM helpers.

For the series:
Acked-by: Maxime Ripard 

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v2 08/10] drm/simpledrm: Acquire clocks from DT device node

2021-04-08 Thread Maxime Ripard
Hi,

On Thu, Mar 18, 2021 at 11:29:19AM +0100, Thomas Zimmermann wrote:
> Make sure required hardware clocks are enabled while the firmware
> framebuffer is in use.
> 
> The basic code has been taken from the simplefb driver and adapted
> to DRM. Clocks are released automatically via devres helpers.
> 
> Signed-off-by: Thomas Zimmermann 
> Tested-by: nerdopolis 

Even though it's definitely simpler to review, merging the driver first
and then the clocks and regulators will break bisection on the platforms
that rely on them

Another thing worth considering is also that both drivers will probe if
they are enabled (which is pretty likely), which is not great :)

I guess we should make them mutually exclusive through Kconfig

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v3 10/11] drm: Use state helper instead of the plane state pointer

2021-04-08 Thread Maxime Ripard
Hi Stephen,

On Tue, Mar 30, 2021 at 11:56:15AM -0700, Stephen Boyd wrote:
> Quoting Maxime Ripard (2021-03-30 08:35:27)
> > Hi Stephen,
> > 
> > On Mon, Mar 29, 2021 at 06:52:01PM -0700, Stephen Boyd wrote:
> > > Trimming Cc list way down, sorry if that's too much.
> > > 
> > > Quoting Maxime Ripard (2021-02-19 04:00:30)
> > > > Many drivers reference the plane->state pointer in order to get the
> > > > current plane state in their atomic_update or atomic_disable hooks,
> > > > which would be the new plane state in the global atomic state since
> > > > _swap_state happened when those hooks are run.
> > > 
> > > Does this mean drm_atomic_helper_swap_state()?
> > 
> > Yep. Previous to that call in drm_atomic_helper_commit, plane->state is
> > the state currently programmed in the hardware, so the old state (that's
> > the case you have with atomic_check for example)
> > 
> > Once drm_atomic_helper_swap_state has run, plane->state is now the state
> > that needs to be programmed into the hardware, so the new state.
> 
> Ok, and I suppose that is called by drm_atomic_helper_commit()?

Yep :)

> So presumably a modeset is causing this? I get the NULL pointer around
> the time we switch from the splash screen to the login screen. I think
> there's a modeset during that transition.

It's very likely yeah. I really don't get how that pointer could be null
though :/

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PULL] drm-misc-next

2021-04-09 Thread Maxime Ripard
Hi Dave, Daniel,

Like you asked, here's this week drm-misc-next PR

Maxime

drm-misc-next-2021-04-09:
drm-misc-next for 5.13:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - bridge: Fix Kconfig dependency
  - cmdline: Refuse zero width/height mode
  - ttm: Ignore signaled move fences, ioremap buffer according to mem
 caching settins

Driver Changes:
  - Conversions to sysfs_emit
  - tegra: Don't register DP AUX channels before connectors
  - zynqmp: Fix for an out-of-bound (but within struct padding) memset
The following changes since commit 6c744983004ebc66756e582294672f8b991288d5:

  drm/bridge: anx7625: disable regulators when power off (2021-04-01 10:38:02 
+0200)

are available in the Git repository at:

  git://anongit.freedesktop.org/drm/drm-misc tags/drm-misc-next-2021-04-09

for you to fetch changes up to e8b8b0df8694e39ea6bbbdb9e2fcfa78a61e2e42:

  drm/panel: Convert sysfs sprintf/snprintf family to sysfs_emit (2021-04-08 
20:41:38 -0400)


drm-misc-next for 5.13:

UAPI Changes:

Cross-subsystem Changes:

Core Changes:
  - bridge: Fix Kconfig dependency
  - cmdline: Refuse zero width/height mode
  - ttm: Ignore signaled move fences, ioremap buffer according to mem
 caching settins

Driver Changes:
  - Conversions to sysfs_emit
  - tegra: Don't register DP AUX channels before connectors
  - zynqmp: Fix for an out-of-bound (but within struct padding) memset


Carsten Haitzler (1):
  drm/komeda: Fix bit check to import to value of proper type

Christian König (1):
  drm/sched: add missing member documentation

Dafna Hirschfeld (1):
  drm/bridge: fix typo in Kconfig

Dan Carpenter (1):
  drm: xlnx: zynqmp: fix a memset in zynqmp_dp_train()

David Stevens (1):
  drm/syncobj: use newly allocated stub fences

Felix Kuehling (1):
  drm/ttm: Ignore signaled move fences

Guobin Huang (1):
  gma500: Use DEFINE_SPINLOCK() for spinlock

Julian Braha (1):
  drivers: gpu: drm: bridge: fix kconfig dependency on DRM_KMS_HELPER

Lyude Paul (4):
  drm/dp: Fixup kernel docs for struct drm_dp_aux
  drm/tegra: Don't register DP AUX channels before connectors
  drm/print: Fixup DRM_DEBUG_KMS_RATELIMITED()
  drm/dp_mst: Drop DRM_ERROR() on kzalloc() fail in 
drm_dp_mst_handle_up_req()

Oak Zeng (1):
  drm/ttm: ioremap buffer according to TTM mem caching setting

Tian Tao (2):
  drm/komeda: Convert sysfs sprintf/snprintf family to sysfs_emit
  drm/panel: Convert sysfs sprintf/snprintf family to sysfs_emit

Ville Syrjälä (2):
  drm: Refuse to create zero width/height cmdline modes
  drm/vblank: Do not store a new vblank timestamp in drm_vblank_restore()

Wan Jiabing (1):
  drm/drm_internal.h: Remove repeated struct declaration

Zhang Jianhua (1):
  drm/bridge: lt8912b: Add header file 

 drivers/dma-buf/dma-fence.c| 27 -
 drivers/gpu/drm/arm/display/include/malidp_utils.h |  3 --
 drivers/gpu/drm/arm/display/komeda/komeda_dev.c|  6 +--
 .../gpu/drm/arm/display/komeda/komeda_pipeline.c   | 16 +---
 .../drm/arm/display/komeda/komeda_pipeline_state.c | 19 ++
 drivers/gpu/drm/bridge/Kconfig |  3 +-
 drivers/gpu/drm/bridge/lontium-lt8912b.c   |  1 +
 drivers/gpu/drm/drm_dp_mst_topology.c  |  5 +--
 drivers/gpu/drm/drm_internal.h |  1 -
 drivers/gpu/drm/drm_modes.c|  3 ++
 drivers/gpu/drm/drm_syncobj.c  | 25 +---
 drivers/gpu/drm/drm_vblank.c   |  3 +-
 drivers/gpu/drm/gma500/power.c |  3 +-
 drivers/gpu/drm/panel/panel-tpo-td043mtea1.c   |  4 +-
 drivers/gpu/drm/tegra/dpaux.c  | 11 +++---
 drivers/gpu/drm/ttm/ttm_bo.c   |  3 +-
 drivers/gpu/drm/ttm/ttm_bo_util.c  | 14 +++
 drivers/gpu/drm/xlnx/zynqmp_dp.c   |  2 +-
 include/drm/drm_dp_helper.h| 44 +++---
 include/drm/drm_print.h| 20 ++
 include/drm/gpu_scheduler.h|  1 +
 include/linux/dma-fence.h  |  1 +
 22 files changed, 142 insertions(+), 73 deletions(-)


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


Re: [PATCH v2 4/5] drm/vc4: hdmi: Enable the scrambler

2021-04-09 Thread Maxime Ripard
Hi Dave,

On Thu, Apr 01, 2021 at 12:30:45PM +0100, Dave Stevenson wrote:
> > Signed-off-by: Maxime Ripard 
> > ---
> >  drivers/gpu/drm/vc4/vc4_hdmi.c  | 56 +
> >  drivers/gpu/drm/vc4/vc4_hdmi_regs.h |  3 ++
> >  2 files changed, 59 insertions(+)
> >
> > diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
> > index 0924a1b9e186..530c83097b1a 100644
> > --- a/drivers/gpu/drm/vc4/vc4_hdmi.c
> > +++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
> > @@ -35,6 +35,7 @@
> >  #include 
> >  #include 
> >  #include 
> > +#include 
> >  #include 
> >  #include 
> >  #include 
> > @@ -76,6 +77,8 @@
> >  #define VC5_HDMI_VERTB_VSPO_SHIFT  16
> >  #define VC5_HDMI_VERTB_VSPO_MASK   VC4_MASK(29, 16)
> >
> > +#define VC5_HDMI_SCRAMBLER_CTL_ENABLE  BIT(0)
> > +
> >  #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_SHIFT 8
> >  #define VC5_HDMI_DEEP_COLOR_CONFIG_1_INIT_PACK_PHASE_MASK  
> > VC4_MASK(10, 8)
> >
> > @@ -457,6 +460,56 @@ static void vc4_hdmi_set_infoframes(struct drm_encoder 
> > *encoder)
> > vc4_hdmi_set_audio_infoframe(encoder);
> >  }
> >
> > +static bool vc4_hdmi_supports_scrambling(struct drm_encoder *encoder,
> > +struct drm_display_mode *mode)
> > +{
> > +   struct vc4_hdmi_encoder *vc4_encoder = to_vc4_hdmi_encoder(encoder);
> > +   struct vc4_hdmi *vc4_hdmi = encoder_to_vc4_hdmi(encoder);
> > +   struct drm_display_info *display = 
> > &vc4_hdmi->connector.display_info;
> > +
> > +   if (!vc4_encoder->hdmi_monitor)
> > +   return false;
> > +
> > +   if (!display->hdmi.scdc.supported ||
> > +   !display->hdmi.scdc.scrambling.supported)
> > +   return false;
> > +
> 
> I think I made this comment last time, but possibly not very clearly.

I must have missed it then, sorry :/

> Up to this point in the function is whether the display/hdmi interface
> *supports* scrambling. The bit after is whether it is *required* to be
> enabled by the mode.

Thomas made a suggestion to move the mode clock check to a helper, so
we'll end up with essentially a helper about whether we support
scrambling or not and if the mode requires it.

> At the moment, if the display/interface supports scrambling but the
> mode doesn't need it, then the scrambling setup is never touched. That
> makes a few assumptions about the default settings.
> Boot with the firmware selecting 4k60 (ie scrambling on), but
> video=HDMI-1:1920x1080 in the kernel command line and you'll have a
> mess with scrambling enabled but not signalled.
> 
> I'd be happier if the display/interface says scrambling is supported
> then we always call drm_scdc_set_high_tmds_clock_ratio,
> drm_scdc_set_scrambling and set the VC5_HDMI_SCRAMBLER_CTL_ENABLE
> register bit appropriately for the mode. Feel free to disagree with me
> though.

I think part of it is due to our custom helpers never being called
currently during the boot process. Once that is fixed, the disable
helpers will be called and will disable the scrambling so we should be
good there.

This creates another issue though. That function takes the mode as the
argument and at boot time the mode pointer will be null. I think we can
work around it by assuming that we need to disable it at boot all the
time (and thus ignore the test if our pointer is null).

Would that work for you?

Maxime


signature.asc
Description: PGP signature
___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 2/2] drm/vc4: hdmi: Convert to the new clock request API

2021-04-13 Thread Maxime Ripard
The new clock request API allows us to increase the rate of the HSM
clock to match our pixel rate requirements while decreasing it when
we're done, resulting in a better power-efficiency.

Signed-off-by: Maxime Ripard 
---
 drivers/gpu/drm/vc4/vc4_hdmi.c | 19 ---
 drivers/gpu/drm/vc4/vc4_hdmi.h |  3 +++
 2 files changed, 15 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 1fda574579af..244053de6150 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -473,7 +473,9 @@ static void vc4_hdmi_encoder_post_crtc_powerdown(struct 
drm_encoder *encoder,
   HDMI_READ(HDMI_VID_CTL) & ~VC4_HD_VID_CTL_ENABLE);
 
clk_disable_unprepare(vc4_hdmi->pixel_bvb_clock);
+   clk_request_done(vc4_hdmi->bvb_req);
clk_disable_unprepare(vc4_hdmi->hsm_clock);
+   clk_request_done(vc4_hdmi->hsm_req);
clk_disable_unprepare(vc4_hdmi->pixel_clock);
 
ret = pm_runtime_put(&vc4_hdmi->pdev->dev);
@@ -778,9 +780,9 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct 
drm_encoder *encoder,
 * pixel clock, but HSM ends up being the limiting factor.
 */
hsm_rate = max_t(unsigned long, 12000, (pixel_rate / 100) * 101);
-   ret = clk_set_min_rate(vc4_hdmi->hsm_clock, hsm_rate);
-   if (ret) {
-   DRM_ERROR("Failed to set HSM clock rate: %d\n", ret);
+   vc4_hdmi->hsm_req = clk_request_start(vc4_hdmi->hsm_clock, hsm_rate);
+   if (IS_ERR(vc4_hdmi->hsm_req)) {
+   DRM_ERROR("Failed to set HSM clock rate: %ld\n", 
PTR_ERR(vc4_hdmi->hsm_req));
return;
}
 
@@ -797,10 +799,11 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct 
drm_encoder *encoder,
 * FIXME: When the pixel freq is 594MHz (4k60), this needs to be setup
 * at 300MHz.
 */
-   ret = clk_set_min_rate(vc4_hdmi->pixel_bvb_clock,
-  (hsm_rate > VC4_HSM_MID_CLOCK ? 15000 : 
7500));
-   if (ret) {
-   DRM_ERROR("Failed to set pixel bvb clock rate: %d\n", ret);
+   vc4_hdmi->bvb_req = clk_request_start(vc4_hdmi->pixel_bvb_clock,
+ (hsm_rate > VC4_HSM_MID_CLOCK ? 
15000 : 7500));
+   if (IS_ERR(vc4_hdmi->bvb_req)) {
+   DRM_ERROR("Failed to set pixel bvb clock rate: %ld\n", 
PTR_ERR(vc4_hdmi->bvb_req));
+   clk_request_done(vc4_hdmi->hsm_req);
clk_disable_unprepare(vc4_hdmi->hsm_clock);
clk_disable_unprepare(vc4_hdmi->pixel_clock);
return;
@@ -809,6 +812,8 @@ static void vc4_hdmi_encoder_pre_crtc_configure(struct 
drm_encoder *encoder,
ret = clk_prepare_enable(vc4_hdmi->pixel_bvb_clock);
if (ret) {
DRM_ERROR("Failed to turn on pixel bvb clock: %d\n", ret);
+   clk_request_done(vc4_hdmi->bvb_req);
+   clk_request_done(vc4_hdmi->hsm_req);
clk_disable_unprepare(vc4_hdmi->hsm_clock);
clk_disable_unprepare(vc4_hdmi->pixel_clock);
return;
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.h b/drivers/gpu/drm/vc4/vc4_hdmi.h
index 3cebd1fd00fc..9ac4a2c751df 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.h
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.h
@@ -167,6 +167,9 @@ struct vc4_hdmi {
 
struct reset_control *reset;
 
+   struct clk_request *bvb_req;
+   struct clk_request *hsm_req;
+
struct debugfs_regset32 hdmi_regset;
struct debugfs_regset32 hd_regset;
 };
-- 
2.30.2

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH 1/2] clk: Introduce a clock request API

2021-04-13 Thread Maxime Ripard
It's not unusual to find clocks being shared across multiple devices
that need to change the rate depending on what the device is doing at a
given time.

The SoC found on the RaspberryPi4 (BCM2711) is in such a situation
between its two HDMI controllers that share a clock that needs to be
raised depending on the output resolution of each controller.

The current clk_set_rate API doesn't really allow to support that case
since there's really no synchronisation between multiple users, it's
essentially a fire-and-forget solution.

clk_set_min_rate does allow for such a synchronisation, but has another
drawback: it doesn't allow to reduce the clock rate once the work is
over.

In our previous example, this means that if we were to raise the
resolution of one HDMI controller to the largest resolution and then
changing for a smaller one, we would still have the clock running at the
largest resolution rate resulting in a poor power-efficiency.

In order to address both issues, let's create an API that allows user to
create temporary requests to increase the rate to a minimum, before
going back to the initial rate once the request is done.

This introduces mainly two side-effects:

  * There's an interaction between clk_set_rate and requests. This has
been addressed by having clk_set_rate increasing the rate if it's
greater than what the requests asked for, and in any case changing
the rate the clock will return to once all the requests are done.

  * Similarly, clk_round_rate has been adjusted to take the requests
into account and return a rate that will be greater or equal to the
    requested rates.

Signed-off-by: Maxime Ripard 
---
 drivers/clk/clk.c   | 121 
 include/linux/clk.h |   4 ++
 2 files changed, 125 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 5052541a0986..4fd91a57e6db 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -77,12 +77,14 @@ struct clk_core {
unsigned intprotect_count;
unsigned long   min_rate;
unsigned long   max_rate;
+   unsigned long   default_request_rate;
unsigned long   accuracy;
int phase;
struct clk_duty duty;
struct hlist_head   children;
struct hlist_node   child_node;
struct hlist_head   clks;
+   struct list_headpending_requests;
unsigned intnotifier_count;
 #ifdef CONFIG_DEBUG_FS
struct dentry   *dentry;
@@ -105,6 +107,12 @@ struct clk {
struct hlist_node clks_node;
 };
 
+struct clk_request {
+   struct list_head list;
+   struct clk *clk;
+   unsigned long rate;
+};
+
 /***   runtime pm  ***/
 static int clk_pm_runtime_get(struct clk_core *core)
 {
@@ -1434,10 +1442,14 @@ unsigned long clk_hw_round_rate(struct clk_hw *hw, 
unsigned long rate)
 {
int ret;
struct clk_rate_request req;
+   struct clk_request *clk_req;
 
clk_core_get_boundaries(hw->core, &req.min_rate, &req.max_rate);
req.rate = rate;
 
+   list_for_each_entry(clk_req, &hw->core->pending_requests, list)
+   req.min_rate = max(clk_req->rate, req.min_rate);
+
ret = clk_core_round_rate_nolock(hw->core, &req);
if (ret)
return 0;
@@ -1458,6 +1470,7 @@ EXPORT_SYMBOL_GPL(clk_hw_round_rate);
 long clk_round_rate(struct clk *clk, unsigned long rate)
 {
struct clk_rate_request req;
+   struct clk_request *clk_req;
int ret;
 
if (!clk)
@@ -1471,6 +1484,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
req.rate = rate;
 
+   list_for_each_entry(clk_req, &clk->core->pending_requests, list)
+   req.min_rate = max(clk_req->rate, req.min_rate);
+
ret = clk_core_round_rate_nolock(clk->core, &req);
 
if (clk->exclusive_count)
@@ -1938,6 +1954,7 @@ static struct clk_core *clk_calc_new_rates(struct 
clk_core *core,
unsigned long new_rate;
unsigned long min_rate;
unsigned long max_rate;
+   struct clk_request *req;
int p_index = 0;
long ret;
 
@@ -1952,6 +1969,9 @@ static struct clk_core *clk_calc_new_rates(struct 
clk_core *core,
 
clk_core_get_boundaries(core, &min_rate, &max_rate);
 
+   list_for_each_entry(req, &core->pending_requests, list)
+   min_rate = max(req->rate, min_rate);
+
/* find the closest rate and parent clk/rate */
if (clk_core_can_round(core)) {
struct clk_rate_request req;
@@ -2156,6 +2176,7 @@ static unsigned long 
clk_core_req_round_rate_nolock(struct clk_core *core,
 {
int ret, cnt;
struct clk_rate_req

[PATCH 0/2] clk: Implement a clock request API

2021-04-13 Thread Maxime Ripard
Hi,

This is a follow-up of the discussion here:
https://lore.kernel.org/linux-clk/20210319150355.xzw7ikwdaga2dwhv@gilmour/

This implements a mechanism to raise and lower clock rates based on consumer
workloads, with an example of such an implementation for the RaspberryPi4 HDMI
controller.

There's a couple of things worth discussing:

  - The name is in conflict with clk_request_rate, and even though it feels
like the right name to me, we should probably avoid any confusion

  - The code so far implements a policy of always going for the lowest rate
possible. While we don't have an use-case for something else, this should
maybe be made more flexible?

Let me know what you think
Maxime

Maxime Ripard (2):
  clk: Introduce a clock request API
  drm/vc4: hdmi: Convert to the new clock request API

 drivers/clk/clk.c  | 121 +
 drivers/gpu/drm/vc4/vc4_hdmi.c |  19 --
 drivers/gpu/drm/vc4/vc4_hdmi.h |   3 +
 include/linux/clk.h|   4 ++
 4 files changed, 140 insertions(+), 7 deletions(-)

-- 
2.30.2

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v2 2/5] drm/connector: Add helper to compare HDR metadata

2021-04-13 Thread Maxime Ripard
All the drivers that support the HDR metadata property have a similar
function to compare the metadata from one connector state to the next,
and force a mode change if they differ.

All these functions run pretty much the same code, so let's turn it into
an helper that can be shared across those drivers.

Reviewed-by: Harry Wentland 
Reviewed-by: Jernej Skrabec 
Signed-off-by: Maxime Ripard 

---

Changes from v1:
  - Rebased on latest drm-misc-next tag
  - Added the tags
  - Fix build breakage on amdgpu
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 23 ++-
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c | 17 +--
 drivers/gpu/drm/drm_connector.c   | 28 +++
 drivers/gpu/drm/i915/display/intel_atomic.c   | 13 +
 include/drm/drm_connector.h   |  2 ++
 5 files changed, 34 insertions(+), 49 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 1e22ce718010..ed1972fb531d 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -5967,25 +5967,6 @@ static int fill_hdr_info_packet(const struct 
drm_connector_state *state,
return 0;
 }
 
-static bool
-is_hdr_metadata_different(const struct drm_connector_state *old_state,
- const struct drm_connector_state *new_state)
-{
-   struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
-   struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
-
-   if (old_blob != new_blob) {
-   if (old_blob && new_blob &&
-   old_blob->length == new_blob->length)
-   return memcmp(old_blob->data, new_blob->data,
- old_blob->length);
-
-   return true;
-   }
-
-   return false;
-}
-
 static int
 amdgpu_dm_connector_atomic_check(struct drm_connector *conn,
 struct drm_atomic_state *state)
@@ -6003,7 +5984,7 @@ amdgpu_dm_connector_atomic_check(struct drm_connector 
*conn,
if (!crtc)
return 0;
 
-   if (is_hdr_metadata_different(old_con_state, new_con_state)) {
+   if (!drm_connector_atomic_hdr_metadata_equal(old_con_state, 
new_con_state)) {
struct dc_info_packet hdr_infopacket;
 
ret = fill_hdr_info_packet(new_con_state, &hdr_infopacket);
@@ -8357,7 +8338,7 @@ static void amdgpu_dm_atomic_commit_tail(struct 
drm_atomic_state *state)
  dm_old_crtc_state->abm_level;
 
hdr_changed =
-   is_hdr_metadata_different(old_con_state, new_con_state);
+   !drm_connector_atomic_hdr_metadata_equal(old_con_state, 
new_con_state);
 
if (!scaling_changed && !abm_changed && !hdr_changed)
continue;
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c 
b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index f24bbb840dbf..f871e33c2fc9 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2395,21 +2395,6 @@ static int dw_hdmi_connector_get_modes(struct 
drm_connector *connector)
return ret;
 }
 
-static bool hdr_metadata_equal(const struct drm_connector_state *old_state,
-  const struct drm_connector_state *new_state)
-{
-   struct drm_property_blob *old_blob = old_state->hdr_output_metadata;
-   struct drm_property_blob *new_blob = new_state->hdr_output_metadata;
-
-   if (!old_blob || !new_blob)
-   return old_blob == new_blob;
-
-   if (old_blob->length != new_blob->length)
-   return false;
-
-   return !memcmp(old_blob->data, new_blob->data, old_blob->length);
-}
-
 static int dw_hdmi_connector_atomic_check(struct drm_connector *connector,
  struct drm_atomic_state *state)
 {
@@ -2423,7 +2408,7 @@ static int dw_hdmi_connector_atomic_check(struct 
drm_connector *connector,
if (!crtc)
return 0;
 
-   if (!hdr_metadata_equal(old_state, new_state)) {
+   if (!drm_connector_atomic_hdr_metadata_equal(old_state, new_state)) {
crtc_state = drm_atomic_get_crtc_state(state, crtc);
if (IS_ERR(crtc_state))
return PTR_ERR(crtc_state);
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index a4aa2d87af35..b13021b1b128 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2171,6 +2171,34 @@ int 
drm_connector_attach_hdr_output_metadata_property(struct drm_connector *conn
 }
 EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property);
 
+/**
+ * drm_connector_atomic_hdr_metadata_equal - checks if the h

[PATCH v2 4/5] drm/connector: Add a helper to attach the colorspace property

2021-04-13 Thread Maxime Ripard
The intel driver uses the same logic to attach the Colorspace property
in multiple places and we'll need it in vc4 too. Let's move that common
code in a helper.

Signed-off-by: Maxime Ripard 

---

Changes from v1:
  - New patch
---
 drivers/gpu/drm/drm_connector.c   | 20 +++
 .../gpu/drm/i915/display/intel_connector.c|  6 ++
 include/drm/drm_connector.h   |  1 +
 3 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index b13021b1b128..6a20b249e533 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2171,6 +2171,26 @@ int 
drm_connector_attach_hdr_output_metadata_property(struct drm_connector *conn
 }
 EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property);
 
+/**
+ * drm_connector_attach_colorspace_property - attach "Colorspace" property
+ * @connector: connector to attach the property on.
+ *
+ * This is used to allow the userspace to signal the output colorspace
+ * to the driver.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_colorspace_property(struct drm_connector *connector)
+{
+   struct drm_property *prop = connector->colorspace_property;
+
+   drm_object_attach_property(&connector->base, prop, 
DRM_MODE_COLORIMETRY_DEFAULT);
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_colorspace_property);
+
 /**
  * drm_connector_atomic_hdr_metadata_equal - checks if the hdr metadata changed
  * @old_state: old connector state to compare
diff --git a/drivers/gpu/drm/i915/display/intel_connector.c 
b/drivers/gpu/drm/i915/display/intel_connector.c
index d5ceb7bdc14b..9bed1ccecea0 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.c
+++ b/drivers/gpu/drm/i915/display/intel_connector.c
@@ -282,14 +282,12 @@ void
 intel_attach_hdmi_colorspace_property(struct drm_connector *connector)
 {
if (!drm_mode_create_hdmi_colorspace_property(connector))
-   drm_object_attach_property(&connector->base,
-  connector->colorspace_property, 0);
+   drm_connector_attach_colorspace_property(connector);
 }
 
 void
 intel_attach_dp_colorspace_property(struct drm_connector *connector)
 {
if (!drm_mode_create_dp_colorspace_property(connector))
-   drm_object_attach_property(&connector->base,
-  connector->colorspace_property, 0);
+   drm_connector_attach_colorspace_property(connector);
 }
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1f51d73ca715..714d1a01c065 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1671,6 +1671,7 @@ int drm_connector_attach_scaling_mode_property(struct 
drm_connector *connector,
   u32 scaling_mode_mask);
 int drm_connector_attach_vrr_capable_property(
struct drm_connector *connector);
+int drm_connector_attach_colorspace_property(struct drm_connector *connector);
 int drm_connector_attach_hdr_output_metadata_property(struct drm_connector 
*connector);
 bool drm_connector_atomic_hdr_metadata_equal(struct drm_connector_state 
*old_state,
 struct drm_connector_state 
*new_state);
-- 
2.30.2

___
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel


[PATCH v2 1/5] drm/connector: Create a helper to attach the hdr_output_metadata property

2021-04-13 Thread Maxime Ripard
All the drivers that implement HDR output call pretty much the same
function to initialise the hdr_output_metadata property, and while the
creation of that property is in a helper, every driver uses the same
code to attach it.

Provide a helper for it as well

Reviewed-by: Harry Wentland 
Reviewed-by: Jernej Skrabec 
Signed-off-by: Maxime Ripard 

---

Changes from v1:
  - Rebased on latest drm-misc-next tag
  - Added the tags
---
 .../gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c |  4 +---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi.c |  3 +--
 drivers/gpu/drm/drm_connector.c   | 21 +++
 drivers/gpu/drm/i915/display/intel_hdmi.c |  3 +--
 include/drm/drm_connector.h   |  1 +
 5 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c 
b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
index 55e39b462a5e..1e22ce718010 100644
--- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
+++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c
@@ -7078,9 +7078,7 @@ void amdgpu_dm_connector_init_helper(struct 
amdgpu_display_manager *dm,
if (connector_type == DRM_MODE_CONNECTOR_HDMIA ||
connector_type == DRM_MODE_CONNECTOR_DisplayPort ||
connector_type == DRM_MODE_CONNECTOR_eDP) {
-   drm_object_attach_property(
-   &aconnector->base.base,
-   dm->ddev->mode_config.hdr_output_metadata_property, 0);
+   
drm_connector_attach_hdr_output_metadata_property(&aconnector->base);
 
if (!aconnector->mst_port)

drm_connector_attach_vrr_capable_property(&aconnector->base);
diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c 
b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
index dda4fa9a1a08..f24bbb840dbf 100644
--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi.c
@@ -2492,8 +2492,7 @@ static int dw_hdmi_connector_create(struct dw_hdmi *hdmi)
drm_connector_attach_max_bpc_property(connector, 8, 16);
 
if (hdmi->version >= 0x200a && hdmi->plat_data->use_drm_infoframe)
-   drm_object_attach_property(&connector->base,
-   
connector->dev->mode_config.hdr_output_metadata_property, 0);
+   drm_connector_attach_hdr_output_metadata_property(connector);
 
drm_connector_attach_encoder(connector, hdmi->bridge.encoder);
 
diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c
index 7631f76e7f34..a4aa2d87af35 100644
--- a/drivers/gpu/drm/drm_connector.c
+++ b/drivers/gpu/drm/drm_connector.c
@@ -2150,6 +2150,27 @@ int drm_connector_attach_max_bpc_property(struct 
drm_connector *connector,
 }
 EXPORT_SYMBOL(drm_connector_attach_max_bpc_property);
 
+/**
+ * drm_connector_attach_hdr_output_metadata_property - attach 
"HDR_OUTPUT_METADA" property
+ * @connector: connector to attach the property on.
+ *
+ * This is used to allow the userspace to send HDR Metadata to the
+ * driver.
+ *
+ * Returns:
+ * Zero on success, negative errno on failure.
+ */
+int drm_connector_attach_hdr_output_metadata_property(struct drm_connector 
*connector)
+{
+   struct drm_device *dev = connector->dev;
+   struct drm_property *prop = 
dev->mode_config.hdr_output_metadata_property;
+
+   drm_object_attach_property(&connector->base, prop, 0);
+
+   return 0;
+}
+EXPORT_SYMBOL(drm_connector_attach_hdr_output_metadata_property);
+
 /**
  * drm_connector_set_vrr_capable_property - sets the variable refresh rate
  * capable property for a connector
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c 
b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 95919d325b0b..f2f1b025e6ba 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -2965,8 +2965,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, 
struct drm_connector *c
drm_connector_attach_content_type_property(connector);
 
if (INTEL_GEN(dev_priv) >= 10 || IS_GEMINILAKE(dev_priv))
-   drm_object_attach_property(&connector->base,
-   
connector->dev->mode_config.hdr_output_metadata_property, 0);
+   drm_connector_attach_hdr_output_metadata_property(connector);
 
if (!HAS_GMCH(dev_priv))
drm_connector_attach_max_bpc_property(connector, 8, 12);
diff --git a/include/drm/drm_connector.h b/include/drm/drm_connector.h
index 1922b278ffad..32172dab8427 100644
--- a/include/drm/drm_connector.h
+++ b/include/drm/drm_connector.h
@@ -1671,6 +1671,7 @@ int drm_connector_attach_scaling_mode_property(struct 
drm_connector *connector,
   u32 scaling_mode_mask);
 int drm_connector_attach_vrr_capable_property(
struct drm_connector *c

  1   2   3   4   5   6   7   8   9   10   >