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
This commit is contained in:
commit
8a105aaa25
|
@ -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";
|
||||
};
|
|
@ -18,6 +18,15 @@
|
|||
#include <linux/mutex.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 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 {
|
||||
|
@ -69,6 +79,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 +91,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;
|
||||
}
|
||||
|
@ -90,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)
|
||||
{
|
||||
|
@ -113,44 +157,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 (find_components(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;
|
||||
}
|
||||
|
@ -180,18 +224,89 @@ 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;
|
||||
|
||||
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)
|
||||
return -ENOMEM;
|
||||
|
||||
master->dev = dev;
|
||||
master->ops = ops;
|
||||
master->match = match;
|
||||
INIT_LIST_HEAD(&master->components);
|
||||
|
||||
/* Add to the list of available masters. */
|
||||
|
@ -209,6 +324,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,
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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_clk1");
|
||||
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,
|
||||
.init = armada510_crtc_init,
|
||||
.compute_clock = armada510_crtc_compute_clock,
|
||||
};
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include "armada_crtc.h"
|
||||
|
@ -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,
|
||||
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;
|
||||
|
||||
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 +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 */
|
||||
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,
|
|||
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;
|
||||
|
@ -515,7 +537,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);
|
||||
|
@ -537,7 +559,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 */
|
||||
|
@ -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;
|
||||
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];
|
||||
}
|
||||
|
@ -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,
|
||||
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);
|
||||
|
@ -805,12 +827,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) {
|
||||
|
@ -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 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);
|
||||
|
@ -888,6 +908,10 @@ 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);
|
||||
|
||||
of_node_put(dcrtc->crtc.port);
|
||||
|
||||
kfree(dcrtc);
|
||||
}
|
||||
|
||||
|
@ -1027,19 +1051,20 @@ 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 armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
||||
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;
|
||||
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;
|
||||
|
@ -1051,8 +1076,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (dev != drm->dev)
|
||||
dev_set_drvdata(dev, dcrtc);
|
||||
|
||||
dcrtc->variant = variant;
|
||||
dcrtc->base = base;
|
||||
dcrtc->num = num;
|
||||
dcrtc->num = drm->mode_config.num_crtc;
|
||||
dcrtc->clk = ERR_PTR(-EINVAL);
|
||||
dcrtc->csc_yuv_mode = CSC_AUTO;
|
||||
dcrtc->csc_rgb_mode = CSC_AUTO;
|
||||
|
@ -1074,9 +1103,18 @@ 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);
|
||||
|
||||
if (priv->variant->crtc_init) {
|
||||
ret = priv->variant->crtc_init(dcrtc);
|
||||
ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc",
|
||||
dcrtc);
|
||||
if (ret < 0) {
|
||||
kfree(dcrtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dcrtc->variant->init) {
|
||||
ret = dcrtc->variant->init(dcrtc, dev);
|
||||
if (ret) {
|
||||
kfree(dcrtc);
|
||||
return ret;
|
||||
|
@ -1088,7 +1126,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
|||
|
||||
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_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,
|
|||
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;
|
||||
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 {
|
|||
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;
|
||||
struct clk *extclk[2];
|
||||
struct {
|
||||
uint32_t spu_v_h_total;
|
||||
uint32_t spu_v_porch;
|
||||
|
@ -72,12 +75,16 @@ 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 *);
|
||||
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_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 *);
|
||||
|
||||
extern struct platform_driver armada_lcd_platform_driver;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -59,26 +59,23 @@ 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_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;
|
||||
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;
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#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 =
|
||||
|
@ -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)
|
||||
{
|
||||
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;
|
||||
|
@ -107,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,
|
||||
|
@ -128,11 +136,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;
|
||||
|
||||
ret = priv->variant->init(priv, dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
variant = (const struct armada_variant *)id->driver_data;
|
||||
|
||||
INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
|
||||
INIT_KFIFO(priv->fb_unref);
|
||||
|
@ -155,40 +159,50 @@ 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, dev->dev, res[n], irq,
|
||||
variant, NULL);
|
||||
if (ret)
|
||||
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, 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)
|
||||
goto err_kms;
|
||||
goto err_comp;
|
||||
|
||||
dev->vblank_disable_allowed = 1;
|
||||
|
||||
ret = armada_fbdev_init(dev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
goto err_comp;
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
drm_irq_uninstall(dev);
|
||||
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);
|
||||
|
@ -203,7 +217,10 @@ static int armada_drm_unload(struct drm_device *dev)
|
|||
|
||||
drm_kms_helper_poll_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_mm_takedown(&priv->linear);
|
||||
flush_work(&priv->fb_unref_work);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
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 +311,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,19 +330,140 @@ 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,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -402,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);
|
||||
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
#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;
|
|||
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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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__ */
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue