]>
Commit | Line | Data |
---|---|---|
45d59d70 MV |
1 | /* |
2 | * Copyright (C) 2016 Marek Vasut <marex@denx.de> | |
3 | * | |
4 | * This code is based on drivers/video/fbdev/mxsfb.c : | |
5 | * Copyright (C) 2010 Juergen Beisert, Pengutronix | |
6 | * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved. | |
7 | * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved. | |
8 | * | |
9 | * This program is free software; you can redistribute it and/or | |
10 | * modify it under the terms of the GNU General Public License | |
11 | * as published by the Free Software Foundation; either version 2 | |
12 | * of the License, or (at your option) any later version. | |
13 | * This program is distributed in the hope that it will be useful, | |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | * GNU General Public License for more details. | |
17 | */ | |
18 | ||
19 | #include <drm/drmP.h> | |
20 | #include <drm/drm_atomic_helper.h> | |
21 | #include <drm/drm_crtc.h> | |
22 | #include <drm/drm_crtc_helper.h> | |
23 | #include <drm/drm_fb_helper.h> | |
24 | #include <drm/drm_fb_cma_helper.h> | |
25 | #include <drm/drm_gem_cma_helper.h> | |
26 | #include <drm/drm_of.h> | |
27 | #include <drm/drm_plane_helper.h> | |
28 | #include <drm/drm_simple_kms_helper.h> | |
29 | #include <linux/clk.h> | |
30 | #include <linux/iopoll.h> | |
31 | #include <linux/of_graph.h> | |
32 | #include <linux/platform_data/simplefb.h> | |
33 | #include <video/videomode.h> | |
34 | ||
35 | #include "mxsfb_drv.h" | |
36 | #include "mxsfb_regs.h" | |
37 | ||
0f933328 FE |
38 | #define MXS_SET_ADDR 0x4 |
39 | #define MXS_CLR_ADDR 0x8 | |
40 | #define MODULE_CLKGATE BIT(30) | |
41 | #define MODULE_SFTRST BIT(31) | |
42 | /* 1 second delay should be plenty of time for block reset */ | |
43 | #define RESET_TIMEOUT 1000000 | |
44 | ||
45d59d70 MV |
45 | static u32 set_hsync_pulse_width(struct mxsfb_drm_private *mxsfb, u32 val) |
46 | { | |
47 | return (val & mxsfb->devdata->hs_wdth_mask) << | |
48 | mxsfb->devdata->hs_wdth_shift; | |
49 | } | |
50 | ||
51 | /* Setup the MXSFB registers for decoding the pixels out of the framebuffer */ | |
52 | static int mxsfb_set_pixel_fmt(struct mxsfb_drm_private *mxsfb) | |
53 | { | |
54 | struct drm_crtc *crtc = &mxsfb->pipe.crtc; | |
55 | struct drm_device *drm = crtc->dev; | |
438b74a5 | 56 | const u32 format = crtc->primary->state->fb->format->format; |
45d59d70 MV |
57 | u32 ctrl, ctrl1; |
58 | ||
59 | ctrl = CTRL_BYPASS_COUNT | CTRL_MASTER; | |
60 | ||
61 | /* | |
62 | * WARNING: The bus width, CTRL_SET_BUS_WIDTH(), is configured to | |
63 | * match the selected mode here. This differs from the original | |
64 | * MXSFB driver, which had the option to configure the bus width | |
65 | * to arbitrary value. This limitation should not pose an issue. | |
66 | */ | |
67 | ||
68 | /* CTRL1 contains IRQ config and status bits, preserve those. */ | |
69 | ctrl1 = readl(mxsfb->base + LCDC_CTRL1); | |
70 | ctrl1 &= CTRL1_CUR_FRAME_DONE_IRQ_EN | CTRL1_CUR_FRAME_DONE_IRQ; | |
71 | ||
72 | switch (format) { | |
73 | case DRM_FORMAT_RGB565: | |
74 | dev_dbg(drm->dev, "Setting up RGB565 mode\n"); | |
45d59d70 MV |
75 | ctrl |= CTRL_SET_WORD_LENGTH(0); |
76 | ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0xf); | |
77 | break; | |
78 | case DRM_FORMAT_XRGB8888: | |
79 | dev_dbg(drm->dev, "Setting up XRGB8888 mode\n"); | |
45d59d70 MV |
80 | ctrl |= CTRL_SET_WORD_LENGTH(3); |
81 | /* Do not use packed pixels = one pixel per word instead. */ | |
82 | ctrl1 |= CTRL1_SET_BYTE_PACKAGING(0x7); | |
83 | break; | |
84 | default: | |
85 | dev_err(drm->dev, "Unhandled pixel format %08x\n", format); | |
86 | return -EINVAL; | |
87 | } | |
88 | ||
89 | writel(ctrl1, mxsfb->base + LCDC_CTRL1); | |
90 | writel(ctrl, mxsfb->base + LCDC_CTRL); | |
91 | ||
92 | return 0; | |
93 | } | |
94 | ||
10f2889b SA |
95 | static void mxsfb_set_bus_fmt(struct mxsfb_drm_private *mxsfb) |
96 | { | |
97 | struct drm_crtc *crtc = &mxsfb->pipe.crtc; | |
98 | struct drm_device *drm = crtc->dev; | |
99 | u32 bus_format = MEDIA_BUS_FMT_RGB888_1X24; | |
100 | u32 reg; | |
101 | ||
102 | reg = readl(mxsfb->base + LCDC_CTRL); | |
103 | ||
104 | if (mxsfb->connector.display_info.num_bus_formats) | |
105 | bus_format = mxsfb->connector.display_info.bus_formats[0]; | |
106 | ||
107 | reg &= ~CTRL_BUS_WIDTH_MASK; | |
108 | switch (bus_format) { | |
109 | case MEDIA_BUS_FMT_RGB565_1X16: | |
110 | reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_16BIT); | |
111 | break; | |
112 | case MEDIA_BUS_FMT_RGB666_1X18: | |
113 | reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_18BIT); | |
114 | break; | |
115 | case MEDIA_BUS_FMT_RGB888_1X24: | |
116 | reg |= CTRL_SET_BUS_WIDTH(STMLCDIF_24BIT); | |
117 | break; | |
118 | default: | |
119 | dev_err(drm->dev, "Unknown media bus format %d\n", bus_format); | |
120 | break; | |
121 | } | |
122 | writel(reg, mxsfb->base + LCDC_CTRL); | |
123 | } | |
124 | ||
45d59d70 MV |
125 | static void mxsfb_enable_controller(struct mxsfb_drm_private *mxsfb) |
126 | { | |
127 | u32 reg; | |
128 | ||
129 | if (mxsfb->clk_disp_axi) | |
130 | clk_prepare_enable(mxsfb->clk_disp_axi); | |
131 | clk_prepare_enable(mxsfb->clk); | |
132 | mxsfb_enable_axi_clk(mxsfb); | |
133 | ||
134 | /* If it was disabled, re-enable the mode again */ | |
135 | writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_SET); | |
136 | ||
137 | /* Enable the SYNC signals first, then the DMA engine */ | |
138 | reg = readl(mxsfb->base + LCDC_VDCTRL4); | |
139 | reg |= VDCTRL4_SYNC_SIGNALS_ON; | |
140 | writel(reg, mxsfb->base + LCDC_VDCTRL4); | |
141 | ||
142 | writel(CTRL_RUN, mxsfb->base + LCDC_CTRL + REG_SET); | |
143 | } | |
144 | ||
145 | static void mxsfb_disable_controller(struct mxsfb_drm_private *mxsfb) | |
146 | { | |
147 | u32 reg; | |
148 | ||
149 | /* | |
150 | * Even if we disable the controller here, it will still continue | |
151 | * until its FIFOs are running out of data | |
152 | */ | |
153 | writel(CTRL_DOTCLK_MODE, mxsfb->base + LCDC_CTRL + REG_CLR); | |
154 | ||
155 | readl_poll_timeout(mxsfb->base + LCDC_CTRL, reg, !(reg & CTRL_RUN), | |
156 | 0, 1000); | |
157 | ||
158 | reg = readl(mxsfb->base + LCDC_VDCTRL4); | |
159 | reg &= ~VDCTRL4_SYNC_SIGNALS_ON; | |
160 | writel(reg, mxsfb->base + LCDC_VDCTRL4); | |
161 | ||
162 | mxsfb_disable_axi_clk(mxsfb); | |
163 | ||
164 | clk_disable_unprepare(mxsfb->clk); | |
165 | if (mxsfb->clk_disp_axi) | |
166 | clk_disable_unprepare(mxsfb->clk_disp_axi); | |
167 | } | |
168 | ||
0f933328 FE |
169 | /* |
170 | * Clear the bit and poll it cleared. This is usually called with | |
171 | * a reset address and mask being either SFTRST(bit 31) or CLKGATE | |
172 | * (bit 30). | |
173 | */ | |
174 | static int clear_poll_bit(void __iomem *addr, u32 mask) | |
175 | { | |
176 | u32 reg; | |
177 | ||
178 | writel(mask, addr + MXS_CLR_ADDR); | |
179 | return readl_poll_timeout(addr, reg, !(reg & mask), 0, RESET_TIMEOUT); | |
180 | } | |
181 | ||
182 | static int mxsfb_reset_block(void __iomem *reset_addr) | |
183 | { | |
184 | int ret; | |
185 | ||
186 | ret = clear_poll_bit(reset_addr, MODULE_SFTRST); | |
187 | if (ret) | |
188 | return ret; | |
189 | ||
190 | writel(MODULE_CLKGATE, reset_addr + MXS_CLR_ADDR); | |
191 | ||
192 | ret = clear_poll_bit(reset_addr, MODULE_SFTRST); | |
193 | if (ret) | |
194 | return ret; | |
195 | ||
196 | return clear_poll_bit(reset_addr, MODULE_CLKGATE); | |
197 | } | |
198 | ||
45d59d70 MV |
199 | static void mxsfb_crtc_mode_set_nofb(struct mxsfb_drm_private *mxsfb) |
200 | { | |
201 | struct drm_display_mode *m = &mxsfb->pipe.crtc.state->adjusted_mode; | |
202 | const u32 bus_flags = mxsfb->connector.display_info.bus_flags; | |
203 | u32 vdctrl0, vsync_pulse_len, hsync_pulse_len; | |
204 | int err; | |
205 | ||
206 | /* | |
207 | * It seems, you can't re-program the controller if it is still | |
208 | * running. This may lead to shifted pictures (FIFO issue?), so | |
209 | * first stop the controller and drain its FIFOs. | |
210 | */ | |
211 | mxsfb_enable_axi_clk(mxsfb); | |
212 | ||
0f933328 FE |
213 | /* Mandatory eLCDIF reset as per the Reference Manual */ |
214 | err = mxsfb_reset_block(mxsfb->base); | |
215 | if (err) | |
216 | return; | |
217 | ||
45d59d70 MV |
218 | /* Clear the FIFOs */ |
219 | writel(CTRL1_FIFO_CLEAR, mxsfb->base + LCDC_CTRL1 + REG_SET); | |
220 | ||
221 | err = mxsfb_set_pixel_fmt(mxsfb); | |
222 | if (err) | |
223 | return; | |
224 | ||
225 | clk_set_rate(mxsfb->clk, m->crtc_clock * 1000); | |
226 | ||
227 | writel(TRANSFER_COUNT_SET_VCOUNT(m->crtc_vdisplay) | | |
228 | TRANSFER_COUNT_SET_HCOUNT(m->crtc_hdisplay), | |
229 | mxsfb->base + mxsfb->devdata->transfer_count); | |
230 | ||
231 | vsync_pulse_len = m->crtc_vsync_end - m->crtc_vsync_start; | |
232 | ||
233 | vdctrl0 = VDCTRL0_ENABLE_PRESENT | /* Always in DOTCLOCK mode */ | |
234 | VDCTRL0_VSYNC_PERIOD_UNIT | | |
235 | VDCTRL0_VSYNC_PULSE_WIDTH_UNIT | | |
236 | VDCTRL0_SET_VSYNC_PULSE_WIDTH(vsync_pulse_len); | |
237 | if (m->flags & DRM_MODE_FLAG_PHSYNC) | |
238 | vdctrl0 |= VDCTRL0_HSYNC_ACT_HIGH; | |
239 | if (m->flags & DRM_MODE_FLAG_PVSYNC) | |
240 | vdctrl0 |= VDCTRL0_VSYNC_ACT_HIGH; | |
53990e41 SA |
241 | /* Make sure Data Enable is high active by default */ |
242 | if (!(bus_flags & DRM_BUS_FLAG_DE_LOW)) | |
45d59d70 | 243 | vdctrl0 |= VDCTRL0_ENABLE_ACT_HIGH; |
53990e41 SA |
244 | /* |
245 | * DRM_BUS_FLAG_PIXDATA_ defines are controller centric, | |
246 | * controllers VDCTRL0_DOTCLK is display centric. | |
247 | * Drive on positive edge -> display samples on falling edge | |
248 | * DRM_BUS_FLAG_PIXDATA_POSEDGE -> VDCTRL0_DOTCLK_ACT_FALLING | |
249 | */ | |
250 | if (bus_flags & DRM_BUS_FLAG_PIXDATA_POSEDGE) | |
45d59d70 MV |
251 | vdctrl0 |= VDCTRL0_DOTCLK_ACT_FALLING; |
252 | ||
253 | writel(vdctrl0, mxsfb->base + LCDC_VDCTRL0); | |
254 | ||
10f2889b SA |
255 | mxsfb_set_bus_fmt(mxsfb); |
256 | ||
45d59d70 MV |
257 | /* Frame length in lines. */ |
258 | writel(m->crtc_vtotal, mxsfb->base + LCDC_VDCTRL1); | |
259 | ||
260 | /* Line length in units of clocks or pixels. */ | |
261 | hsync_pulse_len = m->crtc_hsync_end - m->crtc_hsync_start; | |
262 | writel(set_hsync_pulse_width(mxsfb, hsync_pulse_len) | | |
263 | VDCTRL2_SET_HSYNC_PERIOD(m->crtc_htotal), | |
264 | mxsfb->base + LCDC_VDCTRL2); | |
265 | ||
d42986b6 FE |
266 | writel(SET_HOR_WAIT_CNT(m->crtc_htotal - m->crtc_hsync_start) | |
267 | SET_VERT_WAIT_CNT(m->crtc_vtotal - m->crtc_vsync_start), | |
45d59d70 MV |
268 | mxsfb->base + LCDC_VDCTRL3); |
269 | ||
270 | writel(SET_DOTCLK_H_VALID_DATA_CNT(m->hdisplay), | |
271 | mxsfb->base + LCDC_VDCTRL4); | |
272 | ||
273 | mxsfb_disable_axi_clk(mxsfb); | |
274 | } | |
275 | ||
276 | void mxsfb_crtc_enable(struct mxsfb_drm_private *mxsfb) | |
277 | { | |
278 | mxsfb_crtc_mode_set_nofb(mxsfb); | |
279 | mxsfb_enable_controller(mxsfb); | |
280 | } | |
281 | ||
282 | void mxsfb_crtc_disable(struct mxsfb_drm_private *mxsfb) | |
283 | { | |
284 | mxsfb_disable_controller(mxsfb); | |
285 | } | |
286 | ||
287 | void mxsfb_plane_atomic_update(struct mxsfb_drm_private *mxsfb, | |
288 | struct drm_plane_state *state) | |
289 | { | |
290 | struct drm_simple_display_pipe *pipe = &mxsfb->pipe; | |
291 | struct drm_crtc *crtc = &pipe->crtc; | |
292 | struct drm_framebuffer *fb = pipe->plane.state->fb; | |
293 | struct drm_pending_vblank_event *event; | |
294 | struct drm_gem_cma_object *gem; | |
295 | ||
296 | if (!crtc) | |
297 | return; | |
298 | ||
299 | spin_lock_irq(&crtc->dev->event_lock); | |
300 | event = crtc->state->event; | |
301 | if (event) { | |
302 | crtc->state->event = NULL; | |
303 | ||
304 | if (drm_crtc_vblank_get(crtc) == 0) { | |
305 | drm_crtc_arm_vblank_event(crtc, event); | |
306 | } else { | |
307 | drm_crtc_send_vblank_event(crtc, event); | |
308 | } | |
309 | } | |
310 | spin_unlock_irq(&crtc->dev->event_lock); | |
311 | ||
312 | if (!fb) | |
313 | return; | |
314 | ||
315 | gem = drm_fb_cma_get_gem_obj(fb, 0); | |
316 | ||
317 | mxsfb_enable_axi_clk(mxsfb); | |
318 | writel(gem->paddr, mxsfb->base + mxsfb->devdata->next_buf); | |
319 | mxsfb_disable_axi_clk(mxsfb); | |
320 | } |