Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

+302 -61
+11 -4
Documentation/devicetree/bindings/display/fsl,dcu.txt
··· 6 6 * "fsl,vf610-dcu". 7 7 8 8 - reg: Address and length of the register set for dcu. 9 - - clocks: From common clock binding: handle to dcu clock. 10 - - clock-names: From common clock binding: Shall be "dcu". 9 + - clocks: Handle to "dcu" and "pix" clock (in the order below) 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 14 - big-endian Boolean property, LS1021A DCU registers are big-endian. 12 15 - fsl,panel: The phandle to panel node. 16 + 17 + Optional properties: 18 + - fsl,tcon: The phandle to the timing controller node. 13 19 14 20 Examples: 15 21 dcu: dcu@2ce0000 { 16 22 compatible = "fsl,ls1021a-dcu"; 17 23 reg = <0x0 0x2ce0000 0x0 0x10000>; 18 - clocks = <&platform_clk 0>; 19 - clock-names = "dcu"; 24 + clocks = <&platform_clk 0>, <&platform_clk 0>; 25 + clock-names = "dcu", "pix"; 20 26 big-endian; 21 27 fsl,panel = <&panel>; 28 + fsl,tcon = <&tcon>; 22 29 };
+18
Documentation/devicetree/bindings/display/fsl,tcon.txt
··· 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 + };
+1
MAINTAINERS
··· 3835 3835 S: Supported 3836 3836 F: drivers/gpu/drm/fsl-dcu/ 3837 3837 F: Documentation/devicetree/bindings/display/fsl,dcu.txt 3838 + F: Documentation/devicetree/bindings/display/fsl,tcon.txt 3838 3839 F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt 3839 3840 3840 3841 DRM DRIVERS FOR FREESCALE IMX
+2 -1
drivers/gpu/drm/fsl-dcu/Makefile
··· 3 3 fsl_dcu_drm_rgb.o \ 4 4 fsl_dcu_drm_plane.o \ 5 5 fsl_dcu_drm_crtc.o \ 6 - fsl_dcu_drm_fbdev.o 6 + fsl_dcu_drm_fbdev.o \ 7 + fsl_tcon.o 7 8 obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o
+2 -5
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
··· 67 67 struct drm_device *dev = crtc->dev; 68 68 struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 69 69 struct drm_display_mode *mode = &crtc->state->mode; 70 - unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index, pol = 0; 71 - unsigned long dcuclk; 70 + unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0; 72 71 73 72 index = drm_crtc_index(crtc); 74 - dcuclk = clk_get_rate(fsl_dev->clk); 75 - div = dcuclk / mode->clock / 1000; 73 + clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000); 76 74 77 75 /* Configure timings: */ 78 76 hbp = mode->htotal - mode->hsync_end; ··· 97 99 regmap_write(fsl_dev->regmap, DCU_DISP_SIZE, 98 100 DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) | 99 101 DCU_DISP_SIZE_DELTA_X(mode->hdisplay)); 100 - regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div); 101 102 regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol); 102 103 regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) | 103 104 DCU_BGND_G(0) | DCU_BGND_B(0));
+93 -42
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
··· 23 23 24 24 #include <drm/drmP.h> 25 25 #include <drm/drm_crtc_helper.h> 26 + #include <drm/drm_fb_cma_helper.h> 26 27 #include <drm/drm_gem_cma_helper.h> 27 28 28 29 #include "fsl_dcu_drm_crtc.h" 29 30 #include "fsl_dcu_drm_drv.h" 31 + #include "fsl_tcon.h" 30 32 31 33 static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg) 32 34 { ··· 64 62 return ret; 65 63 } 66 64 67 - static int fsl_dcu_load(struct drm_device *drm, unsigned long flags) 65 + static int fsl_dcu_load(struct drm_device *dev, unsigned long flags) 68 66 { 69 - struct device *dev = drm->dev; 70 - struct fsl_dcu_drm_device *fsl_dev = drm->dev_private; 67 + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 71 68 int ret; 72 69 73 70 ret = fsl_dcu_drm_modeset_init(fsl_dev); 74 71 if (ret < 0) { 75 - dev_err(dev, "failed to initialize mode setting\n"); 72 + dev_err(dev->dev, "failed to initialize mode setting\n"); 76 73 return ret; 77 74 } 78 75 79 - ret = drm_vblank_init(drm, drm->mode_config.num_crtc); 76 + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); 80 77 if (ret < 0) { 81 - dev_err(dev, "failed to initialize vblank\n"); 78 + dev_err(dev->dev, "failed to initialize vblank\n"); 82 79 goto done; 83 80 } 84 - drm->vblank_disable_allowed = true; 81 + dev->vblank_disable_allowed = true; 85 82 86 - ret = fsl_dcu_drm_irq_init(drm); 83 + ret = fsl_dcu_drm_irq_init(dev); 87 84 if (ret < 0) 88 85 goto done; 89 - drm->irq_enabled = true; 86 + dev->irq_enabled = true; 90 87 91 - fsl_dcu_fbdev_init(drm); 88 + fsl_dcu_fbdev_init(dev); 92 89 93 90 return 0; 94 91 done: 95 - if (ret) { 96 - drm_mode_config_cleanup(drm); 97 - drm_vblank_cleanup(drm); 98 - drm_irq_uninstall(drm); 99 - drm->dev_private = NULL; 100 - } 92 + drm_kms_helper_poll_fini(dev); 93 + 94 + if (fsl_dev->fbdev) 95 + drm_fbdev_cma_fini(fsl_dev->fbdev); 96 + 97 + drm_mode_config_cleanup(dev); 98 + drm_vblank_cleanup(dev); 99 + drm_irq_uninstall(dev); 100 + dev->dev_private = NULL; 101 101 102 102 return ret; 103 103 } 104 104 105 105 static int fsl_dcu_unload(struct drm_device *dev) 106 106 { 107 + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 108 + 109 + drm_kms_helper_poll_fini(dev); 110 + 111 + if (fsl_dev->fbdev) 112 + drm_fbdev_cma_fini(fsl_dev->fbdev); 113 + 107 114 drm_mode_config_cleanup(dev); 108 115 drm_vblank_cleanup(dev); 109 116 drm_irq_uninstall(dev); ··· 168 157 regmap_write(fsl_dev->regmap, DCU_INT_MASK, value); 169 158 } 170 159 160 + static void fsl_dcu_drm_lastclose(struct drm_device *dev) 161 + { 162 + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 163 + 164 + drm_fbdev_cma_restore_mode(fsl_dev->fbdev); 165 + } 166 + 171 167 static const struct file_operations fsl_dcu_drm_fops = { 172 168 .owner = THIS_MODULE, 173 169 .open = drm_open, ··· 192 174 static struct drm_driver fsl_dcu_drm_driver = { 193 175 .driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET 194 176 | DRIVER_PRIME | DRIVER_ATOMIC, 177 + .lastclose = fsl_dcu_drm_lastclose, 195 178 .load = fsl_dcu_load, 196 179 .unload = fsl_dcu_unload, 197 180 .irq_handler = fsl_dcu_drm_irq, ··· 216 197 .fops = &fsl_dcu_drm_fops, 217 198 .name = "fsl-dcu-drm", 218 199 .desc = "Freescale DCU DRM", 219 - .date = "20150213", 200 + .date = "20160425", 220 201 .major = 1, 221 - .minor = 0, 202 + .minor = 1, 222 203 }; 223 204 224 205 #ifdef CONFIG_PM_SLEEP ··· 302 283 struct resource *res; 303 284 void __iomem *base; 304 285 struct drm_driver *driver = &fsl_dcu_drm_driver; 286 + struct clk *pix_clk_in; 287 + char pix_clk_name[32]; 288 + const char *pix_clk_in_name; 305 289 const struct of_device_id *id; 306 290 int ret; 307 291 308 292 fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL); 309 293 if (!fsl_dev) 310 294 return -ENOMEM; 295 + 296 + id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node); 297 + if (!id) 298 + return -ENODEV; 299 + fsl_dev->soc = id->data; 311 300 312 301 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 313 302 if (!res) { ··· 335 308 return -ENXIO; 336 309 } 337 310 338 - fsl_dev->clk = devm_clk_get(dev, "dcu"); 339 - if (IS_ERR(fsl_dev->clk)) { 340 - ret = PTR_ERR(fsl_dev->clk); 341 - dev_err(dev, "failed to get dcu clock\n"); 342 - return ret; 343 - } 344 - ret = clk_prepare(fsl_dev->clk); 345 - if (ret < 0) { 346 - dev_err(dev, "failed to prepare dcu clk\n"); 347 - return ret; 348 - } 349 - ret = clk_enable(fsl_dev->clk); 350 - if (ret < 0) { 351 - dev_err(dev, "failed to enable dcu clk\n"); 352 - clk_unprepare(fsl_dev->clk); 353 - return ret; 354 - } 355 - 356 311 fsl_dev->regmap = devm_regmap_init_mmio(dev, base, 357 312 &fsl_dcu_regmap_config); 358 313 if (IS_ERR(fsl_dev->regmap)) { ··· 342 333 return PTR_ERR(fsl_dev->regmap); 343 334 } 344 335 345 - id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node); 346 - if (!id) 347 - return -ENODEV; 348 - fsl_dev->soc = id->data; 336 + fsl_dev->clk = devm_clk_get(dev, "dcu"); 337 + if (IS_ERR(fsl_dev->clk)) { 338 + dev_err(dev, "failed to get dcu clock\n"); 339 + return PTR_ERR(fsl_dev->clk); 340 + } 341 + ret = clk_prepare_enable(fsl_dev->clk); 342 + if (ret < 0) { 343 + dev_err(dev, "failed to enable dcu clk\n"); 344 + return ret; 345 + } 346 + 347 + pix_clk_in = devm_clk_get(dev, "pix"); 348 + if (IS_ERR(pix_clk_in)) { 349 + /* legancy binding, use dcu clock as pixel clock input */ 350 + pix_clk_in = fsl_dev->clk; 351 + } 352 + 353 + pix_clk_in_name = __clk_get_name(pix_clk_in); 354 + snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name); 355 + fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name, 356 + pix_clk_in_name, 0, base + DCU_DIV_RATIO, 357 + 0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL); 358 + if (IS_ERR(fsl_dev->pix_clk)) { 359 + dev_err(dev, "failed to register pix clk\n"); 360 + ret = PTR_ERR(fsl_dev->pix_clk); 361 + goto disable_clk; 362 + } 363 + 364 + ret = clk_prepare_enable(fsl_dev->pix_clk); 365 + if (ret < 0) { 366 + dev_err(dev, "failed to enable pix clk\n"); 367 + goto unregister_pix_clk; 368 + } 369 + 370 + fsl_dev->tcon = fsl_tcon_init(dev); 349 371 350 372 drm = drm_dev_alloc(driver, dev); 351 - if (!drm) 352 - return -ENOMEM; 373 + if (!drm) { 374 + ret = -ENOMEM; 375 + goto disable_pix_clk; 376 + } 353 377 354 378 fsl_dev->dev = dev; 355 379 fsl_dev->drm = drm; ··· 402 360 403 361 unref: 404 362 drm_dev_unref(drm); 363 + disable_pix_clk: 364 + clk_disable_unprepare(fsl_dev->pix_clk); 365 + unregister_pix_clk: 366 + clk_unregister(fsl_dev->pix_clk); 367 + disable_clk: 368 + clk_disable_unprepare(fsl_dev->clk); 405 369 return ret; 406 370 } 407 371 ··· 415 367 { 416 368 struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev); 417 369 370 + clk_disable_unprepare(fsl_dev->clk); 371 + clk_disable_unprepare(fsl_dev->pix_clk); 372 + clk_unregister(fsl_dev->pix_clk); 418 373 drm_put_dev(fsl_dev->drm); 419 374 420 375 return 0;
+2
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
··· 183 183 struct regmap *regmap; 184 184 int irq; 185 185 struct clk *clk; 186 + struct clk *pix_clk; 187 + struct fsl_tcon *tcon; 186 188 /*protects hardware register*/ 187 189 spinlock_t irq_lock; 188 190 struct drm_device *drm;
+29 -9
drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_rgb.c
··· 17 17 #include <drm/drm_panel.h> 18 18 19 19 #include "fsl_dcu_drm_drv.h" 20 + #include "fsl_tcon.h" 20 21 21 22 static int 22 23 fsl_dcu_drm_encoder_atomic_check(struct drm_encoder *encoder, ··· 29 28 30 29 static void fsl_dcu_drm_encoder_disable(struct drm_encoder *encoder) 31 30 { 31 + struct drm_device *dev = encoder->dev; 32 + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 33 + 34 + if (fsl_dev->tcon) 35 + fsl_tcon_bypass_disable(fsl_dev->tcon); 32 36 } 33 37 34 38 static void fsl_dcu_drm_encoder_enable(struct drm_encoder *encoder) 35 39 { 40 + struct drm_device *dev = encoder->dev; 41 + struct fsl_dcu_drm_device *fsl_dev = dev->dev_private; 42 + 43 + if (fsl_dev->tcon) 44 + fsl_tcon_bypass_enable(fsl_dev->tcon); 36 45 } 37 46 38 47 static const struct drm_encoder_helper_funcs encoder_helper_funcs = { ··· 79 68 80 69 static void fsl_dcu_drm_connector_destroy(struct drm_connector *connector) 81 70 { 71 + struct fsl_dcu_drm_connector *fsl_con = to_fsl_dcu_connector(connector); 72 + 82 73 drm_connector_unregister(connector); 74 + drm_panel_detach(fsl_con->panel); 83 75 drm_connector_cleanup(connector); 84 76 } 85 77 ··· 145 131 struct drm_encoder *encoder) 146 132 { 147 133 struct drm_connector *connector = &fsl_dev->connector.base; 148 - struct drm_mode_config mode_config = fsl_dev->drm->mode_config; 134 + struct drm_mode_config *mode_config = &fsl_dev->drm->mode_config; 149 135 struct device_node *panel_node; 150 136 int ret; 151 137 ··· 167 153 goto err_sysfs; 168 154 169 155 drm_object_property_set_value(&connector->base, 170 - mode_config.dpms_property, 156 + mode_config->dpms_property, 171 157 DRM_MODE_DPMS_OFF); 172 158 173 159 panel_node = of_parse_phandle(fsl_dev->np, "fsl,panel", 0); 174 - if (panel_node) { 175 - fsl_dev->connector.panel = of_drm_find_panel(panel_node); 176 - if (!fsl_dev->connector.panel) { 177 - ret = -EPROBE_DEFER; 178 - goto err_sysfs; 179 - } 180 - of_node_put(panel_node); 160 + if (!panel_node) { 161 + dev_err(fsl_dev->dev, "fsl,panel property not found\n"); 162 + ret = -ENODEV; 163 + goto err_sysfs; 181 164 } 165 + 166 + fsl_dev->connector.panel = of_drm_find_panel(panel_node); 167 + if (!fsl_dev->connector.panel) { 168 + ret = -EPROBE_DEFER; 169 + goto err_panel; 170 + } 171 + of_node_put(panel_node); 182 172 183 173 ret = drm_panel_attach(fsl_dev->connector.panel, connector); 184 174 if (ret) { ··· 192 174 193 175 return 0; 194 176 177 + err_panel: 178 + of_node_put(panel_node); 195 179 err_sysfs: 196 180 drm_connector_unregister(connector); 197 181 err_cleanup:
+111
drivers/gpu/drm/fsl-dcu/fsl_tcon.c
··· 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 +
+33
drivers/gpu/drm/fsl-dcu/fsl_tcon.h
··· 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__ */