MLK-16918-7: drm/mxsfb: Update mxsfb to support a bridge
authorRobert Chiras <robert.chiras@nxp.com>
Thu, 9 Nov 2017 07:36:30 +0000 (09:36 +0200)
committerNitin Garg <nitin.garg@nxp.com>
Mon, 19 Mar 2018 20:47:07 +0000 (15:47 -0500)
Currently, the MXSFB DRM driver only supports a panel. But, its output
display signal can also be redirected to another encoder, like a DSI
controller. In this case, that DSI controller may act like a drm_bridge.
In order support this use-case too, this patch adds support for
drm_bridge in mxsfb.

Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
drivers/gpu/drm/mxsfb/mxsfb_crtc.c
drivers/gpu/drm/mxsfb/mxsfb_drv.c
drivers/gpu/drm/mxsfb/mxsfb_drv.h
drivers/gpu/drm/mxsfb/mxsfb_out.c
drivers/gpu/drm/mxsfb/mxsfb_regs.h

index 0818903..363ce0c 100644 (file)
@@ -64,13 +64,13 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
 
        switch (format) {
        case DRM_FORMAT_RGB565:
-               dev_dbg(drm->dev, "Setting up RGB565 mode\n");
+               DRM_DEV_DEBUG_DRIVER(drm->dev, "Setting up RGB565 mode\n");
                ctrl |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT);
                ctrl |= CTRL_SET_WORD_LENGTH(0);
                ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
                break;
        case DRM_FORMAT_XRGB8888:
-               dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
+               DRM_DEV_DEBUG_DRIVER(drm->dev, "Setting up XRGB8888 mode\n");
                ctrl |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT);
                ctrl |= CTRL_SET_WORD_LENGTH(3);
                /* Do not use packed pixels = one pixel per word instead. */
@@ -83,6 +83,8 @@ static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb)
 
        writel(ctrl1, mxsfb->base + LCDC_CTRL1);
        writel(ctrl, mxsfb->base + LCDC_CTRL);
+       ctrl = readl(mxsfb->base + LCDC_CTRL);
+       ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
 
        return 0;
 }
