Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support
@ 2026-05-19  9:01 Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 1/8] drm: atmel-hlcdc: Fix off-by-one in vertical back porch setting Manikandan Muralidharan
                   ` (7 more replies)
  0 siblings, 8 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

This series fixes several issues in the atmel-hlcdc CRTC clock handling
and adds LVDS display support for XLCDC-based SoCs.

The first patch fixes a pre-existing off-by-one error in the vertical
back porch register calculation. The following patches progressively
clean up the clock divider logic by introducing DIV_ROUND_CLOSEST,
defining a named maximum divider constant, and extracting clock setup
into a dedicated helper. Clock bypass support is then added for XLCDC
hardware when the computed divider is less than 2.

The final two patches introduce LVDS PLL clock support, configuring the
PLL to 7x the pixel clock rate before the timing engine is programmed,
and add an LVDS output mode handler to map LVDS bus formats to the
appropriate display output modes.

Manikandan Muralidharan (7):
  drm: atmel-hlcdc: Fix off-by-one in vertical back porch setting
  drm: atmel-hlcdc: simplify clock divider selection with
    DIV_ROUND_CLOSEST
  drm: atmel-hlcdc: define ATMEL_HLCDC_CLKDIV_MAX and fix divider
    fallback
  drm: atmel-hlcdc: extract clock setup into a dedicated helper
  drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers
  drm: atmel-hlcdc: add and configure LVDS PLL clock support
  drm: atmel-hlcdc: add LVDS output mode support

Ryan Wanner (1):
  drm: atmel-hlcdc: reorder timing register writes after clock setup

 .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c    | 190 +++++++++++++-----
 include/linux/mfd/atmel-hlcdc.h               |   2 +
 2 files changed, 139 insertions(+), 53 deletions(-)

-- 
2.25.1



^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/8] drm: atmel-hlcdc: Fix off-by-one in vertical back porch setting
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 2/8] drm: atmel-hlcdc: reorder timing register writes after clock setup Manikandan Muralidharan
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="y", Size: 1120 bytes --]

The vertical back porch field in ATMEL_HLCDC_CFG(2) was programmed
without subtracting 1, unlike the other timing parameters which follow
the controller’s (value - 1) encoding requirement.
This results in an off-by-one error in the vertical back porch timing.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 9dbac2def333..7932d666e9ec 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -115,7 +115,7 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
 
 	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
-		     (vm.vfront_porch - 1) | (vm.vback_porch << 16));
+		     (vm.vfront_porch - 1) | ((vm.vback_porch - 1) << 16));
 
 	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
 		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/8] drm: atmel-hlcdc: reorder timing register writes after clock setup
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 1/8] drm: atmel-hlcdc: Fix off-by-one in vertical back porch setting Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 3/8] drm: atmel-hlcdc: simplify clock divider selection with DIV_ROUND_CLOSEST Manikandan Muralidharan
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m, Ryan Wanner

From: Ryan Wanner <Ryan.Wanner@microchip.com>

Write CFG(1-4) timing registers after CFG(0) clock configuration
rather than before, as required by the datasheet procedure.

Signed-off-by: Ryan Wanner <Ryan.Wanner@microchip.com>
Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c    | 43 ++++++++++---------
 1 file changed, 23 insertions(+), 20 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 7932d666e9ec..9673fbce42a7 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -104,26 +104,6 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 	if (ret)
 		return;
 
-	vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
-	vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
-	vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
-	vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
-	vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
-	vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
-
-	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
-		     (vm.hsync_len - 1) | ((vm.vsync_len - 1) << 16));
-
-	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
-		     (vm.vfront_porch - 1) | ((vm.vback_porch - 1) << 16));
-
-	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
-		     (vm.hfront_porch - 1) | ((vm.hback_porch - 1) << 16));
-
-	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
-		     (adj->crtc_hdisplay - 1) |
-		     ((adj->crtc_vdisplay - 1) << 16));
-
 	prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
 	mode_rate = adj->crtc_clock * 1000;
 	if (!crtc->dc->desc->fixed_clksrc) {
@@ -164,6 +144,29 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 
 	regmap_update_bits(regmap, ATMEL_HLCDC_CFG(0), mask, cfg);
 
+	vm.vfront_porch = adj->crtc_vsync_start - adj->crtc_vdisplay;
+	vm.vback_porch = adj->crtc_vtotal - adj->crtc_vsync_end;
+	vm.vsync_len = adj->crtc_vsync_end - adj->crtc_vsync_start;
+	vm.hfront_porch = adj->crtc_hsync_start - adj->crtc_hdisplay;
+	vm.hback_porch = adj->crtc_htotal - adj->crtc_hsync_end;
+	vm.hsync_len = adj->crtc_hsync_end - adj->crtc_hsync_start;
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(1),
+		     (vm.hsync_len - 1) |
+		     ((vm.vsync_len - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(2),
+		     (vm.vfront_porch - 1) |
+		     ((vm.vback_porch - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(3),
+		     (vm.hfront_porch - 1) |
+		     ((vm.hback_porch - 1) << 16));
+
+	regmap_write(regmap, ATMEL_HLCDC_CFG(4),
+		     (adj->crtc_hdisplay - 1) |
+		     ((adj->crtc_vdisplay - 1) << 16));
+
 	state = drm_crtc_state_to_atmel_hlcdc_crtc_state(c->state);
 	cfg = state->output_mode << 8;
 
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/8] drm: atmel-hlcdc: simplify clock divider selection with DIV_ROUND_CLOSEST
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 1/8] drm: atmel-hlcdc: Fix off-by-one in vertical back porch setting Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 2/8] drm: atmel-hlcdc: reorder timing register writes after clock setup Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 4/8] drm: atmel-hlcdc: define ATMEL_HLCDC_CLKDIV_MAX and fix divider fallback Manikandan Muralidharan
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

DIV_ROUND_CLOSEST naturally selects the nearest divider, making the
manual 10x proximity check redundant. Drop it and switch from
DIV_ROUND_UP accordingly.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 16 ++--------------
 1 file changed, 2 insertions(+), 14 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 9673fbce42a7..f30138da1ed8 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -112,28 +112,16 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 		mask |= ATMEL_HLCDC_CLKSEL;
 	}
 
-	div = DIV_ROUND_UP(prate, mode_rate);
+	div = DIV_ROUND_CLOSEST(prate, mode_rate);
 	if (div < 2) {
 		div = 2;
 	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
 		/* The divider ended up too big, try a lower base rate. */
 		cfg &= ~ATMEL_HLCDC_CLKSEL;
 		prate /= 2;
-		div = DIV_ROUND_UP(prate, mode_rate);
+		div = DIV_ROUND_CLOSEST(prate, mode_rate);
 		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
 			div = ATMEL_HLCDC_CLKDIV_MASK;
-	} else {
-		int div_low = prate / mode_rate;
-
-		if (div_low >= 2 &&
-		    (10 * (prate / div_low - mode_rate) <
-		     (mode_rate - prate / div)))
-			/*
-			 * At least 10 times better when using a higher
-			 * frequency than requested, instead of a lower.
-			 * So, go with that.
-			 */
-			div = div_low;
 	}
 
 	cfg |= ATMEL_HLCDC_CLKDIV(div);
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/8] drm: atmel-hlcdc: define ATMEL_HLCDC_CLKDIV_MAX and fix divider fallback
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
                   ` (2 preceding siblings ...)
  2026-05-19  9:01 ` [PATCH 3/8] drm: atmel-hlcdc: simplify clock divider selection with DIV_ROUND_CLOSEST Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 5/8] drm: atmel-hlcdc: extract clock setup into a dedicated helper Manikandan Muralidharan
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

Introduce ATMEL_HLCDC_CLKDIV_MAX to replace the raw mask.In the divider
overflow fallback, restrict the clock source switch and halved rate
retry to SoCs that support the 2x system clock path.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 9 +++++----
 include/linux/mfd/atmel-hlcdc.h                | 1 +
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index f30138da1ed8..c7e77bb2941a 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -117,11 +117,12 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 		div = 2;
 	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
 		/* The divider ended up too big, try a lower base rate. */
-		cfg &= ~ATMEL_HLCDC_CLKSEL;
-		prate /= 2;
-		div = DIV_ROUND_CLOSEST(prate, mode_rate);
+		if (!crtc->dc->desc->fixed_clksrc) {
+			cfg &= ~ATMEL_HLCDC_CLKSEL;
+			div = DIV_ROUND_CLOSEST(prate >> 1, mode_rate);
+		}
 		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
-			div = ATMEL_HLCDC_CLKDIV_MASK;
+			div = ATMEL_HLCDC_CLKDIV_MAX;
 	}
 
 	cfg |= ATMEL_HLCDC_CLKDIV(div);
diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
index 07c2081867fd..8e86219293b7 100644
--- a/include/linux/mfd/atmel-hlcdc.h
+++ b/include/linux/mfd/atmel-hlcdc.h
@@ -50,6 +50,7 @@
 #define ATMEL_HLCDC_CLKDIV_SHFT		16
 #define ATMEL_HLCDC_CLKDIV_MASK		GENMASK(23, 16)
 #define ATMEL_HLCDC_CLKDIV(div)		((div - 2) << ATMEL_HLCDC_CLKDIV_SHFT)
+#define ATMEL_HLCDC_CLKDIV_MAX		((ATMEL_HLCDC_CLKDIV_MASK >> ATMEL_HLCDC_CLKDIV_SHFT) + 2)
 
 #define ATMEL_HLCDC_PIXEL_CLK		BIT(0)
 #define ATMEL_HLCDC_SYNC		BIT(1)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/8] drm: atmel-hlcdc: extract clock setup into a dedicated helper
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
                   ` (3 preceding siblings ...)
  2026-05-19  9:01 ` [PATCH 4/8] drm: atmel-hlcdc: define ATMEL_HLCDC_CLKDIV_MAX and fix divider fallback Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 6/8] drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers Manikandan Muralidharan
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

Factor clock enable, rate calculation, and divider selection out of
atmel_hlcdc_crtc_mode_set_nofb() into a new
atmel_hlcdc_crtc_setup_clock() helper, preparing for the addition
of LVDS and XLCDC clock bypass support.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c    | 66 +++++++++++--------
 1 file changed, 39 insertions(+), 27 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index c7e77bb2941a..6da428361c19 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -66,6 +66,42 @@ drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
 	return container_of(crtc, struct atmel_hlcdc_crtc, base);
 }
 
+static int atmel_hlcdc_crtc_setup_clock(struct atmel_hlcdc_crtc *crtc,
+					unsigned long mode_rate,
+					unsigned int *cfg,
+					unsigned int *mask)
+{
+	unsigned long prate;
+	int div, ret;
+
+	ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
+	if (ret)
+		return ret;
+
+	prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
+	if (!crtc->dc->desc->fixed_clksrc) {
+		prate *= 2;
+		*cfg |= ATMEL_HLCDC_CLKSEL;
+		*mask |= ATMEL_HLCDC_CLKSEL;
+	}
+
+	div = DIV_ROUND_CLOSEST(prate, mode_rate);
+	if (div < 2) {
+		div = 2;
+	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
+		/* The divider ended up too big, try a lower base rate. */
+		if (!crtc->dc->desc->fixed_clksrc) {
+			*cfg &= ~ATMEL_HLCDC_CLKSEL;
+			div = DIV_ROUND_CLOSEST(prate >> 1, mode_rate);
+		}
+		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
+			div = ATMEL_HLCDC_CLKDIV_MAX;
+	}
+
+	*cfg |= ATMEL_HLCDC_CLKDIV(div);
+	return 0;
+}
+
 static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 {
 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
@@ -76,12 +112,10 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 	struct atmel_hlcdc_crtc_state *state;
 	struct drm_device *ddev = c->dev;
 	struct drm_connector_list_iter iter;
-	unsigned long mode_rate;
 	struct videomode vm;
-	unsigned long prate;
 	unsigned int mask = ATMEL_HLCDC_CLKDIV_MASK | ATMEL_HLCDC_CLKPOL;
 	unsigned int cfg = 0;
-	int div, ret;
+	int ret;
 
 	/* get encoder from crtc */
 	drm_for_each_encoder(en_iter, ddev) {
@@ -100,33 +134,11 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 		drm_connector_list_iter_end(&iter);
 	}
 
-	ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
+	ret = atmel_hlcdc_crtc_setup_clock(crtc, adj->crtc_clock * 1000,
+					   &cfg, &mask);
 	if (ret)
 		return;
 
-	prate = clk_get_rate(crtc->dc->hlcdc->sys_clk);
-	mode_rate = adj->crtc_clock * 1000;
-	if (!crtc->dc->desc->fixed_clksrc) {
-		prate *= 2;
-		cfg |= ATMEL_HLCDC_CLKSEL;
-		mask |= ATMEL_HLCDC_CLKSEL;
-	}
-
-	div = DIV_ROUND_CLOSEST(prate, mode_rate);
-	if (div < 2) {
-		div = 2;
-	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
-		/* The divider ended up too big, try a lower base rate. */
-		if (!crtc->dc->desc->fixed_clksrc) {
-			cfg &= ~ATMEL_HLCDC_CLKSEL;
-			div = DIV_ROUND_CLOSEST(prate >> 1, mode_rate);
-		}
-		if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK)
-			div = ATMEL_HLCDC_CLKDIV_MAX;
-	}
-
-	cfg |= ATMEL_HLCDC_CLKDIV(div);
-
 	if (connector &&
 	    connector->display_info.bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_NEGEDGE)
 		cfg |= ATMEL_HLCDC_CLKPOL;
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/8] drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
                   ` (4 preceding siblings ...)
  2026-05-19  9:01 ` [PATCH 5/8] drm: atmel-hlcdc: extract clock setup into a dedicated helper Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-27 14:55   ` Lee Jones
  2026-05-19  9:01 ` [PATCH 7/8] drm: atmel-hlcdc: add and configure LVDS PLL clock support Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 8/8] drm: atmel-hlcdc: add LVDS output mode support Manikandan Muralidharan
  7 siblings, 1 reply; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

Define ATMEL_XLCDC_CLKBYP and set the clock bypass bit on XLCDC
hardware when the computed divider is less than 2, avoiding an
invalid divider value. Non-XLCDC hardware retains the existing
minimum divider clamp of 2.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 7 +++++++
 include/linux/mfd/atmel-hlcdc.h                | 1 +
 2 files changed, 8 insertions(+)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 6da428361c19..f837684654ea 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -87,6 +87,13 @@ static int atmel_hlcdc_crtc_setup_clock(struct atmel_hlcdc_crtc *crtc,
 
 	div = DIV_ROUND_CLOSEST(prate, mode_rate);
 	if (div < 2) {
+		/* XLCDC: bypass divider when div < 2 */
+		if (crtc->dc->desc->is_xlcdc) {
+			*cfg  |= ATMEL_XLCDC_CLKBYP;
+			*mask |= ATMEL_XLCDC_CLKBYP;
+			return 0;
+		}
+		/* Enforce minimum divider for non-XLCDC */
 		div = 2;
 	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
 		/* The divider ended up too big, try a lower base rate. */
diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
index 8e86219293b7..1463c7db0e64 100644
--- a/include/linux/mfd/atmel-hlcdc.h
+++ b/include/linux/mfd/atmel-hlcdc.h
@@ -44,6 +44,7 @@
 #define ATMEL_XLCDC_HEO_UPDATE		BIT(3)
 
 #define ATMEL_HLCDC_CLKPOL		BIT(0)
+#define ATMEL_XLCDC_CLKBYP		BIT(1)
 #define ATMEL_HLCDC_CLKSEL		BIT(2)
 #define ATMEL_HLCDC_CLKPWMSEL		BIT(3)
 #define ATMEL_HLCDC_CGDIS(i)		BIT(8 + (i))
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 7/8] drm: atmel-hlcdc: add and configure LVDS PLL clock support
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
                   ` (5 preceding siblings ...)
  2026-05-19  9:01 ` [PATCH 6/8] drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  2026-05-19  9:01 ` [PATCH 8/8] drm: atmel-hlcdc: add LVDS output mode support Manikandan Muralidharan
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

