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: Daniel Vetter <daniel.vetter@ffwll.ch>
This commit is contained in:
commit
4dac3edfe6
@ -1610,7 +1610,7 @@ int max_width, max_height;</synopsis>
|
||||
The connector is then registered with a call to
|
||||
<function>drm_connector_init</function> with a pointer to the connector
|
||||
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>
|
||||
Supported connector types are
|
||||
@ -1768,7 +1768,7 @@ int max_width, max_height;</synopsis>
|
||||
(<function>drm_encoder_cleanup</function>) and connectors
|
||||
(<function>drm_connector_cleanup</function>). Furthermore, connectors
|
||||
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>.
|
||||
</para>
|
||||
<para>
|
||||
@ -1813,7 +1813,7 @@ void intel_crt_init(struct drm_device *dev)
|
||||
drm_encoder_helper_add(&intel_output->enc, &intel_crt_helper_funcs);
|
||||
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
}]]></programlisting>
|
||||
<para>
|
||||
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)
|
||||
!Pdrivers/gpu/drm/drm_dp_helper.c dp helpers
|
||||
!Iinclude/drm/drm_dp_helper.h
|
||||
!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>
|
||||
<title>EDID Helper Functions Reference</title>
|
||||
|
@ -0,0 +1,30 @@
|
||||
Device Tree bindings for Armada DRM CRTC driver
|
||||
|
||||
Required properties:
|
||||
- compatible: value should be "marvell,dove-lcd".
|
||||
- reg: base address and size of the LCD controller
|
||||
- interrupts: single interrupt number for the LCD controller
|
||||
- port: video output port with endpoints, as described by graph.txt
|
||||
|
||||
Optional properties:
|
||||
|
||||
- clocks: as described by clock-bindings.txt
|
||||
- clock-names: as described by clock-bindings.txt
|
||||
"axiclk" - axi bus clock for pixel clock
|
||||
"plldivider" - pll divider clock for pixel clock
|
||||
"ext_ref_clk0" - external clock 0 for pixel clock
|
||||
"ext_ref_clk1" - external clock 1 for pixel clock
|
||||
|
||||
Note: all clocks are optional but at least one must be specified.
|
||||
Further clocks may be added in the future according to requirements of
|
||||
different SoCs.
|
||||
|
||||
Example:
|
||||
|
||||
lcd0: lcd-controller@820000 {
|
||||
compatible = "marvell,dove-lcd";
|
||||
reg = <0x820000 0x1000>;
|
||||
interrupts = <47>;
|
||||
clocks = <&si5351 0>;
|
||||
clock-names = "ext_ref_clk_1";
|
||||
};
|
@ -18,6 +18,15 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct component_match {
|
||||
size_t alloc;
|
||||
size_t num;
|
||||
struct {
|
||||
void *data;
|
||||
int (*fn)(struct device *, void *);
|
||||
} compare[0];
|
||||
};
|
||||
|
||||
struct master {
|
||||
struct list_head node;
|
||||
struct list_head components;
|
||||
@ -25,6 +34,7 @@ struct master {
|
||||
|
||||
const struct component_master_ops *ops;
|
||||
struct device *dev;
|
||||
struct component_match *match;
|
||||
};
|
||||
|
||||
struct component {
|
||||
@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c)
|
||||
c->master = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a component to a master, finding the component via the compare
|
||||
* function and compare data. This is safe to call for duplicate matches
|
||||
* and will not result in the same component being added multiple times.
|
||||
*/
|
||||
int component_master_add_child(struct master *master,
|
||||
int (*compare)(struct device *, void *), void *compare_data)
|
||||
{
|
||||
@ -76,11 +91,12 @@ int component_master_add_child(struct master *master,
|
||||
int ret = -ENXIO;
|
||||
|
||||
list_for_each_entry(c, &component_list, node) {
|
||||
if (c->master)
|
||||
if (c->master && c->master != master)
|
||||
continue;
|
||||
|
||||
if (compare(c->dev, compare_data)) {
|
||||
component_attach_master(master, c);
|
||||
if (!c->master)
|
||||
component_attach_master(master, c);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
@ -90,6 +106,34 @@ int component_master_add_child(struct master *master,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add_child);
|
||||
|
||||
static int find_components(struct master *master)
|
||||
{
|
||||
struct component_match *match = master->match;
|
||||
size_t i;
|
||||
int ret = 0;
|
||||
|
||||
if (!match) {
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
return master->ops->add_components(master->dev, master);
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the array of match functions and attach
|
||||
* any components which are found to this master.
|
||||
*/
|
||||
for (i = 0; i < match->num; i++) {
|
||||
ret = component_master_add_child(master,
|
||||
match->compare[i].fn,
|
||||
match->compare[i].data);
|
||||
if (ret)
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Detach all attached components from this master */
|
||||
static void master_remove_components(struct master *master)
|
||||
{
|
||||
@ -113,44 +157,44 @@ static void master_remove_components(struct master *master)
|
||||
static int try_to_bring_up_master(struct master *master,
|
||||
struct component *component)
|
||||
{
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (!master->bound) {
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
if (master->ops->add_components(master->dev, master)) {
|
||||
/* Failed to find all components */
|
||||
master_remove_components(master);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
if (master->bound)
|
||||
return 0;
|
||||
|
||||
if (component && component->master != master) {
|
||||
master_remove_components(master);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Found all components */
|
||||
ret = master->ops->bind(master->dev);
|
||||
if (ret < 0) {
|
||||
devres_release_group(master->dev, NULL);
|
||||
dev_info(master->dev, "master bind failed: %d\n", ret);
|
||||
master_remove_components(master);
|
||||
goto out;
|
||||
}
|
||||
|
||||
master->bound = true;
|
||||
ret = 1;
|
||||
/*
|
||||
* Search the list of components, looking for components that
|
||||
* belong to this master, and attach them to the master.
|
||||
*/
|
||||
if (find_components(master)) {
|
||||
/* Failed to find all components */
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (component && component->master != master) {
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Found all components */
|
||||
ret = master->ops->bind(master->dev);
|
||||
if (ret < 0) {
|
||||
devres_release_group(master->dev, NULL);
|
||||
dev_info(master->dev, "master bind failed: %d\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
master->bound = true;
|
||||
return 1;
|
||||
|
||||
out:
|
||||
master_remove_components(master);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -180,18 +224,89 @@ static void take_down_master(struct master *master)
|
||||
master_remove_components(master);
|
||||
}
|
||||
|
||||
int component_master_add(struct device *dev,
|
||||
const struct component_master_ops *ops)
|
||||
static size_t component_match_size(size_t num)
|
||||
{
|
||||
return offsetof(struct component_match, compare[num]);
|
||||
}
|
||||
|
||||
static struct component_match *component_match_realloc(struct device *dev,
|
||||
struct component_match *match, size_t num)
|
||||
{
|
||||
struct component_match *new;
|
||||
|
||||
if (match && match->alloc == num)
|
||||
return match;
|
||||
|
||||
new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);
|
||||
if (!new)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (match) {
|
||||
memcpy(new, match, component_match_size(min(match->num, num)));
|
||||
devm_kfree(dev, match);
|
||||
} else {
|
||||
new->num = 0;
|
||||
}
|
||||
|
||||
new->alloc = num;
|
||||
|
||||
return new;
|
||||
}
|
||||
|
||||
/*
|
||||
* Add a component to be matched.
|
||||
*
|
||||
* The match array is first created or extended if necessary.
|
||||
*/
|
||||
void component_match_add(struct device *dev, struct component_match **matchptr,
|
||||
int (*compare)(struct device *, void *), void *compare_data)
|
||||
{
|
||||
struct component_match *match = *matchptr;
|
||||
|
||||
if (IS_ERR(match))
|
||||
return;
|
||||
|
||||
if (!match || match->num == match->alloc) {
|
||||
size_t new_size = match ? match->alloc + 16 : 15;
|
||||
|
||||
match = component_match_realloc(dev, match, new_size);
|
||||
|
||||
*matchptr = match;
|
||||
|
||||
if (IS_ERR(match))
|
||||
return;
|
||||
}
|
||||
|
||||
match->compare[match->num].fn = compare;
|
||||
match->compare[match->num].data = compare_data;
|
||||
match->num++;
|
||||
}
|
||||
EXPORT_SYMBOL(component_match_add);
|
||||
|
||||
int component_master_add_with_match(struct device *dev,
|
||||
const struct component_master_ops *ops,
|
||||
struct component_match *match)
|
||||
{
|
||||
struct master *master;
|
||||
int ret;
|
||||
|
||||
if (ops->add_components && match)
|
||||
return -EINVAL;
|
||||
|
||||
if (match) {
|
||||
/* Reallocate the match array for its true size */
|
||||
match = component_match_realloc(dev, match, match->num);
|
||||
if (IS_ERR(match))
|
||||
return PTR_ERR(match);
|
||||
}
|
||||
|
||||
master = kzalloc(sizeof(*master), GFP_KERNEL);
|
||||
if (!master)
|
||||
return -ENOMEM;
|
||||
|
||||
master->dev = dev;
|
||||
master->ops = ops;
|
||||
master->match = match;
|
||||
INIT_LIST_HEAD(&master->components);
|
||||
|
||||
/* Add to the list of available masters. */
|
||||
@ -209,6 +324,13 @@ int component_master_add(struct device *dev,
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add_with_match);
|
||||
|
||||
int component_master_add(struct device *dev,
|
||||
const struct component_master_ops *ops)
|
||||
{
|
||||
return component_master_add_with_match(dev, ops, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(component_master_add);
|
||||
|
||||
void component_master_del(struct device *dev,
|
||||
|
@ -20,11 +20,12 @@ drm-$(CONFIG_COMPAT) += drm_ioc32.o
|
||||
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
|
||||
drm-$(CONFIG_PCI) += ati_pcigart.o
|
||||
drm-$(CONFIG_DRM_PANEL) += drm_panel.o
|
||||
drm-$(CONFIG_OF) += drm_of.o
|
||||
|
||||
drm-usb-y := drm_usb.o
|
||||
|
||||
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_KMS_FB_HELPER) += drm_fb_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
|
||||
|
@ -15,20 +15,19 @@
|
||||
#include "armada_drm.h"
|
||||
#include "armada_hw.h"
|
||||
|
||||
static int armada510_init(struct armada_private *priv, struct device *dev)
|
||||
static int armada510_crtc_init(struct armada_crtc *dcrtc, struct device *dev)
|
||||
{
|
||||
priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
|
||||
struct clk *clk;
|
||||
|
||||
if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
|
||||
priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
|
||||
clk = devm_clk_get(dev, "ext_ref_clk1");
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk) == -ENOENT ? -EPROBE_DEFER : PTR_ERR(clk);
|
||||
|
||||
return PTR_RET(priv->extclk[0]);
|
||||
}
|
||||
dcrtc->extclk[0] = clk;
|
||||
|
||||
static int armada510_crtc_init(struct armada_crtc *dcrtc)
|
||||
{
|
||||
/* Lower the watermark so to eliminate jitter at higher bandwidths */
|
||||
armada_updatel(0x20, (1 << 11) | 0xff, dcrtc->base + LCD_CFG_RDREG4F);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -45,8 +44,7 @@ static int armada510_crtc_init(struct armada_crtc *dcrtc)
|
||||
static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
|
||||
const struct drm_display_mode *mode, uint32_t *sclk)
|
||||
{
|
||||
struct armada_private *priv = dcrtc->crtc.dev->dev_private;
|
||||
struct clk *clk = priv->extclk[0];
|
||||
struct clk *clk = dcrtc->extclk[0];
|
||||
int ret;
|
||||
|
||||
if (dcrtc->num == 1)
|
||||
@ -81,7 +79,6 @@ static int armada510_crtc_compute_clock(struct armada_crtc *dcrtc,
|
||||
const struct armada_variant armada510_ops = {
|
||||
.has_spu_adv_reg = true,
|
||||
.spu_adv_reg = ADV_HWC32ENABLE | ADV_HWC32ARGB | ADV_HWC32BLEND,
|
||||
.init = armada510_init,
|
||||
.crtc_init = armada510_crtc_init,
|
||||
.crtc_compute_clock = armada510_crtc_compute_clock,
|
||||
.init = armada510_crtc_init,
|
||||
.compute_clock = armada510_crtc_compute_clock,
|
||||
};
|
||||
|
@ -7,6 +7,9 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include "armada_crtc.h"
|
||||
@ -332,24 +335,23 @@ static void armada_drm_crtc_commit(struct drm_crtc *crtc)
|
||||
static bool armada_drm_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
const struct drm_display_mode *mode, struct drm_display_mode *adj)
|
||||
{
|
||||
struct armada_private *priv = crtc->dev->dev_private;
|
||||
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
||||
int ret;
|
||||
|
||||
/* We can't do interlaced modes if we don't have the SPU_ADV_REG */
|
||||
if (!priv->variant->has_spu_adv_reg &&
|
||||
if (!dcrtc->variant->has_spu_adv_reg &&
|
||||
adj->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return false;
|
||||
|
||||
/* Check whether the display mode is possible */
|
||||
ret = priv->variant->crtc_compute_clock(dcrtc, adj, NULL);
|
||||
ret = dcrtc->variant->compute_clock(dcrtc, adj, NULL);
|
||||
if (ret)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
|
||||
static void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
|
||||
{
|
||||
struct armada_vbl_event *e, *n;
|
||||
void __iomem *base = dcrtc->base;
|
||||
@ -410,6 +412,27 @@ void armada_drm_crtc_irq(struct armada_crtc *dcrtc, u32 stat)
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t armada_drm_irq(int irq, void *arg)
|
||||
{
|
||||
struct armada_crtc *dcrtc = arg;
|
||||
u32 v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
|
||||
|
||||
/*
|
||||
* This is rediculous - rather than writing bits to clear, we
|
||||
* have to set the actual status register value. This is racy.
|
||||
*/
|
||||
writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
|
||||
|
||||
/* Mask out those interrupts we haven't enabled */
|
||||
v = stat & dcrtc->irq_ena;
|
||||
|
||||
if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
|
||||
armada_drm_crtc_irq(dcrtc, stat);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
/* These are locked by dev->vbl_lock */
|
||||
void armada_drm_crtc_disable_irq(struct armada_crtc *dcrtc, u32 mask)
|
||||
{
|
||||
@ -470,7 +493,6 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode, struct drm_display_mode *adj,
|
||||
int x, int y, struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct armada_private *priv = crtc->dev->dev_private;
|
||||
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
||||
struct armada_regs regs[17];
|
||||
uint32_t lm, rm, tm, bm, val, sclk;
|
||||
@ -515,7 +537,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
||||
}
|
||||
|
||||
/* Now compute the divider for real */
|
||||
priv->variant->crtc_compute_clock(dcrtc, adj, &sclk);
|
||||
dcrtc->variant->compute_clock(dcrtc, adj, &sclk);
|
||||
|
||||
/* Ensure graphic fifo is enabled */
|
||||
armada_reg_queue_mod(regs, i, 0, CFG_PDWN64x66, LCD_SPU_SRAM_PARA1);
|
||||
@ -537,7 +559,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
||||
dcrtc->v[1].spu_v_porch = tm << 16 | bm;
|
||||
val = adj->crtc_hsync_start;
|
||||
dcrtc->v[1].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
|
||||
priv->variant->spu_adv_reg;
|
||||
dcrtc->variant->spu_adv_reg;
|
||||
|
||||
if (interlaced) {
|
||||
/* Odd interlaced frame */
|
||||
@ -546,7 +568,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
||||
dcrtc->v[0].spu_v_porch = dcrtc->v[1].spu_v_porch + 1;
|
||||
val = adj->crtc_hsync_start - adj->crtc_htotal / 2;
|
||||
dcrtc->v[0].spu_adv_reg = val << 20 | val | ADV_VSYNCOFFEN |
|
||||
priv->variant->spu_adv_reg;
|
||||
dcrtc->variant->spu_adv_reg;
|
||||
} else {
|
||||
dcrtc->v[0] = dcrtc->v[1];
|
||||
}
|
||||
@ -561,7 +583,7 @@ static int armada_drm_crtc_mode_set(struct drm_crtc *crtc,
|
||||
armada_reg_queue_set(regs, i, dcrtc->v[0].spu_v_h_total,
|
||||
LCD_SPUT_V_H_TOTAL);
|
||||
|
||||
if (priv->variant->has_spu_adv_reg) {
|
||||
if (dcrtc->variant->has_spu_adv_reg) {
|
||||
armada_reg_queue_mod(regs, i, dcrtc->v[0].spu_adv_reg,
|
||||
ADV_VSYNC_L_OFF | ADV_VSYNC_H_OFF |
|
||||
ADV_VSYNCOFFEN, LCD_SPU_ADV_REG);
|
||||
@ -805,12 +827,11 @@ static int armada_drm_crtc_cursor_set(struct drm_crtc *crtc,
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
||||
struct armada_private *priv = crtc->dev->dev_private;
|
||||
struct armada_gem_object *obj = NULL;
|
||||
int ret;
|
||||
|
||||
/* If no cursor support, replicate drm's return value */
|
||||
if (!priv->variant->has_spu_adv_reg)
|
||||
if (!dcrtc->variant->has_spu_adv_reg)
|
||||
return -ENXIO;
|
||||
|
||||
if (handle && w > 0 && h > 0) {
|
||||
@ -858,11 +879,10 @@ static int armada_drm_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
|
||||
struct armada_private *priv = crtc->dev->dev_private;
|
||||
int ret;
|
||||
|
||||
/* If no cursor support, replicate drm's return value */
|
||||
if (!priv->variant->has_spu_adv_reg)
|
||||
if (!dcrtc->variant->has_spu_adv_reg)
|
||||
return -EFAULT;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
@ -888,6 +908,10 @@ static void armada_drm_crtc_destroy(struct drm_crtc *crtc)
|
||||
if (!IS_ERR(dcrtc->clk))
|
||||
clk_disable_unprepare(dcrtc->clk);
|
||||
|
||||
writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ENA);
|
||||
|
||||
of_node_put(dcrtc->crtc.port);
|
||||
|
||||
kfree(dcrtc);
|
||||
}
|
||||
|
||||
@ -1027,19 +1051,20 @@ static int armada_drm_crtc_create_properties(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
||||
struct resource *res)
|
||||
int armada_drm_crtc_create(struct drm_device *drm, struct device *dev,
|
||||
struct resource *res, int irq, const struct armada_variant *variant,
|
||||
struct device_node *port)
|
||||
{
|
||||
struct armada_private *priv = dev->dev_private;
|
||||
struct armada_private *priv = drm->dev_private;
|
||||
struct armada_crtc *dcrtc;
|
||||
void __iomem *base;
|
||||
int ret;
|
||||
|
||||
ret = armada_drm_crtc_create_properties(dev);
|
||||
ret = armada_drm_crtc_create_properties(drm);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
base = devm_request_and_ioremap(dev->dev, res);
|
||||
base = devm_request_and_ioremap(dev, res);
|
||||
if (!base) {
|
||||
DRM_ERROR("failed to ioremap register\n");
|
||||
return -ENOMEM;
|
||||
@ -1051,8 +1076,12 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (dev != drm->dev)
|
||||
dev_set_drvdata(dev, dcrtc);
|
||||
|
||||
dcrtc->variant = variant;
|
||||
dcrtc->base = base;
|
||||
dcrtc->num = num;
|
||||
dcrtc->num = drm->mode_config.num_crtc;
|
||||
dcrtc->clk = ERR_PTR(-EINVAL);
|
||||
dcrtc->csc_yuv_mode = CSC_AUTO;
|
||||
dcrtc->csc_rgb_mode = CSC_AUTO;
|
||||
@ -1074,9 +1103,18 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
||||
CFG_PDWN64x66, dcrtc->base + LCD_SPU_SRAM_PARA1);
|
||||
writel_relaxed(0x2032ff81, dcrtc->base + LCD_SPU_DMA_CTRL1);
|
||||
writel_relaxed(0x00000000, dcrtc->base + LCD_SPU_GRA_OVSA_HPXL_VLN);
|
||||
writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
|
||||
writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
|
||||
|
||||
if (priv->variant->crtc_init) {
|
||||
ret = priv->variant->crtc_init(dcrtc);
|
||||
ret = devm_request_irq(dev, irq, armada_drm_irq, 0, "armada_drm_crtc",
|
||||
dcrtc);
|
||||
if (ret < 0) {
|
||||
kfree(dcrtc);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dcrtc->variant->init) {
|
||||
ret = dcrtc->variant->init(dcrtc, dev);
|
||||
if (ret) {
|
||||
kfree(dcrtc);
|
||||
return ret;
|
||||
@ -1088,7 +1126,8 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
||||
|
||||
priv->dcrtc[dcrtc->num] = dcrtc;
|
||||
|
||||
drm_crtc_init(dev, &dcrtc->crtc, &armada_crtc_funcs);
|
||||
dcrtc->crtc.port = port;
|
||||
drm_crtc_init(drm, &dcrtc->crtc, &armada_crtc_funcs);
|
||||
drm_crtc_helper_add(&dcrtc->crtc, &armada_crtc_helper_funcs);
|
||||
|
||||
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_yuv_prop,
|
||||
@ -1096,5 +1135,107 @@ int armada_drm_crtc_create(struct drm_device *dev, unsigned num,
|
||||
drm_object_attach_property(&dcrtc->crtc.base, priv->csc_rgb_prop,
|
||||
dcrtc->csc_rgb_mode);
|
||||
|
||||
return armada_overlay_plane_create(dev, 1 << dcrtc->num);
|
||||
return armada_overlay_plane_create(drm, 1 << dcrtc->num);
|
||||
}
|
||||
|
||||
static int
|
||||
armada_lcd_bind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct drm_device *drm = data;
|
||||
struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
int irq = platform_get_irq(pdev, 0);
|
||||
const struct armada_variant *variant;
|
||||
struct device_node *port = NULL;
|
||||
|
||||
if (irq < 0)
|
||||
return irq;
|
||||
|
||||
if (!dev->of_node) {
|
||||
const struct platform_device_id *id;
|
||||
|
||||
id = platform_get_device_id(pdev);
|
||||
if (!id)
|
||||
return -ENXIO;
|
||||
|
||||
variant = (const struct armada_variant *)id->driver_data;
|
||||
} else {
|
||||
const struct of_device_id *match;
|
||||
struct device_node *np, *parent = dev->of_node;
|
||||
|
||||
match = of_match_device(dev->driver->of_match_table, dev);
|
||||
if (!match)
|
||||
return -ENXIO;
|
||||
|
||||
np = of_get_child_by_name(parent, "ports");
|
||||
if (np)
|
||||
parent = np;
|
||||
port = of_get_child_by_name(parent, "port");
|
||||
of_node_put(np);
|
||||
if (!port) {
|
||||
dev_err(dev, "no port node found in %s\n",
|
||||
parent->full_name);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
variant = match->data;
|
||||
}
|
||||
|
||||
return armada_drm_crtc_create(drm, dev, res, irq, variant, port);
|
||||
}
|
||||
|
||||
static void
|
||||
armada_lcd_unbind(struct device *dev, struct device *master, void *data)
|
||||
{
|
||||
struct armada_crtc *dcrtc = dev_get_drvdata(dev);
|
||||
|
||||
armada_drm_crtc_destroy(&dcrtc->crtc);
|
||||
}
|
||||
|
||||
static const struct component_ops armada_lcd_ops = {
|
||||
.bind = armada_lcd_bind,
|
||||
.unbind = armada_lcd_unbind,
|
||||
};
|
||||
|
||||
static int armada_lcd_probe(struct platform_device *pdev)
|
||||
{
|
||||
return component_add(&pdev->dev, &armada_lcd_ops);
|
||||
}
|
||||
|
||||
static int armada_lcd_remove(struct platform_device *pdev)
|
||||
{
|
||||
component_del(&pdev->dev, &armada_lcd_ops);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct of_device_id armada_lcd_of_match[] = {
|
||||
{
|
||||
.compatible = "marvell,dove-lcd",
|
||||
.data = &armada510_ops,
|
||||
},
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, armada_lcd_of_match);
|
||||
|
||||
static const struct platform_device_id armada_lcd_platform_ids[] = {
|
||||
{
|
||||
.name = "armada-lcd",
|
||||
.driver_data = (unsigned long)&armada510_ops,
|
||||
}, {
|
||||
.name = "armada-510-lcd",
|
||||
.driver_data = (unsigned long)&armada510_ops,
|
||||
},
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, armada_lcd_platform_ids);
|
||||
|
||||
struct platform_driver armada_lcd_platform_driver = {
|
||||
.probe = armada_lcd_probe,
|
||||
.remove = armada_lcd_remove,
|
||||
.driver = {
|
||||
.name = "armada-lcd",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = armada_lcd_of_match,
|
||||
},
|
||||
.id_table = armada_lcd_platform_ids,
|
||||
};
|
||||
|
@ -32,12 +32,15 @@ struct armada_regs {
|
||||
armada_reg_queue_mod(_r, _i, 0, 0, ~0)
|
||||
|
||||
struct armada_frame_work;
|
||||
struct armada_variant;
|
||||
|
||||
struct armada_crtc {
|
||||
struct drm_crtc crtc;
|
||||
const struct armada_variant *variant;
|
||||
unsigned num;
|
||||
void __iomem *base;
|
||||
struct clk *clk;
|
||||
struct clk *extclk[2];
|
||||
struct {
|
||||
uint32_t spu_v_h_total;
|
||||
uint32_t spu_v_porch;
|
||||
@ -72,12 +75,16 @@ struct armada_crtc {
|
||||
};
|
||||
#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
|
||||
|
||||
int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
|
||||
struct device_node;
|
||||
int armada_drm_crtc_create(struct drm_device *, struct device *,
|
||||
struct resource *, int, const struct armada_variant *,
|
||||
struct device_node *);
|
||||
void armada_drm_crtc_gamma_set(struct drm_crtc *, u16, u16, u16, int);
|
||||
void armada_drm_crtc_gamma_get(struct drm_crtc *, u16 *, u16 *, u16 *, int);
|
||||
void armada_drm_crtc_irq(struct armada_crtc *, u32);
|
||||
void armada_drm_crtc_disable_irq(struct armada_crtc *, u32);
|
||||
void armada_drm_crtc_enable_irq(struct armada_crtc *, u32);
|
||||
void armada_drm_crtc_update_regs(struct armada_crtc *, struct armada_regs *);
|
||||
|
||||
extern struct platform_driver armada_lcd_platform_driver;
|
||||
|
||||
#endif
|
||||
|
@ -59,26 +59,23 @@ void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
|
||||
struct armada_private;
|
||||
|
||||
struct armada_variant {
|
||||
bool has_spu_adv_reg;
|
||||
bool has_spu_adv_reg;
|
||||
uint32_t spu_adv_reg;
|
||||
int (*init)(struct armada_private *, struct device *);
|
||||
int (*crtc_init)(struct armada_crtc *);
|
||||
int (*crtc_compute_clock)(struct armada_crtc *,
|
||||
const struct drm_display_mode *,
|
||||
uint32_t *);
|
||||
int (*init)(struct armada_crtc *, struct device *);
|
||||
int (*compute_clock)(struct armada_crtc *,
|
||||
const struct drm_display_mode *,
|
||||
uint32_t *);
|
||||
};
|
||||
|
||||
/* Variant ops */
|
||||
extern const struct armada_variant armada510_ops;
|
||||
|
||||
struct armada_private {
|
||||
const struct armada_variant *variant;
|
||||
struct work_struct fb_unref_work;
|
||||
DECLARE_KFIFO(fb_unref, struct drm_framebuffer *, 8);
|
||||
struct drm_fb_helper *fbdev;
|
||||
struct armada_crtc *dcrtc[2];
|
||||
struct drm_mm linear;
|
||||
struct clk *extclk[2];
|
||||
struct drm_property *csc_yuv_prop;
|
||||
struct drm_property *csc_rgb_prop;
|
||||
struct drm_property *colorkey_prop;
|
||||
|
@ -6,7 +6,9 @@
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#include <linux/clk.h>
|
||||
#include <linux/component.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include "armada_crtc.h"
|
||||
@ -52,6 +54,11 @@ static const struct armada_drm_slave_config tda19988_config = {
|
||||
};
|
||||
#endif
|
||||
|
||||
static bool is_componentized(struct device *dev)
|
||||
{
|
||||
return dev->of_node || dev->platform_data;
|
||||
}
|
||||
|
||||
static void armada_drm_unref_work(struct work_struct *work)
|
||||
{
|
||||
struct armada_private *priv =
|
||||
@ -85,6 +92,7 @@ void armada_drm_queue_unref_work(struct drm_device *dev,
|
||||
static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
const struct platform_device_id *id;
|
||||
const struct armada_variant *variant;
|
||||
struct armada_private *priv;
|
||||
struct resource *res[ARRAY_SIZE(priv->dcrtc)];
|
||||
struct resource *mem = NULL;
|
||||
@ -107,7 +115,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!res[0] || !mem)
|
||||
if (!mem)
|
||||
return -ENXIO;
|
||||
|
||||
if (!devm_request_mem_region(dev->dev, mem->start,
|
||||
@ -128,11 +136,7 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
if (!id)
|
||||
return -ENXIO;
|
||||
|
||||
priv->variant = (struct armada_variant *)id->driver_data;
|
||||
|
||||
ret = priv->variant->init(priv, dev->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
variant = (const struct armada_variant *)id->driver_data;
|
||||
|
||||
INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
|
||||
INIT_KFIFO(priv->fb_unref);
|
||||
@ -155,40 +159,50 @@ static int armada_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
/* Create all LCD controllers */
|
||||
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
|
||||
int irq;
|
||||
|
||||
if (!res[n])
|
||||
break;
|
||||
|
||||
ret = armada_drm_crtc_create(dev, n, res[n]);
|
||||
irq = platform_get_irq(dev->platformdev, n);
|
||||
if (irq < 0)
|
||||
goto err_kms;
|
||||
|
||||
ret = armada_drm_crtc_create(dev, dev->dev, res[n], irq,
|
||||
variant, NULL);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
}
|
||||
|
||||
if (is_componentized(dev->dev)) {
|
||||
ret = component_bind_all(dev->dev, dev);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
} else {
|
||||
#ifdef CONFIG_DRM_ARMADA_TDA1998X
|
||||
ret = armada_drm_connector_slave_create(dev, &tda19988_config);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
ret = armada_drm_connector_slave_create(dev, &tda19988_config);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
#endif
|
||||
}
|
||||
|
||||
ret = drm_vblank_init(dev, n);
|
||||
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
|
||||
ret = drm_irq_install(dev, platform_get_irq(dev->platformdev, 0));
|
||||
if (ret)
|
||||
goto err_kms;
|
||||
goto err_comp;
|
||||
|
||||
dev->vblank_disable_allowed = 1;
|
||||
|
||||
ret = armada_fbdev_init(dev);
|
||||
if (ret)
|
||||
goto err_irq;
|
||||
goto err_comp;
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err_irq:
|
||||
drm_irq_uninstall(dev);
|
||||
err_comp:
|
||||
if (is_componentized(dev->dev))
|
||||
component_unbind_all(dev->dev, dev);
|
||||
err_kms:
|
||||
drm_mode_config_cleanup(dev);
|
||||
drm_mm_takedown(&priv->linear);
|
||||
@ -203,7 +217,10 @@ static int armada_drm_unload(struct drm_device *dev)
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
armada_fbdev_fini(dev);
|
||||
drm_irq_uninstall(dev);
|
||||
|
||||
if (is_componentized(dev->dev))
|
||||
component_unbind_all(dev->dev, dev);
|
||||
|
||||
drm_mode_config_cleanup(dev);
|
||||
drm_mm_takedown(&priv->linear);
|
||||
flush_work(&priv->fb_unref_work);
|
||||
@ -259,52 +276,6 @@ static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
|
||||
armada_drm_crtc_disable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
|
||||
}
|
||||
|
||||
static irqreturn_t armada_drm_irq_handler(int irq, void *arg)
|
||||
{
|
||||
struct drm_device *dev = arg;
|
||||
struct armada_private *priv = dev->dev_private;
|
||||
struct armada_crtc *dcrtc = priv->dcrtc[0];
|
||||
uint32_t v, stat = readl_relaxed(dcrtc->base + LCD_SPU_IRQ_ISR);
|
||||
irqreturn_t handled = IRQ_NONE;
|
||||
|
||||
/*
|
||||
* This is rediculous - rather than writing bits to clear, we
|
||||
* have to set the actual status register value. This is racy.
|
||||
*/
|
||||
writel_relaxed(0, dcrtc->base + LCD_SPU_IRQ_ISR);
|
||||
|
||||
/* Mask out those interrupts we haven't enabled */
|
||||
v = stat & dcrtc->irq_ena;
|
||||
|
||||
if (v & (VSYNC_IRQ|GRA_FRAME_IRQ|DUMB_FRAMEDONE)) {
|
||||
armada_drm_crtc_irq(dcrtc, stat);
|
||||
handled = IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
static int armada_drm_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
struct armada_private *priv = dev->dev_private;
|
||||
struct armada_crtc *dcrtc = priv->dcrtc[0];
|
||||
|
||||
spin_lock_irq(&dev->vbl_lock);
|
||||
writel_relaxed(dcrtc->irq_ena, dcrtc->base + LCD_SPU_IRQ_ENA);
|
||||
writel(0, dcrtc->base + LCD_SPU_IRQ_ISR);
|
||||
spin_unlock_irq(&dev->vbl_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void armada_drm_irq_uninstall(struct drm_device *dev)
|
||||
{
|
||||
struct armada_private *priv = dev->dev_private;
|
||||
struct armada_crtc *dcrtc = priv->dcrtc[0];
|
||||
|
||||
writel(0, dcrtc->base + LCD_SPU_IRQ_ENA);
|
||||
}
|
||||
|
||||
static struct drm_ioctl_desc armada_ioctls[] = {
|
||||
DRM_IOCTL_DEF_DRV(ARMADA_GEM_CREATE, armada_gem_create_ioctl,
|
||||
DRM_UNLOCKED),
|
||||
@ -340,9 +311,6 @@ static struct drm_driver armada_drm_driver = {
|
||||
.get_vblank_counter = drm_vblank_count,
|
||||
.enable_vblank = armada_drm_enable_vblank,
|
||||
.disable_vblank = armada_drm_disable_vblank,
|
||||
.irq_handler = armada_drm_irq_handler,
|
||||
.irq_postinstall = armada_drm_irq_postinstall,
|
||||
.irq_uninstall = armada_drm_irq_uninstall,
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
.debugfs_init = armada_drm_debugfs_init,
|
||||
.debugfs_cleanup = armada_drm_debugfs_cleanup,
|
||||
@ -362,19 +330,140 @@ static struct drm_driver armada_drm_driver = {
|
||||
.desc = "Armada SoC DRM",
|
||||
.date = "20120730",
|
||||
.driver_features = DRIVER_GEM | DRIVER_MODESET |
|
||||
DRIVER_HAVE_IRQ | DRIVER_PRIME,
|
||||
DRIVER_PRIME,
|
||||
.ioctls = armada_ioctls,
|
||||
.fops = &armada_drm_fops,
|
||||
};
|
||||
|
||||
static int armada_drm_bind(struct device *dev)
|
||||
{
|
||||
return drm_platform_init(&armada_drm_driver, to_platform_device(dev));
|
||||
}
|
||||
|
||||
static void armada_drm_unbind(struct device *dev)
|
||||
{
|
||||
drm_put_dev(dev_get_drvdata(dev));
|
||||
}
|
||||
|
||||
static int compare_of(struct device *dev, void *data)
|
||||
{
|
||||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
static int compare_dev_name(struct device *dev, void *data)
|
||||
{
|
||||
const char *name = data;
|
||||
return !strcmp(dev_name(dev), name);
|
||||
}
|
||||
|
||||
static void armada_add_endpoints(struct device *dev,
|
||||
struct component_match **match, struct device_node *port)
|
||||
{
|
||||
struct device_node *ep, *remote;
|
||||
|
||||
for_each_child_of_node(port, ep) {
|
||||
remote = of_graph_get_remote_port_parent(ep);
|
||||
if (!remote || !of_device_is_available(remote)) {
|
||||
of_node_put(remote);
|
||||
continue;
|
||||
} else if (!of_device_is_available(remote->parent)) {
|
||||
dev_warn(dev, "parent device of %s is not available\n",
|
||||
remote->full_name);
|
||||
of_node_put(remote);
|
||||
continue;
|
||||
}
|
||||
|
||||
component_match_add(dev, match, compare_of, remote);
|
||||
of_node_put(remote);
|
||||
}
|
||||
}
|
||||
|
||||
static int armada_drm_find_components(struct device *dev,
|
||||
struct component_match **match)
|
||||
{
|
||||
struct device_node *port;
|
||||
int i;
|
||||
|
||||
if (dev->of_node) {
|
||||
struct device_node *np = dev->of_node;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
port = of_parse_phandle(np, "ports", i);
|
||||
if (!port)
|
||||
break;
|
||||
|
||||
component_match_add(dev, match, compare_of, port);
|
||||
of_node_put(port);
|
||||
}
|
||||
|
||||
if (i == 0) {
|
||||
dev_err(dev, "missing 'ports' property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
port = of_parse_phandle(np, "ports", i);
|
||||
if (!port)
|
||||
break;
|
||||
|
||||
armada_add_endpoints(dev, match, port);
|
||||
of_node_put(port);
|
||||
}
|
||||
} else if (dev->platform_data) {
|
||||
char **devices = dev->platform_data;
|
||||
struct device *d;
|
||||
|
||||
for (i = 0; devices[i]; i++)
|
||||
component_match_add(dev, match, compare_dev_name,
|
||||
devices[i]);
|
||||
|
||||
if (i == 0) {
|
||||
dev_err(dev, "missing 'ports' property\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
for (i = 0; devices[i]; i++) {
|
||||
d = bus_find_device_by_name(&platform_bus_type, NULL,
|
||||
devices[i]);
|
||||
if (d && d->of_node) {
|
||||
for_each_child_of_node(d->of_node, port)
|
||||
armada_add_endpoints(dev, match, port);
|
||||
}
|
||||
put_device(d);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct component_master_ops armada_master_ops = {
|
||||
.bind = armada_drm_bind,
|
||||
.unbind = armada_drm_unbind,
|
||||
};
|
||||
|
||||
static int armada_drm_probe(struct platform_device *pdev)
|
||||
{
|
||||
return drm_platform_init(&armada_drm_driver, pdev);
|
||||
if (is_componentized(&pdev->dev)) {
|
||||
struct component_match *match = NULL;
|
||||
int ret;
|
||||
|
||||
ret = armada_drm_find_components(&pdev->dev, &match);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return component_master_add_with_match(&pdev->dev,
|
||||
&armada_master_ops, match);
|
||||
} else {
|
||||
return drm_platform_init(&armada_drm_driver, pdev);
|
||||
}
|
||||
}
|
||||
|
||||
static int armada_drm_remove(struct platform_device *pdev)
|
||||
{
|
||||
drm_put_dev(platform_get_drvdata(pdev));
|
||||
if (is_componentized(&pdev->dev))
|
||||
component_master_del(&pdev->dev, &armada_master_ops);
|
||||
else
|
||||
drm_put_dev(platform_get_drvdata(pdev));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -402,14 +491,24 @@ static struct platform_driver armada_drm_platform_driver = {
|
||||
|
||||
static int __init armada_drm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
armada_drm_driver.num_ioctls = ARRAY_SIZE(armada_ioctls);
|
||||
return platform_driver_register(&armada_drm_platform_driver);
|
||||
|
||||
ret = platform_driver_register(&armada_lcd_platform_driver);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = platform_driver_register(&armada_drm_platform_driver);
|
||||
if (ret)
|
||||
platform_driver_unregister(&armada_lcd_platform_driver);
|
||||
return ret;
|
||||
}
|
||||
module_init(armada_drm_init);
|
||||
|
||||
static void __exit armada_drm_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&armada_drm_platform_driver);
|
||||
platform_driver_unregister(&armada_lcd_platform_driver);
|
||||
}
|
||||
module_exit(armada_drm_exit);
|
||||
|
||||
|
@ -131,7 +131,7 @@ static int armada_fb_probe(struct drm_fb_helper *fbh,
|
||||
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_get = armada_drm_crtc_gamma_get,
|
||||
.fb_probe = armada_fb_probe,
|
||||
@ -149,7 +149,7 @@ int armada_fbdev_init(struct drm_device *dev)
|
||||
|
||||
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);
|
||||
if (ret) {
|
||||
|
@ -48,7 +48,7 @@ static void armada_drm_connector_destroy(struct drm_connector *conn)
|
||||
{
|
||||
struct armada_connector *dconn = drm_to_armada_conn(conn);
|
||||
|
||||
drm_sysfs_connector_remove(conn);
|
||||
drm_connector_unregister(conn);
|
||||
drm_connector_cleanup(conn);
|
||||
kfree(dconn);
|
||||
}
|
||||
@ -141,7 +141,7 @@ int armada_output_create(struct drm_device *dev,
|
||||
if (ret)
|
||||
goto err_conn;
|
||||
|
||||
ret = drm_sysfs_connector_add(&dconn->conn);
|
||||
ret = drm_connector_register(&dconn->conn);
|
||||
if (ret)
|
||||
goto err_sysfs;
|
||||
|
||||
|
@ -362,7 +362,7 @@ static inline int ast_bo_reserve(struct ast_bo *bo, bool no_wait)
|
||||
{
|
||||
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 != -ERESTARTSYS && ret != -EBUSY)
|
||||
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,
|
||||
*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_get = ast_fb_gamma_get,
|
||||
.fb_probe = astfb_create,
|
||||
@ -328,8 +328,10 @@ int ast_fbdev_init(struct drm_device *dev)
|
||||
return -ENOMEM;
|
||||
|
||||
ast->fbdev = afbdev;
|
||||
afbdev->helper.funcs = &ast_fb_helper_funcs;
|
||||
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,
|
||||
1, 1);
|
||||
if (ret) {
|
||||
|
@ -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)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id) {
|
||||
obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
encoder = obj_to_encoder(obj);
|
||||
return encoder;
|
||||
}
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -829,7 +821,7 @@ static void ast_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct ast_connector *ast_connector = to_ast_connector(connector);
|
||||
ast_i2c_destroy(ast_connector->i2c);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -871,7 +863,7 @@ static int ast_connector_init(struct drm_device *dev)
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
|
||||
|
@ -97,6 +97,7 @@ static struct drm_driver bochs_driver = {
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* pm interface */
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int bochs_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
@ -131,6 +132,7 @@ static int bochs_pm_resume(struct device *dev)
|
||||
drm_kms_helper_poll_enable(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops bochs_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(bochs_pm_suspend,
|
||||
|
@ -72,7 +72,7 @@ static int bochsfb_create(struct drm_fb_helper *helper,
|
||||
|
||||
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)
|
||||
return ret;
|
||||
|
||||
@ -179,7 +179,7 @@ void bochs_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
|
||||
*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_get = bochs_fb_gamma_get,
|
||||
.fb_probe = bochsfb_create,
|
||||
@ -189,7 +189,8 @@ int bochs_fbdev_init(struct bochs_device *bochs)
|
||||
{
|
||||
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,
|
||||
1, 1);
|
||||
|
@ -53,7 +53,7 @@ static int bochs_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
|
||||
if (old_fb) {
|
||||
bochs_fb = to_bochs_framebuffer(old_fb);
|
||||
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) {
|
||||
DRM_ERROR("failed to reserve old_fb bo\n");
|
||||
} else {
|
||||
@ -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);
|
||||
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)
|
||||
return ret;
|
||||
|
||||
@ -216,18 +216,9 @@ static struct drm_encoder *
|
||||
bochs_connector_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id) {
|
||||
obj = drm_mode_object_find(connector->dev, enc_id,
|
||||
DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
encoder = obj_to_encoder(obj);
|
||||
return encoder;
|
||||
}
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -387,7 +387,7 @@ int bochs_gem_create(struct drm_device *dev, u32 size, bool iskernel,
|
||||
|
||||
*obj = NULL;
|
||||
|
||||
size = ALIGN(size, PAGE_SIZE);
|
||||
size = PAGE_ALIGN(size);
|
||||
if (size == 0)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -328,7 +328,7 @@ int ptn3460_init(struct drm_device *dev, struct drm_encoder *encoder,
|
||||
}
|
||||
drm_connector_helper_add(&ptn_bridge->connector,
|
||||
&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);
|
||||
|
||||
return 0;
|
||||
|
@ -76,6 +76,7 @@ static void cirrus_pci_remove(struct pci_dev *pdev)
|
||||
drm_put_dev(dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cirrus_pm_suspend(struct device *dev)
|
||||
{
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
@ -110,6 +111,7 @@ static int cirrus_pm_resume(struct device *dev)
|
||||
drm_kms_helper_poll_enable(drm_dev);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct file_operations cirrus_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
|
@ -241,7 +241,7 @@ static inline int cirrus_bo_reserve(struct cirrus_bo *bo, bool no_wait)
|
||||
{
|
||||
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 != -ERESTARTSYS && ret != -EBUSY)
|
||||
DRM_ERROR("reserve failed %p\n", bo);
|
||||
|
@ -288,7 +288,7 @@ static int cirrus_fbdev_destroy(struct drm_device *dev,
|
||||
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_get = cirrus_crtc_fb_gamma_get,
|
||||
.fb_probe = cirrusfb_create,
|
||||
@ -306,9 +306,11 @@ int cirrus_fbdev_init(struct cirrus_device *cdev)
|
||||
return -ENOMEM;
|
||||
|
||||
cdev->mode_info.gfbdev = gfbdev;
|
||||
gfbdev->helper.funcs = &cirrus_fb_helper_funcs;
|
||||
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,
|
||||
cdev->num_crtc, CIRRUSFB_CONN_LIMIT);
|
||||
if (ret) {
|
||||
|
@ -509,19 +509,9 @@ static struct drm_encoder *cirrus_connector_best_encoder(struct drm_connector
|
||||
*connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id) {
|
||||
obj =
|
||||
drm_mode_object_find(connector->dev, enc_id,
|
||||
DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
encoder = obj_to_encoder(obj);
|
||||
return encoder;
|
||||
}
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -80,11 +80,7 @@ int drm_buffer_alloc(struct drm_buffer **buf, int size)
|
||||
|
||||
error_out:
|
||||
|
||||
/* Only last element can be null pointer so check for it first. */
|
||||
if ((*buf)->data[idx])
|
||||
kfree((*buf)->data[idx]);
|
||||
|
||||
for (--idx; idx >= 0; --idx)
|
||||
for (; idx >= 0; --idx)
|
||||
kfree((*buf)->data[idx]);
|
||||
|
||||
kfree(*buf);
|
||||
|
@ -894,6 +894,8 @@ int drm_connector_init(struct drm_device *dev,
|
||||
drm_object_attach_property(&connector->base,
|
||||
dev->mode_config.dpms_property, 0);
|
||||
|
||||
connector->debugfs_entry = NULL;
|
||||
|
||||
out_put:
|
||||
if (ret)
|
||||
drm_mode_object_put(dev, &connector->base);
|
||||
@ -933,6 +935,47 @@ void drm_connector_cleanup(struct drm_connector *connector)
|
||||
}
|
||||
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
|
||||
* @dev: drm device
|
||||
@ -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 */
|
||||
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);
|
||||
@ -1227,6 +1270,7 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
|
||||
{
|
||||
struct drm_property *edid;
|
||||
struct drm_property *dpms;
|
||||
struct drm_property *dev_path;
|
||||
|
||||
/*
|
||||
* Standard properties (apply to all connectors)
|
||||
@ -1241,6 +1285,12 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
|
||||
ARRAY_SIZE(drm_dpms_enum_list));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -1510,6 +1560,15 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
|
||||
}
|
||||
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
|
||||
* @out: drm_mode_modeinfo struct to return to the user
|
||||
@ -3362,7 +3421,7 @@ fail:
|
||||
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
|
||||
* @flags: flags specifying the property type
|
||||
* @name: name of the property
|
||||
@ -3408,7 +3467,7 @@ struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags,
|
||||
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
|
||||
* @flags: flags specifying the property type
|
||||
* @name: name of the property
|
||||
@ -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
|
||||
* @flags: flags specifying the property type
|
||||
* @name: name of the property
|
||||
@ -3898,6 +3957,25 @@ done:
|
||||
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
|
||||
* @connector: drm connector
|
||||
@ -3915,6 +3993,10 @@ int drm_mode_connector_update_edid_property(struct drm_connector *connector,
|
||||
struct drm_device *dev = connector->dev;
|
||||
int ret, size;
|
||||
|
||||
/* ignore requests to set edid when overridden */
|
||||
if (connector->override_edid)
|
||||
return 0;
|
||||
|
||||
if (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,
|
||||
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
|
||||
&fb->bits_per_pixel);
|
||||
fb->pixel_format = mode_cmd->pixel_format;
|
||||
fb->flags = mode_cmd->flags;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
|
||||
|
||||
|
@ -35,6 +35,7 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/export.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
#if defined(CONFIG_DEBUG_FS)
|
||||
|
||||
@ -237,5 +238,186 @@ int drm_debugfs_cleanup(struct drm_minor *minor)
|
||||
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 */
|
||||
|
||||
|
2715
drivers/gpu/drm/drm_dp_mst_topology.c
Normal file
2715
drivers/gpu/drm/drm_dp_mst_topology.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -233,7 +233,7 @@ module_exit(drm_core_exit);
|
||||
/**
|
||||
* 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;
|
||||
|
||||
|
@ -3305,6 +3305,7 @@ struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
|
||||
struct drm_device *dev = encoder->dev;
|
||||
|
||||
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)
|
||||
if (connector->encoder == encoder && connector->eld[0])
|
||||
|
@ -327,7 +327,7 @@ err_drm_gem_cma_free_object:
|
||||
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,
|
||||
};
|
||||
|
||||
@ -354,9 +354,10 @@ struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
|
||||
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);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
|
||||
|
@ -49,10 +49,11 @@ static LIST_HEAD(kernel_fb_helper_list);
|
||||
* helper functions used by many drivers to implement the kernel mode setting
|
||||
* interfaces.
|
||||
*
|
||||
* Initialization is done as a three-step process with drm_fb_helper_init(),
|
||||
* drm_fb_helper_single_add_all_connectors() and drm_fb_helper_initial_config().
|
||||
* Drivers with fancier requirements than the default behaviour can override the
|
||||
* second step with their own code. Teardown is done with drm_fb_helper_fini().
|
||||
* Initialization is done as a four-step process with drm_fb_helper_prepare(),
|
||||
* drm_fb_helper_init(), drm_fb_helper_single_add_all_connectors() and
|
||||
* drm_fb_helper_initial_config(). Drivers with fancier requirements than the
|
||||
* 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
|
||||
* drm_fb_helper_restore_fbdev_mode() from their ->lastclose callback. They
|
||||
@ -63,6 +64,19 @@ static LIST_HEAD(kernel_fb_helper_list);
|
||||
*
|
||||
* All other functions exported by the fb helper library can be used to
|
||||
* 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 @@ fail:
|
||||
}
|
||||
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)
|
||||
{
|
||||
struct drm_fb_helper_connector *fb_helper_conn;
|
||||
@ -199,9 +265,6 @@ int drm_fb_helper_debug_enter(struct fb_info *info)
|
||||
struct drm_crtc_helper_funcs *funcs;
|
||||
int i;
|
||||
|
||||
if (list_empty(&kernel_fb_helper_list))
|
||||
return false;
|
||||
|
||||
list_for_each_entry(helper, &kernel_fb_helper_list, kernel_fb_list) {
|
||||
for (i = 0; i < helper->crtc_count; i++) {
|
||||
struct drm_mode_set *mode_set =
|
||||
@ -530,6 +593,24 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
|
||||
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
|
||||
* @dev: drm device
|
||||
@ -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()
|
||||
* to allow driver writes more control over the exact init sequence.
|
||||
*
|
||||
* Drivers must set fb_helper->funcs before calling
|
||||
* drm_fb_helper_initial_config().
|
||||
* Drivers must call drm_fb_helper_prepare() before calling this function.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero if everything went ok, nonzero otherwise.
|
||||
@ -558,10 +638,6 @@ int drm_fb_helper_init(struct drm_device *dev,
|
||||
if (!max_conn_count)
|
||||
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);
|
||||
if (!fb_helper->crtc_info)
|
||||
return -ENOMEM;
|
||||
@ -572,6 +648,7 @@ int drm_fb_helper_init(struct drm_device *dev,
|
||||
kfree(fb_helper->crtc_info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
fb_helper->connector_info_alloc_count = dev->mode_config.num_connector;
|
||||
fb_helper->connector_count = 0;
|
||||
|
||||
for (i = 0; i < crtc_count; i++) {
|
||||
@ -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.ywrapstep = 0;
|
||||
info->fix.accel = FB_ACCEL_NONE;
|
||||
info->fix.type_aux = 0;
|
||||
|
||||
info->fix.line_length = pitch;
|
||||
return;
|
||||
@ -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
|
||||
* hotplug interrupt).
|
||||
*
|
||||
* Note that the driver must ensure that this is only called _after_ the fb has
|
||||
* been fully set up, i.e. after the call to drm_fb_helper_initial_config.
|
||||
* Note that drivers may call this even before calling
|
||||
* 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:
|
||||
* 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)
|
||||
struct drm_device *dev = fb_helper->dev;
|
||||
u32 max_width, max_height;
|
||||
|
||||
if (!fb_helper->fb)
|
||||
return 0;
|
||||
|
||||
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;
|
||||
mutex_unlock(&fb_helper->dev->mode_config.mutex);
|
||||
return 0;
|
||||
|
@ -441,18 +441,31 @@ EXPORT_SYMBOL(drm_gem_create_mmap_offset);
|
||||
* drm_gem_get_pages - helper to allocate backing pages for a GEM object
|
||||
* from shmem
|
||||
* @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 page *p, **pages;
|
||||
int i, npages;
|
||||
|
||||
/* This is the shared memory object that backs the GEM resource */
|
||||
inode = file_inode(obj->filp);
|
||||
mapping = inode->i_mapping;
|
||||
mapping = file_inode(obj->filp)->i_mapping;
|
||||
|
||||
/* We already BUG_ON() for non-page-aligned sizes in
|
||||
* 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)
|
||||
if (pages == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
gfpmask |= mapping_gfp_mask(mapping);
|
||||
|
||||
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))
|
||||
goto fail;
|
||||
pages[i] = p;
|
||||
@ -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)
|
||||
* 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));
|
||||
}
|
||||
|
||||
|
@ -327,7 +327,7 @@ drm_gem_cma_prime_import_sg_table(struct drm_device *dev, size_t size,
|
||||
/* Create a CMA GEM buffer. */
|
||||
cma_obj = __drm_gem_cma_create(dev, size);
|
||||
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->sgt = sgt;
|
||||
|
@ -342,8 +342,6 @@ drm_setclientcap(struct drm_device *dev, void *data, struct drm_file *file_priv)
|
||||
file_priv->stereo_allowed = req->value;
|
||||
break;
|
||||
case DRM_CLIENT_CAP_UNIVERSAL_PLANES:
|
||||
if (!drm_universal_planes)
|
||||
return -EINVAL;
|
||||
if (req->value > 1)
|
||||
return -EINVAL;
|
||||
file_priv->universal_planes = req->value;
|
||||
|
67
drivers/gpu/drm/drm_of.c
Normal file
67
drivers/gpu/drm/drm_of.c
Normal file
@ -0,0 +1,67 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_of.h>
|
||||
|
||||
/**
|
||||
* drm_crtc_port_mask - find the mask of a registered CRTC by port OF node
|
||||
* @dev: DRM device
|
||||
* @port: port OF node
|
||||
*
|
||||
* Given a port OF node, return the possible mask of the corresponding
|
||||
* CRTC within a device's list of CRTCs. Returns zero if not found.
|
||||
*/
|
||||
static uint32_t drm_crtc_port_mask(struct drm_device *dev,
|
||||
struct device_node *port)
|
||||
{
|
||||
unsigned int index = 0;
|
||||
struct drm_crtc *tmp;
|
||||
|
||||
list_for_each_entry(tmp, &dev->mode_config.crtc_list, head) {
|
||||
if (tmp->port == port)
|
||||
return 1 << index;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_of_find_possible_crtcs - find the possible CRTCs for an encoder port
|
||||
* @dev: DRM device
|
||||
* @port: encoder port to scan for endpoints
|
||||
*
|
||||
* Scan all endpoints attached to a port, locate their attached CRTCs,
|
||||
* and generate the DRM mask of CRTCs which may be attached to this
|
||||
* encoder.
|
||||
*
|
||||
* See Documentation/devicetree/bindings/graph.txt for the bindings.
|
||||
*/
|
||||
uint32_t drm_of_find_possible_crtcs(struct drm_device *dev,
|
||||
struct device_node *port)
|
||||
{
|
||||
struct device_node *remote_port, *ep = NULL;
|
||||
uint32_t possible_crtcs = 0;
|
||||
|
||||
do {
|
||||
ep = of_graph_get_next_endpoint(port, ep);
|
||||
if (!ep)
|
||||
break;
|
||||
|
||||
remote_port = of_graph_get_remote_port(ep);
|
||||
if (!remote_port) {
|
||||
of_node_put(ep);
|
||||
return 0;
|
||||
}
|
||||
|
||||
possible_crtcs |= drm_crtc_port_mask(dev, remote_port);
|
||||
|
||||
of_node_put(remote_port);
|
||||
} while (1);
|
||||
|
||||
return possible_crtcs;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_of_find_possible_crtcs);
|
@ -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 */
|
||||
ret = drm_plane_init(dev, primary, 0, &drm_primary_helper_funcs,
|
||||
formats, num_formats,
|
||||
DRM_PLANE_TYPE_PRIMARY);
|
||||
ret = drm_universal_plane_init(dev, primary, 0,
|
||||
&drm_primary_helper_funcs,
|
||||
formats, num_formats,
|
||||
DRM_PLANE_TYPE_PRIMARY);
|
||||
if (ret) {
|
||||
kfree(primary);
|
||||
primary = NULL;
|
||||
|
@ -130,7 +130,14 @@ static int drm_helper_probe_single_connector_modes_merge_bits(struct drm_connect
|
||||
count = drm_load_edid_firmware(connector);
|
||||
if (count == 0)
|
||||
#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)
|
||||
count = drm_add_modes_noedid(connector, 1024, 768);
|
||||
|
@ -37,18 +37,9 @@
|
||||
unsigned int drm_debug = 0; /* 1 to enable debug output */
|
||||
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. */
|
||||
EXPORT_SYMBOL(drm_vblank_offdelay);
|
||||
|
||||
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
|
||||
@ -60,14 +51,11 @@ MODULE_AUTHOR(CORE_AUTHOR);
|
||||
MODULE_DESCRIPTION(CORE_DESC);
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
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(timestamp_precision_usec, "Max. error on timestamps [usecs]");
|
||||
MODULE_PARM_DESC(timestamp_monotonic, "Use monotonic timestamps");
|
||||
|
||||
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(timestamp_precision_usec, drm_timestamp_precision, 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,
|
||||
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);
|
||||
if (ret)
|
||||
goto err_minors;
|
||||
|
@ -438,7 +438,6 @@ err_out_files:
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sysfs_connector_add);
|
||||
|
||||
/**
|
||||
* drm_sysfs_connector_remove - remove an connector device from sysfs
|
||||
@ -468,7 +467,6 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
|
||||
device_unregister(connector->kdev);
|
||||
connector->kdev = NULL;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sysfs_connector_remove);
|
||||
|
||||
/**
|
||||
* drm_sysfs_hotplug_event - generate a DRM uevent
|
||||
|
@ -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_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
|
@ -117,20 +117,7 @@ static struct drm_encoder *exynos_drm_best_encoder(
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct exynos_drm_connector *exynos_connector =
|
||||
to_exynos_connector(connector);
|
||||
struct drm_mode_object *obj;
|
||||
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;
|
||||
return drm_encoder_find(dev, exynos_connector->encoder_id);
|
||||
}
|
||||
|
||||
static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
|
||||
@ -185,7 +172,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
|
||||
struct exynos_drm_connector *exynos_connector =
|
||||
to_exynos_connector(connector);
|
||||
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(exynos_connector);
|
||||
}
|
||||
@ -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_helper_add(connector, &exynos_connector_helper_funcs);
|
||||
|
||||
err = drm_sysfs_connector_add(connector);
|
||||
err = drm_connector_register(connector);
|
||||
if (err)
|
||||
goto err_connector;
|
||||
|
||||
@ -250,7 +237,7 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
|
||||
return connector;
|
||||
|
||||
err_sysfs:
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
err_connector:
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(exynos_connector);
|
||||
|
@ -48,7 +48,7 @@ exynos_dpi_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
static void exynos_dpi_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
}
|
||||
|
||||
@ -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_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
|
@ -39,8 +39,6 @@
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
#define VBLANK_OFF_DELAY 50000
|
||||
|
||||
static struct platform_device *exynos_drm_pdev;
|
||||
|
||||
static DEFINE_MUTEX(drm_component_lock);
|
||||
@ -103,8 +101,6 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
/* setup possible_clones. */
|
||||
exynos_drm_encoder_setup(dev);
|
||||
|
||||
drm_vblank_offdelay = VBLANK_OFF_DELAY;
|
||||
|
||||
platform_set_drvdata(dev->platformdev, dev);
|
||||
|
||||
/* Try to bind all sub drivers. */
|
||||
|
@ -40,8 +40,6 @@ struct drm_device;
|
||||
struct exynos_drm_overlay;
|
||||
struct drm_connector;
|
||||
|
||||
extern unsigned int drm_vblank_offdelay;
|
||||
|
||||
/* This enumerates device type. */
|
||||
enum exynos_drm_device_type {
|
||||
EXYNOS_DEVICE_TYPE_NONE,
|
||||
|
@ -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_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
|
@ -225,7 +225,7 @@ out:
|
||||
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,
|
||||
};
|
||||
|
||||
@ -266,7 +266,8 @@ int exynos_drm_fbdev_init(struct drm_device *dev)
|
||||
return -ENOMEM;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -562,7 +562,7 @@ static int vidi_create_connector(struct exynos_drm_display *display,
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &vidi_connector_helper_funcs);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
|
@ -1129,7 +1129,7 @@ static int hdmi_create_connector(struct exynos_drm_display *display,
|
||||
}
|
||||
|
||||
drm_connector_helper_add(connector, &hdmi_connector_helper_funcs);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
|
||||
return 0;
|
||||
|
@ -192,7 +192,7 @@ static void cdv_intel_crt_destroy(struct drm_connector *connector)
|
||||
struct gma_encoder *gma_encoder = gma_attached_encoder(connector);
|
||||
|
||||
psb_intel_i2c_destroy(gma_encoder->ddc_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -304,7 +304,7 @@ void cdv_intel_crt_init(struct drm_device *dev,
|
||||
drm_connector_helper_add(connector,
|
||||
&cdv_intel_crt_connector_helper_funcs);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
return;
|
||||
failed_ddc:
|
||||
|
@ -1713,7 +1713,7 @@ cdv_intel_dp_destroy(struct drm_connector *connector)
|
||||
}
|
||||
}
|
||||
i2c_del_adapter(&intel_dp->adapter);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -1847,7 +1847,7 @@ cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
/* Set up the DDC bus. */
|
||||
switch (output_reg) {
|
||||
|
@ -248,7 +248,7 @@ static void cdv_hdmi_destroy(struct drm_connector *connector)
|
||||
|
||||
if (gma_encoder->i2c_bus)
|
||||
psb_intel_i2c_destroy(gma_encoder->i2c_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -356,7 +356,7 @@ void cdv_hdmi_init(struct drm_device *dev,
|
||||
|
||||
hdmi_priv->hdmi_i2c_adapter = &(gma_encoder->i2c_bus->adapter);
|
||||
hdmi_priv->dev = dev;
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return;
|
||||
|
||||
failed_ddc:
|
||||
|
@ -446,7 +446,7 @@ static void cdv_intel_lvds_destroy(struct drm_connector *connector)
|
||||
|
||||
if (gma_encoder->i2c_bus)
|
||||
psb_intel_i2c_destroy(gma_encoder->i2c_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -774,7 +774,7 @@ void cdv_intel_lvds_init(struct drm_device *dev,
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
|
@ -561,7 +561,7 @@ static int psbfb_probe(struct drm_fb_helper *helper,
|
||||
return psbfb_create(psb_fbdev, sizes);
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs psb_fb_helper_funcs = {
|
||||
static const struct drm_fb_helper_funcs psb_fb_helper_funcs = {
|
||||
.gamma_set = psbfb_gamma_set,
|
||||
.gamma_get = psbfb_gamma_get,
|
||||
.fb_probe = psbfb_probe,
|
||||
@ -600,7 +600,8 @@ int psb_fbdev_init(struct drm_device *dev)
|
||||
}
|
||||
|
||||
dev_priv->fbdev = fbdev;
|
||||
fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
|
||||
|
||||
drm_fb_helper_prepare(dev, &fbdev->psb_fb_helper, &psb_fb_helper_funcs);
|
||||
|
||||
drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
|
||||
INTELFB_CONN_LIMIT);
|
||||
|
@ -206,7 +206,7 @@ static int psb_gtt_attach_pages(struct gtt_range *gt)
|
||||
|
||||
WARN_ON(gt->pages);
|
||||
|
||||
pages = drm_gem_get_pages(>->gem, 0);
|
||||
pages = drm_gem_get_pages(>->gem);
|
||||
if (IS_ERR(pages))
|
||||
return PTR_ERR(pages);
|
||||
|
||||
|
@ -318,7 +318,7 @@ static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
|
||||
|
||||
if (!dsi_connector)
|
||||
return;
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
sender = dsi_connector->pkg_sender;
|
||||
mdfld_dsi_pkg_sender_destroy(sender);
|
||||
@ -597,7 +597,7 @@ void mdfld_dsi_output_init(struct drm_device *dev,
|
||||
dsi_config->encoder = encoder;
|
||||
encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
|
||||
INTEL_OUTPUT_MIPI2;
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return;
|
||||
|
||||
/*TODO: add code to destroy outputs on error*/
|
||||
|
@ -665,7 +665,7 @@ void oaktrail_hdmi_init(struct drm_device *dev,
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
dev_info(dev->dev, "HDMI initialised.\n");
|
||||
|
||||
return;
|
||||
|
@ -404,7 +404,7 @@ void oaktrail_lvds_init(struct drm_device *dev,
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
|
@ -563,7 +563,7 @@ void psb_intel_lvds_destroy(struct drm_connector *connector)
|
||||
|
||||
if (lvds_priv->ddc_bus)
|
||||
psb_intel_i2c_destroy(lvds_priv->ddc_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -829,7 +829,7 @@ void psb_intel_lvds_init(struct drm_device *dev,
|
||||
*/
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
|
@ -1682,7 +1682,7 @@ static void psb_intel_sdvo_destroy(struct drm_connector *connector)
|
||||
psb_intel_sdvo_connector->tv_format);
|
||||
|
||||
psb_intel_sdvo_destroy_enhance_property(connector);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
@ -2071,7 +2071,7 @@ psb_intel_sdvo_connector_init(struct psb_intel_sdvo_connector *connector,
|
||||
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
|
||||
gma_connector_attach_encoder(&connector->base, &encoder->base);
|
||||
drm_sysfs_connector_add(&connector->base.base);
|
||||
drm_connector_register(&connector->base.base);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1196,8 +1196,7 @@ tda998x_encoder_destroy(struct drm_encoder *encoder)
|
||||
if (priv->hdmi->irq)
|
||||
free_irq(priv->hdmi->irq, priv);
|
||||
|
||||
if (priv->cec)
|
||||
i2c_unregister_device(priv->cec);
|
||||
i2c_unregister_device(priv->cec);
|
||||
drm_i2c_encoder_destroy(encoder);
|
||||
kfree(priv);
|
||||
}
|
||||
|
@ -59,6 +59,7 @@ i915-y += dvo_ch7017.o \
|
||||
intel_crt.o \
|
||||
intel_ddi.o \
|
||||
intel_dp.o \
|
||||
intel_dp_mst.o \
|
||||
intel_dsi_cmd.o \
|
||||
intel_dsi.o \
|
||||
intel_dsi_pll.o \
|
||||
|
@ -2216,13 +2216,15 @@ static void intel_connector_info(struct seq_file *m,
|
||||
seq_printf(m, "\tCEA rev: %d\n",
|
||||
connector->display_info.cea_rev);
|
||||
}
|
||||
if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
|
||||
intel_encoder->type == INTEL_OUTPUT_EDP)
|
||||
intel_dp_info(m, intel_connector);
|
||||
else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
|
||||
intel_hdmi_info(m, intel_connector);
|
||||
else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
|
||||
intel_lvds_info(m, intel_connector);
|
||||
if (intel_encoder) {
|
||||
if (intel_encoder->type == INTEL_OUTPUT_DISPLAYPORT ||
|
||||
intel_encoder->type == INTEL_OUTPUT_EDP)
|
||||
intel_dp_info(m, intel_connector);
|
||||
else if (intel_encoder->type == INTEL_OUTPUT_HDMI)
|
||||
intel_hdmi_info(m, intel_connector);
|
||||
else if (intel_encoder->type == INTEL_OUTPUT_LVDS)
|
||||
intel_lvds_info(m, intel_connector);
|
||||
}
|
||||
|
||||
seq_printf(m, "\tmodes:\n");
|
||||
list_for_each_entry(mode, &connector->modes, head)
|
||||
@ -2410,6 +2412,28 @@ struct pipe_crc_info {
|
||||
enum pipe pipe;
|
||||
};
|
||||
|
||||
static int i915_dp_mst_info(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_encoder *encoder;
|
||||
struct intel_encoder *intel_encoder;
|
||||
struct intel_digital_port *intel_dig_port;
|
||||
drm_modeset_lock_all(dev);
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
intel_encoder = to_intel_encoder(encoder);
|
||||
if (intel_encoder->type != INTEL_OUTPUT_DISPLAYPORT)
|
||||
continue;
|
||||
intel_dig_port = enc_to_dig_port(encoder);
|
||||
if (!intel_dig_port->dp.can_mst)
|
||||
continue;
|
||||
|
||||
drm_dp_mst_dump_topology(m, &intel_dig_port->dp.mst_mgr);
|
||||
}
|
||||
drm_modeset_unlock_all(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_pipe_crc_open(struct inode *inode, struct file *filep)
|
||||
{
|
||||
struct pipe_crc_info *info = inode->i_private;
|
||||
@ -3911,6 +3935,7 @@ static const struct drm_info_list i915_debugfs_list[] = {
|
||||
{"i915_display_info", i915_display_info, 0},
|
||||
{"i915_semaphore_status", i915_semaphore_status, 0},
|
||||
{"i915_shared_dplls_info", i915_shared_dplls_info, 0},
|
||||
{"i915_dp_mst_info", i915_dp_mst_info, 0},
|
||||
};
|
||||
#define I915_DEBUGFS_ENTRIES ARRAY_SIZE(i915_debugfs_list)
|
||||
|
||||
|
@ -1379,9 +1379,6 @@ static int i915_load_modeset_init(struct drm_device *dev)
|
||||
*/
|
||||
intel_fbdev_initial_config(dev);
|
||||
|
||||
/* Only enable hotplug handling once the fbdev is fully set up. */
|
||||
dev_priv->enable_hotplug_processing = true;
|
||||
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
return 0;
|
||||
@ -1730,6 +1727,13 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
||||
goto out_mtrrfree;
|
||||
}
|
||||
|
||||
dev_priv->dp_wq = alloc_ordered_workqueue("i915-dp", 0);
|
||||
if (dev_priv->dp_wq == NULL) {
|
||||
DRM_ERROR("Failed to create our dp workqueue.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_freewq;
|
||||
}
|
||||
|
||||
intel_irq_init(dev);
|
||||
intel_uncore_sanitize(dev);
|
||||
|
||||
@ -1805,6 +1809,8 @@ out_gem_unload:
|
||||
intel_teardown_gmbus(dev);
|
||||
intel_teardown_mchbar(dev);
|
||||
pm_qos_remove_request(&dev_priv->pm_qos);
|
||||
destroy_workqueue(dev_priv->dp_wq);
|
||||
out_freewq:
|
||||
destroy_workqueue(dev_priv->wq);
|
||||
out_mtrrfree:
|
||||
arch_phys_wc_del(dev_priv->gtt.mtrr);
|
||||
@ -1905,6 +1911,7 @@ int i915_driver_unload(struct drm_device *dev)
|
||||
intel_teardown_gmbus(dev);
|
||||
intel_teardown_mchbar(dev);
|
||||
|
||||
destroy_workqueue(dev_priv->dp_wq);
|
||||
destroy_workqueue(dev_priv->wq);
|
||||
pm_qos_remove_request(&dev_priv->pm_qos);
|
||||
|
||||
|
@ -520,13 +520,6 @@ static int i915_drm_freeze(struct drm_device *dev)
|
||||
return error;
|
||||
}
|
||||
|
||||
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
|
||||
|
||||
intel_runtime_pm_disable_interrupts(dev);
|
||||
dev_priv->enable_hotplug_processing = false;
|
||||
|
||||
intel_suspend_gt_powersave(dev);
|
||||
|
||||
/*
|
||||
* Disable CRTCs directly since we want to preserve sw state
|
||||
* for _thaw. Also, power gate the CRTC power wells.
|
||||
@ -536,6 +529,14 @@ static int i915_drm_freeze(struct drm_device *dev)
|
||||
intel_crtc_control(crtc, false);
|
||||
drm_modeset_unlock_all(dev);
|
||||
|
||||
intel_dp_mst_suspend(dev);
|
||||
|
||||
flush_delayed_work(&dev_priv->rps.delayed_resume_work);
|
||||
|
||||
intel_runtime_pm_disable_interrupts(dev);
|
||||
|
||||
intel_suspend_gt_powersave(dev);
|
||||
|
||||
intel_modeset_suspend_hw(dev);
|
||||
}
|
||||
|
||||
@ -650,6 +651,15 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
|
||||
|
||||
intel_modeset_init_hw(dev);
|
||||
|
||||
{
|
||||
unsigned long irqflags;
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
|
||||
if (dev_priv->display.hpd_irq_setup)
|
||||
dev_priv->display.hpd_irq_setup(dev);
|
||||
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
|
||||
}
|
||||
|
||||
intel_dp_mst_resume(dev);
|
||||
drm_modeset_lock_all(dev);
|
||||
intel_modeset_setup_hw_state(dev, true);
|
||||
drm_modeset_unlock_all(dev);
|
||||
@ -661,7 +671,6 @@ static int __i915_drm_thaw(struct drm_device *dev, bool restore_gtt_mappings)
|
||||
* notifications.
|
||||
* */
|
||||
intel_hpd_init(dev);
|
||||
dev_priv->enable_hotplug_processing = true;
|
||||
/* Config may have changed between suspend and resume */
|
||||
drm_helper_hpd_irq_event(dev);
|
||||
}
|
||||
|
@ -1446,7 +1446,6 @@ struct drm_i915_private {
|
||||
u32 pipestat_irq_mask[I915_MAX_PIPES];
|
||||
|
||||
struct work_struct hotplug_work;
|
||||
bool enable_hotplug_processing;
|
||||
struct {
|
||||
unsigned long hpd_last_jiffies;
|
||||
int hpd_cnt;
|
||||
@ -1604,6 +1603,15 @@ struct drm_i915_private {
|
||||
u32 short_hpd_port_mask;
|
||||
struct work_struct dig_port_work;
|
||||
|
||||
/*
|
||||
* if we get a HPD irq from DP and a HPD irq from non-DP
|
||||
* the non-DP HPD could block the workqueue on a mode config
|
||||
* mutex getting, that userspace may have taken. However
|
||||
* userspace is waiting on the DP workqueue to run which is
|
||||
* blocked behind the non-DP one.
|
||||
*/
|
||||
struct workqueue_struct *dp_wq;
|
||||
|
||||
/* Old dri1 support infrastructure, beware the dragons ya fools entering
|
||||
* here! */
|
||||
struct i915_dri1_state dri1;
|
||||
|
@ -1156,10 +1156,6 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
||||
bool changed = false;
|
||||
u32 hpd_event_bits;
|
||||
|
||||
/* HPD irq before everything is fully set up. */
|
||||
if (!dev_priv->enable_hotplug_processing)
|
||||
return;
|
||||
|
||||
mutex_lock(&mode_config->mutex);
|
||||
DRM_DEBUG_KMS("running encoder hotplug functions\n");
|
||||
|
||||
@ -1169,6 +1165,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
||||
dev_priv->hpd_event_bits = 0;
|
||||
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
||||
intel_connector = to_intel_connector(connector);
|
||||
if (!intel_connector->encoder)
|
||||
continue;
|
||||
intel_encoder = intel_connector->encoder;
|
||||
if (intel_encoder->hpd_pin > HPD_NONE &&
|
||||
dev_priv->hpd_stats[intel_encoder->hpd_pin].hpd_mark == HPD_MARK_DISABLED &&
|
||||
@ -1199,6 +1197,8 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
||||
|
||||
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
||||
intel_connector = to_intel_connector(connector);
|
||||
if (!intel_connector->encoder)
|
||||
continue;
|
||||
intel_encoder = intel_connector->encoder;
|
||||
if (hpd_event_bits & (1 << intel_encoder->hpd_pin)) {
|
||||
if (intel_encoder->hot_plug)
|
||||
@ -1846,7 +1846,7 @@ static inline void intel_hpd_irq_handler(struct drm_device *dev,
|
||||
* deadlock.
|
||||
*/
|
||||
if (queue_dig)
|
||||
schedule_work(&dev_priv->dig_port_work);
|
||||
queue_work(dev_priv->dp_wq, &dev_priv->dig_port_work);
|
||||
if (queue_hp)
|
||||
schedule_work(&dev_priv->hotplug_work);
|
||||
}
|
||||
@ -4759,7 +4759,9 @@ void intel_hpd_init(struct drm_device *dev)
|
||||
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
connector->polled = intel_connector->polled;
|
||||
if (!connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
|
||||
if (connector->encoder && !connector->polled && I915_HAS_HOTPLUG(dev) && intel_connector->encoder->hpd_pin > HPD_NONE)
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
if (intel_connector->mst_port)
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
}
|
||||
|
||||
|
@ -5821,6 +5821,7 @@ enum punit_power_well {
|
||||
#define TRANS_DDI_EDP_INPUT_A_ONOFF (4<<12)
|
||||
#define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12)
|
||||
#define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12)
|
||||
#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1<<8)
|
||||
#define TRANS_DDI_BFI_ENABLE (1<<4)
|
||||
|
||||
/* DisplayPort Transport Control */
|
||||
@ -5830,6 +5831,7 @@ enum punit_power_well {
|
||||
#define DP_TP_CTL_ENABLE (1<<31)
|
||||
#define DP_TP_CTL_MODE_SST (0<<27)
|
||||
#define DP_TP_CTL_MODE_MST (1<<27)
|
||||
#define DP_TP_CTL_FORCE_ACT (1<<25)
|
||||
#define DP_TP_CTL_ENHANCED_FRAME_ENABLE (1<<18)
|
||||
#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15)
|
||||
#define DP_TP_CTL_LINK_TRAIN_MASK (7<<8)
|
||||
@ -5844,8 +5846,13 @@ enum punit_power_well {
|
||||
#define DP_TP_STATUS_A 0x64044
|
||||
#define DP_TP_STATUS_B 0x64144
|
||||
#define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
|
||||
#define DP_TP_STATUS_IDLE_DONE (1<<25)
|
||||
#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12)
|
||||
#define DP_TP_STATUS_IDLE_DONE (1<<25)
|
||||
#define DP_TP_STATUS_ACT_SENT (1<<24)
|
||||
#define DP_TP_STATUS_MODE_STATUS_MST (1<<23)
|
||||
#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12)
|
||||
#define DP_TP_STATUS_PAYLOAD_MAPPING_VC2 (3 << 8)
|
||||
#define DP_TP_STATUS_PAYLOAD_MAPPING_VC1 (3 << 4)
|
||||
#define DP_TP_STATUS_PAYLOAD_MAPPING_VC0 (3 << 0)
|
||||
|
||||
/* DDI Buffer Control */
|
||||
#define DDI_BUF_CTL_A 0x64000
|
||||
|
@ -895,7 +895,7 @@ void intel_crt_init(struct drm_device *dev)
|
||||
|
||||
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
if (!I915_HAS_HOTPLUG(dev))
|
||||
intel_connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
|
@ -116,7 +116,10 @@ enum port intel_ddi_get_encoder_port(struct intel_encoder *intel_encoder)
|
||||
struct drm_encoder *encoder = &intel_encoder->base;
|
||||
int type = intel_encoder->type;
|
||||
|
||||
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
|
||||
if (type == INTEL_OUTPUT_DP_MST) {
|
||||
struct intel_digital_port *intel_dig_port = enc_to_mst(encoder)->primary;
|
||||
return intel_dig_port->port;
|
||||
} else if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP ||
|
||||
type == INTEL_OUTPUT_HDMI || type == INTEL_OUTPUT_UNKNOWN) {
|
||||
struct intel_digital_port *intel_dig_port =
|
||||
enc_to_dig_port(encoder);
|
||||
@ -365,6 +368,18 @@ void hsw_fdi_link_train(struct drm_crtc *crtc)
|
||||
DRM_ERROR("FDI link training failed!\n");
|
||||
}
|
||||
|
||||
void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port =
|
||||
enc_to_dig_port(&encoder->base);
|
||||
|
||||
intel_dp->DP = intel_dig_port->saved_port_bits |
|
||||
DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
|
||||
intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
|
||||
|
||||
}
|
||||
|
||||
static struct intel_encoder *
|
||||
intel_ddi_get_crtc_encoder(struct drm_crtc *crtc)
|
||||
{
|
||||
@ -572,8 +587,8 @@ static int intel_ddi_calc_wrpll_link(struct drm_i915_private *dev_priv,
|
||||
return (refclk * n * 100) / (p * r);
|
||||
}
|
||||
|
||||
static void intel_ddi_clock_get(struct intel_encoder *encoder,
|
||||
struct intel_crtc_config *pipe_config)
|
||||
void intel_ddi_clock_get(struct intel_encoder *encoder,
|
||||
struct intel_crtc_config *pipe_config)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
|
||||
int link_clock = 0;
|
||||
@ -743,8 +758,7 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
|
||||
int type = intel_encoder->type;
|
||||
uint32_t temp;
|
||||
|
||||
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
|
||||
|
||||
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP || type == INTEL_OUTPUT_DP_MST) {
|
||||
temp = TRANS_MSA_SYNC_CLK;
|
||||
switch (intel_crtc->config.pipe_bpp) {
|
||||
case 18:
|
||||
@ -766,6 +780,21 @@ void intel_ddi_set_pipe_settings(struct drm_crtc *crtc)
|
||||
}
|
||||
}
|
||||
|
||||
void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state)
|
||||
{
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
enum transcoder cpu_transcoder = intel_crtc->config.cpu_transcoder;
|
||||
uint32_t temp;
|
||||
temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
|
||||
if (state == true)
|
||||
temp |= TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
|
||||
else
|
||||
temp &= ~TRANS_DDI_DP_VC_PAYLOAD_ALLOC;
|
||||
I915_WRITE(TRANS_DDI_FUNC_CTL(cpu_transcoder), temp);
|
||||
}
|
||||
|
||||
void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
|
||||
{
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
@ -845,7 +874,19 @@ void intel_ddi_enable_transcoder_func(struct drm_crtc *crtc)
|
||||
type == INTEL_OUTPUT_EDP) {
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
|
||||
temp |= TRANS_DDI_MODE_SELECT_DP_SST;
|
||||
if (intel_dp->is_mst) {
|
||||
temp |= TRANS_DDI_MODE_SELECT_DP_MST;
|
||||
} else
|
||||
temp |= TRANS_DDI_MODE_SELECT_DP_SST;
|
||||
|
||||
temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
|
||||
} else if (type == INTEL_OUTPUT_DP_MST) {
|
||||
struct intel_dp *intel_dp = &enc_to_mst(encoder)->primary->dp;
|
||||
|
||||
if (intel_dp->is_mst) {
|
||||
temp |= TRANS_DDI_MODE_SELECT_DP_MST;
|
||||
} else
|
||||
temp |= TRANS_DDI_MODE_SELECT_DP_SST;
|
||||
|
||||
temp |= DDI_PORT_WIDTH(intel_dp->lane_count);
|
||||
} else {
|
||||
@ -862,7 +903,7 @@ void intel_ddi_disable_transcoder_func(struct drm_i915_private *dev_priv,
|
||||
uint32_t reg = TRANS_DDI_FUNC_CTL(cpu_transcoder);
|
||||
uint32_t val = I915_READ(reg);
|
||||
|
||||
val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK);
|
||||
val &= ~(TRANS_DDI_FUNC_ENABLE | TRANS_DDI_PORT_MASK | TRANS_DDI_DP_VC_PAYLOAD_ALLOC);
|
||||
val |= TRANS_DDI_PORT_NONE;
|
||||
I915_WRITE(reg, val);
|
||||
}
|
||||
@ -901,8 +942,11 @@ bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
|
||||
case TRANS_DDI_MODE_SELECT_DP_SST:
|
||||
if (type == DRM_MODE_CONNECTOR_eDP)
|
||||
return true;
|
||||
case TRANS_DDI_MODE_SELECT_DP_MST:
|
||||
return (type == DRM_MODE_CONNECTOR_DisplayPort);
|
||||
case TRANS_DDI_MODE_SELECT_DP_MST:
|
||||
/* if the transcoder is in MST state then
|
||||
* connector isn't connected */
|
||||
return false;
|
||||
|
||||
case TRANS_DDI_MODE_SELECT_FDI:
|
||||
return (type == DRM_MODE_CONNECTOR_VGA);
|
||||
@ -954,6 +998,9 @@ bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
|
||||
|
||||
if ((tmp & TRANS_DDI_PORT_MASK)
|
||||
== TRANS_DDI_SELECT_PORT(port)) {
|
||||
if ((tmp & TRANS_DDI_MODE_SELECT_MASK) == TRANS_DDI_MODE_SELECT_DP_MST)
|
||||
return false;
|
||||
|
||||
*pipe = i;
|
||||
return true;
|
||||
}
|
||||
@ -1015,12 +1062,8 @@ static void intel_ddi_pre_enable(struct intel_encoder *intel_encoder)
|
||||
|
||||
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP) {
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
struct intel_digital_port *intel_dig_port =
|
||||
enc_to_dig_port(encoder);
|
||||
|
||||
intel_dp->DP = intel_dig_port->saved_port_bits |
|
||||
DDI_BUF_CTL_ENABLE | DDI_BUF_EMP_400MV_0DB_HSW;
|
||||
intel_dp->DP |= DDI_PORT_WIDTH(intel_dp->lane_count);
|
||||
intel_ddi_init_dp_buf_reg(intel_encoder);
|
||||
|
||||
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
|
||||
intel_dp_start_link_train(intel_dp);
|
||||
@ -1264,10 +1307,15 @@ void intel_ddi_prepare_link_retrain(struct drm_encoder *encoder)
|
||||
intel_wait_ddi_buf_idle(dev_priv, port);
|
||||
}
|
||||
|
||||
val = DP_TP_CTL_ENABLE | DP_TP_CTL_MODE_SST |
|
||||
val = DP_TP_CTL_ENABLE |
|
||||
DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_SCRAMBLE_DISABLE;
|
||||
if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
|
||||
val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
|
||||
if (intel_dp->is_mst)
|
||||
val |= DP_TP_CTL_MODE_MST;
|
||||
else {
|
||||
val |= DP_TP_CTL_MODE_SST;
|
||||
if (drm_dp_enhanced_frame_cap(intel_dp->dpcd))
|
||||
val |= DP_TP_CTL_ENHANCED_FRAME_ENABLE;
|
||||
}
|
||||
I915_WRITE(DP_TP_CTL(port), val);
|
||||
POSTING_READ(DP_TP_CTL(port));
|
||||
|
||||
@ -1306,11 +1354,16 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc)
|
||||
|
||||
static void intel_ddi_hot_plug(struct intel_encoder *intel_encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
|
||||
int type = intel_encoder->type;
|
||||
struct intel_digital_port *intel_dig_port = enc_to_dig_port(&intel_encoder->base);
|
||||
int type = intel_dig_port->base.type;
|
||||
|
||||
if (type == INTEL_OUTPUT_DISPLAYPORT || type == INTEL_OUTPUT_EDP)
|
||||
intel_dp_check_link_status(intel_dp);
|
||||
if (type != INTEL_OUTPUT_DISPLAYPORT &&
|
||||
type != INTEL_OUTPUT_EDP &&
|
||||
type != INTEL_OUTPUT_UNKNOWN) {
|
||||
return;
|
||||
}
|
||||
|
||||
intel_dp_hot_plug(intel_encoder);
|
||||
}
|
||||
|
||||
void intel_ddi_get_config(struct intel_encoder *encoder,
|
||||
|
@ -101,6 +101,14 @@ static void haswell_set_pipeconf(struct drm_crtc *crtc);
|
||||
static void intel_set_pipe_csc(struct drm_crtc *crtc);
|
||||
static void vlv_prepare_pll(struct intel_crtc *crtc);
|
||||
|
||||
static struct intel_encoder *intel_find_encoder(struct intel_connector *connector, int pipe)
|
||||
{
|
||||
if (!connector->mst_port)
|
||||
return connector->encoder;
|
||||
else
|
||||
return &connector->mst_port->mst_encoders[pipe]->base;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int min, max;
|
||||
} intel_range_t;
|
||||
@ -4130,6 +4138,9 @@ static void haswell_crtc_enable(struct drm_crtc *crtc)
|
||||
if (intel_crtc->config.has_pch_encoder)
|
||||
lpt_pch_enable(crtc);
|
||||
|
||||
if (intel_crtc->config.dp_encoder_is_mst)
|
||||
intel_ddi_set_vc_payload_alloc(crtc, true);
|
||||
|
||||
for_each_encoder_on_crtc(dev, crtc, encoder) {
|
||||
encoder->enable(encoder);
|
||||
intel_opregion_notify_encoder(encoder, true);
|
||||
@ -4178,6 +4189,9 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
|
||||
|
||||
intel_disable_pipe(dev_priv, pipe);
|
||||
|
||||
if (intel_crtc->config.dp_encoder_is_mst)
|
||||
intel_ddi_set_vc_payload_alloc(crtc, false);
|
||||
|
||||
ironlake_pfit_disable(intel_crtc);
|
||||
|
||||
for_each_encoder_on_crtc(dev, crtc, encoder)
|
||||
@ -4300,6 +4314,27 @@ static void i9xx_pfit_enable(struct intel_crtc *crtc)
|
||||
I915_WRITE(BCLRPAT(crtc->pipe), 0);
|
||||
}
|
||||
|
||||
static enum intel_display_power_domain port_to_power_domain(enum port port)
|
||||
{
|
||||
switch (port) {
|
||||
case PORT_A:
|
||||
return POWER_DOMAIN_PORT_DDI_A_4_LANES;
|
||||
case PORT_B:
|
||||
return POWER_DOMAIN_PORT_DDI_B_4_LANES;
|
||||
case PORT_C:
|
||||
return POWER_DOMAIN_PORT_DDI_C_4_LANES;
|
||||
case PORT_D:
|
||||
return POWER_DOMAIN_PORT_DDI_D_4_LANES;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return POWER_DOMAIN_PORT_OTHER;
|
||||
}
|
||||
}
|
||||
|
||||
#define for_each_power_domain(domain, mask) \
|
||||
for ((domain) = 0; (domain) < POWER_DOMAIN_NUM; (domain)++) \
|
||||
if ((1 << (domain)) & (mask))
|
||||
|
||||
enum intel_display_power_domain
|
||||
intel_display_port_power_domain(struct intel_encoder *intel_encoder)
|
||||
{
|
||||
@ -4314,19 +4349,10 @@ intel_display_port_power_domain(struct intel_encoder *intel_encoder)
|
||||
case INTEL_OUTPUT_HDMI:
|
||||
case INTEL_OUTPUT_EDP:
|
||||
intel_dig_port = enc_to_dig_port(&intel_encoder->base);
|
||||
switch (intel_dig_port->port) {
|
||||
case PORT_A:
|
||||
return POWER_DOMAIN_PORT_DDI_A_4_LANES;
|
||||
case PORT_B:
|
||||
return POWER_DOMAIN_PORT_DDI_B_4_LANES;
|
||||
case PORT_C:
|
||||
return POWER_DOMAIN_PORT_DDI_C_4_LANES;
|
||||
case PORT_D:
|
||||
return POWER_DOMAIN_PORT_DDI_D_4_LANES;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
return POWER_DOMAIN_PORT_OTHER;
|
||||
}
|
||||
return port_to_power_domain(intel_dig_port->port);
|
||||
case INTEL_OUTPUT_DP_MST:
|
||||
intel_dig_port = enc_to_mst(&intel_encoder->base)->primary;
|
||||
return port_to_power_domain(intel_dig_port->port);
|
||||
case INTEL_OUTPUT_ANALOG:
|
||||
return POWER_DOMAIN_PORT_CRT;
|
||||
case INTEL_OUTPUT_DSI:
|
||||
@ -4990,24 +5016,31 @@ static void intel_connector_check_state(struct intel_connector *connector)
|
||||
connector->base.base.id,
|
||||
connector->base.name);
|
||||
|
||||
/* there is no real hw state for MST connectors */
|
||||
if (connector->mst_port)
|
||||
return;
|
||||
|
||||
WARN(connector->base.dpms == DRM_MODE_DPMS_OFF,
|
||||
"wrong connector dpms state\n");
|
||||
WARN(connector->base.encoder != &encoder->base,
|
||||
"active connector not linked to encoder\n");
|
||||
WARN(!encoder->connectors_active,
|
||||
"encoder->connectors_active not set\n");
|
||||
|
||||
encoder_enabled = encoder->get_hw_state(encoder, &pipe);
|
||||
WARN(!encoder_enabled, "encoder not enabled\n");
|
||||
if (WARN_ON(!encoder->base.crtc))
|
||||
return;
|
||||
if (encoder) {
|
||||
WARN(!encoder->connectors_active,
|
||||
"encoder->connectors_active not set\n");
|
||||
|
||||
crtc = encoder->base.crtc;
|
||||
encoder_enabled = encoder->get_hw_state(encoder, &pipe);
|
||||
WARN(!encoder_enabled, "encoder not enabled\n");
|
||||
if (WARN_ON(!encoder->base.crtc))
|
||||
return;
|
||||
|
||||
WARN(!crtc->enabled, "crtc not enabled\n");
|
||||
WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
|
||||
WARN(pipe != to_intel_crtc(crtc)->pipe,
|
||||
"encoder active on the wrong pipe\n");
|
||||
crtc = encoder->base.crtc;
|
||||
|
||||
WARN(!crtc->enabled, "crtc not enabled\n");
|
||||
WARN(!to_intel_crtc(crtc)->active, "crtc not active\n");
|
||||
WARN(pipe != to_intel_crtc(crtc)->pipe,
|
||||
"encoder active on the wrong pipe\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -10508,6 +10541,14 @@ check_encoder_state(struct drm_device *dev)
|
||||
if (connector->base.dpms != DRM_MODE_DPMS_OFF)
|
||||
active = true;
|
||||
}
|
||||
/*
|
||||
* for MST connectors if we unplug the connector is gone
|
||||
* away but the encoder is still connected to a crtc
|
||||
* until a modeset happens in response to the hotplug.
|
||||
*/
|
||||
if (!enabled && encoder->base.encoder_type == DRM_MODE_ENCODER_DPMST)
|
||||
continue;
|
||||
|
||||
WARN(!!encoder->base.crtc != enabled,
|
||||
"encoder's enabled state mismatch "
|
||||
"(expected %i, found %i)\n",
|
||||
@ -11053,7 +11094,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
|
||||
* for them. */
|
||||
for (ro = 0; ro < set->num_connectors; ro++) {
|
||||
if (set->connectors[ro] == &connector->base) {
|
||||
connector->new_encoder = connector->encoder;
|
||||
connector->new_encoder = intel_find_encoder(connector, to_intel_crtc(set->crtc)->pipe);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -11099,7 +11140,7 @@ intel_modeset_stage_output_state(struct drm_device *dev,
|
||||
new_crtc)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
connector->encoder->new_crtc = to_intel_crtc(new_crtc);
|
||||
connector->new_encoder->new_crtc = to_intel_crtc(new_crtc);
|
||||
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s] to [CRTC:%d]\n",
|
||||
connector->base.base.id,
|
||||
@ -11133,7 +11174,12 @@ intel_modeset_stage_output_state(struct drm_device *dev,
|
||||
}
|
||||
}
|
||||
/* Now we've also updated encoder->new_crtc for all encoders. */
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list,
|
||||
base.head) {
|
||||
if (connector->new_encoder)
|
||||
if (connector->new_encoder != connector->encoder)
|
||||
connector->encoder = connector->new_encoder;
|
||||
}
|
||||
for_each_intel_crtc(dev, crtc) {
|
||||
crtc->new_enabled = false;
|
||||
|
||||
@ -11784,21 +11830,20 @@ int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
|
||||
struct drm_mode_object *drmmode_obj;
|
||||
struct drm_crtc *drmmode_crtc;
|
||||
struct intel_crtc *crtc;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -ENODEV;
|
||||
|
||||
drmmode_obj = drm_mode_object_find(dev, pipe_from_crtc_id->crtc_id,
|
||||
DRM_MODE_OBJECT_CRTC);
|
||||
drmmode_crtc = drm_crtc_find(dev, pipe_from_crtc_id->crtc_id);
|
||||
|
||||
if (!drmmode_obj) {
|
||||
if (!drmmode_crtc) {
|
||||
DRM_ERROR("no such CRTC id\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
|
||||
crtc = to_intel_crtc(drmmode_crtc);
|
||||
pipe_from_crtc_id->pipe = crtc->pipe;
|
||||
|
||||
return 0;
|
||||
@ -13031,7 +13076,7 @@ void intel_connector_unregister(struct intel_connector *intel_connector)
|
||||
struct drm_connector *connector = &intel_connector->base;
|
||||
|
||||
intel_panel_destroy_backlight(connector);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
}
|
||||
|
||||
void intel_modeset_cleanup(struct drm_device *dev)
|
||||
|
@ -112,7 +112,7 @@ static void intel_dp_link_down(struct intel_dp *intel_dp);
|
||||
static bool _edp_panel_vdd_on(struct intel_dp *intel_dp);
|
||||
static void edp_panel_vdd_off(struct intel_dp *intel_dp, bool sync);
|
||||
|
||||
static int
|
||||
int
|
||||
intel_dp_max_link_bw(struct intel_dp *intel_dp)
|
||||
{
|
||||
int max_link_bw = intel_dp->dpcd[DP_MAX_LINK_RATE];
|
||||
@ -740,8 +740,9 @@ intel_dp_connector_unregister(struct intel_connector *intel_connector)
|
||||
{
|
||||
struct intel_dp *intel_dp = intel_attached_dp(&intel_connector->base);
|
||||
|
||||
sysfs_remove_link(&intel_connector->base.kdev->kobj,
|
||||
intel_dp->aux.ddc.dev.kobj.name);
|
||||
if (!intel_connector->mst_port)
|
||||
sysfs_remove_link(&intel_connector->base.kdev->kobj,
|
||||
intel_dp->aux.ddc.dev.kobj.name);
|
||||
intel_connector_unregister(intel_connector);
|
||||
}
|
||||
|
||||
@ -3348,6 +3349,33 @@ intel_dp_probe_oui(struct intel_dp *intel_dp)
|
||||
edp_panel_vdd_off(intel_dp, false);
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_probe_mst(struct intel_dp *intel_dp)
|
||||
{
|
||||
u8 buf[1];
|
||||
|
||||
if (!intel_dp->can_mst)
|
||||
return false;
|
||||
|
||||
if (intel_dp->dpcd[DP_DPCD_REV] < 0x12)
|
||||
return false;
|
||||
|
||||
_edp_panel_vdd_on(intel_dp);
|
||||
if (intel_dp_dpcd_read_wake(&intel_dp->aux, DP_MSTM_CAP, buf, 1)) {
|
||||
if (buf[0] & DP_MST_CAP) {
|
||||
DRM_DEBUG_KMS("Sink is MST capable\n");
|
||||
intel_dp->is_mst = true;
|
||||
} else {
|
||||
DRM_DEBUG_KMS("Sink is not MST capable\n");
|
||||
intel_dp->is_mst = false;
|
||||
}
|
||||
}
|
||||
edp_panel_vdd_off(intel_dp, false);
|
||||
|
||||
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
|
||||
return intel_dp->is_mst;
|
||||
}
|
||||
|
||||
int intel_dp_sink_crc(struct intel_dp *intel_dp, u8 *crc)
|
||||
{
|
||||
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
||||
@ -3385,6 +3413,20 @@ intel_dp_get_sink_irq(struct intel_dp *intel_dp, u8 *sink_irq_vector)
|
||||
sink_irq_vector, 1) == 1;
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_get_sink_irq_esi(struct intel_dp *intel_dp, u8 *sink_irq_vector)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = intel_dp_dpcd_read_wake(&intel_dp->aux,
|
||||
DP_SINK_COUNT_ESI,
|
||||
sink_irq_vector, 14);
|
||||
if (ret != 14)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_handle_test_request(struct intel_dp *intel_dp)
|
||||
{
|
||||
@ -3392,6 +3434,63 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
|
||||
drm_dp_dpcd_writeb(&intel_dp->aux, DP_TEST_RESPONSE, DP_TEST_NAK);
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_check_mst_status(struct intel_dp *intel_dp)
|
||||
{
|
||||
bool bret;
|
||||
|
||||
if (intel_dp->is_mst) {
|
||||
u8 esi[16] = { 0 };
|
||||
int ret = 0;
|
||||
int retry;
|
||||
bool handled;
|
||||
bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
|
||||
go_again:
|
||||
if (bret == true) {
|
||||
|
||||
/* check link status - esi[10] = 0x200c */
|
||||
if (intel_dp->active_mst_links && !drm_dp_channel_eq_ok(&esi[10], intel_dp->lane_count)) {
|
||||
DRM_DEBUG_KMS("channel EQ not ok, retraining\n");
|
||||
intel_dp_start_link_train(intel_dp);
|
||||
intel_dp_complete_link_train(intel_dp);
|
||||
intel_dp_stop_link_train(intel_dp);
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("got esi %02x %02x %02x\n", esi[0], esi[1], esi[2]);
|
||||
ret = drm_dp_mst_hpd_irq(&intel_dp->mst_mgr, esi, &handled);
|
||||
|
||||
if (handled) {
|
||||
for (retry = 0; retry < 3; retry++) {
|
||||
int wret;
|
||||
wret = drm_dp_dpcd_write(&intel_dp->aux,
|
||||
DP_SINK_COUNT_ESI+1,
|
||||
&esi[1], 3);
|
||||
if (wret == 3) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bret = intel_dp_get_sink_irq_esi(intel_dp, esi);
|
||||
if (bret == true) {
|
||||
DRM_DEBUG_KMS("got esi2 %02x %02x %02x\n", esi[0], esi[1], esi[2]);
|
||||
goto go_again;
|
||||
}
|
||||
} else
|
||||
ret = 0;
|
||||
|
||||
return ret;
|
||||
} else {
|
||||
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
||||
DRM_DEBUG_KMS("failed to get ESI - device may have failed\n");
|
||||
intel_dp->is_mst = false;
|
||||
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
|
||||
/* send a hotplug event */
|
||||
drm_kms_helper_hotplug_event(intel_dig_port->base.base.dev);
|
||||
}
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* According to DP spec
|
||||
* 5.1.2:
|
||||
@ -3400,7 +3499,6 @@ intel_dp_handle_test_request(struct intel_dp *intel_dp)
|
||||
* 3. Use Link Training from 2.5.3.3 and 3.5.1.3
|
||||
* 4. Check link status on receipt of hot-plug interrupt
|
||||
*/
|
||||
|
||||
void
|
||||
intel_dp_check_link_status(struct intel_dp *intel_dp)
|
||||
{
|
||||
@ -3620,6 +3718,7 @@ intel_dp_detect(struct drm_connector *connector, bool force)
|
||||
enum drm_connector_status status;
|
||||
enum intel_display_power_domain power_domain;
|
||||
struct edid *edid = NULL;
|
||||
bool ret;
|
||||
|
||||
power_domain = intel_display_port_power_domain(intel_encoder);
|
||||
intel_display_power_get(dev_priv, power_domain);
|
||||
@ -3627,6 +3726,14 @@ intel_dp_detect(struct drm_connector *connector, bool force)
|
||||
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
|
||||
connector->base.id, connector->name);
|
||||
|
||||
if (intel_dp->is_mst) {
|
||||
/* MST devices are disconnected from a monitor POV */
|
||||
if (intel_encoder->type != INTEL_OUTPUT_EDP)
|
||||
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
|
||||
status = connector_status_disconnected;
|
||||
goto out;
|
||||
}
|
||||
|
||||
intel_dp->has_audio = false;
|
||||
|
||||
if (HAS_PCH_SPLIT(dev))
|
||||
@ -3639,6 +3746,16 @@ intel_dp_detect(struct drm_connector *connector, bool force)
|
||||
|
||||
intel_dp_probe_oui(intel_dp);
|
||||
|
||||
ret = intel_dp_probe_mst(intel_dp);
|
||||
if (ret) {
|
||||
/* if we are in MST mode then this connector
|
||||
won't appear connected or have anything with EDID on it */
|
||||
if (intel_encoder->type != INTEL_OUTPUT_EDP)
|
||||
intel_encoder->type = INTEL_OUTPUT_DISPLAYPORT;
|
||||
status = connector_status_disconnected;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
|
||||
intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
|
||||
} else {
|
||||
@ -3831,6 +3948,7 @@ void intel_dp_encoder_destroy(struct drm_encoder *encoder)
|
||||
struct drm_device *dev = intel_dp_to_dev(intel_dp);
|
||||
|
||||
drm_dp_aux_unregister(&intel_dp->aux);
|
||||
intel_dp_mst_encoder_cleanup(intel_dig_port);
|
||||
drm_encoder_cleanup(encoder);
|
||||
if (is_edp(intel_dp)) {
|
||||
cancel_delayed_work_sync(&intel_dp->panel_vdd_work);
|
||||
@ -3859,28 +3977,62 @@ static const struct drm_encoder_funcs intel_dp_enc_funcs = {
|
||||
.destroy = intel_dp_encoder_destroy,
|
||||
};
|
||||
|
||||
static void
|
||||
void
|
||||
intel_dp_hot_plug(struct intel_encoder *intel_encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&intel_encoder->base);
|
||||
|
||||
intel_dp_check_link_status(intel_dp);
|
||||
return;
|
||||
}
|
||||
|
||||
bool
|
||||
intel_dp_hpd_pulse(struct intel_digital_port *intel_dig_port, bool long_hpd)
|
||||
{
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
if (intel_dig_port->base.type != INTEL_OUTPUT_EDP)
|
||||
intel_dig_port->base.type = INTEL_OUTPUT_DISPLAYPORT;
|
||||
|
||||
if (long_hpd)
|
||||
return true;
|
||||
DRM_DEBUG_KMS("got hpd irq on port %d - %s\n", intel_dig_port->port,
|
||||
long_hpd ? "long" : "short");
|
||||
|
||||
/*
|
||||
* we'll check the link status via the normal hot plug path later -
|
||||
* but for short hpds we should check it now
|
||||
*/
|
||||
intel_dp_check_link_status(intel_dp);
|
||||
if (long_hpd) {
|
||||
if (!ibx_digital_port_connected(dev_priv, intel_dig_port))
|
||||
goto mst_fail;
|
||||
|
||||
if (!intel_dp_get_dpcd(intel_dp)) {
|
||||
goto mst_fail;
|
||||
}
|
||||
|
||||
intel_dp_probe_oui(intel_dp);
|
||||
|
||||
if (!intel_dp_probe_mst(intel_dp))
|
||||
goto mst_fail;
|
||||
|
||||
} else {
|
||||
if (intel_dp->is_mst) {
|
||||
ret = intel_dp_check_mst_status(intel_dp);
|
||||
if (ret == -EINVAL)
|
||||
goto mst_fail;
|
||||
}
|
||||
|
||||
if (!intel_dp->is_mst) {
|
||||
/*
|
||||
* we'll check the link status via the normal hot plug path later -
|
||||
* but for short hpds we should check it now
|
||||
*/
|
||||
intel_dp_check_link_status(intel_dp);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
mst_fail:
|
||||
/* if we were in MST mode, and device is not there get out of MST mode */
|
||||
if (intel_dp->is_mst) {
|
||||
DRM_DEBUG_KMS("MST device may have disappeared %d vs %d\n", intel_dp->is_mst, intel_dp->mst_mgr.mst_state);
|
||||
intel_dp->is_mst = false;
|
||||
drm_dp_mst_topology_mgr_set_mst(&intel_dp->mst_mgr, intel_dp->is_mst);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return which DP Port should be selected for Transcoder DP control */
|
||||
@ -3931,7 +4083,7 @@ bool intel_dp_is_edp(struct drm_device *dev, enum port port)
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
@ -4397,7 +4549,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
||||
edp_panel_vdd_work);
|
||||
|
||||
intel_connector_attach_encoder(intel_connector, intel_encoder);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
if (HAS_DDI(dev))
|
||||
intel_connector->get_hw_state = intel_ddi_connector_get_hw_state;
|
||||
@ -4430,6 +4582,13 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
||||
|
||||
intel_dp_aux_init(intel_dp, intel_connector);
|
||||
|
||||
/* init MST on ports that can support it */
|
||||
if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
|
||||
if (port == PORT_B || port == PORT_C || port == PORT_D) {
|
||||
intel_dp_mst_encoder_init(intel_dig_port, intel_connector->base.base.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!intel_edp_init_connector(intel_dp, intel_connector, &power_seq)) {
|
||||
drm_dp_aux_unregister(&intel_dp->aux);
|
||||
if (is_edp(intel_dp)) {
|
||||
@ -4438,7 +4597,7 @@ intel_dp_init_connector(struct intel_digital_port *intel_dig_port,
|
||||
edp_panel_vdd_off_sync(intel_dp);
|
||||
drm_modeset_unlock(&dev->mode_config.connection_mutex);
|
||||
}
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
return false;
|
||||
}
|
||||
@ -4526,3 +4685,46 @@ intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
|
||||
kfree(intel_connector);
|
||||
}
|
||||
}
|
||||
|
||||
void intel_dp_mst_suspend(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
/* disable MST */
|
||||
for (i = 0; i < I915_MAX_PORTS; i++) {
|
||||
struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i];
|
||||
if (!intel_dig_port)
|
||||
continue;
|
||||
|
||||
if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
|
||||
if (!intel_dig_port->dp.can_mst)
|
||||
continue;
|
||||
if (intel_dig_port->dp.is_mst)
|
||||
drm_dp_mst_topology_mgr_suspend(&intel_dig_port->dp.mst_mgr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void intel_dp_mst_resume(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I915_MAX_PORTS; i++) {
|
||||
struct intel_digital_port *intel_dig_port = dev_priv->hpd_irq_port[i];
|
||||
if (!intel_dig_port)
|
||||
continue;
|
||||
if (intel_dig_port->base.type == INTEL_OUTPUT_DISPLAYPORT) {
|
||||
int ret;
|
||||
|
||||
if (!intel_dig_port->dp.can_mst)
|
||||
continue;
|
||||
|
||||
ret = drm_dp_mst_topology_mgr_resume(&intel_dig_port->dp.mst_mgr);
|
||||
if (ret != 0) {
|
||||
intel_dp_check_mst_status(&intel_dig_port->dp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
548
drivers/gpu/drm/i915/intel_dp_mst.c
Normal file
548
drivers/gpu/drm/i915/intel_dp_mst.c
Normal file
@ -0,0 +1,548 @@
|
||||
/*
|
||||
* Copyright © 2008 Intel Corporation
|
||||
* 2014 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "i915_drv.h"
|
||||
#include "intel_drv.h"
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_edid.h>
|
||||
|
||||
static bool intel_dp_mst_compute_config(struct intel_encoder *encoder,
|
||||
struct intel_crtc_config *pipe_config)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
int bpp;
|
||||
int lane_count, slots;
|
||||
struct drm_display_mode *adjusted_mode = &pipe_config->adjusted_mode;
|
||||
struct intel_connector *found = NULL, *intel_connector;
|
||||
int mst_pbn;
|
||||
|
||||
pipe_config->dp_encoder_is_mst = true;
|
||||
pipe_config->has_pch_encoder = false;
|
||||
pipe_config->has_dp_encoder = true;
|
||||
bpp = 24;
|
||||
/*
|
||||
* for MST we always configure max link bw - the spec doesn't
|
||||
* seem to suggest we should do otherwise.
|
||||
*/
|
||||
lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
|
||||
intel_dp->link_bw = intel_dp_max_link_bw(intel_dp);
|
||||
intel_dp->lane_count = lane_count;
|
||||
|
||||
pipe_config->pipe_bpp = 24;
|
||||
pipe_config->port_clock = drm_dp_bw_code_to_link_rate(intel_dp->link_bw);
|
||||
|
||||
list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
|
||||
if (intel_connector->new_encoder == encoder) {
|
||||
found = intel_connector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
DRM_ERROR("can't find connector\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
mst_pbn = drm_dp_calc_pbn_mode(adjusted_mode->clock, bpp);
|
||||
|
||||
pipe_config->pbn = mst_pbn;
|
||||
slots = drm_dp_find_vcpi_slots(&intel_dp->mst_mgr, mst_pbn);
|
||||
|
||||
intel_link_compute_m_n(bpp, lane_count,
|
||||
adjusted_mode->crtc_clock,
|
||||
pipe_config->port_clock,
|
||||
&pipe_config->dp_m_n);
|
||||
|
||||
pipe_config->dp_m_n.tu = slots;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
static void intel_mst_disable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
|
||||
|
||||
drm_dp_mst_reset_vcpi_slots(&intel_dp->mst_mgr, intel_mst->port);
|
||||
|
||||
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to update payload %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_mst_post_disable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
|
||||
DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
|
||||
|
||||
/* this can fail */
|
||||
drm_dp_check_act_status(&intel_dp->mst_mgr);
|
||||
/* and this can also fail */
|
||||
drm_dp_update_payload_part2(&intel_dp->mst_mgr);
|
||||
|
||||
drm_dp_mst_deallocate_vcpi(&intel_dp->mst_mgr, intel_mst->port);
|
||||
|
||||
intel_dp->active_mst_links--;
|
||||
intel_mst->port = NULL;
|
||||
if (intel_dp->active_mst_links == 0) {
|
||||
intel_dig_port->base.post_disable(&intel_dig_port->base);
|
||||
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_mst_pre_enable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
enum port port = intel_dig_port->port;
|
||||
int ret;
|
||||
uint32_t temp;
|
||||
struct intel_connector *found = NULL, *intel_connector;
|
||||
int slots;
|
||||
struct drm_crtc *crtc = encoder->base.crtc;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
|
||||
list_for_each_entry(intel_connector, &dev->mode_config.connector_list, base.head) {
|
||||
if (intel_connector->new_encoder == encoder) {
|
||||
found = intel_connector;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
DRM_ERROR("can't find connector\n");
|
||||
return;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
|
||||
intel_mst->port = found->port;
|
||||
|
||||
if (intel_dp->active_mst_links == 0) {
|
||||
enum port port = intel_ddi_get_encoder_port(encoder);
|
||||
|
||||
I915_WRITE(PORT_CLK_SEL(port), intel_crtc->config.ddi_pll_sel);
|
||||
|
||||
intel_ddi_init_dp_buf_reg(&intel_dig_port->base);
|
||||
|
||||
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
|
||||
|
||||
|
||||
intel_dp_start_link_train(intel_dp);
|
||||
intel_dp_complete_link_train(intel_dp);
|
||||
intel_dp_stop_link_train(intel_dp);
|
||||
}
|
||||
|
||||
ret = drm_dp_mst_allocate_vcpi(&intel_dp->mst_mgr,
|
||||
intel_mst->port, intel_crtc->config.pbn, &slots);
|
||||
if (ret == false) {
|
||||
DRM_ERROR("failed to allocate vcpi\n");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
intel_dp->active_mst_links++;
|
||||
temp = I915_READ(DP_TP_STATUS(port));
|
||||
I915_WRITE(DP_TP_STATUS(port), temp);
|
||||
|
||||
ret = drm_dp_update_payload_part1(&intel_dp->mst_mgr);
|
||||
}
|
||||
|
||||
static void intel_mst_enable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
enum port port = intel_dig_port->port;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%d\n", intel_dp->active_mst_links);
|
||||
|
||||
if (wait_for((I915_READ(DP_TP_STATUS(port)) & DP_TP_STATUS_ACT_SENT),
|
||||
1))
|
||||
DRM_ERROR("Timed out waiting for ACT sent\n");
|
||||
|
||||
ret = drm_dp_check_act_status(&intel_dp->mst_mgr);
|
||||
|
||||
ret = drm_dp_update_payload_part2(&intel_dp->mst_mgr);
|
||||
}
|
||||
|
||||
static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
*pipe = intel_mst->pipe;
|
||||
if (intel_mst->port)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static void intel_dp_mst_enc_get_config(struct intel_encoder *encoder,
|
||||
struct intel_crtc_config *pipe_config)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(&encoder->base);
|
||||
struct intel_digital_port *intel_dig_port = intel_mst->primary;
|
||||
struct intel_crtc *crtc = to_intel_crtc(encoder->base.crtc);
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
enum transcoder cpu_transcoder = crtc->config.cpu_transcoder;
|
||||
u32 temp, flags = 0;
|
||||
|
||||
pipe_config->has_dp_encoder = true;
|
||||
|
||||
temp = I915_READ(TRANS_DDI_FUNC_CTL(cpu_transcoder));
|
||||
if (temp & TRANS_DDI_PHSYNC)
|
||||
flags |= DRM_MODE_FLAG_PHSYNC;
|
||||
else
|
||||
flags |= DRM_MODE_FLAG_NHSYNC;
|
||||
if (temp & TRANS_DDI_PVSYNC)
|
||||
flags |= DRM_MODE_FLAG_PVSYNC;
|
||||
else
|
||||
flags |= DRM_MODE_FLAG_NVSYNC;
|
||||
|
||||
switch (temp & TRANS_DDI_BPC_MASK) {
|
||||
case TRANS_DDI_BPC_6:
|
||||
pipe_config->pipe_bpp = 18;
|
||||
break;
|
||||
case TRANS_DDI_BPC_8:
|
||||
pipe_config->pipe_bpp = 24;
|
||||
break;
|
||||
case TRANS_DDI_BPC_10:
|
||||
pipe_config->pipe_bpp = 30;
|
||||
break;
|
||||
case TRANS_DDI_BPC_12:
|
||||
pipe_config->pipe_bpp = 36;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
pipe_config->adjusted_mode.flags |= flags;
|
||||
intel_dp_get_m_n(crtc, pipe_config);
|
||||
|
||||
intel_ddi_clock_get(&intel_dig_port->base, pipe_config);
|
||||
}
|
||||
|
||||
static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
struct edid *edid;
|
||||
int ret;
|
||||
|
||||
edid = drm_dp_mst_get_edid(connector, &intel_dp->mst_mgr, intel_connector->port);
|
||||
if (!edid)
|
||||
return 0;
|
||||
|
||||
ret = intel_connector_update_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
intel_mst_port_dp_detect(struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
|
||||
return drm_dp_mst_detect_port(&intel_dp->mst_mgr, intel_connector->port);
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
intel_dp_mst_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
enum drm_connector_status status;
|
||||
status = intel_mst_port_dp_detect(connector);
|
||||
return status;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_dp_mst_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
uint64_t val)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_mst_connector_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
|
||||
if (!IS_ERR_OR_NULL(intel_connector->edid))
|
||||
kfree(intel_connector->edid);
|
||||
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
static const struct drm_connector_funcs intel_dp_mst_connector_funcs = {
|
||||
.dpms = intel_connector_dpms,
|
||||
.detect = intel_dp_mst_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = intel_dp_mst_set_property,
|
||||
.destroy = intel_dp_mst_connector_destroy,
|
||||
};
|
||||
|
||||
static int intel_dp_mst_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
return intel_dp_mst_get_ddc_modes(connector);
|
||||
}
|
||||
|
||||
static enum drm_mode_status
|
||||
intel_dp_mst_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
/* TODO - validate mode against available PBN for link */
|
||||
if (mode->clock < 10000)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLCLK)
|
||||
return MODE_H_ILLEGAL;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static struct drm_encoder *intel_mst_best_encoder(struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct intel_dp *intel_dp = intel_connector->mst_port;
|
||||
return &intel_dp->mst_encoders[0]->base.base;
|
||||
}
|
||||
|
||||
static const struct drm_connector_helper_funcs intel_dp_mst_connector_helper_funcs = {
|
||||
.get_modes = intel_dp_mst_get_modes,
|
||||
.mode_valid = intel_dp_mst_mode_valid,
|
||||
.best_encoder = intel_mst_best_encoder,
|
||||
};
|
||||
|
||||
static void intel_dp_mst_encoder_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
kfree(intel_mst);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs intel_dp_mst_enc_funcs = {
|
||||
.destroy = intel_dp_mst_encoder_destroy,
|
||||
};
|
||||
|
||||
static bool intel_dp_mst_get_hw_state(struct intel_connector *connector)
|
||||
{
|
||||
if (connector->encoder) {
|
||||
enum pipe pipe;
|
||||
if (!connector->encoder->get_hw_state(connector->encoder, &pipe))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void intel_connector_add_to_fbdev(struct intel_connector *connector)
|
||||
{
|
||||
#ifdef CONFIG_DRM_I915_FBDEV
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
drm_fb_helper_add_one_connector(&dev_priv->fbdev->helper, &connector->base);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void intel_connector_remove_from_fbdev(struct intel_connector *connector)
|
||||
{
|
||||
#ifdef CONFIG_DRM_I915_FBDEV
|
||||
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
|
||||
drm_fb_helper_remove_one_connector(&dev_priv->fbdev->helper, &connector->base);
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port, char *pathprop)
|
||||
{
|
||||
struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
|
||||
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
struct intel_connector *intel_connector;
|
||||
struct drm_connector *connector;
|
||||
int i;
|
||||
|
||||
intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL);
|
||||
if (!intel_connector)
|
||||
return NULL;
|
||||
|
||||
connector = &intel_connector->base;
|
||||
drm_connector_init(dev, connector, &intel_dp_mst_connector_funcs, DRM_MODE_CONNECTOR_DisplayPort);
|
||||
drm_connector_helper_add(connector, &intel_dp_mst_connector_helper_funcs);
|
||||
|
||||
intel_connector->unregister = intel_connector_unregister;
|
||||
intel_connector->get_hw_state = intel_dp_mst_get_hw_state;
|
||||
intel_connector->mst_port = intel_dp;
|
||||
intel_connector->port = port;
|
||||
|
||||
for (i = PIPE_A; i <= PIPE_C; i++) {
|
||||
drm_mode_connector_attach_encoder(&intel_connector->base,
|
||||
&intel_dp->mst_encoders[i]->base.base);
|
||||
}
|
||||
intel_dp_add_properties(intel_dp, connector);
|
||||
|
||||
drm_object_attach_property(&connector->base, dev->mode_config.path_property, 0);
|
||||
drm_mode_connector_set_path_property(connector, pathprop);
|
||||
drm_reinit_primary_mode_group(dev);
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
intel_connector_add_to_fbdev(intel_connector);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
drm_connector_register(&intel_connector->base);
|
||||
return connector;
|
||||
}
|
||||
|
||||
static void intel_dp_destroy_mst_connector(struct drm_dp_mst_topology_mgr *mgr,
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
struct intel_connector *intel_connector = to_intel_connector(connector);
|
||||
struct drm_device *dev = connector->dev;
|
||||
/* need to nuke the connector */
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
intel_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
intel_connector->unregister(intel_connector);
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
intel_connector_remove_from_fbdev(intel_connector);
|
||||
drm_connector_cleanup(connector);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
drm_reinit_primary_mode_group(dev);
|
||||
|
||||
kfree(intel_connector);
|
||||
DRM_DEBUG_KMS("\n");
|
||||
}
|
||||
|
||||
static void intel_dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
|
||||
{
|
||||
struct intel_dp *intel_dp = container_of(mgr, struct intel_dp, mst_mgr);
|
||||
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
|
||||
drm_kms_helper_hotplug_event(dev);
|
||||
}
|
||||
|
||||
static struct drm_dp_mst_topology_cbs mst_cbs = {
|
||||
.add_connector = intel_dp_add_mst_connector,
|
||||
.destroy_connector = intel_dp_destroy_mst_connector,
|
||||
.hotplug = intel_dp_mst_hotplug,
|
||||
};
|
||||
|
||||
static struct intel_dp_mst_encoder *
|
||||
intel_dp_create_fake_mst_encoder(struct intel_digital_port *intel_dig_port, enum pipe pipe)
|
||||
{
|
||||
struct intel_dp_mst_encoder *intel_mst;
|
||||
struct intel_encoder *intel_encoder;
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
|
||||
intel_mst = kzalloc(sizeof(*intel_mst), GFP_KERNEL);
|
||||
|
||||
if (!intel_mst)
|
||||
return NULL;
|
||||
|
||||
intel_mst->pipe = pipe;
|
||||
intel_encoder = &intel_mst->base;
|
||||
intel_mst->primary = intel_dig_port;
|
||||
|
||||
drm_encoder_init(dev, &intel_encoder->base, &intel_dp_mst_enc_funcs,
|
||||
DRM_MODE_ENCODER_DPMST);
|
||||
|
||||
intel_encoder->type = INTEL_OUTPUT_DP_MST;
|
||||
intel_encoder->crtc_mask = 0x7;
|
||||
intel_encoder->cloneable = 0;
|
||||
|
||||
intel_encoder->compute_config = intel_dp_mst_compute_config;
|
||||
intel_encoder->disable = intel_mst_disable_dp;
|
||||
intel_encoder->post_disable = intel_mst_post_disable_dp;
|
||||
intel_encoder->pre_enable = intel_mst_pre_enable_dp;
|
||||
intel_encoder->enable = intel_mst_enable_dp;
|
||||
intel_encoder->get_hw_state = intel_dp_mst_enc_get_hw_state;
|
||||
intel_encoder->get_config = intel_dp_mst_enc_get_config;
|
||||
|
||||
return intel_mst;
|
||||
|
||||
}
|
||||
|
||||
static bool
|
||||
intel_dp_create_fake_mst_encoders(struct intel_digital_port *intel_dig_port)
|
||||
{
|
||||
int i;
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
|
||||
for (i = PIPE_A; i <= PIPE_C; i++)
|
||||
intel_dp->mst_encoders[i] = intel_dp_create_fake_mst_encoder(intel_dig_port, i);
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_base_id)
|
||||
{
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
struct drm_device *dev = intel_dig_port->base.base.dev;
|
||||
int ret;
|
||||
|
||||
intel_dp->can_mst = true;
|
||||
intel_dp->mst_mgr.cbs = &mst_cbs;
|
||||
|
||||
/* create encoders */
|
||||
intel_dp_create_fake_mst_encoders(intel_dig_port);
|
||||
ret = drm_dp_mst_topology_mgr_init(&intel_dp->mst_mgr, dev->dev, &intel_dp->aux, 16, 3, conn_base_id);
|
||||
if (ret) {
|
||||
intel_dp->can_mst = false;
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port)
|
||||
{
|
||||
struct intel_dp *intel_dp = &intel_dig_port->dp;
|
||||
|
||||
if (!intel_dp->can_mst)
|
||||
return;
|
||||
|
||||
drm_dp_mst_topology_mgr_destroy(&intel_dp->mst_mgr);
|
||||
/* encoders will get killed by normal cleanup */
|
||||
}
|
@ -32,7 +32,7 @@
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
#include <drm/drm_dp_mst_helper.h>
|
||||
|
||||
/**
|
||||
* _wait_for - magic (register) wait macro
|
||||
@ -100,6 +100,7 @@
|
||||
#define INTEL_OUTPUT_EDP 8
|
||||
#define INTEL_OUTPUT_DSI 9
|
||||
#define INTEL_OUTPUT_UNKNOWN 10
|
||||
#define INTEL_OUTPUT_DP_MST 11
|
||||
|
||||
#define INTEL_DVO_CHIP_NONE 0
|
||||
#define INTEL_DVO_CHIP_LVDS 1
|
||||
@ -208,6 +209,10 @@ struct intel_connector {
|
||||
/* since POLL and HPD connectors may use the same HPD line keep the native
|
||||
state of connector->polled in case hotplug storm detection changes it */
|
||||
u8 polled;
|
||||
|
||||
void *port; /* store this opaque as its illegal to dereference it */
|
||||
|
||||
struct intel_dp *mst_port;
|
||||
};
|
||||
|
||||
typedef struct dpll {
|
||||
@ -352,6 +357,9 @@ struct intel_crtc_config {
|
||||
bool ips_enabled;
|
||||
|
||||
bool double_wide;
|
||||
|
||||
bool dp_encoder_is_mst;
|
||||
int pbn;
|
||||
};
|
||||
|
||||
struct intel_pipe_wm {
|
||||
@ -509,6 +517,7 @@ struct intel_hdmi {
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
};
|
||||
|
||||
struct intel_dp_mst_encoder;
|
||||
#define DP_MAX_DOWNSTREAM_PORTS 0x10
|
||||
|
||||
/**
|
||||
@ -548,8 +557,16 @@ struct intel_dp {
|
||||
unsigned long last_power_on;
|
||||
unsigned long last_backlight_off;
|
||||
bool use_tps3;
|
||||
bool can_mst; /* this port supports mst */
|
||||
bool is_mst;
|
||||
int active_mst_links;
|
||||
/* connector directly attached - won't be use for modeset in mst world */
|
||||
struct intel_connector *attached_connector;
|
||||
|
||||
/* mst connector list */
|
||||
struct intel_dp_mst_encoder *mst_encoders[I915_MAX_PIPES];
|
||||
struct drm_dp_mst_topology_mgr mst_mgr;
|
||||
|
||||
uint32_t (*get_aux_clock_divider)(struct intel_dp *dp, int index);
|
||||
/*
|
||||
* This function returns the value we have to program the AUX_CTL
|
||||
@ -576,6 +593,13 @@ struct intel_digital_port {
|
||||
bool (*hpd_pulse)(struct intel_digital_port *, bool);
|
||||
};
|
||||
|
||||
struct intel_dp_mst_encoder {
|
||||
struct intel_encoder base;
|
||||
enum pipe pipe;
|
||||
struct intel_digital_port *primary;
|
||||
void *port; /* store this opaque as its illegal to dereference it */
|
||||
};
|
||||
|
||||
static inline int
|
||||
vlv_dport_to_channel(struct intel_digital_port *dport)
|
||||
{
|
||||
@ -660,6 +684,12 @@ enc_to_dig_port(struct drm_encoder *encoder)
|
||||
return container_of(encoder, struct intel_digital_port, base.base);
|
||||
}
|
||||
|
||||
static inline struct intel_dp_mst_encoder *
|
||||
enc_to_mst(struct drm_encoder *encoder)
|
||||
{
|
||||
return container_of(encoder, struct intel_dp_mst_encoder, base.base);
|
||||
}
|
||||
|
||||
static inline struct intel_dp *enc_to_intel_dp(struct drm_encoder *encoder)
|
||||
{
|
||||
return &enc_to_dig_port(encoder)->dp;
|
||||
@ -730,6 +760,10 @@ void intel_ddi_fdi_disable(struct drm_crtc *crtc);
|
||||
void intel_ddi_get_config(struct intel_encoder *encoder,
|
||||
struct intel_crtc_config *pipe_config);
|
||||
|
||||
void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder);
|
||||
void intel_ddi_clock_get(struct intel_encoder *encoder,
|
||||
struct intel_crtc_config *pipe_config);
|
||||
void intel_ddi_set_vc_payload_alloc(struct drm_crtc *crtc, bool state);
|
||||
|
||||
/* intel_display.c */
|
||||
const char *intel_output_name(int output);
|
||||
@ -886,6 +920,15 @@ void intel_edp_psr_flush(struct drm_device *dev,
|
||||
unsigned frontbuffer_bits);
|
||||
void intel_edp_psr_init(struct drm_device *dev);
|
||||
|
||||
int intel_dp_handle_hpd_irq(struct intel_digital_port *digport, bool long_hpd);
|
||||
void intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connector);
|
||||
void intel_dp_mst_suspend(struct drm_device *dev);
|
||||
void intel_dp_mst_resume(struct drm_device *dev);
|
||||
int intel_dp_max_link_bw(struct intel_dp *intel_dp);
|
||||
void intel_dp_hot_plug(struct intel_encoder *intel_encoder);
|
||||
/* intel_dp_mst.c */
|
||||
int intel_dp_mst_encoder_init(struct intel_digital_port *intel_dig_port, int conn_id);
|
||||
void intel_dp_mst_encoder_cleanup(struct intel_digital_port *intel_dig_port);
|
||||
/* intel_dsi.c */
|
||||
void intel_dsi_init(struct drm_device *dev);
|
||||
|
||||
|
@ -742,7 +742,7 @@ void intel_dsi_init(struct drm_device *dev)
|
||||
|
||||
intel_connector_attach_encoder(intel_connector, intel_encoder);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev);
|
||||
if (!fixed_mode) {
|
||||
|
@ -566,7 +566,7 @@ void intel_dvo_init(struct drm_device *dev)
|
||||
intel_dvo->panel_wants_dither = true;
|
||||
}
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -478,7 +478,7 @@ out:
|
||||
return true;
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
|
||||
static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
|
||||
.initial_config = intel_fb_initial_config,
|
||||
.gamma_set = intel_crtc_fb_gamma_set,
|
||||
.gamma_get = intel_crtc_fb_gamma_get,
|
||||
@ -649,7 +649,8 @@ int intel_fbdev_init(struct drm_device *dev)
|
||||
if (ifbdev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ifbdev->helper.funcs = &intel_fb_helper_funcs;
|
||||
drm_fb_helper_prepare(dev, &ifbdev->helper, &intel_fb_helper_funcs);
|
||||
|
||||
if (!intel_fbdev_init_bios(dev, ifbdev))
|
||||
ifbdev->preferred_bpp = 32;
|
||||
|
||||
|
@ -1585,7 +1585,7 @@ void intel_hdmi_init_connector(struct intel_digital_port *intel_dig_port,
|
||||
intel_hdmi_add_properties(intel_hdmi, connector);
|
||||
|
||||
intel_connector_attach_encoder(intel_connector, intel_encoder);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
|
||||
* 0xd. Failure to do so will result in spurious interrupts being
|
||||
|
@ -1107,7 +1107,7 @@ out:
|
||||
DRM_DEBUG_KMS("lid notifier registration failed\n");
|
||||
lvds_connector->lid_notifier.notifier_call = NULL;
|
||||
}
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
intel_panel_init(&intel_connector->panel, fixed_mode, downclock_mode);
|
||||
intel_panel_setup_backlight(connector);
|
||||
|
@ -352,6 +352,7 @@ int intel_opregion_notify_encoder(struct intel_encoder *intel_encoder,
|
||||
case INTEL_OUTPUT_UNKNOWN:
|
||||
case INTEL_OUTPUT_DISPLAYPORT:
|
||||
case INTEL_OUTPUT_HDMI:
|
||||
case INTEL_OUTPUT_DP_MST:
|
||||
type = DISPLAY_TYPE_EXTERNAL_FLAT_PANEL;
|
||||
break;
|
||||
case INTEL_OUTPUT_EDP:
|
||||
|
@ -1039,7 +1039,7 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
|
||||
struct drm_intel_overlay_put_image *put_image_rec = data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_overlay *overlay;
|
||||
struct drm_mode_object *drmmode_obj;
|
||||
struct drm_crtc *drmmode_crtc;
|
||||
struct intel_crtc *crtc;
|
||||
struct drm_i915_gem_object *new_bo;
|
||||
struct put_image_params *params;
|
||||
@ -1068,13 +1068,12 @@ int intel_overlay_put_image(struct drm_device *dev, void *data,
|
||||
if (!params)
|
||||
return -ENOMEM;
|
||||
|
||||
drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
|
||||
DRM_MODE_OBJECT_CRTC);
|
||||
if (!drmmode_obj) {
|
||||
drmmode_crtc = drm_crtc_find(dev, put_image_rec->crtc_id);
|
||||
if (!drmmode_crtc) {
|
||||
ret = -ENOENT;
|
||||
goto out_free;
|
||||
}
|
||||
crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
|
||||
crtc = to_intel_crtc(drmmode_crtc);
|
||||
|
||||
new_bo = to_intel_bo(drm_gem_object_lookup(dev, file_priv,
|
||||
put_image_rec->bo_handle));
|
||||
|
@ -2433,7 +2433,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
|
||||
connector->base.unregister = intel_sdvo_connector_unregister;
|
||||
|
||||
intel_connector_attach_encoder(&connector->base, &encoder->base);
|
||||
ret = drm_sysfs_connector_add(drm_connector);
|
||||
ret = drm_connector_register(drm_connector);
|
||||
if (ret < 0)
|
||||
goto err1;
|
||||
|
||||
@ -2446,7 +2446,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
|
||||
return 0;
|
||||
|
||||
err2:
|
||||
drm_sysfs_connector_remove(drm_connector);
|
||||
drm_connector_unregister(drm_connector);
|
||||
err1:
|
||||
drm_connector_cleanup(drm_connector);
|
||||
|
||||
@ -2559,7 +2559,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
|
||||
return true;
|
||||
|
||||
err:
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
intel_sdvo_destroy(connector);
|
||||
return false;
|
||||
}
|
||||
@ -2638,7 +2638,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
|
||||
return true;
|
||||
|
||||
err:
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
intel_sdvo_destroy(connector);
|
||||
return false;
|
||||
}
|
||||
@ -2711,7 +2711,7 @@ static void intel_sdvo_output_cleanup(struct intel_sdvo *intel_sdvo)
|
||||
list_for_each_entry_safe(connector, tmp,
|
||||
&dev->mode_config.connector_list, head) {
|
||||
if (intel_attached_encoder(connector) == &intel_sdvo->base) {
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
intel_sdvo_destroy(connector);
|
||||
}
|
||||
}
|
||||
|
@ -1126,7 +1126,6 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_intel_sprite_colorkey *set = data;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_plane *plane;
|
||||
struct intel_plane *intel_plane;
|
||||
int ret = 0;
|
||||
@ -1140,13 +1139,12 @@ int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
|
||||
obj = drm_mode_object_find(dev, set->plane_id, DRM_MODE_OBJECT_PLANE);
|
||||
if (!obj) {
|
||||
plane = drm_plane_find(dev, set->plane_id);
|
||||
if (!plane) {
|
||||
ret = -ENOENT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
plane = obj_to_plane(obj);
|
||||
intel_plane = to_intel_plane(plane);
|
||||
ret = intel_plane->update_colorkey(plane, set);
|
||||
|
||||
@ -1159,7 +1157,6 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_intel_sprite_colorkey *get = data;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_plane *plane;
|
||||
struct intel_plane *intel_plane;
|
||||
int ret = 0;
|
||||
@ -1169,13 +1166,12 @@ int intel_sprite_get_colorkey(struct drm_device *dev, void *data,
|
||||
|
||||
drm_modeset_lock_all(dev);
|
||||
|
||||
obj = drm_mode_object_find(dev, get->plane_id, DRM_MODE_OBJECT_PLANE);
|
||||
if (!obj) {
|
||||
plane = drm_plane_find(dev, get->plane_id);
|
||||
if (!plane) {
|
||||
ret = -ENOENT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
plane = obj_to_plane(obj);
|
||||
intel_plane = to_intel_plane(plane);
|
||||
intel_plane->get_colorkey(plane, get);
|
||||
|
||||
|
@ -1680,5 +1680,5 @@ intel_tv_init(struct drm_device *dev)
|
||||
drm_object_attach_property(&connector->base,
|
||||
dev->mode_config.tv_bottom_margin_property,
|
||||
intel_tv->margin[TV_MARGIN_BOTTOM]);
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
}
|
||||
|
@ -280,7 +280,7 @@ static inline int mgag200_bo_reserve(struct mgag200_bo *bo, bool no_wait)
|
||||
{
|
||||
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 != -ERESTARTSYS && ret != -EBUSY)
|
||||
DRM_ERROR("reserve failed %p\n", bo);
|
||||
|
@ -272,7 +272,7 @@ static int mga_fbdev_destroy(struct drm_device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs mga_fb_helper_funcs = {
|
||||
static const struct drm_fb_helper_funcs mga_fb_helper_funcs = {
|
||||
.gamma_set = mga_crtc_fb_gamma_set,
|
||||
.gamma_get = mga_crtc_fb_gamma_get,
|
||||
.fb_probe = mgag200fb_create,
|
||||
@ -293,9 +293,10 @@ int mgag200_fbdev_init(struct mga_device *mdev)
|
||||
return -ENOMEM;
|
||||
|
||||
mdev->mfbdev = mfbdev;
|
||||
mfbdev->helper.funcs = &mga_fb_helper_funcs;
|
||||
spin_lock_init(&mfbdev->dirty_lock);
|
||||
|
||||
drm_fb_helper_prepare(mdev->dev, &mfbdev->helper, &mga_fb_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(mdev->dev, &mfbdev->helper,
|
||||
mdev->num_crtc, MGAG200FB_CONN_LIMIT);
|
||||
if (ret)
|
||||
|
@ -1562,19 +1562,9 @@ static struct drm_encoder *mga_connector_best_encoder(struct drm_connector
|
||||
*connector)
|
||||
{
|
||||
int enc_id = connector->encoder_ids[0];
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
/* pick the encoder ids */
|
||||
if (enc_id) {
|
||||
obj =
|
||||
drm_mode_object_find(connector->dev, enc_id,
|
||||
DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj)
|
||||
return NULL;
|
||||
encoder = obj_to_encoder(obj);
|
||||
return encoder;
|
||||
}
|
||||
if (enc_id)
|
||||
return drm_encoder_find(connector->dev, enc_id);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -1621,7 +1611,7 @@ static struct drm_connector *mga_vga_init(struct drm_device *dev)
|
||||
|
||||
drm_connector_helper_add(connector, &mga_vga_connector_helper_funcs);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
mga_connector->i2c = mgag200_i2c_create(dev);
|
||||
if (!mga_connector->i2c)
|
||||
|
@ -306,7 +306,7 @@ static void hdmi_connector_destroy(struct drm_connector *connector)
|
||||
|
||||
hdp_disable(hdmi_connector);
|
||||
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
|
||||
hdmi_unreference(hdmi_connector->hdmi);
|
||||
@ -416,7 +416,7 @@ struct drm_connector *hdmi_connector_init(struct hdmi *hdmi)
|
||||
connector->interlace_allowed = 1;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
ret = hpd_enable(hdmi_connector);
|
||||
if (ret) {
|
||||
|
@ -905,12 +905,41 @@ static int compare_of(struct device *dev, void *data)
|
||||
{
|
||||
return dev->of_node == data;
|
||||
}
|
||||
|
||||
static int msm_drm_add_components(struct device *master, struct master *m)
|
||||
#else
|
||||
static int compare_dev(struct device *dev, void *data)
|
||||
{
|
||||
struct device_node *np = master->of_node;
|
||||
return dev == data;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int msm_drm_bind(struct device *dev)
|
||||
{
|
||||
return drm_platform_init(&msm_driver, to_platform_device(dev));
|
||||
}
|
||||
|
||||
static void msm_drm_unbind(struct device *dev)
|
||||
{
|
||||
drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
|
||||
}
|
||||
|
||||
static const struct component_master_ops msm_drm_ops = {
|
||||
.bind = msm_drm_bind,
|
||||
.unbind = msm_drm_unbind,
|
||||
};
|
||||
|
||||
/*
|
||||
* Platform driver:
|
||||
*/
|
||||
|
||||
static int msm_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct component_match *match = NULL;
|
||||
#ifdef CONFIG_OF
|
||||
/* NOTE: the CONFIG_OF case duplicates the same code as exynos or imx
|
||||
* (or probably any other).. so probably some room for some helpers
|
||||
*/
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
unsigned i;
|
||||
int ret;
|
||||
|
||||
for (i = 0; ; i++) {
|
||||
struct device_node *node;
|
||||
@ -919,22 +948,9 @@ static int msm_drm_add_components(struct device *master, struct master *m)
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
ret = component_master_add_child(m, compare_of, node);
|
||||
of_node_put(node);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
component_match_add(&pdev->dev, &match, compare_of, node);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int compare_dev(struct device *dev, void *data)
|
||||
{
|
||||
return dev == data;
|
||||
}
|
||||
|
||||
static int msm_drm_add_components(struct device *master, struct master *m)
|
||||
{
|
||||
/* For non-DT case, it kinda sucks. We don't actually have a way
|
||||
* to know whether or not we are waiting for certain devices (or if
|
||||
* they are simply not present). But for non-DT we only need to
|
||||
@ -958,41 +974,12 @@ static int msm_drm_add_components(struct device *master, struct master *m)
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
ret = component_master_add_child(m, compare_dev, dev);
|
||||
if (ret) {
|
||||
DBG("could not add child: %d", ret);
|
||||
return ret;
|
||||
}
|
||||
component_match_add(&pdev->dev, &match, compare_dev, dev);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int msm_drm_bind(struct device *dev)
|
||||
{
|
||||
return drm_platform_init(&msm_driver, to_platform_device(dev));
|
||||
}
|
||||
|
||||
static void msm_drm_unbind(struct device *dev)
|
||||
{
|
||||
drm_put_dev(platform_get_drvdata(to_platform_device(dev)));
|
||||
}
|
||||
|
||||
static const struct component_master_ops msm_drm_ops = {
|
||||
.add_components = msm_drm_add_components,
|
||||
.bind = msm_drm_bind,
|
||||
.unbind = msm_drm_unbind,
|
||||
};
|
||||
|
||||
/*
|
||||
* Platform driver:
|
||||
*/
|
||||
|
||||
static int msm_pdev_probe(struct platform_device *pdev)
|
||||
{
|
||||
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
|
||||
return component_master_add(&pdev->dev, &msm_drm_ops);
|
||||
return component_master_add_with_match(&pdev->dev, &msm_drm_ops, match);
|
||||
}
|
||||
|
||||
static int msm_pdev_remove(struct platform_device *pdev)
|
||||
|
@ -177,7 +177,7 @@ static void msm_crtc_fb_gamma_get(struct drm_crtc *crtc,
|
||||
DBG("fbdev: get gamma");
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs msm_fb_helper_funcs = {
|
||||
static const struct drm_fb_helper_funcs msm_fb_helper_funcs = {
|
||||
.gamma_set = msm_crtc_fb_gamma_set,
|
||||
.gamma_get = msm_crtc_fb_gamma_get,
|
||||
.fb_probe = msm_fbdev_create,
|
||||
@ -197,7 +197,7 @@ struct drm_fb_helper *msm_fbdev_init(struct drm_device *dev)
|
||||
|
||||
helper = &fbdev->base;
|
||||
|
||||
helper->funcs = &msm_fb_helper_funcs;
|
||||
drm_fb_helper_prepare(dev, helper, &msm_fb_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, helper,
|
||||
priv->num_crtcs, priv->num_connectors);
|
||||
|
@ -73,7 +73,7 @@ static struct page **get_pages(struct drm_gem_object *obj)
|
||||
int npages = obj->size >> PAGE_SHIFT;
|
||||
|
||||
if (iommu_present(&platform_bus_type))
|
||||
p = drm_gem_get_pages(obj, 0);
|
||||
p = drm_gem_get_pages(obj);
|
||||
else
|
||||
p = get_pages_vram(obj, npages);
|
||||
|
||||
|
@ -309,7 +309,7 @@ nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
|
||||
struct ttm_buffer_object *bo = &nvbo->bo;
|
||||
int ret;
|
||||
|
||||
ret = ttm_bo_reserve(bo, false, false, false, 0);
|
||||
ret = ttm_bo_reserve(bo, false, false, false, NULL);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
@ -350,7 +350,7 @@ nouveau_bo_unpin(struct nouveau_bo *nvbo)
|
||||
struct ttm_buffer_object *bo = &nvbo->bo;
|
||||
int ret, ref;
|
||||
|
||||
ret = ttm_bo_reserve(bo, false, false, false, 0);
|
||||
ret = ttm_bo_reserve(bo, false, false, false, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -385,7 +385,7 @@ nouveau_bo_map(struct nouveau_bo *nvbo)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -63,7 +63,7 @@ find_encoder(struct drm_connector *connector, int type)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_encoder *enc;
|
||||
int i, id;
|
||||
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
@ -71,10 +71,10 @@ find_encoder(struct drm_connector *connector, int type)
|
||||
if (!id)
|
||||
break;
|
||||
|
||||
obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj)
|
||||
enc = drm_encoder_find(dev, id);
|
||||
if (!enc)
|
||||
continue;
|
||||
nv_encoder = nouveau_encoder(obj_to_encoder(obj));
|
||||
nv_encoder = nouveau_encoder(enc);
|
||||
|
||||
if (type == DCB_OUTPUT_ANY ||
|
||||
(nv_encoder->dcb && nv_encoder->dcb->type == type))
|
||||
@ -104,7 +104,7 @@ nouveau_connector_destroy(struct drm_connector *connector)
|
||||
struct nouveau_connector *nv_connector = nouveau_connector(connector);
|
||||
nouveau_event_ref(NULL, &nv_connector->hpd);
|
||||
kfree(nv_connector->edid);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
if (nv_connector->aux.transfer)
|
||||
drm_dp_aux_unregister(&nv_connector->aux);
|
||||
@ -119,7 +119,7 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
||||
struct nouveau_drm *drm = nouveau_drm(dev);
|
||||
struct nouveau_gpio *gpio = nouveau_gpio(drm->device);
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_encoder *encoder;
|
||||
int i, panel = -ENODEV;
|
||||
|
||||
/* eDP panels need powering on by us (if the VBIOS doesn't default it
|
||||
@ -139,10 +139,10 @@ nouveau_connector_ddc_detect(struct drm_connector *connector)
|
||||
if (id == 0)
|
||||
break;
|
||||
|
||||
obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
|
||||
if (!obj)
|
||||
encoder = drm_encoder_find(dev, id);
|
||||
if (!encoder)
|
||||
continue;
|
||||
nv_encoder = nouveau_encoder(obj_to_encoder(obj));
|
||||
nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
if (nv_encoder->dcb->type == DCB_OUTPUT_DP) {
|
||||
int ret = nouveau_dp_detect(nv_encoder);
|
||||
@ -1236,6 +1236,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
|
||||
|
||||
INIT_WORK(&nv_connector->work, nouveau_connector_hotplug_work);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
return connector;
|
||||
}
|
||||
|
@ -438,7 +438,7 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
|
||||
info->flags |= FBINFO_HWACCEL_DISABLED;
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
|
||||
static const struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
|
||||
.gamma_set = nouveau_fbcon_gamma_set,
|
||||
.gamma_get = nouveau_fbcon_gamma_get,
|
||||
.fb_probe = nouveau_fbcon_create,
|
||||
@ -464,7 +464,8 @@ nouveau_fbcon_init(struct drm_device *dev)
|
||||
|
||||
fbcon->dev = dev;
|
||||
drm->fbcon = fbcon;
|
||||
fbcon->helper.funcs = &nouveau_fbcon_helper_funcs;
|
||||
|
||||
drm_fb_helper_prepare(dev, &fbcon->helper, &nouveau_fbcon_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, &fbcon->helper,
|
||||
dev->mode_config.num_crtc, 4);
|
||||
|
@ -61,7 +61,7 @@ nouveau_gem_object_open(struct drm_gem_object *gem, struct drm_file *file_priv)
|
||||
if (!cli->base.vm)
|
||||
return 0;
|
||||
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -132,7 +132,7 @@ nouveau_gem_object_close(struct drm_gem_object *gem, struct drm_file *file_priv)
|
||||
if (!cli->base.vm)
|
||||
return;
|
||||
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
|
||||
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, NULL);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
|
@ -76,6 +76,7 @@ static int
|
||||
nouveau_vram_manager_new(struct ttm_mem_type_manager *man,
|
||||
struct ttm_buffer_object *bo,
|
||||
struct ttm_placement *placement,
|
||||
uint32_t flags,
|
||||
struct ttm_mem_reg *mem)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_bdev(man->bdev);
|
||||
@ -162,6 +163,7 @@ static int
|
||||
nouveau_gart_manager_new(struct ttm_mem_type_manager *man,
|
||||
struct ttm_buffer_object *bo,
|
||||
struct ttm_placement *placement,
|
||||
uint32_t flags,
|
||||
struct ttm_mem_reg *mem)
|
||||
{
|
||||
struct nouveau_drm *drm = nouveau_bdev(bo->bdev);
|
||||
@ -242,6 +244,7 @@ static int
|
||||
nv04_gart_manager_new(struct ttm_mem_type_manager *man,
|
||||
struct ttm_buffer_object *bo,
|
||||
struct ttm_placement *placement,
|
||||
uint32_t flags,
|
||||
struct ttm_mem_reg *mem)
|
||||
{
|
||||
struct nouveau_mem *node;
|
||||
|
@ -130,7 +130,7 @@ static void omap_connector_destroy(struct drm_connector *connector)
|
||||
struct omap_dss_device *dssdev = omap_connector->dssdev;
|
||||
|
||||
DBG("%s", omap_connector->dssdev->name);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_unregister(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(omap_connector);
|
||||
|
||||
@ -307,7 +307,7 @@ struct drm_connector *omap_connector_init(struct drm_device *dev,
|
||||
connector->interlace_allowed = 1;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
drm_connector_register(connector);
|
||||
|
||||
return connector;
|
||||
|
||||
|
@ -199,7 +199,7 @@ static struct dmm_txn *dmm_txn_init(struct dmm *dmm, struct tcm *tcm)
|
||||
static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
|
||||
struct page **pages, uint32_t npages, uint32_t roll)
|
||||
{
|
||||
dma_addr_t pat_pa = 0;
|
||||
dma_addr_t pat_pa = 0, data_pa = 0;
|
||||
uint32_t *data;
|
||||
struct pat *pat;
|
||||
struct refill_engine *engine = txn->engine_handle;
|
||||
@ -223,7 +223,9 @@ static void dmm_txn_append(struct dmm_txn *txn, struct pat_area *area,
|
||||
.lut_id = engine->tcm->lut_id,
|
||||
};
|
||||
|
||||
data = alloc_dma(txn, 4*i, &pat->data_pa);
|
||||
data = alloc_dma(txn, 4*i, &data_pa);
|
||||
/* FIXME: what if data_pa is more than 32-bit ? */
|
||||
pat->data_pa = data_pa;
|
||||
|
||||
while (i--) {
|
||||
int n = i + roll;
|
||||
|
@ -281,7 +281,7 @@ fail:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs omap_fb_helper_funcs = {
|
||||
static const struct drm_fb_helper_funcs omap_fb_helper_funcs = {
|
||||
.fb_probe = omap_fbdev_create,
|
||||
};
|
||||
|
||||
@ -325,7 +325,7 @@ struct drm_fb_helper *omap_fbdev_init(struct drm_device *dev)
|
||||
|
||||
helper = &fbdev->base;
|
||||
|
||||
helper->funcs = &omap_fb_helper_funcs;
|
||||
drm_fb_helper_prepare(dev, helper, &omap_fb_helper_funcs);
|
||||
|
||||
ret = drm_fb_helper_init(dev, helper,
|
||||
priv->num_crtcs, priv->num_connectors);
|
||||
|
@ -233,11 +233,7 @@ static int omap_gem_attach_pages(struct drm_gem_object *obj)
|
||||
|
||||
WARN_ON(omap_obj->pages);
|
||||
|
||||
/* TODO: __GFP_DMA32 .. but somehow GFP_HIGHMEM is coming from the
|
||||
* mapping_gfp_mask(mapping) which conflicts w/ GFP_DMA32.. probably
|
||||
* we actually want CMA memory for it all anyways..
|
||||
*/
|
||||
pages = drm_gem_get_pages(obj, GFP_KERNEL);
|
||||
pages = drm_gem_get_pages(obj);
|
||||
if (IS_ERR(pages)) {
|
||||
dev_err(obj->dev->dev, "could not get pages: %ld\n", PTR_ERR(pages));
|
||||
return PTR_ERR(pages);
|
||||
@ -791,7 +787,7 @@ int omap_gem_get_paddr(struct drm_gem_object *obj,
|
||||
omap_obj->paddr = tiler_ssptr(block);
|
||||
omap_obj->block = block;
|
||||
|
||||
DBG("got paddr: %08x", omap_obj->paddr);
|
||||
DBG("got paddr: %pad", &omap_obj->paddr);
|
||||
}
|
||||
|
||||
omap_obj->paddr_cnt++;
|
||||
@ -985,9 +981,9 @@ void omap_gem_describe(struct drm_gem_object *obj, struct seq_file *m)
|
||||
|
||||
off = drm_vma_node_start(&obj->vma_node);
|
||||
|
||||
seq_printf(m, "%08x: %2d (%2d) %08llx %08Zx (%2d) %p %4d",
|
||||
seq_printf(m, "%08x: %2d (%2d) %08llx %pad (%2d) %p %4d",
|
||||
omap_obj->flags, obj->name, obj->refcount.refcount.counter,
|
||||
off, omap_obj->paddr, omap_obj->paddr_cnt,
|
||||
off, &omap_obj->paddr, omap_obj->paddr_cnt,
|
||||
omap_obj->vaddr, omap_obj->roll);
|
||||
|
||||
if (omap_obj->flags & OMAP_BO_TILED) {
|
||||
@ -1183,9 +1179,7 @@ int omap_gem_op_sync(struct drm_gem_object *obj, enum omap_gem_op op)
|
||||
}
|
||||
}
|
||||
spin_unlock(&sync_lock);
|
||||
|
||||
if (waiter)
|
||||
kfree(waiter);
|
||||
kfree(waiter);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -1347,6 +1341,7 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
|
||||
struct omap_drm_private *priv = dev->dev_private;
|
||||
struct omap_gem_object *omap_obj;
|
||||
struct drm_gem_object *obj = NULL;
|
||||
struct address_space *mapping;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
@ -1404,14 +1399,16 @@ struct drm_gem_object *omap_gem_new(struct drm_device *dev,
|
||||
omap_obj->height = gsize.tiled.height;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM))
|
||||
if (flags & (OMAP_BO_DMA|OMAP_BO_EXT_MEM)) {
|
||||
drm_gem_private_object_init(dev, obj, size);
|
||||
else
|
||||
} else {
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
if (ret)
|
||||
goto fail;
|
||||
|
||||
if (ret)
|
||||
goto fail;
|
||||
mapping = file_inode(obj->filp)->i_mapping;
|
||||
mapping_set_gfp_mask(mapping, GFP_USER | __GFP_DMA32);
|
||||
}
|
||||
|
||||
return obj;
|
||||
|
||||
@ -1467,8 +1464,8 @@ void omap_gem_init(struct drm_device *dev)
|
||||
entry->paddr = tiler_ssptr(block);
|
||||
entry->block = block;
|
||||
|
||||
DBG("%d:%d: %dx%d: paddr=%08x stride=%d", i, j, w, h,
|
||||
entry->paddr,
|
||||
DBG("%d:%d: %dx%d: paddr=%pad stride=%d", i, j, w, h,
|
||||
&entry->paddr,
|
||||
usergart[i].stride_pfn << PAGE_SHIFT);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user