* [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* 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
* [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