Set the LVDS PLL rate to 7x the pixel clock.
set ATMEL_XLCDC_CLKBYP for the LVDS path, leaving clock
lifecycle management to the atomic callbacks and fallback to
sys_clk for non-LVDS displays with proper error handling.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c    | 45 +++++++++++++++++--
 1 file changed, 42 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index f837684654ea..73a8650bc9b7 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -26,6 +26,8 @@
 
 #include "atmel_hlcdc_dc.h"
 
+#define ATMEL_LVDS_PLL_MULT	7
+
 /**
  * struct atmel_hlcdc_crtc_state - Atmel HLCDC CRTC state structure
  *
@@ -66,6 +68,14 @@ drm_crtc_to_atmel_hlcdc_crtc(struct drm_crtc *crtc)
 	return container_of(crtc, struct atmel_hlcdc_crtc, base);
 }
 
+static void atmel_hlcdc_crtc_disable_clock(struct atmel_hlcdc_crtc *crtc)
+{
+	if (crtc->dc->hlcdc->lvds_pll_clk)
+		clk_disable_unprepare(crtc->dc->hlcdc->lvds_pll_clk);
+	else
+		clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
+}
+
 static int atmel_hlcdc_crtc_setup_clock(struct atmel_hlcdc_crtc *crtc,
 					unsigned long mode_rate,
 					unsigned int *cfg,
@@ -74,6 +84,12 @@ static int atmel_hlcdc_crtc_setup_clock(struct atmel_hlcdc_crtc *crtc,
 	unsigned long prate;
 	int div, ret;
 
+	if (crtc->dc->hlcdc->lvds_pll_clk) {
+		*cfg |= ATMEL_XLCDC_CLKBYP;
+		*mask |= ATMEL_XLCDC_CLKBYP;
+		return clk_prepare_enable(crtc->dc->hlcdc->lvds_pll_clk);
+	}
+
 	ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
 	if (ret)
 		return ret;
@@ -198,7 +214,7 @@ static void atmel_hlcdc_crtc_mode_set_nofb(struct drm_crtc *c)
 			   ATMEL_XLCDC_DPI : ATMEL_HLCDC_MODE_MASK),
 			   cfg);
 
-	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
+	atmel_hlcdc_crtc_disable_clock(crtc);
 }
 
 static enum drm_mode_status
@@ -254,7 +270,8 @@ static void atmel_hlcdc_crtc_atomic_disable(struct drm_crtc *c,
 				    10, 1000))
 		drm_warn(dev, "Atmel LCDC status register CLKSTS timeout\n");
 
-	clk_disable_unprepare(crtc->dc->hlcdc->sys_clk);
+	atmel_hlcdc_crtc_disable_clock(crtc);
+
 	pinctrl_pm_select_sleep_state(dev->dev);
 
 	pm_runtime_allow(dev->dev);
@@ -267,15 +284,33 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
 {
 	struct drm_device *dev = c->dev;
 	struct atmel_hlcdc_crtc *crtc = drm_crtc_to_atmel_hlcdc_crtc(c);
+	struct drm_display_mode *adj = &c->state->adjusted_mode;
 	struct regmap *regmap = crtc->dc->hlcdc->regmap;
 	unsigned int status;
+	int ret;
 
 	pm_runtime_get_sync(dev->dev);
 
 	pm_runtime_forbid(dev->dev);
 
 	pinctrl_pm_select_default_state(dev->dev);
-	clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
+
+	/*
+	 * Set LVDS PLL clock rate (7x pixel clock) if available
+	 */
+	if (crtc->dc->hlcdc->lvds_pll_clk) {
+		ret = clk_set_rate(crtc->dc->hlcdc->lvds_pll_clk,
+				   adj->clock * 1000 * ATMEL_LVDS_PLL_MULT);
+		if (ret) {
+			drm_err(dev, "Failed to set LVDS PLL clk rate: %d\n", ret);
+			goto err_clk;
+		}
+		ret = clk_prepare_enable(crtc->dc->hlcdc->lvds_pll_clk);
+	} else {
+		ret = clk_prepare_enable(crtc->dc->hlcdc->sys_clk);
+	}
+	if (ret)
+		goto err_clk;
 
 	regmap_write(regmap, ATMEL_HLCDC_EN, ATMEL_HLCDC_PIXEL_CLK);
 	if (regmap_read_poll_timeout(regmap, ATMEL_HLCDC_SR, status,
@@ -310,7 +345,11 @@ static void atmel_hlcdc_crtc_atomic_enable(struct drm_crtc *c,
 	}
 
 	pm_runtime_put_sync(dev->dev);
+	return;
 
+err_clk:
+	pm_runtime_allow(dev->dev);
+	pm_runtime_put_sync(dev->dev);
 }
 
 #define ATMEL_HLCDC_RGB444_OUTPUT		BIT(0)
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 8/8] drm: atmel-hlcdc: add LVDS output mode support
  2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
                   ` (6 preceding siblings ...)
  2026-05-19  9:01 ` [PATCH 7/8] drm: atmel-hlcdc: add and configure LVDS PLL clock support Manikandan Muralidharan
