blob: 05fc78fa025db13e107e3909d7331e47f0eaf9f1 [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Marek Vasut45d59d72016-08-18 20:23:01 +02002/*
3 * Copyright (C) 2016 Marek Vasut <marex@denx.de>
4 *
5 * This code is based on drivers/video/fbdev/mxsfb.c :
6 * Copyright (C) 2010 Juergen Beisert, Pengutronix
7 * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
8 * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
Marek Vasut45d59d72016-08-18 20:23:01 +02009 */
10
Sam Ravnborgd5742c62019-06-30 08:18:54 +020011#include <linux/clk.h>
Laurent Pinchartae1ed002020-07-27 05:06:43 +030012#include <linux/io.h>
Sam Ravnborgd5742c62019-06-30 08:18:54 +020013#include <linux/iopoll.h>
Laurent Pinchartae1ed002020-07-27 05:06:43 +030014#include <linux/pm_runtime.h>
Laurent Pinchartf16a0082020-07-27 05:06:41 +030015#include <linux/spinlock.h>
Sam Ravnborgd5742c62019-06-30 08:18:54 +020016
Laurent Pinchartae1ed002020-07-27 05:06:43 +030017#include <drm/drm_atomic.h>
18#include <drm/drm_atomic_helper.h>
Laurent Pinchartf16a0082020-07-27 05:06:41 +030019#include <drm/drm_bridge.h>
Marek Vasut45d59d72016-08-18 20:23:01 +020020#include <drm/drm_crtc.h>
Laurent Pinchartae1ed002020-07-27 05:06:43 +030021#include <drm/drm_encoder.h>
Marek Vasut45d59d72016-08-18 20:23:01 +020022#include <drm/drm_fb_cma_helper.h>
Laurent Pinchartf16a0082020-07-27 05:06:41 +030023#include <drm/drm_fourcc.h>
Thomas Zimmermann820c1702021-02-22 15:17:56 +010024#include <drm/drm_gem_atomic_helper.h>
Marek Vasut45d59d72016-08-18 20:23:01 +020025#include <drm/drm_gem_cma_helper.h>
Laurent Pinchartae1ed002020-07-27 05:06:43 +030026#include <drm/drm_plane.h>
27#include <drm/drm_plane_helper.h>
Sam Ravnborgd5742c62019-06-30 08:18:54 +020028#include <drm/drm_vblank.h>
Marek Vasut45d59d72016-08-18 20:23:01 +020029
30#include "mxsfb_drv.h"
31#include "mxsfb_regs.h"
32
Fabio Estevam0f933322017-05-05 15:01:41 -030033/* 1 second delay should be plenty of time for block reset */
34#define RESET_TIMEOUT 1000000
35
Laurent Pinchartae1ed002020-07-27 05:06:43 +030036/* -----------------------------------------------------------------------------
37 * CRTC
38 */
39
Marek Vasut45d59d72016-08-18 20:23:01 +020040static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val)
41{
42 return (val & mxsfb->devdata->hs_wdth_mask) <<
43 mxsfb->devdata->hs_wdth_shift;
44}
45
Laurent Pinchart51b777f2020-07-27 05:06:52 +030046/*
47 * Setup the MXSFB registers for decoding the pixels out of the framebuffer and
48 * outputting them on the bus.
49 */
50static void mxsfb_set_formats(struct mxsfb_drm_private *mxsfb)
Marek Vasut45d59d72016-08-18 20:23:01 +020051{
Laurent Pinchartae1ed002020-07-27 05:06:43 +030052 struct drm_device *drm = mxsfb->drm;
53 const u32 format = mxsfb->crtc.primary->state->fb->format->format;
Laurent Pinchart51b777f2020-07-27 05:06:52 +030054 u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24;
Marek Vasut45d59d72016-08-18 20:23:01 +020055 u32 ctrl, ctrl1;
56
Laurent Pinchart51b777f2020-07-27 05:06:52 +030057 if (mxsfb->connector->display_info.num_bus_formats)
58 bus_format = mxsfb->connector->display_info.bus_formats[0];
59
60 DRM_DEV_DEBUG_DRIVER(drm->dev, "Using bus_format: 0x%08X\n",
61 bus_format);
62
Marek Vasut45d59d72016-08-18 20:23:01 +020063 ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER;
64
Marek Vasut45d59d72016-08-18 20:23:01 +020065 /* CTRL1 contains IRQ config and status bits, preserve those. */
66 ctrl1 = readl(mxsfb->base + LCDC_CTRL1);
67 ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ;
68
69 switch (format) {
70 case DRM_FORMAT_RGB565:
71 dev_dbg(drm->dev, "Setting up RGB565 mode\n");
Laurent Pinchart8a460062020-07-27 05:06:37 +030072 ctrl |= CTRL_WORD_LENGTH_16;
Marek Vasut45d59d72016-08-18 20:23:01 +020073 ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf);
74 break;
75 case DRM_FORMAT_XRGB8888:
76 dev_dbg(drm->dev, "Setting up XRGB8888 mode\n");
Laurent Pinchart8a460062020-07-27 05:06:37 +030077 ctrl |= CTRL_WORD_LENGTH_24;
Marek Vasut45d59d72016-08-18 20:23:01 +020078 /* Do not use packed pixels = one pixel per word instead. */
79 ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7);
80 break;
Marek Vasut45d59d72016-08-18 20:23:01 +020081 }
82
Stefan Agner10f28892016-12-14 17:28:41 -080083 switch (bus_format) {
84 case MEDIA_BUS_FMT_RGB565_1X16:
Laurent Pinchart51b777f2020-07-27 05:06:52 +030085 ctrl |= CTRL_BUS_WIDTH_16;
Stefan Agner10f28892016-12-14 17:28:41 -080086 break;
87 case MEDIA_BUS_FMT_RGB666_1X18:
Laurent Pinchart51b777f2020-07-27 05:06:52 +030088 ctrl |= CTRL_BUS_WIDTH_18;
Stefan Agner10f28892016-12-14 17:28:41 -080089 break;
90 case MEDIA_BUS_FMT_RGB888_1X24:
Laurent Pinchart51b777f2020-07-27 05:06:52 +030091 ctrl |= CTRL_BUS_WIDTH_24;
Stefan Agner10f28892016-12-14 17:28:41 -080092 break;
93 default:
94 dev_err(drm->dev, "Unknown media bus format %d\n", bus_format);
95 break;
96 }
Laurent Pinchart51b777f2020-07-27 05:06:52 +030097
98 writel(ctrl1, mxsfb->base + LCDC_CTRL1);
99 writel(ctrl, mxsfb->base + LCDC_CTRL);
Stefan Agner10f28892016-12-14 17:28:41 -0800100}
101
Marek Vasut45d59d72016-08-18 20:23:01 +0200102static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb)
103{
104 u32 reg;
105
106 if (mxsfb->clk_disp_axi)
107 clk_prepare_enable(mxsfb->clk_disp_axi);
108 clk_prepare_enable(mxsfb->clk);
Marek Vasut45d59d72016-08-18 20:23:01 +0200109
110 /* If it was disabled, re-enable the mode again */
111 writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET);
112
113 /* Enable the SYNC signals first, then the DMA engine */
114 reg = readl(mxsfb->base + LCDC_VDCTRL4);
115 reg |= VDCTRL4_SYNC_SIGNALS_ON;
116 writel(reg, mxsfb->base + LCDC_VDCTRL4);
117
Marek Vasut0c9856e2021-06-21 00:47:01 +0200118 /*
119 * Enable recovery on underflow.
120 *
121 * There is some sort of corner case behavior of the controller,
122 * which could rarely be triggered at least on i.MX6SX connected
123 * to 800x480 DPI panel and i.MX8MM connected to DPI->DSI->LVDS
124 * bridged 1920x1080 panel (and likely on other setups too), where
125 * the image on the panel shifts to the right and wraps around.
126 * This happens either when the controller is enabled on boot or
127 * even later during run time. The condition does not correct
128 * itself automatically, i.e. the display image remains shifted.
129 *
130 * It seems this problem is known and is due to sporadic underflows
131 * of the LCDIF FIFO. While the LCDIF IP does have underflow/overflow
132 * IRQs, neither of the IRQs trigger and neither IRQ status bit is
133 * asserted when this condition occurs.
134 *
135 * All known revisions of the LCDIF IP have CTRL1 RECOVER_ON_UNDERFLOW
136 * bit, which is described in the reference manual since i.MX23 as
137 * "
138 * Set this bit to enable the LCDIF block to recover in the next
139 * field/frame if there was an underflow in the current field/frame.
140 * "
141 * Enable this bit to mitigate the sporadic underflows.
142 */
143 reg = readl(mxsfb->base + LCDC_CTRL1);
144 reg |= CTRL1_RECOVER_ON_UNDERFLOW;
145 writel(reg, mxsfb->base + LCDC_CTRL1);
146
Marek Vasut45d59d72016-08-18 20:23:01 +0200147 writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET);
148}
149
150static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb)
151{
152 u32 reg;
153
154 /*
155 * Even if we disable the controller here, it will still continue
156 * until its FIFOs are running out of data
157 */
158 writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR);
159
160 readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN),
161 0, 1000);
162
163 reg = readl(mxsfb->base + LCDC_VDCTRL4);
164 reg &= ~VDCTRL4_SYNC_SIGNALS_ON;
165 writel(reg, mxsfb->base + LCDC_VDCTRL4);
166
Marek Vasut45d59d72016-08-18 20:23:01 +0200167 clk_disable_unprepare(mxsfb->clk);
168 if (mxsfb->clk_disp_axi)
169 clk_disable_unprepare(mxsfb->clk_disp_axi);
170}
171
Fabio Estevam0f933322017-05-05 15:01:41 -0300172/*
173 * Clear the bit and poll it cleared. This is usually called with
174 * a reset address and mask being either SFTRST(bit 31) or CLKGATE
175 * (bit 30).
176 */
177static int clear_poll_bit(void __iomem *addr, u32 mask)
178{
179 u32 reg;
180
Laurent Pincharteb28c5c2020-07-27 05:06:40 +0300181 writel(mask, addr + REG_CLR);
Fabio Estevam0f933322017-05-05 15:01:41 -0300182 return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT);
183}
184
Laurent Pinchartf14fec82020-07-27 05:06:38 +0300185static int mxsfb_reset_block(struct mxsfb_drm_private *mxsfb)
Fabio Estevam0f933322017-05-05 15:01:41 -0300186{
187 int ret;
188
Laurent Pincharteb28c5c2020-07-27 05:06:40 +0300189 ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
Fabio Estevam0f933322017-05-05 15:01:41 -0300190 if (ret)
191 return ret;
192
Laurent Pincharteb28c5c2020-07-27 05:06:40 +0300193 writel(CTRL_CLKGATE, mxsfb->base + LCDC_CTRL + REG_CLR);
Fabio Estevam0f933322017-05-05 15:01:41 -0300194
Laurent Pincharteb28c5c2020-07-27 05:06:40 +0300195 ret = clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_SFTRST);
Fabio Estevam0f933322017-05-05 15:01:41 -0300196 if (ret)
197 return ret;
198
Laurent Pincharteb28c5c2020-07-27 05:06:40 +0300199 return clear_poll_bit(mxsfb->base + LCDC_CTRL, CTRL_CLKGATE);
Fabio Estevam0f933322017-05-05 15:01:41 -0300200}
201
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300202static dma_addr_t mxsfb_get_fb_paddr(struct drm_plane *plane)
Leonard Crestez2dc36202018-09-17 16:42:12 +0300203{
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300204 struct drm_framebuffer *fb = plane->state->fb;
Leonard Crestez2dc36202018-09-17 16:42:12 +0300205 struct drm_gem_cma_object *gem;
206
207 if (!fb)
208 return 0;
209
210 gem = drm_fb_cma_get_gem_obj(fb, 0);
211 if (!gem)
212 return 0;
213
214 return gem->paddr;
215}
216
Marek Vasut45d59d72016-08-18 20:23:01 +0200217static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb)
218{
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300219 struct drm_device *drm = mxsfb->crtc.dev;
220 struct drm_display_mode *m = &mxsfb->crtc.state->adjusted_mode;
Guido Güntherb1d06692019-08-29 14:30:03 +0300221 u32 bus_flags = mxsfb->connector->display_info.bus_flags;
Marek Vasut45d59d72016-08-18 20:23:01 +0200222 u32 vdctrl0, vsync_pulse_len, hsync_pulse_len;
223 int err;
224
225 /*
226 * It seems, you can't re-program the controller if it is still
227 * running. This may lead to shifted pictures (FIFO issue?), so
228 * first stop the controller and drain its FIFOs.
229 */
Marek Vasut45d59d72016-08-18 20:23:01 +0200230
Fabio Estevam0f933322017-05-05 15:01:41 -0300231 /* Mandatory eLCDIF reset as per the Reference Manual */
Laurent Pinchartf14fec82020-07-27 05:06:38 +0300232 err = mxsfb_reset_block(mxsfb);
Fabio Estevam0f933322017-05-05 15:01:41 -0300233 if (err)
234 return;
235
Marek Vasut45d59d72016-08-18 20:23:01 +0200236 /* Clear the FIFOs */
237 writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET);
238
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300239 if (mxsfb->devdata->has_overlay)
240 writel(0, mxsfb->base + LCDC_AS_CTRL);
241
Laurent Pinchart51b777f2020-07-27 05:06:52 +0300242 mxsfb_set_formats(mxsfb);
Marek Vasut45d59d72016-08-18 20:23:01 +0200243
244 clk_set_rate(mxsfb->clk, m->crtc_clock * 1000);
245
Guido Güntherb1d06692019-08-29 14:30:03 +0300246 if (mxsfb->bridge && mxsfb->bridge->timings)
247 bus_flags = mxsfb->bridge->timings->input_bus_flags;
248
Robert Chirasd0234042019-08-29 14:30:02 +0300249 DRM_DEV_DEBUG_DRIVER(drm->dev, "Pixel clock: %dkHz (actual: %dkHz)\n",
250 m->crtc_clock,
251 (int)(clk_get_rate(mxsfb->clk) / 1000));
252 DRM_DEV_DEBUG_DRIVER(drm->dev, "Connector bus_flags: 0x%08X\n",
253 bus_flags);
254 DRM_DEV_DEBUG_DRIVER(drm->dev, "Mode flags: 0x%08X\n", m->flags);
255
Marek Vasut45d59d72016-08-18 20:23:01 +0200256 writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) |
257 TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay),
258 mxsfb->base + mxsfb->devdata->transfer_count);
259
260 vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start;
261
262 vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */
263 VDCTRL0_VSYNC_PERIOD_UNIT |
264 VDCTRL0_VSYNC_PULSE_WIDTH_UNIT |
265 VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len);
266 if (m->flags & DRM_MODE_FLAG_PHSYNC)
267 vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH;
268 if (m->flags & DRM_MODE_FLAG_PVSYNC)
269 vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH;
Stefan Agner53990e42016-12-14 12:48:09 -0800270 /* Make sure Data Enable is high active by default */
271 if (!(bus_flags & DRM_BUS_FLAG_DE_LOW))
Marek Vasut45d59d72016-08-18 20:23:01 +0200272 vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH;
Stefan Agner53990e42016-12-14 12:48:09 -0800273 /*
Laurent Pinchart88bc4172018-09-22 15:02:42 +0300274 * DRM_BUS_FLAG_PIXDATA_DRIVE_ defines are controller centric,
Stefan Agner53990e42016-12-14 12:48:09 -0800275 * controllers VDCTRL0_DOTCLK is display centric.
276 * Drive on positive edge -> display samples on falling edge
Laurent Pinchart88bc4172018-09-22 15:02:42 +0300277 * DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING
Stefan Agner53990e42016-12-14 12:48:09 -0800278 */
Laurent Pinchart88bc4172018-09-22 15:02:42 +0300279 if (bus_flags & DRM_BUS_FLAG_PIXDATA_DRIVE_POSEDGE)
Marek Vasut45d59d72016-08-18 20:23:01 +0200280 vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING;
281
282 writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0);
283
Marek Vasut45d59d72016-08-18 20:23:01 +0200284 /* Frame length in lines. */
285 writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1);
286
287 /* Line length in units of clocks or pixels. */
288 hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start;
289 writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) |
290 VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal),
291 mxsfb->base + LCDC_VDCTRL2);
292
Fabio Estevamd42986b2017-02-02 19:26:38 -0200293 writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) |
294 SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start),
Marek Vasut45d59d72016-08-18 20:23:01 +0200295 mxsfb->base + LCDC_VDCTRL3);
296
297 writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay),
298 mxsfb->base + LCDC_VDCTRL4);
Marek Vasut45d59d72016-08-18 20:23:01 +0200299}
300
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300301static int mxsfb_crtc_atomic_check(struct drm_crtc *crtc,
Maxime Ripard29b77ad2020-10-28 13:32:21 +0100302 struct drm_atomic_state *state)
Marek Vasut45d59d72016-08-18 20:23:01 +0200303{
Maxime Ripard29b77ad2020-10-28 13:32:21 +0100304 struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
305 crtc);
306 bool has_primary = crtc_state->plane_mask &
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300307 drm_plane_mask(crtc->primary);
308
309 /* The primary plane has to be enabled when the CRTC is active. */
Maxime Ripard29b77ad2020-10-28 13:32:21 +0100310 if (crtc_state->active && !has_primary)
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300311 return -EINVAL;
312
313 /* TODO: Is this needed ? */
Maxime Ripardd74252b2020-11-02 14:38:34 +0100314 return drm_atomic_add_affected_planes(state, crtc);
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300315}
316
Laurent Pinchart1e5d7962020-07-27 05:06:44 +0300317static void mxsfb_crtc_atomic_flush(struct drm_crtc *crtc,
Maxime Ripardf6ebe9f2020-10-28 13:32:22 +0100318 struct drm_atomic_state *state)
Laurent Pinchart1e5d7962020-07-27 05:06:44 +0300319{
320 struct drm_pending_vblank_event *event;
321
322 event = crtc->state->event;
323 crtc->state->event = NULL;
324
325 if (!event)
326 return;
327
328 spin_lock_irq(&crtc->dev->event_lock);
329 if (drm_crtc_vblank_get(crtc) == 0)
330 drm_crtc_arm_vblank_event(crtc, event);
331 else
332 drm_crtc_send_vblank_event(crtc, event);
333 spin_unlock_irq(&crtc->dev->event_lock);
334}
335
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300336static void mxsfb_crtc_atomic_enable(struct drm_crtc *crtc,
Maxime Ripard351f9502020-10-08 14:44:08 +0200337 struct drm_atomic_state *state)
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300338{
339 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
340 struct drm_device *drm = mxsfb->drm;
Leonard Crestez2dc36202018-09-17 16:42:12 +0300341 dma_addr_t paddr;
342
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300343 pm_runtime_get_sync(drm->dev);
Leonard Crestez626a2c52018-09-17 16:42:11 +0300344 mxsfb_enable_axi_clk(mxsfb);
Laurent Pinchartb9f59372020-07-27 05:06:46 +0300345
346 drm_crtc_vblank_on(crtc);
347
Marek Vasut45d59d72016-08-18 20:23:01 +0200348 mxsfb_crtc_mode_set_nofb(mxsfb);
Leonard Crestez2dc36202018-09-17 16:42:12 +0300349
350 /* Write cur_buf as well to avoid an initial corrupt frame */
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300351 paddr = mxsfb_get_fb_paddr(crtc->primary);
Leonard Crestez2dc36202018-09-17 16:42:12 +0300352 if (paddr) {
353 writel(paddr, mxsfb->base + mxsfb->devdata->cur_buf);
354 writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
355 }
356
Marek Vasut45d59d72016-08-18 20:23:01 +0200357 mxsfb_enable_controller(mxsfb);
358}
359
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300360static void mxsfb_crtc_atomic_disable(struct drm_crtc *crtc,
Maxime Ripard351f9502020-10-08 14:44:08 +0200361 struct drm_atomic_state *state)
Marek Vasut45d59d72016-08-18 20:23:01 +0200362{
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300363 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
364 struct drm_device *drm = mxsfb->drm;
365 struct drm_pending_vblank_event *event;
366
Marek Vasut45d59d72016-08-18 20:23:01 +0200367 mxsfb_disable_controller(mxsfb);
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300368
369 spin_lock_irq(&drm->event_lock);
370 event = crtc->state->event;
371 if (event) {
372 crtc->state->event = NULL;
373 drm_crtc_send_vblank_event(crtc, event);
374 }
375 spin_unlock_irq(&drm->event_lock);
Laurent Pinchart07b7fd72020-07-27 05:06:45 +0300376
Laurent Pinchartb9f59372020-07-27 05:06:46 +0300377 drm_crtc_vblank_off(crtc);
378
Laurent Pinchart07b7fd72020-07-27 05:06:45 +0300379 mxsfb_disable_axi_clk(mxsfb);
380 pm_runtime_put_sync(drm->dev);
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300381}
382
383static int mxsfb_crtc_enable_vblank(struct drm_crtc *crtc)
384{
385 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
386
387 /* Clear and enable VBLANK IRQ */
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300388 writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
389 writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_SET);
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300390
391 return 0;
392}
393
394static void mxsfb_crtc_disable_vblank(struct drm_crtc *crtc)
395{
396 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(crtc->dev);
397
398 /* Disable and clear VBLANK IRQ */
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300399 writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
400 writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
Marek Vasut45d59d72016-08-18 20:23:01 +0200401}
402
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300403static const struct drm_crtc_helper_funcs mxsfb_crtc_helper_funcs = {
404 .atomic_check = mxsfb_crtc_atomic_check,
Laurent Pinchart1e5d7962020-07-27 05:06:44 +0300405 .atomic_flush = mxsfb_crtc_atomic_flush,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300406 .atomic_enable = mxsfb_crtc_atomic_enable,
407 .atomic_disable = mxsfb_crtc_atomic_disable,
408};
409
410static const struct drm_crtc_funcs mxsfb_crtc_funcs = {
411 .reset = drm_atomic_helper_crtc_reset,
412 .destroy = drm_crtc_cleanup,
413 .set_config = drm_atomic_helper_set_config,
414 .page_flip = drm_atomic_helper_page_flip,
415 .atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
416 .atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
417 .enable_vblank = mxsfb_crtc_enable_vblank,
418 .disable_vblank = mxsfb_crtc_disable_vblank,
419};
420
421/* -----------------------------------------------------------------------------
422 * Encoder
423 */
424
425static const struct drm_encoder_funcs mxsfb_encoder_funcs = {
426 .destroy = drm_encoder_cleanup,
427};
428
429/* -----------------------------------------------------------------------------
430 * Planes
431 */
432
433static int mxsfb_plane_atomic_check(struct drm_plane *plane,
Maxime Ripard7c11b992021-02-19 13:00:24 +0100434 struct drm_atomic_state *state)
Marek Vasut45d59d72016-08-18 20:23:01 +0200435{
Maxime Ripard7c11b992021-02-19 13:00:24 +0100436 struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state,
437 plane);
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300438 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
439 struct drm_crtc_state *crtc_state;
440
Maxime Riparddec92022021-02-19 13:00:25 +0100441 crtc_state = drm_atomic_get_new_crtc_state(state,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300442 &mxsfb->crtc);
443
444 return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
445 DRM_PLANE_HELPER_NO_SCALING,
446 DRM_PLANE_HELPER_NO_SCALING,
447 false, true);
448}
449
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300450static void mxsfb_plane_primary_atomic_update(struct drm_plane *plane,
Maxime Ripard977697e2021-02-19 13:00:29 +0100451 struct drm_atomic_state *state)
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300452{
453 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
Leonard Crestez2dc36202018-09-17 16:42:12 +0300454 dma_addr_t paddr;
Marek Vasut45d59d72016-08-18 20:23:01 +0200455
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300456 paddr = mxsfb_get_fb_paddr(plane);
Laurent Pinchart07b7fd72020-07-27 05:06:45 +0300457 if (paddr)
Leonard Crestez2dc36202018-09-17 16:42:12 +0300458 writel(paddr, mxsfb->base + mxsfb->devdata->next_buf);
Marek Vasut45d59d72016-08-18 20:23:01 +0200459}
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300460
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300461static void mxsfb_plane_overlay_atomic_update(struct drm_plane *plane,
Maxime Ripard977697e2021-02-19 13:00:29 +0100462 struct drm_atomic_state *state)
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300463{
Maxime Ripard977697e2021-02-19 13:00:29 +0100464 struct drm_plane_state *old_pstate = drm_atomic_get_old_plane_state(state,
465 plane);
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300466 struct mxsfb_drm_private *mxsfb = to_mxsfb_drm_private(plane->dev);
Maxime Ripard37418bf2021-02-19 13:00:30 +0100467 struct drm_plane_state *new_pstate = drm_atomic_get_new_plane_state(state,
468 plane);
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300469 dma_addr_t paddr;
470 u32 ctrl;
471
472 paddr = mxsfb_get_fb_paddr(plane);
473 if (!paddr) {
474 writel(0, mxsfb->base + LCDC_AS_CTRL);
475 return;
476 }
477
478 /*
479 * HACK: The hardware seems to output 64 bytes of data of unknown
480 * origin, and then to proceed with the framebuffer. Until the reason
481 * is understood, live with the 16 initial invalid pixels on the first
482 * line and start 64 bytes within the framebuffer.
483 */
484 paddr += 64;
485
486 writel(paddr, mxsfb->base + LCDC_AS_NEXT_BUF);
487
488 /*
489 * If the plane was previously disabled, write LCDC_AS_BUF as well to
490 * provide the first buffer.
491 */
492 if (!old_pstate->fb)
493 writel(paddr, mxsfb->base + LCDC_AS_BUF);
494
495 ctrl = AS_CTRL_AS_ENABLE | AS_CTRL_ALPHA(255);
496
Maxime Ripard41016fe2021-02-19 13:00:28 +0100497 switch (new_pstate->fb->format->format) {
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300498 case DRM_FORMAT_XRGB4444:
499 ctrl |= AS_CTRL_FORMAT_RGB444 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
500 break;
501 case DRM_FORMAT_ARGB4444:
502 ctrl |= AS_CTRL_FORMAT_ARGB4444 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
503 break;
504 case DRM_FORMAT_XRGB1555:
505 ctrl |= AS_CTRL_FORMAT_RGB555 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
506 break;
507 case DRM_FORMAT_ARGB1555:
508 ctrl |= AS_CTRL_FORMAT_ARGB1555 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
509 break;
510 case DRM_FORMAT_RGB565:
511 ctrl |= AS_CTRL_FORMAT_RGB565 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
512 break;
513 case DRM_FORMAT_XRGB8888:
514 ctrl |= AS_CTRL_FORMAT_RGB888 | AS_CTRL_ALPHA_CTRL_OVERRIDE;
515 break;
516 case DRM_FORMAT_ARGB8888:
517 ctrl |= AS_CTRL_FORMAT_ARGB8888 | AS_CTRL_ALPHA_CTRL_EMBEDDED;
518 break;
519 }
520
521 writel(ctrl, mxsfb->base + LCDC_AS_CTRL);
522}
523
Daniel Abrechtc70582b2020-11-08 21:00:01 +0000524static bool mxsfb_format_mod_supported(struct drm_plane *plane,
525 uint32_t format,
526 uint64_t modifier)
527{
528 return modifier == DRM_FORMAT_MOD_LINEAR;
529}
530
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300531static const struct drm_plane_helper_funcs mxsfb_plane_primary_helper_funcs = {
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300532 .atomic_check = mxsfb_plane_atomic_check,
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300533 .atomic_update = mxsfb_plane_primary_atomic_update,
534};
535
536static const struct drm_plane_helper_funcs mxsfb_plane_overlay_helper_funcs = {
537 .atomic_check = mxsfb_plane_atomic_check,
538 .atomic_update = mxsfb_plane_overlay_atomic_update,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300539};
540
541static const struct drm_plane_funcs mxsfb_plane_funcs = {
Daniel Abrechtc70582b2020-11-08 21:00:01 +0000542 .format_mod_supported = mxsfb_format_mod_supported,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300543 .update_plane = drm_atomic_helper_update_plane,
544 .disable_plane = drm_atomic_helper_disable_plane,
545 .destroy = drm_plane_cleanup,
546 .reset = drm_atomic_helper_plane_reset,
547 .atomic_duplicate_state = drm_atomic_helper_plane_duplicate_state,
548 .atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
549};
550
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300551static const uint32_t mxsfb_primary_plane_formats[] = {
552 DRM_FORMAT_RGB565,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300553 DRM_FORMAT_XRGB8888,
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300554};
555
556static const uint32_t mxsfb_overlay_plane_formats[] = {
557 DRM_FORMAT_XRGB4444,
558 DRM_FORMAT_ARGB4444,
559 DRM_FORMAT_XRGB1555,
560 DRM_FORMAT_ARGB1555,
561 DRM_FORMAT_RGB565,
562 DRM_FORMAT_XRGB8888,
563 DRM_FORMAT_ARGB8888,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300564};
565
566static const uint64_t mxsfb_modifiers[] = {
567 DRM_FORMAT_MOD_LINEAR,
568 DRM_FORMAT_MOD_INVALID
569};
570
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300571/* -----------------------------------------------------------------------------
572 * Initialization
573 */
574
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300575int mxsfb_kms_init(struct mxsfb_drm_private *mxsfb)
576{
577 struct drm_encoder *encoder = &mxsfb->encoder;
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300578 struct drm_crtc *crtc = &mxsfb->crtc;
579 int ret;
580
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300581 drm_plane_helper_add(&mxsfb->planes.primary,
582 &mxsfb_plane_primary_helper_funcs);
583 ret = drm_universal_plane_init(mxsfb->drm, &mxsfb->planes.primary, 1,
584 &mxsfb_plane_funcs,
585 mxsfb_primary_plane_formats,
586 ARRAY_SIZE(mxsfb_primary_plane_formats),
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300587 mxsfb_modifiers, DRM_PLANE_TYPE_PRIMARY,
588 NULL);
589 if (ret)
590 return ret;
591
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300592 if (mxsfb->devdata->has_overlay) {
593 drm_plane_helper_add(&mxsfb->planes.overlay,
594 &mxsfb_plane_overlay_helper_funcs);
595 ret = drm_universal_plane_init(mxsfb->drm,
596 &mxsfb->planes.overlay, 1,
597 &mxsfb_plane_funcs,
598 mxsfb_overlay_plane_formats,
599 ARRAY_SIZE(mxsfb_overlay_plane_formats),
600 mxsfb_modifiers, DRM_PLANE_TYPE_OVERLAY,
601 NULL);
602 if (ret)
603 return ret;
604 }
605
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300606 drm_crtc_helper_add(crtc, &mxsfb_crtc_helper_funcs);
Laurent Pinchart63aa5812020-07-27 05:06:54 +0300607 ret = drm_crtc_init_with_planes(mxsfb->drm, crtc,
608 &mxsfb->planes.primary, NULL,
Laurent Pinchartae1ed002020-07-27 05:06:43 +0300609 &mxsfb_crtc_funcs, NULL);
610 if (ret)
611 return ret;
612
613 encoder->possible_crtcs = drm_crtc_mask(crtc);
614 return drm_encoder_init(mxsfb->drm, encoder, &mxsfb_encoder_funcs,
615 DRM_MODE_ENCODER_NONE, NULL);
616}