diff options
author | Jiri Kosina <jkosina@suse.cz> | 2015-09-01 09:35:24 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2015-09-01 09:35:24 -0400 |
commit | 067e2601d3c076abbf45db91261f9065eaa879b2 (patch) | |
tree | 86c8d4b913873dbd3b4ff23562a3a8597984b4df /drivers/gpu/drm/tilcdc | |
parent | 3e097d1271ecdff2f251a54ddfc5eaa1f9821e96 (diff) | |
parent | 931830aa5c251e0803523213428f777a48bde254 (diff) |
Merge branch 'for-4.3/gembird' into for-linus
Diffstat (limited to 'drivers/gpu/drm/tilcdc')
-rw-r--r-- | drivers/gpu/drm/tilcdc/Kconfig | 12 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/Makefile | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 36 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.c | 99 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_drv.h | 6 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_external.c | 166 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_external.h (renamed from drivers/gpu/drm/tilcdc/tilcdc_slave.h) | 17 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_slave.c | 411 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c | 270 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts | 72 | ||||
-rw-r--r-- | drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h | 25 |
11 files changed, 670 insertions, 449 deletions
diff --git a/drivers/gpu/drm/tilcdc/Kconfig b/drivers/gpu/drm/tilcdc/Kconfig index 8394a0b3993e..78beafb0742c 100644 --- a/drivers/gpu/drm/tilcdc/Kconfig +++ b/drivers/gpu/drm/tilcdc/Kconfig | |||
@@ -12,3 +12,15 @@ config DRM_TILCDC | |||
12 | Choose this option if you have an TI SoC with LCDC display | 12 | Choose this option if you have an TI SoC with LCDC display |
13 | controller, for example AM33xx in beagle-bone, DA8xx, or | 13 | controller, for example AM33xx in beagle-bone, DA8xx, or |
14 | OMAP-L1xx. This driver replaces the FB_DA8XX fbdev driver. | 14 | OMAP-L1xx. This driver replaces the FB_DA8XX fbdev driver. |
15 | |||
16 | config DRM_TILCDC_SLAVE_COMPAT | ||
17 | bool "Support device tree blobs using TI LCDC Slave binding" | ||
18 | depends on DRM_TILCDC | ||
19 | default y | ||
20 | select OF_RESOLVE | ||
21 | select OF_OVERLAY | ||
22 | help | ||
23 | Choose this option if you need a kernel that is compatible | ||
24 | with device tree blobs using the obsolete "ti,tilcdc,slave" | ||
25 | binding. If you find "ti,tilcdc,slave"-string from your DTB, | ||
26 | you probably need this. Otherwise you do not. | ||
diff --git a/drivers/gpu/drm/tilcdc/Makefile b/drivers/gpu/drm/tilcdc/Makefile index 7d2eefe94bf7..deeca4869d94 100644 --- a/drivers/gpu/drm/tilcdc/Makefile +++ b/drivers/gpu/drm/tilcdc/Makefile | |||
@@ -3,11 +3,14 @@ ifeq (, $(findstring -W,$(EXTRA_CFLAGS))) | |||
3 | ccflags-y += -Werror | 3 | ccflags-y += -Werror |
4 | endif | 4 | endif |
5 | 5 | ||
6 | obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \ | ||
7 | tilcdc_slave_compat.dtb.o | ||
8 | |||
6 | tilcdc-y := \ | 9 | tilcdc-y := \ |
7 | tilcdc_crtc.o \ | 10 | tilcdc_crtc.o \ |
8 | tilcdc_tfp410.o \ | 11 | tilcdc_tfp410.o \ |
9 | tilcdc_slave.o \ | ||
10 | tilcdc_panel.o \ | 12 | tilcdc_panel.o \ |
13 | tilcdc_external.o \ | ||
11 | tilcdc_drv.o | 14 | tilcdc_drv.o |
12 | 15 | ||
13 | obj-$(CONFIG_DRM_TILCDC) += tilcdc.o | 16 | obj-$(CONFIG_DRM_TILCDC) += tilcdc.o |
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c index c73588483be0..7d07733bdc86 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c | |||
@@ -37,6 +37,9 @@ struct tilcdc_crtc { | |||
37 | 37 | ||
38 | /* for deferred fb unref's: */ | 38 | /* for deferred fb unref's: */ |
39 | struct drm_flip_work unref_work; | 39 | struct drm_flip_work unref_work; |
40 | |||
41 | /* Only set if an external encoder is connected */ | ||
42 | bool simulate_vesa_sync; | ||
40 | }; | 43 | }; |
41 | #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) | 44 | #define to_tilcdc_crtc(x) container_of(x, struct tilcdc_crtc, base) |
42 | 45 | ||
@@ -135,11 +138,12 @@ static void stop(struct drm_crtc *crtc) | |||
135 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); | 138 | tilcdc_clear(dev, LCDC_RASTER_CTRL_REG, LCDC_RASTER_ENABLE); |
136 | } | 139 | } |
137 | 140 | ||
141 | static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode); | ||
138 | static void tilcdc_crtc_destroy(struct drm_crtc *crtc) | 142 | static void tilcdc_crtc_destroy(struct drm_crtc *crtc) |
139 | { | 143 | { |
140 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 144 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); |
141 | 145 | ||
142 | WARN_ON(tilcdc_crtc->dpms == DRM_MODE_DPMS_ON); | 146 | tilcdc_crtc_dpms(crtc, DRM_MODE_DPMS_OFF); |
143 | 147 | ||
144 | drm_crtc_cleanup(crtc); | 148 | drm_crtc_cleanup(crtc); |
145 | drm_flip_work_cleanup(&tilcdc_crtc->unref_work); | 149 | drm_flip_work_cleanup(&tilcdc_crtc->unref_work); |
@@ -213,6 +217,28 @@ static bool tilcdc_crtc_mode_fixup(struct drm_crtc *crtc, | |||
213 | const struct drm_display_mode *mode, | 217 | const struct drm_display_mode *mode, |
214 | struct drm_display_mode *adjusted_mode) | 218 | struct drm_display_mode *adjusted_mode) |
215 | { | 219 | { |
220 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
221 | |||
222 | if (!tilcdc_crtc->simulate_vesa_sync) | ||
223 | return true; | ||
224 | |||
225 | /* | ||
226 | * tilcdc does not generate VESA-compliant sync but aligns | ||
227 | * VS on the second edge of HS instead of first edge. | ||
228 | * We use adjusted_mode, to fixup sync by aligning both rising | ||
229 | * edges and add HSKEW offset to fix the sync. | ||
230 | */ | ||
231 | adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; | ||
232 | adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; | ||
233 | |||
234 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) { | ||
235 | adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; | ||
236 | adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; | ||
237 | } else { | ||
238 | adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; | ||
239 | adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; | ||
240 | } | ||
241 | |||
216 | return true; | 242 | return true; |
217 | } | 243 | } |
218 | 244 | ||
@@ -533,6 +559,14 @@ void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, | |||
533 | tilcdc_crtc->info = info; | 559 | tilcdc_crtc->info = info; |
534 | } | 560 | } |
535 | 561 | ||
562 | void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, | ||
563 | bool simulate_vesa_sync) | ||
564 | { | ||
565 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | ||
566 | |||
567 | tilcdc_crtc->simulate_vesa_sync = simulate_vesa_sync; | ||
568 | } | ||
569 | |||
536 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc) | 570 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc) |
537 | { | 571 | { |
538 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); | 572 | struct tilcdc_crtc *tilcdc_crtc = to_tilcdc_crtc(crtc); |
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.c b/drivers/gpu/drm/tilcdc/tilcdc_drv.c index 095fca91525c..0f283a3b932c 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c | |||
@@ -17,16 +17,17 @@ | |||
17 | 17 | ||
18 | /* LCDC DRM driver, based on da8xx-fb */ | 18 | /* LCDC DRM driver, based on da8xx-fb */ |
19 | 19 | ||
20 | #include <linux/component.h> | ||
21 | |||
20 | #include "tilcdc_drv.h" | 22 | #include "tilcdc_drv.h" |
21 | #include "tilcdc_regs.h" | 23 | #include "tilcdc_regs.h" |
22 | #include "tilcdc_tfp410.h" | 24 | #include "tilcdc_tfp410.h" |
23 | #include "tilcdc_slave.h" | ||
24 | #include "tilcdc_panel.h" | 25 | #include "tilcdc_panel.h" |
26 | #include "tilcdc_external.h" | ||
25 | 27 | ||
26 | #include "drm_fb_helper.h" | 28 | #include "drm_fb_helper.h" |
27 | 29 | ||
28 | static LIST_HEAD(module_list); | 30 | static LIST_HEAD(module_list); |
29 | static bool slave_probing; | ||
30 | 31 | ||
31 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, | 32 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, |
32 | const struct tilcdc_module_ops *funcs) | 33 | const struct tilcdc_module_ops *funcs) |
@@ -42,11 +43,6 @@ void tilcdc_module_cleanup(struct tilcdc_module *mod) | |||
42 | list_del(&mod->list); | 43 | list_del(&mod->list); |
43 | } | 44 | } |
44 | 45 | ||
45 | void tilcdc_slave_probedefer(bool defered) | ||
46 | { | ||
47 | slave_probing = defered; | ||
48 | } | ||
49 | |||
50 | static struct of_device_id tilcdc_of_match[]; | 46 | static struct of_device_id tilcdc_of_match[]; |
51 | 47 | ||
52 | static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, | 48 | static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, |
@@ -80,13 +76,6 @@ static int modeset_init(struct drm_device *dev) | |||
80 | mod->funcs->modeset_init(mod, dev); | 76 | mod->funcs->modeset_init(mod, dev); |
81 | } | 77 | } |
82 | 78 | ||
83 | if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) { | ||
84 | /* oh nos! */ | ||
85 | dev_err(dev->dev, "no encoders/connectors found\n"); | ||
86 | drm_mode_config_cleanup(dev); | ||
87 | return -ENXIO; | ||
88 | } | ||
89 | |||
90 | dev->mode_config.min_width = 0; | 79 | dev->mode_config.min_width = 0; |
91 | dev->mode_config.min_height = 0; | 80 | dev->mode_config.min_height = 0; |
92 | dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); | 81 | dev->mode_config.max_width = tilcdc_crtc_max_width(priv->crtc); |
@@ -121,6 +110,8 @@ static int tilcdc_unload(struct drm_device *dev) | |||
121 | { | 110 | { |
122 | struct tilcdc_drm_private *priv = dev->dev_private; | 111 | struct tilcdc_drm_private *priv = dev->dev_private; |
123 | 112 | ||
113 | tilcdc_remove_external_encoders(dev); | ||
114 | |||
124 | drm_fbdev_cma_fini(priv->fbdev); | 115 | drm_fbdev_cma_fini(priv->fbdev); |
125 | drm_kms_helper_poll_fini(dev); | 116 | drm_kms_helper_poll_fini(dev); |
126 | drm_mode_config_cleanup(dev); | 117 | drm_mode_config_cleanup(dev); |
@@ -171,6 +162,9 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
171 | 162 | ||
172 | dev->dev_private = priv; | 163 | dev->dev_private = priv; |
173 | 164 | ||
165 | priv->is_componentized = | ||
166 | tilcdc_get_external_components(dev->dev, NULL) > 0; | ||
167 | |||
174 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); | 168 | priv->wq = alloc_ordered_workqueue("tilcdc", 0); |
175 | if (!priv->wq) { | 169 | if (!priv->wq) { |
176 | ret = -ENOMEM; | 170 | ret = -ENOMEM; |
@@ -233,6 +227,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
233 | DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock); | 227 | DBG("Maximum Pixel Clock Value %dKHz", priv->max_pixelclock); |
234 | 228 | ||
235 | pm_runtime_enable(dev->dev); | 229 | pm_runtime_enable(dev->dev); |
230 | pm_runtime_irq_safe(dev->dev); | ||
236 | 231 | ||
237 | /* Determine LCD IP Version */ | 232 | /* Determine LCD IP Version */ |
238 | pm_runtime_get_sync(dev->dev); | 233 | pm_runtime_get_sync(dev->dev); |
@@ -260,10 +255,28 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
260 | goto fail_cpufreq_unregister; | 255 | goto fail_cpufreq_unregister; |
261 | } | 256 | } |
262 | 257 | ||
258 | platform_set_drvdata(pdev, dev); | ||
259 | |||
260 | if (priv->is_componentized) { | ||
261 | ret = component_bind_all(dev->dev, dev); | ||
262 | if (ret < 0) | ||
263 | goto fail_mode_config_cleanup; | ||
264 | |||
265 | ret = tilcdc_add_external_encoders(dev, &bpp); | ||
266 | if (ret < 0) | ||
267 | goto fail_component_cleanup; | ||
268 | } | ||
269 | |||
270 | if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) { | ||
271 | dev_err(dev->dev, "no encoders/connectors found\n"); | ||
272 | ret = -ENXIO; | ||
273 | goto fail_external_cleanup; | ||
274 | } | ||
275 | |||
263 | ret = drm_vblank_init(dev, 1); | 276 | ret = drm_vblank_init(dev, 1); |
264 | if (ret < 0) { | 277 | if (ret < 0) { |
265 | dev_err(dev->dev, "failed to initialize vblank\n"); | 278 | dev_err(dev->dev, "failed to initialize vblank\n"); |
266 | goto fail_mode_config_cleanup; | 279 | goto fail_external_cleanup; |
267 | } | 280 | } |
268 | 281 | ||
269 | pm_runtime_get_sync(dev->dev); | 282 | pm_runtime_get_sync(dev->dev); |
@@ -274,9 +287,6 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags) | |||
274 | goto fail_vblank_cleanup; | 287 | goto fail_vblank_cleanup; |
275 | } | 288 | } |
276 | 289 | ||
277 | platform_set_drvdata(pdev, dev); | ||
278 | |||
279 | |||
280 | list_for_each_entry(mod, &module_list, list) { | 290 | list_for_each_entry(mod, &module_list, list) { |
281 | DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp); | 291 | DBG("%s: preferred_bpp: %d", mod->name, mod->preferred_bpp); |
282 | bpp = mod->preferred_bpp; | 292 | bpp = mod->preferred_bpp; |
@@ -307,6 +317,13 @@ fail_vblank_cleanup: | |||
307 | fail_mode_config_cleanup: | 317 | fail_mode_config_cleanup: |
308 | drm_mode_config_cleanup(dev); | 318 | drm_mode_config_cleanup(dev); |
309 | 319 | ||
320 | fail_component_cleanup: | ||
321 | if (priv->is_componentized) | ||
322 | component_unbind_all(dev->dev, dev); | ||
323 | |||
324 | fail_external_cleanup: | ||
325 | tilcdc_remove_external_encoders(dev); | ||
326 | |||
310 | fail_cpufreq_unregister: | 327 | fail_cpufreq_unregister: |
311 | pm_runtime_disable(dev->dev); | 328 | pm_runtime_disable(dev->dev); |
312 | #ifdef CONFIG_CPU_FREQ | 329 | #ifdef CONFIG_CPU_FREQ |
@@ -612,24 +629,56 @@ static const struct dev_pm_ops tilcdc_pm_ops = { | |||
612 | * Platform driver: | 629 | * Platform driver: |
613 | */ | 630 | */ |
614 | 631 | ||
632 | static int tilcdc_bind(struct device *dev) | ||
633 | { | ||
634 | return drm_platform_init(&tilcdc_driver, to_platform_device(dev)); | ||
635 | } | ||
636 | |||
637 | static void tilcdc_unbind(struct device *dev) | ||
638 | { | ||
639 | drm_put_dev(dev_get_drvdata(dev)); | ||
640 | } | ||
641 | |||
642 | static const struct component_master_ops tilcdc_comp_ops = { | ||
643 | .bind = tilcdc_bind, | ||
644 | .unbind = tilcdc_unbind, | ||
645 | }; | ||
646 | |||
615 | static int tilcdc_pdev_probe(struct platform_device *pdev) | 647 | static int tilcdc_pdev_probe(struct platform_device *pdev) |
616 | { | 648 | { |
649 | struct component_match *match = NULL; | ||
650 | int ret; | ||
651 | |||
617 | /* bail out early if no DT data: */ | 652 | /* bail out early if no DT data: */ |
618 | if (!pdev->dev.of_node) { | 653 | if (!pdev->dev.of_node) { |
619 | dev_err(&pdev->dev, "device-tree data is missing\n"); | 654 | dev_err(&pdev->dev, "device-tree data is missing\n"); |
620 | return -ENXIO; | 655 | return -ENXIO; |
621 | } | 656 | } |
622 | 657 | ||
623 | /* defer probing if slave is in deferred probing */ | 658 | ret = tilcdc_get_external_components(&pdev->dev, &match); |
624 | if (slave_probing == true) | 659 | if (ret < 0) |
625 | return -EPROBE_DEFER; | 660 | return ret; |
626 | 661 | else if (ret == 0) | |
627 | return drm_platform_init(&tilcdc_driver, pdev); | 662 | return drm_platform_init(&tilcdc_driver, pdev); |
663 | else | ||
664 | return component_master_add_with_match(&pdev->dev, | ||
665 | &tilcdc_comp_ops, | ||
666 | match); | ||
628 | } | 667 | } |
629 | 668 | ||
630 | static int tilcdc_pdev_remove(struct platform_device *pdev) | 669 | static int tilcdc_pdev_remove(struct platform_device *pdev) |
631 | { | 670 | { |
632 | drm_put_dev(platform_get_drvdata(pdev)); | 671 | struct drm_device *ddev = dev_get_drvdata(&pdev->dev); |
672 | struct tilcdc_drm_private *priv = ddev->dev_private; | ||
673 | |||
674 | /* Check if a subcomponent has already triggered the unloading. */ | ||
675 | if (!priv) | ||
676 | return 0; | ||
677 | |||
678 | if (priv->is_componentized) | ||
679 | component_master_del(&pdev->dev, &tilcdc_comp_ops); | ||
680 | else | ||
681 | drm_put_dev(platform_get_drvdata(pdev)); | ||
633 | 682 | ||
634 | return 0; | 683 | return 0; |
635 | } | 684 | } |
@@ -654,7 +703,6 @@ static int __init tilcdc_drm_init(void) | |||
654 | { | 703 | { |
655 | DBG("init"); | 704 | DBG("init"); |
656 | tilcdc_tfp410_init(); | 705 | tilcdc_tfp410_init(); |
657 | tilcdc_slave_init(); | ||
658 | tilcdc_panel_init(); | 706 | tilcdc_panel_init(); |
659 | return platform_driver_register(&tilcdc_platform_driver); | 707 | return platform_driver_register(&tilcdc_platform_driver); |
660 | } | 708 | } |
@@ -664,7 +712,6 @@ static void __exit tilcdc_drm_fini(void) | |||
664 | DBG("fini"); | 712 | DBG("fini"); |
665 | platform_driver_unregister(&tilcdc_platform_driver); | 713 | platform_driver_unregister(&tilcdc_platform_driver); |
666 | tilcdc_panel_fini(); | 714 | tilcdc_panel_fini(); |
667 | tilcdc_slave_fini(); | ||
668 | tilcdc_tfp410_fini(); | 715 | tilcdc_tfp410_fini(); |
669 | } | 716 | } |
670 | 717 | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_drv.h b/drivers/gpu/drm/tilcdc/tilcdc_drv.h index 7596c144a9fb..e863ad0d26fe 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_drv.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.h | |||
@@ -85,6 +85,9 @@ struct tilcdc_drm_private { | |||
85 | 85 | ||
86 | unsigned int num_connectors; | 86 | unsigned int num_connectors; |
87 | struct drm_connector *connectors[8]; | 87 | struct drm_connector *connectors[8]; |
88 | const struct drm_connector_helper_funcs *connector_funcs[8]; | ||
89 | |||
90 | bool is_componentized; | ||
88 | }; | 91 | }; |
89 | 92 | ||
90 | /* Sub-module for display. Since we don't know at compile time what panels | 93 | /* Sub-module for display. Since we don't know at compile time what panels |
@@ -116,7 +119,6 @@ struct tilcdc_module { | |||
116 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, | 119 | void tilcdc_module_init(struct tilcdc_module *mod, const char *name, |
117 | const struct tilcdc_module_ops *funcs); | 120 | const struct tilcdc_module_ops *funcs); |
118 | void tilcdc_module_cleanup(struct tilcdc_module *mod); | 121 | void tilcdc_module_cleanup(struct tilcdc_module *mod); |
119 | void tilcdc_slave_probedefer(bool defered); | ||
120 | 122 | ||
121 | /* Panel config that needs to be set in the crtc, but is not coming from | 123 | /* Panel config that needs to be set in the crtc, but is not coming from |
122 | * the mode timings. The display module is expected to call | 124 | * the mode timings. The display module is expected to call |
@@ -166,6 +168,8 @@ irqreturn_t tilcdc_crtc_irq(struct drm_crtc *crtc); | |||
166 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc); | 168 | void tilcdc_crtc_update_clk(struct drm_crtc *crtc); |
167 | void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, | 169 | void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, |
168 | const struct tilcdc_panel_info *info); | 170 | const struct tilcdc_panel_info *info); |
171 | void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc, | ||
172 | bool simulate_vesa_sync); | ||
169 | int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); | 173 | int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); |
170 | int tilcdc_crtc_max_width(struct drm_crtc *crtc); | 174 | int tilcdc_crtc_max_width(struct drm_crtc *crtc); |
171 | 175 | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_external.c b/drivers/gpu/drm/tilcdc/tilcdc_external.c new file mode 100644 index 000000000000..03acb4f99982 --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.c | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Texas Instruments | ||
3 | * Author: Jyri Sarha <jsarha@ti.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 | */ | ||
10 | |||
11 | #include <linux/component.h> | ||
12 | #include <linux/of_graph.h> | ||
13 | |||
14 | #include "tilcdc_drv.h" | ||
15 | #include "tilcdc_external.h" | ||
16 | |||
17 | static const struct tilcdc_panel_info panel_info_tda998x = { | ||
18 | .ac_bias = 255, | ||
19 | .ac_bias_intrpt = 0, | ||
20 | .dma_burst_sz = 16, | ||
21 | .bpp = 16, | ||
22 | .fdd = 0x80, | ||
23 | .tft_alt_mode = 0, | ||
24 | .invert_pxl_clk = 1, | ||
25 | .sync_edge = 1, | ||
26 | .sync_ctrl = 1, | ||
27 | .raster_order = 0, | ||
28 | }; | ||
29 | |||
30 | static int tilcdc_external_mode_valid(struct drm_connector *connector, | ||
31 | struct drm_display_mode *mode) | ||
32 | { | ||
33 | struct tilcdc_drm_private *priv = connector->dev->dev_private; | ||
34 | int ret, i; | ||
35 | |||
36 | ret = tilcdc_crtc_mode_valid(priv->crtc, mode); | ||
37 | if (ret != MODE_OK) | ||
38 | return ret; | ||
39 | |||
40 | for (i = 0; i < priv->num_connectors && | ||
41 | priv->connectors[i] != connector; i++) | ||
42 | ; | ||
43 | |||
44 | BUG_ON(priv->connectors[i] != connector); | ||
45 | BUG_ON(!priv->connector_funcs[i]); | ||
46 | |||
47 | /* If the connector has its own mode_valid call it. */ | ||
48 | if (!IS_ERR(priv->connector_funcs[i]) && | ||
49 | priv->connector_funcs[i]->mode_valid) | ||
50 | return priv->connector_funcs[i]->mode_valid(connector, mode); | ||
51 | |||
52 | return MODE_OK; | ||
53 | } | ||
54 | |||
55 | static int tilcdc_add_external_encoder(struct drm_device *dev, int *bpp, | ||
56 | struct drm_connector *connector) | ||
57 | { | ||
58 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
59 | struct drm_connector_helper_funcs *connector_funcs; | ||
60 | |||
61 | priv->connectors[priv->num_connectors] = connector; | ||
62 | priv->encoders[priv->num_encoders++] = connector->encoder; | ||
63 | |||
64 | /* Only tda998x is supported at the moment. */ | ||
65 | tilcdc_crtc_set_simulate_vesa_sync(priv->crtc, true); | ||
66 | tilcdc_crtc_set_panel_info(priv->crtc, &panel_info_tda998x); | ||
67 | *bpp = panel_info_tda998x.bpp; | ||
68 | |||
69 | connector_funcs = devm_kzalloc(dev->dev, sizeof(*connector_funcs), | ||
70 | GFP_KERNEL); | ||
71 | if (!connector_funcs) | ||
72 | return -ENOMEM; | ||
73 | |||
74 | /* connector->helper_private contains always struct | ||
75 | * connector_helper_funcs pointer. For tilcdc crtc to have a | ||
76 | * say if a specific mode is Ok, we need to install our own | ||
77 | * helper functions. In our helper functions we copy | ||
78 | * everything else but use our own mode_valid() (above). | ||
79 | */ | ||
80 | if (connector->helper_private) { | ||
81 | priv->connector_funcs[priv->num_connectors] = | ||
82 | connector->helper_private; | ||
83 | *connector_funcs = *priv->connector_funcs[priv->num_connectors]; | ||
84 | } else { | ||
85 | priv->connector_funcs[priv->num_connectors] = ERR_PTR(-ENOENT); | ||
86 | } | ||
87 | connector_funcs->mode_valid = tilcdc_external_mode_valid; | ||
88 | drm_connector_helper_add(connector, connector_funcs); | ||
89 | priv->num_connectors++; | ||
90 | |||
91 | dev_dbg(dev->dev, "External encoder '%s' connected\n", | ||
92 | connector->encoder->name); | ||
93 | |||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp) | ||
98 | { | ||
99 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
100 | struct drm_connector *connector; | ||
101 | int num_internal_connectors = priv->num_connectors; | ||
102 | |||
103 | list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | ||
104 | bool found = false; | ||
105 | int i, ret; | ||
106 | |||
107 | for (i = 0; i < num_internal_connectors; i++) | ||
108 | if (connector == priv->connectors[i]) | ||
109 | found = true; | ||
110 | if (!found) { | ||
111 | ret = tilcdc_add_external_encoder(dev, bpp, connector); | ||
112 | if (ret) | ||
113 | return ret; | ||
114 | } | ||
115 | } | ||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | void tilcdc_remove_external_encoders(struct drm_device *dev) | ||
120 | { | ||
121 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
122 | int i; | ||
123 | |||
124 | /* Restore the original helper functions, if any. */ | ||
125 | for (i = 0; i < priv->num_connectors; i++) | ||
126 | if (IS_ERR(priv->connector_funcs[i])) | ||
127 | drm_connector_helper_add(priv->connectors[i], NULL); | ||
128 | else if (priv->connector_funcs[i]) | ||
129 | drm_connector_helper_add(priv->connectors[i], | ||
130 | priv->connector_funcs[i]); | ||
131 | } | ||
132 | |||
133 | static int dev_match_of(struct device *dev, void *data) | ||
134 | { | ||
135 | return dev->of_node == data; | ||
136 | } | ||
137 | |||
138 | int tilcdc_get_external_components(struct device *dev, | ||
139 | struct component_match **match) | ||
140 | { | ||
141 | struct device_node *ep = NULL; | ||
142 | int count = 0; | ||
143 | |||
144 | while ((ep = of_graph_get_next_endpoint(dev->of_node, ep))) { | ||
145 | struct device_node *node; | ||
146 | |||
147 | node = of_graph_get_remote_port_parent(ep); | ||
148 | if (!node && !of_device_is_available(node)) { | ||
149 | of_node_put(node); | ||
150 | continue; | ||
151 | } | ||
152 | |||
153 | dev_dbg(dev, "Subdevice node '%s' found\n", node->name); | ||
154 | if (match) | ||
155 | component_match_add(dev, match, dev_match_of, node); | ||
156 | of_node_put(node); | ||
157 | count++; | ||
158 | } | ||
159 | |||
160 | if (count > 1) { | ||
161 | dev_err(dev, "Only one external encoder is supported\n"); | ||
162 | return -EINVAL; | ||
163 | } | ||
164 | |||
165 | return count; | ||
166 | } | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.h b/drivers/gpu/drm/tilcdc/tilcdc_external.h index 2f8504848320..6aabe2788760 100644 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.h +++ b/drivers/gpu/drm/tilcdc/tilcdc_external.h | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2012 Texas Instruments | 2 | * Copyright (C) 2015 Texas Instruments |
3 | * Author: Rob Clark <robdclark@gmail.com> | 3 | * Author: Jyri Sarha <jsarha@ti.com> |
4 | * | 4 | * |
5 | * This program is free software; you can redistribute it and/or modify it | 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 | 6 | * under the terms of the GNU General Public License version 2 as published by |
@@ -15,12 +15,11 @@ | |||
15 | * this program. If not, see <http://www.gnu.org/licenses/>. | 15 | * this program. If not, see <http://www.gnu.org/licenses/>. |
16 | */ | 16 | */ |
17 | 17 | ||
18 | #ifndef __TILCDC_SLAVE_H__ | 18 | #ifndef __TILCDC_EXTERNAL_H__ |
19 | #define __TILCDC_SLAVE_H__ | 19 | #define __TILCDC_EXTERNAL_H__ |
20 | |||
21 | /* sub-module for i2c slave encoder output */ | ||
22 | |||
23 | int tilcdc_slave_init(void); | ||
24 | void tilcdc_slave_fini(void); | ||
25 | 20 | ||
21 | int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp); | ||
22 | void tilcdc_remove_external_encoders(struct drm_device *dev); | ||
23 | int tilcdc_get_external_components(struct device *dev, | ||
24 | struct component_match **match); | ||
26 | #endif /* __TILCDC_SLAVE_H__ */ | 25 | #endif /* __TILCDC_SLAVE_H__ */ |
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave.c b/drivers/gpu/drm/tilcdc/tilcdc_slave.c deleted file mode 100644 index 3775fd49dac4..000000000000 --- a/drivers/gpu/drm/tilcdc/tilcdc_slave.c +++ /dev/null | |||
@@ -1,411 +0,0 @@ | |||
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 | #include <linux/i2c.h> | ||
19 | #include <linux/pinctrl/pinmux.h> | ||
20 | #include <linux/pinctrl/consumer.h> | ||
21 | #include <drm/drm_encoder_slave.h> | ||
22 | |||
23 | #include "tilcdc_drv.h" | ||
24 | |||
25 | struct slave_module { | ||
26 | struct tilcdc_module base; | ||
27 | struct i2c_adapter *i2c; | ||
28 | }; | ||
29 | #define to_slave_module(x) container_of(x, struct slave_module, base) | ||
30 | |||
31 | static const struct tilcdc_panel_info slave_info = { | ||
32 | .bpp = 16, | ||
33 | .ac_bias = 255, | ||
34 | .ac_bias_intrpt = 0, | ||
35 | .dma_burst_sz = 16, | ||
36 | .fdd = 0x80, | ||
37 | .tft_alt_mode = 0, | ||
38 | .sync_edge = 0, | ||
39 | .sync_ctrl = 1, | ||
40 | .raster_order = 0, | ||
41 | }; | ||
42 | |||
43 | |||
44 | /* | ||
45 | * Encoder: | ||
46 | */ | ||
47 | |||
48 | struct slave_encoder { | ||
49 | struct drm_encoder_slave base; | ||
50 | struct slave_module *mod; | ||
51 | }; | ||
52 | #define to_slave_encoder(x) container_of(to_encoder_slave(x), struct slave_encoder, base) | ||
53 | |||
54 | static inline struct drm_encoder_slave_funcs * | ||
55 | get_slave_funcs(struct drm_encoder *enc) | ||
56 | { | ||
57 | return to_encoder_slave(enc)->slave_funcs; | ||
58 | } | ||
59 | |||
60 | static void slave_encoder_destroy(struct drm_encoder *encoder) | ||
61 | { | ||
62 | struct slave_encoder *slave_encoder = to_slave_encoder(encoder); | ||
63 | if (get_slave_funcs(encoder)) | ||
64 | get_slave_funcs(encoder)->destroy(encoder); | ||
65 | drm_encoder_cleanup(encoder); | ||
66 | kfree(slave_encoder); | ||
67 | } | ||
68 | |||
69 | static void slave_encoder_prepare(struct drm_encoder *encoder) | ||
70 | { | ||
71 | drm_i2c_encoder_prepare(encoder); | ||
72 | tilcdc_crtc_set_panel_info(encoder->crtc, &slave_info); | ||
73 | } | ||
74 | |||
75 | static bool slave_encoder_fixup(struct drm_encoder *encoder, | ||
76 | const struct drm_display_mode *mode, | ||
77 | struct drm_display_mode *adjusted_mode) | ||
78 | { | ||
79 | /* | ||
80 | * tilcdc does not generate VESA-complient sync but aligns | ||
81 | * VS on the second edge of HS instead of first edge. | ||
82 | * We use adjusted_mode, to fixup sync by aligning both rising | ||
83 | * edges and add HSKEW offset to let the slave encoder fix it up. | ||
84 | */ | ||
85 | adjusted_mode->hskew = mode->hsync_end - mode->hsync_start; | ||
86 | adjusted_mode->flags |= DRM_MODE_FLAG_HSKEW; | ||
87 | |||
88 | if (mode->flags & DRM_MODE_FLAG_NHSYNC) { | ||
89 | adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC; | ||
90 | adjusted_mode->flags &= ~DRM_MODE_FLAG_NHSYNC; | ||
91 | } else { | ||
92 | adjusted_mode->flags |= DRM_MODE_FLAG_NHSYNC; | ||
93 | adjusted_mode->flags &= ~DRM_MODE_FLAG_PHSYNC; | ||
94 | } | ||
95 | |||
96 | return drm_i2c_encoder_mode_fixup(encoder, mode, adjusted_mode); | ||
97 | } | ||
98 | |||
99 | |||
100 | static const struct drm_encoder_funcs slave_encoder_funcs = { | ||
101 | .destroy = slave_encoder_destroy, | ||
102 | }; | ||
103 | |||
104 | static const struct drm_encoder_helper_funcs slave_encoder_helper_funcs = { | ||
105 | .dpms = drm_i2c_encoder_dpms, | ||
106 | .mode_fixup = slave_encoder_fixup, | ||
107 | .prepare = slave_encoder_prepare, | ||
108 | .commit = drm_i2c_encoder_commit, | ||
109 | .mode_set = drm_i2c_encoder_mode_set, | ||
110 | .save = drm_i2c_encoder_save, | ||
111 | .restore = drm_i2c_encoder_restore, | ||
112 | }; | ||
113 | |||
114 | static const struct i2c_board_info info = { | ||
115 | I2C_BOARD_INFO("tda998x", 0x70) | ||
116 | }; | ||
117 | |||
118 | static struct drm_encoder *slave_encoder_create(struct drm_device *dev, | ||
119 | struct slave_module *mod) | ||
120 | { | ||
121 | struct slave_encoder *slave_encoder; | ||
122 | struct drm_encoder *encoder; | ||
123 | int ret; | ||
124 | |||
125 | slave_encoder = kzalloc(sizeof(*slave_encoder), GFP_KERNEL); | ||
126 | if (!slave_encoder) { | ||
127 | dev_err(dev->dev, "allocation failed\n"); | ||
128 | return NULL; | ||
129 | } | ||
130 | |||
131 | slave_encoder->mod = mod; | ||
132 | |||
133 | encoder = &slave_encoder->base.base; | ||
134 | encoder->possible_crtcs = 1; | ||
135 | |||
136 | ret = drm_encoder_init(dev, encoder, &slave_encoder_funcs, | ||
137 | DRM_MODE_ENCODER_TMDS); | ||
138 | if (ret) | ||
139 | goto fail; | ||
140 | |||
141 | drm_encoder_helper_add(encoder, &slave_encoder_helper_funcs); | ||
142 | |||
143 | ret = drm_i2c_encoder_init(dev, to_encoder_slave(encoder), mod->i2c, &info); | ||
144 | if (ret) | ||
145 | goto fail; | ||
146 | |||
147 | return encoder; | ||
148 | |||
149 | fail: | ||
150 | slave_encoder_destroy(encoder); | ||
151 | return NULL; | ||
152 | } | ||
153 | |||
154 | /* | ||
155 | * Connector: | ||
156 | */ | ||
157 | |||
158 | struct slave_connector { | ||
159 | struct drm_connector base; | ||
160 | |||
161 | struct drm_encoder *encoder; /* our connected encoder */ | ||
162 | struct slave_module *mod; | ||
163 | }; | ||
164 | #define to_slave_connector(x) container_of(x, struct slave_connector, base) | ||
165 | |||
166 | static void slave_connector_destroy(struct drm_connector *connector) | ||
167 | { | ||
168 | struct slave_connector *slave_connector = to_slave_connector(connector); | ||
169 | drm_connector_unregister(connector); | ||
170 | drm_connector_cleanup(connector); | ||
171 | kfree(slave_connector); | ||
172 | } | ||
173 | |||
174 | static enum drm_connector_status slave_connector_detect( | ||
175 | struct drm_connector *connector, | ||
176 | bool force) | ||
177 | { | ||
178 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
179 | return get_slave_funcs(encoder)->detect(encoder, connector); | ||
180 | } | ||
181 | |||
182 | static int slave_connector_get_modes(struct drm_connector *connector) | ||
183 | { | ||
184 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
185 | return get_slave_funcs(encoder)->get_modes(encoder, connector); | ||
186 | } | ||
187 | |||
188 | static int slave_connector_mode_valid(struct drm_connector *connector, | ||
189 | struct drm_display_mode *mode) | ||
190 | { | ||
191 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
192 | struct tilcdc_drm_private *priv = connector->dev->dev_private; | ||
193 | int ret; | ||
194 | |||
195 | ret = tilcdc_crtc_mode_valid(priv->crtc, mode); | ||
196 | if (ret != MODE_OK) | ||
197 | return ret; | ||
198 | |||
199 | return get_slave_funcs(encoder)->mode_valid(encoder, mode); | ||
200 | } | ||
201 | |||
202 | static struct drm_encoder *slave_connector_best_encoder( | ||
203 | struct drm_connector *connector) | ||
204 | { | ||
205 | struct slave_connector *slave_connector = to_slave_connector(connector); | ||
206 | return slave_connector->encoder; | ||
207 | } | ||
208 | |||
209 | static int slave_connector_set_property(struct drm_connector *connector, | ||
210 | struct drm_property *property, uint64_t value) | ||
211 | { | ||
212 | struct drm_encoder *encoder = to_slave_connector(connector)->encoder; | ||
213 | return get_slave_funcs(encoder)->set_property(encoder, | ||
214 | connector, property, value); | ||
215 | } | ||
216 | |||
217 | static const struct drm_connector_funcs slave_connector_funcs = { | ||
218 | .destroy = slave_connector_destroy, | ||
219 | .dpms = drm_helper_connector_dpms, | ||
220 | .detect = slave_connector_detect, | ||
221 | .fill_modes = drm_helper_probe_single_connector_modes, | ||
222 | .set_property = slave_connector_set_property, | ||
223 | }; | ||
224 | |||
225 | static const struct drm_connector_helper_funcs slave_connector_helper_funcs = { | ||
226 | .get_modes = slave_connector_get_modes, | ||
227 | .mode_valid = slave_connector_mode_valid, | ||
228 | .best_encoder = slave_connector_best_encoder, | ||
229 | }; | ||
230 | |||
231 | static struct drm_connector *slave_connector_create(struct drm_device *dev, | ||
232 | struct slave_module *mod, struct drm_encoder *encoder) | ||
233 | { | ||
234 | struct slave_connector *slave_connector; | ||
235 | struct drm_connector *connector; | ||
236 | int ret; | ||
237 | |||
238 | slave_connector = kzalloc(sizeof(*slave_connector), GFP_KERNEL); | ||
239 | if (!slave_connector) { | ||
240 | dev_err(dev->dev, "allocation failed\n"); | ||
241 | return NULL; | ||
242 | } | ||
243 | |||
244 | slave_connector->encoder = encoder; | ||
245 | slave_connector->mod = mod; | ||
246 | |||
247 | connector = &slave_connector->base; | ||
248 | |||
249 | drm_connector_init(dev, connector, &slave_connector_funcs, | ||
250 | DRM_MODE_CONNECTOR_HDMIA); | ||
251 | drm_connector_helper_add(connector, &slave_connector_helper_funcs); | ||
252 | |||
253 | connector->polled = DRM_CONNECTOR_POLL_CONNECT | | ||
254 | DRM_CONNECTOR_POLL_DISCONNECT; | ||
255 | |||
256 | connector->interlace_allowed = 0; | ||
257 | connector->doublescan_allowed = 0; | ||
258 | |||
259 | get_slave_funcs(encoder)->create_resources(encoder, connector); | ||
260 | |||
261 | ret = drm_mode_connector_attach_encoder(connector, encoder); | ||
262 | if (ret) | ||
263 | goto fail; | ||
264 | |||
265 | drm_connector_register(connector); | ||
266 | |||
267 | return connector; | ||
268 | |||
269 | fail: | ||
270 | slave_connector_destroy(connector); | ||
271 | return NULL; | ||
272 | } | ||
273 | |||
274 | /* | ||
275 | * Module: | ||
276 | */ | ||
277 | |||
278 | static int slave_modeset_init(struct tilcdc_module *mod, struct drm_device *dev) | ||
279 | { | ||
280 | struct slave_module *slave_mod = to_slave_module(mod); | ||
281 | struct tilcdc_drm_private *priv = dev->dev_private; | ||
282 | struct drm_encoder *encoder; | ||
283 | struct drm_connector *connector; | ||
284 | |||
285 | encoder = slave_encoder_create(dev, slave_mod); | ||
286 | if (!encoder) | ||
287 | return -ENOMEM; | ||
288 | |||
289 | connector = slave_connector_create(dev, slave_mod, encoder); | ||
290 | if (!connector) | ||
291 | return -ENOMEM; | ||
292 | |||
293 | priv->encoders[priv->num_encoders++] = encoder; | ||
294 | priv->connectors[priv->num_connectors++] = connector; | ||
295 | |||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | static const struct tilcdc_module_ops slave_module_ops = { | ||
300 | .modeset_init = slave_modeset_init, | ||
301 | }; | ||
302 | |||
303 | /* | ||
304 | * Device: | ||
305 | */ | ||
306 | |||
307 | static struct of_device_id slave_of_match[]; | ||
308 | |||
309 | static int slave_probe(struct platform_device *pdev) | ||
310 | { | ||
311 | struct device_node *node = pdev->dev.of_node; | ||
312 | struct device_node *i2c_node; | ||
313 | struct slave_module *slave_mod; | ||
314 | struct tilcdc_module *mod; | ||
315 | struct pinctrl *pinctrl; | ||
316 | uint32_t i2c_phandle; | ||
317 | struct i2c_adapter *slavei2c; | ||
318 | int ret = -EINVAL; | ||
319 | |||
320 | /* bail out early if no DT data: */ | ||
321 | if (!node) { | ||
322 | dev_err(&pdev->dev, "device-tree data is missing\n"); | ||
323 | return -ENXIO; | ||
324 | } | ||
325 | |||
326 | /* Bail out early if i2c not specified */ | ||
327 | if (of_property_read_u32(node, "i2c", &i2c_phandle)) { | ||
328 | dev_err(&pdev->dev, "could not get i2c bus phandle\n"); | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | i2c_node = of_find_node_by_phandle(i2c_phandle); | ||
333 | if (!i2c_node) { | ||
334 | dev_err(&pdev->dev, "could not get i2c bus node\n"); | ||
335 | return ret; | ||
336 | } | ||
337 | |||
338 | /* but defer the probe if it can't be initialized it might come later */ | ||
339 | slavei2c = of_find_i2c_adapter_by_node(i2c_node); | ||
340 | of_node_put(i2c_node); | ||
341 | |||
342 | if (!slavei2c) { | ||
343 | ret = -EPROBE_DEFER; | ||
344 | tilcdc_slave_probedefer(true); | ||
345 | dev_err(&pdev->dev, "could not get i2c\n"); | ||
346 | return ret; | ||
347 | } | ||
348 | |||
349 | slave_mod = kzalloc(sizeof(*slave_mod), GFP_KERNEL); | ||
350 | if (!slave_mod) { | ||
351 | ret = -ENOMEM; | ||
352 | goto fail_adapter; | ||
353 | } | ||
354 | |||
355 | mod = &slave_mod->base; | ||
356 | pdev->dev.platform_data = mod; | ||
357 | |||
358 | mod->preferred_bpp = slave_info.bpp; | ||
359 | |||
360 | slave_mod->i2c = slavei2c; | ||
361 | |||
362 | tilcdc_module_init(mod, "slave", &slave_module_ops); | ||
363 | |||
364 | pinctrl = devm_pinctrl_get_select_default(&pdev->dev); | ||
365 | if (IS_ERR(pinctrl)) | ||
366 | dev_warn(&pdev->dev, "pins are not configured\n"); | ||
367 | |||
368 | tilcdc_slave_probedefer(false); | ||
369 | |||
370 | return 0; | ||
371 | |||
372 | fail_adapter: | ||
373 | i2c_put_adapter(slavei2c); | ||
374 | return ret; | ||
375 | } | ||
376 | |||
377 | static int slave_remove(struct platform_device *pdev) | ||
378 | { | ||
379 | struct tilcdc_module *mod = dev_get_platdata(&pdev->dev); | ||
380 | struct slave_module *slave_mod = to_slave_module(mod); | ||
381 | |||
382 | tilcdc_module_cleanup(mod); | ||
383 | kfree(slave_mod); | ||
384 | |||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static struct of_device_id slave_of_match[] = { | ||
389 | { .compatible = "ti,tilcdc,slave", }, | ||
390 | { }, | ||
391 | }; | ||
392 | |||
393 | struct platform_driver slave_driver = { | ||
394 | .probe = slave_probe, | ||
395 | .remove = slave_remove, | ||
396 | .driver = { | ||
397 | .owner = THIS_MODULE, | ||
398 | .name = "slave", | ||
399 | .of_match_table = slave_of_match, | ||
400 | }, | ||
401 | }; | ||
402 | |||
403 | int __init tilcdc_slave_init(void) | ||
404 | { | ||
405 | return platform_driver_register(&slave_driver); | ||
406 | } | ||
407 | |||
408 | void __exit tilcdc_slave_fini(void) | ||
409 | { | ||
410 | platform_driver_unregister(&slave_driver); | ||
411 | } | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c new file mode 100644 index 000000000000..106679bca6cb --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Texas Instruments | ||
3 | * Author: Jyri Sarha <jsarha@ti.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 | */ | ||
10 | |||
11 | /* | ||
12 | * To support the old "ti,tilcdc,slave" binding the binding has to be | ||
13 | * transformed to the new external encoder binding. | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/of.h> | ||
18 | #include <linux/of_graph.h> | ||
19 | #include <linux/of_fdt.h> | ||
20 | #include <linux/slab.h> | ||
21 | #include <linux/list.h> | ||
22 | |||
23 | #include "tilcdc_slave_compat.h" | ||
24 | |||
25 | struct kfree_table { | ||
26 | int total; | ||
27 | int num; | ||
28 | void **table; | ||
29 | }; | ||
30 | |||
31 | static int __init kfree_table_init(struct kfree_table *kft) | ||
32 | { | ||
33 | kft->total = 32; | ||
34 | kft->num = 0; | ||
35 | kft->table = kmalloc(kft->total * sizeof(*kft->table), | ||
36 | GFP_KERNEL); | ||
37 | if (!kft->table) | ||
38 | return -ENOMEM; | ||
39 | |||
40 | return 0; | ||
41 | } | ||
42 | |||
43 | static int __init kfree_table_add(struct kfree_table *kft, void *p) | ||
44 | { | ||
45 | if (kft->num == kft->total) { | ||
46 | void **old = kft->table; | ||
47 | |||
48 | kft->total *= 2; | ||
49 | kft->table = krealloc(old, kft->total * sizeof(*kft->table), | ||
50 | GFP_KERNEL); | ||
51 | if (!kft->table) { | ||
52 | kft->table = old; | ||
53 | kfree(p); | ||
54 | return -ENOMEM; | ||
55 | } | ||
56 | } | ||
57 | kft->table[kft->num++] = p; | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static void __init kfree_table_free(struct kfree_table *kft) | ||
62 | { | ||
63 | int i; | ||
64 | |||
65 | for (i = 0; i < kft->num; i++) | ||
66 | kfree(kft->table[i]); | ||
67 | |||
68 | kfree(kft->table); | ||
69 | } | ||
70 | |||
71 | static | ||
72 | struct property * __init tilcdc_prop_dup(const struct property *prop, | ||
73 | struct kfree_table *kft) | ||
74 | { | ||
75 | struct property *nprop; | ||
76 | |||
77 | nprop = kzalloc(sizeof(*nprop), GFP_KERNEL); | ||
78 | if (!nprop || kfree_table_add(kft, nprop)) | ||
79 | return NULL; | ||
80 | |||
81 | nprop->name = kstrdup(prop->name, GFP_KERNEL); | ||
82 | if (!nprop->name || kfree_table_add(kft, nprop->name)) | ||
83 | return NULL; | ||
84 | |||
85 | nprop->value = kmemdup(prop->value, prop->length, GFP_KERNEL); | ||
86 | if (!nprop->value || kfree_table_add(kft, nprop->value)) | ||
87 | return NULL; | ||
88 | |||
89 | nprop->length = prop->length; | ||
90 | |||
91 | return nprop; | ||
92 | } | ||
93 | |||
94 | static void __init tilcdc_copy_props(struct device_node *from, | ||
95 | struct device_node *to, | ||
96 | const char * const props[], | ||
97 | struct kfree_table *kft) | ||
98 | { | ||
99 | struct property *prop; | ||
100 | int i; | ||
101 | |||
102 | for (i = 0; props[i]; i++) { | ||
103 | prop = of_find_property(from, props[i], NULL); | ||
104 | if (!prop) | ||
105 | continue; | ||
106 | |||
107 | prop = tilcdc_prop_dup(prop, kft); | ||
108 | if (!prop) | ||
109 | continue; | ||
110 | |||
111 | prop->next = to->properties; | ||
112 | to->properties = prop; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | static int __init tilcdc_prop_str_update(struct property *prop, | ||
117 | const char *str, | ||
118 | struct kfree_table *kft) | ||
119 | { | ||
120 | prop->value = kstrdup(str, GFP_KERNEL); | ||
121 | if (kfree_table_add(kft, prop->value) || !prop->value) | ||
122 | return -ENOMEM; | ||
123 | prop->length = strlen(str)+1; | ||
124 | return 0; | ||
125 | } | ||
126 | |||
127 | static void __init tilcdc_node_disable(struct device_node *node) | ||
128 | { | ||
129 | struct property *prop; | ||
130 | |||
131 | prop = kzalloc(sizeof(*prop), GFP_KERNEL); | ||
132 | if (!prop) | ||
133 | return; | ||
134 | |||
135 | prop->name = "status"; | ||
136 | prop->value = "disabled"; | ||
137 | prop->length = strlen((char *)prop->value)+1; | ||
138 | |||
139 | of_update_property(node, prop); | ||
140 | } | ||
141 | |||
142 | struct device_node * __init tilcdc_get_overlay(struct kfree_table *kft) | ||
143 | { | ||
144 | const int size = __dtb_tilcdc_slave_compat_end - | ||
145 | __dtb_tilcdc_slave_compat_begin; | ||
146 | static void *overlay_data; | ||
147 | struct device_node *overlay; | ||
148 | int ret; | ||
149 | |||
150 | if (!size) { | ||
151 | pr_warn("%s: No overlay data\n", __func__); | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | overlay_data = kmemdup(__dtb_tilcdc_slave_compat_begin, | ||
156 | size, GFP_KERNEL); | ||
157 | if (!overlay_data || kfree_table_add(kft, overlay_data)) | ||
158 | return NULL; | ||
159 | |||
160 | of_fdt_unflatten_tree(overlay_data, &overlay); | ||
161 | if (!overlay) { | ||
162 | pr_warn("%s: Unfattening overlay tree failed\n", __func__); | ||
163 | return NULL; | ||
164 | } | ||
165 | |||
166 | of_node_set_flag(overlay, OF_DETACHED); | ||
167 | ret = of_resolve_phandles(overlay); | ||
168 | if (ret) { | ||
169 | pr_err("%s: Failed to resolve phandles: %d\n", __func__, ret); | ||
170 | return NULL; | ||
171 | } | ||
172 | |||
173 | return overlay; | ||
174 | } | ||
175 | |||
176 | static const struct of_device_id tilcdc_slave_of_match[] __initconst = { | ||
177 | { .compatible = "ti,tilcdc,slave", }, | ||
178 | {}, | ||
179 | }; | ||
180 | |||
181 | static const struct of_device_id tilcdc_of_match[] __initconst = { | ||
182 | { .compatible = "ti,am33xx-tilcdc", }, | ||
183 | {}, | ||
184 | }; | ||
185 | |||
186 | static const struct of_device_id tilcdc_tda998x_of_match[] __initconst = { | ||
187 | { .compatible = "nxp,tda998x", }, | ||
188 | {}, | ||
189 | }; | ||
190 | |||
191 | static const char * const tilcdc_slave_props[] __initconst = { | ||
192 | "pinctrl-names", | ||
193 | "pinctrl-0", | ||
194 | "pinctrl-1", | ||
195 | NULL | ||
196 | }; | ||
197 | |||
198 | void __init tilcdc_convert_slave_node(void) | ||
199 | { | ||
200 | struct device_node *slave = NULL, *lcdc = NULL; | ||
201 | struct device_node *i2c = NULL, *fragment = NULL; | ||
202 | struct device_node *overlay, *encoder; | ||
203 | struct property *prop; | ||
204 | /* For all memory needed for the overlay tree. This memory can | ||
205 | be freed after the overlay has been applied. */ | ||
206 | struct kfree_table kft; | ||
207 | int ret; | ||
208 | |||
209 | if (kfree_table_init(&kft)) | ||
210 | goto out; | ||
211 | |||
212 | lcdc = of_find_matching_node(NULL, tilcdc_of_match); | ||
213 | slave = of_find_matching_node(NULL, tilcdc_slave_of_match); | ||
214 | |||
215 | if (!slave || !of_device_is_available(lcdc)) | ||
216 | goto out; | ||
217 | |||
218 | i2c = of_parse_phandle(slave, "i2c", 0); | ||
219 | if (!i2c) { | ||
220 | pr_err("%s: Can't find i2c node trough phandle\n", __func__); | ||
221 | goto out; | ||
222 | } | ||
223 | |||
224 | overlay = tilcdc_get_overlay(&kft); | ||
225 | if (!overlay) | ||
226 | goto out; | ||
227 | |||
228 | encoder = of_find_matching_node(overlay, tilcdc_tda998x_of_match); | ||
229 | if (!encoder) { | ||
230 | pr_err("%s: Failed to find tda998x node\n", __func__); | ||
231 | goto out; | ||
232 | } | ||
233 | |||
234 | tilcdc_copy_props(slave, encoder, tilcdc_slave_props, &kft); | ||
235 | |||
236 | for_each_child_of_node(overlay, fragment) { | ||
237 | prop = of_find_property(fragment, "target-path", NULL); | ||
238 | if (!prop) | ||
239 | continue; | ||
240 | if (!strncmp("i2c", (char *)prop->value, prop->length)) | ||
241 | if (tilcdc_prop_str_update(prop, i2c->full_name, &kft)) | ||
242 | goto out; | ||
243 | if (!strncmp("lcdc", (char *)prop->value, prop->length)) | ||
244 | if (tilcdc_prop_str_update(prop, lcdc->full_name, &kft)) | ||
245 | goto out; | ||
246 | } | ||
247 | |||
248 | tilcdc_node_disable(slave); | ||
249 | |||
250 | ret = of_overlay_create(overlay); | ||
251 | if (ret) | ||
252 | pr_err("%s: Creating overlay failed: %d\n", __func__, ret); | ||
253 | else | ||
254 | pr_info("%s: ti,tilcdc,slave node successfully converted\n", | ||
255 | __func__); | ||
256 | out: | ||
257 | kfree_table_free(&kft); | ||
258 | of_node_put(i2c); | ||
259 | of_node_put(slave); | ||
260 | of_node_put(lcdc); | ||
261 | of_node_put(fragment); | ||
262 | } | ||
263 | |||
264 | int __init tilcdc_slave_compat_init(void) | ||
265 | { | ||
266 | tilcdc_convert_slave_node(); | ||
267 | return 0; | ||
268 | } | ||
269 | |||
270 | subsys_initcall(tilcdc_slave_compat_init); | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts new file mode 100644 index 000000000000..693f8b0aea2d --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts | |||
@@ -0,0 +1,72 @@ | |||
1 | /* | ||
2 | * DTS overlay for converting ti,tilcdc,slave binding to new binding. | ||
3 | * | ||
4 | * Copyright (C) 2015 Texas Instruments Inc. | ||
5 | * Author: Jyri Sarha <jsarha@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * version 2 as published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * target-path property values are simple tags that are replaced with | ||
14 | * correct values in tildcdc_slave_compat.c. Some properties are also | ||
15 | * copied over from the ti,tilcdc,slave node. | ||
16 | */ | ||
17 | |||
18 | /dts-v1/; | ||
19 | / { | ||
20 | fragment@0 { | ||
21 | target-path = "i2c"; | ||
22 | __overlay__ { | ||
23 | #address-cells = <1>; | ||
24 | #size-cells = <0>; | ||
25 | tda19988 { | ||
26 | compatible = "nxp,tda998x"; | ||
27 | reg = <0x70>; | ||
28 | status = "okay"; | ||
29 | |||
30 | port { | ||
31 | hdmi_0: endpoint@0 { | ||
32 | remote-endpoint = <&lcd_0>; | ||
33 | }; | ||
34 | }; | ||
35 | }; | ||
36 | }; | ||
37 | }; | ||
38 | |||
39 | fragment@1 { | ||
40 | target-path = "lcdc"; | ||
41 | __overlay__ { | ||
42 | port { | ||
43 | lcd_0: endpoint@0 { | ||
44 | remote-endpoint = <&hdmi_0>; | ||
45 | }; | ||
46 | }; | ||
47 | }; | ||
48 | }; | ||
49 | |||
50 | __local_fixups__ { | ||
51 | fragment@0 { | ||
52 | __overlay__ { | ||
53 | tda19988 { | ||
54 | port { | ||
55 | endpoint@0 { | ||
56 | remote-endpoint = <0>; | ||
57 | }; | ||
58 | }; | ||
59 | }; | ||
60 | }; | ||
61 | }; | ||
62 | fragment@1 { | ||
63 | __overlay__ { | ||
64 | port { | ||
65 | endpoint@0 { | ||
66 | remote-endpoint = <0>; | ||
67 | }; | ||
68 | }; | ||
69 | }; | ||
70 | }; | ||
71 | }; | ||
72 | }; | ||
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h new file mode 100644 index 000000000000..403d35d87d0b --- /dev/null +++ b/drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h | |||
@@ -0,0 +1,25 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015 Texas Instruments | ||
3 | * Author: Jyri Sarha <jsarha@ti.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 | /* This header declares the symbols defined in tilcdc_slave_compat.dts */ | ||
18 | |||
19 | #ifndef __TILCDC_SLAVE_COMPAT_H__ | ||
20 | #define __TILCDC_SLAVE_COMPAT_H__ | ||
21 | |||
22 | extern uint8_t __dtb_tilcdc_slave_compat_begin[]; | ||
23 | extern uint8_t __dtb_tilcdc_slave_compat_end[]; | ||
24 | |||
25 | #endif /* __TILCDC_SLAVE_COMPAT_H__ */ | ||