@ 2026-05-19  9:01 ` Manikandan Muralidharan
  7 siblings, 0 replies; 10+ messages in thread
From: Manikandan Muralidharan @ 2026-05-19  9:01 UTC (permalink / raw)
  To: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, lee, dri-devel,
	linux-arm-kernel, linux-kernel
  Cc: manikandan.m

Introduce atmel_xlcdc_connector_output_lvds() to map LVDS bus
formats to the appropriate output mode, and wire it into
atmel_hlcdc_connector_output_mode() for LVDS encoder types.

Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
---
 .../gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c    | 44 +++++++++++++++++--
 1 file changed, 41 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
index 73a8650bc9b7..9f46c368835e 100644
--- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
+++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
@@ -407,6 +407,42 @@ static int atmel_xlcdc_connector_output_dsi(struct drm_encoder *encoder,
 	return supported_fmts;
 }
 
+static int atmel_xlcdc_connector_output_lvds(struct drm_encoder *encoder,
+					     struct drm_display_info *info)
+{
+	int j;
+	unsigned int supported_fmts = 0;
+
+	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
+	case 0:
+		break;
+	case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+	case MEDIA_BUS_FMT_RGB666_1X18:
+		return ATMEL_HLCDC_RGB666_OUTPUT;
+	case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+	case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+		return ATMEL_HLCDC_RGB888_OUTPUT;
+	default:
+		return -EINVAL;
+	}
+
+	for (j = 0; j < info->num_bus_formats; j++) {
+		switch (info->bus_formats[j]) {
+		case MEDIA_BUS_FMT_RGB666_1X7X3_SPWG:
+		case MEDIA_BUS_FMT_RGB666_1X18:
+			supported_fmts |= ATMEL_HLCDC_RGB666_OUTPUT;
+			break;
+		case MEDIA_BUS_FMT_RGB888_1X7X4_SPWG:
+		case MEDIA_BUS_FMT_RGB888_1X7X4_JEIDA:
+			supported_fmts |= ATMEL_HLCDC_RGB888_OUTPUT;
+			break;
+		default:
+			break;
+		}
+	}
+	return supported_fmts;
+}
+
 static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state)
 {
 	struct drm_connector *connector = state->connector;
@@ -419,12 +455,14 @@ static int atmel_hlcdc_connector_output_mode(struct drm_connector_state *state)
 	if (!encoder)
 		encoder = connector->encoder;
 	/*
-	 * atmel-hlcdc to support DSI formats with DSI video pipeline
-	 * when DRM_MODE_ENCODER_DSI type is set by
-	 * connector driver component.
+	 * Select the output format based on the encoder type.
+	 * DSI and LVDS encoders have dedicated format handlers,
+	 * while other encoder types fall through to the generic path
 	 */
 	if (encoder->encoder_type == DRM_MODE_ENCODER_DSI)
 		return atmel_xlcdc_connector_output_dsi(encoder, info);
+	if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS)
+		return atmel_xlcdc_connector_output_lvds(encoder, info);
 
 	switch (atmel_hlcdc_encoder_get_bus_fmt(encoder)) {
 	case 0:
-- 
2.25.1



^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH 6/8] drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers
  2026-05-19  9:01 ` [PATCH 6/8] drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers Manikandan Muralidharan
