1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (C) 2016 Marek Vasut <marex@denx.de>
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * This code is based on drivers/video/fbdev/mxsfb.c :
6*4882a593Smuzhiyun * Copyright (C) 2010 Juergen Beisert, Pengutronix
7*4882a593Smuzhiyun * Copyright (C) 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
8*4882a593Smuzhiyun * Copyright (C) 2008 Embedded Alley Solutions, Inc All Rights Reserved.
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <linux/clk.h>
12*4882a593Smuzhiyun #include <linux/dma-mapping.h>
13*4882a593Smuzhiyun #include <linux/io.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun #include <linux/of_device.h>
16*4882a593Smuzhiyun #include <linux/platform_device.h>
17*4882a593Smuzhiyun #include <linux/pm_runtime.h>
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
20*4882a593Smuzhiyun #include <drm/drm_bridge.h>
21*4882a593Smuzhiyun #include <drm/drm_connector.h>
22*4882a593Smuzhiyun #include <drm/drm_drv.h>
23*4882a593Smuzhiyun #include <drm/drm_fb_helper.h>
24*4882a593Smuzhiyun #include <drm/drm_fourcc.h>
25*4882a593Smuzhiyun #include <drm/drm_gem_cma_helper.h>
26*4882a593Smuzhiyun #include <drm/drm_gem_framebuffer_helper.h>
27*4882a593Smuzhiyun #include <drm/drm_irq.h>
28*4882a593Smuzhiyun #include <drm/drm_mode_config.h>
29*4882a593Smuzhiyun #include <drm/drm_of.h>
30*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
31*4882a593Smuzhiyun #include <drm/drm_vblank.h>
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun #include "mxsfb_drv.h"
34*4882a593Smuzhiyun #include "mxsfb_regs.h"
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun enum mxsfb_devtype {
37*4882a593Smuzhiyun MXSFB_V3,
38*4882a593Smuzhiyun MXSFB_V4,
39*4882a593Smuzhiyun /*
40*4882a593Smuzhiyun * Starting at i.MX6 the hardware version register is gone, use the
41*4882a593Smuzhiyun * i.MX family number as the version.
42*4882a593Smuzhiyun */
43*4882a593Smuzhiyun MXSFB_V6,
44*4882a593Smuzhiyun };
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun static const struct mxsfb_devdata mxsfb_devdata[] = {
47*4882a593Smuzhiyun [MXSFB_V3] = {
48*4882a593Smuzhiyun .transfer_count = LCDC_V3_TRANSFER_COUNT,
49*4882a593Smuzhiyun .cur_buf = LCDC_V3_CUR_BUF,
50*4882a593Smuzhiyun .next_buf = LCDC_V3_NEXT_BUF,
51*4882a593Smuzhiyun .hs_wdth_mask = 0xff,
52*4882a593Smuzhiyun .hs_wdth_shift = 24,
53*4882a593Smuzhiyun .has_overlay = false,
54*4882a593Smuzhiyun .has_ctrl2 = false,
55*4882a593Smuzhiyun },
56*4882a593Smuzhiyun [MXSFB_V4] = {
57*4882a593Smuzhiyun .transfer_count = LCDC_V4_TRANSFER_COUNT,
58*4882a593Smuzhiyun .cur_buf = LCDC_V4_CUR_BUF,
59*4882a593Smuzhiyun .next_buf = LCDC_V4_NEXT_BUF,
60*4882a593Smuzhiyun .hs_wdth_mask = 0x3fff,
61*4882a593Smuzhiyun .hs_wdth_shift = 18,
62*4882a593Smuzhiyun .has_overlay = false,
63*4882a593Smuzhiyun .has_ctrl2 = true,
64*4882a593Smuzhiyun },
65*4882a593Smuzhiyun [MXSFB_V6] = {
66*4882a593Smuzhiyun .transfer_count = LCDC_V4_TRANSFER_COUNT,
67*4882a593Smuzhiyun .cur_buf = LCDC_V4_CUR_BUF,
68*4882a593Smuzhiyun .next_buf = LCDC_V4_NEXT_BUF,
69*4882a593Smuzhiyun .hs_wdth_mask = 0x3fff,
70*4882a593Smuzhiyun .hs_wdth_shift = 18,
71*4882a593Smuzhiyun .has_overlay = true,
72*4882a593Smuzhiyun .has_ctrl2 = true,
73*4882a593Smuzhiyun },
74*4882a593Smuzhiyun };
75*4882a593Smuzhiyun
mxsfb_enable_axi_clk(struct mxsfb_drm_private * mxsfb)76*4882a593Smuzhiyun void mxsfb_enable_axi_clk(struct mxsfb_drm_private *mxsfb)
77*4882a593Smuzhiyun {
78*4882a593Smuzhiyun if (mxsfb->clk_axi)
79*4882a593Smuzhiyun clk_prepare_enable(mxsfb->clk_axi);
80*4882a593Smuzhiyun }
81*4882a593Smuzhiyun
mxsfb_disable_axi_clk(struct mxsfb_drm_private * mxsfb)82*4882a593Smuzhiyun void mxsfb_disable_axi_clk(struct mxsfb_drm_private *mxsfb)
83*4882a593Smuzhiyun {
84*4882a593Smuzhiyun if (mxsfb->clk_axi)
85*4882a593Smuzhiyun clk_disable_unprepare(mxsfb->clk_axi);
86*4882a593Smuzhiyun }
87*4882a593Smuzhiyun
88*4882a593Smuzhiyun static struct drm_framebuffer *
mxsfb_fb_create(struct drm_device * dev,struct drm_file * file_priv,const struct drm_mode_fb_cmd2 * mode_cmd)89*4882a593Smuzhiyun mxsfb_fb_create(struct drm_device *dev, struct drm_file *file_priv,
90*4882a593Smuzhiyun const struct drm_mode_fb_cmd2 *mode_cmd)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun const struct drm_format_info *info;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun info = drm_get_format_info(dev, mode_cmd);
95*4882a593Smuzhiyun if (!info)
96*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun if (mode_cmd->width * info->cpp[0] != mode_cmd->pitches[0]) {
99*4882a593Smuzhiyun dev_dbg(dev->dev, "Invalid pitch: fb width must match pitch\n");
100*4882a593Smuzhiyun return ERR_PTR(-EINVAL);
101*4882a593Smuzhiyun }
102*4882a593Smuzhiyun
103*4882a593Smuzhiyun return drm_gem_fb_create(dev, file_priv, mode_cmd);
104*4882a593Smuzhiyun }
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun static const struct drm_mode_config_funcs mxsfb_mode_config_funcs = {
107*4882a593Smuzhiyun .fb_create = mxsfb_fb_create,
108*4882a593Smuzhiyun .atomic_check = drm_atomic_helper_check,
109*4882a593Smuzhiyun .atomic_commit = drm_atomic_helper_commit,
110*4882a593Smuzhiyun };
111*4882a593Smuzhiyun
112*4882a593Smuzhiyun static const struct drm_mode_config_helper_funcs mxsfb_mode_config_helpers = {
113*4882a593Smuzhiyun .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
114*4882a593Smuzhiyun };
115*4882a593Smuzhiyun
mxsfb_attach_bridge(struct mxsfb_drm_private * mxsfb)116*4882a593Smuzhiyun static int mxsfb_attach_bridge(struct mxsfb_drm_private *mxsfb)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun struct drm_device *drm = mxsfb->drm;
119*4882a593Smuzhiyun struct drm_connector_list_iter iter;
120*4882a593Smuzhiyun struct drm_panel *panel;
121*4882a593Smuzhiyun struct drm_bridge *bridge;
122*4882a593Smuzhiyun int ret;
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun ret = drm_of_find_panel_or_bridge(drm->dev->of_node, 0, 0, &panel,
125*4882a593Smuzhiyun &bridge);
126*4882a593Smuzhiyun if (ret)
127*4882a593Smuzhiyun return ret;
128*4882a593Smuzhiyun
129*4882a593Smuzhiyun if (panel) {
130*4882a593Smuzhiyun bridge = devm_drm_panel_bridge_add_typed(drm->dev, panel,
131*4882a593Smuzhiyun DRM_MODE_CONNECTOR_DPI);
132*4882a593Smuzhiyun if (IS_ERR(bridge))
133*4882a593Smuzhiyun return PTR_ERR(bridge);
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun
136*4882a593Smuzhiyun if (!bridge)
137*4882a593Smuzhiyun return -ENODEV;
138*4882a593Smuzhiyun
139*4882a593Smuzhiyun ret = drm_bridge_attach(&mxsfb->encoder, bridge, NULL, 0);
140*4882a593Smuzhiyun if (ret)
141*4882a593Smuzhiyun return dev_err_probe(drm->dev, ret, "Failed to attach bridge\n");
142*4882a593Smuzhiyun
143*4882a593Smuzhiyun mxsfb->bridge = bridge;
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /*
146*4882a593Smuzhiyun * Get hold of the connector. This is a bit of a hack, until the bridge
147*4882a593Smuzhiyun * API gives us bus flags and formats.
148*4882a593Smuzhiyun */
149*4882a593Smuzhiyun drm_connector_list_iter_begin(drm, &iter);
150*4882a593Smuzhiyun mxsfb->connector = drm_connector_list_iter_next(&iter);
151*4882a593Smuzhiyun drm_connector_list_iter_end(&iter);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun return 0;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun
mxsfb_load(struct drm_device * drm,const struct mxsfb_devdata * devdata)156*4882a593Smuzhiyun static int mxsfb_load(struct drm_device *drm,
157*4882a593Smuzhiyun const struct mxsfb_devdata *devdata)
158*4882a593Smuzhiyun {
159*4882a593Smuzhiyun struct platform_device *pdev = to_platform_device(drm->dev);
160*4882a593Smuzhiyun struct mxsfb_drm_private *mxsfb;
161*4882a593Smuzhiyun struct resource *res;
162*4882a593Smuzhiyun int ret;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun mxsfb = devm_kzalloc(&pdev->dev, sizeof(*mxsfb), GFP_KERNEL);
165*4882a593Smuzhiyun if (!mxsfb)
166*4882a593Smuzhiyun return -ENOMEM;
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun mxsfb->drm = drm;
169*4882a593Smuzhiyun drm->dev_private = mxsfb;
170*4882a593Smuzhiyun mxsfb->devdata = devdata;
171*4882a593Smuzhiyun
172*4882a593Smuzhiyun res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
173*4882a593Smuzhiyun mxsfb->base = devm_ioremap_resource(drm->dev, res);
174*4882a593Smuzhiyun if (IS_ERR(mxsfb->base))
175*4882a593Smuzhiyun return PTR_ERR(mxsfb->base);
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun mxsfb->clk = devm_clk_get(drm->dev, NULL);
178*4882a593Smuzhiyun if (IS_ERR(mxsfb->clk))
179*4882a593Smuzhiyun return PTR_ERR(mxsfb->clk);
180*4882a593Smuzhiyun
181*4882a593Smuzhiyun mxsfb->clk_axi = devm_clk_get(drm->dev, "axi");
182*4882a593Smuzhiyun if (IS_ERR(mxsfb->clk_axi))
183*4882a593Smuzhiyun mxsfb->clk_axi = NULL;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun mxsfb->clk_disp_axi = devm_clk_get(drm->dev, "disp_axi");
186*4882a593Smuzhiyun if (IS_ERR(mxsfb->clk_disp_axi))
187*4882a593Smuzhiyun mxsfb->clk_disp_axi = NULL;
188*4882a593Smuzhiyun
189*4882a593Smuzhiyun ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
190*4882a593Smuzhiyun if (ret)
191*4882a593Smuzhiyun return ret;
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun pm_runtime_enable(drm->dev);
194*4882a593Smuzhiyun
195*4882a593Smuzhiyun /* Modeset init */
196*4882a593Smuzhiyun drm_mode_config_init(drm);
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun ret = mxsfb_kms_init(mxsfb);
199*4882a593Smuzhiyun if (ret < 0) {
200*4882a593Smuzhiyun dev_err(drm->dev, "Failed to initialize KMS pipeline\n");
201*4882a593Smuzhiyun goto err_vblank;
202*4882a593Smuzhiyun }
203*4882a593Smuzhiyun
204*4882a593Smuzhiyun ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
205*4882a593Smuzhiyun if (ret < 0) {
206*4882a593Smuzhiyun dev_err(drm->dev, "Failed to initialise vblank\n");
207*4882a593Smuzhiyun goto err_vblank;
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun /* Start with vertical blanking interrupt reporting disabled. */
211*4882a593Smuzhiyun drm_crtc_vblank_off(&mxsfb->crtc);
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun ret = mxsfb_attach_bridge(mxsfb);
214*4882a593Smuzhiyun if (ret) {
215*4882a593Smuzhiyun if (ret != -EPROBE_DEFER)
216*4882a593Smuzhiyun dev_err(drm->dev, "Cannot connect bridge: %d\n", ret);
217*4882a593Smuzhiyun goto err_vblank;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun drm->mode_config.min_width = MXSFB_MIN_XRES;
221*4882a593Smuzhiyun drm->mode_config.min_height = MXSFB_MIN_YRES;
222*4882a593Smuzhiyun drm->mode_config.max_width = MXSFB_MAX_XRES;
223*4882a593Smuzhiyun drm->mode_config.max_height = MXSFB_MAX_YRES;
224*4882a593Smuzhiyun drm->mode_config.funcs = &mxsfb_mode_config_funcs;
225*4882a593Smuzhiyun drm->mode_config.helper_private = &mxsfb_mode_config_helpers;
226*4882a593Smuzhiyun
227*4882a593Smuzhiyun drm_mode_config_reset(drm);
228*4882a593Smuzhiyun
229*4882a593Smuzhiyun pm_runtime_get_sync(drm->dev);
230*4882a593Smuzhiyun ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
231*4882a593Smuzhiyun pm_runtime_put_sync(drm->dev);
232*4882a593Smuzhiyun
233*4882a593Smuzhiyun if (ret < 0) {
234*4882a593Smuzhiyun dev_err(drm->dev, "Failed to install IRQ handler\n");
235*4882a593Smuzhiyun goto err_vblank;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun
238*4882a593Smuzhiyun drm_kms_helper_poll_init(drm);
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun platform_set_drvdata(pdev, drm);
241*4882a593Smuzhiyun
242*4882a593Smuzhiyun drm_helper_hpd_irq_event(drm);
243*4882a593Smuzhiyun
244*4882a593Smuzhiyun return 0;
245*4882a593Smuzhiyun
246*4882a593Smuzhiyun err_vblank:
247*4882a593Smuzhiyun pm_runtime_disable(drm->dev);
248*4882a593Smuzhiyun
249*4882a593Smuzhiyun return ret;
250*4882a593Smuzhiyun }
251*4882a593Smuzhiyun
mxsfb_unload(struct drm_device * drm)252*4882a593Smuzhiyun static void mxsfb_unload(struct drm_device *drm)
253*4882a593Smuzhiyun {
254*4882a593Smuzhiyun drm_kms_helper_poll_fini(drm);
255*4882a593Smuzhiyun drm_mode_config_cleanup(drm);
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun pm_runtime_get_sync(drm->dev);
258*4882a593Smuzhiyun drm_irq_uninstall(drm);
259*4882a593Smuzhiyun pm_runtime_put_sync(drm->dev);
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun drm->dev_private = NULL;
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun pm_runtime_disable(drm->dev);
264*4882a593Smuzhiyun }
265*4882a593Smuzhiyun
mxsfb_irq_disable(struct drm_device * drm)266*4882a593Smuzhiyun static void mxsfb_irq_disable(struct drm_device *drm)
267*4882a593Smuzhiyun {
268*4882a593Smuzhiyun struct mxsfb_drm_private *mxsfb = drm->dev_private;
269*4882a593Smuzhiyun
270*4882a593Smuzhiyun mxsfb_enable_axi_clk(mxsfb);
271*4882a593Smuzhiyun
272*4882a593Smuzhiyun /* Disable and clear VBLANK IRQ */
273*4882a593Smuzhiyun writel(CTRL1_CUR_FRAME_DONE_IRQ_EN, mxsfb->base + LCDC_CTRL1 + REG_CLR);
274*4882a593Smuzhiyun writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
275*4882a593Smuzhiyun
276*4882a593Smuzhiyun mxsfb_disable_axi_clk(mxsfb);
277*4882a593Smuzhiyun }
278*4882a593Smuzhiyun
mxsfb_irq_handler(int irq,void * data)279*4882a593Smuzhiyun static irqreturn_t mxsfb_irq_handler(int irq, void *data)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun struct drm_device *drm = data;
282*4882a593Smuzhiyun struct mxsfb_drm_private *mxsfb = drm->dev_private;
283*4882a593Smuzhiyun u32 reg;
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun reg = readl(mxsfb->base + LCDC_CTRL1);
286*4882a593Smuzhiyun
287*4882a593Smuzhiyun if (reg & CTRL1_CUR_FRAME_DONE_IRQ)
288*4882a593Smuzhiyun drm_crtc_handle_vblank(&mxsfb->crtc);
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun writel(CTRL1_CUR_FRAME_DONE_IRQ, mxsfb->base + LCDC_CTRL1 + REG_CLR);
291*4882a593Smuzhiyun
292*4882a593Smuzhiyun return IRQ_HANDLED;
293*4882a593Smuzhiyun }
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun DEFINE_DRM_GEM_CMA_FOPS(fops);
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun static struct drm_driver mxsfb_driver = {
298*4882a593Smuzhiyun .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
299*4882a593Smuzhiyun .irq_handler = mxsfb_irq_handler,
300*4882a593Smuzhiyun .irq_preinstall = mxsfb_irq_disable,
301*4882a593Smuzhiyun .irq_uninstall = mxsfb_irq_disable,
302*4882a593Smuzhiyun DRM_GEM_CMA_DRIVER_OPS,
303*4882a593Smuzhiyun .fops = &fops,
304*4882a593Smuzhiyun .name = "mxsfb-drm",
305*4882a593Smuzhiyun .desc = "MXSFB Controller DRM",
306*4882a593Smuzhiyun .date = "20160824",
307*4882a593Smuzhiyun .major = 1,
308*4882a593Smuzhiyun .minor = 0,
309*4882a593Smuzhiyun };
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun static const struct of_device_id mxsfb_dt_ids[] = {
312*4882a593Smuzhiyun { .compatible = "fsl,imx23-lcdif", .data = &mxsfb_devdata[MXSFB_V3], },
313*4882a593Smuzhiyun { .compatible = "fsl,imx28-lcdif", .data = &mxsfb_devdata[MXSFB_V4], },
314*4882a593Smuzhiyun { .compatible = "fsl,imx6sx-lcdif", .data = &mxsfb_devdata[MXSFB_V6], },
315*4882a593Smuzhiyun { /* sentinel */ }
316*4882a593Smuzhiyun };
317*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, mxsfb_dt_ids);
318*4882a593Smuzhiyun
mxsfb_probe(struct platform_device * pdev)319*4882a593Smuzhiyun static int mxsfb_probe(struct platform_device *pdev)
320*4882a593Smuzhiyun {
321*4882a593Smuzhiyun struct drm_device *drm;
322*4882a593Smuzhiyun const struct of_device_id *of_id =
323*4882a593Smuzhiyun of_match_device(mxsfb_dt_ids, &pdev->dev);
324*4882a593Smuzhiyun int ret;
325*4882a593Smuzhiyun
326*4882a593Smuzhiyun if (!pdev->dev.of_node)
327*4882a593Smuzhiyun return -ENODEV;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun drm = drm_dev_alloc(&mxsfb_driver, &pdev->dev);
330*4882a593Smuzhiyun if (IS_ERR(drm))
331*4882a593Smuzhiyun return PTR_ERR(drm);
332*4882a593Smuzhiyun
333*4882a593Smuzhiyun ret = mxsfb_load(drm, of_id->data);
334*4882a593Smuzhiyun if (ret)
335*4882a593Smuzhiyun goto err_free;
336*4882a593Smuzhiyun
337*4882a593Smuzhiyun ret = drm_dev_register(drm, 0);
338*4882a593Smuzhiyun if (ret)
339*4882a593Smuzhiyun goto err_unload;
340*4882a593Smuzhiyun
341*4882a593Smuzhiyun drm_fbdev_generic_setup(drm, 32);
342*4882a593Smuzhiyun
343*4882a593Smuzhiyun return 0;
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun err_unload:
346*4882a593Smuzhiyun mxsfb_unload(drm);
347*4882a593Smuzhiyun err_free:
348*4882a593Smuzhiyun drm_dev_put(drm);
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun return ret;
351*4882a593Smuzhiyun }
352*4882a593Smuzhiyun
mxsfb_remove(struct platform_device * pdev)353*4882a593Smuzhiyun static int mxsfb_remove(struct platform_device *pdev)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun struct drm_device *drm = platform_get_drvdata(pdev);
356*4882a593Smuzhiyun
357*4882a593Smuzhiyun drm_dev_unregister(drm);
358*4882a593Smuzhiyun mxsfb_unload(drm);
359*4882a593Smuzhiyun drm_dev_put(drm);
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun return 0;
362*4882a593Smuzhiyun }
363*4882a593Smuzhiyun
364*4882a593Smuzhiyun #ifdef CONFIG_PM_SLEEP
mxsfb_suspend(struct device * dev)365*4882a593Smuzhiyun static int mxsfb_suspend(struct device *dev)
366*4882a593Smuzhiyun {
367*4882a593Smuzhiyun struct drm_device *drm = dev_get_drvdata(dev);
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun return drm_mode_config_helper_suspend(drm);
370*4882a593Smuzhiyun }
371*4882a593Smuzhiyun
mxsfb_resume(struct device * dev)372*4882a593Smuzhiyun static int mxsfb_resume(struct device *dev)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun struct drm_device *drm = dev_get_drvdata(dev);
375*4882a593Smuzhiyun
376*4882a593Smuzhiyun return drm_mode_config_helper_resume(drm);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun #endif
379*4882a593Smuzhiyun
380*4882a593Smuzhiyun static const struct dev_pm_ops mxsfb_pm_ops = {
381*4882a593Smuzhiyun SET_SYSTEM_SLEEP_PM_OPS(mxsfb_suspend, mxsfb_resume)
382*4882a593Smuzhiyun };
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun static struct platform_driver mxsfb_platform_driver = {
385*4882a593Smuzhiyun .probe = mxsfb_probe,
386*4882a593Smuzhiyun .remove = mxsfb_remove,
387*4882a593Smuzhiyun .driver = {
388*4882a593Smuzhiyun .name = "mxsfb",
389*4882a593Smuzhiyun .of_match_table = mxsfb_dt_ids,
390*4882a593Smuzhiyun .pm = &mxsfb_pm_ops,
391*4882a593Smuzhiyun },
392*4882a593Smuzhiyun };
393*4882a593Smuzhiyun
394*4882a593Smuzhiyun module_platform_driver(mxsfb_platform_driver);
395*4882a593Smuzhiyun
396*4882a593Smuzhiyun MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
397*4882a593Smuzhiyun MODULE_DESCRIPTION("Freescale MXS DRM/KMS driver");
398*4882a593Smuzhiyun MODULE_LICENSE("GPL");
399