From c334940ea26cb58f5514dcbb225a3f397b2684ad Mon Sep 17 00:00:00 2001 From: Russell King Date: Wed, 23 Apr 2014 10:52:17 +0100 Subject: [PATCH 01/14] component: fix missed cleanup in case of devres failure In try_to_bring_up_master(), we tear down the master's component list for each error case, except for devres group failure. Fix this oversight by making the code less prone to such mistakes. Acked-by: Laurent Pinchart Signed-off-by: Russell King --- drivers/base/component.c | 68 ++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index c4778995cd72..d0ebd4431736 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -113,44 +113,44 @@ static void master_remove_components(struct master *master) static int try_to_bring_up_master(struct master *master, struct component *component) { - int ret = 0; + int ret; - if (!master->bound) { - /* - * Search the list of components, looking for components that - * belong to this master, and attach them to the master. - */ - if (master->ops->add_components(master->dev, master)) { - /* Failed to find all components */ - master_remove_components(master); - ret = 0; - goto out; - } + if (master->bound) + return 0; - if (component && component->master != master) { - master_remove_components(master); - ret = 0; - goto out; - } - - if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { - ret = -ENOMEM; - goto out; - } - - /* Found all components */ - ret = master->ops->bind(master->dev); - if (ret < 0) { - devres_release_group(master->dev, NULL); - dev_info(master->dev, "master bind failed: %d\n", ret); - master_remove_components(master); - goto out; - } - - master->bound = true; - ret = 1; + /* + * Search the list of components, looking for components that + * belong to this master, and attach them to the master. + */ + if (master->ops->add_components(master->dev, master)) { + /* Failed to find all components */ + ret = 0; + goto out; } + + if (component && component->master != master) { + ret = 0; + goto out; + } + + if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { + ret = -ENOMEM; + goto out; + } + + /* Found all components */ + ret = master->ops->bind(master->dev); + if (ret < 0) { + devres_release_group(master->dev, NULL); + dev_info(master->dev, "master bind failed: %d\n", ret); + goto out; + } + + master->bound = true; + return 1; + out: + master_remove_components(master); return ret; } From fcbcebce7159c928692dc6a5e88869f6e44438b9 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 18 Apr 2014 20:16:22 +0100 Subject: [PATCH 02/14] component: ignore multiple additions of the same component Permit masters to call component_master_add_child() and match the same child multiple times. This may happen if there's multiple connections to a single component device from other devices. In such scenarios, we should not return a failure, but instead ignore the attempt. Acked-by: Laurent Pinchart Signed-off-by: Russell King --- drivers/base/component.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index d0ebd4431736..55813e91bf0d 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -69,6 +69,11 @@ static void component_detach_master(struct master *master, struct component *c) c->master = NULL; } +/* + * Add a component to a master, finding the component via the compare + * function and compare data. This is safe to call for duplicate matches + * and will not result in the same component being added multiple times. + */ int component_master_add_child(struct master *master, int (*compare)(struct device *, void *), void *compare_data) { @@ -76,11 +81,12 @@ int component_master_add_child(struct master *master, int ret = -ENXIO; list_for_each_entry(c, &component_list, node) { - if (c->master) + if (c->master && c->master != master) continue; if (compare(c->dev, compare_data)) { - component_attach_master(master, c); + if (!c->master) + component_attach_master(master, c); ret = 0; break; } From 6955b58254c2bcee8a7b55ce06468a645dc98ec5 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 19 Apr 2014 11:18:01 +0100 Subject: [PATCH 03/14] component: add support for component match array Add support for generating a set of component matches at master probe time, and submitting them to the component layer. This allows the component layer to perform the matches internally without needing to call into the master driver, and allows for further restructuring of the component helper. Acked-by: Laurent Pinchart Signed-off-by: Russell King --- drivers/base/component.c | 120 +++++++++++++++++++++++++++++++++++++- include/linux/component.h | 7 +++ 2 files changed, 124 insertions(+), 3 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index 55813e91bf0d..b4236daed4fa 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -18,6 +18,15 @@ #include #include +struct component_match { + size_t alloc; + size_t num; + struct { + void *data; + int (*fn)(struct device *, void *); + } compare[0]; +}; + struct master { struct list_head node; struct list_head components; @@ -25,6 +34,7 @@ struct master { const struct component_master_ops *ops; struct device *dev; + struct component_match *match; }; struct component { @@ -96,6 +106,34 @@ int component_master_add_child(struct master *master, } EXPORT_SYMBOL_GPL(component_master_add_child); +static int find_components(struct master *master) +{ + struct component_match *match = master->match; + size_t i; + int ret = 0; + + if (!match) { + /* + * Search the list of components, looking for components that + * belong to this master, and attach them to the master. + */ + return master->ops->add_components(master->dev, master); + } + + /* + * Scan the array of match functions and attach + * any components which are found to this master. + */ + for (i = 0; i < match->num; i++) { + ret = component_master_add_child(master, + match->compare[i].fn, + match->compare[i].data); + if (ret) + break; + } + return ret; +} + /* Detach all attached components from this master */ static void master_remove_components(struct master *master) { @@ -128,7 +166,7 @@ static int try_to_bring_up_master(struct master *master, * Search the list of components, looking for components that * belong to this master, and attach them to the master. */ - if (master->ops->add_components(master->dev, master)) { + if (find_components(master)) { /* Failed to find all components */ ret = 0; goto out; @@ -186,18 +224,87 @@ static void take_down_master(struct master *master) master_remove_components(master); } -int component_master_add(struct device *dev, - const struct component_master_ops *ops) +static size_t component_match_size(size_t num) +{ + return offsetof(struct component_match, compare[num]); +} + +static struct component_match *component_match_realloc(struct device *dev, + struct component_match *match, size_t num) +{ + struct component_match *new; + + if (match && match->alloc == num) + return match; + + new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + if (match) { + memcpy(new, match, component_match_size(min(match->num, num))); + devm_kfree(dev, match); + } else { + new->num = 0; + } + + new->alloc = num; + + return new; +} + +/* + * Add a component to be matched. + * + * The match array is first created or extended if necessary. + */ +void component_match_add(struct device *dev, struct component_match **matchptr, + int (*compare)(struct device *, void *), void *compare_data) +{ + struct component_match *match = *matchptr; + + if (IS_ERR(match)) + return; + + if (!match || match->num == match->alloc) { + size_t new_size = match ? match->alloc + 16 : 15; + + match = component_match_realloc(dev, match, new_size); + + *matchptr = match; + + if (IS_ERR(match)) + return; + } + + match->compare[match->num].fn = compare; + match->compare[match->num].data = compare_data; + match->num++; +} +EXPORT_SYMBOL(component_match_add); + +int component_master_add_with_match(struct device *dev, + const struct component_master_ops *ops, + struct component_match *match) { struct master *master; int ret; + if (ops->add_components && match) + return -EINVAL; + + /* Reallocate the match array for its true size */ + match = component_match_realloc(dev, match, match->num); + if (IS_ERR(match)) + return PTR_ERR(match); + master = kzalloc(sizeof(*master), GFP_KERNEL); if (!master) return -ENOMEM; master->dev = dev; master->ops = ops; + master->match = match; INIT_LIST_HEAD(&master->components); /* Add to the list of available masters. */ @@ -215,6 +322,13 @@ int component_master_add(struct device *dev, return ret < 0 ? ret : 0; } +EXPORT_SYMBOL_GPL(component_master_add_with_match); + +int component_master_add(struct device *dev, + const struct component_master_ops *ops) +{ + return component_master_add_with_match(dev, ops, NULL); +} EXPORT_SYMBOL_GPL(component_master_add); void component_master_del(struct device *dev, diff --git a/include/linux/component.h b/include/linux/component.h index 68870182ca1e..c00dcc302611 100644 --- a/include/linux/component.h +++ b/include/linux/component.h @@ -29,4 +29,11 @@ void component_master_del(struct device *, int component_master_add_child(struct master *master, int (*compare)(struct device *, void *), void *compare_data); +struct component_match; + +int component_master_add_with_match(struct device *, + const struct component_master_ops *, struct component_match *); +void component_match_add(struct device *, struct component_match **, + int (*compare)(struct device *, void *), void *compare_data); + #endif From e5d9ddfbb79667fb4b16db07c3282a2304fbf3ff Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 26 Apr 2014 15:19:38 +0100 Subject: [PATCH 04/14] drm/armada: move IRQ handling into CRTC Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 36 ++++++++++++++- drivers/gpu/drm/armada/armada_crtc.h | 4 +- drivers/gpu/drm/armada/armada_drv.c | 68 ++++------------------------ 3 files changed, 45 insertions(+), 63 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 81c34f949dfc..9341a94cc906 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -349,7 +349,7 @@ static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, return true; } -void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) +static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) { struct armada_vbl_event *e, *n; void __iomem *base = dcrtc->base; @@ -410,6 +410,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) } } +static irqreturn_t armada_drm_irq(int irq, void *arg) +{ + struct armada_crtc *dcrtc = arg; + u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); + + /* + * This is rediculous - rather than writing bits to clear, we + * have to set the actual status register value. This is racy. + */ + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); + + /* Mask out those interrupts we haven't enabled */ + v = stat & dcrtc->irq_ena; + + if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { + armada_drm_crtc_irq(dcrtc, stat); + return IRQ_HANDLED; + } + return IRQ_NONE; +} + /* These are locked by dev->vbl_lock */ void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) { @@ -888,6 +909,8 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) if (!IS_ERR(dcrtc->clk)) clk_disable_unprepare(dcrtc->clk); + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); + kfree(dcrtc); } @@ -1028,7 +1051,7 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) } int armada_drm_crtc_create(struct drm_device *dev, unsigned num, - struct resource *res) + struct resource *res, int irq) { struct armada_private *priv = dev->dev_private; struct armada_crtc *dcrtc; @@ -1074,6 +1097,15 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN); + writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); + writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); + + ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc", + dcrtc); + if (ret < 0) { + kfree(dcrtc); + return ret; + } if (priv->variant->crtc_init) { ret = priv->variant->crtc_init(dcrtc); diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 9c10a07e7492..0e952b271212 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -72,10 +72,10 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *); +int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *, + int); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); -void armada_drm_crtc_irq(struct armada_crtc *, u32); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 8ab3cd1a8cdb..58884d807e6c 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -155,10 +155,16 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) /* Create all LCD controllers */ for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { + int irq; + if (!res[n]) break; - ret = armada_drm_crtc_create(dev, n, res[n]); + irq = platform_get_irq(dev->platformdev, n); + if (irq < 0) + goto err_kms; + + ret = armada_drm_crtc_create(dev, n, res[n], irq); if (ret) goto err_kms; } @@ -173,22 +179,16 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (ret) goto err_kms; - ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0)); - if (ret) - goto err_kms; - dev->vblank_disable_allowed = 1; ret = armada_fbdev_init(dev); if (ret) - goto err_irq; + goto err_kms; drm_kms_helper_poll_init(dev); return 0; - err_irq: - drm_irq_uninstall(dev); err_kms: drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); @@ -203,7 +203,6 @@ static int armada_drm_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); armada_fbdev_fini(dev); - drm_irq_uninstall(dev); drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); flush_work(&priv->fb_unref_work); @@ -259,52 +258,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc) armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); } -static irqreturn_t armada_drm_irq_handler(int irq, void *arg) -{ - struct drm_device *dev = arg; - struct armada_private *priv = dev->dev_private; - struct armada_crtc *dcrtc = priv->dcrtc[0]; - uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR); - irqreturn_t handled = IRQ_NONE; - - /* - * This is rediculous - rather than writing bits to clear, we - * have to set the actual status register value. This is racy. - */ - writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR); - - /* Mask out those interrupts we haven't enabled */ - v = stat & dcrtc->irq_ena; - - if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) { - armada_drm_crtc_irq(dcrtc, stat); - handled = IRQ_HANDLED; - } - - return handled; -} - -static int armada_drm_irq_postinstall(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - struct armada_crtc *dcrtc = priv->dcrtc[0]; - - spin_lock_irq(&dev->vbl_lock); - writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA); - writel(0, dcrtc->base + LCD_SPU_IRQ_ISR); - spin_unlock_irq(&dev->vbl_lock); - - return 0; -} - -static void armada_drm_irq_uninstall(struct drm_device *dev) -{ - struct armada_private *priv = dev->dev_private; - struct armada_crtc *dcrtc = priv->dcrtc[0]; - - writel(0, dcrtc->base + LCD_SPU_IRQ_ENA); -} - static struct drm_ioctl_desc armada_ioctls[] = { DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, DRM_UNLOCKED), @@ -340,9 +293,6 @@ static struct drm_driver armada_drm_driver = { .get_vblank_counter = drm_vblank_count, .enable_vblank = armada_drm_enable_vblank, .disable_vblank = armada_drm_disable_vblank, - .irq_handler = armada_drm_irq_handler, - .irq_postinstall = armada_drm_irq_postinstall, - .irq_uninstall = armada_drm_irq_uninstall, #ifdef CONFIG_DEBUG_FS .debugfs_init = armada_drm_debugfs_init, .debugfs_cleanup = armada_drm_debugfs_cleanup, @@ -362,7 +312,7 @@ static struct drm_driver armada_drm_driver = { .desc = "Armada SoC DRM", .date = "20120730", .driver_features = DRIVER_GEM | DRIVER_MODESET | - DRIVER_HAVE_IRQ | DRIVER_PRIME, + DRIVER_PRIME, .ioctls = armada_ioctls, .fops = &armada_drm_fops, }; From d016540722bb412ffa8376a9db0de757ce915bfb Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 11:02:23 +0100 Subject: [PATCH 05/14] drm/armada: use number of CRTCs registered Use the number of CRTCs registered to size the vblank arrays rather than our own count. Number CRTCs using this as well. This permits us to register CRTCs as components in the near future rather than as part of a single device. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 6 +++--- drivers/gpu/drm/armada/armada_crtc.h | 3 +-- drivers/gpu/drm/armada/armada_drv.c | 4 ++-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 9341a94cc906..4336dfd3585d 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -1050,8 +1050,8 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) return 0; } -int armada_drm_crtc_create(struct drm_device *dev, unsigned num, - struct resource *res, int irq) +int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, + int irq) { struct armada_private *priv = dev->dev_private; struct armada_crtc *dcrtc; @@ -1075,7 +1075,7 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, } dcrtc->base = base; - dcrtc->num = num; + dcrtc->num = dev->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); dcrtc->csc_yuv_mode = CSC_AUTO; dcrtc->csc_rgb_mode = CSC_AUTO; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 0e952b271212..531a9b0bdcfb 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -72,8 +72,7 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *, - int); +int armada_drm_crtc_create(struct drm_device *, struct resource *, int); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 58884d807e6c..add8b101fa9e 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -164,7 +164,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (irq < 0) goto err_kms; - ret = armada_drm_crtc_create(dev, n, res[n], irq); + ret = armada_drm_crtc_create(dev, res[n], irq); if (ret) goto err_kms; } @@ -175,7 +175,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) goto err_kms; #endif - ret = drm_vblank_init(dev, n); + ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) goto err_kms; From 3ecea269959afaae50b001deb294cfb9539dbea3 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 15:21:30 +0100 Subject: [PATCH 06/14] drm/armada: move variant initialisation to CRTC init Move the variant initialisation entirely to the CRTC init function - the variant support is really about the CRTC properties than the whole system, and we want to treat each CRTC individually when we support DT. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_510.c | 19 ++++++++----------- drivers/gpu/drm/armada/armada_crtc.c | 2 +- drivers/gpu/drm/armada/armada_crtc.h | 1 + drivers/gpu/drm/armada/armada_drm.h | 6 ++---- drivers/gpu/drm/armada/armada_drv.c | 4 ---- 5 files changed, 12 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index 59948eff6095..a9dabcaef92e 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -15,20 +15,19 @@ #include "armada_drm.h" #include "armada_hw.h" -static int armada510_init(struct armada_private *priv, struct device *dev) +static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) { - priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1"); + struct clk *clk; - if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT) - priv->extclk[0] = ERR_PTR(-EPROBE_DEFER); + clk = devm_clk_get(dev, "ext_ref_clk_1"); + if (IS_ERR(clk)) + return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk); - return PTR_RET(priv->extclk[0]); -} + dcrtc->extclk[0] = clk; -static int armada510_crtc_init(struct armada_crtc *dcrtc) -{ /* Lower the watermark so to eliminate jitter at higher bandwidths */ armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); + return 0; } @@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc) static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, const struct drm_display_mode *mode, uint32_t *sclk) { - struct armada_private *priv = dcrtc->crtc.dev->dev_private; - struct clk *clk = priv->extclk[0]; + struct clk *clk = dcrtc->extclk[0]; int ret; if (dcrtc->num == 1) @@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, const struct armada_variant armada510_ops = { .has_spu_adv_reg = true, .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, - .init = armada510_init, .crtc_init = armada510_crtc_init, .crtc_compute_clock = armada510_crtc_compute_clock, }; diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 4336dfd3585d..3adddabc3626 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -1108,7 +1108,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, } if (priv->variant->crtc_init) { - ret = priv->variant->crtc_init(dcrtc); + ret = priv->variant->crtc_init(dcrtc, dev->dev); if (ret) { kfree(dcrtc); return ret; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 531a9b0bdcfb..3f0e70bb2e9c 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -38,6 +38,7 @@ struct armada_crtc { unsigned num; void __iomem *base; struct clk *clk; + struct clk *extclk[2]; struct { uint32_t spu_v_h_total; uint32_t spu_v_porch; diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index a72cae03b99b..a5452ae883d1 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -59,10 +59,9 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *, struct armada_private; struct armada_variant { - bool has_spu_adv_reg; + bool has_spu_adv_reg; uint32_t spu_adv_reg; - int (*init)(struct armada_private *, struct device *); - int (*crtc_init)(struct armada_crtc *); + int (*crtc_init)(struct armada_crtc *, struct device *); int (*crtc_compute_clock)(struct armada_crtc *, const struct drm_display_mode *, uint32_t *); @@ -78,7 +77,6 @@ struct armada_private { struct drm_fb_helper *fbdev; struct armada_crtc *dcrtc[2]; struct drm_mm linear; - struct clk *extclk[2]; struct drm_property *csc_yuv_prop; struct drm_property *csc_rgb_prop; struct drm_property *colorkey_prop; diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index add8b101fa9e..4939a86a2afc 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -130,10 +130,6 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) priv->variant = (struct armada_variant *)id->driver_data; - ret = priv->variant->init(priv, dev->dev); - if (ret) - return ret; - INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); From 42e62ba7a484e12a77b9e330a29bbf7c99d3e60e Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 15:24:03 +0100 Subject: [PATCH 07/14] drm/armada: make variant a CRTC thing Move the variant pointer into the armada_crtc structure, and update for the resulting changes. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_510.c | 4 ++-- drivers/gpu/drm/armada/armada_crtc.c | 27 ++++++++++++--------------- drivers/gpu/drm/armada/armada_crtc.h | 5 ++++- drivers/gpu/drm/armada/armada_drm.h | 9 ++++----- drivers/gpu/drm/armada/armada_drv.c | 5 +++-- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index a9dabcaef92e..1bcf1a8c1915 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -79,6 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, const struct armada_variant armada510_ops = { .has_spu_adv_reg = true, .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, - .crtc_init = armada510_crtc_init, - .crtc_compute_clock = armada510_crtc_compute_clock, + .init = armada510_crtc_init, + .compute_clock = armada510_crtc_compute_clock, }; diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 3adddabc3626..afa497489000 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -332,17 +332,16 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc) static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, struct drm_display_mode *adj) { - struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); int ret; /* We can't do interlaced modes if we don't have the SPU_ADV_REG */ - if (!priv->variant->has_spu_adv_reg && + if (!dcrtc->variant->has_spu_adv_reg && adj->flags & DRM_MODE_FLAG_INTERLACE) return false; /* Check whether the display mode is possible */ - ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL); + ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL); if (ret) return false; @@ -491,7 +490,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_display_mode *adj, int x, int y, struct drm_framebuffer *old_fb) { - struct armada_private *priv = crtc->dev->dev_private; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_regs regs[17]; uint32_t lm, rm, tm, bm, val, sclk; @@ -536,7 +534,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, } /* Now compute the divider for real */ - priv->variant->crtc_compute_clock(dcrtc, adj, &sclk); + dcrtc->variant->compute_clock(dcrtc, adj, &sclk); /* Ensure graphic fifo is enabled */ armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); @@ -558,7 +556,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, dcrtc->v[1].spu_v_porch = tm << 16 | bm; val = adj->crtc_hsync_start; dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | - priv->variant->spu_adv_reg; + dcrtc->variant->spu_adv_reg; if (interlaced) { /* Odd interlaced frame */ @@ -567,7 +565,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1; val = adj->crtc_hsync_start - adj->crtc_htotal / 2; dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | - priv->variant->spu_adv_reg; + dcrtc->variant->spu_adv_reg; } else { dcrtc->v[0] = dcrtc->v[1]; } @@ -582,7 +580,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total, LCD_SPUT_V_H_TOTAL); - if (priv->variant->has_spu_adv_reg) { + if (dcrtc->variant->has_spu_adv_reg) { armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg, ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); @@ -826,12 +824,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_private *priv = crtc->dev->dev_private; struct armada_gem_object *obj = NULL; int ret; /* If no cursor support, replicate drm's return value */ - if (!priv->variant->has_spu_adv_reg) + if (!dcrtc->variant->has_spu_adv_reg) return -ENXIO; if (handle && w > 0 && h > 0) { @@ -879,11 +876,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) { struct drm_device *dev = crtc->dev; struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); - struct armada_private *priv = crtc->dev->dev_private; int ret; /* If no cursor support, replicate drm's return value */ - if (!priv->variant->has_spu_adv_reg) + if (!dcrtc->variant->has_spu_adv_reg) return -EFAULT; mutex_lock(&dev->struct_mutex); @@ -1051,7 +1047,7 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) } int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, - int irq) + int irq, const struct armada_variant *variant) { struct armada_private *priv = dev->dev_private; struct armada_crtc *dcrtc; @@ -1074,6 +1070,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, return -ENOMEM; } + dcrtc->variant = variant; dcrtc->base = base; dcrtc->num = dev->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); @@ -1107,8 +1104,8 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, return ret; } - if (priv->variant->crtc_init) { - ret = priv->variant->crtc_init(dcrtc, dev->dev); + if (dcrtc->variant->init) { + ret = dcrtc->variant->init(dcrtc, dev->dev); if (ret) { kfree(dcrtc); return ret; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 3f0e70bb2e9c..73efcfcdf814 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -32,9 +32,11 @@ struct armada_regs { armada_reg_queue_mod(_r, _i, 0, 0, ~0) struct armada_frame_work; +struct armada_variant; struct armada_crtc { struct drm_crtc crtc; + const struct armada_variant *variant; unsigned num; void __iomem *base; struct clk *clk; @@ -73,7 +75,8 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, struct resource *, int); +int armada_drm_crtc_create(struct drm_device *, struct resource *, int, + const struct armada_variant *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); diff --git a/drivers/gpu/drm/armada/armada_drm.h b/drivers/gpu/drm/armada/armada_drm.h index a5452ae883d1..ea63c6c7c66f 100644 --- a/drivers/gpu/drm/armada/armada_drm.h +++ b/drivers/gpu/drm/armada/armada_drm.h @@ -61,17 +61,16 @@ struct armada_private; struct armada_variant { bool has_spu_adv_reg; uint32_t spu_adv_reg; - int (*crtc_init)(struct armada_crtc *, struct device *); - int (*crtc_compute_clock)(struct armada_crtc *, - const struct drm_display_mode *, - uint32_t *); + int (*init)(struct armada_crtc *, struct device *); + int (*compute_clock)(struct armada_crtc *, + const struct drm_display_mode *, + uint32_t *); }; /* Variant ops */ extern const struct armada_variant armada510_ops; struct armada_private { - const struct armada_variant *variant; struct work_struct fb_unref_work; DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); struct drm_fb_helper *fbdev; diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 4939a86a2afc..3995be3c686b 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -85,6 +85,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev, static int armada_drm_load(struct drm_device *dev, unsigned long flags) { const struct platform_device_id *id; + const struct armada_variant *variant; struct armada_private *priv; struct resource *res[ARRAY_SIZE(priv->dcrtc)]; struct resource *mem = NULL; @@ -128,7 +129,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (!id) return -ENXIO; - priv->variant = (struct armada_variant *)id->driver_data; + variant = (const struct armada_variant *)id->driver_data; INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work); INIT_KFIFO(priv->fb_unref); @@ -160,7 +161,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (irq < 0) goto err_kms; - ret = armada_drm_crtc_create(dev, res[n], irq); + ret = armada_drm_crtc_create(dev, res[n], irq, variant); if (ret) goto err_kms; } From b509cc802239a8b5ba7d1d2cc5adfb9d984b7ed8 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 4 Jul 2014 13:23:46 +0100 Subject: [PATCH 08/14] component: fix bug with legacy API Sachin Kamat reports that "component: add support for component match array" broke Exynos DRM due to a NULL pointer deref. Fix this. Reported-by: Sachin Kamat Tested-by: Sachin Kamat Signed-off-by: Russell King --- drivers/base/component.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/base/component.c b/drivers/base/component.c index b4236daed4fa..f748430bb654 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -293,10 +293,12 @@ int component_master_add_with_match(struct device *dev, if (ops->add_components && match) return -EINVAL; - /* Reallocate the match array for its true size */ - match = component_match_realloc(dev, match, match->num); - if (IS_ERR(match)) - return PTR_ERR(match); + if (match) { + /* Reallocate the match array for its true size */ + match = component_match_realloc(dev, match, match->num); + if (IS_ERR(match)) + return PTR_ERR(match); + } master = kzalloc(sizeof(*master), GFP_KERNEL); if (!master) From 7e435aad38083f378ba7889d8b8fdcdbfe7088e7 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 15 Jun 2014 11:07:12 +0100 Subject: [PATCH 09/14] drm: add of_graph endpoint helper to find possible CRTCs Add a helper to allow encoders to find their possible CRTCs from the OF graph without having to re-implement this functionality. We add a device_node to drm_crtc which corresponds with the port node in the DT description of the CRTC device. We can then scan the DRM device list for CRTCs to find their index, matching the appropriate CRTC using the port device_node, thus building up the possible CRTC mask. Reviewed-by: Rob Clark Signed-off-by: Russell King --- drivers/gpu/drm/Makefile | 1 + drivers/gpu/drm/drm_of.c | 67 ++++++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 2 ++ include/drm/drm_of.h | 18 +++++++++++ 4 files changed, 88 insertions(+) create mode 100644 drivers/gpu/drm/drm_of.c create mode 100644 include/drm/drm_of.h diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index dd2ba4269740..533d011eab3e 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o +drm-$(CONFIG_OF) += drm_of.o drm-usb-y := drm_usb.o diff --git a/drivers/gpu/drm/drm_of.c b/drivers/gpu/drm/drm_of.c new file mode 100644 index 000000000000..16150a00c237 --- /dev/null +++ b/drivers/gpu/drm/drm_of.c @@ -0,0 +1,67 @@ +#include +#include +#include +#include +#include +#include + +/** + * drm_crtc_port_mask - find the mask of a registered CRTC by port OF node + * @dev: DRM device + * @port: port OF node + * + * Given a port OF node, return the possible mask of the corresponding + * CRTC within a device's list of CRTCs. Returns zero if not found. + */ +static uint32_t drm_crtc_port_mask(struct drm_device *dev, + struct device_node *port) +{ + unsigned int index = 0; + struct drm_crtc *tmp; + + list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) { + if (tmp->port == port) + return 1 << index; + + index++; + } + + return 0; +} + +/** + * drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port + * @dev: DRM device + * @port: encoder port to scan for endpoints + * + * Scan all endpoints attached to a port, locate their attached CRTCs, + * and generate the DRM mask of CRTCs which may be attached to this + * encoder. + * + * See Documentation/devicetree/bindings/graph.txt for the bindings. + */ +uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + struct device_node *remote_port, *ep = NULL; + uint32_t possible_crtcs = 0; + + do { + ep = of_graph_get_next_endpoint(port, ep); + if (!ep) + break; + + remote_port = of_graph_get_remote_port(ep); + if (!remote_port) { + of_node_put(ep); + return 0; + } + + possible_crtcs |= drm_crtc_port_mask(dev, remote_port); + + of_node_put(remote_port); + } while (1); + + return possible_crtcs; +} +EXPORT_SYMBOL(drm_of_find_possible_crtcs); diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 251b75e6bf7a..6a94909f1ca9 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -41,6 +41,7 @@ struct drm_framebuffer; struct drm_object_properties; struct drm_file; struct drm_clip_rect; +struct device_node; #define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 @@ -314,6 +315,7 @@ struct drm_crtc_funcs { */ struct drm_crtc { struct drm_device *dev; + struct device_node *port; struct list_head head; /** diff --git a/include/drm/drm_of.h b/include/drm/drm_of.h new file mode 100644 index 000000000000..2441f7112074 --- /dev/null +++ b/include/drm/drm_of.h @@ -0,0 +1,18 @@ +#ifndef __DRM_OF_H__ +#define __DRM_OF_H__ + +struct drm_device; +struct device_node; + +#ifdef CONFIG_OF +extern uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port); +#else +static inline uint32_t drm_of_find_possible_crtcs(struct drm_device *dev, + struct device_node *port) +{ + return 0; +} +#endif + +#endif /* __DRM_OF_H__ */ From 0101fd0043075a2855ea98f07e6f2c7a72a5cbab Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 4 Feb 2014 11:57:06 +0000 Subject: [PATCH 10/14] drm/armada: convert to componentized support Convert the Armada DRM driver to use the component helpers, which will permit us to clean up the driver and move towards an implementation which is compatible with a DT description of the hardware. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_drv.c | 155 ++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 3995be3c686b..2b6fc6cf62f2 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -6,7 +6,9 @@ * published by the Free Software Foundation. */ #include +#include #include +#include #include #include #include "armada_crtc.h" @@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = { }; #endif +static bool is_componentized(struct device *dev) +{ + return dev->of_node || dev->platform_data; +} + static void armada_drm_unref_work(struct work_struct *work) { struct armada_private *priv = @@ -166,26 +173,35 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) goto err_kms; } + if (is_componentized(dev->dev)) { + ret = component_bind_all(dev->dev, dev); + if (ret) + goto err_kms; + } else { #ifdef CONFIG_DRM_ARMADA_TDA1998X - ret = armada_drm_connector_slave_create(dev, &tda19988_config); - if (ret) - goto err_kms; + ret = armada_drm_connector_slave_create(dev, &tda19988_config); + if (ret) + goto err_kms; #endif + } ret = drm_vblank_init(dev, dev->mode_config.num_crtc); if (ret) - goto err_kms; + goto err_comp; dev->vblank_disable_allowed = 1; ret = armada_fbdev_init(dev); if (ret) - goto err_kms; + goto err_comp; drm_kms_helper_poll_init(dev); return 0; + err_comp: + if (is_componentized(dev->dev)) + component_unbind_all(dev->dev, dev); err_kms: drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); @@ -200,6 +216,10 @@ static int armada_drm_unload(struct drm_device *dev) drm_kms_helper_poll_fini(dev); armada_fbdev_fini(dev); + + if (is_componentized(dev->dev)) + component_unbind_all(dev->dev, dev); + drm_mode_config_cleanup(dev); drm_mm_takedown(&priv->linear); flush_work(&priv->fb_unref_work); @@ -314,14 +334,135 @@ static struct drm_driver armada_drm_driver = { .fops = &armada_drm_fops, }; +static int armada_drm_bind(struct device *dev) +{ + return drm_platform_init(&armada_drm_driver, to_platform_device(dev)); +} + +static void armada_drm_unbind(struct device *dev) +{ + drm_put_dev(dev_get_drvdata(dev)); +} + +static int compare_of(struct device *dev, void *data) +{ + return dev->of_node == data; +} + +static int compare_dev_name(struct device *dev, void *data) +{ + const char *name = data; + return !strcmp(dev_name(dev), name); +} + +static void armada_add_endpoints(struct device *dev, + struct component_match **match, struct device_node *port) +{ + struct device_node *ep, *remote; + + for_each_child_of_node(port, ep) { + remote = of_graph_get_remote_port_parent(ep); + if (!remote || !of_device_is_available(remote)) { + of_node_put(remote); + continue; + } else if (!of_device_is_available(remote->parent)) { + dev_warn(dev, "parent device of %s is not available\n", + remote->full_name); + of_node_put(remote); + continue; + } + + component_match_add(dev, match, compare_of, remote); + of_node_put(remote); + } +} + +static int armada_drm_find_components(struct device *dev, + struct component_match **match) +{ + struct device_node *port; + int i; + + if (dev->of_node) { + struct device_node *np = dev->of_node; + + for (i = 0; ; i++) { + port = of_parse_phandle(np, "ports", i); + if (!port) + break; + + component_match_add(dev, match, compare_of, port); + of_node_put(port); + } + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + for (i = 0; ; i++) { + port = of_parse_phandle(np, "ports", i); + if (!port) + break; + + armada_add_endpoints(dev, match, port); + of_node_put(port); + } + } else if (dev->platform_data) { + char **devices = dev->platform_data; + struct device *d; + + for (i = 0; devices[i]; i++) + component_match_add(dev, match, compare_dev_name, + devices[i]); + + if (i == 0) { + dev_err(dev, "missing 'ports' property\n"); + return -ENODEV; + } + + for (i = 0; devices[i]; i++) { + d = bus_find_device_by_name(&platform_bus_type, NULL, + devices[i]); + if (d && d->of_node) { + for_each_child_of_node(d->of_node, port) + armada_add_endpoints(dev, match, port); + } + put_device(d); + } + } + + return 0; +} + +static const struct component_master_ops armada_master_ops = { + .bind = armada_drm_bind, + .unbind = armada_drm_unbind, +}; + static int armada_drm_probe(struct platform_device *pdev) { - return drm_platform_init(&armada_drm_driver, pdev); + if (is_componentized(&pdev->dev)) { + struct component_match *match = NULL; + int ret; + + ret = armada_drm_find_components(&pdev->dev, &match); + if (ret < 0) + return ret; + + return component_master_add_with_match(&pdev->dev, + &armada_master_ops, match); + } else { + return drm_platform_init(&armada_drm_driver, pdev); + } } static int armada_drm_remove(struct platform_device *pdev) { - drm_put_dev(platform_get_drvdata(pdev)); + if (is_componentized(&pdev->dev)) + component_master_del(&pdev->dev, &armada_master_ops); + else + drm_put_dev(platform_get_drvdata(pdev)); return 0; } From fe42487284efc584a43e75a835881bff2cf21b6a Mon Sep 17 00:00:00 2001 From: Russell King Date: Sat, 5 Jul 2014 11:16:58 +0100 Subject: [PATCH 11/14] drm/armada: update Armada 510 (Dove) to use "ext_ref_clk1" as the clock Remove the underscore between "clk" and "1" so that we match the name of the clock given in the documentation. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_510.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/armada/armada_510.c b/drivers/gpu/drm/armada/armada_510.c index 1bcf1a8c1915..ad3d2ebf95c9 100644 --- a/drivers/gpu/drm/armada/armada_510.c +++ b/drivers/gpu/drm/armada/armada_510.c @@ -19,7 +19,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev) { struct clk *clk; - clk = devm_clk_get(dev, "ext_ref_clk_1"); + clk = devm_clk_get(dev, "ext_ref_clk1"); if (IS_ERR(clk)) return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk); From ad49579adfd4b8097b391f838b62b5a6fa346a4a Mon Sep 17 00:00:00 2001 From: Russell King Date: Mon, 30 Jun 2014 15:38:18 +0100 Subject: [PATCH 12/14] dt-bindings: add Marvell Dove LCD controller documentation Add the Marvell Dove LCD controller DT binding documentation. The clock names used here are intentionally taken from the specification for the Dove SoC. Signed-off-by: Russell King --- .../bindings/drm/armada/marvell,dove-lcd.txt | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt diff --git a/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt b/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt new file mode 100644 index 000000000000..46525ea3e646 --- /dev/null +++ b/Documentation/devicetree/bindings/drm/armada/marvell,dove-lcd.txt @@ -0,0 +1,30 @@ +Device Tree bindings for Armada DRM CRTC driver + +Required properties: + - compatible: value should be "marvell,dove-lcd". + - reg: base address and size of the LCD controller + - interrupts: single interrupt number for the LCD controller + - port: video output port with endpoints, as described by graph.txt + +Optional properties: + + - clocks: as described by clock-bindings.txt + - clock-names: as described by clock-bindings.txt + "axiclk" - axi bus clock for pixel clock + "plldivider" - pll divider clock for pixel clock + "ext_ref_clk0" - external clock 0 for pixel clock + "ext_ref_clk1" - external clock 1 for pixel clock + +Note: all clocks are optional but at least one must be specified. +Further clocks may be added in the future according to requirements of +different SoCs. + +Example: + + lcd0: lcd-controller@820000 { + compatible = "marvell,dove-lcd"; + reg = <0x820000 0x1000>; + interrupts = <47>; + clocks = <&si5351 0>; + clock-names = "ext_ref_clk_1"; + }; From d8c96083cf5e4a910c20836414e8a06ebcf07317 Mon Sep 17 00:00:00 2001 From: Russell King Date: Tue, 22 Apr 2014 11:10:15 +0100 Subject: [PATCH 13/14] drm/armada: permit CRTCs to be registered as separate devices Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 113 ++++++++++++++++++++++++--- drivers/gpu/drm/armada/armada_crtc.h | 6 +- drivers/gpu/drm/armada/armada_drv.c | 17 +++- 3 files changed, 122 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index afa497489000..0642b5cfa563 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -7,6 +7,9 @@ * published by the Free Software Foundation. */ #include +#include +#include +#include #include #include #include "armada_crtc.h" @@ -1046,19 +1049,19 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) return 0; } -int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, - int irq, const struct armada_variant *variant) +int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, + struct resource *res, int irq, const struct armada_variant *variant) { - struct armada_private *priv = dev->dev_private; + struct armada_private *priv = drm->dev_private; struct armada_crtc *dcrtc; void __iomem *base; int ret; - ret = armada_drm_crtc_create_properties(dev); + ret = armada_drm_crtc_create_properties(drm); if (ret) return ret; - base = devm_request_and_ioremap(dev->dev, res); + base = devm_request_and_ioremap(dev, res); if (!base) { DRM_ERROR("failed to ioremap register\n"); return -ENOMEM; @@ -1070,9 +1073,12 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, return -ENOMEM; } + if (dev != drm->dev) + dev_set_drvdata(dev, dcrtc); + dcrtc->variant = variant; dcrtc->base = base; - dcrtc->num = dev->mode_config.num_crtc; + dcrtc->num = drm->mode_config.num_crtc; dcrtc->clk = ERR_PTR(-EINVAL); dcrtc->csc_yuv_mode = CSC_AUTO; dcrtc->csc_rgb_mode = CSC_AUTO; @@ -1105,7 +1111,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, } if (dcrtc->variant->init) { - ret = dcrtc->variant->init(dcrtc, dev->dev); + ret = dcrtc->variant->init(dcrtc, dev); if (ret) { kfree(dcrtc); return ret; @@ -1117,7 +1123,7 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, priv->dcrtc[dcrtc->num] = dcrtc; - drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs); + drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs); drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, @@ -1125,5 +1131,94 @@ int armada_drm_crtc_create(struct drm_device *dev, struct resource *res, drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, dcrtc->csc_rgb_mode); - return armada_overlay_plane_create(dev, 1 << dcrtc->num); + return armada_overlay_plane_create(drm, 1 << dcrtc->num); } + +static int +armada_lcd_bind(struct device *dev, struct device *master, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int irq = platform_get_irq(pdev, 0); + const struct armada_variant *variant; + + if (irq < 0) + return irq; + + if (!dev->of_node) { + const struct platform_device_id *id; + + id = platform_get_device_id(pdev); + if (!id) + return -ENXIO; + + variant = (const struct armada_variant *)id->driver_data; + } else { + const struct of_device_id *match; + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match) + return -ENXIO; + + variant = match->data; + } + + return armada_drm_crtc_create(drm, dev, res, irq, variant); +} + +static void +armada_lcd_unbind(struct device *dev, struct device *master, void *data) +{ + struct armada_crtc *dcrtc = dev_get_drvdata(dev); + + armada_drm_crtc_destroy(&dcrtc->crtc); +} + +static const struct component_ops armada_lcd_ops = { + .bind = armada_lcd_bind, + .unbind = armada_lcd_unbind, +}; + +static int armada_lcd_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &armada_lcd_ops); +} + +static int armada_lcd_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &armada_lcd_ops); + return 0; +} + +static struct of_device_id armada_lcd_of_match[] = { + { + .compatible = "marvell,dove-lcd", + .data = &armada510_ops, + }, + {} +}; +MODULE_DEVICE_TABLE(of, armada_lcd_of_match); + +static const struct platform_device_id armada_lcd_platform_ids[] = { + { + .name = "armada-lcd", + .driver_data = (unsigned long)&armada510_ops, + }, { + .name = "armada-510-lcd", + .driver_data = (unsigned long)&armada510_ops, + }, + { }, +}; +MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids); + +struct platform_driver armada_lcd_platform_driver = { + .probe = armada_lcd_probe, + .remove = armada_lcd_remove, + .driver = { + .name = "armada-lcd", + .owner = THIS_MODULE, + .of_match_table = armada_lcd_of_match, + }, + .id_table = armada_lcd_platform_ids, +}; diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 73efcfcdf814..0d9a80392b1e 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -75,12 +75,14 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) -int armada_drm_crtc_create(struct drm_device *, struct resource *, int, - const struct armada_variant *); +int armada_drm_crtc_create(struct drm_device *, struct device *, + struct resource *, int, const struct armada_variant *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); void armada_drm_crtc_enable_irq(struct armada_crtc *, u32); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *); +extern struct platform_driver armada_lcd_platform_driver; + #endif diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index 2b6fc6cf62f2..f237f44d8b6d 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -115,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) return -EINVAL; } - if (!res[0] || !mem) + if (!mem) return -ENXIO; if (!devm_request_mem_region(dev->dev, mem->start, @@ -168,7 +168,8 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) if (irq < 0) goto err_kms; - ret = armada_drm_crtc_create(dev, res[n], irq, variant); + ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq, + variant); if (ret) goto err_kms; } @@ -490,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = { static int __init armada_drm_init(void) { + int ret; + armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls); - return platform_driver_register(&armada_drm_platform_driver); + + ret = platform_driver_register(&armada_lcd_platform_driver); + if (ret) + return ret; + ret = platform_driver_register(&armada_drm_platform_driver); + if (ret) + platform_driver_unregister(&armada_lcd_platform_driver); + return ret; } module_init(armada_drm_init); static void __exit armada_drm_exit(void) { platform_driver_unregister(&armada_drm_platform_driver); + platform_driver_unregister(&armada_lcd_platform_driver); } module_exit(armada_drm_exit); From 9611cb93fa65dde199f4f888bd034ffc80c7adf0 Mon Sep 17 00:00:00 2001 From: Russell King Date: Sun, 15 Jun 2014 11:21:23 +0100 Subject: [PATCH 14/14] drm/armada: register crtc with port Register the CRTC with the port node so that the DRM OF helpers can find the appropriate CRTC. This is important so that encoders can identify their corresponding possible CRTCs. Signed-off-by: Russell King --- drivers/gpu/drm/armada/armada_crtc.c | 21 +++++++++++++++++++-- drivers/gpu/drm/armada/armada_crtc.h | 4 +++- drivers/gpu/drm/armada/armada_drv.c | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c index 0642b5cfa563..3f620e21e06b 100644 --- a/drivers/gpu/drm/armada/armada_crtc.c +++ b/drivers/gpu/drm/armada/armada_crtc.c @@ -910,6 +910,8 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA); + of_node_put(dcrtc->crtc.port); + kfree(dcrtc); } @@ -1050,7 +1052,8 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) } int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, - struct resource *res, int irq, const struct armada_variant *variant) + struct resource *res, int irq, const struct armada_variant *variant, + struct device_node *port) { struct armada_private *priv = drm->dev_private; struct armada_crtc *dcrtc; @@ -1123,6 +1126,7 @@ int armada_drm_crtc_create(struct drm_device *drm, struct device *dev, priv->dcrtc[dcrtc->num] = dcrtc; + dcrtc->crtc.port = port; drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs); drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); @@ -1142,6 +1146,7 @@ armada_lcd_bind(struct device *dev, struct device *master, void *data) struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); const struct armada_variant *variant; + struct device_node *port = NULL; if (irq < 0) return irq; @@ -1156,15 +1161,27 @@ armada_lcd_bind(struct device *dev, struct device *master, void *data) variant = (const struct armada_variant *)id->driver_data; } else { const struct of_device_id *match; + struct device_node *np, *parent = dev->of_node; match = of_match_device(dev->driver->of_match_table, dev); if (!match) return -ENXIO; + np = of_get_child_by_name(parent, "ports"); + if (np) + parent = np; + port = of_get_child_by_name(parent, "port"); + of_node_put(np); + if (!port) { + dev_err(dev, "no port node found in %s\n", + parent->full_name); + return -ENXIO; + } + variant = match->data; } - return armada_drm_crtc_create(drm, dev, res, irq, variant); + return armada_drm_crtc_create(drm, dev, res, irq, variant, port); } static void diff --git a/drivers/gpu/drm/armada/armada_crtc.h b/drivers/gpu/drm/armada/armada_crtc.h index 0d9a80392b1e..98102a5a9af5 100644 --- a/drivers/gpu/drm/armada/armada_crtc.h +++ b/drivers/gpu/drm/armada/armada_crtc.h @@ -75,8 +75,10 @@ struct armada_crtc { }; #define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc) +struct device_node; int armada_drm_crtc_create(struct drm_device *, struct device *, - struct resource *, int, const struct armada_variant *); + struct resource *, int, const struct armada_variant *, + struct device_node *); void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int); void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int); void armada_drm_crtc_disable_irq(struct armada_crtc *, u32); diff --git a/drivers/gpu/drm/armada/armada_drv.c b/drivers/gpu/drm/armada/armada_drv.c index f237f44d8b6d..e2d5792b140f 100644 --- a/drivers/gpu/drm/armada/armada_drv.c +++ b/drivers/gpu/drm/armada/armada_drv.c @@ -169,7 +169,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) goto err_kms; ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq, - variant); + variant, NULL); if (ret) goto err_kms; }