xref: /openbmc/linux/drivers/gpu/drm/mxsfb/mxsfb_kms.c (revision aa656d48)
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