aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2016-04-29 00:57:51 -0400
committerDave Airlie <airlied@redhat.com>2016-04-29 00:57:51 -0400
commitb89359bdf0f1e95a4c5f92300594ba9dde323fc4 (patch)
tree2d43e69adbfa1c46230190ab463c841832d24d61
parentd3a8f6784a9cb47c344073624491e571ff1616ec (diff)
parent0449eefe2db1038a327db45d5428c196f63c0cb9 (diff)
Merge branch 'for-next' of http://git.agner.ch/git/linux-drm-fsl-dcu into drm-next
This adds very rudimentary TCON (timing controller for raw LCD displays) support to enable the bypass mode in order to use the DCU controller on Freescale/NXP Vybrid SoC's. Additionally the register clock and pixel clock has been separated, but are currently still enabled and disabled pairwise. Other than that, fixes and cleanups accross the driver. * 'for-next' of http://git.agner.ch/git/linux-drm-fsl-dcu: drm/fsl-dcu: increment version and date drm/fsl-dcu: implement lastclose callback drm/fsl-dcu: disable output polling on driver unload drm/fsl-dcu: deallocate fbdev CMA on unload drm/fsl-dcu: use variable name dev for struct drm_device drm/fsl-dcu: handle missing panel gracefully drm/fsl-dcu: detach panel on destroy drm/layerscape: reduce excessive stack usage drm/fsl-dcu: add TCON driver drm/fsl-dcu: use common clock framework for pixel clock divider drm/fsl-dcu: add extra clock for pixel clock drm/fsl-dcu: disable clock on initialization failure and remove
-rw-r--r--Documentation/devicetree/bindings/display/fsl,dcu.txt15
-rw-r--r--Documentation/devicetree/bindings/display/fsl,tcon.txt18
-rw-r--r--MAINTAINERS1
-rw-r--r--drivers/gpu/drm/fsl-dcu/Makefile3
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c7
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c127
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h2
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c38
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.c111
-rw-r--r--drivers/gpu/drm/fsl-dcu/fsl_tcon.h33
10 files changed, 298 insertions, 57 deletions
diff --git a/Documentation/devicetree/bindings/display/fsl,dcu.txt b/Documentation/devicetree/bindings/display/fsl,dcu.txt
index ebf1be9ae393..ae55cde1b69e 100644
--- a/Documentation/devicetree/bindings/display/fsl,dcu.txt
+++ b/Documentation/devicetree/bindings/display/fsl,dcu.txt
@@ -6,17 +6,24 @@ Required properties:
6 * "fsl,vf610-dcu". 6 * "fsl,vf610-dcu".
7 7
8- reg: Address and length of the register set for dcu. 8- reg: Address and length of the register set for dcu.
9- clocks: From common clock binding: handle to dcu clock. 9- clocks: Handle to "dcu" and "pix" clock (in the order below)
10- clock-names: From common clock binding: Shall be "dcu". 10 This can be the same clock (e.g. LS1021a)
11 See ../clocks/clock-bindings.txt for details.
12- clock-names: Should be "dcu" and "pix"
13 See ../clocks/clock-bindings.txt for details.
11- big-endian Boolean property, LS1021A DCU registers are big-endian. 14- big-endian Boolean property, LS1021A DCU registers are big-endian.
12- fsl,panel: The phandle to panel node. 15- fsl,panel: The phandle to panel node.
13 16
17Optional properties:
18- fsl,tcon: The phandle to the timing controller node.
19
14Examples: 20Examples:
15dcu: dcu@2ce0000 { 21dcu: dcu@2ce0000 {
16 compatible = "fsl,ls1021a-dcu"; 22 compatible = "fsl,ls1021a-dcu";
17 reg = <0x0 0x2ce0000 0x0 0x10000>; 23 reg = <0x0 0x2ce0000 0x0 0x10000>;
18 clocks = <&platform_clk 0>; 24 clocks = <&platform_clk 0>, <&platform_clk 0>;
19 clock-names = "dcu"; 25 clock-names = "dcu", "pix";
20 big-endian; 26 big-endian;
21 fsl,panel = <&panel>; 27 fsl,panel = <&panel>;
28 fsl,tcon = <&tcon>;
22}; 29};
diff --git a/Documentation/devicetree/bindings/display/fsl,tcon.txt b/Documentation/devicetree/bindings/display/fsl,tcon.txt
new file mode 100644
index 000000000000..6fa4ab668db5
--- /dev/null
+++ b/Documentation/devicetree/bindings/display/fsl,tcon.txt
@@ -0,0 +1,18 @@
1Device Tree bindings for Freescale TCON Driver
2
3Required properties:
4- compatible: Should be one of
5 * "fsl,vf610-tcon".
6
7- reg: Address and length of the register set for tcon.
8- clocks: From common clock binding: handle to tcon ipg clock.
9- clock-names: From common clock binding: Shall be "ipg".
10
11Examples:
12timing-controller@4003d000 {
13 compatible = "fsl,vf610-tcon";
14 reg = <0x4003d000 0x1000>;
15 clocks = <&clks VF610_CLK_TCON0>;
16 clock-names = "ipg";
17 status = "okay";
18};
diff --git a/MAINTAINERS b/MAINTAINERS
index 4c38e9e7ef62..9ef7b408beb3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3835,6 +3835,7 @@ L: dri-devel@lists.freedesktop.org
3835S: Supported 3835S: Supported
3836F: drivers/gpu/drm/fsl-dcu/ 3836F: drivers/gpu/drm/fsl-dcu/
3837F: Documentation/devicetree/bindings/display/fsl,dcu.txt 3837F: Documentation/devicetree/bindings/display/fsl,dcu.txt
3838F: Documentation/devicetree/bindings/display/fsl,tcon.txt
3838F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt 3839F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt
3839 3840
3840DRM DRIVERS FOR FREESCALE IMX 3841DRM DRIVERS FOR FREESCALE IMX
diff --git a/drivers/gpu/drm/fsl-dcu/Makefile b/drivers/gpu/drm/fsl-dcu/Makefile
index 6ea1523ae6ec..b35a292287f3 100644
--- a/drivers/gpu/drm/fsl-dcu/Makefile
+++ b/drivers/gpu/drm/fsl-dcu/Makefile
@@ -3,5 +3,6 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
3 fsl_dcu_drm_rgb.o \ 3 fsl_dcu_drm_rgb.o \
4 fsl_dcu_drm_plane.o \ 4 fsl_dcu_drm_plane.o \
5 fsl_dcu_drm_crtc.o \ 5 fsl_dcu_drm_crtc.o \
6 fsl_dcu_drm_fbdev.o 6 fsl_dcu_drm_fbdev.o \
7 fsl_tcon.o
7obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o 8obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
index 4ed7798533f9..365809edf29a 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
@@ -67,12 +67,10 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
67 struct drm_device *dev = crtc->dev; 67 struct drm_device *dev = crtc->dev;
68 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 68 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
69 struct drm_display_mode *mode = &crtc->state->mode; 69 struct drm_display_mode *mode = &crtc->state->mode;
70 unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index, pol = 0; 70 unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;
71 unsigned long dcuclk;
72 71
73 index = drm_crtc_index(crtc); 72 index = drm_crtc_index(crtc);
74 dcuclk = clk_get_rate(fsl_dev->clk); 73 clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);
75 div = dcuclk / mode->clock / 1000;
76 74
77 /* Configure timings: */ 75 /* Configure timings: */
78 hbp = mode->htotal - mode->hsync_end; 76 hbp = mode->htotal - mode->hsync_end;
@@ -99,7 +97,6 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
99 regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, 97 regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
100 DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | 98 DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
101 DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); 99 DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
102 regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div);
103 regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); 100 regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
104 regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | 101 regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
105 DCU_BGND_G(0) | DCU_BGND_B(0)); 102 DCU_BGND_G(0) | DCU_BGND_B(0));
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
index e8d9337a66d8..44f6f262d75a 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
@@ -23,10 +23,12 @@
23 23
24#include <drm/drmP.h> 24#include <drm/drmP.h>
25#include <drm/drm_crtc_helper.h> 25#include <drm/drm_crtc_helper.h>
26#include <drm/drm_fb_cma_helper.h>
26#include <drm/drm_gem_cma_helper.h> 27#include <drm/drm_gem_cma_helper.h>
27 28
28#include "fsl_dcu_drm_crtc.h" 29#include "fsl_dcu_drm_crtc.h"
29#include "fsl_dcu_drm_drv.h" 30#include "fsl_dcu_drm_drv.h"
31#include "fsl_tcon.h"
30 32
31static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg) 33static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
32{ 34{
@@ -62,46 +64,55 @@ static int fsl_dcu_drm_irq_init(struct drm_device *dev)
62 return ret; 64 return ret;
63} 65}
64 66
65static int fsl_dcu_load(struct drm_device *drm, unsigned long flags) 67static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
66{ 68{
67 struct device *dev = drm->dev; 69 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
68 struct fsl_dcu_drm_device *fsl_dev = drm->dev_private;
69 int ret; 70 int ret;
70 71
71 ret = fsl_dcu_drm_modeset_init(fsl_dev); 72 ret = fsl_dcu_drm_modeset_init(fsl_dev);
72 if (ret < 0) { 73 if (ret < 0) {
73 dev_err(dev, "failed to initialize mode setting\n"); 74 dev_err(dev->dev, "failed to initialize mode setting\n");
74 return ret; 75 return ret;
75 } 76 }
76 77
77 ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 78 ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
78 if (ret < 0) { 79 if (ret < 0) {
79 dev_err(dev, "failed to initialize vblank\n"); 80 dev_err(dev->dev, "failed to initialize vblank\n");
80 goto done; 81 goto done;
81 } 82 }
82 drm->vblank_disable_allowed = true; 83 dev->vblank_disable_allowed = true;
83 84
84 ret = fsl_dcu_drm_irq_init(drm); 85 ret = fsl_dcu_drm_irq_init(dev);
85 if (ret < 0) 86 if (ret < 0)
86 goto done; 87 goto done;
87 drm->irq_enabled = true; 88 dev->irq_enabled = true;
88 89
89 fsl_dcu_fbdev_init(drm); 90 fsl_dcu_fbdev_init(dev);
90 91
91 return 0; 92 return 0;
92done: 93done:
93 if (ret) { 94 drm_kms_helper_poll_fini(dev);
94 drm_mode_config_cleanup(drm); 95
95 drm_vblank_cleanup(drm); 96 if (fsl_dev->fbdev)
96 drm_irq_uninstall(drm); 97 drm_fbdev_cma_fini(fsl_dev->fbdev);
97 drm->dev_private = NULL; 98
98 } 99 drm_mode_config_cleanup(dev);
100 drm_vblank_cleanup(dev);
101 drm_irq_uninstall(dev);
102 dev->dev_private = NULL;
99 103
100 return ret; 104 return ret;
101} 105}
102 106
103static int fsl_dcu_unload(struct drm_device *dev) 107static int fsl_dcu_unload(struct drm_device *dev)
104{ 108{
109 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
110
111 drm_kms_helper_poll_fini(dev);
112
113 if (fsl_dev->fbdev)
114 drm_fbdev_cma_fini(fsl_dev->fbdev);
115
105 drm_mode_config_cleanup(dev); 116 drm_mode_config_cleanup(dev);
106 drm_vblank_cleanup(dev); 117 drm_vblank_cleanup(dev);
107 drm_irq_uninstall(dev); 118 drm_irq_uninstall(dev);
@@ -157,6 +168,13 @@ static void fsl_dcu_drm_disable_vblank(struct drm_device *dev,
157 regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); 168 regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
158} 169}
159 170
171static void fsl_dcu_drm_lastclose(struct drm_device *dev)
172{
173 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
174
175 drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
176}
177
160static const struct file_operations fsl_dcu_drm_fops = { 178static const struct file_operations fsl_dcu_drm_fops = {
161 .owner = THIS_MODULE, 179 .owner = THIS_MODULE,
162 .open = drm_open, 180 .open = drm_open,
@@ -174,6 +192,7 @@ static const struct file_operations fsl_dcu_drm_fops = {
174static struct drm_driver fsl_dcu_drm_driver = { 192static struct drm_driver fsl_dcu_drm_driver = {
175 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET 193 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
176 | DRIVER_PRIME | DRIVER_ATOMIC, 194 | DRIVER_PRIME | DRIVER_ATOMIC,
195 .lastclose = fsl_dcu_drm_lastclose,
177 .load = fsl_dcu_load, 196 .load = fsl_dcu_load,
178 .unload = fsl_dcu_unload, 197 .unload = fsl_dcu_unload,
179 .irq_handler = fsl_dcu_drm_irq, 198 .irq_handler = fsl_dcu_drm_irq,
@@ -197,9 +216,9 @@ static struct drm_driver fsl_dcu_drm_driver = {
197 .fops = &fsl_dcu_drm_fops, 216 .fops = &fsl_dcu_drm_fops,
198 .name = "fsl-dcu-drm", 217 .name = "fsl-dcu-drm",
199 .desc = "Freescale DCU DRM", 218 .desc = "Freescale DCU DRM",
200 .date = "20150213", 219 .date = "20160425",
201 .major = 1, 220 .major = 1,
202 .minor = 0, 221 .minor = 1,
203}; 222};
204 223
205#ifdef CONFIG_PM_SLEEP 224#ifdef CONFIG_PM_SLEEP
@@ -283,6 +302,9 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
283 struct resource *res; 302 struct resource *res;
284 void __iomem *base; 303 void __iomem *base;
285 struct drm_driver *driver = &fsl_dcu_drm_driver; 304 struct drm_driver *driver = &fsl_dcu_drm_driver;
305 struct clk *pix_clk_in;
306 char pix_clk_name[32];
307 const char *pix_clk_in_name;
286 const struct of_device_id *id; 308 const struct of_device_id *id;
287 int ret; 309 int ret;
288 310
@@ -290,6 +312,11 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
290 if (!fsl_dev) 312 if (!fsl_dev)
291 return -ENOMEM; 313 return -ENOMEM;
292 314
315 id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
316 if (!id)
317 return -ENODEV;
318 fsl_dev->soc = id->data;
319
293 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 320 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
294 if (!res) { 321 if (!res) {
295 dev_err(dev, "could not get memory IO resource\n"); 322 dev_err(dev, "could not get memory IO resource\n");
@@ -308,39 +335,54 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
308 return -ENXIO; 335 return -ENXIO;
309 } 336 }
310 337
338 fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
339 &fsl_dcu_regmap_config);
340 if (IS_ERR(fsl_dev->regmap)) {
341 dev_err(dev, "regmap init failed\n");
342 return PTR_ERR(fsl_dev->regmap);
343 }
344
311 fsl_dev->clk = devm_clk_get(dev, "dcu"); 345 fsl_dev->clk = devm_clk_get(dev, "dcu");
312 if (IS_ERR(fsl_dev->clk)) { 346 if (IS_ERR(fsl_dev->clk)) {
313 ret = PTR_ERR(fsl_dev->clk);
314 dev_err(dev, "failed to get dcu clock\n"); 347 dev_err(dev, "failed to get dcu clock\n");
315 return ret; 348 return PTR_ERR(fsl_dev->clk);
316 }
317 ret = clk_prepare(fsl_dev->clk);
318 if (ret < 0) {
319 dev_err(dev, "failed to prepare dcu clk\n");
320 return ret;
321 } 349 }
322 ret = clk_enable(fsl_dev->clk); 350 ret = clk_prepare_enable(fsl_dev->clk);
323 if (ret < 0) { 351 if (ret < 0) {
324 dev_err(dev, "failed to enable dcu clk\n"); 352 dev_err(dev, "failed to enable dcu clk\n");
325 clk_unprepare(fsl_dev->clk);
326 return ret; 353 return ret;
327 } 354 }
328 355
329 fsl_dev->regmap = devm_regmap_init_mmio(dev, base, 356 pix_clk_in = devm_clk_get(dev, "pix");
330 &fsl_dcu_regmap_config); 357 if (IS_ERR(pix_clk_in)) {
331 if (IS_ERR(fsl_dev->regmap)) { 358 /* legancy binding, use dcu clock as pixel clock input */
332 dev_err(dev, "regmap init failed\n"); 359 pix_clk_in = fsl_dev->clk;
333 return PTR_ERR(fsl_dev->regmap);
334 } 360 }
335 361
336 id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node); 362 pix_clk_in_name = __clk_get_name(pix_clk_in);
337 if (!id) 363 snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
338 return -ENODEV; 364 fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
339 fsl_dev->soc = id->data; 365 pix_clk_in_name, 0, base + DCU_DIV_RATIO,
366 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
367 if (IS_ERR(fsl_dev->pix_clk)) {
368 dev_err(dev, "failed to register pix clk\n");
369 ret = PTR_ERR(fsl_dev->pix_clk);
370 goto disable_clk;
371 }
372
373 ret = clk_prepare_enable(fsl_dev->pix_clk);
374 if (ret < 0) {
375 dev_err(dev, "failed to enable pix clk\n");
376 goto unregister_pix_clk;
377 }
378
379 fsl_dev->tcon = fsl_tcon_init(dev);
340 380
341 drm = drm_dev_alloc(driver, dev); 381 drm = drm_dev_alloc(driver, dev);
342 if (!drm) 382 if (!drm) {
343 return -ENOMEM; 383 ret = -ENOMEM;
384 goto disable_pix_clk;
385 }
344 386
345 fsl_dev->dev = dev; 387 fsl_dev->dev = dev;
346 fsl_dev->drm = drm; 388 fsl_dev->drm = drm;
@@ -360,6 +402,12 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
360 402
361unref: 403unref:
362 drm_dev_unref(drm); 404 drm_dev_unref(drm);
405disable_pix_clk:
406 clk_disable_unprepare(fsl_dev->pix_clk);
407unregister_pix_clk:
408 clk_unregister(fsl_dev->pix_clk);
409disable_clk:
410 clk_disable_unprepare(fsl_dev->clk);
363 return ret; 411 return ret;
364} 412}
365 413
@@ -367,6 +415,9 @@ static int fsl_dcu_drm_remove(struct platform_device *pdev)
367{ 415{
368 struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); 416 struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);
369 417
418 clk_disable_unprepare(fsl_dev->clk);
419 clk_disable_unprepare(fsl_dev->pix_clk);
420 clk_unregister(fsl_dev->pix_clk);
370 drm_put_dev(fsl_dev->drm); 421 drm_put_dev(fsl_dev->drm);
371 422
372 return 0; 423 return 0;
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
index 6413ac9e4769..5bb7c261fe95 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
@@ -183,6 +183,8 @@ struct fsl_dcu_drm_device {
183 struct regmap *regmap; 183 struct regmap *regmap;
184 int irq; 184 int irq;
185 struct clk *clk; 185 struct clk *clk;
186 struct clk *pix_clk;
187 struct fsl_tcon *tcon;
186 /*protects hardware register*/ 188 /*protects hardware register*/
187 spinlock_t irq_lock; 189 spinlock_t irq_lock;
188 struct drm_device *drm; 190 struct drm_device *drm;
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
index 8780deba5e8a..98c998da91eb 100644
--- a/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
+++ b/drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
@@ -17,6 +17,7 @@
17#include <drm/drm_panel.h> 17#include <drm/drm_panel.h>
18 18
19#include "fsl_dcu_drm_drv.h" 19#include "fsl_dcu_drm_drv.h"
20#include "fsl_tcon.h"
20 21
21static int 22static int
22fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder, 23fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
@@ -28,10 +29,20 @@ fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder,
28 29
29static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder) 30static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder)
30{ 31{
32 struct drm_device *dev = encoder->dev;
33 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
34
35 if (fsl_dev->tcon)
36 fsl_tcon_bypass_disable(fsl_dev->tcon);
31} 37}
32 38
33static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder) 39static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder)
34{ 40{
41 struct drm_device *dev = encoder->dev;
42 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
43
44 if (fsl_dev->tcon)
45 fsl_tcon_bypass_enable(fsl_dev->tcon);
35} 46}
36 47
37static const struct drm_encoder_helper_funcs encoder_helper_funcs = { 48static const struct drm_encoder_helper_funcs encoder_helper_funcs = {
@@ -68,7 +79,10 @@ int fsl_dcu_drm_encoder_create(struct fsl_dcu_drm_device *fsl_dev,
68 79
69static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector) 80static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector)
70{ 81{
82 struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector);
83
71 drm_connector_unregister(connector); 84 drm_connector_unregister(connector);
85 drm_panel_detach(fsl_con->panel);
72 drm_connector_cleanup(connector); 86 drm_connector_cleanup(connector);
73} 87}
74 88
@@ -131,7 +145,7 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev,
131 struct drm_encoder *encoder) 145 struct drm_encoder *encoder)
132{ 146{
133 struct drm_connector *connector = &fsl_dev->connector.base; 147 struct drm_connector *connector = &fsl_dev->connector.base;
134 struct drm_mode_config mode_config = fsl_dev->drm->mode_config; 148 struct drm_mode_config *mode_config = &fsl_dev->drm->mode_config;
135 struct device_node *panel_node; 149 struct device_node *panel_node;
136 int ret; 150 int ret;
137 151
@@ -153,19 +167,23 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev,
153 goto err_sysfs; 167 goto err_sysfs;
154 168
155 drm_object_property_set_value(&connector->base, 169 drm_object_property_set_value(&connector->base,
156 mode_config.dpms_property, 170 mode_config->dpms_property,
157 DRM_MODE_DPMS_OFF); 171 DRM_MODE_DPMS_OFF);
158 172
159 panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0); 173 panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0);
160 if (panel_node) { 174 if (!panel_node) {
161 fsl_dev->connector.panel = of_drm_find_panel(panel_node); 175 dev_err(fsl_dev->dev, "fsl,panel property not found\n");
162 if (!fsl_dev->connector.panel) { 176 ret = -ENODEV;
163 ret = -EPROBE_DEFER; 177 goto err_sysfs;
164 goto err_sysfs;
165 }
166 of_node_put(panel_node);
167 } 178 }
168 179
180 fsl_dev->connector.panel = of_drm_find_panel(panel_node);
181 if (!fsl_dev->connector.panel) {
182 ret = -EPROBE_DEFER;
183 goto err_panel;
184 }
185 of_node_put(panel_node);
186
169 ret = drm_panel_attach(fsl_dev->connector.panel, connector); 187 ret = drm_panel_attach(fsl_dev->connector.panel, connector);
170 if (ret) { 188 if (ret) {
171 dev_err(fsl_dev->dev, "failed to attach panel\n"); 189 dev_err(fsl_dev->dev, "failed to attach panel\n");
@@ -174,6 +192,8 @@ int fsl_dcu_drm_connector_create(struct fsl_dcu_drm_device *fsl_dev,
174 192
175 return 0; 193 return 0;
176 194
195err_panel:
196 of_node_put(panel_node);
177err_sysfs: 197err_sysfs:
178 drm_connector_unregister(connector); 198 drm_connector_unregister(connector);
179err_cleanup: 199err_cleanup:
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.c b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
new file mode 100644
index 000000000000..bbe34f1c0505
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.c
@@ -0,0 +1,111 @@
1/*
2 * Copyright 2015 Toradex AG
3 *
4 * Stefan Agner <stefan@agner.ch>
5 *
6 * Freescale TCON device driver
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#include <linux/clk.h>
15#include <linux/io.h>
16#include <linux/mm.h>
17#include <linux/of_address.h>
18#include <linux/platform_device.h>
19#include <linux/regmap.h>
20
21#include "fsl_tcon.h"
22
23void fsl_tcon_bypass_disable(struct fsl_tcon *tcon)
24{
25 regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
26 FSL_TCON_CTRL1_TCON_BYPASS, 0);
27}
28
29void fsl_tcon_bypass_enable(struct fsl_tcon *tcon)
30{
31 regmap_update_bits(tcon->regs, FSL_TCON_CTRL1,
32 FSL_TCON_CTRL1_TCON_BYPASS,
33 FSL_TCON_CTRL1_TCON_BYPASS);
34}
35
36static struct regmap_config fsl_tcon_regmap_config = {
37 .reg_bits = 32,
38 .reg_stride = 4,
39 .val_bits = 32,
40
41 .name = "tcon",
42};
43
44static int fsl_tcon_init_regmap(struct device *dev,
45 struct fsl_tcon *tcon,
46 struct device_node *np)
47{
48 struct resource res;
49 void __iomem *regs;
50
51 if (of_address_to_resource(np, 0, &res))
52 return -EINVAL;
53
54 regs = devm_ioremap_resource(dev, &res);
55 if (IS_ERR(regs))
56 return PTR_ERR(regs);
57
58 tcon->regs = devm_regmap_init_mmio(dev, regs,
59 &fsl_tcon_regmap_config);
60 if (IS_ERR(tcon->regs))
61 return PTR_ERR(tcon->regs);
62
63 return 0;
64}
65
66struct fsl_tcon *fsl_tcon_init(struct device *dev)
67{
68 struct fsl_tcon *tcon;
69 struct device_node *np;
70 int ret;
71
72 /* TCON node is not mandatory, some devices do not provide TCON */
73 np = of_parse_phandle(dev->of_node, "fsl,tcon", 0);
74 if (!np)
75 return NULL;
76
77 tcon = devm_kzalloc(dev, sizeof(*tcon), GFP_KERNEL);
78 if (!tcon) {
79 ret = -ENOMEM;
80 goto err_node_put;
81 }
82
83 ret = fsl_tcon_init_regmap(dev, tcon, np);
84 if (ret) {
85 dev_err(dev, "Couldn't create the TCON regmap\n");
86 goto err_node_put;
87 }
88
89 tcon->ipg_clk = of_clk_get_by_name(np, "ipg");
90 if (IS_ERR(tcon->ipg_clk)) {
91 dev_err(dev, "Couldn't get the TCON bus clock\n");
92 goto err_node_put;
93 }
94
95 clk_prepare_enable(tcon->ipg_clk);
96
97 dev_info(dev, "Using TCON in bypass mode\n");
98
99 return tcon;
100
101err_node_put:
102 of_node_put(np);
103 return NULL;
104}
105
106void fsl_tcon_free(struct fsl_tcon *tcon)
107{
108 clk_disable_unprepare(tcon->ipg_clk);
109 clk_put(tcon->ipg_clk);
110}
111
diff --git a/drivers/gpu/drm/fsl-dcu/fsl_tcon.h b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
new file mode 100644
index 000000000000..80a7617de58f
--- /dev/null
+++ b/drivers/gpu/drm/fsl-dcu/fsl_tcon.h
@@ -0,0 +1,33 @@
1/*
2 * Copyright 2015 Toradex AG
3 *
4 * Stefan Agner <stefan@agner.ch>
5 *
6 * Freescale TCON device driver
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 */
13
14#ifndef __FSL_TCON_H__
15#define __FSL_TCON_H__
16
17#include <linux/bitops.h>
18
19#define FSL_TCON_CTRL1 0x0
20#define FSL_TCON_CTRL1_TCON_BYPASS BIT(29)
21
22struct fsl_tcon {
23 struct regmap *regs;
24 struct clk *ipg_clk;
25};
26
27struct fsl_tcon *fsl_tcon_init(struct device *dev);
28void fsl_tcon_free(struct fsl_tcon *tcon);
29
30void fsl_tcon_bypass_disable(struct fsl_tcon *tcon);
31void fsl_tcon_bypass_enable(struct fsl_tcon *tcon);
32
33#endif /* __FSL_TCON_H__ */