140a726b8SLaurent Pinchart // SPDX-License-Identifier: GPL-2.0-or-later
240a726b8SLaurent Pinchart /*
340a726b8SLaurent Pinchart * Copyright (C) 2016 Marek Vasut <marex@denx.de>
440a726b8SLaurent Pinchart *
540a726b8SLaurent Pinchart * This code is based on drivers/video/fbdev/mxsfb.c :
640a726b8SLaurent Pinchart * Copyright (C) 2010 Juergen Beisert, Pengutronix
740a726b8SLaurent Pinchart * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
840a726b8SLaurent Pinchart * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
940a726b8SLaurent Pinchart */
1040a726b8SLaurent Pinchart
1140a726b8SLaurent Pinchart #include <linux/clk.h>
12ae1ed009SLaurent Pinchart #include <linux/io.h>
1340a726b8SLaurent Pinchart #include <linux/iopoll.h>
1472bd9ea3SVille Syrjälä #include <linux/media-bus-format.h>
15ae1ed009SLaurent Pinchart #include <linux/pm_runtime.h>
1640a726b8SLaurent Pinchart #include <linux/spinlock.h>
1740a726b8SLaurent Pinchart
18ae1ed009SLaurent Pinchart #include <drm/drm_atomic.h>
19ae1ed009SLaurent Pinchart #include <drm/drm_atomic_helper.h>
2040a726b8SLaurent Pinchart #include <drm/drm_bridge.h>
2140a726b8SLaurent Pinchart #include <drm/drm_crtc.h>
22ae1ed009SLaurent Pinchart #include <drm/drm_encoder.h>
236bcfe8eaSDanilo Krummrich #include <drm/drm_fb_dma_helper.h>
2440a726b8SLaurent Pinchart #include <drm/drm_fourcc.h>
25720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
26820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
274a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
28ae1ed009SLaurent Pinchart #include <drm/drm_plane.h>
2940a726b8SLaurent Pinchart #include <drm/drm_vblank.h>
3040a726b8SLaurent Pinchart
3140a726b8SLaurent Pinchart #include "mxsfb_drv.h"
3240a726b8SLaurent Pinchart #include "mxsfb_regs.h"
3340a726b8SLaurent Pinchart
3440a726b8SLaurent Pinchart /* 1 second delay should be plenty of time for block reset */
3540a726b8SLaurent Pinchart #define RESET_TIMEOUT 1000000
3640a726b8SLaurent Pinchart
37ae1ed009SLaurent Pinchart /* -----------------------------------------------------------------------------
38ae1ed009SLaurent Pinchart * CRTC
39ae1ed009SLaurent Pinchart */
40ae1ed009SLaurent Pinchart
set_hsync_pulse_width(struct mxsfb_drm_private * mxsfb,u32 val)4140a726b8SLaurent Pinchart static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
4240a726b8SLaurent Pinchart {
4340a726b8SLaurent Pinchart return (val & mxsfb->devdata->hs_wdth_mask) <<
4440a726b8SLaurent Pinchart mxsfb->devdata->hs_wdth_shift;
4540a726b8SLaurent Pinchart }
4640a726b8SLaurent Pinchart
4751b777f2SLaurent Pinchart /*
4851b777f2SLaurent Pinchart * Setup the MXSFB registers for decoding the pixels out of the framebuffer and
4951b777f2SLaurent Pinchart * outputting them on the bus.
5051b777f2SLaurent Pinchart */
mxsfb_set_formats(struct mxsfb_drm_private * mxsfb,const u32 bus_format)51b776b0f0SMarek Vasut static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb,
52b776b0f0SMarek Vasut const u32 bus_format)
5340a726b8SLaurent Pinchart {
54ae1ed009SLaurent Pinchart struct drm_device *drm = mxsfb->drm;
55ae1ed009SLaurent Pinchart const u32 format = mxsfb->crtc.primary->state->fb->format->format;
5640a726b8SLaurent Pinchart u32 ctrl, ctrl1;
5740a726b8SLaurent Pinchart
5851b777f2SLaurent Pinchart DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
5951b777f2SLaurent Pinchart bus_format);
6051b777f2SLaurent Pinchart
6140a726b8SLaurent Pinchart ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
6240a726b8SLaurent Pinchart
6340a726b8SLaurent Pinchart /* CTRL1 contains IRQ config and status bits, preserve those. */
6440a726b8SLaurent Pinchart ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
6540a726b8SLaurent Pinchart ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
6640a726b8SLaurent Pinchart
6740a726b8SLaurent Pinchart switch (format) {
6840a726b8SLaurent Pinchart case DRM_FORMAT_RGB565:
6940a726b8SLaurent Pinchart dev_dbg(drm->dev, "Setting up RGB565 mode\n");
7040a726b8SLaurent Pinchart ctrl |= CTRL_WORD_LENGTH_16;
7140a726b8SLaurent Pinchart ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
7240a726b8SLaurent Pinchart break;
7340a726b8SLaurent Pinchart case DRM_FORMAT_XRGB8888:
7440a726b8SLaurent Pinchart dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
7540a726b8SLaurent Pinchart ctrl |= CTRL_WORD_LENGTH_24;
7640a726b8SLaurent Pinchart /* Do not use packed pixels = one pixel per word instead. */
7740a726b8SLaurent Pinchart ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
7840a726b8SLaurent Pinchart break;
7940a726b8SLaurent Pinchart }
8040a726b8SLaurent Pinchart
8140a726b8SLaurent Pinchart switch (bus_format) {
8240a726b8SLaurent Pinchart case MEDIA_BUS_FMT_RGB565_1X16:
8351b777f2SLaurent Pinchart ctrl |= CTRL_BUS_WIDTH_16;
8440a726b8SLaurent Pinchart break;
8540a726b8SLaurent Pinchart case MEDIA_BUS_FMT_RGB666_1X18:
8651b777f2SLaurent Pinchart ctrl |= CTRL_BUS_WIDTH_18;
8740a726b8SLaurent Pinchart break;
8840a726b8SLaurent Pinchart case MEDIA_BUS_FMT_RGB888_1X24:
8951b777f2SLaurent Pinchart ctrl |= CTRL_BUS_WIDTH_24;
9040a726b8SLaurent Pinchart break;
9140a726b8SLaurent Pinchart default:
92e2e0ee7eSGuido Günther dev_err(drm->dev, "Unknown media bus format 0x%x\n", bus_format);
9340a726b8SLaurent Pinchart break;
9440a726b8SLaurent Pinchart }
9551b777f2SLaurent Pinchart
9651b777f2SLaurent Pinchart writel(ctrl1, mxsfb->base + LCDC_CTRL1);
9751b777f2SLaurent Pinchart writel(ctrl, mxsfb->base + LCDC_CTRL);
9840a726b8SLaurent Pinchart }
9940a726b8SLaurent Pinchart
mxsfb_set_mode(struct mxsfb_drm_private * mxsfb,u32 bus_flags)100268a06e1SMarek Vasut static void mxsfb_set_mode(struct mxsfb_drm_private *mxsfb, u32 bus_flags)
101268a06e1SMarek Vasut {
102268a06e1SMarek Vasut struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;
103268a06e1SMarek Vasut u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
104268a06e1SMarek Vasut
105268a06e1SMarek Vasut writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
106268a06e1SMarek Vasut TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
107268a06e1SMarek Vasut mxsfb->base + mxsfb->devdata->transfer_count);
108268a06e1SMarek Vasut
109268a06e1SMarek Vasut vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
110268a06e1SMarek Vasut
111268a06e1SMarek Vasut vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
112268a06e1SMarek Vasut VDCTRL0_VSYNC_PERIOD_UNIT |
113268a06e1SMarek Vasut VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
114268a06e1SMarek Vasut VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
115268a06e1SMarek Vasut if (m->flags & DRM_MODE_FLAG_PHSYNC)
116268a06e1SMarek Vasut vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
117268a06e1SMarek Vasut if (m->flags & DRM_MODE_FLAG_PVSYNC)
118268a06e1SMarek Vasut vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
119268a06e1SMarek Vasut /* Make sure Data Enable is high active by default */
120268a06e1SMarek Vasut if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
121268a06e1SMarek Vasut vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
122268a06e1SMarek Vasut /*
123268a06e1SMarek Vasut * DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
124268a06e1SMarek Vasut * controllers VDCTRL0_DOTCLK is display centric.
125268a06e1SMarek Vasut * Drive on positive edge -> display samples on falling edge
126268a06e1SMarek Vasut * DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
127268a06e1SMarek Vasut */
128268a06e1SMarek Vasut if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
129268a06e1SMarek Vasut vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
130268a06e1SMarek Vasut
131268a06e1SMarek Vasut writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
132268a06e1SMarek Vasut
133268a06e1SMarek Vasut /* Frame length in lines. */
134268a06e1SMarek Vasut writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
135268a06e1SMarek Vasut
136268a06e1SMarek Vasut /* Line length in units of clocks or pixels. */
137268a06e1SMarek Vasut hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
138268a06e1SMarek Vasut writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
139268a06e1SMarek Vasut VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
140268a06e1SMarek Vasut mxsfb->base + LCDC_VDCTRL2);
141268a06e1SMarek Vasut
142268a06e1SMarek Vasut writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
143268a06e1SMarek Vasut SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
144268a06e1SMarek Vasut mxsfb->base + LCDC_VDCTRL3);
145268a06e1SMarek Vasut
146268a06e1SMarek Vasut writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
147268a06e1SMarek Vasut mxsfb->base + LCDC_VDCTRL4);
148268a06e1SMarek Vasut
149268a06e1SMarek Vasut }
150268a06e1SMarek Vasut
mxsfb_enable_controller(struct mxsfb_drm_private * mxsfb)15140a726b8SLaurent Pinchart static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
15240a726b8SLaurent Pinchart {
15340a726b8SLaurent Pinchart u32 reg;
15440a726b8SLaurent Pinchart
15540a726b8SLaurent Pinchart if (mxsfb->clk_disp_axi)
15640a726b8SLaurent Pinchart clk_prepare_enable(mxsfb->clk_disp_axi);
15740a726b8SLaurent Pinchart clk_prepare_enable(mxsfb->clk);
15840a726b8SLaurent Pinchart
1599891cb54SMarek Vasut /* Increase number of outstanding requests on all supported IPs */
1609891cb54SMarek Vasut if (mxsfb->devdata->has_ctrl2) {
1619891cb54SMarek Vasut reg = readl(mxsfb->base + LCDC_V4_CTRL2);
1629891cb54SMarek Vasut reg &= ~CTRL2_SET_OUTSTANDING_REQS_MASK;
1639891cb54SMarek Vasut reg |= CTRL2_SET_OUTSTANDING_REQS_16;
1649891cb54SMarek Vasut writel(reg, mxsfb->base + LCDC_V4_CTRL2);
1659891cb54SMarek Vasut }
1669891cb54SMarek Vasut
16740a726b8SLaurent Pinchart /* If it was disabled, re-enable the mode again */
16840a726b8SLaurent Pinchart writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
16940a726b8SLaurent Pinchart
17040a726b8SLaurent Pinchart /* Enable the SYNC signals first, then the DMA engine */
17140a726b8SLaurent Pinchart reg = readl(mxsfb->base + LCDC_VDCTRL4);
17240a726b8SLaurent Pinchart reg |= VDCTRL4_SYNC_SIGNALS_ON;
17340a726b8SLaurent Pinchart writel(reg, mxsfb->base + LCDC_VDCTRL4);
17440a726b8SLaurent Pinchart
1750c9856e4SMarek Vasut /*
1760c9856e4SMarek Vasut * Enable recovery on underflow.
1770c9856e4SMarek Vasut *
1780c9856e4SMarek Vasut * There is some sort of corner case behavior of the controller,
1790c9856e4SMarek Vasut * which could rarely be triggered at least on i.MX6SX connected
1800c9856e4SMarek Vasut * to 800x480 DPI panel and i.MX8MM connected to DPI->DSI->LVDS
1810c9856e4SMarek Vasut * bridged 1920x1080 panel (and likely on other setups too), where
1820c9856e4SMarek Vasut * the image on the panel shifts to the right and wraps around.
1830c9856e4SMarek Vasut * This happens either when the controller is enabled on boot or
1840c9856e4SMarek Vasut * even later during run time. The condition does not correct
1850c9856e4SMarek Vasut * itself automatically, i.e. the display image remains shifted.
1860c9856e4SMarek Vasut *
1870c9856e4SMarek Vasut * It seems this problem is known and is due to sporadic underflows
1880c9856e4SMarek Vasut * of the LCDIF FIFO. While the LCDIF IP does have underflow/overflow
1890c9856e4SMarek Vasut * IRQs, neither of the IRQs trigger and neither IRQ status bit is
1900c9856e4SMarek Vasut * asserted when this condition occurs.
1910c9856e4SMarek Vasut *
1920c9856e4SMarek Vasut * All known revisions of the LCDIF IP have CTRL1 RECOVER_ON_UNDERFLOW
1930c9856e4SMarek Vasut * bit, which is described in the reference manual since i.MX23 as
1940c9856e4SMarek Vasut * "
1950c9856e4SMarek Vasut * Set this bit to enable the LCDIF block to recover in the next
1960c9856e4SMarek Vasut * field/frame if there was an underflow in the current field/frame.
1970c9856e4SMarek Vasut * "
1980c9856e4SMarek Vasut * Enable this bit to mitigate the sporadic underflows.
1990c9856e4SMarek Vasut */
2000c9856e4SMarek Vasut reg = readl(mxsfb->base + LCDC_CTRL1);
2010c9856e4SMarek Vasut reg |= CTRL1_RECOVER_ON_UNDERFLOW;
2020c9856e4SMarek Vasut writel(reg, mxsfb->base + LCDC_CTRL1);
2030c9856e4SMarek Vasut
20440a726b8SLaurent Pinchart writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
20540a726b8SLaurent Pinchart }
20640a726b8SLaurent Pinchart
mxsfb_disable_controller(struct mxsfb_drm_private * mxsfb)20740a726b8SLaurent Pinchart static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
20840a726b8SLaurent Pinchart {
20940a726b8SLaurent Pinchart u32 reg;
21040a726b8SLaurent Pinchart
21140a726b8SLaurent Pinchart /*
21240a726b8SLaurent Pinchart * Even if we disable the controller here, it will still continue
21340a726b8SLaurent Pinchart * until its FIFOs are running out of data
21440a726b8SLaurent Pinchart */
21540a726b8SLaurent Pinchart writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
21640a726b8SLaurent Pinchart
21740a726b8SLaurent Pinchart readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
21840a726b8SLaurent Pinchart 0, 1000);
21940a726b8SLaurent Pinchart
22040a726b8SLaurent Pinchart reg = readl(mxsfb->base + LCDC_VDCTRL4);
22140a726b8SLaurent Pinchart reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
22240a726b8SLaurent Pinchart writel(reg, mxsfb->base + LCDC_VDCTRL4);
22340a726b8SLaurent Pinchart
22440a726b8SLaurent Pinchart clk_disable_unprepare(mxsfb->clk);
22540a726b8SLaurent Pinchart if (mxsfb->clk_disp_axi)
22640a726b8SLaurent Pinchart clk_disable_unprepare(mxsfb->clk_disp_axi);
22740a726b8SLaurent Pinchart }
22840a726b8SLaurent Pinchart
22940a726b8SLaurent Pinchart /*
23040a726b8SLaurent Pinchart * Clear the bit and poll it cleared. This is usually called with
23140a726b8SLaurent Pinchart * a reset address and mask being either SFTRST(bit 31) or CLKGATE
23240a726b8SLaurent Pinchart * (bit 30).
23340a726b8SLaurent Pinchart */
clear_poll_bit(void __iomem * addr,u32 mask)23440a726b8SLaurent Pinchart static int clear_poll_bit(void __iomem *addr, u32 mask)
23540a726b8SLaurent Pinchart {
23640a726b8SLaurent Pinchart u32 reg;
23740a726b8SLaurent Pinchart
23840a726b8SLaurent Pinchart writel(mask, addr + REG_CLR);
23940a726b8SLaurent Pinchart return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
24040a726b8SLaurent Pinchart }
24140a726b8SLaurent Pinchart
mxsfb_reset_block(struct mxsfb_drm_private * mxsfb)24240a726b8SLaurent Pinchart static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
24340a726b8SLaurent Pinchart {
24440a726b8SLaurent Pinchart int ret;
24540a726b8SLaurent Pinchart
246e518a9dcSMarek Vasut /*
247e518a9dcSMarek Vasut * It seems, you can't re-program the controller if it is still
248e518a9dcSMarek Vasut * running. This may lead to shifted pictures (FIFO issue?), so
249e518a9dcSMarek Vasut * first stop the controller and drain its FIFOs.
250e518a9dcSMarek Vasut */
251e518a9dcSMarek Vasut
25240a726b8SLaurent Pinchart ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
25340a726b8SLaurent Pinchart if (ret)
25440a726b8SLaurent Pinchart return ret;
25540a726b8SLaurent Pinchart
25640a726b8SLaurent Pinchart writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
25740a726b8SLaurent Pinchart
25840a726b8SLaurent Pinchart ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
25940a726b8SLaurent Pinchart if (ret)
26040a726b8SLaurent Pinchart return ret;
26140a726b8SLaurent Pinchart
262e518a9dcSMarek Vasut ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
263e518a9dcSMarek Vasut if (ret)
264e518a9dcSMarek Vasut return ret;
265e518a9dcSMarek Vasut
266e518a9dcSMarek Vasut /* Clear the FIFOs */
267e518a9dcSMarek Vasut writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
268e518a9dcSMarek Vasut readl(mxsfb->base + LCDC_CTRL1);
269e518a9dcSMarek Vasut writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_CLR);
270e518a9dcSMarek Vasut readl(mxsfb->base + LCDC_CTRL1);
271e518a9dcSMarek Vasut
272e518a9dcSMarek Vasut if (mxsfb->devdata->has_overlay)
273e518a9dcSMarek Vasut writel(0, mxsfb->base + LCDC_AS_CTRL);
274e518a9dcSMarek Vasut
275e518a9dcSMarek Vasut return 0;
27640a726b8SLaurent Pinchart }
27740a726b8SLaurent Pinchart
mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private * mxsfb,struct drm_bridge_state * bridge_state,const u32 bus_format)278b776b0f0SMarek Vasut static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb,
27957e6f056SMarek Vasut struct drm_bridge_state *bridge_state,
280b776b0f0SMarek Vasut const u32 bus_format)
28140a726b8SLaurent Pinchart {
282ae1ed009SLaurent Pinchart struct drm_device *drm = mxsfb->crtc.dev;
283ae1ed009SLaurent Pinchart struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;
28440a726b8SLaurent Pinchart u32 bus_flags = mxsfb->connector->display_info.bus_flags;
28540a726b8SLaurent Pinchart int err;
28640a726b8SLaurent Pinchart
28740a726b8SLaurent Pinchart if (mxsfb->bridge && mxsfb->bridge->timings)
28840a726b8SLaurent Pinchart bus_flags = mxsfb->bridge->timings->input_bus_flags;
28957e6f056SMarek Vasut else if (bridge_state)
29057e6f056SMarek Vasut bus_flags = bridge_state->input_bus_cfg.flags;
29140a726b8SLaurent Pinchart
29240a726b8SLaurent Pinchart DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
29340a726b8SLaurent Pinchart m->crtc_clock,
29440a726b8SLaurent Pinchart (int)(clk_get_rate(mxsfb->clk) / 1000));
29540a726b8SLaurent Pinchart DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
29640a726b8SLaurent Pinchart bus_flags);
29740a726b8SLaurent Pinchart DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
29840a726b8SLaurent Pinchart
2993604f1e5SMarek Vasut /* Mandatory eLCDIF reset as per the Reference Manual */
3003604f1e5SMarek Vasut err = mxsfb_reset_block(mxsfb);
3013604f1e5SMarek Vasut if (err)
3023604f1e5SMarek Vasut return;
3033604f1e5SMarek Vasut
3043604f1e5SMarek Vasut mxsfb_set_formats(mxsfb, bus_format);
3053604f1e5SMarek Vasut
3063604f1e5SMarek Vasut clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
3073604f1e5SMarek Vasut
308268a06e1SMarek Vasut mxsfb_set_mode(mxsfb, bus_flags);
30940a726b8SLaurent Pinchart }
31040a726b8SLaurent Pinchart
mxsfb_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)311ae1ed009SLaurent Pinchart static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
31229b77ad7SMaxime Ripard struct drm_atomic_state *state)
31340a726b8SLaurent Pinchart {
31429b77ad7SMaxime Ripard struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
31529b77ad7SMaxime Ripard crtc);
31629b77ad7SMaxime Ripard bool has_primary = crtc_state->plane_mask &
317ae1ed009SLaurent Pinchart drm_plane_mask(crtc->primary);
318ae1ed009SLaurent Pinchart
319ae1ed009SLaurent Pinchart /* The primary plane has to be enabled when the CRTC is active. */
32029b77ad7SMaxime Ripard if (crtc_state->active && !has_primary)
321ae1ed009SLaurent Pinchart return -EINVAL;
322ae1ed009SLaurent Pinchart
323ae1ed009SLaurent Pinchart /* TODO: Is this needed ? */
324d74252bbSMaxime Ripard return drm_atomic_add_affected_planes(state, crtc);
325ae1ed009SLaurent Pinchart }
326ae1ed009SLaurent Pinchart
mxsfb_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)3271e5d7963SLaurent Pinchart static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
328f6ebe9f9SMaxime Ripard struct drm_atomic_state *state)
3291e5d7963SLaurent Pinchart {
3301e5d7963SLaurent Pinchart struct drm_pending_vblank_event *event;
3311e5d7963SLaurent Pinchart
3321e5d7963SLaurent Pinchart event = crtc->state->event;
3331e5d7963SLaurent Pinchart crtc->state->event = NULL;
3341e5d7963SLaurent Pinchart
3351e5d7963SLaurent Pinchart if (!event)
3361e5d7963SLaurent Pinchart return;
3371e5d7963SLaurent Pinchart
3381e5d7963SLaurent Pinchart spin_lock_irq(&crtc->dev->event_lock);
3391e5d7963SLaurent Pinchart if (drm_crtc_vblank_get(crtc) == 0)
3401e5d7963SLaurent Pinchart drm_crtc_arm_vblank_event(crtc, event);
3411e5d7963SLaurent Pinchart else
3421e5d7963SLaurent Pinchart drm_crtc_send_vblank_event(crtc, event);
3431e5d7963SLaurent Pinchart spin_unlock_irq(&crtc->dev->event_lock);
3441e5d7963SLaurent Pinchart }
3451e5d7963SLaurent Pinchart
mxsfb_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)346ae1ed009SLaurent Pinchart static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
347351f950dSMaxime Ripard struct drm_atomic_state *state)
348ae1ed009SLaurent Pinchart {
349ae1ed009SLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
350cb285a53SMarek Vasut struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
351cb285a53SMarek Vasut crtc->primary);
35257e6f056SMarek Vasut struct drm_bridge_state *bridge_state = NULL;
353ae1ed009SLaurent Pinchart struct drm_device *drm = mxsfb->drm;
354b776b0f0SMarek Vasut u32 bus_format = 0;
3558c30eeccSDanilo Krummrich dma_addr_t dma_addr;
35640a726b8SLaurent Pinchart
357ae1ed009SLaurent Pinchart pm_runtime_get_sync(drm->dev);
35840a726b8SLaurent Pinchart mxsfb_enable_axi_clk(mxsfb);
359b9f59376SLaurent Pinchart
360b9f59376SLaurent Pinchart drm_crtc_vblank_on(crtc);
361b9f59376SLaurent Pinchart
362b776b0f0SMarek Vasut /* If there is a bridge attached to the LCDIF, use its bus format */
363b776b0f0SMarek Vasut if (mxsfb->bridge) {
364b776b0f0SMarek Vasut bridge_state =
365b776b0f0SMarek Vasut drm_atomic_get_new_bridge_state(state,
366b776b0f0SMarek Vasut mxsfb->bridge);
367622c9a3aSAlexander Stein if (!bridge_state)
368622c9a3aSAlexander Stein bus_format = MEDIA_BUS_FMT_FIXED;
369622c9a3aSAlexander Stein else
370b776b0f0SMarek Vasut bus_format = bridge_state->input_bus_cfg.format;
371622c9a3aSAlexander Stein
3721db06050SGuido Günther if (bus_format == MEDIA_BUS_FMT_FIXED) {
3731db06050SGuido Günther dev_warn_once(drm->dev,
3741db06050SGuido Günther "Bridge does not provide bus format, assuming MEDIA_BUS_FMT_RGB888_1X24.\n"
3751db06050SGuido Günther "Please fix bridge driver by handling atomic_get_input_bus_fmts.\n");
3761db06050SGuido Günther bus_format = MEDIA_BUS_FMT_RGB888_1X24;
3771db06050SGuido Günther }
378b776b0f0SMarek Vasut }
379b776b0f0SMarek Vasut
380b776b0f0SMarek Vasut /* If there is no bridge, use bus format from connector */
381b776b0f0SMarek Vasut if (!bus_format && mxsfb->connector->display_info.num_bus_formats)
382b776b0f0SMarek Vasut bus_format = mxsfb->connector->display_info.bus_formats[0];
383b776b0f0SMarek Vasut
384b776b0f0SMarek Vasut /* If all else fails, default to RGB888_1X24 */
385b776b0f0SMarek Vasut if (!bus_format)
386b776b0f0SMarek Vasut bus_format = MEDIA_BUS_FMT_RGB888_1X24;
387b776b0f0SMarek Vasut
38857e6f056SMarek Vasut mxsfb_crtc_mode_set_nofb(mxsfb, bridge_state, bus_format);
38940a726b8SLaurent Pinchart
39040a726b8SLaurent Pinchart /* Write cur_buf as well to avoid an initial corrupt frame */
3918c30eeccSDanilo Krummrich dma_addr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);
3928c30eeccSDanilo Krummrich if (dma_addr) {
3938c30eeccSDanilo Krummrich writel(dma_addr, mxsfb->base + mxsfb->devdata->cur_buf);
3948c30eeccSDanilo Krummrich writel(dma_addr, mxsfb->base + mxsfb->devdata->next_buf);
39540a726b8SLaurent Pinchart }
39640a726b8SLaurent Pinchart
39740a726b8SLaurent Pinchart mxsfb_enable_controller(mxsfb);
39840a726b8SLaurent Pinchart }
39940a726b8SLaurent Pinchart
mxsfb_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)400ae1ed009SLaurent Pinchart static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
401351f950dSMaxime Ripard struct drm_atomic_state *state)
40240a726b8SLaurent Pinchart {
403ae1ed009SLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
404ae1ed009SLaurent Pinchart struct drm_device *drm = mxsfb->drm;
405ae1ed009SLaurent Pinchart struct drm_pending_vblank_event *event;
406ae1ed009SLaurent Pinchart
40740a726b8SLaurent Pinchart mxsfb_disable_controller(mxsfb);
408ae1ed009SLaurent Pinchart
409ae1ed009SLaurent Pinchart spin_lock_irq(&drm->event_lock);
410ae1ed009SLaurent Pinchart event = crtc->state->event;
411ae1ed009SLaurent Pinchart if (event) {
412ae1ed009SLaurent Pinchart crtc->state->event = NULL;
413ae1ed009SLaurent Pinchart drm_crtc_send_vblank_event(crtc, event);
414ae1ed009SLaurent Pinchart }
415ae1ed009SLaurent Pinchart spin_unlock_irq(&drm->event_lock);
41607b7fd77SLaurent Pinchart
417b9f59376SLaurent Pinchart drm_crtc_vblank_off(crtc);
418b9f59376SLaurent Pinchart
41907b7fd77SLaurent Pinchart mxsfb_disable_axi_clk(mxsfb);
42007b7fd77SLaurent Pinchart pm_runtime_put_sync(drm->dev);
421ae1ed009SLaurent Pinchart }
422ae1ed009SLaurent Pinchart
mxsfb_crtc_enable_vblank(struct drm_crtc * crtc)423ae1ed009SLaurent Pinchart static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc)
424ae1ed009SLaurent Pinchart {
425ae1ed009SLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
426ae1ed009SLaurent Pinchart
427ae1ed009SLaurent Pinchart /* Clear and enable VBLANK IRQ */
428ae1ed009SLaurent Pinchart writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
429ae1ed009SLaurent Pinchart writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
430ae1ed009SLaurent Pinchart
431ae1ed009SLaurent Pinchart return 0;
432ae1ed009SLaurent Pinchart }
433ae1ed009SLaurent Pinchart
mxsfb_crtc_disable_vblank(struct drm_crtc * crtc)434ae1ed009SLaurent Pinchart static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
435ae1ed009SLaurent Pinchart {
436ae1ed009SLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
437ae1ed009SLaurent Pinchart
438ae1ed009SLaurent Pinchart /* Disable and clear VBLANK IRQ */
439ae1ed009SLaurent Pinchart writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
440ae1ed009SLaurent Pinchart writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
44140a726b8SLaurent Pinchart }
44240a726b8SLaurent Pinchart
mxsfb_crtc_set_crc_source(struct drm_crtc * crtc,const char * source)44305ecc678SMarek Vasut static int mxsfb_crtc_set_crc_source(struct drm_crtc *crtc, const char *source)
44405ecc678SMarek Vasut {
44505ecc678SMarek Vasut struct mxsfb_drm_private *mxsfb;
44605ecc678SMarek Vasut
44705ecc678SMarek Vasut if (!crtc)
44805ecc678SMarek Vasut return -ENODEV;
44905ecc678SMarek Vasut
45005ecc678SMarek Vasut mxsfb = to_mxsfb_drm_private(crtc->dev);
45105ecc678SMarek Vasut
45205ecc678SMarek Vasut if (source && strcmp(source, "auto") == 0)
45305ecc678SMarek Vasut mxsfb->crc_active = true;
45405ecc678SMarek Vasut else if (!source)
45505ecc678SMarek Vasut mxsfb->crc_active = false;
45605ecc678SMarek Vasut else
45705ecc678SMarek Vasut return -EINVAL;
45805ecc678SMarek Vasut
45905ecc678SMarek Vasut return 0;
46005ecc678SMarek Vasut }
46105ecc678SMarek Vasut
mxsfb_crtc_verify_crc_source(struct drm_crtc * crtc,const char * source,size_t * values_cnt)46205ecc678SMarek Vasut static int mxsfb_crtc_verify_crc_source(struct drm_crtc *crtc,
46305ecc678SMarek Vasut const char *source, size_t *values_cnt)
46405ecc678SMarek Vasut {
46505ecc678SMarek Vasut if (!crtc)
46605ecc678SMarek Vasut return -ENODEV;
46705ecc678SMarek Vasut
46805ecc678SMarek Vasut if (source && strcmp(source, "auto") != 0) {
46905ecc678SMarek Vasut DRM_DEBUG_DRIVER("Unknown CRC source %s for %s\n",
47005ecc678SMarek Vasut source, crtc->name);
47105ecc678SMarek Vasut return -EINVAL;
47205ecc678SMarek Vasut }
47305ecc678SMarek Vasut
47405ecc678SMarek Vasut *values_cnt = 1;
47505ecc678SMarek Vasut return 0;
47605ecc678SMarek Vasut }
47705ecc678SMarek Vasut
478ae1ed009SLaurent Pinchart static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
479ae1ed009SLaurent Pinchart .atomic_check = mxsfb_crtc_atomic_check,
4801e5d7963SLaurent Pinchart .atomic_flush = mxsfb_crtc_atomic_flush,
481ae1ed009SLaurent Pinchart .atomic_enable = mxsfb_crtc_atomic_enable,
482ae1ed009SLaurent Pinchart .atomic_disable = mxsfb_crtc_atomic_disable,
483ae1ed009SLaurent Pinchart };
484ae1ed009SLaurent Pinchart
485ae1ed009SLaurent Pinchart static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
486ae1ed009SLaurent Pinchart .reset = drm_atomic_helper_crtc_reset,
487ae1ed009SLaurent Pinchart .destroy = drm_crtc_cleanup,
488ae1ed009SLaurent Pinchart .set_config = drm_atomic_helper_set_config,
489ae1ed009SLaurent Pinchart .page_flip = drm_atomic_helper_page_flip,
490ae1ed009SLaurent Pinchart .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
491ae1ed009SLaurent Pinchart .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
492ae1ed009SLaurent Pinchart .enable_vblank = mxsfb_crtc_enable_vblank,
493ae1ed009SLaurent Pinchart .disable_vblank = mxsfb_crtc_disable_vblank,
494ae1ed009SLaurent Pinchart };
495ae1ed009SLaurent Pinchart
49605ecc678SMarek Vasut static const struct drm_crtc_funcs mxsfb_crtc_with_crc_funcs = {
49705ecc678SMarek Vasut .reset = drm_atomic_helper_crtc_reset,
49805ecc678SMarek Vasut .destroy = drm_crtc_cleanup,
49905ecc678SMarek Vasut .set_config = drm_atomic_helper_set_config,
50005ecc678SMarek Vasut .page_flip = drm_atomic_helper_page_flip,
50105ecc678SMarek Vasut .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
50205ecc678SMarek Vasut .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
50305ecc678SMarek Vasut .enable_vblank = mxsfb_crtc_enable_vblank,
50405ecc678SMarek Vasut .disable_vblank = mxsfb_crtc_disable_vblank,
50505ecc678SMarek Vasut .set_crc_source = mxsfb_crtc_set_crc_source,
50605ecc678SMarek Vasut .verify_crc_source = mxsfb_crtc_verify_crc_source,
50705ecc678SMarek Vasut };
50805ecc678SMarek Vasut
509ae1ed009SLaurent Pinchart /* -----------------------------------------------------------------------------
510ae1ed009SLaurent Pinchart * Encoder
511ae1ed009SLaurent Pinchart */
512ae1ed009SLaurent Pinchart
513ae1ed009SLaurent Pinchart static const struct drm_encoder_funcs mxsfb_encoder_funcs = {
514ae1ed009SLaurent Pinchart .destroy = drm_encoder_cleanup,
515ae1ed009SLaurent Pinchart };
516ae1ed009SLaurent Pinchart
517ae1ed009SLaurent Pinchart /* -----------------------------------------------------------------------------
518ae1ed009SLaurent Pinchart * Planes
519ae1ed009SLaurent Pinchart */
520ae1ed009SLaurent Pinchart
mxsfb_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)521ae1ed009SLaurent Pinchart static int mxsfb_plane_atomic_check(struct drm_plane *plane,
5227c11b99aSMaxime Ripard struct drm_atomic_state *state)
52340a726b8SLaurent Pinchart {
5247c11b99aSMaxime Ripard struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
5257c11b99aSMaxime Ripard plane);
526ae1ed009SLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
527ae1ed009SLaurent Pinchart struct drm_crtc_state *crtc_state;
528ae1ed009SLaurent Pinchart
529dec92020SMaxime Ripard crtc_state = drm_atomic_get_new_crtc_state(state,
530ae1ed009SLaurent Pinchart &mxsfb->crtc);
531ae1ed009SLaurent Pinchart
532ae1ed009SLaurent Pinchart return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
533cce32e4eSThomas Zimmermann DRM_PLANE_NO_SCALING,
534cce32e4eSThomas Zimmermann DRM_PLANE_NO_SCALING,
535ae1ed009SLaurent Pinchart false, true);
536ae1ed009SLaurent Pinchart }
537ae1ed009SLaurent Pinchart
mxsfb_plane_primary_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)53863aa581cSLaurent Pinchart static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
539977697e2SMaxime Ripard struct drm_atomic_state *state)
540ae1ed009SLaurent Pinchart {
541ae1ed009SLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
542cb285a53SMarek Vasut struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
543cb285a53SMarek Vasut plane);
5448c30eeccSDanilo Krummrich dma_addr_t dma_addr;
54540a726b8SLaurent Pinchart
5468c30eeccSDanilo Krummrich dma_addr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);
5478c30eeccSDanilo Krummrich if (dma_addr)
5488c30eeccSDanilo Krummrich writel(dma_addr, mxsfb->base + mxsfb->devdata->next_buf);
54940a726b8SLaurent Pinchart }
550ae1ed009SLaurent Pinchart
mxsfb_plane_overlay_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)55163aa581cSLaurent Pinchart static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
552977697e2SMaxime Ripard struct drm_atomic_state *state)
55363aa581cSLaurent Pinchart {
554977697e2SMaxime Ripard struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state,
555977697e2SMaxime Ripard plane);
55663aa581cSLaurent Pinchart struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
55737418bf1SMaxime Ripard struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
55837418bf1SMaxime Ripard plane);
5598c30eeccSDanilo Krummrich dma_addr_t dma_addr;
56063aa581cSLaurent Pinchart u32 ctrl;
56163aa581cSLaurent Pinchart
5628c30eeccSDanilo Krummrich dma_addr = drm_fb_dma_get_gem_addr(new_pstate->fb, new_pstate, 0);
5638c30eeccSDanilo Krummrich if (!dma_addr) {
56463aa581cSLaurent Pinchart writel(0, mxsfb->base + LCDC_AS_CTRL);
56563aa581cSLaurent Pinchart return;
56663aa581cSLaurent Pinchart }
56763aa581cSLaurent Pinchart
56863aa581cSLaurent Pinchart /*
56963aa581cSLaurent Pinchart * HACK: The hardware seems to output 64 bytes of data of unknown
57063aa581cSLaurent Pinchart * origin, and then to proceed with the framebuffer. Until the reason
57163aa581cSLaurent Pinchart * is understood, live with the 16 initial invalid pixels on the first
57263aa581cSLaurent Pinchart * line and start 64 bytes within the framebuffer.
57363aa581cSLaurent Pinchart */
5748c30eeccSDanilo Krummrich dma_addr += 64;
57563aa581cSLaurent Pinchart
5768c30eeccSDanilo Krummrich writel(dma_addr, mxsfb->base + LCDC_AS_NEXT_BUF);
57763aa581cSLaurent Pinchart
57863aa581cSLaurent Pinchart /*
57963aa581cSLaurent Pinchart * If the plane was previously disabled, write LCDC_AS_BUF as well to
58063aa581cSLaurent Pinchart * provide the first buffer.
58163aa581cSLaurent Pinchart */
58263aa581cSLaurent Pinchart if (!old_pstate->fb)
5838c30eeccSDanilo Krummrich writel(dma_addr, mxsfb->base + LCDC_AS_BUF);
58463aa581cSLaurent Pinchart
58563aa581cSLaurent Pinchart ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
58663aa581cSLaurent Pinchart
58741016fe1SMaxime Ripard switch (new_pstate->fb->format->format) {
58863aa581cSLaurent Pinchart case DRM_FORMAT_XRGB4444:
58963aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
59063aa581cSLaurent Pinchart break;
59163aa581cSLaurent Pinchart case DRM_FORMAT_ARGB4444:
59263aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
59363aa581cSLaurent Pinchart break;
59463aa581cSLaurent Pinchart case DRM_FORMAT_XRGB1555:
59563aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
59663aa581cSLaurent Pinchart break;
59763aa581cSLaurent Pinchart case DRM_FORMAT_ARGB1555:
59863aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
59963aa581cSLaurent Pinchart break;
60063aa581cSLaurent Pinchart case DRM_FORMAT_RGB565:
60163aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
60263aa581cSLaurent Pinchart break;
60363aa581cSLaurent Pinchart case DRM_FORMAT_XRGB8888:
60463aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
60563aa581cSLaurent Pinchart break;
60663aa581cSLaurent Pinchart case DRM_FORMAT_ARGB8888:
60763aa581cSLaurent Pinchart ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
60863aa581cSLaurent Pinchart break;
60963aa581cSLaurent Pinchart }
61063aa581cSLaurent Pinchart
61163aa581cSLaurent Pinchart writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
61263aa581cSLaurent Pinchart }
61363aa581cSLaurent Pinchart
mxsfb_plane_overlay_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)614*aa656d48SLiu Ying static void mxsfb_plane_overlay_atomic_disable(struct drm_plane *plane,
615*aa656d48SLiu Ying struct drm_atomic_state *state)
616*aa656d48SLiu Ying {
617*aa656d48SLiu Ying struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
618*aa656d48SLiu Ying
619*aa656d48SLiu Ying writel(0, mxsfb->base + LCDC_AS_CTRL);
620*aa656d48SLiu Ying }
621*aa656d48SLiu Ying
mxsfb_format_mod_supported(struct drm_plane * plane,uint32_t format,uint64_t modifier)622c70582bbSDaniel Abrecht static bool mxsfb_format_mod_supported(struct drm_plane *plane,
623c70582bbSDaniel Abrecht uint32_t format,
624c70582bbSDaniel Abrecht uint64_t modifier)
625c70582bbSDaniel Abrecht {
626c70582bbSDaniel Abrecht return modifier == DRM_FORMAT_MOD_LINEAR;
627c70582bbSDaniel Abrecht }
628c70582bbSDaniel Abrecht
62963aa581cSLaurent Pinchart static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
630ae1ed009SLaurent Pinchart .atomic_check = mxsfb_plane_atomic_check,
63163aa581cSLaurent Pinchart .atomic_update = mxsfb_plane_primary_atomic_update,
63263aa581cSLaurent Pinchart };
63363aa581cSLaurent Pinchart
63463aa581cSLaurent Pinchart static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
63563aa581cSLaurent Pinchart .atomic_check = mxsfb_plane_atomic_check,
63663aa581cSLaurent Pinchart .atomic_update = mxsfb_plane_overlay_atomic_update,
637*aa656d48SLiu Ying .atomic_disable = mxsfb_plane_overlay_atomic_disable,
638ae1ed009SLaurent Pinchart };
639ae1ed009SLaurent Pinchart
640ae1ed009SLaurent Pinchart static const struct drm_plane_funcs mxsfb_plane_funcs = {
641c70582bbSDaniel Abrecht .format_mod_supported = mxsfb_format_mod_supported,
642ae1ed009SLaurent Pinchart .update_plane = drm_atomic_helper_update_plane,
643ae1ed009SLaurent Pinchart .disable_plane = drm_atomic_helper_disable_plane,
644ae1ed009SLaurent Pinchart .destroy = drm_plane_cleanup,
645ae1ed009SLaurent Pinchart .reset = drm_atomic_helper_plane_reset,
646ae1ed009SLaurent Pinchart .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
647ae1ed009SLaurent Pinchart .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
648ae1ed009SLaurent Pinchart };
649ae1ed009SLaurent Pinchart
65063aa581cSLaurent Pinchart static const uint32_t mxsfb_primary_plane_formats[] = {
65163aa581cSLaurent Pinchart DRM_FORMAT_RGB565,
652ae1ed009SLaurent Pinchart DRM_FORMAT_XRGB8888,
65363aa581cSLaurent Pinchart };
65463aa581cSLaurent Pinchart
65563aa581cSLaurent Pinchart static const uint32_t mxsfb_overlay_plane_formats[] = {
65663aa581cSLaurent Pinchart DRM_FORMAT_XRGB4444,
65763aa581cSLaurent Pinchart DRM_FORMAT_ARGB4444,
65863aa581cSLaurent Pinchart DRM_FORMAT_XRGB1555,
65963aa581cSLaurent Pinchart DRM_FORMAT_ARGB1555,
66063aa581cSLaurent Pinchart DRM_FORMAT_RGB565,
66163aa581cSLaurent Pinchart DRM_FORMAT_XRGB8888,
66263aa581cSLaurent Pinchart DRM_FORMAT_ARGB8888,
663ae1ed009SLaurent Pinchart };
664ae1ed009SLaurent Pinchart
665ae1ed009SLaurent Pinchart static const uint64_t mxsfb_modifiers[] = {
666ae1ed009SLaurent Pinchart DRM_FORMAT_MOD_LINEAR,
667ae1ed009SLaurent Pinchart DRM_FORMAT_MOD_INVALID
668ae1ed009SLaurent Pinchart };
669ae1ed009SLaurent Pinchart
67063aa581cSLaurent Pinchart /* -----------------------------------------------------------------------------
67163aa581cSLaurent Pinchart * Initialization
67263aa581cSLaurent Pinchart */
67363aa581cSLaurent Pinchart
mxsfb_kms_init(struct mxsfb_drm_private * mxsfb)674ae1ed009SLaurent Pinchart int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
675ae1ed009SLaurent Pinchart {
676ae1ed009SLaurent Pinchart struct drm_encoder *encoder = &mxsfb->encoder;
677ae1ed009SLaurent Pinchart struct drm_crtc *crtc = &mxsfb->crtc;
678ae1ed009SLaurent Pinchart int ret;
679ae1ed009SLaurent Pinchart
68063aa581cSLaurent Pinchart drm_plane_helper_add(&mxsfb->planes.primary,
68163aa581cSLaurent Pinchart &mxsfb_plane_primary_helper_funcs);
68263aa581cSLaurent Pinchart ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
68363aa581cSLaurent Pinchart &mxsfb_plane_funcs,
68463aa581cSLaurent Pinchart mxsfb_primary_plane_formats,
68563aa581cSLaurent Pinchart ARRAY_SIZE(mxsfb_primary_plane_formats),
686ae1ed009SLaurent Pinchart mxsfb_modifiers, DRM_PLANE_TYPE_PRIMARY,
687ae1ed009SLaurent Pinchart NULL);
688ae1ed009SLaurent Pinchart if (ret)
689ae1ed009SLaurent Pinchart return ret;
690ae1ed009SLaurent Pinchart
69163aa581cSLaurent Pinchart if (mxsfb->devdata->has_overlay) {
69263aa581cSLaurent Pinchart drm_plane_helper_add(&mxsfb->planes.overlay,
69363aa581cSLaurent Pinchart &mxsfb_plane_overlay_helper_funcs);
69463aa581cSLaurent Pinchart ret = drm_universal_plane_init(mxsfb->drm,
69563aa581cSLaurent Pinchart &mxsfb->planes.overlay, 1,
69663aa581cSLaurent Pinchart &mxsfb_plane_funcs,
69763aa581cSLaurent Pinchart mxsfb_overlay_plane_formats,
69863aa581cSLaurent Pinchart ARRAY_SIZE(mxsfb_overlay_plane_formats),
69963aa581cSLaurent Pinchart mxsfb_modifiers, DRM_PLANE_TYPE_OVERLAY,
70063aa581cSLaurent Pinchart NULL);
70163aa581cSLaurent Pinchart if (ret)
70263aa581cSLaurent Pinchart return ret;
70363aa581cSLaurent Pinchart }
70463aa581cSLaurent Pinchart
705ae1ed009SLaurent Pinchart drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
70605ecc678SMarek Vasut if (mxsfb->devdata->has_crc32) {
70705ecc678SMarek Vasut ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
70805ecc678SMarek Vasut &mxsfb->planes.primary, NULL,
70905ecc678SMarek Vasut &mxsfb_crtc_with_crc_funcs,
71005ecc678SMarek Vasut NULL);
71105ecc678SMarek Vasut } else {
71263aa581cSLaurent Pinchart ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
71363aa581cSLaurent Pinchart &mxsfb->planes.primary, NULL,
714ae1ed009SLaurent Pinchart &mxsfb_crtc_funcs, NULL);
71505ecc678SMarek Vasut }
716ae1ed009SLaurent Pinchart if (ret)
717ae1ed009SLaurent Pinchart return ret;
718ae1ed009SLaurent Pinchart
719ae1ed009SLaurent Pinchart encoder->possible_crtcs = drm_crtc_mask(crtc);
720ae1ed009SLaurent Pinchart return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,
721ae1ed009SLaurent Pinchart DRM_MODE_ENCODER_NONE, NULL);
722ae1ed009SLaurent Pinchart }
723