4 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
17 #include <linux/clk.h>
18 #include <drm/drm_crtc_helper.h>
19 #include <drm/drm_fb_cma_helper.h>
20 #include <drm/drm_gem_cma_helper.h>
21 #include <drm/drm_atomic_helper.h>
22 #include <linux/of_reserved_mem.h>
25 #include "arcpgu_regs.h"
27 static void arcpgu_fb_output_poll_changed(struct drm_device
*dev
)
29 struct arcpgu_drm_private
*arcpgu
= dev
->dev_private
;
31 drm_fbdev_cma_hotplug_event(arcpgu
->fbdev
);
34 static struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs
= {
35 .fb_create
= drm_fb_cma_create
,
36 .output_poll_changed
= arcpgu_fb_output_poll_changed
,
37 .atomic_check
= drm_atomic_helper_check
,
38 .atomic_commit
= drm_atomic_helper_commit
,
41 static void arcpgu_setup_mode_config(struct drm_device
*drm
)
43 drm_mode_config_init(drm
);
44 drm
->mode_config
.min_width
= 0;
45 drm
->mode_config
.min_height
= 0;
46 drm
->mode_config
.max_width
= 1920;
47 drm
->mode_config
.max_height
= 1080;
48 drm
->mode_config
.funcs
= &arcpgu_drm_modecfg_funcs
;
51 DEFINE_DRM_GEM_CMA_FOPS(arcpgu_drm_ops
);
53 static void arcpgu_lastclose(struct drm_device
*drm
)
55 struct arcpgu_drm_private
*arcpgu
= drm
->dev_private
;
57 drm_fbdev_cma_restore_mode(arcpgu
->fbdev
);
60 static int arcpgu_load(struct drm_device
*drm
)
62 struct platform_device
*pdev
= to_platform_device(drm
->dev
);
63 struct arcpgu_drm_private
*arcpgu
;
64 struct device_node
*encoder_node
;
68 arcpgu
= devm_kzalloc(&pdev
->dev
, sizeof(*arcpgu
), GFP_KERNEL
);
72 drm
->dev_private
= arcpgu
;
74 arcpgu
->clk
= devm_clk_get(drm
->dev
, "pxlclk");
75 if (IS_ERR(arcpgu
->clk
))
76 return PTR_ERR(arcpgu
->clk
);
78 arcpgu_setup_mode_config(drm
);
80 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
81 arcpgu
->regs
= devm_ioremap_resource(&pdev
->dev
, res
);
82 if (IS_ERR(arcpgu
->regs
))
83 return PTR_ERR(arcpgu
->regs
);
85 dev_info(drm
->dev
, "arc_pgu ID: 0x%x\n",
86 arc_pgu_read(arcpgu
, ARCPGU_REG_ID
));
88 /* Get the optional framebuffer memory resource */
89 ret
= of_reserved_mem_device_init(drm
->dev
);
90 if (ret
&& ret
!= -ENODEV
)
93 if (dma_set_mask_and_coherent(drm
->dev
, DMA_BIT_MASK(32)))
96 if (arc_pgu_setup_crtc(drm
) < 0)
99 /* find the encoder node and initialize it */
100 encoder_node
= of_parse_phandle(drm
->dev
->of_node
, "encoder-slave", 0);
102 ret
= arcpgu_drm_hdmi_init(drm
, encoder_node
);
103 of_node_put(encoder_node
);
107 ret
= arcpgu_drm_sim_init(drm
, NULL
);
112 drm_mode_config_reset(drm
);
113 drm_kms_helper_poll_init(drm
);
115 arcpgu
->fbdev
= drm_fbdev_cma_init(drm
, 16,
116 drm
->mode_config
.num_connector
);
117 if (IS_ERR(arcpgu
->fbdev
)) {
118 ret
= PTR_ERR(arcpgu
->fbdev
);
119 arcpgu
->fbdev
= NULL
;
123 platform_set_drvdata(pdev
, arcpgu
);
127 static int arcpgu_unload(struct drm_device
*drm
)
129 struct arcpgu_drm_private
*arcpgu
= drm
->dev_private
;
132 drm_fbdev_cma_fini(arcpgu
->fbdev
);
133 arcpgu
->fbdev
= NULL
;
135 drm_kms_helper_poll_fini(drm
);
136 drm_mode_config_cleanup(drm
);
141 #ifdef CONFIG_DEBUG_FS
142 static int arcpgu_show_pxlclock(struct seq_file
*m
, void *arg
)
144 struct drm_info_node
*node
= (struct drm_info_node
*)m
->private;
145 struct drm_device
*drm
= node
->minor
->dev
;
146 struct arcpgu_drm_private
*arcpgu
= drm
->dev_private
;
147 unsigned long clkrate
= clk_get_rate(arcpgu
->clk
);
148 unsigned long mode_clock
= arcpgu
->crtc
.mode
.crtc_clock
* 1000;
150 seq_printf(m
, "hw : %lu\n", clkrate
);
151 seq_printf(m
, "mode: %lu\n", mode_clock
);
155 static struct drm_info_list arcpgu_debugfs_list
[] = {
156 { "clocks", arcpgu_show_pxlclock
, 0 },
157 { "fb", drm_fb_cma_debugfs_show
, 0 },
160 static int arcpgu_debugfs_init(struct drm_minor
*minor
)
162 return drm_debugfs_create_files(arcpgu_debugfs_list
,
163 ARRAY_SIZE(arcpgu_debugfs_list
), minor
->debugfs_root
, minor
);
167 static struct drm_driver arcpgu_drm_driver
= {
168 .driver_features
= DRIVER_MODESET
| DRIVER_GEM
| DRIVER_PRIME
|
170 .lastclose
= arcpgu_lastclose
,
172 .desc
= "ARC PGU Controller",
177 .fops
= &arcpgu_drm_ops
,
178 .dumb_create
= drm_gem_cma_dumb_create
,
179 .dumb_map_offset
= drm_gem_cma_dumb_map_offset
,
180 .dumb_destroy
= drm_gem_dumb_destroy
,
181 .prime_handle_to_fd
= drm_gem_prime_handle_to_fd
,
182 .prime_fd_to_handle
= drm_gem_prime_fd_to_handle
,
183 .gem_free_object_unlocked
= drm_gem_cma_free_object
,
184 .gem_vm_ops
= &drm_gem_cma_vm_ops
,
185 .gem_prime_export
= drm_gem_prime_export
,
186 .gem_prime_import
= drm_gem_prime_import
,
187 .gem_prime_get_sg_table
= drm_gem_cma_prime_get_sg_table
,
188 .gem_prime_import_sg_table
= drm_gem_cma_prime_import_sg_table
,
189 .gem_prime_vmap
= drm_gem_cma_prime_vmap
,
190 .gem_prime_vunmap
= drm_gem_cma_prime_vunmap
,
191 .gem_prime_mmap
= drm_gem_cma_prime_mmap
,
192 #ifdef CONFIG_DEBUG_FS
193 .debugfs_init
= arcpgu_debugfs_init
,
197 static int arcpgu_probe(struct platform_device
*pdev
)
199 struct drm_device
*drm
;
202 drm
= drm_dev_alloc(&arcpgu_drm_driver
, &pdev
->dev
);
206 ret
= arcpgu_load(drm
);
210 ret
= drm_dev_register(drm
, 0);
225 static int arcpgu_remove(struct platform_device
*pdev
)
227 struct drm_device
*drm
= platform_get_drvdata(pdev
);
229 drm_dev_unregister(drm
);
236 static const struct of_device_id arcpgu_of_table
[] = {
237 {.compatible
= "snps,arcpgu"},
241 MODULE_DEVICE_TABLE(of
, arcpgu_of_table
);
243 static struct platform_driver arcpgu_platform_driver
= {
244 .probe
= arcpgu_probe
,
245 .remove
= arcpgu_remove
,
248 .of_match_table
= arcpgu_of_table
,
252 module_platform_driver(arcpgu_platform_driver
);
254 MODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>");
255 MODULE_DESCRIPTION("ARC PGU DRM driver");
256 MODULE_LICENSE("GPL");