@ 2026-05-27 14:55   ` Lee Jones
  0 siblings, 0 replies; 10+ messages in thread
From: Lee Jones @ 2026-05-27 14:55 UTC (permalink / raw)
  To: Manikandan Muralidharan
  Cc: maarten.lankhorst, mripard, tzimmermann, airlied, simona,
	nicolas.ferre, alexandre.belloni, claudiu.beznea, dri-devel,
	linux-arm-kernel, linux-kernel

On Tue, 19 May 2026, Manikandan Muralidharan wrote:

> Define ATMEL_XLCDC_CLKBYP and set the clock bypass bit on XLCDC
> hardware when the computed divider is less than 2, avoiding an
> invalid divider value. Non-XLCDC hardware retains the existing
> minimum divider clamp of 2.
> 
> Signed-off-by: Manikandan Muralidharan <manikandan.m@microchip.com>
> ---
>  drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c | 7 +++++++
>  include/linux/mfd/atmel-hlcdc.h                | 1 +

Acked-by: Lee Jones <lee@kernel.org>

>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> index 6da428361c19..f837684654ea 100644
> --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_crtc.c
> @@ -87,6 +87,13 @@ static int atmel_hlcdc_crtc_setup_clock(struct atmel_hlcdc_crtc *crtc,
>  
>  	div = DIV_ROUND_CLOSEST(prate, mode_rate);
>  	if (div < 2) {
> +		/* XLCDC: bypass divider when div < 2 */
> +		if (crtc->dc->desc->is_xlcdc) {
> +			*cfg  |= ATMEL_XLCDC_CLKBYP;
> +			*mask |= ATMEL_XLCDC_CLKBYP;
> +			return 0;
> +		}
> +		/* Enforce minimum divider for non-XLCDC */
>  		div = 2;
>  	} else if (ATMEL_HLCDC_CLKDIV(div) & ~ATMEL_HLCDC_CLKDIV_MASK) {
>  		/* The divider ended up too big, try a lower base rate. */
> diff --git a/include/linux/mfd/atmel-hlcdc.h b/include/linux/mfd/atmel-hlcdc.h
> index 8e86219293b7..1463c7db0e64 100644
> --- a/include/linux/mfd/atmel-hlcdc.h
> +++ b/include/linux/mfd/atmel-hlcdc.h
> @@ -44,6 +44,7 @@
>  #define ATMEL_XLCDC_HEO_UPDATE		BIT(3)
>  
>  #define ATMEL_HLCDC_CLKPOL		BIT(0)
> +#define ATMEL_XLCDC_CLKBYP		BIT(1)
>  #define ATMEL_HLCDC_CLKSEL		BIT(2)
>  #define ATMEL_HLCDC_CLKPWMSEL		BIT(3)
>  #define ATMEL_HLCDC_CGDIS(i)		BIT(8 + (i))
> -- 
> 2.25.1
> 

-- 
Lee Jones


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2026-05-27 14:55 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-19  9:01 [PATCH 0/8] drm: atmel-hlcdc: fix clock handling and add LVDS support Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 1/8] drm: atmel-hlcdc: Fix off-by-one in vertical back porch setting Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 2/8] drm: atmel-hlcdc: reorder timing register writes after clock setup Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 3/8] drm: atmel-hlcdc: simplify clock divider selection with DIV_ROUND_CLOSEST Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 4/8] drm: atmel-hlcdc: define ATMEL_HLCDC_CLKDIV_MAX and fix divider fallback Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 5/8] drm: atmel-hlcdc: extract clock setup into a dedicated helper Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 6/8] drm: atmel-hlcdc: add XLCDC clock bypass support for small dividers Manikandan Muralidharan
2026-05-27 14:55   ` Lee Jones
2026-05-19  9:01 ` [PATCH 7/8] drm: atmel-hlcdc: add and configure LVDS PLL clock support Manikandan Muralidharan
2026-05-19  9:01 ` [PATCH 8/8] drm: atmel-hlcdc: add LVDS output mode support Manikandan Muralidharan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox