MLK-17537-4: drm/bridge: nwl-dsi: implement mode_valid()
authorRobert Chiras <robert.chiras@nxp.com>
Mon, 4 Feb 2019 12:53:45 +0000 (14:53 +0200)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
This patch removes the exported function nwl_dsi_get_bit_clock that was
used by nwl_dsi-imx driver in order to configure the phy driver speed
and move this configuration directly into the nwl-dsi driver.
This function is now used directly by nwl-dsi to verify which mode can or
cannot be supported by the DSI PHY.
Also, in nwl-dsi, add support for mode_valid and add each supported mode
into a list kept internally so that it can apply the needed
configuration (phyref rate, dsi lanes, bit-clock) later when the mode is
used.

Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
Reviewed-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
drivers/gpu/drm/bridge/nwl-dsi.c
drivers/gpu/drm/imx/nwl_dsi-imx.c
include/drm/bridge/nwl_dsi.h

index 32575ab..d910dd9 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/of_graph.h>
 #include <linux/of_platform.h>
 #include <linux/phy/phy.h>
+#include <linux/phy/phy-mixel-mipi-dsi.h>
 #include <linux/spinlock.h>
 #include <video/mipi_display.h>
 #include <video/videomode.h>
 
 static const char IRQ_NAME[] = "nwl-dsi";
 
+/* Possible valid PHY reference clock rates*/
+static u32 phyref_rates[] = {
+       27000000,
+       25000000,
+       24000000,
+};
+
 enum {
        CLK_PHY_REF     = BIT(1),
        CLK_RX_ESC      = BIT(2),
@@ -181,6 +189,14 @@ struct clk_config {
        bool enabled;
 };
 
+struct mode_config {
+       int                             pixclock;
+       unsigned int                    lanes;
+       unsigned long                   bitclock;
+       u32                             phyref_rate;
+       struct list_head                list;
+};
+
 struct nwl_mipi_dsi {
        struct device                   *dev;
        struct drm_panel                *panel;
@@ -199,10 +215,11 @@ struct nwl_mipi_dsi {
 
        void __iomem                    *base;
        int                             irq;
-       struct videomode                vm;
 
        struct mipi_dsi_transfer        *xfer;
 
+       struct drm_display_mode         *curr_mode;
+       struct list_head                valid_modes;
        u32                             lanes;
        bool                            no_clk_reset;
        bool                            enabled;
@@ -267,22 +284,14 @@ static enum dpi_pixel_format nwl_dsi_get_dpi_pixel_format(
        }
 }
 
-unsigned long nwl_dsi_get_bit_clock(struct drm_bridge *bridge,
-       unsigned long pixclock)
+static unsigned long nwl_dsi_get_bit_clock(struct nwl_mipi_dsi *dsi,
+               unsigned long pixclock)
 {
-       struct nwl_mipi_dsi *dsi;
-       struct mipi_dsi_device *dsi_device;
+       struct mipi_dsi_device *dsi_device = dsi->dsi_device;
        int bpp;
        u32 bus_fmt;
        struct drm_crtc *crtc = 0;
 
-       /* Make sure the bridge is correctly initialized */
-       if (!bridge || !bridge->driver_private)
-               return 0;
-
-       dsi = bridge->driver_private;
-       dsi_device = dsi->dsi_device;
-
        if (dsi_device->lanes < 1 || dsi_device->lanes > 4)
                return 0;
 
@@ -299,9 +308,8 @@ unsigned long nwl_dsi_get_bit_clock(struct drm_bridge *bridge,
 
        bpp = mipi_dsi_pixel_format_to_bpp(dsi_device->format);
 
-       return (pixclock / dsi->lanes) * bpp;
+       return (pixclock * bpp) / dsi_device->lanes;
 }
-EXPORT_SYMBOL_GPL(nwl_dsi_get_bit_clock);
 
 static void nwl_dsi_config_host(struct nwl_mipi_dsi *dsi)
 {
@@ -336,13 +344,15 @@ static void nwl_dsi_config_dpi(struct nwl_mipi_dsi *dsi)
 {
        struct device *dev = dsi->dev;
        struct mipi_dsi_device *dsi_device = dsi->dsi_device;
-       struct videomode *vm = &dsi->vm;
+       struct videomode vm;
        enum dpi_pixel_format pixel_format =
                nwl_dsi_get_dpi_pixel_format(dsi_device->format);
        enum dpi_interface_color_coding color_coding =
                nwl_dsi_get_dpi_interface_color_coding(dsi_device->format);
        bool burst_mode;
 
+       drm_display_mode_to_videomode(dsi->curr_mode, &vm);
+
        nwl_dsi_write(dsi, INTERFACE_COLOR_CODING, color_coding);
        nwl_dsi_write(dsi, PIXEL_FORMAT, pixel_format);
        DRM_DEV_DEBUG_DRIVER(dev, "DSI format is: %d (CC=%d, PF=%d)\n",
@@ -360,12 +370,12 @@ static void nwl_dsi_config_dpi(struct nwl_mipi_dsi *dsi)
                nwl_dsi_write(dsi, PIXEL_FIFO_SEND_LEVEL, 256);
        } else {
                nwl_dsi_write(dsi, VIDEO_MODE, 0x0);
-               nwl_dsi_write(dsi, PIXEL_FIFO_SEND_LEVEL, vm->hactive);
+               nwl_dsi_write(dsi, PIXEL_FIFO_SEND_LEVEL, vm.hactive);
        }
 
-       nwl_dsi_write(dsi, HFP, vm->hfront_porch);
-       nwl_dsi_write(dsi, HBP, vm->hback_porch);
-       nwl_dsi_write(dsi, HSA, vm->hsync_len);
+       nwl_dsi_write(dsi, HFP, vm.hfront_porch);
+       nwl_dsi_write(dsi, HBP, vm.hback_porch);
+       nwl_dsi_write(dsi, HSA, vm.hsync_len);
 
        nwl_dsi_write(dsi, ENABLE_MULT_PKTS, 0x0);
        nwl_dsi_write(dsi, BLLP_MODE, 0x1);
@@ -373,10 +383,10 @@ static void nwl_dsi_config_dpi(struct nwl_mipi_dsi *dsi)
        nwl_dsi_write(dsi, USE_NULL_PKT_BLLP, 0x0);
        nwl_dsi_write(dsi, VC, 0x0);
 
-       nwl_dsi_write(dsi, PIXEL_PAYLOAD_SIZE, vm->hactive);
-       nwl_dsi_write(dsi, VACTIVE, vm->vactive);
-       nwl_dsi_write(dsi, VBP, vm->vback_porch);
-       nwl_dsi_write(dsi, VFP, vm->vfront_porch);
+       nwl_dsi_write(dsi, PIXEL_PAYLOAD_SIZE, vm.hactive);
+       nwl_dsi_write(dsi, VACTIVE, vm.vactive);
+       nwl_dsi_write(dsi, VBP, vm.vback_porch);
+       nwl_dsi_write(dsi, VFP, vm.vfront_porch);
 }
 
 static void nwl_dsi_enable_clocks(struct nwl_mipi_dsi *dsi, u32 clks)
@@ -445,23 +455,102 @@ static void nwl_dsi_init_interrupts(struct nwl_mipi_dsi *dsi)
        nwl_dsi_write(dsi, IRQ_MASK, irq_enable);
 }
 
+/*
+ * This function will try the required phy speed for current mode
+ * If the phy speed can be achieved, the phy will save the speed
+ * configuration
+ */
+static struct mode_config *nwl_dsi_mode_probe(struct nwl_mipi_dsi *dsi,
+                           const struct drm_display_mode *mode)
+{
+       struct device *dev = dsi->dev;
+       struct mode_config *config;
+       unsigned long pixclock = mode->clock * 1000;
+       unsigned long bit_clk = 0;
+       u32 phyref_rate = 0, lanes = dsi->lanes;
+       size_t i = 0, num_rates = ARRAY_SIZE(phyref_rates);
+       int ret = 0;
+
+       list_for_each_entry(config, &dsi->valid_modes, list)
+               if (config->pixclock == pixclock)
+                       return config;
+
+       while (i < num_rates) {
+               bit_clk = nwl_dsi_get_bit_clock(dsi, pixclock);
+               phyref_rate = phyref_rates[i];
+               ret = mixel_phy_mipi_set_phy_speed(dsi->phy,
+                       bit_clk,
+                       phyref_rate,
+                       false);
+
+               /* Pick the first non-failing rate */
+               if (!ret)
+                       break;
+
+               /* Reached the end of phyref_rates, try another lane config */
+               if ((i++ == num_rates - 1) && (--lanes > 1)) {
+                       i = 0;
+                       continue;
+               }
+       }
+
+       if (ret < 0) {
+               DRM_DEV_DEBUG_DRIVER(dev,
+                       "Cannot setup PHY for mode: %ux%u @%d kHz\n",
+                       mode->hdisplay,
+                       mode->vdisplay,
+                       mode->clock);
+               DRM_DEV_DEBUG_DRIVER(dev, "phy_ref clk: %u, bit clk: %lu\n",
+                       phyref_rate, bit_clk);
+
+               return NULL;
+       }
+
+       config = devm_kzalloc(dsi->dev, sizeof(struct mode_config), GFP_KERNEL);
+       config->pixclock = pixclock;
+       config->lanes = lanes;
+       config->bitclock = bit_clk;
+       config->phyref_rate = phyref_rate;
+       list_add(&config->list, &dsi->valid_modes);
+
+       return config;
+}
+
+static enum drm_mode_status nwl_dsi_bridge_mode_valid(struct drm_bridge *bridge,
+                          const struct drm_display_mode *mode)
+{
+       struct nwl_mipi_dsi *dsi = bridge->driver_private;
+
+       DRM_DEV_DEBUG_DRIVER(dsi->dev, "Validating mode:");
+       drm_mode_debug_printmodeline(mode);
+
+       if (!nwl_dsi_mode_probe(dsi, mode))
+               return MODE_NOCLOCK;
+
+       return MODE_OK;
+}
+
 static bool nwl_dsi_bridge_mode_fixup(struct drm_bridge *bridge,
                           const struct drm_display_mode *mode,
-                          struct drm_display_mode *adjusted_mode)
+                          struct drm_display_mode *adjusted)
 {
        struct nwl_mipi_dsi *dsi = bridge->driver_private;
-       int bpp = mipi_dsi_pixel_format_to_bpp(dsi->dsi_device->format);
-       unsigned long pixclock = adjusted_mode->clock * 1000;
-       unsigned long data_rate;
+       struct mode_config *config;
+
+       DRM_DEV_DEBUG_DRIVER(dsi->dev, "Fixup mode:\n");
+       drm_mode_debug_printmodeline(adjusted);
 
-       if (dsi->dsi_device->lanes < 1 || dsi->dsi_device->lanes > 4)
+       config = nwl_dsi_mode_probe(dsi, adjusted);
+       if (!config)
                return false;
 
-       /* Data rate is in bit clock for each lane */
-       data_rate = (pixclock / dsi->lanes) * bpp;
+       DRM_DEV_DEBUG_DRIVER(dsi->dev, "lanes=%u, data_rate=%lu\n",
+                            config->lanes, config->bitclock);
+       if (config->lanes < 1 || config->lanes > 4)
+               return false;
 
        /* Max data rate for this controller is 1.5Gbps */
-       if (data_rate > 1500000000)
+       if (config->bitclock > 1500000000)
                return false;
 
        return true;
@@ -472,11 +561,34 @@ static void nwl_dsi_bridge_mode_set(struct drm_bridge *bridge,
                                     struct drm_display_mode *adjusted)
 {
        struct nwl_mipi_dsi *dsi = bridge->driver_private;
+       struct mode_config *config;
+       u32 actual_phy_rate;
 
        DRM_DEV_DEBUG_DRIVER(dsi->dev, "Setting mode:\n");
        drm_mode_debug_printmodeline(adjusted);
 
-       drm_display_mode_to_videomode(adjusted, &dsi->vm);
+       config = nwl_dsi_mode_probe(dsi, adjusted);
+       /* New mode? This should NOT happen */
+       if (!config) {
+               DRM_DEV_ERROR(dsi->dev, "Unsupported mode provided:\n");
+               drm_mode_debug_printmodeline(adjusted);
+               return;
+       }
+
+       mixel_phy_mipi_set_phy_speed(dsi->phy,
+                       config->bitclock,
+                       config->phyref_rate,
+                       false);
+       clk_set_rate(dsi->phy_ref.clk, config->phyref_rate);
+       actual_phy_rate = clk_get_rate(dsi->phy_ref.clk);
+       dsi->dsi_device->lanes = config->lanes;
+       DRM_DEV_DEBUG_DRIVER(dsi->dev,
+               "Using phy_ref rate: %u (actual: %u), "
+               "bitclock: %lu, lanes: %u\n",
+               config->phyref_rate, actual_phy_rate,
+               config->bitclock, config->lanes);
+
+       dsi->curr_mode = drm_mode_duplicate(bridge->dev, adjusted);
 }
 
 static int nwl_dsi_host_attach(struct mipi_dsi_host *host,
@@ -522,6 +634,35 @@ static int nwl_dsi_host_attach(struct mipi_dsi_host *host,
        dsi->dsi_device = device;
        dsi->lanes = device->lanes;
 
+       /*
+        * If this happened right before a mode_set, it means that our
+        * bridge/panel doesn't like the current mode parameters and changed
+        * something on the dsi_device (lanes or format). In this case, we have
+        * to reconfigure the phy.
+        */
+       if (dsi->curr_mode) {
+               unsigned long pixclock = dsi->curr_mode->clock * 1000;
+               struct mode_config *config;
+
+               DRM_DEV_DEBUG_DRIVER(dsi->dev, "Re-setting mode:\n");
+               drm_mode_debug_printmodeline(dsi->curr_mode);
+               drm_mode_destroy(dsi->bridge.dev, dsi->curr_mode);
+               list_for_each_entry(config, &dsi->valid_modes, list)
+                       if (config->pixclock == pixclock)
+                               break;
+
+               if (device->lanes != config->lanes)
+                       return 0;
+
+               clk_set_rate(dsi->phy_ref.clk, config->phyref_rate);
+               device->lanes = config->lanes;
+               DRM_DEV_DEBUG_DRIVER(dsi->dev,
+                       "Using phy_ref rate: %d (actual: %ld), "
+                       "bitclock: %lu, lanes: %d\n",
+                       config->phyref_rate, clk_get_rate(dsi->phy_ref.clk),
+                       config->bitclock, config->lanes);
+       }
+
        if (dsi->connector.dev)
                drm_helper_hpd_irq_event(dsi->connector.dev);
 
@@ -878,7 +1019,6 @@ static int nwl_dsi_connector_get_modes(struct drm_connector *connector)
        struct nwl_mipi_dsi *dsi = container_of(connector,
                                                struct nwl_mipi_dsi,
                                                connector);
-
        if (dsi->panel)
                return drm_panel_get_modes(dsi->panel);
 
@@ -1099,6 +1239,7 @@ static void nwl_dsi_bridge_disable(struct drm_bridge *bridge)
 static const struct drm_bridge_funcs nwl_dsi_bridge_funcs = {
        .enable = nwl_dsi_bridge_enable,
        .disable = nwl_dsi_bridge_disable,
+       .mode_valid = nwl_dsi_bridge_mode_valid,
        .mode_fixup = nwl_dsi_bridge_mode_fixup,
        .mode_set = nwl_dsi_bridge_mode_set,
        .attach = nwl_dsi_bridge_attach,
@@ -1188,15 +1329,26 @@ static int nwl_dsi_probe(struct platform_device *pdev)
        if (ret < 0)
                dev_err(dev, "Failed to add nwl-dsi bridge (%d)\n", ret);
 
+       INIT_LIST_HEAD(&dsi->valid_modes);
+
        return ret;
 }
 
 static int nwl_dsi_remove(struct platform_device *pdev)
 {
        struct nwl_mipi_dsi *dsi = platform_get_drvdata(pdev);
+       struct mode_config *config;
+       struct list_head *pos, *tmp;
+
 
        drm_bridge_remove(&dsi->bridge);
 
+       list_for_each_safe(pos, tmp, &dsi->valid_modes) {
+               config = list_entry(pos, struct mode_config, list);
+               list_del(pos);
+               devm_kfree(dsi->dev, config);
+       }
+
        pm_runtime_disable(&pdev->dev);
 
        return 0;
index 0be8f2a..85ee45a 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/of.h>
 #include <linux/of_graph.h>
 #include <linux/of_platform.h>
-#include <linux/phy/phy-mixel-mipi-dsi.h>
 #include <linux/phy/phy.h>
 #include <linux/regmap.h>
 #include <soc/imx8/sc/sci.h>
 #define CLK_BYPASS     "bypass"
 #define CLK_PHYREF     "phy_ref"
 
-/* Possible valid PHY reference clock rates*/
-u32 phyref_rates[] = {
-       24000000,
-       25000000,
-       27000000,
-};
-
 struct imx_mipi_dsi {
        struct drm_encoder              encoder;
        struct drm_bridge               bridge;
@@ -86,9 +78,6 @@ struct imx_mipi_dsi {
        u32 tx_ulps_reg;
        u32 pxl2dpi_reg;
 
-       unsigned long                   bit_clk;
-       unsigned long                   pix_clk;
-       u32                             phyref_rate;
        u32                             instance;
        u32                             sync_pol;
        u32                             power_on_delay;
@@ -188,10 +177,28 @@ static void imx_nwl_dsi_set_clocks(struct imx_mipi_dsi *dsi, bool enable)
        struct device *dev = dsi->dev;
        const char *id;
        struct clk *clk;
-       unsigned long new_rate, cur_rate;
+       unsigned long new_rate, cur_rate, phy_rate = 0;
        bool enabled;
        size_t i;
 
+       /*
+        * PHY_REF rate should be set by the nwl-dsi bridge, depending on the
+        * current mode used. We need that rate to set the PIXEL clock for
+        * QM or QXP.
+        */
+       for (i = 0; i < dsi->clk_num; i++) {
+               if (!dsi->clk_config[i].present)
+                       continue;
+               id = dsi->clk_config[i].id;
+               clk = dsi->clk_config[i].clk;
+
+               if (!strcmp(id, CLK_BYPASS) || !strcmp(id, CLK_PHYREF)) {
+                       dsi->clk_config[i].rate = clk_get_rate(clk);
+                       phy_rate = dsi->clk_config[i].rate;
+                       break;
+               }
+       }
+
        for (i = 0; i < dsi->clk_num; i++) {
                if (!dsi->clk_config[i].present)
                        continue;
@@ -201,9 +208,8 @@ static void imx_nwl_dsi_set_clocks(struct imx_mipi_dsi *dsi, bool enable)
                cur_rate = clk_get_rate(clk);
                enabled = dsi->clk_config[i].enabled;
 
-               /* BYPASS clk must have the same rate as PHY_REF clk */
-               if (!strcmp(id, CLK_BYPASS) || !strcmp(id, CLK_PHYREF))
-                       new_rate = dsi->phyref_rate;
+               if (!strcmp(id, CLK_PIXEL) && phy_rate)
+                       new_rate = phy_rate;
 
                if (enable) {
                        if (enabled && new_rate != cur_rate)
@@ -468,7 +474,7 @@ static void imx_nwl_dsi_enable(struct imx_mipi_dsi *dsi)
        const struct of_device_id *of_id = of_match_device(imx_nwl_dsi_dt_ids,
                                                           dev);
        const struct devtype *devtype = of_id->data;
-       unsigned long bit_clk, min_sleep, max_sleep;
+       unsigned long min_sleep, max_sleep;
        int ret;
 
        if (dsi->enabled)
@@ -476,25 +482,6 @@ static void imx_nwl_dsi_enable(struct imx_mipi_dsi *dsi)
 
        DRM_DEV_DEBUG_DRIVER(dev, "id = %s\n", (dsi->instance)?"DSI1":"DSI0");
 
-       /*
-        * TODO: we are doing this here, because the ADV7535 which is a drm
-        * bridge, may change the DSI parameters in mode_set. One of the
-        * changed parameter is DSI lanes, which affects the PHY settings.
-        * This is why, we need run this function again, here, in order
-        * to correctly set-up the PHY. Since we can't do anything here, we
-        * will ignore it's status.
-        * In the future, maybe it will be best to move the PHY handling
-        * into the DSI host driver.
-        */
-       bit_clk = nwl_dsi_get_bit_clock(dsi->next_bridge, dsi->pix_clk);
-       if (bit_clk != dsi->bit_clk) {
-               mixel_phy_mipi_set_phy_speed(dsi->phy,
-                       bit_clk,
-                       dsi->phyref_rate,
-                       false);
-               dsi->bit_clk = bit_clk;
-       }
-
        /*
         * On some systems we need to wait some time before enabling the
         * phy_ref clock, in order to allow the parent PLL to become stable
@@ -544,59 +531,6 @@ static void imx_nwl_dsi_disable(struct imx_mipi_dsi *dsi)
        dsi->enabled = false;
 }
 
-/*
- * This function will try the required phy speed for current mode
- * If the phy speed can be achieved, the phy will save the speed
- * configuration
- */
-static int imx_nwl_try_phy_speed(struct imx_mipi_dsi *dsi,
-                           struct drm_display_mode *mode)
-{
-       struct device *dev = dsi->dev;
-       unsigned long pixclock;
-       unsigned long bit_clk;
-       size_t i, num_rates = ARRAY_SIZE(phyref_rates);
-       int ret = 0;
-
-       pixclock = mode->clock * 1000;
-       /*
-        * DSI host should know the required bit clock, since it has info
-        * about bits-per-pixel and number of lanes from DSI device
-        */
-       bit_clk = nwl_dsi_get_bit_clock(dsi->next_bridge, pixclock);
-
-       /* If bit_clk is the same with current, we're good */
-       if (bit_clk == dsi->bit_clk)
-               return 0;
-
-       for (i = 0; i < num_rates; i++) {
-               dsi->phyref_rate = phyref_rates[i];
-               DRM_DEV_DEBUG_DRIVER(dev, "Trying PHY ref rate: %u\n",
-                       dsi->phyref_rate);
-               ret = mixel_phy_mipi_set_phy_speed(dsi->phy,
-                       bit_clk,
-                       dsi->phyref_rate,
-                       false);
-               /* Pick the first non-failing rate */
-               if (!ret)
-                       break;
-       }
-       if (ret < 0) {
-               DRM_DEV_ERROR(dev,
-                       "Cannot setup PHY for mode: %ux%u @%d kHz\n",
-                       mode->hdisplay,
-                       mode->vdisplay,
-                       mode->clock);
-               DRM_DEV_ERROR(dev, "PHY_REF clk: %u, bit clk: %lu\n",
-                       dsi->phyref_rate, bit_clk);
-       } else {
-               dsi->bit_clk = bit_clk;
-               dsi->pix_clk = pixclock;
-       }
-
-       return ret;
-}
-
 static void imx_nwl_dsi_encoder_enable(struct drm_encoder *encoder)
 {
        struct imx_mipi_dsi *dsi = encoder_to_dsi(encoder);
@@ -634,7 +568,7 @@ static bool imx_nwl_dsi_mode_fixup(struct imx_mipi_dsi *dsi,
                *flags |= DRM_MODE_FLAG_NVSYNC;
        }
 
-       return !imx_nwl_try_phy_speed(dsi, mode);
+       return true;
 }
 
 static int imx_nwl_dsi_encoder_atomic_check(struct drm_encoder *encoder,
index 2ca1e58..df5f25b 100644 (file)
@@ -39,7 +39,4 @@ enum dpi_pixel_format {
        DPI_FMT_24_BIT /* 0x3 */
 };
 
-unsigned long nwl_dsi_get_bit_clock(struct drm_bridge *bridge,
-       unsigned long pixclock);
-
 #endif /* __NWL_DSI_H__ */