From 4c6d8fc20b09f9684743afd72e4dbc3f15524479 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Mon, 14 Jan 2019 17:44:56 +0100 Subject: [PATCH 1/7] drm: rcar-du: add missing of_node_put Add an of_node_put when the result of of_graph_get_remote_port_parent is not available. Add a second of_node_put if no encoder is selected (encoder remains NULL). The semantic match that finds the first problem is as follows (http://coccinelle.lip6.fr): // @r exists@ local idexpression e; expression x; @@ e = of_graph_get_remote_port_parent(...); ... when != x = e when != true e == NULL when != of_node_put(e) when != of_fwnode_handle(e) ( return e; | *return ...; ) // Signed-off-by: Julia Lawall Reviewed-by: Laurent Pinchart Reviewed-by: Kieran Bingham Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 663ab98d1f97..99795c677dd5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -352,6 +352,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, dev_dbg(rcdu->dev, "connected entity %pOF is disabled, skipping\n", entity); + of_node_put(entity); return -ENODEV; } @@ -387,6 +388,7 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, dev_warn(rcdu->dev, "no encoder found for endpoint %pOF, skipping\n", ep->local_node); + of_node_put(entity); return -ENODEV; } From 8a2fe6c09f2ac17c7605982bc6810ea2b2c2a0d0 Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 22 Jan 2019 15:25:46 +0000 Subject: [PATCH 2/7] dt-bindings: display: renesas: lvds: Document r8a7744 bindings Document the RZ/G1N (R8A7744) LVDS bindings. Signed-off-by: Biju Das Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman Reviewed-by: Rob Herring --- .../devicetree/bindings/display/bridge/renesas,lvds.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt index 27a054e1bb5f..900a884ad9f5 100644 --- a/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt +++ b/Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt @@ -8,6 +8,7 @@ Required properties: - compatible : Shall contain one of - "renesas,r8a7743-lvds" for R8A7743 (RZ/G1M) compatible LVDS encoders + - "renesas,r8a7744-lvds" for R8A7744 (RZ/G1N) compatible LVDS encoders - "renesas,r8a774c0-lvds" for R8A774C0 (RZ/G2E) compatible LVDS encoders - "renesas,r8a7790-lvds" for R8A7790 (R-Car H2) compatible LVDS encoders - "renesas,r8a7791-lvds" for R8A7791 (R-Car M2-W) compatible LVDS encoders From fc59d7d491a9758be2915caf400d342881a8b86e Mon Sep 17 00:00:00 2001 From: Biju Das Date: Tue, 22 Jan 2019 15:25:47 +0000 Subject: [PATCH 3/7] drm: rcar-du: lvds: Add r8a7744 support The LVDS encoders on RZ/G1N SoC is similar to RZ/G1M. Add support for RZ/G1N (R8A7744) SoC to the LVDS encoder driver. Signed-off-by: Biju Das Reviewed-by: Laurent Pinchart Signed-off-by: Laurent Pinchart Reviewed-by: Simon Horman --- drivers/gpu/drm/rcar-du/rcar_lvds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 044c65680ef3..997927e8b0b6 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -786,6 +786,7 @@ static const struct rcar_lvds_device_info rcar_lvds_r8a77995_info = { static const struct of_device_id rcar_lvds_of_table[] = { { .compatible = "renesas,r8a7743-lvds", .data = &rcar_lvds_gen2_info }, + { .compatible = "renesas,r8a7744-lvds", .data = &rcar_lvds_gen2_info }, { .compatible = "renesas,r8a774c0-lvds", .data = &rcar_lvds_r8a77990_info }, { .compatible = "renesas,r8a7790-lvds", .data = &rcar_lvds_r8a7790_info }, { .compatible = "renesas,r8a7791-lvds", .data = &rcar_lvds_gen2_info }, From 5aebc852af25d8e1bacee664a1e3abae13dab799 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 17 Jan 2019 00:40:49 +0200 Subject: [PATCH 4/7] drm: rcar-du: Simplify encoder registration Before the driver fully moved to drm_bridge and drm_panel, it was necessary to parse DT and locate encoder and connector nodes. The connector node is now unused and can be removed as a parameter to rcar_du_encoder_init(). As a consequence rcar_du_encoders_init_one() can be greatly simplified, removing most of the DT parsing. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 3 +- drivers/gpu/drm/rcar-du/rcar_du_encoder.h | 3 +- drivers/gpu/drm/rcar-du/rcar_du_kms.c | 52 ++--------------------- 3 files changed, 6 insertions(+), 52 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index 369e930750e9..ed7ccee705cb 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -30,8 +30,7 @@ static const struct drm_encoder_funcs encoder_funcs = { int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_output output, - struct device_node *enc_node, - struct device_node *con_node) + struct device_node *enc_node) { struct rcar_du_encoder *renc; struct drm_encoder *encoder; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h index 552f2a02e5b5..df9be4524301 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.h @@ -26,7 +26,6 @@ struct rcar_du_encoder { int rcar_du_encoder_init(struct rcar_du_device *rcdu, enum rcar_du_output output, - struct device_node *enc_node, - struct device_node *con_node); + struct device_node *enc_node); #endif /* __RCAR_DU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/rcar-du/rcar_du_kms.c b/drivers/gpu/drm/rcar-du/rcar_du_kms.c index 99795c677dd5..b0c80dffd8b8 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_kms.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_kms.c @@ -330,17 +330,10 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, enum rcar_du_output output, struct of_endpoint *ep) { - struct device_node *connector = NULL; - struct device_node *encoder = NULL; - struct device_node *ep_node = NULL; - struct device_node *entity_ep_node; struct device_node *entity; int ret; - /* - * Locate the connected entity and infer its type from the number of - * endpoints. - */ + /* Locate the connected entity and initialize the encoder. */ entity = of_graph_get_remote_port_parent(ep->local_node); if (!entity) { dev_dbg(rcdu->dev, "unconnected endpoint %pOF, skipping\n", @@ -356,50 +349,13 @@ static int rcar_du_encoders_init_one(struct rcar_du_device *rcdu, return -ENODEV; } - entity_ep_node = of_graph_get_remote_endpoint(ep->local_node); - - for_each_endpoint_of_node(entity, ep_node) { - if (ep_node == entity_ep_node) - continue; - - /* - * We've found one endpoint other than the input, this must - * be an encoder. Locate the connector. - */ - encoder = entity; - connector = of_graph_get_remote_port_parent(ep_node); - of_node_put(ep_node); - - if (!connector) { - dev_warn(rcdu->dev, - "no connector for encoder %pOF, skipping\n", - encoder); - of_node_put(entity_ep_node); - of_node_put(encoder); - return -ENODEV; - } - - break; - } - - of_node_put(entity_ep_node); - - if (!encoder) { - dev_warn(rcdu->dev, - "no encoder found for endpoint %pOF, skipping\n", - ep->local_node); - of_node_put(entity); - return -ENODEV; - } - - ret = rcar_du_encoder_init(rcdu, output, encoder, connector); + ret = rcar_du_encoder_init(rcdu, output, entity); if (ret && ret != -EPROBE_DEFER) dev_warn(rcdu->dev, "failed to initialize encoder %pOF on output %u (%d), skipping\n", - encoder, output, ret); + entity, output, ret); - of_node_put(encoder); - of_node_put(connector); + of_node_put(entity); return ret; } From 6e1f8557818f3c2476404ba1d4a5157b823b79f0 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 17 Jan 2019 00:43:08 +0200 Subject: [PATCH 5/7] drm: rcar-du: lvds: Don't fail probe if output is not connected on D3/E3 On the D3 and E3 SoCs the LVDS encoder has an extended internal PLL and supplies a clock to the DU. That clock is used not only for the LVDS outputs but also for the DPAD output. The LVDS encoder thus needs to be available to the DU even when its output is disabled. Don't fail probe in that case on D3 and E3. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_lvds.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index 997927e8b0b6..a7789c5b3880 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -545,7 +545,10 @@ static int rcar_lvds_attach(struct drm_bridge *bridge) return drm_bridge_attach(bridge->encoder, lvds->next_bridge, bridge); - /* Otherwise we have a panel, create a connector. */ + /* Otherwise if we have a panel, create a connector. */ + if (!lvds->panel) + return 0; + ret = drm_connector_init(bridge->dev, connector, &rcar_lvds_conn_funcs, DRM_MODE_CONNECTOR_LVDS); if (ret < 0) @@ -593,7 +596,8 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) local_output = of_graph_get_endpoint_by_regs(lvds->dev->of_node, 1, 0); if (!local_output) { dev_dbg(lvds->dev, "unconnected port@1\n"); - return -ENODEV; + ret = -ENODEV; + goto done; } /* @@ -643,6 +647,15 @@ static int rcar_lvds_parse_dt(struct rcar_lvds *lvds) of_node_put(remote_input); of_node_put(remote); + /* + * On D3/E3 the LVDS encoder provides a clock to the DU, which can be + * used for the DPAD output even when the LVDS output is not connected. + * Don't fail probe in that case as the DU will need the bridge to + * control the clock. + */ + if (lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL) + return ret == -ENODEV ? 0 : ret; + return ret; } From 02f2b30032c12b1b91abe5f2bd0d74ba1f700ea1 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 17 Jan 2019 01:11:37 +0200 Subject: [PATCH 6/7] drm: rcar-du: lvds: Add API to enable/disable clock output On the D3 and E3 platforms, the LVDS internal PLL supplies the pixel clock to the DU. This works automatically for LVDS outputs as the LVDS encoder is enabled through the bridge API, enabling the internal PLL and clock output. However, when using the DU DPAD output with the LVDS outputs turned off, the LVDS PLL needs to be controlled manually. Add an API to do so, to be called by the DU driver. The drivers/gpu/drm/rcar-du/ directory has to be treated as obj-y unconditionally, as the LVDS driver could be built-in while the DU driver is compiled as a module. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/Makefile | 2 +- drivers/gpu/drm/rcar-du/Kconfig | 1 + drivers/gpu/drm/rcar-du/rcar_lvds.c | 77 +++++++++++++++++++++++++---- drivers/gpu/drm/rcar-du/rcar_lvds.h | 27 ++++++++++ 4 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 drivers/gpu/drm/rcar-du/rcar_lvds.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index f0c1f8731a27..1ac55c65eac0 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -81,7 +81,7 @@ obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_ARMADA) += armada/ obj-$(CONFIG_DRM_ATMEL_HLCDC) += atmel-hlcdc/ -obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ +obj-y += rcar-du/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-y += omapdrm/ obj-$(CONFIG_DRM_SUN4I) += sun4i/ diff --git a/drivers/gpu/drm/rcar-du/Kconfig b/drivers/gpu/drm/rcar-du/Kconfig index 225141656e19..7c36e2777a15 100644 --- a/drivers/gpu/drm/rcar-du/Kconfig +++ b/drivers/gpu/drm/rcar-du/Kconfig @@ -4,6 +4,7 @@ config DRM_RCAR_DU depends on DRM && OF depends on ARM || ARM64 depends on ARCH_RENESAS || COMPILE_TEST + imply DRM_RCAR_LVDS select DRM_KMS_HELPER select DRM_KMS_CMA_HELPER select DRM_GEM_CMA_HELPER diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.c b/drivers/gpu/drm/rcar-du/rcar_lvds.c index a7789c5b3880..7ef97b2a6eda 100644 --- a/drivers/gpu/drm/rcar-du/rcar_lvds.c +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.c @@ -23,6 +23,7 @@ #include #include +#include "rcar_lvds.h" #include "rcar_lvds_regs.h" struct rcar_lvds; @@ -183,8 +184,9 @@ struct pll_info { static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, unsigned long target, struct pll_info *pll, - u32 clksel) + u32 clksel, bool dot_clock_only) { + unsigned int div7 = dot_clock_only ? 1 : 7; unsigned long output; unsigned long fin; unsigned int m_min; @@ -218,9 +220,9 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, * `------------> | | * |/ * - * The /7 divider is optional when the LVDS PLL is used to generate a - * dot clock for the DU RGB output, without using the LVDS encoder. We - * don't support this configuration yet. + * The /7 divider is optional, it is enabled when the LVDS PLL is used + * to drive the LVDS encoder, and disabled when used to generate a dot + * clock for the DU RGB output, without using the LVDS encoder. * * The PLL allowed input frequency range is 12 MHz to 192 MHz. */ @@ -280,7 +282,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, * the PLL, followed by a an optional fixed /7 * divider. */ - fout = fvco / (1 << e) / 7; + fout = fvco / (1 << e) / div7; div = DIV_ROUND_CLOSEST(fout, target); diff = abs(fout / div - target); @@ -301,7 +303,7 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, done: output = fin * pll->pll_n / pll->pll_m / (1 << pll->pll_e) - / 7 / pll->div; + / div7 / pll->div; error = (long)(output - target) * 10000 / (long)target; dev_dbg(lvds->dev, @@ -311,17 +313,18 @@ static void rcar_lvds_d3_e3_pll_calc(struct rcar_lvds *lvds, struct clk *clk, pll->pll_m, pll->pll_n, pll->pll_e, pll->div); } -static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) +static void __rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, + unsigned int freq, bool dot_clock_only) { struct pll_info pll = { .diff = (unsigned long)-1 }; u32 lvdpllcr; rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[0], freq, &pll, - LVDPLLCR_CKSEL_DU_DOTCLKIN(0)); + LVDPLLCR_CKSEL_DU_DOTCLKIN(0), dot_clock_only); rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.dotclkin[1], freq, &pll, - LVDPLLCR_CKSEL_DU_DOTCLKIN(1)); + LVDPLLCR_CKSEL_DU_DOTCLKIN(1), dot_clock_only); rcar_lvds_d3_e3_pll_calc(lvds, lvds->clocks.extal, freq, &pll, - LVDPLLCR_CKSEL_EXTAL); + LVDPLLCR_CKSEL_EXTAL, dot_clock_only); lvdpllcr = LVDPLLCR_PLLON | pll.clksel | LVDPLLCR_CLKOUT | LVDPLLCR_PLLN(pll.pll_n - 1) | LVDPLLCR_PLLM(pll.pll_m - 1); @@ -330,6 +333,9 @@ static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) lvdpllcr |= LVDPLLCR_STP_CLKOUTE | LVDPLLCR_OUTCLKSEL | LVDPLLCR_PLLE(pll.pll_e - 1); + if (dot_clock_only) + lvdpllcr |= LVDPLLCR_OCKSEL; + rcar_lvds_write(lvds, LVDPLLCR, lvdpllcr); if (pll.div > 1) @@ -343,6 +349,57 @@ static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) rcar_lvds_write(lvds, LVDDIV, 0); } +static void rcar_lvds_pll_setup_d3_e3(struct rcar_lvds *lvds, unsigned int freq) +{ + __rcar_lvds_pll_setup_d3_e3(lvds, freq, false); +} + +/* ----------------------------------------------------------------------------- + * Clock - D3/E3 only + */ + +int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + int ret; + + if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) + return -ENODEV; + + dev_dbg(lvds->dev, "enabling LVDS PLL, freq=%luHz\n", freq); + + WARN_ON(lvds->enabled); + + ret = clk_prepare_enable(lvds->clocks.mod); + if (ret < 0) + return ret; + + __rcar_lvds_pll_setup_d3_e3(lvds, freq, true); + + lvds->enabled = true; + return 0; +} +EXPORT_SYMBOL_GPL(rcar_lvds_clk_enable); + +void rcar_lvds_clk_disable(struct drm_bridge *bridge) +{ + struct rcar_lvds *lvds = bridge_to_rcar_lvds(bridge); + + if (WARN_ON(!(lvds->info->quirks & RCAR_LVDS_QUIRK_EXT_PLL))) + return; + + dev_dbg(lvds->dev, "disabling LVDS PLL\n"); + + WARN_ON(!lvds->enabled); + + rcar_lvds_write(lvds, LVDPLLCR, 0); + + clk_disable_unprepare(lvds->clocks.mod); + + lvds->enabled = false; +} +EXPORT_SYMBOL_GPL(rcar_lvds_clk_disable); + /* ----------------------------------------------------------------------------- * Bridge */ diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.h b/drivers/gpu/drm/rcar-du/rcar_lvds.h new file mode 100644 index 000000000000..a709cae1bc32 --- /dev/null +++ b/drivers/gpu/drm/rcar-du/rcar_lvds.h @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * rcar_lvds.h -- R-Car LVDS Encoder + * + * Copyright (C) 2013-2018 Renesas Electronics Corporation + * + * Contact: Laurent Pinchart (laurent.pinchart@ideasonboard.com) + */ + +#ifndef __RCAR_LVDS_H__ +#define __RCAR_LVDS_H__ + +struct drm_bridge; + +#if IS_ENABLED(CONFIG_DRM_RCAR_LVDS) +int rcar_lvds_clk_enable(struct drm_bridge *bridge, unsigned long freq); +void rcar_lvds_clk_disable(struct drm_bridge *bridge); +#else +static inline int rcar_lvds_clk_enable(struct drm_bridge *bridge, + unsigned long freq) +{ + return -ENOSYS; +} +static inline void rcar_lvds_clk_disable(struct drm_bridge *bridge) { } +#endif /* CONFIG_DRM_RCAR_LVDS */ + +#endif /* __RCAR_LVDS_H__ */ From a6cc417d3eee4ac37aa9547cf82d4ff155d1780c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Thu, 17 Jan 2019 01:59:34 +0200 Subject: [PATCH 7/7] drm: rcar-du: Turn LVDS clock output on/off for DPAD0 output on D3/E3 On the D3 and E3 SoCs the LVDS PLL clock output provides the dot clock to the DU channels, even when the LVDS outputs are not in use. Enable and disable the LVDS clock output when enabling or disabling a CRTC connected to the DPAD0 output. Signed-off-by: Laurent Pinchart --- drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 34 +++++++++++++++++++++++ drivers/gpu/drm/rcar-du/rcar_du_drv.h | 3 ++ drivers/gpu/drm/rcar-du/rcar_du_encoder.c | 1 + 3 files changed, 38 insertions(+) diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c index 93ee0020c9b1..96175d48a902 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c @@ -25,6 +25,7 @@ #include "rcar_du_plane.h" #include "rcar_du_regs.h" #include "rcar_du_vsp.h" +#include "rcar_lvds.h" static u32 rcar_du_crtc_read(struct rcar_du_crtc *rcrtc, u32 reg) { @@ -656,8 +657,27 @@ static void rcar_du_crtc_atomic_enable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(crtc->state); + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_crtc_get(rcrtc); + + /* + * On D3/E3 the dot clock is provided by the LVDS encoder attached to + * the DU channel. We need to enable its clock output explicitly if + * the LVDS output is disabled. + */ + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && + rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { + struct rcar_du_encoder *encoder = + rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index]; + const struct drm_display_mode *mode = + &crtc->state->adjusted_mode; + + rcar_lvds_clk_enable(encoder->base.bridge, + mode->clock * 1000); + } + rcar_du_crtc_start(rcrtc); } @@ -665,10 +685,24 @@ static void rcar_du_crtc_atomic_disable(struct drm_crtc *crtc, struct drm_crtc_state *old_state) { struct rcar_du_crtc *rcrtc = to_rcar_crtc(crtc); + struct rcar_du_crtc_state *rstate = to_rcar_crtc_state(old_state); + struct rcar_du_device *rcdu = rcrtc->group->dev; rcar_du_crtc_stop(rcrtc); rcar_du_crtc_put(rcrtc); + if (rcdu->info->lvds_clk_mask & BIT(rcrtc->index) && + rstate->outputs == BIT(RCAR_DU_OUTPUT_DPAD0)) { + struct rcar_du_encoder *encoder = + rcdu->encoders[RCAR_DU_OUTPUT_LVDS0 + rcrtc->index]; + + /* + * Disable the LVDS clock output, see + * rcar_du_crtc_atomic_enable(). + */ + rcar_lvds_clk_disable(encoder->base.bridge); + } + spin_lock_irq(&crtc->dev->event_lock); if (crtc->state->event) { drm_crtc_send_vblank_event(crtc, crtc->state->event); diff --git a/drivers/gpu/drm/rcar-du/rcar_du_drv.h b/drivers/gpu/drm/rcar-du/rcar_du_drv.h index 6c187d0bf7c2..1327cd0df90a 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_drv.h +++ b/drivers/gpu/drm/rcar-du/rcar_du_drv.h @@ -22,6 +22,7 @@ struct device; struct drm_device; struct drm_property; struct rcar_du_device; +struct rcar_du_encoder; #define RCAR_DU_FEATURE_CRTC_IRQ_CLOCK BIT(0) /* Per-CRTC IRQ and clock */ #define RCAR_DU_FEATURE_VSP1_SOURCE BIT(1) /* Has inputs from VSP1 */ @@ -81,6 +82,8 @@ struct rcar_du_device { struct rcar_du_crtc crtcs[RCAR_DU_MAX_CRTCS]; unsigned int num_crtcs; + struct rcar_du_encoder *encoders[RCAR_DU_OUTPUT_MAX]; + struct rcar_du_group groups[RCAR_DU_MAX_GROUPS]; struct rcar_du_vsp vsps[RCAR_DU_MAX_VSPS]; diff --git a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c index ed7ccee705cb..8ee4e762f4e5 100644 --- a/drivers/gpu/drm/rcar-du/rcar_du_encoder.c +++ b/drivers/gpu/drm/rcar-du/rcar_du_encoder.c @@ -41,6 +41,7 @@ int rcar_du_encoder_init(struct rcar_du_device *rcdu, if (renc == NULL) return -ENOMEM; + rcdu->encoders[output] = renc; renc->output = output; encoder = rcar_encoder_to_drm_encoder(renc);