Commit 8a105aaa authored by Dave Airlie's avatar Dave Airlie

Merge branch 'drm-armada-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm into drm-next

Merge armada changes, I've confirmed the componenet changes are same as in Greg's tree.
* 'drm-armada-devel' of git://ftp.arm.linux.org.uk/~rmk/linux-arm:
  drm/armada: register crtc with port
  drm/armada: permit CRTCs to be registered as separate devices
  dt-bindings: add Marvell Dove LCD controller documentation
  drm/armada: update Armada 510 (Dove) to use "ext_ref_clk1" as the clock
  drm/armada: convert to componentized support
  drm: add of_graph endpoint helper to find possible CRTCs
  component: fix bug with legacy API
  drm/armada: make variant a CRTC thing
  drm/armada: move variant initialisation to CRTC init
  drm/armada: use number of CRTCs registered
  drm/armada: move IRQ handling into CRTC
  component: add support for component match array
  component: ignore multiple additions of the same component
  component: fix missed cleanup in case of devres failure
parents 7296c849 9611cb93
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";
};
...@@ -18,6 +18,15 @@ ...@@ -18,6 +18,15 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/slab.h> #include <linux/slab.h>
struct component_match {
size_t alloc;
size_t num;
struct {
void *data;
int (*fn)(struct device *, void *);
} compare[0];
};
struct master { struct master {
struct list_head node; struct list_head node;
struct list_head components; struct list_head components;
...@@ -25,6 +34,7 @@ struct master { ...@@ -25,6 +34,7 @@ struct master {
const struct component_master_ops *ops; const struct component_master_ops *ops;
struct device *dev; struct device *dev;
struct component_match *match;
}; };
struct component { struct component {
...@@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c) ...@@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c)
c->master = NULL; 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 component_master_add_child(struct master *master,
int (*compare)(struct device *, void *), void *compare_data) int (*compare)(struct device *, void *), void *compare_data)
{ {
...@@ -76,11 +91,12 @@ int component_master_add_child(struct master *master, ...@@ -76,11 +91,12 @@ int component_master_add_child(struct master *master,
int ret = -ENXIO; int ret = -ENXIO;
list_for_each_entry(c, &component_list, node) { list_for_each_entry(c, &component_list, node) {
if (c->master) if (c->master && c->master != master)
continue; continue;
if (compare(c->dev, compare_data)) { if (compare(c->dev, compare_data)) {
component_attach_master(master, c); if (!c->master)
component_attach_master(master, c);
ret = 0; ret = 0;
break; break;
} }
...@@ -90,6 +106,34 @@ int component_master_add_child(struct master *master, ...@@ -90,6 +106,34 @@ int component_master_add_child(struct master *master,
} }
EXPORT_SYMBOL_GPL(component_master_add_child); 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 */ /* Detach all attached components from this master */
static void master_remove_components(struct master *master) static void master_remove_components(struct master *master)
{ {
...@@ -113,44 +157,44 @@ static void master_remove_components(struct master *master) ...@@ -113,44 +157,44 @@ static void master_remove_components(struct master *master)
static int try_to_bring_up_master(struct master *master, static int try_to_bring_up_master(struct master *master,
struct component *component) struct component *component)
{ {
int ret = 0; int ret;
if (!master->bound) { if (master->bound)
/* return 0;
* 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 (component && component->master != master) { /*
master_remove_components(master); * Search the list of components, looking for components that
ret = 0; * belong to this master, and attach them to the master.
goto out; */
} if (find_components(master)) {
/* Failed to find all components */
ret = 0;
goto out;
}
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { if (component && component->master != master) {
ret = -ENOMEM; ret = 0;
goto out; goto out;
} }
/* Found all components */ if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
ret = master->ops->bind(master->dev); ret = -ENOMEM;
if (ret < 0) { goto out;
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; /* Found all components */
ret = 1; 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: out:
master_remove_components(master);
return ret; return ret;
} }
...@@ -180,18 +224,89 @@ static void take_down_master(struct master *master) ...@@ -180,18 +224,89 @@ static void take_down_master(struct master *master)
master_remove_components(master); master_remove_components(master);
} }
int component_master_add(struct device *dev, static size_t component_match_size(size_t num)
const struct component_master_ops *ops) {
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; struct master *master;
int ret; int ret;
if (ops->add_components && match)
return -EINVAL;
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); master = kzalloc(sizeof(*master), GFP_KERNEL);
if (!master) if (!master)
return -ENOMEM; return -ENOMEM;
master->dev = dev; master->dev = dev;
master->ops = ops; master->ops = ops;
master->match = match;
INIT_LIST_HEAD(&master->components); INIT_LIST_HEAD(&master->components);
/* Add to the list of available masters. */ /* Add to the list of available masters. */
...@@ -209,6 +324,13 @@ int component_master_add(struct device *dev, ...@@ -209,6 +324,13 @@ int component_master_add(struct device *dev,
return ret < 0 ? ret : 0; 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); EXPORT_SYMBOL_GPL(component_master_add);
void component_master_del(struct device *dev, void component_master_del(struct device *dev,
......
...@@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o ...@@ -20,6 +20,7 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
drm-$(CONFIG_PCI) += ati_pcigart.o drm-$(CONFIG_PCI) += ati_pcigart.o
drm-$(CONFIG_DRM_PANEL) += drm_panel.o drm-$(CONFIG_DRM_PANEL) += drm_panel.o
drm-$(CONFIG_OF) += drm_of.o
drm-usb-y := drm_usb.o drm-usb-y := drm_usb.o
......
...@@ -15,20 +15,19 @@ ...@@ -15,20 +15,19 @@
#include "armada_drm.h" #include "armada_drm.h"
#include "armada_hw.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) clk = devm_clk_get(dev, "ext_ref_clk1");
priv->extclk[0] = ERR_PTR(-EPROBE_DEFER); 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 */ /* Lower the watermark so to eliminate jitter at higher bandwidths */
armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F); armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
return 0; return 0;
} }
...@@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc) ...@@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc)
static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
const struct drm_display_mode *mode, uint32_t *sclk) const struct drm_display_mode *mode, uint32_t *sclk)
{ {
struct armada_private *priv = dcrtc->crtc.dev->dev_private; struct clk *clk = dcrtc->extclk[0];
struct clk *clk = priv->extclk[0];
int ret; int ret;
if (dcrtc->num == 1) if (dcrtc->num == 1)
...@@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc, ...@@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
const struct armada_variant armada510_ops = { const struct armada_variant armada510_ops = {
.has_spu_adv_reg = true, .has_spu_adv_reg = true,
.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND, .spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
.init = armada510_init, .init = armada510_crtc_init,
.crtc_init = armada510_crtc_init, .compute_clock = armada510_crtc_compute_clock,
.crtc_compute_clock = armada510_crtc_compute_clock,
}; };
...@@ -7,6 +7,9 @@ ...@@ -7,6 +7,9 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include "armada_crtc.h" #include "armada_crtc.h"
...@@ -332,24 +335,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc) ...@@ -332,24 +335,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc)
static bool armada_drm_crtc_mode_fixup(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) 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); struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
int ret; int ret;
/* We can't do interlaced modes if we don't have the SPU_ADV_REG */ /* 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) adj->flags & DRM_MODE_FLAG_INTERLACE)
return false; return false;
/* Check whether the display mode is possible */ /* 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) if (ret)
return false; return false;
return true; 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; struct armada_vbl_event *e, *n;
void __iomem *base = dcrtc->base; void __iomem *base = dcrtc->base;
...@@ -410,6 +412,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat) ...@@ -410,6 +412,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 */ /* These are locked by dev->vbl_lock */
void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask) void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
{ {
...@@ -470,7 +493,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, ...@@ -470,7 +493,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *adj, struct drm_display_mode *mode, struct drm_display_mode *adj,
int x, int y, struct drm_framebuffer *old_fb) 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_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct armada_regs regs[17]; struct armada_regs regs[17];
uint32_t lm, rm, tm, bm, val, sclk; uint32_t lm, rm, tm, bm, val, sclk;
...@@ -515,7 +537,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, ...@@ -515,7 +537,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
} }
/* Now compute the divider for real */ /* 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 */ /* Ensure graphic fifo is enabled */
armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1); armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
...@@ -537,7 +559,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, ...@@ -537,7 +559,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
dcrtc->v[1].spu_v_porch = tm << 16 | bm; dcrtc->v[1].spu_v_porch = tm << 16 | bm;
val = adj->crtc_hsync_start; val = adj->crtc_hsync_start;
dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
priv->variant->spu_adv_reg; dcrtc->variant->spu_adv_reg;
if (interlaced) { if (interlaced) {
/* Odd interlaced frame */ /* Odd interlaced frame */
...@@ -546,7 +568,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, ...@@ -546,7 +568,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; dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
val = adj->crtc_hsync_start - adj->crtc_htotal / 2; val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN | dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
priv->variant->spu_adv_reg; dcrtc->variant->spu_adv_reg;
} else { } else {
dcrtc->v[0] = dcrtc->v[1]; dcrtc->v[0] = dcrtc->v[1];
} }
...@@ -561,7 +583,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc, ...@@ -561,7 +583,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, armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
LCD_SPUT_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, armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF | ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
ADV_VSYNCOFFEN, LCD_SPU_ADV_REG); ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
...@@ -805,12 +827,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc, ...@@ -805,12 +827,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct armada_private *priv = crtc->dev->dev_private;
struct armada_gem_object *obj = NULL; struct armada_gem_object *obj = NULL;
int ret; int ret;
/* If no cursor support, replicate drm's return value */ /* 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; return -ENXIO;
if (handle && w > 0 && h > 0) { if (handle && w > 0 && h > 0) {
...@@ -858,11 +879,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) ...@@ -858,11 +879,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc); struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
struct armada_private *priv = crtc->dev->dev_private;
int ret; int ret;
/* If no cursor support, replicate drm's return value */ /* 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; return -EFAULT;
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
...@@ -888,6 +908,10 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc) ...@@ -888,6 +908,10 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
if (!IS_ERR(dcrtc->clk)) if (!IS_ERR(dcrtc->clk))
clk_disable_unprepare(dcrtc->clk); clk_disable_unprepare(dcrtc->clk);
writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA);
of_node_put(dcrtc->crtc.port);
kfree(dcrtc); kfree(dcrtc);
} }
...@@ -1027,19 +1051,20 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev) ...@@ -1027,19 +1051,20 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
return 0; return 0;
} }
int armada_drm_crtc_create(struct drm_device *dev, unsigned num, int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
struct resource *res) struct resource *res, int irq, const struct armada_variant *variant,
struct device_node *port)
{ {
struct armada_private *priv = dev->dev_private; struct armada_private *priv = drm->dev_private;
struct armada_crtc *dcrtc; struct armada_crtc *dcrtc;
void __iomem *base; void __iomem *base;
int ret; int ret;
ret = armada_drm_crtc_create_properties(dev); ret = armada_drm_crtc_create_properties(drm);
if (ret) if (ret)
return ret; return ret;
base = devm_request_and_ioremap(dev->dev, res); base = devm_request_and_ioremap(dev, res);
if (!base) { if (!base) {
DRM_ERROR("failed to ioremap register\n"); DRM_ERROR("failed to ioremap register\n");
return -ENOMEM; return -ENOMEM;
...@@ -1051,8 +1076,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, ...@@ -1051,8 +1076,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
return -ENOMEM; return -ENOMEM;
} }
if (dev != drm->dev)
dev_set_drvdata(dev, dcrtc);
dcrtc->variant = variant;
dcrtc->base = base; dcrtc->base = base;
dcrtc->num = num; dcrtc->num = drm->mode_config.num_crtc;
dcrtc->clk = ERR_PTR(-EINVAL); dcrtc->clk = ERR_PTR(-EINVAL);
dcrtc->csc_yuv_mode = CSC_AUTO; dcrtc->csc_yuv_mode = CSC_AUTO;
dcrtc->csc_rgb_mode = CSC_AUTO; dcrtc->csc_rgb_mode = CSC_AUTO;
...@@ -1074,9 +1103,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, ...@@ -1074,9 +1103,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1); CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1); writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN); 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) { if (dcrtc->variant->init) {
ret = priv->variant->crtc_init(dcrtc); ret = dcrtc->variant->init(dcrtc, dev);
if (ret) { if (ret) {
kfree(dcrtc); kfree(dcrtc);
return ret; return ret;
...@@ -1088,7 +1126,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, ...@@ -1088,7 +1126,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
priv->dcrtc[dcrtc->num] = dcrtc; priv->dcrtc[dcrtc->num] = dcrtc;
drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs); dcrtc->crtc.port = port;
drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs); drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop, drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
...@@ -1096,5 +1135,107 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num, ...@@ -1096,5 +1135,107 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop, drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
dcrtc->csc_rgb_mode); 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;
struct device_node *port = NULL;
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;
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, port);
}
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,
};
...@@ -32,12 +32,15 @@ struct armada_regs { ...@@ -32,12 +32,15 @@ struct armada_regs {
armada_reg_queue_mod(_r, _i, 0, 0, ~0) armada_reg_queue_mod(_r, _i, 0, 0, ~0)
struct armada_frame_work; struct armada_frame_work;
struct armada_variant;
struct armada_crtc { struct armada_crtc {
struct drm_crtc crtc; struct drm_crtc crtc;
const struct armada_variant *variant;
unsigned num; unsigned num;
void __iomem *base; void __iomem *base;
struct clk *clk; struct clk *clk;
struct clk *extclk[2];
struct { struct {
uint32_t spu_v_h_total; uint32_t spu_v_h_total;
uint32_t spu_v_porch; uint32_t spu_v_porch;
...@@ -72,12 +75,16 @@ struct armada_crtc { ...@@ -72,12 +75,16 @@ struct armada_crtc {
}; };
#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, 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 *); struct device_node;
int armada_drm_crtc_create(struct drm_device *, struct device *,
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_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_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_disable_irq(struct armada_crtc *, u32);
void armada_drm_crtc_enable_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 *); void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
extern struct platform_driver armada_lcd_platform_driver;
#endif #endif
...@@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *, ...@@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
struct armada_private; struct armada_private;
struct armada_variant { struct armada_variant {
bool has_spu_adv_reg; bool has_spu_adv_reg;
uint32_t spu_adv_reg; uint32_t spu_adv_reg;
int (*init)(struct armada_private *, struct device *); int (*init)(struct armada_crtc *, struct device *);
int (*crtc_init)(struct armada_crtc *); int (*compute_clock)(struct armada_crtc *,
int (*crtc_compute_clock)(struct armada_crtc *, const struct drm_display_mode *,
const struct drm_display_mode *, uint32_t *);
uint32_t *);
}; };
/* Variant ops */ /* Variant ops */
extern const struct armada_variant armada510_ops; extern const struct armada_variant armada510_ops;
struct armada_private { struct armada_private {
const struct armada_variant *variant;
struct work_struct fb_unref_work; struct work_struct fb_unref_work;
DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8); DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
struct drm_fb_helper *fbdev; struct drm_fb_helper *fbdev;
struct armada_crtc *dcrtc[2]; struct armada_crtc *dcrtc[2];
struct drm_mm linear; struct drm_mm linear;
struct clk *extclk[2];
struct drm_property *csc_yuv_prop; struct drm_property *csc_yuv_prop;
struct drm_property *csc_rgb_prop; struct drm_property *csc_rgb_prop;
struct drm_property *colorkey_prop; struct drm_property *colorkey_prop;
......
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/component.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_graph.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include "armada_crtc.h" #include "armada_crtc.h"
...@@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = { ...@@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = {
}; };
#endif #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) static void armada_drm_unref_work(struct work_struct *work)
{ {
struct armada_private *priv = struct armada_private *priv =
...@@ -85,6 +92,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev, ...@@ -85,6 +92,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
static int armada_drm_load(struct drm_device *dev, unsigned long flags) static int armada_drm_load(struct drm_device *dev, unsigned long flags)
{ {
const struct platform_device_id *id; const struct platform_device_id *id;
const struct armada_variant *variant;
struct armada_private *priv; struct armada_private *priv;
struct resource *res[ARRAY_SIZE(priv->dcrtc)]; struct resource *res[ARRAY_SIZE(priv->dcrtc)];
struct resource *mem = NULL; struct resource *mem = NULL;
...@@ -107,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -107,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
return -EINVAL; return -EINVAL;
} }
if (!res[0] || !mem) if (!mem)
return -ENXIO; return -ENXIO;
if (!devm_request_mem_region(dev->dev, mem->start, if (!devm_request_mem_region(dev->dev, mem->start,
...@@ -128,11 +136,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -128,11 +136,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
if (!id) if (!id)
return -ENXIO; return -ENXIO;
priv->variant = (struct armada_variant *)id->driver_data; variant = (const 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_WORK(&priv->fb_unref_work, armada_drm_unref_work);
INIT_KFIFO(priv->fb_unref); INIT_KFIFO(priv->fb_unref);
...@@ -155,40 +159,50 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -155,40 +159,50 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
/* Create all LCD controllers */ /* Create all LCD controllers */
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) { for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
int irq;
if (!res[n]) if (!res[n])
break; 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, dev->dev, res[n], irq,
variant, NULL);
if (ret) if (ret)
goto err_kms; 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 #ifdef CONFIG_DRM_ARMADA_TDA1998X
ret = armada_drm_connector_slave_create(dev, &tda19988_config); ret = armada_drm_connector_slave_create(dev, &tda19988_config);
if (ret) if (ret)
goto err_kms; goto err_kms;
#endif #endif
}
ret = drm_vblank_init(dev, n); ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret)
goto err_kms;
ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
if (ret) if (ret)
goto err_kms; goto err_comp;
dev->vblank_disable_allowed = 1; dev->vblank_disable_allowed = 1;
ret = armada_fbdev_init(dev); ret = armada_fbdev_init(dev);
if (ret) if (ret)
goto err_irq; goto err_comp;
drm_kms_helper_poll_init(dev); drm_kms_helper_poll_init(dev);
return 0; return 0;
err_irq: err_comp:
drm_irq_uninstall(dev); if (is_componentized(dev->dev))
component_unbind_all(dev->dev, dev);
err_kms: err_kms:
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear); drm_mm_takedown(&priv->linear);
...@@ -203,7 +217,10 @@ static int armada_drm_unload(struct drm_device *dev) ...@@ -203,7 +217,10 @@ static int armada_drm_unload(struct drm_device *dev)
drm_kms_helper_poll_fini(dev); drm_kms_helper_poll_fini(dev);
armada_fbdev_fini(dev); armada_fbdev_fini(dev);
drm_irq_uninstall(dev);
if (is_componentized(dev->dev))
component_unbind_all(dev->dev, dev);
drm_mode_config_cleanup(dev); drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear); drm_mm_takedown(&priv->linear);
flush_work(&priv->fb_unref_work); flush_work(&priv->fb_unref_work);
...@@ -259,52 +276,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc) ...@@ -259,52 +276,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA); 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[] = { static struct drm_ioctl_desc armada_ioctls[] = {
DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl, DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
DRM_UNLOCKED), DRM_UNLOCKED),
...@@ -340,9 +311,6 @@ static struct drm_driver armada_drm_driver = { ...@@ -340,9 +311,6 @@ static struct drm_driver armada_drm_driver = {
.get_vblank_counter = drm_vblank_count, .get_vblank_counter = drm_vblank_count,
.enable_vblank = armada_drm_enable_vblank, .enable_vblank = armada_drm_enable_vblank,
.disable_vblank = armada_drm_disable_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 #ifdef CONFIG_DEBUG_FS
.debugfs_init = armada_drm_debugfs_init, .debugfs_init = armada_drm_debugfs_init,
.debugfs_cleanup = armada_drm_debugfs_cleanup, .debugfs_cleanup = armada_drm_debugfs_cleanup,
...@@ -362,19 +330,140 @@ static struct drm_driver armada_drm_driver = { ...@@ -362,19 +330,140 @@ static struct drm_driver armada_drm_driver = {
.desc = "Armada SoC DRM", .desc = "Armada SoC DRM",
.date = "20120730", .date = "20120730",
.driver_features = DRIVER_GEM | DRIVER_MODESET | .driver_features = DRIVER_GEM | DRIVER_MODESET |
DRIVER_HAVE_IRQ | DRIVER_PRIME, DRIVER_PRIME,
.ioctls = armada_ioctls, .ioctls = armada_ioctls,
.fops = &armada_drm_fops, .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) 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) 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; return 0;
} }
...@@ -402,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = { ...@@ -402,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = {
static int __init armada_drm_init(void) static int __init armada_drm_init(void)
{ {
int ret;
armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls); 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); module_init(armada_drm_init);
static void __exit armada_drm_exit(void) static void __exit armada_drm_exit(void)
{ {
platform_driver_unregister(&armada_drm_platform_driver); platform_driver_unregister(&armada_drm_platform_driver);
platform_driver_unregister(&armada_lcd_platform_driver);
} }
module_exit(armada_drm_exit); module_exit(armada_drm_exit);
......
#include <linux/export.h>
#include <linux/list.h>
#include <linux/of_graph.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_of.h>
/**
* 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);
...@@ -41,6 +41,7 @@ struct drm_framebuffer; ...@@ -41,6 +41,7 @@ struct drm_framebuffer;
struct drm_object_properties; struct drm_object_properties;
struct drm_file; struct drm_file;
struct drm_clip_rect; struct drm_clip_rect;
struct device_node;
#define DRM_MODE_OBJECT_CRTC 0xcccccccc #define DRM_MODE_OBJECT_CRTC 0xcccccccc
#define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0 #define DRM_MODE_OBJECT_CONNECTOR 0xc0c0c0c0
...@@ -314,6 +315,7 @@ struct drm_crtc_funcs { ...@@ -314,6 +315,7 @@ struct drm_crtc_funcs {
*/ */
struct drm_crtc { struct drm_crtc {
struct drm_device *dev; struct drm_device *dev;
struct device_node *port;
struct list_head head; struct list_head head;
/** /**
......
#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__ */
...@@ -29,4 +29,11 @@ void component_master_del(struct device *, ...@@ -29,4 +29,11 @@ void component_master_del(struct device *,
int component_master_add_child(struct master *master, int component_master_add_child(struct master *master,
int (*compare)(struct device *, void *), void *compare_data); 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 #endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment