diff options
author | Dave Airlie <airlied@redhat.com> | 2016-04-29 00:57:51 -0400 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2016-04-29 00:57:51 -0400 |
commit | b89359bdf0f1e95a4c5f92300594ba9dde323fc4 (patch) | |
tree | 2d43e69adbfa1c46230190ab463c841832d24d61 | |
parent | d3a8f6784a9cb47c344073624491e571ff1616ec (diff) | |
parent | 0449eefe2db1038a327db45d5428c196f63c0cb9 (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.txt | 15 | ||||
-rw-r--r-- | Documentation/devicetree/bindings/display/fsl,tcon.txt | 18 | ||||
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c | 7 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c | 127 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c | 38 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/fsl_tcon.c | 111 | ||||
-rw-r--r-- | drivers/gpu/drm/fsl-dcu/fsl_tcon.h | 33 |
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 | ||
17 | Optional properties: | ||
18 | - fsl,tcon: The phandle to the timing controller node. | ||
19 | |||
14 | Examples: | 20 | Examples: |
15 | dcu: dcu@2ce0000 { | 21 | dcu: 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 @@ | |||
1 | Device Tree bindings for Freescale TCON Driver | ||
2 | |||
3 | Required 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 | |||
11 | Examples: | ||
12 | timing-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 | |||
3835 | S: Supported | 3835 | S: Supported |
3836 | F: drivers/gpu/drm/fsl-dcu/ | 3836 | F: drivers/gpu/drm/fsl-dcu/ |
3837 | F: Documentation/devicetree/bindings/display/fsl,dcu.txt | 3837 | F: Documentation/devicetree/bindings/display/fsl,dcu.txt |
3838 | F: Documentation/devicetree/bindings/display/fsl,tcon.txt | ||
3838 | F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt | 3839 | F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt |
3839 | 3840 | ||
3840 | DRM DRIVERS FOR FREESCALE IMX | 3841 | DRM 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 | ||
7 | obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o | 8 | obj-$(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 | ||
31 | static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg) | 33 | static 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 | ||
65 | static int fsl_dcu_load(struct drm_device *drm, unsigned long flags) | 67 | static 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; |
92 | done: | 93 | done: |
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 | ||
103 | static int fsl_dcu_unload(struct drm_device *dev) | 107 | static 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 | ||
171 | static 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 | |||
160 | static const struct file_operations fsl_dcu_drm_fops = { | 178 | static 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 = { | |||
174 | static struct drm_driver fsl_dcu_drm_driver = { | 192 | static 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 | ||
361 | unref: | 403 | unref: |
362 | drm_dev_unref(drm); | 404 | drm_dev_unref(drm); |
405 | disable_pix_clk: | ||
406 | clk_disable_unprepare(fsl_dev->pix_clk); | ||
407 | unregister_pix_clk: | ||
408 | clk_unregister(fsl_dev->pix_clk); | ||
409 | disable_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 | ||
21 | static int | 22 | static int |
22 | fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder, | 23 | fsl_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 | ||
29 | static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder) | 30 | static 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 | ||
33 | static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder) | 39 | static 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 | ||
37 | static const struct drm_encoder_helper_funcs encoder_helper_funcs = { | 48 | static 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 | ||
69 | static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector) | 80 | static 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 | ||
195 | err_panel: | ||
196 | of_node_put(panel_node); | ||
177 | err_sysfs: | 197 | err_sysfs: |
178 | drm_connector_unregister(connector); | 198 | drm_connector_unregister(connector); |
179 | err_cleanup: | 199 | err_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 | |||
23 | void 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 | |||
29 | void 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 | |||
36 | static 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 | |||
44 | static 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 | |||
66 | struct 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 | |||
101 | err_node_put: | ||
102 | of_node_put(np); | ||
103 | return NULL; | ||
104 | } | ||
105 | |||
106 | void 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 | |||
22 | struct fsl_tcon { | ||
23 | struct regmap *regs; | ||
24 | struct clk *ipg_clk; | ||
25 | }; | ||
26 | |||
27 | struct fsl_tcon *fsl_tcon_init(struct device *dev); | ||
28 | void fsl_tcon_free(struct fsl_tcon *tcon); | ||
29 | |||
30 | void fsl_tcon_bypass_disable(struct fsl_tcon *tcon); | ||
31 | void fsl_tcon_bypass_enable(struct fsl_tcon *tcon); | ||
32 | |||
33 | #endif /* __FSL_TCON_H__ */ | ||