@@ -91,11 +93,17 @@ static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
 {
        u32 reg;
 
+       if (mxsfb->enabled)
+               return;
+
        if (mxsfb->clk_disp_axi)
                clk_prepare_enable(mxsfb->clk_disp_axi);
        clk_prepare_enable(mxsfb->clk);
        mxsfb_enable_axi_clk(mxsfb);
 
+       writel(CTRL2_OUTSTANDING_REQS__REQ_16,
+               mxsfb->base + LCDC_V4_CTRL2 + REG_SET);
+
        /* If it was disabled, re-enable the mode again */
        writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
 
@@ -104,13 +112,23 @@ static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
        reg |= VDCTRL4_SYNC_SIGNALS_ON;
        writel(reg, mxsfb->base + LCDC_VDCTRL4);
 
+       writel(CTRL_MASTER, mxsfb->base + LCDC_CTRL + REG_SET);
        writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
+
+       writel(CTRL1_RECOVERY_ON_UNDERFLOW, mxsfb->base + LCDC_CTRL1 + REG_SET);
+
+       mxsfb->enabled = true;
 }
 
 static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
 {
        u32 reg;
 
+       if (!mxsfb->enabled)
+               return;
+
+       writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_CLR);
+
        /*
         * Even if we disable the controller here, it will still continue
         * until its FIFOs are running out of data
@@ -120,21 +138,27 @@ static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
        readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
                           0, 1000);
 
+       writel(CTRL_MASTER, mxsfb->base + LCDC_CTRL + REG_CLR);
+
        reg = readl(mxsfb->base + LCDC_VDCTRL4);
        reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
        writel(reg, mxsfb->base + LCDC_VDCTRL4);
 
        mxsfb_disable_axi_clk(mxsfb);
 
-       clk_disable_unprepare(mxsfb->clk);
        if (mxsfb->clk_disp_axi)
                clk_disable_unprepare(mxsfb->clk_disp_axi);
+       clk_disable_unprepare(mxsfb->clk);
+
+       mxsfb->enabled = false;
 }
 
 static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
 {
        struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode;
        const u32 bus_flags = mxsfb->connector.display_info.bus_flags;
+       u32 hbp = m->crtc_hblank_end - m->crtc_hsync_end;
+       u32 vbp = m->crtc_vblank_end - m->crtc_vsync_end;
        u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
        int err;
 
@@ -184,18 +208,25 @@ static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
               VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
               mxsfb->base + LCDC_VDCTRL2);
 
-       writel(SET_HOR_WAIT_CNT(m->crtc_hblank_end - m->crtc_hsync_end) |
-              SET_VERT_WAIT_CNT(m->crtc_vblank_end - m->crtc_vsync_end),
+       writel(SET_HOR_WAIT_CNT(hbp + hsync_pulse_len) |
+              SET_VERT_WAIT_CNT(vbp + vsync_pulse_len),
               mxsfb->base + LCDC_VDCTRL3);
 
        writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
               mxsfb->base + LCDC_VDCTRL4);
 
+       if (mxsfb->gem != NULL) {
+               writel(mxsfb->gem->paddr,
+                      mxsfb->base + mxsfb->devdata->next_buf);
+               mxsfb->gem = NULL;
+       }
+
        mxsfb_disable_axi_clk(mxsfb);
 }
 
 void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb)
 {
+       writel(0, mxsfb->base + LCDC_CTRL);
        mxsfb_crtc_mode_set_nofb(mxsfb);
        mxsfb_enable_controller(mxsfb);
 }
@@ -213,10 +244,12 @@ void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
        struct drm_framebuffer *fb = pipe->plane.state->fb;
        struct drm_pending_vblank_event *event;
        struct drm_gem_cma_object *gem;
+       u32 val;
 
        if (!crtc)
                return;
 
+
        spin_lock_irq(&crtc->dev->event_lock);
        event = crtc->state->event;
        if (event) {
@@ -234,8 +267,19 @@ void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb,
                return;
 
        gem = drm_fb_cma_get_gem_obj(fb, 0);
+       pr_info("GEM paddr=0x%llx\n", gem->paddr);
+
+       if (!mxsfb->enabled) {
+               mxsfb->gem = gem;
+               return;
+       }
 
        mxsfb_enable_axi_clk(mxsfb);
        writel(gem->paddr, mxsfb->base + mxsfb->devdata->next_buf);
        mxsfb_disable_axi_clk(mxsfb);
+
+       val = readl(mxsfb->base + mxsfb->devdata->cur_buf);
+       pr_info("REG[%02X]=%08x\n", mxsfb->devdata->cur_buf, val);
+       val = readl(mxsfb->base + mxsfb->devdata->next_buf);
+       pr_info("REG[%02X]=%08x\n", mxsfb->devdata->next_buf, val);
 }
index 79a18bf..21cf19d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/of_reserved_mem.h>
 #include <linux/pm_runtime.h>
 #include <linux/reservation.h>
+#include <linux/version.h>
 
 #include <drm/drmP.h>
 #include <drm/drm_atomic.h>
@@ -120,17 +121,21 @@ static void mxsfb_pipe_update(struct drm_simple_display_pipe *pipe,
        mxsfb_plane_atomic_update(mxsfb, plane_state);
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
 static int mxsfb_pipe_prepare_fb(struct drm_simple_display_pipe *pipe,
                                 struct drm_plane_state *plane_state)
 {
        return drm_fb_cma_prepare_fb(&pipe->plane, plane_state);
 }
+#endif
 
 struct drm_simple_display_pipe_funcs mxsfb_funcs = {
        .enable         = mxsfb_pipe_enable,
        .disable        = mxsfb_pipe_disable,
        .update         = mxsfb_pipe_update,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
        .prepare_fb     = mxsfb_pipe_prepare_fb,
+#endif
 };
 
 static int mxsfb_load(struct drm_device *drm, unsigned long flags)
@@ -193,10 +198,21 @@ static int mxsfb_load(struct drm_device *drm, unsigned long flags)
                goto err_vblank;
        }
 
-       ret = drm_panel_attach(mxsfb->panel, &mxsfb->connector);
-       if (ret) {
-               dev_err(drm->dev, "Cannot connect panel\n");
-               goto err_vblank;
+       /* Attach panel only if there is one */
+       if (mxsfb->panel) {
+               ret = drm_panel_attach(mxsfb->panel, &mxsfb->connector);
+               if (ret) {
+                       dev_err(drm->dev, "Cannot connect panel\n");
+                       goto err_vblank;
+               }
+       } else if (mxsfb->bridge) {
+               ret = drm_simple_display_pipe_attach_bridge(&mxsfb->pipe,
+                               mxsfb->bridge);
+               if (ret) {
+                       dev_err(drm->dev, "Cannot connect bridge\n");
+                       goto err_vblank;
+               }
+
        }
 
        drm->mode_config.min_width      = MXSFB_MIN_XRES;
@@ -432,7 +448,7 @@ static struct platform_driver mxsfb_platform_driver = {
        .remove         = mxsfb_remove,
        .id_table       = mxsfb_devtype,
        .driver = {
-               .name           = "mxsfb",
+               .name           = "mxsfb_drm",
                .of_match_table = mxsfb_dt_ids,
        },
 };
index 5d0883f..3340997 100644 (file)
@@ -37,7 +37,11 @@ struct mxsfb_drm_private {
        struct drm_simple_display_pipe  pipe;
        struct drm_connector            connector;
        struct drm_panel                *panel;
+       struct drm_bridge               *bridge;
        struct drm_fbdev_cma            *fbdev;
+
+       struct drm_gem_cma_object       *gem;
+       bool                            enabled;
 };
 
 int mxsfb_setup_crtc(struct drm_device *dev);
