Skip to content

Commit

Permalink
Merge branch 'for-next' of http://git.agner.ch/git/linux-drm-fsl-dcu
Browse files Browse the repository at this point in the history
…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
  • Loading branch information
Dave Airlie committed Apr 29, 2016
2 parents d3a8f67 + 0449eef commit b89359b
Show file tree
Hide file tree
Showing 10 changed files with 298 additions and 57 deletions.
15 changes: 11 additions & 4 deletions Documentation/devicetree/bindings/display/fsl,dcu.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,24 @@ Required properties:
* "fsl,vf610-dcu".

- reg: Address and length of the register set for dcu.
- clocks: From common clock binding: handle to dcu clock.
- clock-names: From common clock binding: Shall be "dcu".
- clocks: Handle to "dcu" and "pix" clock (in the order below)
This can be the same clock (e.g. LS1021a)
See ../clocks/clock-bindings.txt for details.
- clock-names: Should be "dcu" and "pix"
See ../clocks/clock-bindings.txt for details.
- big-endian Boolean property, LS1021A DCU registers are big-endian.
- fsl,panel: The phandle to panel node.

Optional properties:
- fsl,tcon: The phandle to the timing controller node.

Examples:
dcu: dcu@2ce0000 {
compatible = "fsl,ls1021a-dcu";
reg = <0x0 0x2ce0000 0x0 0x10000>;
clocks = <&platform_clk 0>;
clock-names = "dcu";
clocks = <&platform_clk 0>, <&platform_clk 0>;
clock-names = "dcu", "pix";
big-endian;
fsl,panel = <&panel>;
fsl,tcon = <&tcon>;
};
18 changes: 18 additions & 0 deletions Documentation/devicetree/bindings/display/fsl,tcon.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
Device Tree bindings for Freescale TCON Driver

Required properties:
- compatible: Should be one of
* "fsl,vf610-tcon".

- reg: Address and length of the register set for tcon.
- clocks: From common clock binding: handle to tcon ipg clock.
- clock-names: From common clock binding: Shall be "ipg".

Examples:
timing-controller@4003d000 {
compatible = "fsl,vf610-tcon";
reg = <0x4003d000 0x1000>;
clocks = <&clks VF610_CLK_TCON0>;
clock-names = "ipg";
status = "okay";
};
1 change: 1 addition & 0 deletions MAINTAINERS
Original file line number Diff line number Diff line change
Expand Up @@ -3835,6 +3835,7 @@ L: dri-devel@lists.freedesktop.org
S: Supported
F: drivers/gpu/drm/fsl-dcu/
F: Documentation/devicetree/bindings/display/fsl,dcu.txt
F: Documentation/devicetree/bindings/display/fsl,tcon.txt
F: Documentation/devicetree/bindings/display/panel/nec,nl4827hc19_05b.txt

DRM DRIVERS FOR FREESCALE IMX
Expand Down
3 changes: 2 additions & 1 deletion drivers/gpu/drm/fsl-dcu/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ fsl-dcu-drm-y := fsl_dcu_drm_drv.o \
fsl_dcu_drm_rgb.o \
fsl_dcu_drm_plane.o \
fsl_dcu_drm_crtc.o \
fsl_dcu_drm_fbdev.o
fsl_dcu_drm_fbdev.o \
fsl_tcon.o
obj-$(CONFIG_DRM_FSL_DCU) += fsl-dcu-drm.o
7 changes: 2 additions & 5 deletions drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_crtc.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,10 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
struct drm_device *dev = crtc->dev;
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
struct drm_display_mode *mode = &crtc->state->mode;
unsigned int hbp, hfp, hsw, vbp, vfp, vsw, div, index, pol = 0;
unsigned long dcuclk;
unsigned int hbp, hfp, hsw, vbp, vfp, vsw, index, pol = 0;

index = drm_crtc_index(crtc);
dcuclk = clk_get_rate(fsl_dev->clk);
div = dcuclk / mode->clock / 1000;
clk_set_rate(fsl_dev->pix_clk, mode->clock * 1000);

