blob: 5b35e5dbae38027df46fc5c752d2a20ab8cd3dca [file] [log] [blame]
Carlos Palminha51dacf22016-02-19 15:30:26 +03001/*
2 * ARC PGU DRM driver.
3 *
4 * Copyright (C) 2016 Synopsys, Inc. (www.synopsys.com)
5 *
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.
9 *
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.
14 *
15 */
16
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
23#include "arcpgu.h"
24#include "arcpgu_regs.h"
25
26static void arcpgu_fb_output_poll_changed(struct drm_device *dev)
27{
28 struct arcpgu_drm_private *arcpgu = dev->dev_private;
29
30 if (arcpgu->fbdev)
31 drm_fbdev_cma_hotplug_event(arcpgu->fbdev);
32}
33
34static int arcpgu_atomic_commit(struct drm_device *dev,
35 struct drm_atomic_state *state, bool async)
36{
37 return drm_atomic_helper_commit(dev, state, false);
38}
39
40static struct drm_mode_config_funcs arcpgu_drm_modecfg_funcs = {
41 .fb_create = drm_fb_cma_create,
42 .output_poll_changed = arcpgu_fb_output_poll_changed,
43 .atomic_check = drm_atomic_helper_check,
44 .atomic_commit = arcpgu_atomic_commit,
45};
46
47static void arcpgu_setup_mode_config(struct drm_device *drm)
48{
49 drm_mode_config_init(drm);
50 drm->mode_config.min_width = 0;
51 drm->mode_config.min_height = 0;
52 drm->mode_config.max_width = 1920;
53 drm->mode_config.max_height = 1080;
54 drm->mode_config.funcs = &arcpgu_drm_modecfg_funcs;
55}
56
57int arcpgu_gem_mmap(struct file *filp, struct vm_area_struct *vma)
58{
59 int ret;
60
61 ret = drm_gem_mmap(filp, vma);
62 if (ret)
63 return ret;
64
65 vma->vm_page_prot = pgprot_noncached(vm_get_page_prot(vma->vm_flags));
66 return 0;
67}
68
69static const struct file_operations arcpgu_drm_ops = {
70 .owner = THIS_MODULE,
71 .open = drm_open,
72 .release = drm_release,
73 .unlocked_ioctl = drm_ioctl,
74#ifdef CONFIG_COMPAT
75 .compat_ioctl = drm_compat_ioctl,
76#endif
77 .poll = drm_poll,
78 .read = drm_read,
79 .llseek = no_llseek,
80 .mmap = arcpgu_gem_mmap,
81};
82
83static void arcpgu_preclose(struct drm_device *drm, struct drm_file *file)
84{
85 struct arcpgu_drm_private *arcpgu = drm->dev_private;
86 struct drm_pending_vblank_event *e, *t;
87 unsigned long flags;
88
89 spin_lock_irqsave(&drm->event_lock, flags);
90 list_for_each_entry_safe(e, t, &arcpgu->event_list, base.link) {
91 if (e->base.file_priv != file)
92 continue;
93 list_del(&e->base.link);
94 e->base.destroy(&e->base);
95 }
96 spin_unlock_irqrestore(&drm->event_lock, flags);
97}
98
99static void arcpgu_lastclose(struct drm_device *drm)
100{
101 struct arcpgu_drm_private *arcpgu = drm->dev_private;
102
103 drm_fbdev_cma_restore_mode(arcpgu->fbdev);
104}
105
106static int arcpgu_load(struct drm_device *drm)
107{
108 struct platform_device *pdev = to_platform_device(drm->dev);
109 struct arcpgu_drm_private *arcpgu;
110 struct device_node *encoder_node;
111 struct resource *res;
112 int ret;
113
114 arcpgu = devm_kzalloc(&pdev->dev, sizeof(*arcpgu), GFP_KERNEL);
115 if (arcpgu == NULL)
116 return -ENOMEM;
117
118 drm->dev_private = arcpgu;
119
120 arcpgu->clk = devm_clk_get(drm->dev, "pxlclk");
121 if (IS_ERR(arcpgu->clk))
122 return PTR_ERR(arcpgu->clk);
123
124 INIT_LIST_HEAD(&arcpgu->event_list);
125
126 arcpgu_setup_mode_config(drm);
127
128 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
129 arcpgu->regs = devm_ioremap_resource(&pdev->dev, res);
130 if (IS_ERR(arcpgu->regs)) {
131 dev_err(drm->dev, "Could not remap IO mem\n");
132 return PTR_ERR(arcpgu->regs);
133 }
134
135 dev_info(drm->dev, "arc_pgu ID: 0x%x\n",
136 arc_pgu_read(arcpgu, ARCPGU_REG_ID));
137
138 if (dma_set_mask_and_coherent(drm->dev, DMA_BIT_MASK(32)))
139 return -ENODEV;
140
141 if (arc_pgu_setup_crtc(drm) < 0)
142 return -ENODEV;
143
144 /* find the encoder node and initialize it */
145 encoder_node = of_parse_phandle(drm->dev->of_node, "encoder-slave", 0);
146 if (!encoder_node) {
147 dev_err(drm->dev, "failed to get an encoder slave node\n");
148 return -ENODEV;
149 }
150
151 ret = arcpgu_drm_hdmi_init(drm, encoder_node);
152 if (ret < 0)
153 return ret;
154
155 drm_mode_config_reset(drm);
156 drm_kms_helper_poll_init(drm);
157
158 arcpgu->fbdev = drm_fbdev_cma_init(drm, 16,
159 drm->mode_config.num_crtc,
160 drm->mode_config.num_connector);
161 if (IS_ERR(arcpgu->fbdev)) {
162 ret = PTR_ERR(arcpgu->fbdev);
163 arcpgu->fbdev = NULL;
164 return -ENODEV;
165 }
166
167 platform_set_drvdata(pdev, arcpgu);
168 return 0;
169}
170
171int arcpgu_unload(struct drm_device *drm)
172{
173 struct arcpgu_drm_private *arcpgu = drm->dev_private;
174
175 if (arcpgu->fbdev) {
176 drm_fbdev_cma_fini(arcpgu->fbdev);
177 arcpgu->fbdev = NULL;
178 }
179 drm_kms_helper_poll_fini(drm);
180 drm_vblank_cleanup(drm);
181 drm_mode_config_cleanup(drm);
182
183 return 0;
184}
185
186static struct drm_driver arcpgu_drm_driver = {
187 .driver_features = DRIVER_MODESET | DRIVER_GEM | DRIVER_PRIME |
188 DRIVER_ATOMIC,
189 .preclose = arcpgu_preclose,
190 .lastclose = arcpgu_lastclose,
191 .name = "drm-arcpgu",
192 .desc = "ARC PGU Controller",
193 .date = "20160219",
194 .major = 1,
195 .minor = 0,
196 .patchlevel = 0,
197 .fops = &arcpgu_drm_ops,
198 .dumb_create = drm_gem_cma_dumb_create,
199 .dumb_map_offset = drm_gem_cma_dumb_map_offset,
200 .dumb_destroy = drm_gem_dumb_destroy,
201 .get_vblank_counter = drm_vblank_no_hw_counter,
202 .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
203 .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
204 .gem_free_object = drm_gem_cma_free_object,
205 .gem_vm_ops = &drm_gem_cma_vm_ops,
206 .gem_prime_export = drm_gem_prime_export,
207 .gem_prime_import = drm_gem_prime_import,
208 .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table,
209 .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table,
210 .gem_prime_vmap = drm_gem_cma_prime_vmap,
211 .gem_prime_vunmap = drm_gem_cma_prime_vunmap,
212 .gem_prime_mmap = drm_gem_cma_prime_mmap,
213};
214
215static int arcpgu_probe(struct platform_device *pdev)
216{
217 struct drm_device *drm;
218 int ret;
219
220 drm = drm_dev_alloc(&arcpgu_drm_driver, &pdev->dev);
221 if (!drm)
222 return -ENOMEM;
223
224 ret = arcpgu_load(drm);
225 if (ret)
226 goto err_unref;
227
228 ret = drm_dev_register(drm, 0);
229 if (ret)
230 goto err_unload;
231
232 ret = drm_connector_register_all(drm);
233 if (ret)
234 goto err_unregister;
235
236 return 0;
237
238err_unregister:
239 drm_dev_unregister(drm);
240
241err_unload:
242 arcpgu_unload(drm);
243
244err_unref:
245 drm_dev_unref(drm);
246
247 return ret;
248}
249
250static int arcpgu_remove(struct platform_device *pdev)
251{
252 struct drm_device *drm = platform_get_drvdata(pdev);
253
254 drm_connector_unregister_all(drm);
255 drm_dev_unregister(drm);
256 arcpgu_unload(drm);
257 drm_dev_unref(drm);
258
259 return 0;
260}
261
262static const struct of_device_id arcpgu_of_table[] = {
263 {.compatible = "snps,arcpgu"},
264 {}
265};
266
267MODULE_DEVICE_TABLE(of, arcpgu_of_table);
268
269static struct platform_driver arcpgu_platform_driver = {
270 .probe = arcpgu_probe,
271 .remove = arcpgu_remove,
272 .driver = {
273 .name = "arcpgu",
274 .of_match_table = arcpgu_of_table,
275 },
276};
277
278module_platform_driver(arcpgu_platform_driver);
279
280MODULE_AUTHOR("Carlos Palminha <palminha@synopsys.com>");
281MODULE_DESCRIPTION("ARC PGU DRM driver");
282MODULE_LICENSE("GPL");