index fa8d173..cecbd08 100644 (file)
@@ -87,25 +87,26 @@ static int mxsfb_attach_endpoint(struct drm_device *drm,
 {
        struct mxsfb_drm_private *mxsfb = drm->dev_private;
        struct device_node *np;
-       struct drm_panel *panel;
-       int ret = -EPROBE_DEFER;
+       int ret = 0;
 
        np = of_graph_get_remote_port_parent(ep->local_node);
-       panel = of_drm_find_panel(np);
+       mxsfb->panel = of_drm_find_panel(np);
+       if (!mxsfb->panel)
+               mxsfb->bridge = of_drm_find_bridge(np);
        of_node_put(np);
 
-       if (!panel)
+       if (!mxsfb->panel && !mxsfb->bridge)
                return -EPROBE_DEFER;
 
-       mxsfb->connector.dpms = DRM_MODE_DPMS_OFF;
-       mxsfb->connector.polled = 0;
-       drm_connector_helper_add(&mxsfb->connector,
-                       &mxsfb_panel_connector_helper_funcs);
-       ret = drm_connector_init(drm, &mxsfb->connector,
-                                &mxsfb_panel_connector_funcs,
-                                DRM_MODE_CONNECTOR_Unknown);
-       if (!ret)
-               mxsfb->panel = panel;
+       if (mxsfb->panel) {
+               mxsfb->connector.dpms = DRM_MODE_DPMS_OFF;
+               mxsfb->connector.polled = 0;
+               drm_connector_helper_add(&mxsfb->connector,
+                               &mxsfb_panel_connector_helper_funcs);
+               ret = drm_connector_init(drm, &mxsfb->connector,
+                                        &mxsfb_panel_connector_funcs,
+                                        DRM_MODE_CONNECTOR_Unknown);
+       }
 
        return ret;
 }
index 31d62cd..e663932 100644 (file)
 
 #define LCDC_CTRL                      0x00
 #define LCDC_CTRL1                     0x10
+#define LCDC_V4_CTRL2                  0x20
 #define LCDC_V3_TRANSFER_COUNT         0x20
 #define LCDC_V4_TRANSFER_COUNT         0x30
 #define LCDC_V4_CUR_BUF                        0x40
 #define LCDC_V4_NEXT_BUF               0x50
 #define LCDC_V3_CUR_BUF                        0x30
 #define LCDC_V3_NEXT_BUF               0x40
+#define LCDC_TIMING                    0x60
 #define LCDC_VDCTRL0                   0x70
 #define LCDC_VDCTRL1                   0x80
 #define LCDC_VDCTRL2                   0x90
 #define LCDC_VDCTRL3                   0xa0
 #define LCDC_VDCTRL4                   0xb0
+#define LCDC_DVICTRL0                  0xc0
+#define LCDC_DVICTRL1                  0xd0
+#define LCDC_DVICTRL2                  0xe0
+#define LCDC_DVICTRL3                  0xf0
+#define LCDC_DVICTRL4                  0x100
+#define LCDC_V4_DATA                   0x180
+#define LCDC_V3_DATA                   0x1b0
 #define LCDC_V4_DEBUG0                 0x1d0
 #define LCDC_V3_DEBUG0                 0x1f0
+#define LCDC_AS_CTRL                   0x210
+#define LCDC_AS_BUF                    0x220
+#define LCDC_AS_NEXT_BUF               0x230
 
 #define CTRL_SFTRST                    (1 << 31)
 #define CTRL_CLKGATE                   (1 << 30)
 #define CTRL_DF24                      (1 << 1)
 #define CTRL_RUN                       (1 << 0)
 
+#define CTRL1_RECOVERY_ON_UNDERFLOW            (1 << 24)
 #define CTRL1_FIFO_CLEAR               (1 << 21)
 #define CTRL1_SET_BYTE_PACKAGING(x)    (((x) & 0xf) << 16)
 #define CTRL1_GET_BYTE_PACKAGING(x)    (((x) >> 16) & 0xf)
 #define CTRL1_CUR_FRAME_DONE_IRQ_EN    (1 << 13)
 #define CTRL1_CUR_FRAME_DONE_IRQ       (1 << 9)
 
+#define CTRL2_OUTSTANDING_REQS__REQ_16         (4 << 21)
+
 #define TRANSFER_COUNT_SET_VCOUNT(x)   (((x) & 0xffff) << 16)
 #define TRANSFER_COUNT_GET_VCOUNT(x)   (((x) >> 16) & 0xffff)
 #define TRANSFER_COUNT_SET_HCOUNT(x)   ((x) & 0xffff)