1c942fddfSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
245d59d70SMarek Vasut /*
345d59d70SMarek Vasut * Copyright (C) 2016 Marek Vasut <marex@denx.de>
445d59d70SMarek Vasut *
545d59d70SMarek Vasut * This code is based on drivers/video/fbdev/mxsfb.c :
645d59d70SMarek Vasut * Copyright (C) 2010 Juergen Beisert, Pengutronix
745d59d70SMarek Vasut * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
845d59d70SMarek Vasut * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
945d59d70SMarek Vasut */
1045d59d70SMarek Vasut
1145d59d70SMarek Vasut #include <linux/clk.h>
12d5742c6cSSam Ravnborg #include <linux/dma-mapping.h>
13ae1ed009SLaurent Pinchart #include <linux/io.h>
14d5742c6cSSam Ravnborg #include <linux/module.h>
1545d59d70SMarek Vasut #include <linux/of_device.h>
16ae1ed009SLaurent Pinchart #include <linux/platform_device.h>
1745d59d70SMarek Vasut #include <linux/pm_runtime.h>
1845d59d70SMarek Vasut
1945d59d70SMarek Vasut #include <drm/drm_atomic_helper.h>
20ae1ed009SLaurent Pinchart #include <drm/drm_bridge.h>
21ae1ed009SLaurent Pinchart #include <drm/drm_connector.h>
22d5742c6cSSam Ravnborg #include <drm/drm_drv.h>
235fe96f6aSThomas Zimmermann #include <drm/drm_fbdev_dma.h>
24d5a0c816SStefan Agner #include <drm/drm_fourcc.h>
254a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
2698f3eac5SNoralf Trønnes #include <drm/drm_gem_framebuffer_helper.h>
27ae1ed009SLaurent Pinchart #include <drm/drm_mode_config.h>
28d405054dSJavier Martinez Canillas #include <drm/drm_module.h>
2945d59d70SMarek Vasut #include <drm/drm_of.h>
30fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
31d5742c6cSSam Ravnborg #include <drm/drm_vblank.h>
3245d59d70SMarek Vasut
3345d59d70SMarek Vasut #include "mxsfb_drv.h"
3445d59d70SMarek Vasut #include "mxsfb_regs.h"
3545d59d70SMarek Vasut
3645d59d70SMarek Vasut enum mxsfb_devtype {
3745d59d70SMarek Vasut MXSFB_V3,
3845d59d70SMarek Vasut MXSFB_V4,
39f6d94e71SLaurent Pinchart /*
40f6d94e71SLaurent Pinchart * Starting at i.MX6 the hardware version register is gone, use the
41f6d94e71SLaurent Pinchart * i.MX family number as the version.
42f6d94e71SLaurent Pinchart */
43f6d94e71SLaurent Pinchart MXSFB_V6,
4445d59d70SMarek Vasut };
4545d59d70SMarek Vasut
4645d59d70SMarek Vasut static const struct mxsfb_devdata mxsfb_devdata[] = {
4745d59d70SMarek Vasut [MXSFB_V3] = {
4845d59d70SMarek Vasut .transfer_count = LCDC_V3_TRANSFER_COUNT,
4945d59d70SMarek Vasut .cur_buf = LCDC_V3_CUR_BUF,
5045d59d70SMarek Vasut .next_buf = LCDC_V3_NEXT_BUF,
5145d59d70SMarek Vasut .hs_wdth_mask = 0xff,
5245d59d70SMarek Vasut .hs_wdth_shift = 24,
5363aa581cSLaurent Pinchart .has_overlay = false,
549891cb54SMarek Vasut .has_ctrl2 = false,
5505ecc678SMarek Vasut .has_crc32 = false,
5645d59d70SMarek Vasut },
5745d59d70SMarek Vasut [MXSFB_V4] = {
5845d59d70SMarek Vasut .transfer_count = LCDC_V4_TRANSFER_COUNT,
5945d59d70SMarek Vasut .cur_buf = LCDC_V4_CUR_BUF,
6045d59d70SMarek Vasut .next_buf = LCDC_V4_NEXT_BUF,
6145d59d70SMarek Vasut .hs_wdth_mask = 0x3fff,
6245d59d70SMarek Vasut .hs_wdth_shift = 18,
6363aa581cSLaurent Pinchart .has_overlay = false,
649891cb54SMarek Vasut .has_ctrl2 = true,
6505ecc678SMarek Vasut .has_crc32 = true,
6645d59d70SMarek Vasut },
67f6d94e71SLaurent Pinchart [MXSFB_V6] = {
68f6d94e71SLaurent Pinchart .transfer_count = LCDC_V4_TRANSFER_COUNT,
69f6d94e71SLaurent Pinchart .cur_buf = LCDC_V4_CUR_BUF,
70f6d94e71SLaurent Pinchart .next_buf = LCDC_V4_NEXT_BUF,
71f6d94e71SLaurent Pinchart .hs_wdth_mask = 0x3fff,
72f6d94e71SLaurent Pinchart .hs_wdth_shift = 18,
7363aa581cSLaurent Pinchart .has_overlay = true,
749891cb54SMarek Vasut .has_ctrl2 = true,
7505ecc678SMarek Vasut .has_crc32 = true,
76f6d94e71SLaurent Pinchart },
7745d59d70SMarek Vasut };
7845d59d70SMarek Vasut
mxsfb_enable_axi_clk(struct mxsfb_drm_private * mxsfb)7945d59d70SMarek Vasut void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
8045d59d70SMarek Vasut {
8145d59d70SMarek Vasut clk_prepare_enable(mxsfb->clk_axi);
8245d59d70SMarek Vasut }
8345d59d70SMarek Vasut
mxsfb_disable_axi_clk(struct mxsfb_drm_private * mxsfb)8445d59d70SMarek Vasut void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb)
8545d59d70SMarek Vasut {
8645d59d70SMarek Vasut clk_disable_unprepare(mxsfb->clk_axi);
8745d59d70SMarek Vasut }
8845d59d70SMarek Vasut
89d5a0c816SStefan Agner static struct drm_framebuffer *
mxsfb_fb_create(struct drm_device * dev,struct drm_file * file_priv,const struct drm_mode_fb_cmd2 * mode_cmd)90d5a0c816SStefan Agner mxsfb_fb_create(struct drm_device *dev, struct drm_file *file_priv,
91d5a0c816SStefan Agner const struct drm_mode_fb_cmd2 *mode_cmd)
92d5a0c816SStefan Agner {
93d5a0c816SStefan Agner const struct drm_format_info *info;
94d5a0c816SStefan Agner
95d5a0c816SStefan Agner info = drm_get_format_info(dev, mode_cmd);
96d5a0c816SStefan Agner if (!info)
97d5a0c816SStefan Agner return ERR_PTR(-EINVAL);
98d5a0c816SStefan Agner
99d5a0c816SStefan Agner if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) {
100d5a0c816SStefan Agner dev_dbg(dev->dev, "Invalid pitch: fb width must match pitch\n");
101d5a0c816SStefan Agner return ERR_PTR(-EINVAL);
102d5a0c816SStefan Agner }
103d5a0c816SStefan Agner
104d5a0c816SStefan Agner return drm_gem_fb_create(dev, file_priv, mode_cmd);
105d5a0c816SStefan Agner }
106d5a0c816SStefan Agner
10745d59d70SMarek Vasut static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
108d5a0c816SStefan Agner .fb_create = mxsfb_fb_create,
10945d59d70SMarek Vasut .atomic_check = drm_atomic_helper_check,
11045d59d70SMarek Vasut .atomic_commit = drm_atomic_helper_commit,
11145d59d70SMarek Vasut };
11245d59d70SMarek Vasut
1139f19fd3bSLeonard Crestez static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = {
1149f19fd3bSLeonard Crestez .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
1159f19fd3bSLeonard Crestez };
1169f19fd3bSLeonard Crestez
mxsfb_attach_bridge(struct mxsfb_drm_private * mxsfb)117c42001e3SLaurent Pinchart static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
118c42001e3SLaurent Pinchart {
119c42001e3SLaurent Pinchart struct drm_device *drm = mxsfb->drm;
120c42001e3SLaurent Pinchart struct drm_connector_list_iter iter;
121c42001e3SLaurent Pinchart struct drm_panel *panel;
122c42001e3SLaurent Pinchart struct drm_bridge *bridge;
123c42001e3SLaurent Pinchart int ret;
124c42001e3SLaurent Pinchart
125c42001e3SLaurent Pinchart ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
126c42001e3SLaurent Pinchart &bridge);
127c42001e3SLaurent Pinchart if (ret)
128c42001e3SLaurent Pinchart return ret;
129c42001e3SLaurent Pinchart
130c42001e3SLaurent Pinchart if (panel) {
131c42001e3SLaurent Pinchart bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
132c42001e3SLaurent Pinchart DRM_MODE_CONNECTOR_DPI);
133c42001e3SLaurent Pinchart if (IS_ERR(bridge))
134c42001e3SLaurent Pinchart return PTR_ERR(bridge);
135c42001e3SLaurent Pinchart }
136c42001e3SLaurent Pinchart
137c42001e3SLaurent Pinchart if (!bridge)
138c42001e3SLaurent Pinchart return -ENODEV;
139c42001e3SLaurent Pinchart
140ae1ed009SLaurent Pinchart ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0);
141ee46d16dSGuido Günther if (ret)
142ee46d16dSGuido Günther return dev_err_probe(drm->dev, ret, "Failed to attach bridge\n");
143c42001e3SLaurent Pinchart
144c42001e3SLaurent Pinchart mxsfb->bridge = bridge;
145c42001e3SLaurent Pinchart
146c42001e3SLaurent Pinchart /*
147c42001e3SLaurent Pinchart * Get hold of the connector. This is a bit of a hack, until the bridge
148c42001e3SLaurent Pinchart * API gives us bus flags and formats.
149c42001e3SLaurent Pinchart */
150c42001e3SLaurent Pinchart drm_connector_list_iter_begin(drm, &iter);
151c42001e3SLaurent Pinchart mxsfb->connector = drm_connector_list_iter_next(&iter);
152c42001e3SLaurent Pinchart drm_connector_list_iter_end(&iter);
153c42001e3SLaurent Pinchart
154c42001e3SLaurent Pinchart return 0;
155c42001e3SLaurent Pinchart }
156c42001e3SLaurent Pinchart
mxsfb_irq_handler(int irq,void * data)1575fc40f41SThomas Zimmermann static irqreturn_t mxsfb_irq_handler(int irq, void *data)
1585fc40f41SThomas Zimmermann {
1595fc40f41SThomas Zimmermann struct drm_device *drm = data;
1605fc40f41SThomas Zimmermann struct mxsfb_drm_private *mxsfb = drm->dev_private;
16105ecc678SMarek Vasut u32 reg, crc;
16205ecc678SMarek Vasut u64 vbc;
1635fc40f41SThomas Zimmermann
1645fc40f41SThomas Zimmermann reg = readl(mxsfb->base + LCDC_CTRL1);
1655fc40f41SThomas Zimmermann
16605ecc678SMarek Vasut if (reg & CTRL1_CUR_FRAME_DONE_IRQ) {
1675fc40f41SThomas Zimmermann drm_crtc_handle_vblank(&mxsfb->crtc);
16805ecc678SMarek Vasut if (mxsfb->crc_active) {
16905ecc678SMarek Vasut crc = readl(mxsfb->base + LCDC_V4_CRC_STAT);
17005ecc678SMarek Vasut vbc = drm_crtc_accurate_vblank_count(&mxsfb->crtc);
17105ecc678SMarek Vasut drm_crtc_add_crc_entry(&mxsfb->crtc, true, vbc, &crc);
17205ecc678SMarek Vasut }
17305ecc678SMarek Vasut }
1745fc40f41SThomas Zimmermann
1755fc40f41SThomas Zimmermann writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
1765fc40f41SThomas Zimmermann
1775fc40f41SThomas Zimmermann return IRQ_HANDLED;
1785fc40f41SThomas Zimmermann }
1795fc40f41SThomas Zimmermann
mxsfb_irq_disable(struct drm_device * drm)1805fc40f41SThomas Zimmermann static void mxsfb_irq_disable(struct drm_device *drm)
1815fc40f41SThomas Zimmermann {
1825fc40f41SThomas Zimmermann struct mxsfb_drm_private *mxsfb = drm->dev_private;
1835fc40f41SThomas Zimmermann
1845fc40f41SThomas Zimmermann mxsfb_enable_axi_clk(mxsfb);
1853cfc1830SMarek Vasut
1863cfc1830SMarek Vasut /* Disable and clear VBLANK IRQ */
1873cfc1830SMarek Vasut writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
1883cfc1830SMarek Vasut writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
1893cfc1830SMarek Vasut
1905fc40f41SThomas Zimmermann mxsfb_disable_axi_clk(mxsfb);
1915fc40f41SThomas Zimmermann }
1925fc40f41SThomas Zimmermann
mxsfb_irq_install(struct drm_device * dev,int irq)1935fc40f41SThomas Zimmermann static int mxsfb_irq_install(struct drm_device *dev, int irq)
1945fc40f41SThomas Zimmermann {
1955fc40f41SThomas Zimmermann if (irq == IRQ_NOTCONNECTED)
1965fc40f41SThomas Zimmermann return -ENOTCONN;
1975fc40f41SThomas Zimmermann
1985fc40f41SThomas Zimmermann mxsfb_irq_disable(dev);
1995fc40f41SThomas Zimmermann
2005fc40f41SThomas Zimmermann return request_irq(irq, mxsfb_irq_handler, 0, dev->driver->name, dev);
2015fc40f41SThomas Zimmermann }
2025fc40f41SThomas Zimmermann
mxsfb_irq_uninstall(struct drm_device * dev)2035fc40f41SThomas Zimmermann static void mxsfb_irq_uninstall(struct drm_device *dev)
2045fc40f41SThomas Zimmermann {
2055fc40f41SThomas Zimmermann struct mxsfb_drm_private *mxsfb = dev->dev_private;
2065fc40f41SThomas Zimmermann
2075fc40f41SThomas Zimmermann mxsfb_irq_disable(dev);
2085fc40f41SThomas Zimmermann free_irq(mxsfb->irq, dev);
2095fc40f41SThomas Zimmermann }
2105fc40f41SThomas Zimmermann
mxsfb_load(struct drm_device * drm,const struct mxsfb_devdata * devdata)211c6ddee82SLaurent Pinchart static int mxsfb_load(struct drm_device *drm,
212c6ddee82SLaurent Pinchart const struct mxsfb_devdata *devdata)
21345d59d70SMarek Vasut {
21445d59d70SMarek Vasut struct platform_device *pdev = to_platform_device(drm->dev);
21545d59d70SMarek Vasut struct mxsfb_drm_private *mxsfb;
21645d59d70SMarek Vasut struct resource *res;
21745d59d70SMarek Vasut int ret;
21845d59d70SMarek Vasut
21945d59d70SMarek Vasut mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL);
22045d59d70SMarek Vasut if (!mxsfb)
22145d59d70SMarek Vasut return -ENOMEM;
22245d59d70SMarek Vasut
223c42001e3SLaurent Pinchart mxsfb->drm = drm;
22445d59d70SMarek Vasut drm->dev_private = mxsfb;
225c6ddee82SLaurent Pinchart mxsfb->devdata = devdata;
22645d59d70SMarek Vasut
22745d59d70SMarek Vasut res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
22845d59d70SMarek Vasut mxsfb->base = devm_ioremap_resource(drm->dev, res);
22945d59d70SMarek Vasut if (IS_ERR(mxsfb->base))
23045d59d70SMarek Vasut return PTR_ERR(mxsfb->base);
23145d59d70SMarek Vasut
23245d59d70SMarek Vasut mxsfb->clk = devm_clk_get(drm->dev, NULL);
23345d59d70SMarek Vasut if (IS_ERR(mxsfb->clk))
23445d59d70SMarek Vasut return PTR_ERR(mxsfb->clk);
23545d59d70SMarek Vasut
2366d6defd4SUwe Kleine-König mxsfb->clk_axi = devm_clk_get_optional(drm->dev, "axi");
23745d59d70SMarek Vasut if (IS_ERR(mxsfb->clk_axi))
2386d6defd4SUwe Kleine-König return PTR_ERR(mxsfb->clk_axi);
23945d59d70SMarek Vasut
24045d59d70SMarek Vasut mxsfb->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
24145d59d70SMarek Vasut if (IS_ERR(mxsfb->clk_disp_axi))
24245d59d70SMarek Vasut mxsfb->clk_disp_axi = NULL;
24345d59d70SMarek Vasut
24445d59d70SMarek Vasut ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
24545d59d70SMarek Vasut if (ret)
24645d59d70SMarek Vasut return ret;
24745d59d70SMarek Vasut
24845d59d70SMarek Vasut pm_runtime_enable(drm->dev);
24945d59d70SMarek Vasut
25045d59d70SMarek Vasut /* Modeset init */
25145d59d70SMarek Vasut drm_mode_config_init(drm);
25245d59d70SMarek Vasut
253ae1ed009SLaurent Pinchart ret = mxsfb_kms_init(mxsfb);
25445d59d70SMarek Vasut if (ret < 0) {
255ae1ed009SLaurent Pinchart dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
25645d59d70SMarek Vasut goto err_vblank;
25745d59d70SMarek Vasut }
25845d59d70SMarek Vasut
259b9f59376SLaurent Pinchart ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
260b9f59376SLaurent Pinchart if (ret < 0) {
261b9f59376SLaurent Pinchart dev_err(drm->dev, "Failed to initialise vblank\n");
262b9f59376SLaurent Pinchart goto err_vblank;
263b9f59376SLaurent Pinchart }
264b9f59376SLaurent Pinchart
265b9f59376SLaurent Pinchart /* Start with vertical blanking interrupt reporting disabled. */
266b9f59376SLaurent Pinchart drm_crtc_vblank_off(&mxsfb->crtc);
267b9f59376SLaurent Pinchart
268c42001e3SLaurent Pinchart ret = mxsfb_attach_bridge(mxsfb);
269d0234043SRobert Chiras if (ret) {
270c5e804baSAlexander Stein dev_err_probe(drm->dev, ret, "Cannot connect bridge\n");
271d0234043SRobert Chiras goto err_vblank;
272d0234043SRobert Chiras }
27345d59d70SMarek Vasut
27445d59d70SMarek Vasut drm->mode_config.min_width = MXSFB_MIN_XRES;
27545d59d70SMarek Vasut drm->mode_config.min_height = MXSFB_MIN_YRES;
27645d59d70SMarek Vasut drm->mode_config.max_width = MXSFB_MAX_XRES;
27745d59d70SMarek Vasut drm->mode_config.max_height = MXSFB_MAX_YRES;
27845d59d70SMarek Vasut drm->mode_config.funcs = &mxsfb_mode_config_funcs;
2799f19fd3bSLeonard Crestez drm->mode_config.helper_private = &mxsfb_mode_config_helpers;
28045d59d70SMarek Vasut
28145d59d70SMarek Vasut drm_mode_config_reset(drm);
28245d59d70SMarek Vasut
2835fc40f41SThomas Zimmermann ret = platform_get_irq(pdev, 0);
2845fc40f41SThomas Zimmermann if (ret < 0)
2855fc40f41SThomas Zimmermann goto err_vblank;
2865fc40f41SThomas Zimmermann mxsfb->irq = ret;
2875fc40f41SThomas Zimmermann
28845d59d70SMarek Vasut pm_runtime_get_sync(drm->dev);
2895fc40f41SThomas Zimmermann ret = mxsfb_irq_install(drm, mxsfb->irq);
29045d59d70SMarek Vasut pm_runtime_put_sync(drm->dev);
29145d59d70SMarek Vasut
29245d59d70SMarek Vasut if (ret < 0) {
29345d59d70SMarek Vasut dev_err(drm->dev, "Failed to install IRQ handler\n");
294c42001e3SLaurent Pinchart goto err_vblank;
29545d59d70SMarek Vasut }
29645d59d70SMarek Vasut
29745d59d70SMarek Vasut drm_kms_helper_poll_init(drm);
29845d59d70SMarek Vasut
29945d59d70SMarek Vasut platform_set_drvdata(pdev, drm);
30045d59d70SMarek Vasut
30145d59d70SMarek Vasut drm_helper_hpd_irq_event(drm);
30245d59d70SMarek Vasut
30345d59d70SMarek Vasut return 0;
30445d59d70SMarek Vasut
30545d59d70SMarek Vasut err_vblank:
30645d59d70SMarek Vasut pm_runtime_disable(drm->dev);
30745d59d70SMarek Vasut
30845d59d70SMarek Vasut return ret;
30945d59d70SMarek Vasut }
31045d59d70SMarek Vasut
mxsfb_unload(struct drm_device * drm)31145d59d70SMarek Vasut static void mxsfb_unload(struct drm_device *drm)
31245d59d70SMarek Vasut {
31345d59d70SMarek Vasut drm_kms_helper_poll_fini(drm);
31445d59d70SMarek Vasut drm_mode_config_cleanup(drm);
31545d59d70SMarek Vasut
31645d59d70SMarek Vasut pm_runtime_get_sync(drm->dev);
3175fc40f41SThomas Zimmermann mxsfb_irq_uninstall(drm);
31845d59d70SMarek Vasut pm_runtime_put_sync(drm->dev);
31945d59d70SMarek Vasut
32045d59d70SMarek Vasut drm->dev_private = NULL;
32145d59d70SMarek Vasut
32245d59d70SMarek Vasut pm_runtime_disable(drm->dev);
32345d59d70SMarek Vasut }
32445d59d70SMarek Vasut
3254a83c26aSDanilo Krummrich DEFINE_DRM_GEM_DMA_FOPS(fops);
32645d59d70SMarek Vasut
32770a59dd8SDaniel Vetter static const struct drm_driver mxsfb_driver = {
3280424fdafSDaniel Vetter .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
3294a83c26aSDanilo Krummrich DRM_GEM_DMA_DRIVER_OPS,
33045d59d70SMarek Vasut .fops = &fops,
33145d59d70SMarek Vasut .name = "mxsfb-drm",
33245d59d70SMarek Vasut .desc = "MXSFB Controller DRM",
33345d59d70SMarek Vasut .date = "20160824",
33445d59d70SMarek Vasut .major = 1,
33545d59d70SMarek Vasut .minor = 0,
33645d59d70SMarek Vasut };
33745d59d70SMarek Vasut
33845d59d70SMarek Vasut static const struct of_device_id mxsfb_dt_ids[] = {
339c6ddee82SLaurent Pinchart { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], },
340c6ddee82SLaurent Pinchart { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], },
341c6ddee82SLaurent Pinchart { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], },
34245d59d70SMarek Vasut { /* sentinel */ }
34345d59d70SMarek Vasut };
34445d59d70SMarek Vasut MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
34545d59d70SMarek Vasut
mxsfb_probe(struct platform_device * pdev)34645d59d70SMarek Vasut static int mxsfb_probe(struct platform_device *pdev)
34745d59d70SMarek Vasut {
34845d59d70SMarek Vasut struct drm_device *drm;
34945d59d70SMarek Vasut const struct of_device_id *of_id =
35045d59d70SMarek Vasut of_match_device(mxsfb_dt_ids, &pdev->dev);
35145d59d70SMarek Vasut int ret;
35245d59d70SMarek Vasut
35345d59d70SMarek Vasut if (!pdev->dev.of_node)
35445d59d70SMarek Vasut return -ENODEV;
35545d59d70SMarek Vasut
35645d59d70SMarek Vasut drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
357e89e50acSDan Carpenter if (IS_ERR(drm))
358e89e50acSDan Carpenter return PTR_ERR(drm);
35945d59d70SMarek Vasut
360c6ddee82SLaurent Pinchart ret = mxsfb_load(drm, of_id->data);
36145d59d70SMarek Vasut if (ret)
36245d59d70SMarek Vasut goto err_free;
36345d59d70SMarek Vasut
36445d59d70SMarek Vasut ret = drm_dev_register(drm, 0);
36545d59d70SMarek Vasut if (ret)
36645d59d70SMarek Vasut goto err_unload;
36745d59d70SMarek Vasut
3685fe96f6aSThomas Zimmermann drm_fbdev_dma_setup(drm, 32);
3698e93f102SNoralf Trønnes
37045d59d70SMarek Vasut return 0;
37145d59d70SMarek Vasut
37245d59d70SMarek Vasut err_unload:
37345d59d70SMarek Vasut mxsfb_unload(drm);
37445d59d70SMarek Vasut err_free:
375808bad32SFernando Ramos drm_dev_put(drm);
37645d59d70SMarek Vasut
37745d59d70SMarek Vasut return ret;
37845d59d70SMarek Vasut }
37945d59d70SMarek Vasut
mxsfb_remove(struct platform_device * pdev)380*bd296a59SUwe Kleine-König static void mxsfb_remove(struct platform_device *pdev)
38145d59d70SMarek Vasut {
38245d59d70SMarek Vasut struct drm_device *drm = platform_get_drvdata(pdev);
38345d59d70SMarek Vasut
38445d59d70SMarek Vasut drm_dev_unregister(drm);
38572f6c033SMarek Vasut drm_atomic_helper_shutdown(drm);
38645d59d70SMarek Vasut mxsfb_unload(drm);
387808bad32SFernando Ramos drm_dev_put(drm);
38845d59d70SMarek Vasut }
38945d59d70SMarek Vasut
mxsfb_shutdown(struct platform_device * pdev)390653af51cSMarek Vasut static void mxsfb_shutdown(struct platform_device *pdev)
391653af51cSMarek Vasut {
392653af51cSMarek Vasut struct drm_device *drm = platform_get_drvdata(pdev);
393653af51cSMarek Vasut
394653af51cSMarek Vasut drm_atomic_helper_shutdown(drm);
395653af51cSMarek Vasut }
396653af51cSMarek Vasut
397f0525a1cSLeonard Crestez #ifdef CONFIG_PM_SLEEP
mxsfb_suspend(struct device * dev)398f0525a1cSLeonard Crestez static int mxsfb_suspend(struct device *dev)
399f0525a1cSLeonard Crestez {
400f0525a1cSLeonard Crestez struct drm_device *drm = dev_get_drvdata(dev);
401f0525a1cSLeonard Crestez
402f0525a1cSLeonard Crestez return drm_mode_config_helper_suspend(drm);
403f0525a1cSLeonard Crestez }
404f0525a1cSLeonard Crestez
mxsfb_resume(struct device * dev)405f0525a1cSLeonard Crestez static int mxsfb_resume(struct device *dev)
406f0525a1cSLeonard Crestez {
407f0525a1cSLeonard Crestez struct drm_device *drm = dev_get_drvdata(dev);
408f0525a1cSLeonard Crestez
409f0525a1cSLeonard Crestez return drm_mode_config_helper_resume(drm);
410f0525a1cSLeonard Crestez }
411f0525a1cSLeonard Crestez #endif
412f0525a1cSLeonard Crestez
413f0525a1cSLeonard Crestez static const struct dev_pm_ops mxsfb_pm_ops = {
414f0525a1cSLeonard Crestez SET_SYSTEM_SLEEP_PM_OPS(mxsfb_suspend, mxsfb_resume)
415f0525a1cSLeonard Crestez };
416f0525a1cSLeonard Crestez
41745d59d70SMarek Vasut static struct platform_driver mxsfb_platform_driver = {
41845d59d70SMarek Vasut .probe = mxsfb_probe,
419*bd296a59SUwe Kleine-König .remove_new = mxsfb_remove,
420653af51cSMarek Vasut .shutdown = mxsfb_shutdown,
42145d59d70SMarek Vasut .driver = {
42245d59d70SMarek Vasut .name = "mxsfb",
42345d59d70SMarek Vasut .of_match_table = mxsfb_dt_ids,
424f0525a1cSLeonard Crestez .pm = &mxsfb_pm_ops,
42545d59d70SMarek Vasut },
42645d59d70SMarek Vasut };
42745d59d70SMarek Vasut
428d405054dSJavier Martinez Canillas drm_module_platform_driver(mxsfb_platform_driver);
42945d59d70SMarek Vasut
43045d59d70SMarek Vasut MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
43145d59d70SMarek Vasut MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
43245d59d70SMarek Vasut MODULE_LICENSE("GPL");
433