Commit | Line | Data |
---|---|---|
16ea975e RC |
1 | /* |
2 | * Copyright (C) 2012 Texas Instruments | |
3 | * Author: Rob Clark <robdclark@gmail.com> | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify it | |
6 | * under the terms of the GNU General Public License version 2 as published by | |
7 | * the Free Software Foundation. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but WITHOUT | |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
12 | * more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along with | |
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | */ | |
17 | ||
18 | /* LCDC DRM driver, based on da8xx-fb */ | |
19 | ||
103cd8bc | 20 | #include <linux/component.h> |
416a07fb DG |
21 | #include <linux/pinctrl/consumer.h> |
22 | #include <linux/suspend.h> | |
edc43303 JS |
23 | #include <drm/drm_atomic.h> |
24 | #include <drm/drm_atomic_helper.h> | |
bb2af9bd | 25 | #include <drm/drm_fb_helper.h> |
103cd8bc | 26 | |
16ea975e RC |
27 | #include "tilcdc_drv.h" |
28 | #include "tilcdc_regs.h" | |
29 | #include "tilcdc_tfp410.h" | |
0d4bbaf9 | 30 | #include "tilcdc_panel.h" |
103cd8bc | 31 | #include "tilcdc_external.h" |
16ea975e | 32 | |
16ea975e RC |
33 | static LIST_HEAD(module_list); |
34 | ||
bcc5a6f5 JS |
35 | static const u32 tilcdc_rev1_formats[] = { DRM_FORMAT_RGB565 }; |
36 | ||
37 | static const u32 tilcdc_straight_formats[] = { DRM_FORMAT_RGB565, | |
38 | DRM_FORMAT_BGR888, | |
39 | DRM_FORMAT_XBGR8888 }; | |
40 | ||
41 | static const u32 tilcdc_crossed_formats[] = { DRM_FORMAT_BGR565, | |
42 | DRM_FORMAT_RGB888, | |
43 | DRM_FORMAT_XRGB8888 }; | |
44 | ||
45 | static const u32 tilcdc_legacy_formats[] = { DRM_FORMAT_RGB565, | |
46 | DRM_FORMAT_RGB888, | |
47 | DRM_FORMAT_XRGB8888 }; | |
48 | ||
16ea975e RC |
49 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, |
50 | const struct tilcdc_module_ops *funcs) | |
51 | { | |
52 | mod->name = name; | |
53 | mod->funcs = funcs; | |
54 | INIT_LIST_HEAD(&mod->list); | |
55 | list_add(&mod->list, &module_list); | |
56 | } | |
57 | ||
58 | void tilcdc_module_cleanup(struct tilcdc_module *mod) | |
59 | { | |
60 | list_del(&mod->list); | |
61 | } | |
62 | ||
63 | static struct of_device_id tilcdc_of_match[]; | |
64 | ||
65 | static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, | |
1eb83451 | 66 | struct drm_file *file_priv, const struct drm_mode_fb_cmd2 *mode_cmd) |
16ea975e RC |
67 | { |
68 | return drm_fb_cma_create(dev, file_priv, mode_cmd); | |
69 | } | |
70 | ||
71 | static void tilcdc_fb_output_poll_changed(struct drm_device *dev) | |
72 | { | |
73 | struct tilcdc_drm_private *priv = dev->dev_private; | |
c0844817 | 74 | drm_fbdev_cma_hotplug_event(priv->fbdev); |
16ea975e RC |
75 | } |
76 | ||
30457676 WY |
77 | static int tilcdc_atomic_check(struct drm_device *dev, |
78 | struct drm_atomic_state *state) | |
edc43303 JS |
79 | { |
80 | int ret; | |
81 | ||
82 | ret = drm_atomic_helper_check_modeset(dev, state); | |
83 | if (ret) | |
84 | return ret; | |
85 | ||
86 | ret = drm_atomic_helper_check_planes(dev, state); | |
87 | if (ret) | |
88 | return ret; | |
89 | ||
90 | /* | |
91 | * tilcdc ->atomic_check can update ->mode_changed if pixel format | |
92 | * changes, hence will we check modeset changes again. | |
93 | */ | |
94 | ret = drm_atomic_helper_check_modeset(dev, state); | |
95 | if (ret) | |
96 | return ret; | |
97 | ||
98 | return ret; | |
99 | } | |
100 | ||
101 | static int tilcdc_commit(struct drm_device *dev, | |
102 | struct drm_atomic_state *state, | |
103 | bool async) | |
104 | { | |
105 | int ret; | |
106 | ||
107 | ret = drm_atomic_helper_prepare_planes(dev, state); | |
108 | if (ret) | |
109 | return ret; | |
110 | ||
fad9e43a ML |
111 | ret = drm_atomic_helper_swap_state(state, true); |
112 | if (ret) { | |
113 | drm_atomic_helper_cleanup_planes(dev, state); | |
114 | return ret; | |
115 | } | |
edc43303 JS |
116 | |
117 | /* | |
118 | * Everything below can be run asynchronously without the need to grab | |
119 | * any modeset locks at all under one condition: It must be guaranteed | |
120 | * that the asynchronous work has either been cancelled (if the driver | |
121 | * supports it, which at least requires that the framebuffers get | |
122 | * cleaned up with drm_atomic_helper_cleanup_planes()) or completed | |
123 | * before the new state gets committed on the software side with | |
124 | * drm_atomic_helper_swap_state(). | |
125 | * | |
126 | * This scheme allows new atomic state updates to be prepared and | |
127 | * checked in parallel to the asynchronous completion of the previous | |
128 | * update. Which is important since compositors need to figure out the | |
129 | * composition of the next frame right after having submitted the | |
130 | * current layout. | |
131 | */ | |
132 | ||
133 | drm_atomic_helper_commit_modeset_disables(dev, state); | |
134 | ||
2b58e98d | 135 | drm_atomic_helper_commit_planes(dev, state, 0); |
edc43303 JS |
136 | |
137 | drm_atomic_helper_commit_modeset_enables(dev, state); | |
138 | ||
139 | drm_atomic_helper_wait_for_vblanks(dev, state); | |
140 | ||
141 | drm_atomic_helper_cleanup_planes(dev, state); | |
142 | ||
edc43303 JS |
143 | return 0; |
144 | } | |
145 | ||
16ea975e RC |
146 | static const struct drm_mode_config_funcs mode_config_funcs = { |
147 | .fb_create = tilcdc_fb_create, | |
148 | .output_poll_changed = tilcdc_fb_output_poll_changed, | |
edc43303 JS |
149 | .atomic_check = tilcdc_atomic_check, |
150 | .atomic_commit = tilcdc_commit, | |
16ea975e RC |
151 | }; |
152 | ||
9963d36d | 153 | static void modeset_init(struct drm_device *dev) |
16ea975e RC |
154 | { |
155 | struct tilcdc_drm_private *priv = dev->dev_private; | |
156 | struct tilcdc_module *mod; | |
157 | ||
16ea975e RC |
158 | list_for_each_entry(mod, &module_list, list) { |
159 | DBG("loading module: %s", mod->name); | |
160 | mod->funcs->modeset_init(mod, dev); | |
161 | } | |
162 | ||
16ea975e RC |
163 | dev->mode_config.min_width = 0; |
164 | dev->mode_config.min_height = 0; | |
165 | dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); | |
166 | dev->mode_config.max_height = 2048; | |
167 | dev->mode_config.funcs = &mode_config_funcs; | |
16ea975e RC |
168 | } |
169 | ||
170 | #ifdef CONFIG_CPU_FREQ | |
171 | static int cpufreq_transition(struct notifier_block *nb, | |
172 | unsigned long val, void *data) | |
173 | { | |
174 | struct tilcdc_drm_private *priv = container_of(nb, | |
175 | struct tilcdc_drm_private, freq_transition); | |
a6b7ebaa | 176 | |
642e5167 JS |
177 | if (val == CPUFREQ_POSTCHANGE) |
178 | tilcdc_crtc_update_clk(priv->crtc); | |
16ea975e RC |
179 | |
180 | return 0; | |
181 | } | |
182 | #endif | |
183 | ||
184 | /* | |
185 | * DRM operations: | |
186 | */ | |
187 | ||
923310ba | 188 | static void tilcdc_fini(struct drm_device *dev) |
16ea975e RC |
189 | { |
190 | struct tilcdc_drm_private *priv = dev->dev_private; | |
16ea975e | 191 | |
9e79e062 | 192 | if (priv->crtc) |
2d53a180 | 193 | tilcdc_crtc_shutdown(priv->crtc); |
923310ba | 194 | |
9e79e062 JS |
195 | if (priv->is_registered) |
196 | drm_dev_unregister(dev); | |
103cd8bc | 197 | |
16ea975e | 198 | drm_kms_helper_poll_fini(dev); |
9e79e062 JS |
199 | |
200 | if (priv->fbdev) | |
201 | drm_fbdev_cma_fini(priv->fbdev); | |
202 | ||
923310ba | 203 | drm_irq_uninstall(dev); |
16ea975e | 204 | drm_mode_config_cleanup(dev); |
ec9eab09 | 205 | tilcdc_remove_external_device(dev); |
16ea975e RC |
206 | |
207 | #ifdef CONFIG_CPU_FREQ | |
9e79e062 JS |
208 | if (priv->freq_transition.notifier_call) |
209 | cpufreq_unregister_notifier(&priv->freq_transition, | |
210 | CPUFREQ_TRANSITION_NOTIFIER); | |
16ea975e RC |
211 | #endif |
212 | ||
213 | if (priv->clk) | |
214 | clk_put(priv->clk); | |
215 | ||
216 | if (priv->mmio) | |
217 | iounmap(priv->mmio); | |
218 | ||
9e79e062 JS |
219 | if (priv->wq) { |
220 | flush_workqueue(priv->wq); | |
221 | destroy_workqueue(priv->wq); | |
222 | } | |
16ea975e RC |
223 | |
224 | dev->dev_private = NULL; | |
225 | ||
226 | pm_runtime_disable(dev->dev); | |
227 | ||
923310ba | 228 | drm_dev_unref(dev); |
16ea975e RC |
229 | } |
230 | ||
923310ba | 231 | static int tilcdc_init(struct drm_driver *ddrv, struct device *dev) |
16ea975e | 232 | { |
923310ba JS |
233 | struct drm_device *ddev; |
234 | struct platform_device *pdev = to_platform_device(dev); | |
235 | struct device_node *node = dev->of_node; | |
16ea975e RC |
236 | struct tilcdc_drm_private *priv; |
237 | struct resource *res; | |
dc28aa07 | 238 | u32 bpp = 0; |
16ea975e RC |
239 | int ret; |
240 | ||
923310ba | 241 | priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); |
514d1a1f | 242 | if (!priv) { |
923310ba | 243 | dev_err(dev, "failed to allocate private data\n"); |
16ea975e RC |
244 | return -ENOMEM; |
245 | } | |
246 | ||
923310ba JS |
247 | ddev = drm_dev_alloc(ddrv, dev); |
248 | if (IS_ERR(ddev)) | |
249 | return PTR_ERR(ddev); | |
250 | ||
923310ba | 251 | ddev->dev_private = priv; |
9e79e062 JS |
252 | platform_set_drvdata(pdev, ddev); |
253 | drm_mode_config_init(ddev); | |
16ea975e | 254 | |
103cd8bc | 255 | priv->is_componentized = |
923310ba | 256 | tilcdc_get_external_components(dev, NULL) > 0; |
103cd8bc | 257 | |
16ea975e | 258 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); |
b478e336 EG |
259 | if (!priv->wq) { |
260 | ret = -ENOMEM; | |
9e79e062 | 261 | goto init_failed; |
b478e336 | 262 | } |
16ea975e RC |
263 | |
264 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
265 | if (!res) { | |
923310ba | 266 | dev_err(dev, "failed to get memory resource\n"); |
16ea975e | 267 | ret = -EINVAL; |
9e79e062 | 268 | goto init_failed; |
16ea975e RC |
269 | } |
270 | ||
271 | priv->mmio = ioremap_nocache(res->start, resource_size(res)); | |
272 | if (!priv->mmio) { | |
923310ba | 273 | dev_err(dev, "failed to ioremap\n"); |
16ea975e | 274 | ret = -ENOMEM; |
9e79e062 | 275 | goto init_failed; |
16ea975e RC |
276 | } |
277 | ||
923310ba | 278 | priv->clk = clk_get(dev, "fck"); |
16ea975e | 279 | if (IS_ERR(priv->clk)) { |
923310ba | 280 | dev_err(dev, "failed to get functional clock\n"); |
16ea975e | 281 | ret = -ENODEV; |
9e79e062 | 282 | goto init_failed; |
16ea975e RC |
283 | } |
284 | ||
16ea975e | 285 | #ifdef CONFIG_CPU_FREQ |
16ea975e RC |
286 | priv->freq_transition.notifier_call = cpufreq_transition; |
287 | ret = cpufreq_register_notifier(&priv->freq_transition, | |
288 | CPUFREQ_TRANSITION_NOTIFIER); | |
289 | if (ret) { | |
923310ba | 290 | dev_err(dev, "failed to register cpufreq notifier\n"); |
9e79e062 JS |
291 | priv->freq_transition.notifier_call = NULL; |
292 | goto init_failed; | |
16ea975e RC |
293 | } |
294 | #endif | |
295 | ||
296 | if (of_property_read_u32(node, "max-bandwidth", &priv->max_bandwidth)) | |
4e564346 DE |
297 | priv->max_bandwidth = TILCDC_DEFAULT_MAX_BANDWIDTH; |
298 | ||
299 | DBG("Maximum Bandwidth Value %d", priv->max_bandwidth); | |
300 | ||
0186fcce | 301 | if (of_property_read_u32(node, "max-width", &priv->max_width)) |
4e564346 DE |
302 | priv->max_width = TILCDC_DEFAULT_MAX_WIDTH; |
303 | ||
304 | DBG("Maximum Horizontal Pixel Width Value %dpixels", priv->max_width); | |
305 | ||
0186fcce | 306 | if (of_property_read_u32(node, "max-pixelclock", |
4e564346 DE |
307 | &priv->max_pixelclock)) |
308 | priv->max_pixelclock = TILCDC_DEFAULT_MAX_PIXELCLOCK; | |
309 | ||
310 | DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock); | |
16ea975e | 311 | |
923310ba | 312 | pm_runtime_enable(dev); |
16ea975e RC |
313 | |
314 | /* Determine LCD IP Version */ | |
923310ba JS |
315 | pm_runtime_get_sync(dev); |
316 | switch (tilcdc_read(ddev, LCDC_PID_REG)) { | |
16ea975e RC |
317 | case 0x4c100102: |
318 | priv->rev = 1; | |
319 | break; | |
320 | case 0x4f200800: | |
321 | case 0x4f201000: | |
322 | priv->rev = 2; | |
323 | break; | |
324 | default: | |
923310ba JS |
325 | dev_warn(dev, "Unknown PID Reg value 0x%08x, " |
326 | "defaulting to LCD revision 1\n", | |
327 | tilcdc_read(ddev, LCDC_PID_REG)); | |
16ea975e RC |
328 | priv->rev = 1; |
329 | break; | |
330 | } | |
331 | ||
923310ba | 332 | pm_runtime_put_sync(dev); |
16ea975e | 333 | |
bcc5a6f5 JS |
334 | if (priv->rev == 1) { |
335 | DBG("Revision 1 LCDC supports only RGB565 format"); | |
336 | priv->pixelformats = tilcdc_rev1_formats; | |
337 | priv->num_pixelformats = ARRAY_SIZE(tilcdc_rev1_formats); | |
c5665385 | 338 | bpp = 16; |
bcc5a6f5 JS |
339 | } else { |
340 | const char *str = "\0"; | |
341 | ||
342 | of_property_read_string(node, "blue-and-red-wiring", &str); | |
343 | if (0 == strcmp(str, "crossed")) { | |
344 | DBG("Configured for crossed blue and red wires"); | |
345 | priv->pixelformats = tilcdc_crossed_formats; | |
346 | priv->num_pixelformats = | |
347 | ARRAY_SIZE(tilcdc_crossed_formats); | |
c5665385 | 348 | bpp = 32; /* Choose bpp with RGB support for fbdef */ |
bcc5a6f5 JS |
349 | } else if (0 == strcmp(str, "straight")) { |
350 | DBG("Configured for straight blue and red wires"); | |
351 | priv->pixelformats = tilcdc_straight_formats; | |
352 | priv->num_pixelformats = | |
353 | ARRAY_SIZE(tilcdc_straight_formats); | |
c5665385 | 354 | bpp = 16; /* Choose bpp with RGB support for fbdef */ |
bcc5a6f5 JS |
355 | } else { |
356 | DBG("Blue and red wiring '%s' unknown, use legacy mode", | |
357 | str); | |
358 | priv->pixelformats = tilcdc_legacy_formats; | |
359 | priv->num_pixelformats = | |
360 | ARRAY_SIZE(tilcdc_legacy_formats); | |
c5665385 | 361 | bpp = 16; /* This is just a guess */ |
bcc5a6f5 JS |
362 | } |
363 | } | |
364 | ||
9963d36d | 365 | ret = tilcdc_crtc_create(ddev); |
16ea975e | 366 | if (ret < 0) { |
9963d36d | 367 | dev_err(dev, "failed to create crtc\n"); |
9e79e062 | 368 | goto init_failed; |
16ea975e | 369 | } |
9963d36d | 370 | modeset_init(ddev); |
16ea975e | 371 | |
103cd8bc | 372 | if (priv->is_componentized) { |
923310ba | 373 | ret = component_bind_all(dev, ddev); |
103cd8bc | 374 | if (ret < 0) |
9e79e062 | 375 | goto init_failed; |
103cd8bc | 376 | |
ec9eab09 | 377 | ret = tilcdc_add_component_encoder(ddev); |
103cd8bc | 378 | if (ret < 0) |
9e79e062 | 379 | goto init_failed; |
ec9eab09 JS |
380 | } else { |
381 | ret = tilcdc_attach_external_device(ddev); | |
382 | if (ret) | |
383 | goto init_failed; | |
103cd8bc JS |
384 | } |
385 | ||
ec9eab09 JS |
386 | if (!priv->external_connector && |
387 | ((priv->num_encoders == 0) || (priv->num_connectors == 0))) { | |
923310ba | 388 | dev_err(dev, "no encoders/connectors found\n"); |
103cd8bc | 389 | ret = -ENXIO; |
9e79e062 | 390 | goto init_failed; |
103cd8bc JS |
391 | } |
392 | ||
923310ba | 393 | ret = drm_vblank_init(ddev, 1); |
16ea975e | 394 | if (ret < 0) { |
923310ba | 395 | dev_err(dev, "failed to initialize vblank\n"); |
9e79e062 | 396 | goto init_failed; |
16ea975e RC |
397 | } |
398 | ||
923310ba | 399 | ret = drm_irq_install(ddev, platform_get_irq(pdev, 0)); |
16ea975e | 400 | if (ret < 0) { |
923310ba | 401 | dev_err(dev, "failed to install IRQ handler\n"); |
9e79e062 | 402 | goto init_failed; |
16ea975e RC |
403 | } |
404 | ||
923310ba | 405 | drm_mode_config_reset(ddev); |
522a76f8 | 406 | |
923310ba | 407 | priv->fbdev = drm_fbdev_cma_init(ddev, bpp, |
e4563f6b | 408 | ddev->mode_config.num_connector); |
b478e336 EG |
409 | if (IS_ERR(priv->fbdev)) { |
410 | ret = PTR_ERR(priv->fbdev); | |
9e79e062 | 411 | goto init_failed; |
b478e336 | 412 | } |
16ea975e | 413 | |
923310ba JS |
414 | drm_kms_helper_poll_init(ddev); |
415 | ||
416 | ret = drm_dev_register(ddev, 0); | |
417 | if (ret) | |
9e79e062 | 418 | goto init_failed; |
16ea975e | 419 | |
9e79e062 | 420 | priv->is_registered = true; |
16ea975e RC |
421 | return 0; |
422 | ||
9e79e062 JS |
423 | init_failed: |
424 | tilcdc_fini(ddev); | |
d0ec32ca | 425 | |
16ea975e RC |
426 | return ret; |
427 | } | |
428 | ||
16ea975e RC |
429 | static void tilcdc_lastclose(struct drm_device *dev) |
430 | { | |
431 | struct tilcdc_drm_private *priv = dev->dev_private; | |
432 | drm_fbdev_cma_restore_mode(priv->fbdev); | |
433 | } | |
434 | ||
e9f0d76f | 435 | static irqreturn_t tilcdc_irq(int irq, void *arg) |
16ea975e RC |
436 | { |
437 | struct drm_device *dev = arg; | |
438 | struct tilcdc_drm_private *priv = dev->dev_private; | |
439 | return tilcdc_crtc_irq(priv->crtc); | |
440 | } | |
441 | ||
514d1a1f | 442 | #if defined(CONFIG_DEBUG_FS) |
16ea975e RC |
443 | static const struct { |
444 | const char *name; | |
445 | uint8_t rev; | |
446 | uint8_t save; | |
447 | uint32_t reg; | |
32501459 | 448 | } registers[] = { |
16ea975e RC |
449 | #define REG(rev, save, reg) { #reg, rev, save, reg } |
450 | /* exists in revision 1: */ | |
451 | REG(1, false, LCDC_PID_REG), | |
452 | REG(1, true, LCDC_CTRL_REG), | |
453 | REG(1, false, LCDC_STAT_REG), | |
454 | REG(1, true, LCDC_RASTER_CTRL_REG), | |
455 | REG(1, true, LCDC_RASTER_TIMING_0_REG), | |
456 | REG(1, true, LCDC_RASTER_TIMING_1_REG), | |
457 | REG(1, true, LCDC_RASTER_TIMING_2_REG), | |
458 | REG(1, true, LCDC_DMA_CTRL_REG), | |
459 | REG(1, true, LCDC_DMA_FB_BASE_ADDR_0_REG), | |
460 | REG(1, true, LCDC_DMA_FB_CEILING_ADDR_0_REG), | |
461 | REG(1, true, LCDC_DMA_FB_BASE_ADDR_1_REG), | |
462 | REG(1, true, LCDC_DMA_FB_CEILING_ADDR_1_REG), | |
463 | /* new in revision 2: */ | |
464 | REG(2, false, LCDC_RAW_STAT_REG), | |
465 | REG(2, false, LCDC_MASKED_STAT_REG), | |
f3a99946 | 466 | REG(2, true, LCDC_INT_ENABLE_SET_REG), |
16ea975e RC |
467 | REG(2, false, LCDC_INT_ENABLE_CLR_REG), |
468 | REG(2, false, LCDC_END_OF_INT_IND_REG), | |
469 | REG(2, true, LCDC_CLK_ENABLE_REG), | |
16ea975e RC |
470 | #undef REG |
471 | }; | |
29ddd6e1 | 472 | |
16ea975e RC |
473 | #endif |
474 | ||
475 | #ifdef CONFIG_DEBUG_FS | |
476 | static int tilcdc_regs_show(struct seq_file *m, void *arg) | |
477 | { | |
478 | struct drm_info_node *node = (struct drm_info_node *) m->private; | |
479 | struct drm_device *dev = node->minor->dev; | |
480 | struct tilcdc_drm_private *priv = dev->dev_private; | |
481 | unsigned i; | |
482 | ||
483 | pm_runtime_get_sync(dev->dev); | |
484 | ||
485 | seq_printf(m, "revision: %d\n", priv->rev); | |
486 | ||
487 | for (i = 0; i < ARRAY_SIZE(registers); i++) | |
488 | if (priv->rev >= registers[i].rev) | |
489 | seq_printf(m, "%s:\t %08x\n", registers[i].name, | |
490 | tilcdc_read(dev, registers[i].reg)); | |
491 | ||
492 | pm_runtime_put_sync(dev->dev); | |
493 | ||
494 | return 0; | |
495 | } | |
496 | ||
497 | static int tilcdc_mm_show(struct seq_file *m, void *arg) | |
498 | { | |
499 | struct drm_info_node *node = (struct drm_info_node *) m->private; | |
500 | struct drm_device *dev = node->minor->dev; | |
b5c3714f DV |
501 | struct drm_printer p = drm_seq_file_printer(m); |
502 | drm_mm_print(&dev->vma_offset_manager->vm_addr_space_mm, &p); | |
503 | return 0; | |
16ea975e RC |
504 | } |
505 | ||
506 | static struct drm_info_list tilcdc_debugfs_list[] = { | |
507 | { "regs", tilcdc_regs_show, 0 }, | |
508 | { "mm", tilcdc_mm_show, 0 }, | |
509 | { "fb", drm_fb_cma_debugfs_show, 0 }, | |
510 | }; | |
511 | ||
512 | static int tilcdc_debugfs_init(struct drm_minor *minor) | |
513 | { | |
514 | struct drm_device *dev = minor->dev; | |
515 | struct tilcdc_module *mod; | |
516 | int ret; | |
517 | ||
518 | ret = drm_debugfs_create_files(tilcdc_debugfs_list, | |
519 | ARRAY_SIZE(tilcdc_debugfs_list), | |
520 | minor->debugfs_root, minor); | |
521 | ||
522 | list_for_each_entry(mod, &module_list, list) | |
523 | if (mod->funcs->debugfs_init) | |
524 | mod->funcs->debugfs_init(mod, minor); | |
525 | ||
526 | if (ret) { | |
527 | dev_err(dev->dev, "could not install tilcdc_debugfs_list\n"); | |
528 | return ret; | |
529 | } | |
530 | ||
531 | return ret; | |
532 | } | |
16ea975e RC |
533 | #endif |
534 | ||
d55f7e5d | 535 | DEFINE_DRM_GEM_CMA_FOPS(fops); |
16ea975e RC |
536 | |
537 | static struct drm_driver tilcdc_driver = { | |
9c153905 | 538 | .driver_features = (DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET | |
305198de | 539 | DRIVER_PRIME | DRIVER_ATOMIC), |
16ea975e RC |
540 | .lastclose = tilcdc_lastclose, |
541 | .irq_handler = tilcdc_irq, | |
aa0438ce | 542 | .gem_free_object_unlocked = drm_gem_cma_free_object, |
16ea975e RC |
543 | .gem_vm_ops = &drm_gem_cma_vm_ops, |
544 | .dumb_create = drm_gem_cma_dumb_create, | |
9c153905 JS |
545 | |
546 | .prime_handle_to_fd = drm_gem_prime_handle_to_fd, | |
547 | .prime_fd_to_handle = drm_gem_prime_fd_to_handle, | |
548 | .gem_prime_import = drm_gem_prime_import, | |
549 | .gem_prime_export = drm_gem_prime_export, | |
550 | .gem_prime_get_sg_table = drm_gem_cma_prime_get_sg_table, | |
551 | .gem_prime_import_sg_table = drm_gem_cma_prime_import_sg_table, | |
552 | .gem_prime_vmap = drm_gem_cma_prime_vmap, | |
553 | .gem_prime_vunmap = drm_gem_cma_prime_vunmap, | |
554 | .gem_prime_mmap = drm_gem_cma_prime_mmap, | |
16ea975e RC |
555 | #ifdef CONFIG_DEBUG_FS |
556 | .debugfs_init = tilcdc_debugfs_init, | |
16ea975e RC |
557 | #endif |
558 | .fops = &fops, | |
559 | .name = "tilcdc", | |
560 | .desc = "TI LCD Controller DRM", | |
561 | .date = "20121205", | |
562 | .major = 1, | |
563 | .minor = 0, | |
564 | }; | |
565 | ||
566 | /* | |
567 | * Power management: | |
568 | */ | |
569 | ||
570 | #ifdef CONFIG_PM_SLEEP | |
571 | static int tilcdc_pm_suspend(struct device *dev) | |
572 | { | |
573 | struct drm_device *ddev = dev_get_drvdata(dev); | |
574 | struct tilcdc_drm_private *priv = ddev->dev_private; | |
16ea975e | 575 | |
514d1a1f | 576 | priv->saved_state = drm_atomic_helper_suspend(ddev); |
16ea975e | 577 | |
85fd27f8 DE |
578 | /* Select sleep pin state */ |
579 | pinctrl_pm_select_sleep_state(dev); | |
580 | ||
16ea975e RC |
581 | return 0; |
582 | } | |
583 | ||
584 | static int tilcdc_pm_resume(struct device *dev) | |
585 | { | |
586 | struct drm_device *ddev = dev_get_drvdata(dev); | |
587 | struct tilcdc_drm_private *priv = ddev->dev_private; | |
514d1a1f | 588 | int ret = 0; |
16ea975e | 589 | |
416a07fb DG |
590 | /* Select default pin state */ |
591 | pinctrl_pm_select_default_state(dev); | |
592 | ||
514d1a1f JS |
593 | if (priv->saved_state) |
594 | ret = drm_atomic_helper_resume(ddev, priv->saved_state); | |
16ea975e | 595 | |
514d1a1f | 596 | return ret; |
16ea975e RC |
597 | } |
598 | #endif | |
599 | ||
600 | static const struct dev_pm_ops tilcdc_pm_ops = { | |
601 | SET_SYSTEM_SLEEP_PM_OPS(tilcdc_pm_suspend, tilcdc_pm_resume) | |
602 | }; | |
603 | ||
604 | /* | |
605 | * Platform driver: | |
606 | */ | |
103cd8bc JS |
607 | static int tilcdc_bind(struct device *dev) |
608 | { | |
923310ba | 609 | return tilcdc_init(&tilcdc_driver, dev); |
103cd8bc JS |
610 | } |
611 | ||
612 | static void tilcdc_unbind(struct device *dev) | |
613 | { | |
20a98acb JS |
614 | struct drm_device *ddev = dev_get_drvdata(dev); |
615 | ||
616 | /* Check if a subcomponent has already triggered the unloading. */ | |
617 | if (!ddev->dev_private) | |
618 | return; | |
619 | ||
923310ba | 620 | tilcdc_fini(dev_get_drvdata(dev)); |
103cd8bc JS |
621 | } |
622 | ||
623 | static const struct component_master_ops tilcdc_comp_ops = { | |
624 | .bind = tilcdc_bind, | |
625 | .unbind = tilcdc_unbind, | |
626 | }; | |
627 | ||
16ea975e RC |
628 | static int tilcdc_pdev_probe(struct platform_device *pdev) |
629 | { | |
103cd8bc JS |
630 | struct component_match *match = NULL; |
631 | int ret; | |
632 | ||
16ea975e RC |
633 | /* bail out early if no DT data: */ |
634 | if (!pdev->dev.of_node) { | |
635 | dev_err(&pdev->dev, "device-tree data is missing\n"); | |
636 | return -ENXIO; | |
637 | } | |
638 | ||
103cd8bc JS |
639 | ret = tilcdc_get_external_components(&pdev->dev, &match); |
640 | if (ret < 0) | |
641 | return ret; | |
642 | else if (ret == 0) | |
923310ba | 643 | return tilcdc_init(&tilcdc_driver, &pdev->dev); |
103cd8bc JS |
644 | else |
645 | return component_master_add_with_match(&pdev->dev, | |
646 | &tilcdc_comp_ops, | |
647 | match); | |
16ea975e RC |
648 | } |
649 | ||
650 | static int tilcdc_pdev_remove(struct platform_device *pdev) | |
651 | { | |
20a98acb | 652 | int ret; |
103cd8bc | 653 | |
20a98acb JS |
654 | ret = tilcdc_get_external_components(&pdev->dev, NULL); |
655 | if (ret < 0) | |
656 | return ret; | |
657 | else if (ret == 0) | |
923310ba | 658 | tilcdc_fini(platform_get_drvdata(pdev)); |
20a98acb JS |
659 | else |
660 | component_master_del(&pdev->dev, &tilcdc_comp_ops); | |
16ea975e RC |
661 | |
662 | return 0; | |
663 | } | |
664 | ||
665 | static struct of_device_id tilcdc_of_match[] = { | |
666 | { .compatible = "ti,am33xx-tilcdc", }, | |
507b72b2 | 667 | { .compatible = "ti,da850-tilcdc", }, |
16ea975e RC |
668 | { }, |
669 | }; | |
670 | MODULE_DEVICE_TABLE(of, tilcdc_of_match); | |
671 | ||
672 | static struct platform_driver tilcdc_platform_driver = { | |
673 | .probe = tilcdc_pdev_probe, | |
674 | .remove = tilcdc_pdev_remove, | |
675 | .driver = { | |
16ea975e RC |
676 | .name = "tilcdc", |
677 | .pm = &tilcdc_pm_ops, | |
678 | .of_match_table = tilcdc_of_match, | |
679 | }, | |
680 | }; | |
681 | ||
682 | static int __init tilcdc_drm_init(void) | |
683 | { | |
684 | DBG("init"); | |
685 | tilcdc_tfp410_init(); | |
0d4bbaf9 | 686 | tilcdc_panel_init(); |
16ea975e RC |
687 | return platform_driver_register(&tilcdc_platform_driver); |
688 | } | |
689 | ||
690 | static void __exit tilcdc_drm_fini(void) | |
691 | { | |
692 | DBG("fini"); | |
16ea975e | 693 | platform_driver_unregister(&tilcdc_platform_driver); |
eb565a2b | 694 | tilcdc_panel_fini(); |
eb565a2b | 695 | tilcdc_tfp410_fini(); |
16ea975e RC |
696 | } |
697 | ||
2023d84d | 698 | module_init(tilcdc_drm_init); |
16ea975e RC |
699 | module_exit(tilcdc_drm_fini); |
700 | ||
701 | MODULE_AUTHOR("Rob Clark <robdclark@gmail.com"); | |
702 | MODULE_DESCRIPTION("TI LCD Controller DRM Driver"); | |
703 | MODULE_LICENSE("GPL"); |