1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright (C) 2016 Marek Vasut <marex@denx.de>
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.
11 #include <linux/clk.h>
12 #include <linux/dma-mapping.h>
14 #include <linux/mod_devicetable.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/property.h>
18 #include <linux/pm_runtime.h>
20 #include <drm/drm_atomic_helper.h>
21 #include <drm/drm_bridge.h>
22 #include <drm/drm_client_setup.h>
23 #include <drm/drm_connector.h>
24 #include <drm/drm_drv.h>
25 #include <drm/drm_fbdev_dma.h>
26 #include <drm/drm_fourcc.h>
27 #include <drm/drm_gem_dma_helper.h>
28 #include <drm/drm_gem_framebuffer_helper.h>
29 #include <drm/drm_mode_config.h>
30 #include <drm/drm_module.h>
31 #include <drm/drm_of.h>
32 #include <drm/drm_probe_helper.h>
33 #include <drm/drm_vblank.h>
35 #include "mxsfb_drv.h"
36 #include "mxsfb_regs.h"
42 * Starting at i.MX6 the hardware version register is gone, use the
43 * i.MX family number as the version.
48 static const struct mxsfb_devdata mxsfb_devdata
[] = {
50 .transfer_count
= LCDC_V3_TRANSFER_COUNT
,
51 .cur_buf
= LCDC_V3_CUR_BUF
,
52 .next_buf
= LCDC_V3_NEXT_BUF
,
60 .transfer_count
= LCDC_V4_TRANSFER_COUNT
,
61 .cur_buf
= LCDC_V4_CUR_BUF
,
62 .next_buf
= LCDC_V4_NEXT_BUF
,
63 .hs_wdth_mask
= 0x3fff,
70 .transfer_count
= LCDC_V4_TRANSFER_COUNT
,
71 .cur_buf
= LCDC_V4_CUR_BUF
,
72 .next_buf
= LCDC_V4_NEXT_BUF
,
73 .hs_wdth_mask
= 0x3fff,
81 void mxsfb_enable_axi_clk(struct mxsfb_drm_private
*mxsfb
)
83 clk_prepare_enable(mxsfb
->clk_axi
);
86 void mxsfb_disable_axi_clk(struct mxsfb_drm_private
*mxsfb
)
88 clk_disable_unprepare(mxsfb
->clk_axi
);
91 static struct drm_framebuffer
*
92 mxsfb_fb_create(struct drm_device
*dev
, struct drm_file
*file_priv
,
93 const struct drm_mode_fb_cmd2
*mode_cmd
)
95 const struct drm_format_info
*info
;
97 info
= drm_get_format_info(dev
, mode_cmd
);
99 return ERR_PTR(-EINVAL
);
101 if (mode_cmd
->width
* info
->cpp
[0] != mode_cmd
->pitches
[0]) {
102 dev_dbg(dev
->dev
, "Invalid pitch: fb width must match pitch\n");
103 return ERR_PTR(-EINVAL
);
106 return drm_gem_fb_create(dev
, file_priv
, mode_cmd
);
109 static const struct drm_mode_config_funcs mxsfb_mode_config_funcs
= {
110 .fb_create
= mxsfb_fb_create
,
111 .atomic_check
= drm_atomic_helper_check
,
112 .atomic_commit
= drm_atomic_helper_commit
,
115 static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers
= {
116 .atomic_commit_tail
= drm_atomic_helper_commit_tail_rpm
,
119 static int mxsfb_attach_bridge(struct mxsfb_drm_private
*mxsfb
)
121 struct drm_device
*drm
= mxsfb
->drm
;
122 struct drm_connector_list_iter iter
;
123 struct drm_panel
*panel
;
124 struct drm_bridge
*bridge
;
127 ret
= drm_of_find_panel_or_bridge(drm
->dev
->of_node
, 0, 0, &panel
,
133 bridge
= devm_drm_panel_bridge_add_typed(drm
->dev
, panel
,
134 DRM_MODE_CONNECTOR_DPI
);
136 return PTR_ERR(bridge
);
142 ret
= drm_bridge_attach(&mxsfb
->encoder
, bridge
, NULL
, 0);
144 return dev_err_probe(drm
->dev
, ret
, "Failed to attach bridge\n");
146 mxsfb
->bridge
= bridge
;
149 * Get hold of the connector. This is a bit of a hack, until the bridge
150 * API gives us bus flags and formats.
152 drm_connector_list_iter_begin(drm
, &iter
);
153 mxsfb
->connector
= drm_connector_list_iter_next(&iter
);
154 drm_connector_list_iter_end(&iter
);
159 static irqreturn_t
mxsfb_irq_handler(int irq
, void *data
)
161 struct drm_device
*drm
= data
;
162 struct mxsfb_drm_private
*mxsfb
= drm
->dev_private
;
166 reg
= readl(mxsfb
->base
+ LCDC_CTRL1
);
168 if (reg
& CTRL1_CUR_FRAME_DONE_IRQ
) {
169 drm_crtc_handle_vblank(&mxsfb
->crtc
);
170 if (mxsfb
->crc_active
) {
171 crc
= readl(mxsfb
->base
+ LCDC_V4_CRC_STAT
);
172 vbc
= drm_crtc_accurate_vblank_count(&mxsfb
->crtc
);
173 drm_crtc_add_crc_entry(&mxsfb
->crtc
, true, vbc
, &crc
);
177 writel(CTRL1_CUR_FRAME_DONE_IRQ
, mxsfb
->base
+ LCDC_CTRL1
+ REG_CLR
);
182 static void mxsfb_irq_disable(struct drm_device
*drm
)
184 struct mxsfb_drm_private
*mxsfb
= drm
->dev_private
;
186 mxsfb_enable_axi_clk(mxsfb
);
188 /* Disable and clear VBLANK IRQ */
189 writel(CTRL1_CUR_FRAME_DONE_IRQ_EN
, mxsfb
->base
+ LCDC_CTRL1
+ REG_CLR
);
190 writel(CTRL1_CUR_FRAME_DONE_IRQ
, mxsfb
->base
+ LCDC_CTRL1
+ REG_CLR
);
192 mxsfb_disable_axi_clk(mxsfb
);
195 static int mxsfb_irq_install(struct drm_device
*dev
, int irq
)
197 if (irq
== IRQ_NOTCONNECTED
)
200 mxsfb_irq_disable(dev
);
202 return request_irq(irq
, mxsfb_irq_handler
, 0, dev
->driver
->name
, dev
);
205 static void mxsfb_irq_uninstall(struct drm_device
*dev
)
207 struct mxsfb_drm_private
*mxsfb
= dev
->dev_private
;
209 mxsfb_irq_disable(dev
);
210 free_irq(mxsfb
->irq
, dev
);
213 static int mxsfb_load(struct drm_device
*drm
,
214 const struct mxsfb_devdata
*devdata
)
216 struct platform_device
*pdev
= to_platform_device(drm
->dev
);
217 struct mxsfb_drm_private
*mxsfb
;
218 struct resource
*res
;
221 mxsfb
= devm_kzalloc(&pdev
->dev
, sizeof(*mxsfb
), GFP_KERNEL
);
226 drm
->dev_private
= mxsfb
;
227 mxsfb
->devdata
= devdata
;
229 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
230 mxsfb
->base
= devm_ioremap_resource(drm
->dev
, res
);
231 if (IS_ERR(mxsfb
->base
))
232 return PTR_ERR(mxsfb
->base
);
234 mxsfb
->clk
= devm_clk_get(drm
->dev
, NULL
);
235 if (IS_ERR(mxsfb
->clk
))
236 return PTR_ERR(mxsfb
->clk
);
238 mxsfb
->clk_axi
= devm_clk_get_optional(drm
->dev
, "axi");
239 if (IS_ERR(mxsfb
->clk_axi
))
240 return PTR_ERR(mxsfb
->clk_axi
);
242 mxsfb
->clk_disp_axi
= devm_clk_get(drm
->dev
, "disp_axi");
243 if (IS_ERR(mxsfb
->clk_disp_axi
))
244 mxsfb
->clk_disp_axi
= NULL
;
246 ret
= dma_set_mask_and_coherent(drm
->dev
, DMA_BIT_MASK(32));
250 pm_runtime_enable(drm
->dev
);
253 ret
= drmm_mode_config_init(drm
);
255 dev_err(drm
->dev
, "Failed to initialize mode config\n");
259 ret
= mxsfb_kms_init(mxsfb
);
261 dev_err(drm
->dev
, "Failed to initialize KMS pipeline\n");
265 ret
= drm_vblank_init(drm
, drm
->mode_config
.num_crtc
);
267 dev_err(drm
->dev
, "Failed to initialise vblank\n");
271 /* Start with vertical blanking interrupt reporting disabled. */
272 drm_crtc_vblank_off(&mxsfb
->crtc
);
274 ret
= mxsfb_attach_bridge(mxsfb
);
276 dev_err_probe(drm
->dev
, ret
, "Cannot connect bridge\n");
280 drm
->mode_config
.min_width
= MXSFB_MIN_XRES
;
281 drm
->mode_config
.min_height
= MXSFB_MIN_YRES
;
282 drm
->mode_config
.max_width
= MXSFB_MAX_XRES
;
283 drm
->mode_config
.max_height
= MXSFB_MAX_YRES
;
284 drm
->mode_config
.funcs
= &mxsfb_mode_config_funcs
;
285 drm
->mode_config
.helper_private
= &mxsfb_mode_config_helpers
;
287 drm_mode_config_reset(drm
);
289 ret
= platform_get_irq(pdev
, 0);
294 pm_runtime_get_sync(drm
->dev
);
295 ret
= mxsfb_irq_install(drm
, mxsfb
->irq
);
296 pm_runtime_put_sync(drm
->dev
);
299 dev_err(drm
->dev
, "Failed to install IRQ handler\n");
303 drm_kms_helper_poll_init(drm
);
305 platform_set_drvdata(pdev
, drm
);
307 drm_helper_hpd_irq_event(drm
);
312 pm_runtime_disable(drm
->dev
);
317 static void mxsfb_unload(struct drm_device
*drm
)
319 drm_kms_helper_poll_fini(drm
);
321 pm_runtime_get_sync(drm
->dev
);
322 mxsfb_irq_uninstall(drm
);
323 pm_runtime_put_sync(drm
->dev
);
325 drm
->dev_private
= NULL
;
327 pm_runtime_disable(drm
->dev
);
330 DEFINE_DRM_GEM_DMA_FOPS(fops
);
332 static const struct drm_driver mxsfb_driver
= {
333 .driver_features
= DRIVER_GEM
| DRIVER_MODESET
| DRIVER_ATOMIC
,
334 DRM_GEM_DMA_DRIVER_OPS
,
335 DRM_FBDEV_DMA_DRIVER_OPS
,
338 .desc
= "MXSFB Controller DRM",
344 static const struct of_device_id mxsfb_dt_ids
[] = {
345 { .compatible
= "fsl,imx23-lcdif", .data
= &mxsfb_devdata
[MXSFB_V3
], },
346 { .compatible
= "fsl,imx28-lcdif", .data
= &mxsfb_devdata
[MXSFB_V4
], },
347 { .compatible
= "fsl,imx6sx-lcdif", .data
= &mxsfb_devdata
[MXSFB_V6
], },
350 MODULE_DEVICE_TABLE(of
, mxsfb_dt_ids
);
352 static int mxsfb_probe(struct platform_device
*pdev
)
354 struct drm_device
*drm
;
357 drm
= drm_dev_alloc(&mxsfb_driver
, &pdev
->dev
);
361 ret
= mxsfb_load(drm
, device_get_match_data(&pdev
->dev
));
365 ret
= drm_dev_register(drm
, 0);
369 drm_client_setup(drm
, NULL
);
381 static void mxsfb_remove(struct platform_device
*pdev
)
383 struct drm_device
*drm
= platform_get_drvdata(pdev
);
385 drm_dev_unregister(drm
);
386 drm_atomic_helper_shutdown(drm
);
391 static void mxsfb_shutdown(struct platform_device
*pdev
)
393 struct drm_device
*drm
= platform_get_drvdata(pdev
);
395 drm_atomic_helper_shutdown(drm
);
398 #ifdef CONFIG_PM_SLEEP
399 static int mxsfb_suspend(struct device
*dev
)
401 struct drm_device
*drm
= dev_get_drvdata(dev
);
403 return drm_mode_config_helper_suspend(drm
);
406 static int mxsfb_resume(struct device
*dev
)
408 struct drm_device
*drm
= dev_get_drvdata(dev
);
410 return drm_mode_config_helper_resume(drm
);
414 static const struct dev_pm_ops mxsfb_pm_ops
= {
415 SET_SYSTEM_SLEEP_PM_OPS(mxsfb_suspend
, mxsfb_resume
)
418 static struct platform_driver mxsfb_platform_driver
= {
419 .probe
= mxsfb_probe
,
420 .remove
= mxsfb_remove
,
421 .shutdown
= mxsfb_shutdown
,
424 .of_match_table
= mxsfb_dt_ids
,
429 drm_module_platform_driver(mxsfb_platform_driver
);
431 MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
432 MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
433 MODULE_LICENSE("GPL");