/* Configure timings: */
hbp = mode->htotal - mode->hsync_end;
Expand All @@ -99,7 +97,6 @@ static void fsl_dcu_drm_crtc_mode_set_nofb(struct drm_crtc *crtc)
regmap_write(fsl_dev->regmap, DCU_DISP_SIZE,
DCU_DISP_SIZE_DELTA_Y(mode->vdisplay) |
DCU_DISP_SIZE_DELTA_X(mode->hdisplay));
regmap_write(fsl_dev->regmap, DCU_DIV_RATIO, div);
regmap_write(fsl_dev->regmap, DCU_SYN_POL, pol);
regmap_write(fsl_dev->regmap, DCU_BGND, DCU_BGND_R(0) |
DCU_BGND_G(0) | DCU_BGND_B(0));
Expand Down
127 changes: 89 additions & 38 deletions drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@

#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>

#include "fsl_dcu_drm_crtc.h"
#include "fsl_dcu_drm_drv.h"
#include "fsl_tcon.h"

static bool fsl_dcu_drm_is_volatile_reg(struct device *dev, unsigned int reg)
{
Expand Down Expand Up @@ -62,46 +64,55 @@ static int fsl_dcu_drm_irq_init(struct drm_device *dev)
return ret;
}

static int fsl_dcu_load(struct drm_device *drm, unsigned long flags)
static int fsl_dcu_load(struct drm_device *dev, unsigned long flags)
{
struct device *dev = drm->dev;
struct fsl_dcu_drm_device *fsl_dev = drm->dev_private;
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;
int ret;

ret = fsl_dcu_drm_modeset_init(fsl_dev);
if (ret < 0) {
dev_err(dev, "failed to initialize mode setting\n");
dev_err(dev->dev, "failed to initialize mode setting\n");
return ret;
}

ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret < 0) {
dev_err(dev, "failed to initialize vblank\n");
dev_err(dev->dev, "failed to initialize vblank\n");
goto done;
}
drm->vblank_disable_allowed = true;
dev->vblank_disable_allowed = true;

ret = fsl_dcu_drm_irq_init(drm);
ret = fsl_dcu_drm_irq_init(dev);
if (ret < 0)
goto done;
drm->irq_enabled = true;
dev->irq_enabled = true;

fsl_dcu_fbdev_init(drm);
fsl_dcu_fbdev_init(dev);

return 0;
done:
if (ret) {
drm_mode_config_cleanup(drm);
drm_vblank_cleanup(drm);
drm_irq_uninstall(drm);
drm->dev_private = NULL;
}
drm_kms_helper_poll_fini(dev);

if (fsl_dev->fbdev)
drm_fbdev_cma_fini(fsl_dev->fbdev);

drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);
drm_irq_uninstall(dev);
dev->dev_private = NULL;

return ret;
}

static int fsl_dcu_unload(struct drm_device *dev)
{
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;

drm_kms_helper_poll_fini(dev);

if (fsl_dev->fbdev)
drm_fbdev_cma_fini(fsl_dev->fbdev);

drm_mode_config_cleanup(dev);
drm_vblank_cleanup(dev);
drm_irq_uninstall(dev);
Expand Down Expand Up @@ -157,6 +168,13 @@ static void fsl_dcu_drm_disable_vblank(struct drm_device *dev,
regmap_write(fsl_dev->regmap, DCU_INT_MASK, value);
}

static void fsl_dcu_drm_lastclose(struct drm_device *dev)
{
struct fsl_dcu_drm_device *fsl_dev = dev->dev_private;

drm_fbdev_cma_restore_mode(fsl_dev->fbdev);
}

static const struct file_operations fsl_dcu_drm_fops = {
.owner = THIS_MODULE,
.open = drm_open,
Expand All @@ -174,6 +192,7 @@ static const struct file_operations fsl_dcu_drm_fops = {
static struct drm_driver fsl_dcu_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_GEM | DRIVER_MODESET
| DRIVER_PRIME | DRIVER_ATOMIC,
.lastclose = fsl_dcu_drm_lastclose,
.load = fsl_dcu_load,
.unload = fsl_dcu_unload,
.irq_handler = fsl_dcu_drm_irq,
Expand All @@ -197,9 +216,9 @@ static struct drm_driver fsl_dcu_drm_driver = {
.fops = &fsl_dcu_drm_fops,
.name = "fsl-dcu-drm",
.desc = "Freescale DCU DRM",
.date = "20150213",
.date = "20160425",
.major = 1,
.minor = 0,
.minor = 1,
};

#ifdef CONFIG_PM_SLEEP
Expand Down Expand Up @@ -283,13 +302,21 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
struct resource *res;
void __iomem *base;
struct drm_driver *driver = &fsl_dcu_drm_driver;
struct clk *pix_clk_in;
char pix_clk_name[32];
const char *pix_clk_in_name;
const struct of_device_id *id;
int ret;

fsl_dev = devm_kzalloc(dev, sizeof(*fsl_dev), GFP_KERNEL);
if (!fsl_dev)
return -ENOMEM;

id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
if (!id)
return -ENODEV;
fsl_dev->soc = id->data;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "could not get memory IO resource\n");
Expand All @@ -308,39 +335,54 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)
return -ENXIO;
}

fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
&fsl_dcu_regmap_config);
if (IS_ERR(fsl_dev->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(fsl_dev->regmap);
}

fsl_dev->clk = devm_clk_get(dev, "dcu");
if (IS_ERR(fsl_dev->clk)) {
ret = PTR_ERR(fsl_dev->clk);
dev_err(dev, "failed to get dcu clock\n");
return ret;
}
ret = clk_prepare(fsl_dev->clk);
if (ret < 0) {
dev_err(dev, "failed to prepare dcu clk\n");
return ret;
return PTR_ERR(fsl_dev->clk);
}
ret = clk_enable(fsl_dev->clk);
ret = clk_prepare_enable(fsl_dev->clk);
if (ret < 0) {
dev_err(dev, "failed to enable dcu clk\n");
clk_unprepare(fsl_dev->clk);
return ret;
}

fsl_dev->regmap = devm_regmap_init_mmio(dev, base,
&fsl_dcu_regmap_config);
if (IS_ERR(fsl_dev->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(fsl_dev->regmap);
pix_clk_in = devm_clk_get(dev, "pix");
if (IS_ERR(pix_clk_in)) {
/* legancy binding, use dcu clock as pixel clock input */
pix_clk_in = fsl_dev->clk;
}

id = of_match_node(fsl_dcu_of_match, pdev->dev.of_node);
if (!id)
return -ENODEV;
fsl_dev->soc = id->data;
pix_clk_in_name = __clk_get_name(pix_clk_in);
snprintf(pix_clk_name, sizeof(pix_clk_name), "%s_pix", pix_clk_in_name);
fsl_dev->pix_clk = clk_register_divider(dev, pix_clk_name,
pix_clk_in_name, 0, base + DCU_DIV_RATIO,
0, 8, CLK_DIVIDER_ROUND_CLOSEST, NULL);
if (IS_ERR(fsl_dev->pix_clk)) {
dev_err(dev, "failed to register pix clk\n");
ret = PTR_ERR(fsl_dev->pix_clk);
goto disable_clk;
}

ret = clk_prepare_enable(fsl_dev->pix_clk);
if (ret < 0) {
dev_err(dev, "failed to enable pix clk\n");
goto unregister_pix_clk;
}

fsl_dev->tcon = fsl_tcon_init(dev);

drm = drm_dev_alloc(driver, dev);
if (!drm)
return -ENOMEM;
if (!drm) {
ret = -ENOMEM;
goto disable_pix_clk;
}

fsl_dev->dev = dev;
fsl_dev->drm = drm;
Expand All @@ -360,13 +402,22 @@ static int fsl_dcu_drm_probe(struct platform_device *pdev)

unref:
drm_dev_unref(drm);
disable_pix_clk:
clk_disable_unprepare(fsl_dev->pix_clk);
unregister_pix_clk:
clk_unregister(fsl_dev->pix_clk);
disable_clk:
clk_disable_unprepare(fsl_dev->clk);
return ret;
}

static int fsl_dcu_drm_remove(struct platform_device *pdev)
{
struct fsl_dcu_drm_device *fsl_dev = platform_get_drvdata(pdev);

clk_disable_unprepare(fsl_dev->clk);
clk_disable_unprepare(fsl_dev->pix_clk);
clk_unregister(fsl_dev->pix_clk);
drm_put_dev(fsl_dev->drm);

return 0;
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpu/drm/fsl-dcu/fsl_dcu_drm_drv.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ struct fsl_dcu_drm_device {
struct regmap *regmap;
int irq;
struct clk *clk;
struct clk *pix_clk;
struct fsl_tcon *tcon;
/*protects hardware register*/
spinlock_t irq_lock;
struct drm_device *drm;
Expand Down
Loading

0 comments on commit b89359b

Please sign in to comment.