Commit 4dac3edf authored by Daniel Vetter's avatar Daniel Vetter

Merge remote-tracking branch 'airlied/drm-next' into drm-intel-next

Pull in drm-next with Dave's DP MST support so that I can merge some
conflicting patches which also touch the driver load sequencing around
interrupt handling.

Conflicts:
	drivers/gpu/drm/i915/intel_display.c
	drivers/gpu/drm/i915/intel_dp.c
Signed-off-by: default avatarDaniel Vetter <daniel.vetter@ffwll.ch>
parents 48777767 e05444be
...@@ -1610,7 +1610,7 @@ int max_width, max_height;</synopsis> ...@@ -1610,7 +1610,7 @@ int max_width, max_height;</synopsis>
The connector is then registered with a call to The connector is then registered with a call to
<function>drm_connector_init</function> with a pointer to the connector <function>drm_connector_init</function> with a pointer to the connector
functions and a connector type, and exposed through sysfs with a call to functions and a connector type, and exposed through sysfs with a call to
<function>drm_sysfs_connector_add</function>. <function>drm_connector_register</function>.
</para> </para>
<para> <para>
Supported connector types are Supported connector types are
...@@ -1768,7 +1768,7 @@ int max_width, max_height;</synopsis> ...@@ -1768,7 +1768,7 @@ int max_width, max_height;</synopsis>
(<function>drm_encoder_cleanup</function>) and connectors (<function>drm_encoder_cleanup</function>) and connectors
(<function>drm_connector_cleanup</function>). Furthermore, connectors (<function>drm_connector_cleanup</function>). Furthermore, connectors
that have been added to sysfs must be removed by a call to that have been added to sysfs must be removed by a call to
<function>drm_sysfs_connector_remove</function> before calling <function>drm_connector_unregister</function> before calling
<function>drm_connector_cleanup</function>. <function>drm_connector_cleanup</function>.
</para> </para>
<para> <para>
...@@ -1813,7 +1813,7 @@ void intel_crt_init(struct drm_device *dev) ...@@ -1813,7 +1813,7 @@ void intel_crt_init(struct drm_device *dev)
drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs); drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs); drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
drm_sysfs_connector_add(connector); drm_connector_register(connector);
}]]></programlisting> }]]></programlisting>
<para> <para>
In the example above (taken from the i915 driver), a CRTC, connector and In the example above (taken from the i915 driver), a CRTC, connector and
...@@ -2336,6 +2336,12 @@ void intel_crt_init(struct drm_device *dev) ...@@ -2336,6 +2336,12 @@ void intel_crt_init(struct drm_device *dev)
!Pdrivers/gpu/drm/drm_dp_helper.c dp helpers !Pdrivers/gpu/drm/drm_dp_helper.c dp helpers
!Iinclude/drm/drm_dp_helper.h !Iinclude/drm/drm_dp_helper.h
!Edrivers/gpu/drm/drm_dp_helper.c !Edrivers/gpu/drm/drm_dp_helper.c
</sect2>
<sect2>
<title>Display Port MST Helper Functions Reference</title>
!Pdrivers/gpu/drm/drm_dp_mst_topology.c dp mst helper
!Iinclude/drm/drm_dp_mst_helper.h
!Edrivers/gpu/drm/drm_dp_mst_topology.c
</sect2> </sect2>
<sect2> <sect2>
<title>EDID Helper Functions Reference</title> <title>EDID Helper Functions Reference</title>
......
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,11 +20,12 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o ...@@ -20,11 +20,12 @@ 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
drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \ drm_kms_helper-y := drm_crtc_helper.o drm_dp_helper.o drm_probe_helper.o \
drm_plane_helper.o drm_plane_helper.o drm_dp_mst_topology.o
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_FB_HELPER) += drm_fb_helper.o
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.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);
......
...@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh, ...@@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
return ret; return ret;
} }
static struct drm_fb_helper_funcs armada_fb_helper_funcs = { static const struct drm_fb_helper_funcs armada_fb_helper_funcs = {
.gamma_set = armada_drm_crtc_gamma_set, .gamma_set = armada_drm_crtc_gamma_set,
.gamma_get = armada_drm_crtc_gamma_get, .gamma_get = armada_drm_crtc_gamma_get,
.fb_probe = armada_fb_probe, .fb_probe = armada_fb_probe,
...@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev) ...@@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
priv->fbdev = fbh; priv->fbdev = fbh;
fbh->funcs = &armada_fb_helper_funcs; drm_fb_helper_prepare(dev, fbh, &armada_fb_helper_funcs);
ret = drm_fb_helper_init(dev, fbh, 1, 1); ret = drm_fb_helper_init(dev, fbh, 1, 1);
if (ret) { if (ret) {
......
...@@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn) ...@@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
{ {
struct armada_connector *dconn = drm_to_armada_conn(conn); struct armada_connector *dconn = drm_to_armada_conn(conn);
drm_sysfs_connector_remove(conn); drm_connector_unregister(conn);
drm_connector_cleanup(conn); drm_connector_cleanup(conn);
kfree(dconn); kfree(dconn);
} }
...@@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev, ...@@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev,
if (ret) if (ret)
goto err_conn; goto err_conn;
ret = drm_sysfs_connector_add(&dconn->conn); ret = drm_connector_register(&dconn->conn);
if (ret) if (ret)
goto err_sysfs; goto err_sysfs;
......
...@@ -362,7 +362,7 @@ static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait) ...@@ -362,7 +362,7 @@ static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
{ {
int ret; int ret;
ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
if (ret) { if (ret) {
if (ret != -ERESTARTSYS && ret != -EBUSY) if (ret != -ERESTARTSYS && ret != -EBUSY)
DRM_ERROR("reserve failed %p\n", bo); DRM_ERROR("reserve failed %p\n", bo);
......
...@@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, ...@@ -287,7 +287,7 @@ static void ast_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
*blue = ast_crtc->lut_b[regno] << 8; *blue = ast_crtc->lut_b[regno] << 8;
} }
static struct drm_fb_helper_funcs ast_fb_helper_funcs = { static const struct drm_fb_helper_funcs ast_fb_helper_funcs = {
.gamma_set = ast_fb_gamma_set, .gamma_set = ast_fb_gamma_set,
.gamma_get = ast_fb_gamma_get, .gamma_get = ast_fb_gamma_get,
.fb_probe = astfb_create, .fb_probe = astfb_create,
...@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev) ...@@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
return -ENOMEM; return -ENOMEM;
ast->fbdev = afbdev; ast->fbdev = afbdev;
afbdev->helper.funcs = &ast_fb_helper_funcs;
spin_lock_init(&afbdev->dirty_lock); spin_lock_init(&afbdev->dirty_lock);
drm_fb_helper_prepare(dev, &afbdev->helper, &ast_fb_helper_funcs);
ret = drm_fb_helper_init(dev, &afbdev->helper, ret = drm_fb_helper_init(dev, &afbdev->helper,
1, 1); 1, 1);
if (ret) { if (ret) {
......
...@@ -667,17 +667,9 @@ static void ast_encoder_destroy(struct drm_encoder *encoder) ...@@ -667,17 +667,9 @@ static void ast_encoder_destroy(struct drm_encoder *encoder)
static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector) static struct drm_encoder *ast_best_single_encoder(struct drm_connector *connector)
{ {
int enc_id = connector->encoder_ids[0]; int enc_id = connector->encoder_ids[0];
struct drm_mode_object *obj;
struct drm_encoder *encoder;
/* pick the encoder ids */ /* pick the encoder ids */
if (enc_id) { if (enc_id)
obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER); return drm_encoder_find(connector->dev, enc_id);
if (!obj)
return NULL;
encoder = obj_to_encoder(obj);
return encoder;
}
return NULL; return NULL;
} }
...@@ -829,7 +821,7 @@ static void ast_connector_destroy(struct drm_connector *connector) ...@@ -829,7 +821,7 @@ static void ast_connector_destroy(struct drm_connector *connector)
{ {
struct ast_connector *ast_connector = to_ast_connector(connector); struct ast_connector *ast_connector = to_ast_connector(connector);
ast_i2c_destroy(ast_connector->i2c); ast_i2c_destroy(ast_connector->i2c);
drm_sysfs_connector_remove(connector); drm_connector_unregister(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
kfree(connector); kfree(connector);
} }
...@@ -871,7 +863,7 @@ static int ast_connector_init(struct drm_device *dev) ...@@ -871,7 +863,7 @@ static int ast_connector_init(struct drm_device *dev)
connector->interlace_allowed = 0; connector->interlace_allowed = 0;
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
drm_sysfs_connector_add(connector); drm_connector_register(connector);
connector->polled = DRM_CONNECTOR_POLL_CONNECT; connector->polled = DRM_CONNECTOR_POLL_CONNECT;
......
...@@ -97,6 +97,7 @@ static struct drm_driver bochs_driver = { ...@@ -97,6 +97,7 @@ static struct drm_driver bochs_driver = {
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* pm interface */ /* pm interface */
#ifdef CONFIG_PM_SLEEP
static int bochs_pm_suspend(struct device *dev) static int bochs_pm_suspend(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
...@@ -131,6 +132,7 @@ static int bochs_pm_resume(struct device *dev) ...@@ -131,6 +132,7 @@ static int bochs_pm_resume(struct device *dev)
drm_kms_helper_poll_enable(drm_dev); drm_kms_helper_poll_enable(drm_dev);
return 0; return 0;
} }
#endif
static const struct dev_pm_ops bochs_pm_ops = { static const struct dev_pm_ops bochs_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend, SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,
......
...@@ -72,7 +72,7 @@ static int bochsfb_create(struct drm_fb_helper *helper, ...@@ -72,7 +72,7 @@ static int bochsfb_create(struct drm_fb_helper *helper,
bo = gem_to_bochs_bo(gobj); bo = gem_to_bochs_bo(gobj);
ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
if (ret) if (ret)
return ret; return ret;
...@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green, ...@@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
*blue = regno; *blue = regno;
} }
static struct drm_fb_helper_funcs bochs_fb_helper_funcs = { static const struct drm_fb_helper_funcs bochs_fb_helper_funcs = {
.gamma_set = bochs_fb_gamma_set, .gamma_set = bochs_fb_gamma_set,
.gamma_get = bochs_fb_gamma_get, .gamma_get = bochs_fb_gamma_get,
.fb_probe = bochsfb_create, .fb_probe = bochsfb_create,
...@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs) ...@@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
{ {
int ret; int ret;
bochs->fb.helper.funcs = &bochs_fb_helper_funcs; drm_fb_helper_prepare(bochs->dev, &bochs->fb.helper,
&bochs_fb_helper_funcs);
ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper, ret = drm_fb_helper_init(bochs->dev, &bochs->fb.helper,
1, 1); 1, 1);
......
...@@ -53,7 +53,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, ...@@ -53,7 +53,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
if (old_fb) { if (old_fb) {
bochs_fb = to_bochs_framebuffer(old_fb); bochs_fb = to_bochs_framebuffer(old_fb);
bo = gem_to_bochs_bo(bochs_fb->obj); bo = gem_to_bochs_bo(bochs_fb->obj);
ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
if (ret) { if (ret) {
DRM_ERROR("failed to reserve old_fb bo\n"); DRM_ERROR("failed to reserve old_fb bo\n");
} else { } else {
...@@ -67,7 +67,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, ...@@ -67,7 +67,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
bochs_fb = to_bochs_framebuffer(crtc->primary->fb); bochs_fb = to_bochs_framebuffer(crtc->primary->fb);
bo = gem_to_bochs_bo(bochs_fb->obj); bo = gem_to_bochs_bo(bochs_fb->obj);
ret = ttm_bo_reserve(&bo->bo, true, false, false, 0); ret = ttm_bo_reserve(&bo->bo, true, false, false, NULL);
if (ret) if (ret)
return ret; return ret;
...@@ -216,18 +216,9 @@ static struct drm_encoder * ...@@ -216,18 +216,9 @@ static struct drm_encoder *
bochs_connector_best_encoder(struct drm_connector *connector) bochs_connector_best_encoder(struct drm_connector *connector)
{ {
int enc_id = connector->encoder_ids[0]; int enc_id = connector->encoder_ids[0];
struct drm_mode_object *obj;
struct drm_encoder *encoder;
/* pick the encoder ids */ /* pick the encoder ids */
if (enc_id) { if (enc_id)
obj = drm_mode_object_find(connector->dev, enc_id, return drm_encoder_find(connector->dev, enc_id);
DRM_MODE_OBJECT_ENCODER);
if (!obj)
return NULL;
encoder = obj_to_encoder(obj);
return encoder;
}
return NULL; return NULL;
} }
......
...@@ -387,7 +387,7 @@ int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel, ...@@ -387,7 +387,7 @@ int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel,
*obj = NULL; *obj = NULL;
size = ALIGN(size, PAGE_SIZE); size = PAGE_ALIGN(size);
if (size == 0) if (size == 0)
return -EINVAL; return -EINVAL;
......
...@@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder, ...@@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
} }
drm_connector_helper_add(&ptn_bridge->connector, drm_connector_helper_add(&ptn_bridge->connector,
&ptn3460_connector_helper_funcs); &ptn3460_connector_helper_funcs);
drm_sysfs_connector_add(&ptn_bridge->connector); drm_connector_register(&ptn_bridge->connector);
drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder); drm_mode_connector_attach_encoder(&ptn_bridge->connector, encoder);
return 0; return 0;
......
...@@ -76,6 +76,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev) ...@@ -76,6 +76,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev)
drm_put_dev(dev); drm_put_dev(dev);
} }
#ifdef CONFIG_PM_SLEEP
static int cirrus_pm_suspend(struct device *dev) static int cirrus_pm_suspend(struct device *dev)
{ {
struct pci_dev *pdev = to_pci_dev(dev); struct pci_dev *pdev = to_pci_dev(dev);
...@@ -110,6 +111,7 @@ static int cirrus_pm_resume(struct device *dev) ...@@ -110,6 +111,7 @@ static int cirrus_pm_resume(struct device *dev)
drm_kms_helper_poll_enable(drm_dev); drm_kms_helper_poll_enable(drm_dev);
return 0; return 0;
} }
#endif
static const struct file_operations cirrus_driver_fops = { static const struct file_operations cirrus_driver_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
......
...@@ -241,7 +241,7 @@ static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait) ...@@ -241,7 +241,7 @@ static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
{ {
int ret; int ret;
ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, 0); ret = ttm_bo_reserve(&bo->bo, true, no_wait, false, NULL);
if (ret) { if (ret) {
if (ret != -ERESTARTSYS && ret != -EBUSY) if (ret != -ERESTARTSYS && ret != -EBUSY)
DRM_ERROR("reserve failed %p\n", bo); DRM_ERROR("reserve failed %p\n", bo);
......
...@@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev, ...@@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
return 0; return 0;
} }
static struct drm_fb_helper_funcs cirrus_fb_helper_funcs = { static const struct drm_fb_helper_funcs cirrus_fb_helper_funcs = {
.gamma_set = cirrus_crtc_fb_gamma_set, .gamma_set = cirrus_crtc_fb_gamma_set,
.gamma_get = cirrus_crtc_fb_gamma_get, .gamma_get = cirrus_crtc_fb_gamma_get,
.fb_probe = cirrusfb_create, .fb_probe = cirrusfb_create,
...@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev) ...@@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
return -ENOMEM; return -ENOMEM;
cdev->mode_info.gfbdev = gfbdev; cdev->mode_info.gfbdev = gfbdev;
gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
spin_lock_init(&gfbdev->dirty_lock); spin_lock_init(&gfbdev->dirty_lock);
drm_fb_helper_prepare(cdev->dev, &gfbdev->helper,
&cirrus_fb_helper_funcs);
ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper, ret = drm_fb_helper_init(cdev->dev, &gfbdev->helper,
cdev->num_crtc, CIRRUSFB_CONN_LIMIT); cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
if (ret) { if (ret) {
......
...@@ -509,19 +509,9 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector ...@@ -509,19 +509,9 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
*connector) *connector)
{ {
int enc_id = connector->encoder_ids[0]; int enc_id = connector->encoder_ids[0];
struct drm_mode_object *obj;
struct drm_encoder *encoder;
/* pick the encoder ids */ /* pick the encoder ids */
if (enc_id) { if (enc_id)
obj = return drm_encoder_find(connector->dev, enc_id);
drm_mode_object_find(connector->dev, enc_id,
DRM_MODE_OBJECT_ENCODER);
if (!obj)
return NULL;
encoder = obj_to_encoder(obj);
return encoder;
}
return NULL; return NULL;
} }
......
...@@ -80,11 +80,7 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size) ...@@ -80,11 +80,7 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size)
error_out: error_out:
/* Only last element can be null pointer so check for it first. */ for (; idx >= 0; --idx)
if ((*buf)->data[idx])
kfree((*buf)->data[idx]);
for (--idx; idx >= 0; --idx)
kfree((*buf)->data[idx]); kfree((*buf)->data[idx]);
kfree(*buf); kfree(*buf);
......
...@@ -894,6 +894,8 @@ int drm_connector_init(struct drm_device *dev, ...@@ -894,6 +894,8 @@ int drm_connector_init(struct drm_device *dev,
drm_object_attach_property(&connector->base, drm_object_attach_property(&connector->base,
dev->mode_config.dpms_property, 0); dev->mode_config.dpms_property, 0);
connector->debugfs_entry = NULL;
out_put: out_put:
if (ret) if (ret)
drm_mode_object_put(dev, &connector->base); drm_mode_object_put(dev, &connector->base);
...@@ -933,6 +935,47 @@ void drm_connector_cleanup(struct drm_connector *connector) ...@@ -933,6 +935,47 @@ void drm_connector_cleanup(struct drm_connector *connector)
} }
EXPORT_SYMBOL(drm_connector_cleanup); EXPORT_SYMBOL(drm_connector_cleanup);
/**
* drm_connector_register - register a connector
* @connector: the connector to register
*
* Register userspace interfaces for a connector
*
* Returns:
* Zero on success, error code on failure.
*/
int drm_connector_register(struct drm_connector *connector)
{
int ret;
ret = drm_sysfs_connector_add(connector);
if (ret)
return ret;
ret = drm_debugfs_connector_add(connector);
if (ret) {
drm_sysfs_connector_remove(connector);
return ret;
}
return 0;
}
EXPORT_SYMBOL(drm_connector_register);
/**
* drm_connector_unregister - unregister a connector
* @connector: the connector to unregister
*
* Unregister userspace interfaces for a connector
*/
void drm_connector_unregister(struct drm_connector *connector)
{
drm_sysfs_connector_remove(connector);
drm_debugfs_connector_remove(connector);
}
EXPORT_SYMBOL(drm_connector_unregister);
/** /**
* drm_connector_unplug_all - unregister connector userspace interfaces * drm_connector_unplug_all - unregister connector userspace interfaces
* @dev: drm device * @dev: drm device
...@@ -947,7 +990,7 @@ void drm_connector_unplug_all(struct drm_device *dev) ...@@ -947,7 +990,7 @@ void drm_connector_unplug_all(struct drm_device *dev)
/* taking the mode config mutex ends up in a clash with sysfs */ /* taking the mode config mutex ends up in a clash with sysfs */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) list_for_each_entry(connector, &dev->mode_config.connector_list, head)
drm_sysfs_connector_remove(connector); drm_connector_unregister(connector);
} }
EXPORT_SYMBOL(drm_connector_unplug_all); EXPORT_SYMBOL(drm_connector_unplug_all);
...@@ -1227,6 +1270,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) ...@@ -1227,6 +1270,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
{ {
struct drm_property *edid; struct drm_property *edid;
struct drm_property *dpms; struct drm_property *dpms;
struct drm_property *dev_path;
/* /*
* Standard properties (apply to all connectors) * Standard properties (apply to all connectors)
...@@ -1241,6 +1285,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev) ...@@ -1241,6 +1285,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
ARRAY_SIZE(drm_dpms_enum_list)); ARRAY_SIZE(drm_dpms_enum_list));
dev->mode_config.dpms_property = dpms; dev->mode_config.dpms_property = dpms;
dev_path = drm_property_create(dev,
DRM_MODE_PROP_BLOB |
DRM_MODE_PROP_IMMUTABLE,
"PATH", 0);
dev->mode_config.path_property = dev_path;
return 0; return 0;
} }
...@@ -1510,6 +1560,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev, ...@@ -1510,6 +1560,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
} }
EXPORT_SYMBOL(drm_mode_group_init_legacy_group); EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
void drm_reinit_primary_mode_group(struct drm_device *dev)
{
drm_modeset_lock_all(dev);
drm_mode_group_destroy(&dev->primary->mode_group);
drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group);
drm_modeset_unlock_all(dev);
}
EXPORT_SYMBOL(drm_reinit_primary_mode_group);
/** /**
* drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo
* @out: drm_mode_modeinfo struct to return to the user * @out: drm_mode_modeinfo struct to return to the user
...@@ -3362,7 +3421,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags, ...@@ -3362,7 +3421,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
EXPORT_SYMBOL(drm_property_create); EXPORT_SYMBOL(drm_property_create);
/** /**
* drm_property_create - create a new enumeration property type * drm_property_create_enum - create a new enumeration property type
* @dev: drm device * @dev: drm device
* @flags: flags specifying the property type * @flags: flags specifying the property type
* @name: name of the property * @name: name of the property
...@@ -3408,7 +3467,7 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, ...@@ -3408,7 +3467,7 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
EXPORT_SYMBOL(drm_property_create_enum); EXPORT_SYMBOL(drm_property_create_enum);
/** /**
* drm_property_create - create a new bitmask property type * drm_property_create_bitmask - create a new bitmask property type
* @dev: drm device * @dev: drm device
* @flags: flags specifying the property type * @flags: flags specifying the property type
* @name: name of the property * @name: name of the property
...@@ -3479,7 +3538,7 @@ static struct drm_property *property_create_range(struct drm_device *dev, ...@@ -3479,7 +3538,7 @@ static struct drm_property *property_create_range(struct drm_device *dev,
} }
/** /**
* drm_property_create - create a new ranged property type * drm_property_create_range - create a new ranged property type
* @dev: drm device * @dev: drm device
* @flags: flags specifying the property type * @flags: flags specifying the property type
* @name: name of the property * @name: name of the property
...@@ -3898,6 +3957,25 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, ...@@ -3898,6 +3957,25 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
return ret; return ret;
} }
int drm_mode_connector_set_path_property(struct drm_connector *connector,
char *path)
{
struct drm_device *dev = connector->dev;
int ret, size;
size = strlen(path) + 1;
connector->path_blob_ptr = drm_property_create_blob(connector->dev,
size, path);
if (!connector->path_blob_ptr)
return -EINVAL;
ret = drm_object_property_set_value(&connector->base,
dev->mode_config.path_property,
connector->path_blob_ptr->base.id);
return ret;
}
EXPORT_SYMBOL(drm_mode_connector_set_path_property);
/** /**
* drm_mode_connector_update_edid_property - update the edid property of a connector * drm_mode_connector_update_edid_property - update the edid property of a connector
* @connector: drm connector * @connector: drm connector
...@@ -3915,6 +3993,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector, ...@@ -3915,6 +3993,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
int ret, size; int ret, size;
/* ignore requests to set edid when overridden */
if (connector->override_edid)
return 0;
if (connector->edid_blob_ptr) if (connector->edid_blob_ptr)
drm_property_destroy_blob(dev, connector->edid_blob_ptr); drm_property_destroy_blob(dev, connector->edid_blob_ptr);
......
...@@ -818,6 +818,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb, ...@@ -818,6 +818,7 @@ void drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth, drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
&fb->bits_per_pixel); &fb->bits_per_pixel);
fb->pixel_format = mode_cmd->pixel_format; fb->pixel_format = mode_cmd->pixel_format;
fb->flags = mode_cmd->flags;
} }
EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct); EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
......
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/export.h> #include <linux/export.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_edid.h>
#if defined(CONFIG_DEBUG_FS) #if defined(CONFIG_DEBUG_FS)
...@@ -237,5 +238,186 @@ int drm_debugfs_cleanup(struct drm_minor *minor) ...@@ -237,5 +238,186 @@ int drm_debugfs_cleanup(struct drm_minor *minor)
return 0; return 0;
} }
static int connector_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
const char *status;
switch (connector->force) {
case DRM_FORCE_ON:
status = "on\n";
break;
case DRM_FORCE_ON_DIGITAL:
status = "digital\n";
break;
case DRM_FORCE_OFF:
status = "off\n";
break;
case DRM_FORCE_UNSPECIFIED:
status = "unspecified\n";
break;
default:
return 0;
}
seq_puts(m, status);
return 0;
}
static int connector_open(struct inode *inode, struct file *file)
{
struct drm_connector *dev = inode->i_private;
return single_open(file, connector_show, dev);
}
static ssize_t connector_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct drm_connector *connector = m->private;
char buf[12];
if (len > sizeof(buf) - 1)
return -EINVAL;
if (copy_from_user(buf, ubuf, len))
return -EFAULT;
buf[len] = '\0';
if (!strcmp(buf, "on"))
connector->force = DRM_FORCE_ON;
else if (!strcmp(buf, "digital"))
connector->force = DRM_FORCE_ON_DIGITAL;
else if (!strcmp(buf, "off"))
connector->force = DRM_FORCE_OFF;
else if (!strcmp(buf, "unspecified"))
connector->force = DRM_FORCE_UNSPECIFIED;
else
return -EINVAL;
return len;
}
static int edid_show(struct seq_file *m, void *data)
{
struct drm_connector *connector = m->private;
struct drm_property_blob *edid = connector->edid_blob_ptr;
if (connector->override_edid && edid)
seq_write(m, edid->data, edid->length);
return 0;
}
static int edid_open(struct inode *inode, struct file *file)
{
struct drm_connector *dev = inode->i_private;
return single_open(file, edid_show, dev);
}
static ssize_t edid_write(struct file *file, const char __user *ubuf,
size_t len, loff_t *offp)
{
struct seq_file *m = file->private_data;
struct drm_connector *connector = m->private;
char *buf;
struct edid *edid;
int ret;
buf = memdup_user(ubuf, len);
if (IS_ERR(buf))
return PTR_ERR(buf);
edid = (struct edid *) buf;
if (len == 5 && !strncmp(buf, "reset", 5)) {
connector->override_edid = false;
ret = drm_mode_connector_update_edid_property(connector, NULL);
} else if (len < EDID_LENGTH ||
EDID_LENGTH * (1 + edid->extensions) > len)
ret = -EINVAL;
else {
connector->override_edid = false;
ret = drm_mode_connector_update_edid_property(connector, edid);
if (!ret)
connector->override_edid = true;
}
kfree(buf);
return (ret) ? ret : len;
}
static const struct file_operations drm_edid_fops = {
.owner = THIS_MODULE,
.open = edid_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = edid_write
};
static const struct file_operations drm_connector_fops = {
.owner = THIS_MODULE,
.open = connector_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.write = connector_write
};
int drm_debugfs_connector_add(struct drm_connector *connector)
{
struct drm_minor *minor = connector->dev->primary;
struct dentry *root, *ent;
if (!minor->debugfs_root)
return -1;
root = debugfs_create_dir(connector->name, minor->debugfs_root);
if (!root)
return -ENOMEM;
connector->debugfs_entry = root;
/* force */
ent = debugfs_create_file("force", S_IRUGO | S_IWUSR, root, connector,
&drm_connector_fops);
if (!ent)
goto error;
/* edid */
ent = debugfs_create_file("edid_override", S_IRUGO | S_IWUSR, root,
connector, &drm_edid_fops);
if (!ent)
goto error;
return 0;
error:
debugfs_remove_recursive(connector->debugfs_entry);
connector->debugfs_entry = NULL;
return -ENOMEM;
}
void drm_debugfs_connector_remove(struct drm_connector *connector)
{
if (!connector->debugfs_entry)
return;
debugfs_remove_recursive(connector->debugfs_entry);
connector->debugfs_entry = NULL;
}
#endif /* CONFIG_DEBUG_FS */ #endif /* CONFIG_DEBUG_FS */
This diff is collapsed.
...@@ -233,7 +233,7 @@ module_exit(drm_core_exit); ...@@ -233,7 +233,7 @@ module_exit(drm_core_exit);
/** /**
* Copy and IOCTL return string to user space * Copy and IOCTL return string to user space
*/ */
static int drm_copy_field(char *buf, size_t *buf_len, const char *value) static int drm_copy_field(char __user *buf, size_t *buf_len, const char *value)
{ {
int len; int len;
......
...@@ -3305,6 +3305,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder, ...@@ -3305,6 +3305,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
list_for_each_entry(connector, &dev->mode_config.connector_list, head) list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder && connector->eld[0]) if (connector->encoder == encoder && connector->eld[0])
......
...@@ -327,7 +327,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper, ...@@ -327,7 +327,7 @@ static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
return ret; return ret;
} }
static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = { static const struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
.fb_probe = drm_fbdev_cma_create, .fb_probe = drm_fbdev_cma_create,
}; };
...@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev, ...@@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
helper = &fbdev_cma->fb_helper; helper = &fbdev_cma->fb_helper;
drm_fb_helper_prepare(dev, helper, &drm_fb_cma_helper_funcs);
ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count); ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
if (ret < 0) { if (ret < 0) {
dev_err(dev->dev, "Failed to initialize drm fb helper.\n"); dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
......
...@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list); ...@@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
* helper functions used by many drivers to implement the kernel mode setting * helper functions used by many drivers to implement the kernel mode setting
* interfaces. * interfaces.
* *
* Initialization is done as a three-step process with drm_fb_helper_init(), * Initialization is done as a four-step process with drm_fb_helper_prepare(),
* drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config(). * drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
* Drivers with fancier requirements than the default behaviour can override the * drm_fb_helper_initial_config(). Drivers with fancier requirements than the
* second step with their own code. Teardown is done with drm_fb_helper_fini(). * default behaviour can override the third step with their own code.
* Teardown is done with drm_fb_helper_fini().
* *
* At runtime drivers should restore the fbdev console by calling * At runtime drivers should restore the fbdev console by calling
* drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They * drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
...@@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list); ...@@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
* *
* All other functions exported by the fb helper library can be used to * All other functions exported by the fb helper library can be used to
* implement the fbdev driver interface by the driver. * implement the fbdev driver interface by the driver.
*
* It is possible, though perhaps somewhat tricky, to implement race-free
* hotplug detection using the fbdev helpers. The drm_fb_helper_prepare()
* helper must be called first to initialize the minimum required to make
* hotplug detection work. Drivers also need to make sure to properly set up
* the dev->mode_config.funcs member. After calling drm_kms_helper_poll_init()
* it is safe to enable interrupts and start processing hotplug events. At the
* same time, drivers should initialize all modeset objects such as CRTCs,
* encoders and connectors. To finish up the fbdev helper initialization, the
* drm_fb_helper_init() function is called. To probe for all attached displays
* and set up an initial configuration using the detected hardware, drivers
* should call drm_fb_helper_single_add_all_connectors() followed by
* drm_fb_helper_initial_config().
*/ */
/** /**
...@@ -105,6 +119,58 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) ...@@ -105,6 +119,58 @@ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
} }
EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors); EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
int drm_fb_helper_add_one_connector(struct drm_fb_helper *fb_helper, struct drm_connector *connector)
{
struct drm_fb_helper_connector **temp;
struct drm_fb_helper_connector *fb_helper_connector;
WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
if (fb_helper->connector_count + 1 > fb_helper->connector_info_alloc_count) {
temp = krealloc(fb_helper->connector_info, sizeof(struct drm_fb_helper_connector) * (fb_helper->connector_count + 1), GFP_KERNEL);
if (!temp)
return -ENOMEM;
fb_helper->connector_info_alloc_count = fb_helper->connector_count + 1;
fb_helper->connector_info = temp;
}
fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
if (!fb_helper_connector)
return -ENOMEM;
fb_helper_connector->connector = connector;
fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
return 0;
}
EXPORT_SYMBOL(drm_fb_helper_add_one_connector);
int drm_fb_helper_remove_one_connector(struct drm_fb_helper *fb_helper,
struct drm_connector *connector)
{
struct drm_fb_helper_connector *fb_helper_connector;
int i, j;
WARN_ON(!mutex_is_locked(&fb_helper->dev->mode_config.mutex));
for (i = 0; i < fb_helper->connector_count; i++) {
if (fb_helper->connector_info[i]->connector == connector)
break;
}
if (i == fb_helper->connector_count)
return -EINVAL;
fb_helper_connector = fb_helper->connector_info[i];
for (j = i + 1; j < fb_helper->connector_count; j++) {
fb_helper->connector_info[j - 1] = fb_helper->connector_info[j];
}
fb_helper->connector_count--;
kfree(fb_helper_connector);
return 0;
}
EXPORT_SYMBOL(drm_fb_helper_remove_one_connector);
static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper) static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
{ {
struct drm_fb_helper_connector *fb_helper_conn; struct drm_fb_helper_connector *fb_helper_conn;
...@@ -199,9 +265,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info) ...@@ -199,9 +265,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
struct drm_crtc_helper_funcs *funcs; struct drm_crtc_helper_funcs *funcs;
int i; int i;
if (list_empty(&kernel_fb_helper_list))
return false;
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) { list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
for (i = 0; i < helper->crtc_count; i++) { for (i = 0; i < helper->crtc_count; i++) {
struct drm_mode_set *mode_set = struct drm_mode_set *mode_set =
...@@ -530,6 +593,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) ...@@ -530,6 +593,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
kfree(helper->crtc_info); kfree(helper->crtc_info);
} }
/**
* drm_fb_helper_prepare - setup a drm_fb_helper structure
* @dev: DRM device
* @helper: driver-allocated fbdev helper structure to set up
* @funcs: pointer to structure of functions associate with this helper
*
* Sets up the bare minimum to make the framebuffer helper usable. This is
* useful to implement race-free initialization of the polling helpers.
*/
void drm_fb_helper_prepare(struct drm_device *dev, struct drm_fb_helper *helper,
const struct drm_fb_helper_funcs *funcs)
{
INIT_LIST_HEAD(&helper->kernel_fb_list);
helper->funcs = funcs;
helper->dev = dev;
}
EXPORT_SYMBOL(drm_fb_helper_prepare);
/** /**
* drm_fb_helper_init - initialize a drm_fb_helper structure * drm_fb_helper_init - initialize a drm_fb_helper structure
* @dev: drm device * @dev: drm device
...@@ -542,8 +623,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) ...@@ -542,8 +623,7 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
* nor register the fbdev. This is only done in drm_fb_helper_initial_config() * nor register the fbdev. This is only done in drm_fb_helper_initial_config()
* to allow driver writes more control over the exact init sequence. * to allow driver writes more control over the exact init sequence.
* *
* Drivers must set fb_helper->funcs before calling * Drivers must call drm_fb_helper_prepare() before calling this function.
* drm_fb_helper_initial_config().
* *
* RETURNS: * RETURNS:
* Zero if everything went ok, nonzero otherwise. * Zero if everything went ok, nonzero otherwise.
...@@ -558,10 +638,6 @@ int drm_fb_helper_init(struct drm_device *dev, ...@@ -558,10 +638,6 @@ int drm_fb_helper_init(struct drm_device *dev,
if (!max_conn_count) if (!max_conn_count)
return -EINVAL; return -EINVAL;
fb_helper->dev = dev;
INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
if (!fb_helper->crtc_info) if (!fb_helper->crtc_info)
return -ENOMEM; return -ENOMEM;
...@@ -572,6 +648,7 @@ int drm_fb_helper_init(struct drm_device *dev, ...@@ -572,6 +648,7 @@ int drm_fb_helper_init(struct drm_device *dev,
kfree(fb_helper->crtc_info); kfree(fb_helper->crtc_info);
return -ENOMEM; return -ENOMEM;
} }
fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
fb_helper->connector_count = 0; fb_helper->connector_count = 0;
for (i = 0; i < crtc_count; i++) { for (i = 0; i < crtc_count; i++) {
...@@ -1056,7 +1133,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, ...@@ -1056,7 +1133,6 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
info->fix.ypanstep = 1; /* doing it in hw */ info->fix.ypanstep = 1; /* doing it in hw */
info->fix.ywrapstep = 0; info->fix.ywrapstep = 0;
info->fix.accel = FB_ACCEL_NONE; info->fix.accel = FB_ACCEL_NONE;
info->fix.type_aux = 0;
info->fix.line_length = pitch; info->fix.line_length = pitch;
return; return;
...@@ -1613,8 +1689,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); ...@@ -1613,8 +1689,10 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config);
* either the output polling work or a work item launched from the driver's * either the output polling work or a work item launched from the driver's
* hotplug interrupt). * hotplug interrupt).
* *
* Note that the driver must ensure that this is only called _after_ the fb has * Note that drivers may call this even before calling
* been fully set up, i.e. after the call to drm_fb_helper_initial_config. * drm_fb_helper_initial_config but only aftert drm_fb_helper_init. This allows
* for a race-free fbcon setup and will make sure that the fbdev emulation will
* not miss any hotplug events.
* *
* RETURNS: * RETURNS:
* 0 on success and a non-zero error code otherwise. * 0 on success and a non-zero error code otherwise.
...@@ -1624,11 +1702,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper) ...@@ -1624,11 +1702,8 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
struct drm_device *dev = fb_helper->dev; struct drm_device *dev = fb_helper->dev;
u32 max_width, max_height; u32 max_width, max_height;
if (!fb_helper->fb)
return 0;
mutex_lock(&fb_helper->dev->mode_config.mutex); mutex_lock(&fb_helper->dev->mode_config.mutex);
if (!drm_fb_helper_is_bound(fb_helper)) { if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
fb_helper->delayed_hotplug = true; fb_helper->delayed_hotplug = true;
mutex_unlock(&fb_helper->dev->mode_config.mutex); mutex_unlock(&fb_helper->dev->mode_config.mutex);
return 0; return 0;
......
...@@ -441,18 +441,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset); ...@@ -441,18 +441,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset);
* drm_gem_get_pages - helper to allocate backing pages for a GEM object * drm_gem_get_pages - helper to allocate backing pages for a GEM object
* from shmem * from shmem
* @obj: obj in question * @obj: obj in question
* @gfpmask: gfp mask of requested pages *
* This reads the page-array of the shmem-backing storage of the given gem
* object. An array of pages is returned. If a page is not allocated or
* swapped-out, this will allocate/swap-in the required pages. Note that the
* whole object is covered by the page-array and pinned in memory.
*
* Use drm_gem_put_pages() to release the array and unpin all pages.
*
* This uses the GFP-mask set on the shmem-mapping (see mapping_set_gfp_mask()).
* If you require other GFP-masks, you have to do those allocations yourself.
*
* Note that you are not allowed to change gfp-zones during runtime. That is,
* shmem_read_mapping_page_gfp() must be called with the same gfp_zone(gfp) as
* set during initialization. If you have special zone constraints, set them
* after drm_gem_init_object() via mapping_set_gfp_mask(). shmem-core takes care
* to keep pages in the required zone during swap-in.
*/ */
struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) struct page **drm_gem_get_pages(struct drm_gem_object *obj)
{ {
struct inode *inode;
struct address_space *mapping; struct address_space *mapping;
struct page *p, **pages; struct page *p, **pages;
int i, npages; int i, npages;
/* This is the shared memory object that backs the GEM resource */ /* This is the shared memory object that backs the GEM resource */
inode = file_inode(obj->filp); mapping = file_inode(obj->filp)->i_mapping;
mapping = inode->i_mapping;
/* We already BUG_ON() for non-page-aligned sizes in /* We already BUG_ON() for non-page-aligned sizes in
* drm_gem_object_init(), so we should never hit this unless * drm_gem_object_init(), so we should never hit this unless
...@@ -466,10 +479,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) ...@@ -466,10 +479,8 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
if (pages == NULL) if (pages == NULL)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
gfpmask |= mapping_gfp_mask(mapping);
for (i = 0; i < npages; i++) { for (i = 0; i < npages; i++) {
p = shmem_read_mapping_page_gfp(mapping, i, gfpmask); p = shmem_read_mapping_page(mapping, i);
if (IS_ERR(p)) if (IS_ERR(p))
goto fail; goto fail;
pages[i] = p; pages[i] = p;
...@@ -479,7 +490,7 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask) ...@@ -479,7 +490,7 @@ struct page **drm_gem_get_pages(struct drm_gem_object *obj, gfp_t gfpmask)
* __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping) * __GFP_DMA32 to be set in mapping_gfp_mask(inode->i_mapping)
* so shmem can relocate pages during swapin if required. * so shmem can relocate pages during swapin if required.
*/ */
BUG_ON((gfpmask & __GFP_DMA32) && BUG_ON((mapping_gfp_mask(mapping) & __GFP_DMA32) &&
(page_to_pfn(p) >= 0x00100000UL)); (page_to_pfn(p) >= 0x00100000UL));
} }
......
...@@ -327,7 +327,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size, ...@@ -327,7 +327,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
/* Create a CMA GEM buffer. */ /* Create a CMA GEM buffer. */
cma_obj = __drm_gem_cma_create(dev, size); cma_obj = __drm_gem_cma_create(dev, size);
if (IS_ERR(cma_obj)) if (IS_ERR(cma_obj))
return ERR_PTR(PTR_ERR(cma_obj)); return ERR_CAST(cma_obj);
cma_obj->paddr = sg_dma_address(sgt->sgl); cma_obj->paddr = sg_dma_address(sgt->sgl);
cma_obj->sgt = sgt; cma_obj->sgt = sgt;
......
...@@ -342,8 +342,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv) ...@@ -342,8 +342,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
file_priv->stereo_allowed = req->value; file_priv->stereo_allowed = req->value;
break; break;
case DRM_CLIENT_CAP_UNIVERSAL_PLANES: case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
if (!drm_universal_planes)
return -EINVAL;
if (req->value > 1) if (req->value > 1)
return -EINVAL; return -EINVAL;
file_priv->universal_planes = req->value; file_priv->universal_planes = req->value;
......
#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);
...@@ -335,9 +335,10 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev, ...@@ -335,9 +335,10 @@ struct drm_plane *drm_primary_helper_create_plane(struct drm_device *dev,
} }
/* possible_crtc's will be filled in later by crtc_init */ /* possible_crtc's will be filled in later by crtc_init */
ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs, ret = drm_universal_plane_init(dev, primary, 0,
formats, num_formats, &drm_primary_helper_funcs,
DRM_PLANE_TYPE_PRIMARY); formats, num_formats,
DRM_PLANE_TYPE_PRIMARY);
if (ret) { if (ret) {
kfree(primary); kfree(primary);
primary = NULL; primary = NULL;
......
...@@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect ...@@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
count = drm_load_edid_firmware(connector); count = drm_load_edid_firmware(connector);
if (count == 0) if (count == 0)
#endif #endif
count = (*connector_funcs->get_modes)(connector); {
if (connector->override_edid) {
struct edid *edid = (struct edid *) connector->edid_blob_ptr->data;
count = drm_add_edid_modes(connector, edid);
} else
count = (*connector_funcs->get_modes)(connector);
}
if (count == 0 && connector->status == connector_status_connected) if (count == 0 && connector->status == connector_status_connected)
count = drm_add_modes_noedid(connector, 1024, 768); count = drm_add_modes_noedid(connector, 1024, 768);
......
...@@ -37,18 +37,9 @@ ...@@ -37,18 +37,9 @@
unsigned int drm_debug = 0; /* 1 to enable debug output */ unsigned int drm_debug = 0; /* 1 to enable debug output */
EXPORT_SYMBOL(drm_debug); EXPORT_SYMBOL(drm_debug);
unsigned int drm_rnodes = 0; /* 1 to enable experimental render nodes API */
EXPORT_SYMBOL(drm_rnodes);
/* 1 to allow user space to request universal planes (experimental) */
unsigned int drm_universal_planes = 0;
EXPORT_SYMBOL(drm_universal_planes);
unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */ unsigned int drm_vblank_offdelay = 5000; /* Default to 5000 msecs. */
EXPORT_SYMBOL(drm_vblank_offdelay);
unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */ unsigned int drm_timestamp_precision = 20; /* Default to 20 usecs. */
EXPORT_SYMBOL(drm_timestamp_precision);
/* /*
* Default to use monotonic timestamps for wait-for-vblank and page-flip * Default to use monotonic timestamps for wait-for-vblank and page-flip
...@@ -60,14 +51,11 @@ MODULE_AUTHOR(CORE_AUTHOR); ...@@ -60,14 +51,11 @@ MODULE_AUTHOR(CORE_AUTHOR);
MODULE_DESCRIPTION(CORE_DESC); MODULE_DESCRIPTION(CORE_DESC);
MODULE_LICENSE("GPL and additional rights"); MODULE_LICENSE("GPL and additional rights");
MODULE_PARM_DESC(debug, "Enable debug output"); MODULE_PARM_DESC(debug, "Enable debug output");
MODULE_PARM_DESC(rnodes, "Enable experimental render nodes API");
MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]"); MODULE_PARM_DESC(vblankoffdelay, "Delay until vblank irq auto-disable [msecs]");
MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]"); MODULE_PARM_DESC(timestamp_precision_usec, "Max. error on timestamps [usecs]");
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps"); MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
module_param_named(debug, drm_debug, int, 0600); module_param_named(debug, drm_debug, int, 0600);
module_param_named(rnodes, drm_rnodes, int, 0600);
module_param_named(universal_planes, drm_universal_planes, int, 0600);
module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600); module_param_named(vblankoffdelay, drm_vblank_offdelay, int, 0600);
module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600); module_param_named(timestamp_precision_usec, drm_timestamp_precision, int, 0600);
module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600); module_param_named(timestamp_monotonic, drm_timestamp_monotonic, int, 0600);
...@@ -588,7 +576,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver, ...@@ -588,7 +576,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
goto err_minors; goto err_minors;
} }
if (drm_core_check_feature(dev, DRIVER_RENDER) && drm_rnodes) { if (drm_core_check_feature(dev, DRIVER_RENDER)) {
ret = drm_minor_alloc(dev, DRM_MINOR_RENDER); ret = drm_minor_alloc(dev, DRM_MINOR_RENDER);
if (ret) if (ret)
goto err_minors; goto err_minors;
......
...@@ -438,7 +438,6 @@ int drm_sysfs_connector_add(struct drm_connector *connector) ...@@ -438,7 +438,6 @@ int drm_sysfs_connector_add(struct drm_connector *connector)
out: out:
return ret; return ret;
} }
EXPORT_SYMBOL(drm_sysfs_connector_add);
/** /**
* drm_sysfs_connector_remove - remove an connector device from sysfs * drm_sysfs_connector_remove - remove an connector device from sysfs
...@@ -468,7 +467,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector) ...@@ -468,7 +467,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
device_unregister(connector->kdev); device_unregister(connector->kdev);
connector->kdev = NULL; connector->kdev = NULL;
} }
EXPORT_SYMBOL(drm_sysfs_connector_remove);
/** /**
* drm_sysfs_hotplug_event - generate a DRM uevent * drm_sysfs_hotplug_event - generate a DRM uevent
......
...@@ -1018,7 +1018,7 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display, ...@@ -1018,7 +1018,7 @@ static int exynos_dp_create_connector(struct exynos_drm_display *display,
} }
drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs); drm_connector_helper_add(connector, &exynos_dp_connector_helper_funcs);
drm_sysfs_connector_add(connector); drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder); drm_mode_connector_attach_encoder(connector, encoder);
return 0; return 0;
......
...@@ -117,20 +117,7 @@ static struct drm_encoder *exynos_drm_best_encoder( ...@@ -117,20 +117,7 @@ static struct drm_encoder *exynos_drm_best_encoder(
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct exynos_drm_connector *exynos_connector = struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector); to_exynos_connector(connector);
struct drm_mode_object *obj; return drm_encoder_find(dev, exynos_connector->encoder_id);
struct drm_encoder *encoder;
obj = drm_mode_object_find(dev, exynos_connector->encoder_id,
DRM_MODE_OBJECT_ENCODER);
if (!obj) {
DRM_DEBUG_KMS("Unknown ENCODER ID %d\n",
exynos_connector->encoder_id);
return NULL;
}
encoder = obj_to_encoder(obj);
return encoder;
} }
static struct drm_connector_helper_funcs exynos_connector_helper_funcs = { static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
...@@ -185,7 +172,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector) ...@@ -185,7 +172,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
struct exynos_drm_connector *exynos_connector = struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector); to_exynos_connector(connector);
drm_sysfs_connector_remove(connector); drm_connector_unregister(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
kfree(exynos_connector); kfree(exynos_connector);
} }
...@@ -230,7 +217,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, ...@@ -230,7 +217,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
drm_connector_init(dev, connector, &exynos_connector_funcs, type); drm_connector_init(dev, connector, &exynos_connector_funcs, type);
drm_connector_helper_add(connector, &exynos_connector_helper_funcs); drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
err = drm_sysfs_connector_add(connector); err = drm_connector_register(connector);
if (err) if (err)
goto err_connector; goto err_connector;
...@@ -250,7 +237,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev, ...@@ -250,7 +237,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
return connector; return connector;
err_sysfs: err_sysfs:
drm_sysfs_connector_remove(connector); drm_connector_unregister(connector);
err_connector: err_connector:
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
kfree(exynos_connector); kfree(exynos_connector);
......
...@@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force) ...@@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
static void exynos_dpi_connector_destroy(struct drm_connector *connector) static void exynos_dpi_connector_destroy(struct drm_connector *connector)
{ {
drm_sysfs_connector_remove(connector); drm_connector_unregister(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
} }
...@@ -117,7 +117,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display, ...@@ -117,7 +117,7 @@ static int exynos_dpi_create_connector(struct exynos_drm_display *display,
} }
drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs); drm_connector_helper_add(connector, &exynos_dpi_connector_helper_funcs);
drm_sysfs_connector_add(connector); drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder); drm_mode_connector_attach_encoder(connector, encoder);
return 0; return 0;
......
...@@ -39,8 +39,6 @@ ...@@ -39,8 +39,6 @@
#define DRIVER_MAJOR 1 #define DRIVER_MAJOR 1
#define DRIVER_MINOR 0 #define DRIVER_MINOR 0
#define VBLANK_OFF_DELAY 50000
static struct platform_device *exynos_drm_pdev; static struct platform_device *exynos_drm_pdev;
static DEFINE_MUTEX(drm_component_lock); static DEFINE_MUTEX(drm_component_lock);
...@@ -103,8 +101,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags) ...@@ -103,8 +101,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
/* setup possible_clones. */ /* setup possible_clones. */
exynos_drm_encoder_setup(dev); exynos_drm_encoder_setup(dev);
drm_vblank_offdelay = VBLANK_OFF_DELAY;
platform_set_drvdata(dev->platformdev, dev); platform_set_drvdata(dev->platformdev, dev);
/* Try to bind all sub drivers. */ /* Try to bind all sub drivers. */
......
...@@ -40,8 +40,6 @@ struct drm_device; ...@@ -40,8 +40,6 @@ struct drm_device;
struct exynos_drm_overlay; struct exynos_drm_overlay;
struct drm_connector; struct drm_connector;
extern unsigned int drm_vblank_offdelay;
/* This enumerates device type. */ /* This enumerates device type. */
enum exynos_drm_device_type { enum exynos_drm_device_type {
EXYNOS_DEVICE_TYPE_NONE, EXYNOS_DEVICE_TYPE_NONE,
......
...@@ -1246,7 +1246,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display, ...@@ -1246,7 +1246,7 @@ static int exynos_dsi_create_connector(struct exynos_drm_display *display,
} }
drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs); drm_connector_helper_add(connector, &exynos_dsi_connector_helper_funcs);
drm_sysfs_connector_add(connector); drm_connector_register(connector);
drm_mode_connector_attach_encoder(connector, encoder); drm_mode_connector_attach_encoder(connector, encoder);
return 0; return 0;
......
...@@ -225,7 +225,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper, ...@@ -225,7 +225,7 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
return ret; return ret;
} }
static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = { static const struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_create, .fb_probe = exynos_drm_fbdev_create,
}; };
...@@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev) ...@@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
return -ENOMEM; return -ENOMEM;
private->fb_helper = helper = &fbdev->drm_fb_helper; private->fb_helper = helper = &fbdev->drm_fb_helper;
helper->funcs = &exynos_drm_fb_helper_funcs;
drm_fb_helper_prepare(dev, helper, &exynos_drm_fb_helper_funcs);
num_crtc = dev->mode_config.num_crtc; num_crtc = dev->mode_config.num_crtc;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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