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. */
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;
}
{
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);
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
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;
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);
}
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) {
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);
}
#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>
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)
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;
.remove = mxsfb_remove,
.id_table = mxsfb_devtype,
.driver = {
- .name = "mxsfb",
+ .name = "mxsfb_drm",
.of_match_table = mxsfb_dt_ids,
},
};
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);
{
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;
}
#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)