Merge tag 'du-next-20190208' of git://linuxtv.org/pinchartl/media into drm-next
authorDave Airlie <airlied@redhat.com>
Mon, 11 Feb 2019 03:17:00 +0000 (13:17 +1000)
committerDave Airlie <airlied@redhat.com>
Mon, 11 Feb 2019 03:17:05 +0000 (13:17 +1000)
Renesas display drivers changes for v5.1 (2nd part):

- R8A7744 LVDS support
- DPAD0 output support on D3/E3

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190208003355.GG10386@pendragon.ideasonboard.com
Documentation/devicetree/bindings/display/bridge/renesas,lvds.txt
drivers/gpu/drm/Makefile
drivers/gpu/drm/rcar-du/Kconfig
drivers/gpu/drm/rcar-du/rcar_du_crtc.c
drivers/gpu/drm/rcar-du/rcar_du_drv.h
drivers/gpu/drm/rcar-du/rcar_du_encoder.c
drivers/gpu/drm/rcar-du/rcar_du_encoder.h
drivers/gpu/drm/rcar-du/rcar_du_kms.c
drivers/gpu/drm/rcar-du/rcar_lvds.c
drivers/gpu/drm/rcar-du/rcar_lvds.h [new file with mode: 0644]

index 27a054e1bb5fb82ccd4bbc2d3120ac56a9b98e49..900a884ad9f5d2e27742af3472b7147cad4b0690 100644 (file)
@@ -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
index f0c1f8731a2761ed4d1408ced0a8f05c1cc7993d..1ac55c65eac0dc42700aac97c827391527ba4580 100644 (file)
@@ -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/
index 225141656e1903758e096561521629e7154bf78c..7c36e2777a154e98c233a2eb153e4be4c9811299 100644 (file)
@@ -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
index 93ee0020c9b14939605165b932db0d53d8753e01..96175d48a9022494105cfd534b8d6f4c03631aff 100644 (file)
@@ -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);
index 6c187d0bf7c29e546bf27d7c468151186a74c135..1327cd0df90a4b2f9fcf9632fc379aceba9f2e5d 100644 (file)
@@ -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];
 
index 369e930750e99eada343baea2fb8af4b2a591416..8ee4e762f4e5599715973b61f945703ace6ae723 100644 (file)
@@ -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;
@@ -42,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);
 
index 552f2a02e5b5d178e077d8b97c880ce975406137..df9be4524301942bafe388da0eed0d275fcc9a4b 100644 (file)
@@ -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__ */
index 663ab98d1f97f1b6ed8f164ba15a310271f0538a..b0c80dffd8b8ee96af2c4680093e1d0fa8bb4cd0 100644 (file)
@@ -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",
@@ -352,52 +345,17 @@ 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;
        }
 
-       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);
-               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;
 }
index 044c65680ef3240e76f6f499a73831d36a8e36d8..7ef97b2a6edaa63a2c8b0f4638c1e5f18f4f2dc5 100644 (file)
@@ -23,6 +23,7 @@
 #include <drm/drm_panel.h>
 #include <drm/drm_probe_helper.h>
 
+#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 @@ done:
                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
  */
@@ -545,7 +602,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 +653,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 +704,15 @@ done:
        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;
 }
 
@@ -786,6 +856,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 },
diff --git a/drivers/gpu/drm/rcar-du/rcar_lvds.h b/drivers/gpu/drm/rcar-du/rcar_lvds.h
new file mode 100644 (file)
index 0000000..a709cae
--- /dev/null
@@ -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__ */