aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/tilcdc
diff options
context:
space:
mode:
authorJiri Kosina <jkosina@suse.cz>2015-09-01 09:35:24 -0400
committerJiri Kosina <jkosina@suse.cz>2015-09-01 09:35:24 -0400
commit067e2601d3c076abbf45db91261f9065eaa879b2 (patch)
tree86c8d4b913873dbd3b4ff23562a3a8597984b4df /drivers/gpu/drm/tilcdc
parent3e097d1271ecdff2f251a54ddfc5eaa1f9821e96 (diff)
parent931830aa5c251e0803523213428f777a48bde254 (diff)
Merge branch 'for-4.3/gembird' into for-linus
Diffstat (limited to 'drivers/gpu/drm/tilcdc')
-rw-r--r--drivers/gpu/drm/tilcdc/Kconfig12
-rw-r--r--drivers/gpu/drm/tilcdc/Makefile5
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_crtc.c36
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.c99
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_drv.h6
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_external.c166
-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.c411
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_slave_compat.c270
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_slave_compat.dts72
-rw-r--r--drivers/gpu/drm/tilcdc/tilcdc_slave_compat.h25
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
16config 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
4endif 4endif
5 5
6obj-$(CONFIG_DRM_TILCDC_SLAVE_COMPAT) += tilcdc_slave_compat.o \
7 tilcdc_slave_compat.dtb.o
8
6tilcdc-y := \ 9tilcdc-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
13obj-$(CONFIG_DRM_TILCDC) += tilcdc.o 16obj-$(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
141static void tilcdc_crtc_dpms(struct drm_crtc *crtc, int mode);
138static void tilcdc_crtc_destroy(struct drm_crtc *crtc) 142static 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
562void 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
536void tilcdc_crtc_update_clk(struct drm_crtc *crtc) 570void 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
28static LIST_HEAD(module_list); 30static LIST_HEAD(module_list);
29static bool slave_probing;
30 31
31void tilcdc_module_init(struct tilcdc_module *mod, const char *name, 32void 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
45void tilcdc_slave_probedefer(bool defered)
46{
47 slave_probing = defered;
48}
49
50static struct of_device_id tilcdc_of_match[]; 46static struct of_device_id tilcdc_of_match[];
51 47
52static struct drm_framebuffer *tilcdc_fb_create(struct drm_device *dev, 48static 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:
307fail_mode_config_cleanup: 317fail_mode_config_cleanup:
308 drm_mode_config_cleanup(dev); 318 drm_mode_config_cleanup(dev);
309 319
320fail_component_cleanup:
321 if (priv->is_componentized)
322 component_unbind_all(dev->dev, dev);
323
324fail_external_cleanup:
325 tilcdc_remove_external_encoders(dev);
326
310fail_cpufreq_unregister: 327fail_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
632static int tilcdc_bind(struct device *dev)
633{
634 return drm_platform_init(&tilcdc_driver, to_platform_device(dev));
635}
636
637static void tilcdc_unbind(struct device *dev)
638{
639 drm_put_dev(dev_get_drvdata(dev));
640}
641
642static const struct component_master_ops tilcdc_comp_ops = {
643 .bind = tilcdc_bind,
644 .unbind = tilcdc_unbind,
645};
646
615static int tilcdc_pdev_probe(struct platform_device *pdev) 647static 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
630static int tilcdc_pdev_remove(struct platform_device *pdev) 669static 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 {
116void tilcdc_module_init(struct tilcdc_module *mod, const char *name, 119void tilcdc_module_init(struct tilcdc_module *mod, const char *name,
117 const struct tilcdc_module_ops *funcs); 120 const struct tilcdc_module_ops *funcs);
118void tilcdc_module_cleanup(struct tilcdc_module *mod); 121void tilcdc_module_cleanup(struct tilcdc_module *mod);
119void 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);
166void tilcdc_crtc_update_clk(struct drm_crtc *crtc); 168void tilcdc_crtc_update_clk(struct drm_crtc *crtc);
167void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc, 169void tilcdc_crtc_set_panel_info(struct drm_crtc *crtc,
168 const struct tilcdc_panel_info *info); 170 const struct tilcdc_panel_info *info);
171void tilcdc_crtc_set_simulate_vesa_sync(struct drm_crtc *crtc,
172 bool simulate_vesa_sync);
169int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode); 173int tilcdc_crtc_mode_valid(struct drm_crtc *crtc, struct drm_display_mode *mode);
170int tilcdc_crtc_max_width(struct drm_crtc *crtc); 174int 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
17static 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
30static 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
55static 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
97int 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
119void 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
133static int dev_match_of(struct device *dev, void *data)
134{
135 return dev->of_node == data;
136}
137
138int 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
23int tilcdc_slave_init(void);
24void tilcdc_slave_fini(void);
25 20
21int tilcdc_add_external_encoders(struct drm_device *dev, int *bpp);
22void tilcdc_remove_external_encoders(struct drm_device *dev);
23int 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
25struct 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
31static 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
48struct 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
54static inline struct drm_encoder_slave_funcs *
55get_slave_funcs(struct drm_encoder *enc)
56{
57 return to_encoder_slave(enc)->slave_funcs;
58}
59
60static 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
69static 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
75static 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
100static const struct drm_encoder_funcs slave_encoder_funcs = {
101 .destroy = slave_encoder_destroy,
102};
103
104static 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
114static const struct i2c_board_info info = {
115 I2C_BOARD_INFO("tda998x", 0x70)
116};
117
118static 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
149fail:
150 slave_encoder_destroy(encoder);
151 return NULL;
152}
153
154/*
155 * Connector:
156 */
157
158struct 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
166static 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
174static 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
182static 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
188static 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
202static 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
209static 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
217static 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
225static 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
231static 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
269fail:
270 slave_connector_destroy(connector);
271 return NULL;
272}
273
274/*
275 * Module:
276 */
277
278static 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
299static const struct tilcdc_module_ops slave_module_ops = {
300 .modeset_init = slave_modeset_init,
301};
302
303/*
304 * Device:
305 */
306
307static struct of_device_id slave_of_match[];
308
309static 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
372fail_adapter:
373 i2c_put_adapter(slavei2c);
374 return ret;
375}
376
377static 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
388static struct of_device_id slave_of_match[] = {
389 { .compatible = "ti,tilcdc,slave", },
390 { },
391};
392
393struct 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
403int __init tilcdc_slave_init(void)
404{
405 return platform_driver_register(&slave_driver);
406}
407
408void __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
25struct kfree_table {
26 int total;
27 int num;
28 void **table;
29};
30
31static 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
43static 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
61static 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
71static
72struct 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
94static 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
116static 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
127static 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
142struct 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
176static const struct of_device_id tilcdc_slave_of_match[] __initconst = {
177 { .compatible = "ti,tilcdc,slave", },
178 {},
179};
180
181static const struct of_device_id tilcdc_of_match[] __initconst = {
182 { .compatible = "ti,am33xx-tilcdc", },
183 {},
184};
185
186static const struct of_device_id tilcdc_tda998x_of_match[] __initconst = {
187 { .compatible = "nxp,tda998x", },
188 {},
189};
190
191static const char * const tilcdc_slave_props[] __initconst = {
192 "pinctrl-names",
193 "pinctrl-0",
194 "pinctrl-1",
195 NULL
196};
197
198void __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__);
256out:
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
264int __init tilcdc_slave_compat_init(void)
265{
266 tilcdc_convert_slave_node();
267 return 0;
268}
269
270subsys_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
22extern uint8_t __dtb_tilcdc_slave_compat_begin[];
23extern uint8_t __dtb_tilcdc_slave_compat_end[];
24
25#endif /* __TILCDC_SLAVE_COMPAT_H__ */