xref: /OK3568_Linux_fs/kernel/drivers/gpu/drm/arm/hdlcd_drv.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Copyright (C) 2013-2015 ARM Limited
3*4882a593Smuzhiyun  * Author: Liviu Dudau <Liviu.Dudau@arm.com>
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * This file is subject to the terms and conditions of the GNU General Public
6*4882a593Smuzhiyun  * License.  See the file COPYING in the main directory of this archive
7*4882a593Smuzhiyun  * for more details.
8*4882a593Smuzhiyun  *
9*4882a593Smuzhiyun  *  ARM HDLCD Driver
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/module.h>
13*4882a593Smuzhiyun #include <linux/spinlock.h>
14*4882a593Smuzhiyun #include <linux/clk.h>
15*4882a593Smuzhiyun #include <linux/component.h>
16*4882a593Smuzhiyun #include <linux/console.h>
17*4882a593Smuzhiyun #include <linux/dma-mapping.h>
18*4882a593Smuzhiyun #include <linux/list.h>
19*4882a593Smuzhiyun #include <linux/of_graph.h>
20*4882a593Smuzhiyun #include <linux/of_reserved_mem.h>
21*4882a593Smuzhiyun #include <linux/platform_device.h>
22*4882a593Smuzhiyun #include <linux/pm_runtime.h>
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #include <drm/drm_atomic_helper.h>
25*4882a593Smuzhiyun #include <drm/drm_crtc.h>
26*4882a593Smuzhiyun #include <drm/drm_debugfs.h>
27*4882a593Smuzhiyun #include <drm/drm_drv.h>
28*4882a593Smuzhiyun #include <drm/drm_fb_cma_helper.h>
29*4882a593Smuzhiyun #include <drm/drm_fb_helper.h>
30*4882a593Smuzhiyun #include <drm/drm_gem_cma_helper.h>
31*4882a593Smuzhiyun #include <drm/drm_gem_framebuffer_helper.h>
32*4882a593Smuzhiyun #include <drm/drm_irq.h>
33*4882a593Smuzhiyun #include <drm/drm_modeset_helper.h>
34*4882a593Smuzhiyun #include <drm/drm_of.h>
35*4882a593Smuzhiyun #include <drm/drm_probe_helper.h>
36*4882a593Smuzhiyun #include <drm/drm_vblank.h>
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun #include "hdlcd_drv.h"
39*4882a593Smuzhiyun #include "hdlcd_regs.h"
40*4882a593Smuzhiyun 
hdlcd_load(struct drm_device * drm,unsigned long flags)41*4882a593Smuzhiyun static int hdlcd_load(struct drm_device *drm, unsigned long flags)
42*4882a593Smuzhiyun {
43*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
44*4882a593Smuzhiyun 	struct platform_device *pdev = to_platform_device(drm->dev);
45*4882a593Smuzhiyun 	struct resource *res;
46*4882a593Smuzhiyun 	u32 version;
47*4882a593Smuzhiyun 	int ret;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	hdlcd->clk = devm_clk_get(drm->dev, "pxlclk");
50*4882a593Smuzhiyun 	if (IS_ERR(hdlcd->clk))
51*4882a593Smuzhiyun 		return PTR_ERR(hdlcd->clk);
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
54*4882a593Smuzhiyun 	atomic_set(&hdlcd->buffer_underrun_count, 0);
55*4882a593Smuzhiyun 	atomic_set(&hdlcd->bus_error_count, 0);
56*4882a593Smuzhiyun 	atomic_set(&hdlcd->vsync_count, 0);
57*4882a593Smuzhiyun 	atomic_set(&hdlcd->dma_end_count, 0);
58*4882a593Smuzhiyun #endif
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
61*4882a593Smuzhiyun 	hdlcd->mmio = devm_ioremap_resource(drm->dev, res);
62*4882a593Smuzhiyun 	if (IS_ERR(hdlcd->mmio)) {
63*4882a593Smuzhiyun 		DRM_ERROR("failed to map control registers area\n");
64*4882a593Smuzhiyun 		ret = PTR_ERR(hdlcd->mmio);
65*4882a593Smuzhiyun 		hdlcd->mmio = NULL;
66*4882a593Smuzhiyun 		return ret;
67*4882a593Smuzhiyun 	}
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun 	version = hdlcd_read(hdlcd, HDLCD_REG_VERSION);
70*4882a593Smuzhiyun 	if ((version & HDLCD_PRODUCT_MASK) != HDLCD_PRODUCT_ID) {
71*4882a593Smuzhiyun 		DRM_ERROR("unknown product id: 0x%x\n", version);
72*4882a593Smuzhiyun 		return -EINVAL;
73*4882a593Smuzhiyun 	}
74*4882a593Smuzhiyun 	DRM_INFO("found ARM HDLCD version r%dp%d\n",
75*4882a593Smuzhiyun 		(version & HDLCD_VERSION_MAJOR_MASK) >> 8,
76*4882a593Smuzhiyun 		version & HDLCD_VERSION_MINOR_MASK);
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	/* Get the optional framebuffer memory resource */
79*4882a593Smuzhiyun 	ret = of_reserved_mem_device_init(drm->dev);
80*4882a593Smuzhiyun 	if (ret && ret != -ENODEV)
81*4882a593Smuzhiyun 		return ret;
82*4882a593Smuzhiyun 
83*4882a593Smuzhiyun 	ret = dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32));
84*4882a593Smuzhiyun 	if (ret)
85*4882a593Smuzhiyun 		goto setup_fail;
86*4882a593Smuzhiyun 
87*4882a593Smuzhiyun 	ret = hdlcd_setup_crtc(drm);
88*4882a593Smuzhiyun 	if (ret < 0) {
89*4882a593Smuzhiyun 		DRM_ERROR("failed to create crtc\n");
90*4882a593Smuzhiyun 		goto setup_fail;
91*4882a593Smuzhiyun 	}
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun 	ret = drm_irq_install(drm, platform_get_irq(pdev, 0));
94*4882a593Smuzhiyun 	if (ret < 0) {
95*4882a593Smuzhiyun 		DRM_ERROR("failed to install IRQ handler\n");
96*4882a593Smuzhiyun 		goto irq_fail;
97*4882a593Smuzhiyun 	}
98*4882a593Smuzhiyun 
99*4882a593Smuzhiyun 	return 0;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun irq_fail:
102*4882a593Smuzhiyun 	drm_crtc_cleanup(&hdlcd->crtc);
103*4882a593Smuzhiyun setup_fail:
104*4882a593Smuzhiyun 	of_reserved_mem_device_release(drm->dev);
105*4882a593Smuzhiyun 
106*4882a593Smuzhiyun 	return ret;
107*4882a593Smuzhiyun }
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun static const struct drm_mode_config_funcs hdlcd_mode_config_funcs = {
110*4882a593Smuzhiyun 	.fb_create = drm_gem_fb_create,
111*4882a593Smuzhiyun 	.atomic_check = drm_atomic_helper_check,
112*4882a593Smuzhiyun 	.atomic_commit = drm_atomic_helper_commit,
113*4882a593Smuzhiyun };
114*4882a593Smuzhiyun 
hdlcd_setup_mode_config(struct drm_device * drm)115*4882a593Smuzhiyun static void hdlcd_setup_mode_config(struct drm_device *drm)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun 	drm_mode_config_init(drm);
118*4882a593Smuzhiyun 	drm->mode_config.min_width = 0;
119*4882a593Smuzhiyun 	drm->mode_config.min_height = 0;
120*4882a593Smuzhiyun 	drm->mode_config.max_width = HDLCD_MAX_XRES;
121*4882a593Smuzhiyun 	drm->mode_config.max_height = HDLCD_MAX_YRES;
122*4882a593Smuzhiyun 	drm->mode_config.funcs = &hdlcd_mode_config_funcs;
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun 
hdlcd_irq(int irq,void * arg)125*4882a593Smuzhiyun static irqreturn_t hdlcd_irq(int irq, void *arg)
126*4882a593Smuzhiyun {
127*4882a593Smuzhiyun 	struct drm_device *drm = arg;
128*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
129*4882a593Smuzhiyun 	unsigned long irq_status;
130*4882a593Smuzhiyun 
131*4882a593Smuzhiyun 	irq_status = hdlcd_read(hdlcd, HDLCD_REG_INT_STATUS);
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
134*4882a593Smuzhiyun 	if (irq_status & HDLCD_INTERRUPT_UNDERRUN)
135*4882a593Smuzhiyun 		atomic_inc(&hdlcd->buffer_underrun_count);
136*4882a593Smuzhiyun 
137*4882a593Smuzhiyun 	if (irq_status & HDLCD_INTERRUPT_DMA_END)
138*4882a593Smuzhiyun 		atomic_inc(&hdlcd->dma_end_count);
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	if (irq_status & HDLCD_INTERRUPT_BUS_ERROR)
141*4882a593Smuzhiyun 		atomic_inc(&hdlcd->bus_error_count);
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if (irq_status & HDLCD_INTERRUPT_VSYNC)
144*4882a593Smuzhiyun 		atomic_inc(&hdlcd->vsync_count);
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun #endif
147*4882a593Smuzhiyun 	if (irq_status & HDLCD_INTERRUPT_VSYNC)
148*4882a593Smuzhiyun 		drm_crtc_handle_vblank(&hdlcd->crtc);
149*4882a593Smuzhiyun 
150*4882a593Smuzhiyun 	/* acknowledge interrupt(s) */
151*4882a593Smuzhiyun 	hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, irq_status);
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	return IRQ_HANDLED;
154*4882a593Smuzhiyun }
155*4882a593Smuzhiyun 
hdlcd_irq_preinstall(struct drm_device * drm)156*4882a593Smuzhiyun static void hdlcd_irq_preinstall(struct drm_device *drm)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
159*4882a593Smuzhiyun 	/* Ensure interrupts are disabled */
160*4882a593Smuzhiyun 	hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, 0);
161*4882a593Smuzhiyun 	hdlcd_write(hdlcd, HDLCD_REG_INT_CLEAR, ~0);
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
hdlcd_irq_postinstall(struct drm_device * drm)164*4882a593Smuzhiyun static int hdlcd_irq_postinstall(struct drm_device *drm)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
167*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
168*4882a593Smuzhiyun 	unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	/* enable debug interrupts */
171*4882a593Smuzhiyun 	irq_mask |= HDLCD_DEBUG_INT_MASK;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask);
174*4882a593Smuzhiyun #endif
175*4882a593Smuzhiyun 	return 0;
176*4882a593Smuzhiyun }
177*4882a593Smuzhiyun 
hdlcd_irq_uninstall(struct drm_device * drm)178*4882a593Smuzhiyun static void hdlcd_irq_uninstall(struct drm_device *drm)
179*4882a593Smuzhiyun {
180*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
181*4882a593Smuzhiyun 	/* disable all the interrupts that we might have enabled */
182*4882a593Smuzhiyun 	unsigned long irq_mask = hdlcd_read(hdlcd, HDLCD_REG_INT_MASK);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
185*4882a593Smuzhiyun 	/* disable debug interrupts */
186*4882a593Smuzhiyun 	irq_mask &= ~HDLCD_DEBUG_INT_MASK;
187*4882a593Smuzhiyun #endif
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	/* disable vsync interrupts */
190*4882a593Smuzhiyun 	irq_mask &= ~HDLCD_INTERRUPT_VSYNC;
191*4882a593Smuzhiyun 
192*4882a593Smuzhiyun 	hdlcd_write(hdlcd, HDLCD_REG_INT_MASK, irq_mask);
193*4882a593Smuzhiyun }
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
hdlcd_show_underrun_count(struct seq_file * m,void * arg)196*4882a593Smuzhiyun static int hdlcd_show_underrun_count(struct seq_file *m, void *arg)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	struct drm_info_node *node = (struct drm_info_node *)m->private;
199*4882a593Smuzhiyun 	struct drm_device *drm = node->minor->dev;
200*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	seq_printf(m, "underrun : %d\n", atomic_read(&hdlcd->buffer_underrun_count));
203*4882a593Smuzhiyun 	seq_printf(m, "dma_end  : %d\n", atomic_read(&hdlcd->dma_end_count));
204*4882a593Smuzhiyun 	seq_printf(m, "bus_error: %d\n", atomic_read(&hdlcd->bus_error_count));
205*4882a593Smuzhiyun 	seq_printf(m, "vsync    : %d\n", atomic_read(&hdlcd->vsync_count));
206*4882a593Smuzhiyun 	return 0;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun 
hdlcd_show_pxlclock(struct seq_file * m,void * arg)209*4882a593Smuzhiyun static int hdlcd_show_pxlclock(struct seq_file *m, void *arg)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun 	struct drm_info_node *node = (struct drm_info_node *)m->private;
212*4882a593Smuzhiyun 	struct drm_device *drm = node->minor->dev;
213*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
214*4882a593Smuzhiyun 	unsigned long clkrate = clk_get_rate(hdlcd->clk);
215*4882a593Smuzhiyun 	unsigned long mode_clock = hdlcd->crtc.mode.crtc_clock * 1000;
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	seq_printf(m, "hw  : %lu\n", clkrate);
218*4882a593Smuzhiyun 	seq_printf(m, "mode: %lu\n", mode_clock);
219*4882a593Smuzhiyun 	return 0;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun static struct drm_info_list hdlcd_debugfs_list[] = {
223*4882a593Smuzhiyun 	{ "interrupt_count", hdlcd_show_underrun_count, 0 },
224*4882a593Smuzhiyun 	{ "clocks", hdlcd_show_pxlclock, 0 },
225*4882a593Smuzhiyun };
226*4882a593Smuzhiyun 
hdlcd_debugfs_init(struct drm_minor * minor)227*4882a593Smuzhiyun static void hdlcd_debugfs_init(struct drm_minor *minor)
228*4882a593Smuzhiyun {
229*4882a593Smuzhiyun 	drm_debugfs_create_files(hdlcd_debugfs_list,
230*4882a593Smuzhiyun 				 ARRAY_SIZE(hdlcd_debugfs_list),
231*4882a593Smuzhiyun 				 minor->debugfs_root, minor);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun #endif
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun DEFINE_DRM_GEM_CMA_FOPS(fops);
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun static struct drm_driver hdlcd_driver = {
238*4882a593Smuzhiyun 	.driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
239*4882a593Smuzhiyun 	.irq_handler = hdlcd_irq,
240*4882a593Smuzhiyun 	.irq_preinstall = hdlcd_irq_preinstall,
241*4882a593Smuzhiyun 	.irq_postinstall = hdlcd_irq_postinstall,
242*4882a593Smuzhiyun 	.irq_uninstall = hdlcd_irq_uninstall,
243*4882a593Smuzhiyun 	DRM_GEM_CMA_DRIVER_OPS,
244*4882a593Smuzhiyun #ifdef CONFIG_DEBUG_FS
245*4882a593Smuzhiyun 	.debugfs_init = hdlcd_debugfs_init,
246*4882a593Smuzhiyun #endif
247*4882a593Smuzhiyun 	.fops = &fops,
248*4882a593Smuzhiyun 	.name = "hdlcd",
249*4882a593Smuzhiyun 	.desc = "ARM HDLCD Controller DRM",
250*4882a593Smuzhiyun 	.date = "20151021",
251*4882a593Smuzhiyun 	.major = 1,
252*4882a593Smuzhiyun 	.minor = 0,
253*4882a593Smuzhiyun };
254*4882a593Smuzhiyun 
hdlcd_drm_bind(struct device * dev)255*4882a593Smuzhiyun static int hdlcd_drm_bind(struct device *dev)
256*4882a593Smuzhiyun {
257*4882a593Smuzhiyun 	struct drm_device *drm;
258*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd;
259*4882a593Smuzhiyun 	int ret;
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 	hdlcd = devm_kzalloc(dev, sizeof(*hdlcd), GFP_KERNEL);
262*4882a593Smuzhiyun 	if (!hdlcd)
263*4882a593Smuzhiyun 		return -ENOMEM;
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 	drm = drm_dev_alloc(&hdlcd_driver, dev);
266*4882a593Smuzhiyun 	if (IS_ERR(drm))
267*4882a593Smuzhiyun 		return PTR_ERR(drm);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 	drm->dev_private = hdlcd;
270*4882a593Smuzhiyun 	dev_set_drvdata(dev, drm);
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun 	hdlcd_setup_mode_config(drm);
273*4882a593Smuzhiyun 	ret = hdlcd_load(drm, 0);
274*4882a593Smuzhiyun 	if (ret)
275*4882a593Smuzhiyun 		goto err_free;
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 	/* Set the CRTC's port so that the encoder component can find it */
278*4882a593Smuzhiyun 	hdlcd->crtc.port = of_graph_get_port_by_id(dev->of_node, 0);
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	ret = component_bind_all(dev, drm);
281*4882a593Smuzhiyun 	if (ret) {
282*4882a593Smuzhiyun 		DRM_ERROR("Failed to bind all components\n");
283*4882a593Smuzhiyun 		goto err_unload;
284*4882a593Smuzhiyun 	}
285*4882a593Smuzhiyun 
286*4882a593Smuzhiyun 	ret = pm_runtime_set_active(dev);
287*4882a593Smuzhiyun 	if (ret)
288*4882a593Smuzhiyun 		goto err_pm_active;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun 	pm_runtime_enable(dev);
291*4882a593Smuzhiyun 
292*4882a593Smuzhiyun 	ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
293*4882a593Smuzhiyun 	if (ret < 0) {
294*4882a593Smuzhiyun 		DRM_ERROR("failed to initialise vblank\n");
295*4882a593Smuzhiyun 		goto err_vblank;
296*4882a593Smuzhiyun 	}
297*4882a593Smuzhiyun 
298*4882a593Smuzhiyun 	drm_mode_config_reset(drm);
299*4882a593Smuzhiyun 	drm_kms_helper_poll_init(drm);
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 	ret = drm_dev_register(drm, 0);
302*4882a593Smuzhiyun 	if (ret)
303*4882a593Smuzhiyun 		goto err_register;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	drm_fbdev_generic_setup(drm, 32);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	return 0;
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun err_register:
310*4882a593Smuzhiyun 	drm_kms_helper_poll_fini(drm);
311*4882a593Smuzhiyun err_vblank:
312*4882a593Smuzhiyun 	pm_runtime_disable(drm->dev);
313*4882a593Smuzhiyun err_pm_active:
314*4882a593Smuzhiyun 	drm_atomic_helper_shutdown(drm);
315*4882a593Smuzhiyun 	component_unbind_all(dev, drm);
316*4882a593Smuzhiyun err_unload:
317*4882a593Smuzhiyun 	of_node_put(hdlcd->crtc.port);
318*4882a593Smuzhiyun 	hdlcd->crtc.port = NULL;
319*4882a593Smuzhiyun 	drm_irq_uninstall(drm);
320*4882a593Smuzhiyun 	of_reserved_mem_device_release(drm->dev);
321*4882a593Smuzhiyun err_free:
322*4882a593Smuzhiyun 	drm_mode_config_cleanup(drm);
323*4882a593Smuzhiyun 	dev_set_drvdata(dev, NULL);
324*4882a593Smuzhiyun 	drm_dev_put(drm);
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 	return ret;
327*4882a593Smuzhiyun }
328*4882a593Smuzhiyun 
hdlcd_drm_unbind(struct device * dev)329*4882a593Smuzhiyun static void hdlcd_drm_unbind(struct device *dev)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	struct drm_device *drm = dev_get_drvdata(dev);
332*4882a593Smuzhiyun 	struct hdlcd_drm_private *hdlcd = drm->dev_private;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun 	drm_dev_unregister(drm);
335*4882a593Smuzhiyun 	drm_kms_helper_poll_fini(drm);
336*4882a593Smuzhiyun 	component_unbind_all(dev, drm);
337*4882a593Smuzhiyun 	of_node_put(hdlcd->crtc.port);
338*4882a593Smuzhiyun 	hdlcd->crtc.port = NULL;
339*4882a593Smuzhiyun 	pm_runtime_get_sync(dev);
340*4882a593Smuzhiyun 	drm_atomic_helper_shutdown(drm);
341*4882a593Smuzhiyun 	drm_irq_uninstall(drm);
342*4882a593Smuzhiyun 	pm_runtime_put(dev);
343*4882a593Smuzhiyun 	if (pm_runtime_enabled(dev))
344*4882a593Smuzhiyun 		pm_runtime_disable(dev);
345*4882a593Smuzhiyun 	of_reserved_mem_device_release(dev);
346*4882a593Smuzhiyun 	drm_mode_config_cleanup(drm);
347*4882a593Smuzhiyun 	drm->dev_private = NULL;
348*4882a593Smuzhiyun 	dev_set_drvdata(dev, NULL);
349*4882a593Smuzhiyun 	drm_dev_put(drm);
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun static const struct component_master_ops hdlcd_master_ops = {
353*4882a593Smuzhiyun 	.bind		= hdlcd_drm_bind,
354*4882a593Smuzhiyun 	.unbind		= hdlcd_drm_unbind,
355*4882a593Smuzhiyun };
356*4882a593Smuzhiyun 
compare_dev(struct device * dev,void * data)357*4882a593Smuzhiyun static int compare_dev(struct device *dev, void *data)
358*4882a593Smuzhiyun {
359*4882a593Smuzhiyun 	return dev->of_node == data;
360*4882a593Smuzhiyun }
361*4882a593Smuzhiyun 
hdlcd_probe(struct platform_device * pdev)362*4882a593Smuzhiyun static int hdlcd_probe(struct platform_device *pdev)
363*4882a593Smuzhiyun {
364*4882a593Smuzhiyun 	struct device_node *port;
365*4882a593Smuzhiyun 	struct component_match *match = NULL;
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	/* there is only one output port inside each device, find it */
368*4882a593Smuzhiyun 	port = of_graph_get_remote_node(pdev->dev.of_node, 0, 0);
369*4882a593Smuzhiyun 	if (!port)
370*4882a593Smuzhiyun 		return -ENODEV;
371*4882a593Smuzhiyun 
372*4882a593Smuzhiyun 	drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
373*4882a593Smuzhiyun 	of_node_put(port);
374*4882a593Smuzhiyun 
375*4882a593Smuzhiyun 	return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
376*4882a593Smuzhiyun 					       match);
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun 
hdlcd_remove(struct platform_device * pdev)379*4882a593Smuzhiyun static int hdlcd_remove(struct platform_device *pdev)
380*4882a593Smuzhiyun {
381*4882a593Smuzhiyun 	component_master_del(&pdev->dev, &hdlcd_master_ops);
382*4882a593Smuzhiyun 	return 0;
383*4882a593Smuzhiyun }
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun static const struct of_device_id  hdlcd_of_match[] = {
386*4882a593Smuzhiyun 	{ .compatible	= "arm,hdlcd" },
387*4882a593Smuzhiyun 	{},
388*4882a593Smuzhiyun };
389*4882a593Smuzhiyun MODULE_DEVICE_TABLE(of, hdlcd_of_match);
390*4882a593Smuzhiyun 
hdlcd_pm_suspend(struct device * dev)391*4882a593Smuzhiyun static int __maybe_unused hdlcd_pm_suspend(struct device *dev)
392*4882a593Smuzhiyun {
393*4882a593Smuzhiyun 	struct drm_device *drm = dev_get_drvdata(dev);
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	return drm_mode_config_helper_suspend(drm);
396*4882a593Smuzhiyun }
397*4882a593Smuzhiyun 
hdlcd_pm_resume(struct device * dev)398*4882a593Smuzhiyun static int __maybe_unused hdlcd_pm_resume(struct device *dev)
399*4882a593Smuzhiyun {
400*4882a593Smuzhiyun 	struct drm_device *drm = dev_get_drvdata(dev);
401*4882a593Smuzhiyun 
402*4882a593Smuzhiyun 	drm_mode_config_helper_resume(drm);
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	return 0;
405*4882a593Smuzhiyun }
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun static SIMPLE_DEV_PM_OPS(hdlcd_pm_ops, hdlcd_pm_suspend, hdlcd_pm_resume);
408*4882a593Smuzhiyun 
409*4882a593Smuzhiyun static struct platform_driver hdlcd_platform_driver = {
410*4882a593Smuzhiyun 	.probe		= hdlcd_probe,
411*4882a593Smuzhiyun 	.remove		= hdlcd_remove,
412*4882a593Smuzhiyun 	.driver	= {
413*4882a593Smuzhiyun 		.name = "hdlcd",
414*4882a593Smuzhiyun 		.pm = &hdlcd_pm_ops,
415*4882a593Smuzhiyun 		.of_match_table	= hdlcd_of_match,
416*4882a593Smuzhiyun 	},
417*4882a593Smuzhiyun };
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun module_platform_driver(hdlcd_platform_driver);
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun MODULE_AUTHOR("Liviu Dudau");
422*4882a593Smuzhiyun MODULE_DESCRIPTION("ARM HDLCD DRM driver");
423*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
424