提交 c09cd6e9 编写于 作者: D Daniel Vetter

Merge branch 'backlight-rework' into drm-intel-next-queued

Pull in Jani's backlight rework branch. This was merged through a
separate branch to be able to sort out the Broadwell conflicts
properly before pulling it into the main development branch.

Conflicts:
	drivers/gpu/drm/i915/intel_display.c
Signed-off-by: NDaniel Vetter <daniel.vetter@ffwll.ch>
...@@ -2834,7 +2834,9 @@ L: dri-devel@lists.freedesktop.org ...@@ -2834,7 +2834,9 @@ L: dri-devel@lists.freedesktop.org
L: linux-tegra@vger.kernel.org L: linux-tegra@vger.kernel.org
T: git git://anongit.freedesktop.org/tegra/linux.git T: git git://anongit.freedesktop.org/tegra/linux.git
S: Supported S: Supported
F: drivers/gpu/drm/tegra/
F: drivers/gpu/host1x/ F: drivers/gpu/host1x/
F: include/linux/host1x.h
F: include/uapi/drm/tegra_drm.h F: include/uapi/drm/tegra_drm.h
F: Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt F: Documentation/devicetree/bindings/gpu/nvidia,tegra20-host1x.txt
......
...@@ -313,6 +313,16 @@ static size_t __init gen6_stolen_size(int num, int slot, int func) ...@@ -313,6 +313,16 @@ static size_t __init gen6_stolen_size(int num, int slot, int func)
return gmch_ctrl << 25; /* 32 MB units */ return gmch_ctrl << 25; /* 32 MB units */
} }
static inline size_t gen8_stolen_size(int num, int slot, int func)
{
u16 gmch_ctrl;
gmch_ctrl = read_pci_config_16(num, slot, func, SNB_GMCH_CTRL);
gmch_ctrl >>= BDW_GMCH_GMS_SHIFT;
gmch_ctrl &= BDW_GMCH_GMS_MASK;
return gmch_ctrl << 25; /* 32 MB units */
}
typedef size_t (*stolen_size_fn)(int num, int slot, int func); typedef size_t (*stolen_size_fn)(int num, int slot, int func);
static struct pci_device_id intel_stolen_ids[] __initdata = { static struct pci_device_id intel_stolen_ids[] __initdata = {
...@@ -336,6 +346,8 @@ static struct pci_device_id intel_stolen_ids[] __initdata = { ...@@ -336,6 +346,8 @@ static struct pci_device_id intel_stolen_ids[] __initdata = {
INTEL_IVB_D_IDS(gen6_stolen_size), INTEL_IVB_D_IDS(gen6_stolen_size),
INTEL_HSW_D_IDS(gen6_stolen_size), INTEL_HSW_D_IDS(gen6_stolen_size),
INTEL_HSW_M_IDS(gen6_stolen_size), INTEL_HSW_M_IDS(gen6_stolen_size),
INTEL_BDW_M_IDS(gen8_stolen_size),
INTEL_BDW_D_IDS(gen8_stolen_size)
}; };
static void __init intel_graphics_stolen(int num, int slot, int func) static void __init intel_graphics_stolen(int num, int slot, int func)
......
...@@ -176,6 +176,8 @@ source "drivers/gpu/drm/mgag200/Kconfig" ...@@ -176,6 +176,8 @@ source "drivers/gpu/drm/mgag200/Kconfig"
source "drivers/gpu/drm/cirrus/Kconfig" source "drivers/gpu/drm/cirrus/Kconfig"
source "drivers/gpu/drm/armada/Kconfig"
source "drivers/gpu/drm/rcar-du/Kconfig" source "drivers/gpu/drm/rcar-du/Kconfig"
source "drivers/gpu/drm/shmobile/Kconfig" source "drivers/gpu/drm/shmobile/Kconfig"
...@@ -187,3 +189,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig" ...@@ -187,3 +189,5 @@ source "drivers/gpu/drm/tilcdc/Kconfig"
source "drivers/gpu/drm/qxl/Kconfig" source "drivers/gpu/drm/qxl/Kconfig"
source "drivers/gpu/drm/msm/Kconfig" source "drivers/gpu/drm/msm/Kconfig"
source "drivers/gpu/drm/tegra/Kconfig"
...@@ -50,10 +50,12 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/ ...@@ -50,10 +50,12 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_GMA500) += gma500/ obj-$(CONFIG_DRM_GMA500) += gma500/
obj-$(CONFIG_DRM_UDL) += udl/ obj-$(CONFIG_DRM_UDL) += udl/
obj-$(CONFIG_DRM_AST) += ast/ obj-$(CONFIG_DRM_AST) += ast/
obj-$(CONFIG_DRM_ARMADA) += armada/
obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/ obj-$(CONFIG_DRM_RCAR_DU) += rcar-du/
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/ obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
obj-$(CONFIG_DRM_OMAP) += omapdrm/ obj-$(CONFIG_DRM_OMAP) += omapdrm/
obj-$(CONFIG_DRM_TILCDC) += tilcdc/ obj-$(CONFIG_DRM_TILCDC) += tilcdc/
obj-$(CONFIG_DRM_QXL) += qxl/ obj-$(CONFIG_DRM_QXL) += qxl/
obj-$(CONFIG_DRM_MSM) += msm/ obj-$(CONFIG_DRM_MSM) += msm/
obj-$(CONFIG_DRM_TEGRA) += tegra/
obj-y += i2c/ obj-y += i2c/
config DRM_ARMADA
tristate "DRM support for Marvell Armada SoCs"
depends on DRM && HAVE_CLK && ARM
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select DRM_KMS_HELPER
help
Support the "LCD" controllers found on the Marvell Armada 510
devices. There are two controllers on the device, each controller
supports graphics and video overlays.
This driver provides no built-in acceleration; acceleration is
performed by other IP found on the SoC. This driver provides
kernel mode setting and buffer management to userspace.
config DRM_ARMADA_TDA1998X
bool "Support TDA1998X HDMI output"
depends on DRM_ARMADA != n
depends on I2C && DRM_I2C_NXP_TDA998X = y
default y
help
Support the TDA1998x HDMI output device found on the Solid-Run
CuBox.
armada-y := armada_crtc.o armada_drv.o armada_fb.o armada_fbdev.o \
armada_gem.o armada_output.o armada_overlay.o \
armada_slave.o
armada-y += armada_510.o
armada-$(CONFIG_DEBUG_FS) += armada_debugfs.o
obj-$(CONFIG_DRM_ARMADA) := armada.o
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Armada 510 (aka Dove) variant support
*/
#include <linux/clk.h>
#include <linux/io.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_hw.h"
static int armada510_init(struct armada_private *priv, struct device *dev)
{
priv->extclk[0] = devm_clk_get(dev, "ext_ref_clk_1");
if (IS_ERR(priv->extclk[0]) && PTR_ERR(priv->extclk[0]) == -ENOENT)
priv->extclk[0] = ERR_PTR(-EPROBE_DEFER);
return PTR_RET(priv->extclk[0]);
}
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;
}
/*
* Armada510 specific SCLK register selection.
* This gets called with sclk = NULL to test whether the mode is
* supportable, and again with sclk != NULL to set the clocks up for
* that. The former can return an error, but the latter is expected
* not to.
*
* We currently are pretty rudimentary here, always selecting
* EXT_REF_CLK_1 for LCD0 and erroring LCD1. This needs improvement!
*/
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];
int ret;
if (dcrtc->num == 1)
return -EINVAL;
if (IS_ERR(clk))
return PTR_ERR(clk);
if (dcrtc->clk != clk) {
ret = clk_prepare_enable(clk);
if (ret)
return ret;
dcrtc->clk = clk;
}
if (sclk) {
uint32_t rate, ref, div;
rate = mode->clock * 1000;
ref = clk_round_rate(clk, rate);
div = DIV_ROUND_UP(ref, rate);
if (div < 1)
div = 1;
clk_set_rate(clk, ref);
*sclk = div | SCLK_510_EXTCLK1;
}
return 0;
}
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,
};
此差异已折叠。
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_CRTC_H
#define ARMADA_CRTC_H
struct armada_gem_object;
struct armada_regs {
uint32_t offset;
uint32_t mask;
uint32_t val;
};
#define armada_reg_queue_mod(_r, _i, _v, _m, _o) \
do { \
struct armada_regs *__reg = _r; \
__reg[_i].offset = _o; \
__reg[_i].mask = ~(_m); \
__reg[_i].val = _v; \
_i++; \
} while (0)
#define armada_reg_queue_set(_r, _i, _v, _o) \
armada_reg_queue_mod(_r, _i, _v, ~0, _o)
#define armada_reg_queue_end(_r, _i) \
armada_reg_queue_mod(_r, _i, 0, 0, ~0)
struct armada_frame_work;
struct armada_crtc {
struct drm_crtc crtc;
unsigned num;
void __iomem *base;
struct clk *clk;
struct {
uint32_t spu_v_h_total;
uint32_t spu_v_porch;
uint32_t spu_adv_reg;
} v[2];
bool interlaced;
bool cursor_update;
uint8_t csc_yuv_mode;
uint8_t csc_rgb_mode;
struct drm_plane *plane;
struct armada_gem_object *cursor_obj;
int cursor_x;
int cursor_y;
uint32_t cursor_hw_pos;
uint32_t cursor_hw_sz;
uint32_t cursor_w;
uint32_t cursor_h;
int dpms;
uint32_t cfg_dumb_ctrl;
uint32_t dumb_ctrl;
uint32_t spu_iopad_ctrl;
wait_queue_head_t frame_wait;
struct armada_frame_work *frame_work;
spinlock_t irq_lock;
uint32_t irq_ena;
struct list_head vbl_list;
};
#define drm_to_armada_crtc(c) container_of(c, struct armada_crtc, crtc)
int armada_drm_crtc_create(struct drm_device *, unsigned, struct resource *);
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 *);
#endif
/*
* Copyright (C) 2012 Russell King
* Rewritten from the dovefb driver, and Armada510 manuals.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/ctype.h>
#include <linux/debugfs.h>
#include <linux/module.h>
#include <linux/seq_file.h>
#include <drm/drmP.h>
#include "armada_crtc.h"
#include "armada_drm.h"
static int armada_debugfs_gem_linear_show(struct seq_file *m, void *data)
{
struct drm_info_node *node = m->private;
struct drm_device *dev = node->minor->dev;
struct armada_private *priv = dev->dev_private;
int ret;
mutex_lock(&dev->struct_mutex);
ret = drm_mm_dump_table(m, &priv->linear);
mutex_unlock(&dev->struct_mutex);
return ret;
}
static int armada_debugfs_reg_show(struct seq_file *m, void *data)
{
struct drm_device *dev = m->private;
struct armada_private *priv = dev->dev_private;
int n, i;
if (priv) {
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
struct armada_crtc *dcrtc = priv->dcrtc[n];
if (!dcrtc)
continue;
for (i = 0x84; i <= 0x1c4; i += 4) {
uint32_t v = readl_relaxed(dcrtc->base + i);
seq_printf(m, "%u: 0x%04x: 0x%08x\n", n, i, v);
}
}
}
return 0;
}
static int armada_debugfs_reg_r_open(struct inode *inode, struct file *file)
{
return single_open(file, armada_debugfs_reg_show, inode->i_private);
}
static const struct file_operations fops_reg_r = {
.owner = THIS_MODULE,
.open = armada_debugfs_reg_r_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int armada_debugfs_write(struct file *file, const char __user *ptr,
size_t len, loff_t *off)
{
struct drm_device *dev = file->private_data;
struct armada_private *priv = dev->dev_private;
struct armada_crtc *dcrtc = priv->dcrtc[0];
char buf[32], *p;
uint32_t reg, val;
int ret;
if (*off != 0)
return 0;
if (len > sizeof(buf) - 1)
len = sizeof(buf) - 1;
ret = strncpy_from_user(buf, ptr, len);
if (ret < 0)
return ret;
buf[len] = '\0';
reg = simple_strtoul(buf, &p, 16);
if (!isspace(*p))
return -EINVAL;
val = simple_strtoul(p + 1, NULL, 16);
if (reg >= 0x84 && reg <= 0x1c4)
writel(val, dcrtc->base + reg);
return len;
}
static const struct file_operations fops_reg_w = {
.owner = THIS_MODULE,
.open = simple_open,
.write = armada_debugfs_write,
.llseek = noop_llseek,
};
static struct drm_info_list armada_debugfs_list[] = {
{ "gem_linear", armada_debugfs_gem_linear_show, 0 },
};
#define ARMADA_DEBUGFS_ENTRIES ARRAY_SIZE(armada_debugfs_list)
static int drm_add_fake_info_node(struct drm_minor *minor, struct dentry *ent,
const void *key)
{
struct drm_info_node *node;
node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
if (node == NULL) {
debugfs_remove(ent);
return -ENOMEM;
}
node->minor = minor;
node->dent = ent;
node->info_ent = (void *) key;
mutex_lock(&minor->debugfs_lock);
list_add(&node->list, &minor->debugfs_list);
mutex_unlock(&minor->debugfs_lock);
return 0;
}
static int armada_debugfs_create(struct dentry *root, struct drm_minor *minor,
const char *name, umode_t mode, const struct file_operations *fops)
{
struct dentry *de;
de = debugfs_create_file(name, mode, root, minor->dev, fops);
return drm_add_fake_info_node(minor, de, fops);
}
int armada_drm_debugfs_init(struct drm_minor *minor)
{
int ret;
ret = drm_debugfs_create_files(armada_debugfs_list,
ARMADA_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
if (ret)
return ret;
ret = armada_debugfs_create(minor->debugfs_root, minor,
"reg", S_IFREG | S_IRUSR, &fops_reg_r);
if (ret)
goto err_1;
ret = armada_debugfs_create(minor->debugfs_root, minor,
"reg_wr", S_IFREG | S_IWUSR, &fops_reg_w);
if (ret)
goto err_2;
return ret;
err_2:
drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor);
err_1:
drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
minor);
return ret;
}
void armada_drm_debugfs_cleanup(struct drm_minor *minor)
{
drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_w, 1, minor);
drm_debugfs_remove_files((struct drm_info_list *)&fops_reg_r, 1, minor);
drm_debugfs_remove_files(armada_debugfs_list, ARMADA_DEBUGFS_ENTRIES,
minor);
}
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_DRM_H
#define ARMADA_DRM_H
#include <linux/kfifo.h>
#include <linux/io.h>
#include <linux/workqueue.h>
#include <drm/drmP.h>
struct armada_crtc;
struct armada_gem_object;
struct clk;
struct drm_fb_helper;
static inline void
armada_updatel(uint32_t val, uint32_t mask, void __iomem *ptr)
{
uint32_t ov, v;
ov = v = readl_relaxed(ptr);
v = (v & ~mask) | val;
if (ov != v)
writel_relaxed(v, ptr);
}
static inline uint32_t armada_pitch(uint32_t width, uint32_t bpp)
{
uint32_t pitch = bpp != 4 ? width * ((bpp + 7) / 8) : width / 2;
/* 88AP510 spec recommends pitch be a multiple of 128 */
return ALIGN(pitch, 128);
}
struct armada_vbl_event {
struct list_head node;
void *data;
void (*fn)(struct armada_crtc *, void *);
};
void armada_drm_vbl_event_add(struct armada_crtc *,
struct armada_vbl_event *);
void armada_drm_vbl_event_remove(struct armada_crtc *,
struct armada_vbl_event *);
void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *,
struct armada_vbl_event *);
#define armada_drm_vbl_event_init(_e, _f, _d) do { \
struct armada_vbl_event *__e = _e; \
INIT_LIST_HEAD(&__e->node); \
__e->data = _d; \
__e->fn = _f; \
} while (0)
struct armada_private;
struct armada_variant {
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 *);
};
/* 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;
struct drm_property *colorkey_min_prop;
struct drm_property *colorkey_max_prop;
struct drm_property *colorkey_val_prop;
struct drm_property *colorkey_alpha_prop;
struct drm_property *colorkey_mode_prop;
struct drm_property *brightness_prop;
struct drm_property *contrast_prop;
struct drm_property *saturation_prop;
#ifdef CONFIG_DEBUG_FS
struct dentry *de;
#endif
};
void __armada_drm_queue_unref_work(struct drm_device *,
struct drm_framebuffer *);
void armada_drm_queue_unref_work(struct drm_device *,
struct drm_framebuffer *);
extern const struct drm_mode_config_funcs armada_drm_mode_config_funcs;
int armada_fbdev_init(struct drm_device *);
void armada_fbdev_fini(struct drm_device *);
int armada_overlay_plane_create(struct drm_device *, unsigned long);
int armada_drm_debugfs_init(struct drm_minor *);
void armada_drm_debugfs_cleanup(struct drm_minor *);
#endif
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/clk.h>
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_gem.h"
#include "armada_hw.h"
#include <drm/armada_drm.h>
#include "armada_ioctlP.h"
#ifdef CONFIG_DRM_ARMADA_TDA1998X
#include <drm/i2c/tda998x.h>
#include "armada_slave.h"
static struct tda998x_encoder_params params = {
/* With 0x24, there is no translation between vp_out and int_vp
FB LCD out Pins VIP Int Vp
R:23:16 R:7:0 VPC7:0 7:0 7:0[R]
G:15:8 G:15:8 VPB7:0 23:16 23:16[G]
B:7:0 B:23:16 VPA7:0 15:8 15:8[B]
*/
.swap_a = 2,
.swap_b = 3,
.swap_c = 4,
.swap_d = 5,
.swap_e = 0,
.swap_f = 1,
.audio_cfg = BIT(2),
.audio_frame[1] = 1,
.audio_format = AFMT_SPDIF,
.audio_sample_rate = 44100,
};
static const struct armada_drm_slave_config tda19988_config = {
.i2c_adapter_id = 0,
.crtcs = 1 << 0, /* Only LCD0 at the moment */
.polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT,
.interlace_allowed = true,
.info = {
.type = "tda998x",
.addr = 0x70,
.platform_data = &params,
},
};
#endif
static void armada_drm_unref_work(struct work_struct *work)
{
struct armada_private *priv =
container_of(work, struct armada_private, fb_unref_work);
struct drm_framebuffer *fb;
while (kfifo_get(&priv->fb_unref, &fb))
drm_framebuffer_unreference(fb);
}
/* Must be called with dev->event_lock held */
void __armada_drm_queue_unref_work(struct drm_device *dev,
struct drm_framebuffer *fb)
{
struct armada_private *priv = dev->dev_private;
/*
* Yes, we really must jump through these hoops just to store a
* _pointer_ to something into the kfifo. This is utterly insane
* and idiotic, because it kfifo requires the _data_ pointed to by
* the pointer const, not the pointer itself. Not only that, but
* you have to pass a pointer _to_ the pointer you want stored.
*/
const struct drm_framebuffer *silly_api_alert = fb;
WARN_ON(!kfifo_put(&priv->fb_unref, &silly_api_alert));
schedule_work(&priv->fb_unref_work);
}
void armada_drm_queue_unref_work(struct drm_device *dev,
struct drm_framebuffer *fb)
{
unsigned long flags;
spin_lock_irqsave(&dev->event_lock, flags);
__armada_drm_queue_unref_work(dev, fb);
spin_unlock_irqrestore(&dev->event_lock, flags);
}
static int armada_drm_load(struct drm_device *dev, unsigned long flags)
{
const struct platform_device_id *id;
struct armada_private *priv;
struct resource *res[ARRAY_SIZE(priv->dcrtc)];
struct resource *mem = NULL;
int ret, n, i;
memset(res, 0, sizeof(res));
for (n = i = 0; ; n++) {
struct resource *r = platform_get_resource(dev->platformdev,
IORESOURCE_MEM, n);
if (!r)
break;
/* Resources above 64K are graphics memory */
if (resource_size(r) > SZ_64K)
mem = r;
else if (i < ARRAY_SIZE(priv->dcrtc))
res[i++] = r;
else
return -EINVAL;
}
if (!res[0] || !mem)
return -ENXIO;
if (!devm_request_mem_region(dev->dev, mem->start,
resource_size(mem), "armada-drm"))
return -EBUSY;
priv = devm_kzalloc(dev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv) {
DRM_ERROR("failed to allocate private\n");
return -ENOMEM;
}
dev->dev_private = priv;
/* Get the implementation specific driver data. */
id = platform_get_device_id(dev->platformdev);
if (!id)
return -ENXIO;
priv->variant = (struct armada_variant *)id->driver_data;
ret = priv->variant->init(priv, dev->dev);
if (ret)
return ret;
INIT_WORK(&priv->fb_unref_work, armada_drm_unref_work);
INIT_KFIFO(priv->fb_unref);
/* Mode setting support */
drm_mode_config_init(dev);
dev->mode_config.min_width = 320;
dev->mode_config.min_height = 200;
/*
* With vscale enabled, the maximum width is 1920 due to the
* 1920 by 3 lines RAM
*/
dev->mode_config.max_width = 1920;
dev->mode_config.max_height = 2048;
dev->mode_config.preferred_depth = 24;
dev->mode_config.funcs = &armada_drm_mode_config_funcs;
drm_mm_init(&priv->linear, mem->start, resource_size(mem));
/* Create all LCD controllers */
for (n = 0; n < ARRAY_SIZE(priv->dcrtc); n++) {
if (!res[n])
break;
ret = armada_drm_crtc_create(dev, n, res[n]);
if (ret)
goto err_kms;
}
#ifdef CONFIG_DRM_ARMADA_TDA1998X
ret = armada_drm_connector_slave_create(dev, &tda19988_config);
if (ret)
goto err_kms;
#endif
ret = drm_vblank_init(dev, n);
if (ret)
goto err_kms;
ret = drm_irq_install(dev);
if (ret)
goto err_kms;
dev->vblank_disable_allowed = 1;
ret = armada_fbdev_init(dev);
if (ret)
goto err_irq;
drm_kms_helper_poll_init(dev);
return 0;
err_irq:
drm_irq_uninstall(dev);
err_kms:
drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear);
flush_work(&priv->fb_unref_work);
return ret;
}
static int armada_drm_unload(struct drm_device *dev)
{
struct armada_private *priv = dev->dev_private;
drm_kms_helper_poll_fini(dev);
armada_fbdev_fini(dev);
drm_irq_uninstall(dev);
drm_mode_config_cleanup(dev);
drm_mm_takedown(&priv->linear);
flush_work(&priv->fb_unref_work);
dev->dev_private = NULL;
return 0;
}
void armada_drm_vbl_event_add(struct armada_crtc *dcrtc,
struct armada_vbl_event *evt)
{
unsigned long flags;
spin_lock_irqsave(&dcrtc->irq_lock, flags);
if (list_empty(&evt->node)) {
list_add_tail(&evt->node, &dcrtc->vbl_list);
drm_vblank_get(dcrtc->crtc.dev, dcrtc->num);
}
spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
}
void armada_drm_vbl_event_remove(struct armada_crtc *dcrtc,
struct armada_vbl_event *evt)
{
if (!list_empty(&evt->node)) {
list_del_init(&evt->node);
drm_vblank_put(dcrtc->crtc.dev, dcrtc->num);
}
}
void armada_drm_vbl_event_remove_unlocked(struct armada_crtc *dcrtc,
struct armada_vbl_event *evt)
{
unsigned long flags;
spin_lock_irqsave(&dcrtc->irq_lock, flags);
armada_drm_vbl_event_remove(dcrtc, evt);
spin_unlock_irqrestore(&dcrtc->irq_lock, flags);
}
/* These are called under the vbl_lock. */
static int armada_drm_enable_vblank(struct drm_device *dev, int crtc)
{
struct armada_private *priv = dev->dev_private;
armada_drm_crtc_enable_irq(priv->dcrtc[crtc], VSYNC_IRQ_ENA);
return 0;
}
static void armada_drm_disable_vblank(struct drm_device *dev, int crtc)
{
struct armada_private *priv = dev->dev_private;
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),
DRM_IOCTL_DEF_DRV(ARMADA_GEM_MMAP, armada_gem_mmap_ioctl,
DRM_UNLOCKED),
DRM_IOCTL_DEF_DRV(ARMADA_GEM_PWRITE, armada_gem_pwrite_ioctl,
DRM_UNLOCKED),
};
static const struct file_operations armada_drm_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = drm_read,
.poll = drm_poll,
.unlocked_ioctl = drm_ioctl,
.mmap = drm_gem_mmap,
.open = drm_open,
.release = drm_release,
};
static struct drm_driver armada_drm_driver = {
.load = armada_drm_load,
.open = NULL,
.preclose = NULL,
.postclose = NULL,
.lastclose = NULL,
.unload = armada_drm_unload,
.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,
#endif
.gem_free_object = armada_gem_free_object,
.prime_handle_to_fd = drm_gem_prime_handle_to_fd,
.prime_fd_to_handle = drm_gem_prime_fd_to_handle,
.gem_prime_export = armada_gem_prime_export,
.gem_prime_import = armada_gem_prime_import,
.dumb_create = armada_gem_dumb_create,
.dumb_map_offset = armada_gem_dumb_map_offset,
.dumb_destroy = armada_gem_dumb_destroy,
.gem_vm_ops = &armada_gem_vm_ops,
.major = 1,
.minor = 0,
.name = "armada-drm",
.desc = "Armada SoC DRM",
.date = "20120730",
.driver_features = DRIVER_GEM | DRIVER_MODESET |
DRIVER_HAVE_IRQ | DRIVER_PRIME,
.ioctls = armada_ioctls,
.fops = &armada_drm_fops,
};
static int armada_drm_probe(struct platform_device *pdev)
{
return drm_platform_init(&armada_drm_driver, pdev);
}
static int armada_drm_remove(struct platform_device *pdev)
{
drm_platform_exit(&armada_drm_driver, pdev);
return 0;
}
static const struct platform_device_id armada_drm_platform_ids[] = {
{
.name = "armada-drm",
.driver_data = (unsigned long)&armada510_ops,
}, {
.name = "armada-510-drm",
.driver_data = (unsigned long)&armada510_ops,
},
{ },
};
MODULE_DEVICE_TABLE(platform, armada_drm_platform_ids);
static struct platform_driver armada_drm_platform_driver = {
.probe = armada_drm_probe,
.remove = armada_drm_remove,
.driver = {
.name = "armada-drm",
.owner = THIS_MODULE,
},
.id_table = armada_drm_platform_ids,
};
static int __init armada_drm_init(void)
{
armada_drm_driver.num_ioctls = DRM_ARRAY_SIZE(armada_ioctls);
return platform_driver_register(&armada_drm_platform_driver);
}
module_init(armada_drm_init);
static void __exit armada_drm_exit(void)
{
platform_driver_unregister(&armada_drm_platform_driver);
}
module_exit(armada_drm_exit);
MODULE_AUTHOR("Russell King <rmk+kernel@arm.linux.org.uk>");
MODULE_DESCRIPTION("Armada DRM Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:armada-drm");
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_fb_helper.h>
#include "armada_drm.h"
#include "armada_fb.h"
#include "armada_gem.h"
#include "armada_hw.h"
static void armada_fb_destroy(struct drm_framebuffer *fb)
{
struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
drm_framebuffer_cleanup(&dfb->fb);
drm_gem_object_unreference_unlocked(&dfb->obj->obj);
kfree(dfb);
}
static int armada_fb_create_handle(struct drm_framebuffer *fb,
struct drm_file *dfile, unsigned int *handle)
{
struct armada_framebuffer *dfb = drm_fb_to_armada_fb(fb);
return drm_gem_handle_create(dfile, &dfb->obj->obj, handle);
}
static const struct drm_framebuffer_funcs armada_fb_funcs = {
.destroy = armada_fb_destroy,
.create_handle = armada_fb_create_handle,
};
struct armada_framebuffer *armada_framebuffer_create(struct drm_device *dev,
struct drm_mode_fb_cmd2 *mode, struct armada_gem_object *obj)
{
struct armada_framebuffer *dfb;
uint8_t format, config;
int ret;
switch (mode->pixel_format) {
#define FMT(drm, fmt, mod) \
case DRM_FORMAT_##drm: \
format = CFG_##fmt; \
config = mod; \
break
FMT(RGB565, 565, CFG_SWAPRB);
FMT(BGR565, 565, 0);
FMT(ARGB1555, 1555, CFG_SWAPRB);
FMT(ABGR1555, 1555, 0);
FMT(RGB888, 888PACK, CFG_SWAPRB);
FMT(BGR888, 888PACK, 0);
FMT(XRGB8888, X888, CFG_SWAPRB);
FMT(XBGR8888, X888, 0);
FMT(ARGB8888, 8888, CFG_SWAPRB);
FMT(ABGR8888, 8888, 0);
FMT(YUYV, 422PACK, CFG_YUV2RGB | CFG_SWAPYU | CFG_SWAPUV);
FMT(UYVY, 422PACK, CFG_YUV2RGB);
FMT(VYUY, 422PACK, CFG_YUV2RGB | CFG_SWAPUV);
FMT(YVYU, 422PACK, CFG_YUV2RGB | CFG_SWAPYU);
FMT(YUV422, 422, CFG_YUV2RGB);
FMT(YVU422, 422, CFG_YUV2RGB | CFG_SWAPUV);
FMT(YUV420, 420, CFG_YUV2RGB);
FMT(YVU420, 420, CFG_YUV2RGB | CFG_SWAPUV);
FMT(C8, PSEUDO8, 0);
#undef FMT
default:
return ERR_PTR(-EINVAL);
}
dfb = kzalloc(sizeof(*dfb), GFP_KERNEL);
if (!dfb) {
DRM_ERROR("failed to allocate Armada fb object\n");
return ERR_PTR(-ENOMEM);
}
dfb->fmt = format;
dfb->mod = config;
dfb->obj = obj;
drm_helper_mode_fill_fb_struct(&dfb->fb, mode);
ret = drm_framebuffer_init(dev, &dfb->fb, &armada_fb_funcs);
if (ret) {
kfree(dfb);
return ERR_PTR(ret);
}
/*
* Take a reference on our object as we're successful - the
* caller already holds a reference, which keeps us safe for
* the above call, but the caller will drop their reference
* to it. Hence we need to take our own reference.
*/
drm_gem_object_reference(&obj->obj);
return dfb;
}
static struct drm_framebuffer *armada_fb_create(struct drm_device *dev,
struct drm_file *dfile, struct drm_mode_fb_cmd2 *mode)
{
struct armada_gem_object *obj;
struct armada_framebuffer *dfb;
int ret;
DRM_DEBUG_DRIVER("w%u h%u pf%08x f%u p%u,%u,%u\n",
mode->width, mode->height, mode->pixel_format,
mode->flags, mode->pitches[0], mode->pitches[1],
mode->pitches[2]);
/* We can only handle a single plane at the moment */
if (drm_format_num_planes(mode->pixel_format) > 1 &&
(mode->handles[0] != mode->handles[1] ||
mode->handles[0] != mode->handles[2])) {
ret = -EINVAL;
goto err;
}
obj = armada_gem_object_lookup(dev, dfile, mode->handles[0]);
if (!obj) {
ret = -ENOENT;
goto err;
}
if (obj->obj.import_attach && !obj->sgt) {
ret = armada_gem_map_import(obj);
if (ret)
goto err_unref;
}
/* Framebuffer objects must have a valid device address for scanout */
if (obj->dev_addr == DMA_ERROR_CODE) {
ret = -EINVAL;
goto err_unref;
}
dfb = armada_framebuffer_create(dev, mode, obj);
if (IS_ERR(dfb)) {
ret = PTR_ERR(dfb);
goto err;
}
drm_gem_object_unreference_unlocked(&obj->obj);
return &dfb->fb;
err_unref:
drm_gem_object_unreference_unlocked(&obj->obj);
err:
DRM_ERROR("failed to initialize framebuffer: %d\n", ret);
return ERR_PTR(ret);
}
static void armada_output_poll_changed(struct drm_device *dev)
{
struct armada_private *priv = dev->dev_private;
struct drm_fb_helper *fbh = priv->fbdev;
if (fbh)
drm_fb_helper_hotplug_event(fbh);
}
const struct drm_mode_config_funcs armada_drm_mode_config_funcs = {
.fb_create = armada_fb_create,
.output_poll_changed = armada_output_poll_changed,
};
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_FB_H
#define ARMADA_FB_H
struct armada_framebuffer {
struct drm_framebuffer fb;
struct armada_gem_object *obj;
uint8_t fmt;
uint8_t mod;
};
#define drm_fb_to_armada_fb(dfb) \
container_of(dfb, struct armada_framebuffer, fb)
#define drm_fb_obj(fb) drm_fb_to_armada_fb(fb)->obj
struct armada_framebuffer *armada_framebuffer_create(struct drm_device *,
struct drm_mode_fb_cmd2 *, struct armada_gem_object *);
#endif
/*
* Copyright (C) 2012 Russell King
* Written from the i915 driver.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/errno.h>
#include <linux/fb.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_fb_helper.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_fb.h"
#include "armada_gem.h"
static /*const*/ struct fb_ops armada_fb_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_blank = drm_fb_helper_blank,
.fb_setcmap = drm_fb_helper_setcmap,
.fb_debug_enter = drm_fb_helper_debug_enter,
.fb_debug_leave = drm_fb_helper_debug_leave,
};
static int armada_fb_create(struct drm_fb_helper *fbh,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_device *dev = fbh->dev;
struct drm_mode_fb_cmd2 mode;
struct armada_framebuffer *dfb;
struct armada_gem_object *obj;
struct fb_info *info;
int size, ret;
void *ptr;
memset(&mode, 0, sizeof(mode));
mode.width = sizes->surface_width;
mode.height = sizes->surface_height;
mode.pitches[0] = armada_pitch(mode.width, sizes->surface_bpp);
mode.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
sizes->surface_depth);
size = mode.pitches[0] * mode.height;
obj = armada_gem_alloc_private_object(dev, size);
if (!obj) {
DRM_ERROR("failed to allocate fb memory\n");
return -ENOMEM;
}
ret = armada_gem_linear_back(dev, obj);
if (ret) {
drm_gem_object_unreference_unlocked(&obj->obj);
return ret;
}
ptr = armada_gem_map_object(dev, obj);
if (!ptr) {
drm_gem_object_unreference_unlocked(&obj->obj);
return -ENOMEM;
}
dfb = armada_framebuffer_create(dev, &mode, obj);
/*
* A reference is now held by the framebuffer object if
* successful, otherwise this drops the ref for the error path.
*/
drm_gem_object_unreference_unlocked(&obj->obj);
if (IS_ERR(dfb))
return PTR_ERR(dfb);
info = framebuffer_alloc(0, dev->dev);
if (!info) {
ret = -ENOMEM;
goto err_fballoc;
}
ret = fb_alloc_cmap(&info->cmap, 256, 0);
if (ret) {
ret = -ENOMEM;
goto err_fbcmap;
}
strlcpy(info->fix.id, "armada-drmfb", sizeof(info->fix.id));
info->par = fbh;
info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
info->fbops = &armada_fb_ops;
info->fix.smem_start = obj->phys_addr;
info->fix.smem_len = obj->obj.size;
info->screen_size = obj->obj.size;
info->screen_base = ptr;
fbh->fb = &dfb->fb;
fbh->fbdev = info;
drm_fb_helper_fill_fix(info, dfb->fb.pitches[0], dfb->fb.depth);
drm_fb_helper_fill_var(info, fbh, sizes->fb_width, sizes->fb_height);
DRM_DEBUG_KMS("allocated %dx%d %dbpp fb: 0x%08x\n",
dfb->fb.width, dfb->fb.height,
dfb->fb.bits_per_pixel, obj->phys_addr);
return 0;
err_fbcmap:
framebuffer_release(info);
err_fballoc:
dfb->fb.funcs->destroy(&dfb->fb);
return ret;
}
static int armada_fb_probe(struct drm_fb_helper *fbh,
struct drm_fb_helper_surface_size *sizes)
{
int ret = 0;
if (!fbh->fb) {
ret = armada_fb_create(fbh, sizes);
if (ret == 0)
ret = 1;
}
return ret;
}
static 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,
};
int armada_fbdev_init(struct drm_device *dev)
{
struct armada_private *priv = dev->dev_private;
struct drm_fb_helper *fbh;
int ret;
fbh = devm_kzalloc(dev->dev, sizeof(*fbh), GFP_KERNEL);
if (!fbh)
return -ENOMEM;
priv->fbdev = fbh;
fbh->funcs = &armada_fb_helper_funcs;
ret = drm_fb_helper_init(dev, fbh, 1, 1);
if (ret) {
DRM_ERROR("failed to initialize drm fb helper\n");
goto err_fb_helper;
}
ret = drm_fb_helper_single_add_all_connectors(fbh);
if (ret) {
DRM_ERROR("failed to add fb connectors\n");
goto err_fb_setup;
}
ret = drm_fb_helper_initial_config(fbh, 32);
if (ret) {
DRM_ERROR("failed to set initial config\n");
goto err_fb_setup;
}
return 0;
err_fb_setup:
drm_fb_helper_fini(fbh);
err_fb_helper:
priv->fbdev = NULL;
return ret;
}
void armada_fbdev_fini(struct drm_device *dev)
{
struct armada_private *priv = dev->dev_private;
struct drm_fb_helper *fbh = priv->fbdev;
if (fbh) {
struct fb_info *info = fbh->fbdev;
if (info) {
unregister_framebuffer(info);
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
if (fbh->fb)
fbh->fb->funcs->destroy(fbh->fb);
drm_fb_helper_fini(fbh);
priv->fbdev = NULL;
}
}
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/dma-buf.h>
#include <linux/dma-mapping.h>
#include <linux/shmem_fs.h>
#include <drm/drmP.h>
#include "armada_drm.h"
#include "armada_gem.h"
#include <drm/armada_drm.h>
#include "armada_ioctlP.h"
static int armada_gem_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct armada_gem_object *obj = drm_to_armada_gem(vma->vm_private_data);
unsigned long addr = (unsigned long)vmf->virtual_address;
unsigned long pfn = obj->phys_addr >> PAGE_SHIFT;
int ret;
pfn += (addr - vma->vm_start) >> PAGE_SHIFT;
ret = vm_insert_pfn(vma, addr, pfn);
switch (ret) {
case 0:
case -EBUSY:
return VM_FAULT_NOPAGE;
case -ENOMEM:
return VM_FAULT_OOM;
default:
return VM_FAULT_SIGBUS;
}
}
const struct vm_operations_struct armada_gem_vm_ops = {
.fault = armada_gem_vm_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
static size_t roundup_gem_size(size_t size)
{
return roundup(size, PAGE_SIZE);
}
/* dev->struct_mutex is held here */
void armada_gem_free_object(struct drm_gem_object *obj)
{
struct armada_gem_object *dobj = drm_to_armada_gem(obj);
DRM_DEBUG_DRIVER("release obj %p\n", dobj);
drm_gem_free_mmap_offset(&dobj->obj);
if (dobj->page) {
/* page backed memory */
unsigned int order = get_order(dobj->obj.size);
__free_pages(dobj->page, order);
} else if (dobj->linear) {
/* linear backed memory */
drm_mm_remove_node(dobj->linear);
kfree(dobj->linear);
if (dobj->addr)
iounmap(dobj->addr);
}
if (dobj->obj.import_attach) {
/* We only ever display imported data */
dma_buf_unmap_attachment(dobj->obj.import_attach, dobj->sgt,
DMA_TO_DEVICE);
drm_prime_gem_destroy(&dobj->obj, NULL);
}
drm_gem_object_release(&dobj->obj);
kfree(dobj);
}
int
armada_gem_linear_back(struct drm_device *dev, struct armada_gem_object *obj)
{
struct armada_private *priv = dev->dev_private;
size_t size = obj->obj.size;
if (obj->page || obj->linear)
return 0;
/*
* If it is a small allocation (typically cursor, which will
* be 32x64 or 64x32 ARGB pixels) try to get it from the system.
* Framebuffers will never be this small (our minimum size for
* framebuffers is larger than this anyway.) Such objects are
* only accessed by the CPU so we don't need any special handing
* here.
*/
if (size <= 8192) {
unsigned int order = get_order(size);
struct page *p = alloc_pages(GFP_KERNEL, order);
if (p) {
obj->addr = page_address(p);
obj->phys_addr = page_to_phys(p);
obj->page = p;
memset(obj->addr, 0, PAGE_ALIGN(size));
}
}
/*
* We could grab something from CMA if it's enabled, but that
* involves building in a problem:
*
* CMA's interface uses dma_alloc_coherent(), which provides us
* with an CPU virtual address and a device address.
*
* The CPU virtual address may be either an address in the kernel
* direct mapped region (for example, as it would be on x86) or
* it may be remapped into another part of kernel memory space
* (eg, as it would be on ARM.) This means virt_to_phys() on the
* returned virtual address is invalid depending on the architecture
* implementation.
*
* The device address may also not be a physical address; it may
* be that there is some kind of remapping between the device and
* system RAM, which makes the use of the device address also
* unsafe to re-use as a physical address.
*
* This makes DRM usage of dma_alloc_coherent() in a generic way
* at best very questionable and unsafe.
*/
/* Otherwise, grab it from our linear allocation */
if (!obj->page) {
struct drm_mm_node *node;
unsigned align = min_t(unsigned, size, SZ_2M);
void __iomem *ptr;
int ret;
node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOSPC;
mutex_lock(&dev->struct_mutex);
ret = drm_mm_insert_node(&priv->linear, node, size, align,
DRM_MM_SEARCH_DEFAULT);
mutex_unlock(&dev->struct_mutex);
if (ret) {
kfree(node);
return ret;
}
obj->linear = node;
/* Ensure that the memory we're returning is cleared. */
ptr = ioremap_wc(obj->linear->start, size);
if (!ptr) {
mutex_lock(&dev->struct_mutex);
drm_mm_remove_node(obj->linear);
mutex_unlock(&dev->struct_mutex);
kfree(obj->linear);
obj->linear = NULL;
return -ENOMEM;
}
memset_io(ptr, 0, size);
iounmap(ptr);
obj->phys_addr = obj->linear->start;
obj->dev_addr = obj->linear->start;
}
DRM_DEBUG_DRIVER("obj %p phys %#x dev %#x\n",
obj, obj->phys_addr, obj->dev_addr);
return 0;
}
void *
armada_gem_map_object(struct drm_device *dev, struct armada_gem_object *dobj)
{
/* only linear objects need to be ioremap'd */
if (!dobj->addr && dobj->linear)
dobj->addr = ioremap_wc(dobj->phys_addr, dobj->obj.size);
return dobj->addr;
}
struct armada_gem_object *
armada_gem_alloc_private_object(struct drm_device *dev, size_t size)
{
struct armada_gem_object *obj;
size = roundup_gem_size(size);
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj)
return NULL;
drm_gem_private_object_init(dev, &obj->obj, size);
obj->dev_addr = DMA_ERROR_CODE;
DRM_DEBUG_DRIVER("alloc private obj %p size %zu\n", obj, size);
return obj;
}
struct armada_gem_object *armada_gem_alloc_object(struct drm_device *dev,
size_t size)
{
struct armada_gem_object *obj;
struct address_space *mapping;
size = roundup_gem_size(size);
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
if (!obj)
return NULL;
if (drm_gem_object_init(dev, &obj->obj, size)) {
kfree(obj);
return NULL;
}
obj->dev_addr = DMA_ERROR_CODE;
mapping = obj->obj.filp->f_path.dentry->d_inode->i_mapping;
mapping_set_gfp_mask(mapping, GFP_HIGHUSER | __GFP_RECLAIMABLE);
DRM_DEBUG_DRIVER("alloc obj %p size %zu\n", obj, size);
return obj;
}
/* Dumb alloc support */
int armada_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
struct drm_mode_create_dumb *args)
{
struct armada_gem_object *dobj;
u32 handle;
size_t size;
int ret;
args->pitch = armada_pitch(args->width, args->bpp);
args->size = size = args->pitch * args->height;
dobj = armada_gem_alloc_private_object(dev, size);
if (dobj == NULL)
return -ENOMEM;
ret = armada_gem_linear_back(dev, dobj);
if (ret)
goto err;
ret = drm_gem_handle_create(file, &dobj->obj, &handle);
if (ret)
goto err;
args->handle = handle;
/* drop reference from allocate - handle holds it now */
DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
err:
drm_gem_object_unreference_unlocked(&dobj->obj);
return ret;
}
int armada_gem_dumb_map_offset(struct drm_file *file, struct drm_device *dev,
uint32_t handle, uint64_t *offset)
{
struct armada_gem_object *obj;
int ret = 0;
mutex_lock(&dev->struct_mutex);
obj = armada_gem_object_lookup(dev, file, handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object\n");
ret = -EINVAL;
goto err_unlock;
}
/* Don't allow imported objects to be mapped */
if (obj->obj.import_attach) {
ret = -EINVAL;
goto err_unlock;
}
ret = drm_gem_create_mmap_offset(&obj->obj);
if (ret == 0) {
*offset = drm_vma_node_offset_addr(&obj->obj.vma_node);
DRM_DEBUG_DRIVER("handle %#x offset %llx\n", handle, *offset);
}
drm_gem_object_unreference(&obj->obj);
err_unlock:
mutex_unlock(&dev->struct_mutex);
return ret;
}
int armada_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
uint32_t handle)
{
return drm_gem_handle_delete(file, handle);
}
/* Private driver gem ioctls */
int armada_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_armada_gem_create *args = data;
struct armada_gem_object *dobj;
size_t size;
u32 handle;
int ret;
if (args->size == 0)
return -ENOMEM;
size = args->size;
dobj = armada_gem_alloc_object(dev, size);
if (dobj == NULL)
return -ENOMEM;
ret = drm_gem_handle_create(file, &dobj->obj, &handle);
if (ret)
goto err;
args->handle = handle;
/* drop reference from allocate - handle holds it now */
DRM_DEBUG_DRIVER("obj %p size %zu handle %#x\n", dobj, size, handle);
err:
drm_gem_object_unreference_unlocked(&dobj->obj);
return ret;
}
/* Map a shmem-backed object into process memory space */
int armada_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_armada_gem_mmap *args = data;
struct armada_gem_object *dobj;
unsigned long addr;
dobj = armada_gem_object_lookup(dev, file, args->handle);
if (dobj == NULL)
return -ENOENT;
if (!dobj->obj.filp) {
drm_gem_object_unreference(&dobj->obj);
return -EINVAL;
}
addr = vm_mmap(dobj->obj.filp, 0, args->size, PROT_READ | PROT_WRITE,
MAP_SHARED, args->offset);
drm_gem_object_unreference(&dobj->obj);
if (IS_ERR_VALUE(addr))
return addr;
args->addr = addr;
return 0;
}
int armada_gem_pwrite_ioctl(struct drm_device *dev, void *data,
struct drm_file *file)
{
struct drm_armada_gem_pwrite *args = data;
struct armada_gem_object *dobj;
char __user *ptr;
int ret;
DRM_DEBUG_DRIVER("handle %u off %u size %u ptr 0x%llx\n",
args->handle, args->offset, args->size, args->ptr);
if (args->size == 0)
return 0;
ptr = (char __user *)(uintptr_t)args->ptr;
if (!access_ok(VERIFY_READ, ptr, args->size))
return -EFAULT;
ret = fault_in_multipages_readable(ptr, args->size);
if (ret)
return ret;
dobj = armada_gem_object_lookup(dev, file, args->handle);
if (dobj == NULL)
return -ENOENT;
/* Must be a kernel-mapped object */
if (!dobj->addr)
return -EINVAL;
if (args->offset > dobj->obj.size ||
args->size > dobj->obj.size - args->offset) {
DRM_ERROR("invalid size: object size %u\n", dobj->obj.size);
ret = -EINVAL;
goto unref;
}
if (copy_from_user(dobj->addr + args->offset, ptr, args->size)) {
ret = -EFAULT;
} else if (dobj->update) {
dobj->update(dobj->update_data);
ret = 0;
}
unref:
drm_gem_object_unreference_unlocked(&dobj->obj);
return ret;
}
/* Prime support */
struct sg_table *
armada_gem_prime_map_dma_buf(struct dma_buf_attachment *attach,
enum dma_data_direction dir)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct armada_gem_object *dobj = drm_to_armada_gem(obj);
struct scatterlist *sg;
struct sg_table *sgt;
int i, num;
sgt = kmalloc(sizeof(*sgt), GFP_KERNEL);
if (!sgt)
return NULL;
if (dobj->obj.filp) {
struct address_space *mapping;
gfp_t gfp;
int count;
count = dobj->obj.size / PAGE_SIZE;
if (sg_alloc_table(sgt, count, GFP_KERNEL))
goto free_sgt;
mapping = file_inode(dobj->obj.filp)->i_mapping;
gfp = mapping_gfp_mask(mapping);
for_each_sg(sgt->sgl, sg, count, i) {
struct page *page;
page = shmem_read_mapping_page_gfp(mapping, i, gfp);
if (IS_ERR(page)) {
num = i;
goto release;
}
sg_set_page(sg, page, PAGE_SIZE, 0);
}
if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0) {
num = sgt->nents;
goto release;
}
} else if (dobj->page) {
/* Single contiguous page */
if (sg_alloc_table(sgt, 1, GFP_KERNEL))
goto free_sgt;
sg_set_page(sgt->sgl, dobj->page, dobj->obj.size, 0);
if (dma_map_sg(attach->dev, sgt->sgl, sgt->nents, dir) == 0)
goto free_table;
} else if (dobj->linear) {
/* Single contiguous physical region - no struct page */
if (sg_alloc_table(sgt, 1, GFP_KERNEL))
goto free_sgt;
sg_dma_address(sgt->sgl) = dobj->dev_addr;
sg_dma_len(sgt->sgl) = dobj->obj.size;
} else {
goto free_sgt;
}
return sgt;
release:
for_each_sg(sgt->sgl, sg, num, i)
page_cache_release(sg_page(sg));
free_table:
sg_free_table(sgt);
free_sgt:
kfree(sgt);
return NULL;
}
static void armada_gem_prime_unmap_dma_buf(struct dma_buf_attachment *attach,
struct sg_table *sgt, enum dma_data_direction dir)
{
struct drm_gem_object *obj = attach->dmabuf->priv;
struct armada_gem_object *dobj = drm_to_armada_gem(obj);
int i;
if (!dobj->linear)
dma_unmap_sg(attach->dev, sgt->sgl, sgt->nents, dir);
if (dobj->obj.filp) {
struct scatterlist *sg;
for_each_sg(sgt->sgl, sg, sgt->nents, i)
page_cache_release(sg_page(sg));
}
sg_free_table(sgt);
kfree(sgt);
}
static void *armada_gem_dmabuf_no_kmap(struct dma_buf *buf, unsigned long n)
{
return NULL;
}
static void
armada_gem_dmabuf_no_kunmap(struct dma_buf *buf, unsigned long n, void *addr)
{
}
static int
armada_gem_dmabuf_mmap(struct dma_buf *buf, struct vm_area_struct *vma)
{
return -EINVAL;
}
static const struct dma_buf_ops armada_gem_prime_dmabuf_ops = {
.map_dma_buf = armada_gem_prime_map_dma_buf,
.unmap_dma_buf = armada_gem_prime_unmap_dma_buf,
.release = drm_gem_dmabuf_release,
.kmap_atomic = armada_gem_dmabuf_no_kmap,
.kunmap_atomic = armada_gem_dmabuf_no_kunmap,
.kmap = armada_gem_dmabuf_no_kmap,
.kunmap = armada_gem_dmabuf_no_kunmap,
.mmap = armada_gem_dmabuf_mmap,
};
struct dma_buf *
armada_gem_prime_export(struct drm_device *dev, struct drm_gem_object *obj,
int flags)
{
return dma_buf_export(obj, &armada_gem_prime_dmabuf_ops, obj->size,
O_RDWR);
}
struct drm_gem_object *
armada_gem_prime_import(struct drm_device *dev, struct dma_buf *buf)
{
struct dma_buf_attachment *attach;
struct armada_gem_object *dobj;
if (buf->ops == &armada_gem_prime_dmabuf_ops) {
struct drm_gem_object *obj = buf->priv;
if (obj->dev == dev) {
/*
* Importing our own dmabuf(s) increases the
* refcount on the gem object itself.
*/
drm_gem_object_reference(obj);
dma_buf_put(buf);
return obj;
}
}
attach = dma_buf_attach(buf, dev->dev);
if (IS_ERR(attach))
return ERR_CAST(attach);
dobj = armada_gem_alloc_private_object(dev, buf->size);
if (!dobj) {
dma_buf_detach(buf, attach);
return ERR_PTR(-ENOMEM);
}
dobj->obj.import_attach = attach;
/*
* Don't call dma_buf_map_attachment() here - it maps the
* scatterlist immediately for DMA, and this is not always
* an appropriate thing to do.
*/
return &dobj->obj;
}
int armada_gem_map_import(struct armada_gem_object *dobj)
{
int ret;
dobj->sgt = dma_buf_map_attachment(dobj->obj.import_attach,
DMA_TO_DEVICE);
if (!dobj->sgt) {
DRM_ERROR("dma_buf_map_attachment() returned NULL\n");
return -EINVAL;
}
if (IS_ERR(dobj->sgt)) {
ret = PTR_ERR(dobj->sgt);
dobj->sgt = NULL;
DRM_ERROR("dma_buf_map_attachment() error: %d\n", ret);
return ret;
}
if (dobj->sgt->nents > 1) {
DRM_ERROR("dma_buf_map_attachment() returned an (unsupported) scattered list\n");
return -EINVAL;
}
if (sg_dma_len(dobj->sgt->sgl) < dobj->obj.size) {
DRM_ERROR("dma_buf_map_attachment() returned a small buffer\n");
return -EINVAL;
}
dobj->dev_addr = sg_dma_address(dobj->sgt->sgl);
return 0;
}
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_GEM_H
#define ARMADA_GEM_H
/* GEM */
struct armada_gem_object {
struct drm_gem_object obj;
void *addr;
phys_addr_t phys_addr;
resource_size_t dev_addr;
struct drm_mm_node *linear; /* for linear backed */
struct page *page; /* for page backed */
struct sg_table *sgt; /* for imported */
void (*update)(void *);
void *update_data;
};
extern const struct vm_operations_struct armada_gem_vm_ops;
#define drm_to_armada_gem(o) container_of(o, struct armada_gem_object, obj)
void armada_gem_free_object(struct drm_gem_object *);
int armada_gem_linear_back(struct drm_device *, struct armada_gem_object *);
void *armada_gem_map_object(struct drm_device *, struct armada_gem_object *);
struct armada_gem_object *armada_gem_alloc_private_object(struct drm_device *,
size_t);
int armada_gem_dumb_create(struct drm_file *, struct drm_device *,
struct drm_mode_create_dumb *);
int armada_gem_dumb_map_offset(struct drm_file *, struct drm_device *,
uint32_t, uint64_t *);
int armada_gem_dumb_destroy(struct drm_file *, struct drm_device *,
uint32_t);
struct dma_buf *armada_gem_prime_export(struct drm_device *dev,
struct drm_gem_object *obj, int flags);
struct drm_gem_object *armada_gem_prime_import(struct drm_device *,
struct dma_buf *);
int armada_gem_map_import(struct armada_gem_object *);
static inline struct armada_gem_object *armada_gem_object_lookup(
struct drm_device *dev, struct drm_file *dfile, unsigned handle)
{
struct drm_gem_object *obj = drm_gem_object_lookup(dev, dfile, handle);
return obj ? drm_to_armada_gem(obj) : NULL;
}
#endif
/*
* Copyright (C) 2012 Russell King
* Rewritten from the dovefb driver, and Armada510 manuals.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_HW_H
#define ARMADA_HW_H
/*
* Note: the following registers are written from IRQ context:
* LCD_SPU_V_PORCH, LCD_SPU_ADV_REG, LCD_SPUT_V_H_TOTAL
* LCD_SPU_DMA_START_ADDR_[YUV][01], LCD_SPU_DMA_PITCH_YC,
* LCD_SPU_DMA_PITCH_UV, LCD_SPU_DMA_OVSA_HPXL_VLN,
* LCD_SPU_DMA_HPXL_VLN, LCD_SPU_DZM_HPXL_VLN, LCD_SPU_DMA_CTRL0
*/
enum {
LCD_SPU_ADV_REG = 0x0084, /* Armada 510 */
LCD_SPU_DMA_START_ADDR_Y0 = 0x00c0,
LCD_SPU_DMA_START_ADDR_U0 = 0x00c4,
LCD_SPU_DMA_START_ADDR_V0 = 0x00c8,
LCD_CFG_DMA_START_ADDR_0 = 0x00cc,
LCD_SPU_DMA_START_ADDR_Y1 = 0x00d0,
LCD_SPU_DMA_START_ADDR_U1 = 0x00d4,
LCD_SPU_DMA_START_ADDR_V1 = 0x00d8,
LCD_CFG_DMA_START_ADDR_1 = 0x00dc,
LCD_SPU_DMA_PITCH_YC = 0x00e0,
LCD_SPU_DMA_PITCH_UV = 0x00e4,
LCD_SPU_DMA_OVSA_HPXL_VLN = 0x00e8,
LCD_SPU_DMA_HPXL_VLN = 0x00ec,
LCD_SPU_DZM_HPXL_VLN = 0x00f0,
LCD_CFG_GRA_START_ADDR0 = 0x00f4,
LCD_CFG_GRA_START_ADDR1 = 0x00f8,
LCD_CFG_GRA_PITCH = 0x00fc,
LCD_SPU_GRA_OVSA_HPXL_VLN = 0x0100,
LCD_SPU_GRA_HPXL_VLN = 0x0104,
LCD_SPU_GZM_HPXL_VLN = 0x0108,
LCD_SPU_HWC_OVSA_HPXL_VLN = 0x010c,
LCD_SPU_HWC_HPXL_VLN = 0x0110,
LCD_SPUT_V_H_TOTAL = 0x0114,
LCD_SPU_V_H_ACTIVE = 0x0118,
LCD_SPU_H_PORCH = 0x011c,
LCD_SPU_V_PORCH = 0x0120,
LCD_SPU_BLANKCOLOR = 0x0124,
LCD_SPU_ALPHA_COLOR1 = 0x0128,
LCD_SPU_ALPHA_COLOR2 = 0x012c,
LCD_SPU_COLORKEY_Y = 0x0130,
LCD_SPU_COLORKEY_U = 0x0134,
LCD_SPU_COLORKEY_V = 0x0138,
LCD_CFG_RDREG4F = 0x013c, /* Armada 510 */
LCD_SPU_SPI_RXDATA = 0x0140,
LCD_SPU_ISA_RXDATA = 0x0144,
LCD_SPU_HWC_RDDAT = 0x0158,
LCD_SPU_GAMMA_RDDAT = 0x015c,
LCD_SPU_PALETTE_RDDAT = 0x0160,
LCD_SPU_IOPAD_IN = 0x0178,
LCD_CFG_RDREG5F = 0x017c,
LCD_SPU_SPI_CTRL = 0x0180,
LCD_SPU_SPI_TXDATA = 0x0184,
LCD_SPU_SMPN_CTRL = 0x0188,
LCD_SPU_DMA_CTRL0 = 0x0190,
LCD_SPU_DMA_CTRL1 = 0x0194,
LCD_SPU_SRAM_CTRL = 0x0198,
LCD_SPU_SRAM_WRDAT = 0x019c,
LCD_SPU_SRAM_PARA0 = 0x01a0, /* Armada 510 */
LCD_SPU_SRAM_PARA1 = 0x01a4,
LCD_CFG_SCLK_DIV = 0x01a8,
LCD_SPU_CONTRAST = 0x01ac,
LCD_SPU_SATURATION = 0x01b0,
LCD_SPU_CBSH_HUE = 0x01b4,
LCD_SPU_DUMB_CTRL = 0x01b8,
LCD_SPU_IOPAD_CONTROL = 0x01bc,
LCD_SPU_IRQ_ENA = 0x01c0,
LCD_SPU_IRQ_ISR = 0x01c4,
};
/* For LCD_SPU_ADV_REG */
enum {
ADV_VSYNC_L_OFF = 0xfff << 20,
ADV_GRACOLORKEY = 1 << 19,
ADV_VIDCOLORKEY = 1 << 18,
ADV_HWC32BLEND = 1 << 15,
ADV_HWC32ARGB = 1 << 14,
ADV_HWC32ENABLE = 1 << 13,
ADV_VSYNCOFFEN = 1 << 12,
ADV_VSYNC_H_OFF = 0xfff << 0,
};
enum {
CFG_565 = 0,
CFG_1555 = 1,
CFG_888PACK = 2,
CFG_X888 = 3,
CFG_8888 = 4,
CFG_422PACK = 5,
CFG_422 = 6,
CFG_420 = 7,
CFG_PSEUDO4 = 9,
CFG_PSEUDO8 = 10,
CFG_SWAPRB = 1 << 4,
CFG_SWAPUV = 1 << 3,
CFG_SWAPYU = 1 << 2,
CFG_YUV2RGB = 1 << 1,
};
/* For LCD_SPU_DMA_CTRL0 */
enum {
CFG_NOBLENDING = 1 << 31,
CFG_GAMMA_ENA = 1 << 30,
CFG_CBSH_ENA = 1 << 29,
CFG_PALETTE_ENA = 1 << 28,
CFG_ARBFAST_ENA = 1 << 27,
CFG_HWC_1BITMOD = 1 << 26,
CFG_HWC_1BITENA = 1 << 25,
CFG_HWC_ENA = 1 << 24,
CFG_DMAFORMAT = 0xf << 20,
#define CFG_DMA_FMT(x) ((x) << 20)
CFG_GRAFORMAT = 0xf << 16,
#define CFG_GRA_FMT(x) ((x) << 16)
#define CFG_GRA_MOD(x) ((x) << 8)
CFG_GRA_FTOGGLE = 1 << 15,
CFG_GRA_HSMOOTH = 1 << 14,
CFG_GRA_TSTMODE = 1 << 13,
CFG_GRA_ENA = 1 << 8,
#define CFG_DMA_MOD(x) ((x) << 0)
CFG_DMA_FTOGGLE = 1 << 7,
CFG_DMA_HSMOOTH = 1 << 6,
CFG_DMA_TSTMODE = 1 << 5,
CFG_DMA_ENA = 1 << 0,
};
enum {
CKMODE_DISABLE = 0,
CKMODE_Y = 1,
CKMODE_U = 2,
CKMODE_RGB = 3,
CKMODE_V = 4,
CKMODE_R = 5,
CKMODE_G = 6,
CKMODE_B = 7,
};
/* For LCD_SPU_DMA_CTRL1 */
enum {
CFG_FRAME_TRIG = 1 << 31,
CFG_VSYNC_INV = 1 << 27,
CFG_CKMODE_MASK = 0x7 << 24,
#define CFG_CKMODE(x) ((x) << 24)
CFG_CARRY = 1 << 23,
CFG_GATED_CLK = 1 << 21,
CFG_PWRDN_ENA = 1 << 20,
CFG_DSCALE_MASK = 0x3 << 18,
CFG_DSCALE_NONE = 0x0 << 18,
CFG_DSCALE_HALF = 0x1 << 18,
CFG_DSCALE_QUAR = 0x2 << 18,
CFG_ALPHAM_MASK = 0x3 << 16,
CFG_ALPHAM_VIDEO = 0x0 << 16,
CFG_ALPHAM_GRA = 0x1 << 16,
CFG_ALPHAM_CFG = 0x2 << 16,
CFG_ALPHA_MASK = 0xff << 8,
CFG_PIXCMD_MASK = 0xff,
};
/* For LCD_SPU_SRAM_CTRL */
enum {
SRAM_READ = 0 << 14,
SRAM_WRITE = 2 << 14,
SRAM_INIT = 3 << 14,
SRAM_HWC32_RAM1 = 0xc << 8,
SRAM_HWC32_RAM2 = 0xd << 8,
SRAM_HWC32_RAMR = SRAM_HWC32_RAM1,
SRAM_HWC32_RAMG = SRAM_HWC32_RAM2,
SRAM_HWC32_RAMB = 0xe << 8,
SRAM_HWC32_TRAN = 0xf << 8,
SRAM_HWC = 0xf << 8,
};
/* For LCD_SPU_SRAM_PARA1 */
enum {
CFG_CSB_256x32 = 1 << 15, /* cursor */
CFG_CSB_256x24 = 1 << 14, /* palette */
CFG_CSB_256x8 = 1 << 13, /* gamma */
CFG_PDWN1920x32 = 1 << 8, /* Armada 510: power down vscale ram */
CFG_PDWN256x32 = 1 << 7, /* power down cursor */
CFG_PDWN256x24 = 1 << 6, /* power down palette */
CFG_PDWN256x8 = 1 << 5, /* power down gamma */
CFG_PDWNHWC = 1 << 4, /* Armada 510: power down all hwc ram */
CFG_PDWN32x32 = 1 << 3, /* power down slave->smart ram */
CFG_PDWN16x66 = 1 << 2, /* power down UV fifo */
CFG_PDWN32x66 = 1 << 1, /* power down Y fifo */
CFG_PDWN64x66 = 1 << 0, /* power down graphic fifo */
};
/* For LCD_CFG_SCLK_DIV */
enum {
/* Armada 510 */
SCLK_510_AXI = 0x0 << 30,
SCLK_510_EXTCLK0 = 0x1 << 30,
SCLK_510_PLL = 0x2 << 30,
SCLK_510_EXTCLK1 = 0x3 << 30,
SCLK_510_DIV_CHANGE = 1 << 29,
SCLK_510_FRAC_DIV_MASK = 0xfff << 16,
SCLK_510_INT_DIV_MASK = 0xffff << 0,
/* Armada 16x */
SCLK_16X_AHB = 0x0 << 28,
SCLK_16X_PCLK = 0x1 << 28,
SCLK_16X_AXI = 0x4 << 28,
SCLK_16X_PLL = 0x8 << 28,
SCLK_16X_FRAC_DIV_MASK = 0xfff << 16,
SCLK_16X_INT_DIV_MASK = 0xffff << 0,
};
/* For LCD_SPU_DUMB_CTRL */
enum {
DUMB16_RGB565_0 = 0x0 << 28,
DUMB16_RGB565_1 = 0x1 << 28,
DUMB18_RGB666_0 = 0x2 << 28,
DUMB18_RGB666_1 = 0x3 << 28,
DUMB12_RGB444_0 = 0x4 << 28,
DUMB12_RGB444_1 = 0x5 << 28,
DUMB24_RGB888_0 = 0x6 << 28,
DUMB_BLANK = 0x7 << 28,
DUMB_MASK = 0xf << 28,
CFG_BIAS_OUT = 1 << 8,
CFG_REV_RGB = 1 << 7,
CFG_INV_CBLANK = 1 << 6,
CFG_INV_CSYNC = 1 << 5, /* Normally active high */
CFG_INV_HENA = 1 << 4,
CFG_INV_VSYNC = 1 << 3, /* Normally active high */
CFG_INV_HSYNC = 1 << 2, /* Normally active high */
CFG_INV_PCLK = 1 << 1,
CFG_DUMB_ENA = 1 << 0,
};
/* For LCD_SPU_IOPAD_CONTROL */
enum {
CFG_VSCALE_LN_EN = 3 << 18,
CFG_GRA_VM_ENA = 1 << 15,
CFG_DMA_VM_ENA = 1 << 13,
CFG_CMD_VM_ENA = 1 << 11,
CFG_CSC_MASK = 3 << 8,
CFG_CSC_YUV_CCIR709 = 1 << 9,
CFG_CSC_YUV_CCIR601 = 0 << 9,
CFG_CSC_RGB_STUDIO = 1 << 8,
CFG_CSC_RGB_COMPUTER = 0 << 8,
CFG_IOPAD_MASK = 0xf << 0,
CFG_IOPAD_DUMB24 = 0x0 << 0,
CFG_IOPAD_DUMB18SPI = 0x1 << 0,
CFG_IOPAD_DUMB18GPIO = 0x2 << 0,
CFG_IOPAD_DUMB16SPI = 0x3 << 0,
CFG_IOPAD_DUMB16GPIO = 0x4 << 0,
CFG_IOPAD_DUMB12GPIO = 0x5 << 0,
CFG_IOPAD_SMART18 = 0x6 << 0,
CFG_IOPAD_SMART16 = 0x7 << 0,
CFG_IOPAD_SMART8 = 0x8 << 0,
};
#define IOPAD_DUMB24 0x0
/* For LCD_SPU_IRQ_ENA */
enum {
DMA_FRAME_IRQ0_ENA = 1 << 31,
DMA_FRAME_IRQ1_ENA = 1 << 30,
DMA_FRAME_IRQ_ENA = DMA_FRAME_IRQ0_ENA | DMA_FRAME_IRQ1_ENA,
DMA_FF_UNDERFLOW_ENA = 1 << 29,
GRA_FRAME_IRQ0_ENA = 1 << 27,
GRA_FRAME_IRQ1_ENA = 1 << 26,
GRA_FRAME_IRQ_ENA = GRA_FRAME_IRQ0_ENA | GRA_FRAME_IRQ1_ENA,
GRA_FF_UNDERFLOW_ENA = 1 << 25,
VSYNC_IRQ_ENA = 1 << 23,
DUMB_FRAMEDONE_ENA = 1 << 22,
TWC_FRAMEDONE_ENA = 1 << 21,
HWC_FRAMEDONE_ENA = 1 << 20,
SLV_IRQ_ENA = 1 << 19,
SPI_IRQ_ENA = 1 << 18,
PWRDN_IRQ_ENA = 1 << 17,
ERR_IRQ_ENA = 1 << 16,
CLEAN_SPU_IRQ_ISR = 0xffff,
};
/* For LCD_SPU_IRQ_ISR */
enum {
DMA_FRAME_IRQ0 = 1 << 31,
DMA_FRAME_IRQ1 = 1 << 30,
DMA_FRAME_IRQ = DMA_FRAME_IRQ0 | DMA_FRAME_IRQ1,
DMA_FF_UNDERFLOW = 1 << 29,
GRA_FRAME_IRQ0 = 1 << 27,
GRA_FRAME_IRQ1 = 1 << 26,
GRA_FRAME_IRQ = GRA_FRAME_IRQ0 | GRA_FRAME_IRQ1,
GRA_FF_UNDERFLOW = 1 << 25,
VSYNC_IRQ = 1 << 23,
DUMB_FRAMEDONE = 1 << 22,
TWC_FRAMEDONE = 1 << 21,
HWC_FRAMEDONE = 1 << 20,
SLV_IRQ = 1 << 19,
SPI_IRQ = 1 << 18,
PWRDN_IRQ = 1 << 17,
ERR_IRQ = 1 << 16,
DMA_FRAME_IRQ0_LEVEL = 1 << 15,
DMA_FRAME_IRQ1_LEVEL = 1 << 14,
DMA_FRAME_CNT_ISR = 3 << 12,
GRA_FRAME_IRQ0_LEVEL = 1 << 11,
GRA_FRAME_IRQ1_LEVEL = 1 << 10,
GRA_FRAME_CNT_ISR = 3 << 8,
VSYNC_IRQ_LEVEL = 1 << 7,
DUMB_FRAMEDONE_LEVEL = 1 << 6,
TWC_FRAMEDONE_LEVEL = 1 << 5,
HWC_FRAMEDONE_LEVEL = 1 << 4,
SLV_FF_EMPTY = 1 << 3,
DMA_FF_ALLEMPTY = 1 << 2,
GRA_FF_ALLEMPTY = 1 << 1,
PWRDN_IRQ_LEVEL = 1 << 0,
};
#endif
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_IOCTLP_H
#define ARMADA_IOCTLP_H
#define ARMADA_IOCTL_PROTO(name)\
extern int armada_##name##_ioctl(struct drm_device *, void *, struct drm_file *)
ARMADA_IOCTL_PROTO(gem_create);
ARMADA_IOCTL_PROTO(gem_mmap);
ARMADA_IOCTL_PROTO(gem_pwrite);
#endif
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder_slave.h>
#include "armada_output.h"
#include "armada_drm.h"
struct armada_connector {
struct drm_connector conn;
const struct armada_output_type *type;
};
#define drm_to_armada_conn(c) container_of(c, struct armada_connector, conn)
struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn)
{
struct drm_encoder *enc = conn->encoder;
return enc ? enc : drm_encoder_find(conn->dev, conn->encoder_ids[0]);
}
static enum drm_connector_status armada_drm_connector_detect(
struct drm_connector *conn, bool force)
{
struct armada_connector *dconn = drm_to_armada_conn(conn);
enum drm_connector_status status = connector_status_disconnected;
if (dconn->type->detect) {
status = dconn->type->detect(conn, force);
} else {
struct drm_encoder *enc = armada_drm_connector_encoder(conn);
if (enc)
status = encoder_helper_funcs(enc)->detect(enc, conn);
}
return status;
}
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_cleanup(conn);
kfree(dconn);
}
static int armada_drm_connector_set_property(struct drm_connector *conn,
struct drm_property *property, uint64_t value)
{
struct armada_connector *dconn = drm_to_armada_conn(conn);
if (!dconn->type->set_property)
return -EINVAL;
return dconn->type->set_property(conn, property, value);
}
static const struct drm_connector_funcs armada_drm_conn_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = armada_drm_connector_detect,
.destroy = armada_drm_connector_destroy,
.set_property = armada_drm_connector_set_property,
};
void armada_drm_encoder_prepare(struct drm_encoder *encoder)
{
encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_OFF);
}
void armada_drm_encoder_commit(struct drm_encoder *encoder)
{
encoder_helper_funcs(encoder)->dpms(encoder, DRM_MODE_DPMS_ON);
}
bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode, struct drm_display_mode *adjusted)
{
return true;
}
/* Shouldn't this be a generic helper function? */
int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
struct drm_display_mode *mode)
{
struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
int valid = MODE_BAD;
if (encoder) {
struct drm_encoder_slave *slave = to_encoder_slave(encoder);
valid = slave->slave_funcs->mode_valid(encoder, mode);
}
return valid;
}
int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
struct drm_property *property, uint64_t value)
{
struct drm_encoder *encoder = armada_drm_connector_encoder(conn);
int rc = -EINVAL;
if (encoder) {
struct drm_encoder_slave *slave = to_encoder_slave(encoder);
rc = slave->slave_funcs->set_property(encoder, conn, property,
value);
}
return rc;
}
int armada_output_create(struct drm_device *dev,
const struct armada_output_type *type, const void *data)
{
struct armada_connector *dconn;
int ret;
dconn = kzalloc(sizeof(*dconn), GFP_KERNEL);
if (!dconn)
return -ENOMEM;
dconn->type = type;
ret = drm_connector_init(dev, &dconn->conn, &armada_drm_conn_funcs,
type->connector_type);
if (ret) {
DRM_ERROR("unable to init connector\n");
goto err_destroy_dconn;
}
ret = type->create(&dconn->conn, data);
if (ret)
goto err_conn;
ret = drm_sysfs_connector_add(&dconn->conn);
if (ret)
goto err_sysfs;
return 0;
err_sysfs:
if (dconn->conn.encoder)
dconn->conn.encoder->funcs->destroy(dconn->conn.encoder);
err_conn:
drm_connector_cleanup(&dconn->conn);
err_destroy_dconn:
kfree(dconn);
return ret;
}
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_CONNETOR_H
#define ARMADA_CONNETOR_H
#define encoder_helper_funcs(encoder) \
((struct drm_encoder_helper_funcs *)encoder->helper_private)
struct armada_output_type {
int connector_type;
enum drm_connector_status (*detect)(struct drm_connector *, bool);
int (*create)(struct drm_connector *, const void *);
int (*set_property)(struct drm_connector *, struct drm_property *,
uint64_t);
};
struct drm_encoder *armada_drm_connector_encoder(struct drm_connector *conn);
void armada_drm_encoder_prepare(struct drm_encoder *encoder);
void armada_drm_encoder_commit(struct drm_encoder *encoder);
bool armada_drm_encoder_mode_fixup(struct drm_encoder *encoder,
const struct drm_display_mode *mode, struct drm_display_mode *adj);
int armada_drm_slave_encoder_mode_valid(struct drm_connector *conn,
struct drm_display_mode *mode);
int armada_drm_slave_encoder_set_property(struct drm_connector *conn,
struct drm_property *property, uint64_t value);
int armada_output_create(struct drm_device *dev,
const struct armada_output_type *type, const void *data);
#endif
/*
* Copyright (C) 2012 Russell King
* Rewritten from the dovefb driver, and Armada510 manuals.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drmP.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_fb.h"
#include "armada_gem.h"
#include "armada_hw.h"
#include <drm/armada_drm.h>
#include "armada_ioctlP.h"
struct armada_plane_properties {
uint32_t colorkey_yr;
uint32_t colorkey_ug;
uint32_t colorkey_vb;
#define K2R(val) (((val) >> 0) & 0xff)
#define K2G(val) (((val) >> 8) & 0xff)
#define K2B(val) (((val) >> 16) & 0xff)
int16_t brightness;
uint16_t contrast;
uint16_t saturation;
uint32_t colorkey_mode;
};
struct armada_plane {
struct drm_plane base;
spinlock_t lock;
struct drm_framebuffer *old_fb;
uint32_t src_hw;
uint32_t dst_hw;
uint32_t dst_yx;
uint32_t ctrl0;
struct {
struct armada_vbl_event update;
struct armada_regs regs[13];
wait_queue_head_t wait;
} vbl;
struct armada_plane_properties prop;
};
#define drm_to_armada_plane(p) container_of(p, struct armada_plane, base)
static void
armada_ovl_update_attr(struct armada_plane_properties *prop,
struct armada_crtc *dcrtc)
{
writel_relaxed(prop->colorkey_yr, dcrtc->base + LCD_SPU_COLORKEY_Y);
writel_relaxed(prop->colorkey_ug, dcrtc->base + LCD_SPU_COLORKEY_U);
writel_relaxed(prop->colorkey_vb, dcrtc->base + LCD_SPU_COLORKEY_V);
writel_relaxed(prop->brightness << 16 | prop->contrast,
dcrtc->base + LCD_SPU_CONTRAST);
/* Docs say 15:0, but it seems to actually be 31:16 on Armada 510 */
writel_relaxed(prop->saturation << 16,
dcrtc->base + LCD_SPU_SATURATION);
writel_relaxed(0x00002000, dcrtc->base + LCD_SPU_CBSH_HUE);
spin_lock_irq(&dcrtc->irq_lock);
armada_updatel(prop->colorkey_mode | CFG_ALPHAM_GRA,
CFG_CKMODE_MASK | CFG_ALPHAM_MASK | CFG_ALPHA_MASK,
dcrtc->base + LCD_SPU_DMA_CTRL1);
armada_updatel(ADV_GRACOLORKEY, 0, dcrtc->base + LCD_SPU_ADV_REG);
spin_unlock_irq(&dcrtc->irq_lock);
}
/* === Plane support === */
static void armada_plane_vbl(struct armada_crtc *dcrtc, void *data)
{
struct armada_plane *dplane = data;
struct drm_framebuffer *fb;
armada_drm_crtc_update_regs(dcrtc, dplane->vbl.regs);
spin_lock(&dplane->lock);
fb = dplane->old_fb;
dplane->old_fb = NULL;
spin_unlock(&dplane->lock);
if (fb)
armada_drm_queue_unref_work(dcrtc->crtc.dev, fb);
}
static unsigned armada_limit(int start, unsigned size, unsigned max)
{
int end = start + size;
if (end < 0)
return 0;
if (start < 0)
start = 0;
return (unsigned)end > max ? max - start : end - start;
}
static int
armada_plane_update(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb,
int crtc_x, int crtc_y, unsigned crtc_w, unsigned crtc_h,
uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h)
{
struct armada_plane *dplane = drm_to_armada_plane(plane);
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
uint32_t val, ctrl0;
unsigned idx = 0;
int ret;
crtc_w = armada_limit(crtc_x, crtc_w, dcrtc->crtc.mode.hdisplay);
crtc_h = armada_limit(crtc_y, crtc_h, dcrtc->crtc.mode.vdisplay);
ctrl0 = CFG_DMA_FMT(drm_fb_to_armada_fb(fb)->fmt) |
CFG_DMA_MOD(drm_fb_to_armada_fb(fb)->mod) |
CFG_CBSH_ENA | CFG_DMA_HSMOOTH | CFG_DMA_ENA;
/* Does the position/size result in nothing to display? */
if (crtc_w == 0 || crtc_h == 0) {
ctrl0 &= ~CFG_DMA_ENA;
}
/*
* FIXME: if the starting point is off screen, we need to
* adjust src_x, src_y, src_w, src_h appropriately, and
* according to the scale.
*/
if (!dcrtc->plane) {
dcrtc->plane = plane;
armada_ovl_update_attr(&dplane->prop, dcrtc);
}
/* FIXME: overlay on an interlaced display */
/* Just updating the position/size? */
if (plane->fb == fb && dplane->ctrl0 == ctrl0) {
val = (src_h & 0xffff0000) | src_w >> 16;
dplane->src_hw = val;
writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_HPXL_VLN);
val = crtc_h << 16 | crtc_w;
dplane->dst_hw = val;
writel_relaxed(val, dcrtc->base + LCD_SPU_DZM_HPXL_VLN);
val = crtc_y << 16 | crtc_x;
dplane->dst_yx = val;
writel_relaxed(val, dcrtc->base + LCD_SPU_DMA_OVSA_HPXL_VLN);
return 0;
} else if (~dplane->ctrl0 & ctrl0 & CFG_DMA_ENA) {
/* Power up the Y/U/V FIFOs on ENA 0->1 transitions */
armada_updatel(0, CFG_PDWN16x66 | CFG_PDWN32x66,
dcrtc->base + LCD_SPU_SRAM_PARA1);
}
ret = wait_event_timeout(dplane->vbl.wait,
list_empty(&dplane->vbl.update.node),
HZ/25);
if (ret < 0)
return ret;
if (plane->fb != fb) {
struct armada_gem_object *obj = drm_fb_obj(fb);
uint32_t sy, su, sv;
/*
* Take a reference on the new framebuffer - we want to
* hold on to it while the hardware is displaying it.
*/
drm_framebuffer_reference(fb);
if (plane->fb) {
struct drm_framebuffer *older_fb;
spin_lock_irq(&dplane->lock);
older_fb = dplane->old_fb;
dplane->old_fb = plane->fb;
spin_unlock_irq(&dplane->lock);
if (older_fb)
armada_drm_queue_unref_work(dcrtc->crtc.dev,
older_fb);
}
src_y >>= 16;
src_x >>= 16;
sy = obj->dev_addr + fb->offsets[0] + src_y * fb->pitches[0] +
src_x * fb->bits_per_pixel / 8;
su = obj->dev_addr + fb->offsets[1] + src_y * fb->pitches[1] +
src_x;
sv = obj->dev_addr + fb->offsets[2] + src_y * fb->pitches[2] +
src_x;
armada_reg_queue_set(dplane->vbl.regs, idx, sy,
LCD_SPU_DMA_START_ADDR_Y0);
armada_reg_queue_set(dplane->vbl.regs, idx, su,
LCD_SPU_DMA_START_ADDR_U0);
armada_reg_queue_set(dplane->vbl.regs, idx, sv,
LCD_SPU_DMA_START_ADDR_V0);
armada_reg_queue_set(dplane->vbl.regs, idx, sy,
LCD_SPU_DMA_START_ADDR_Y1);
armada_reg_queue_set(dplane->vbl.regs, idx, su,
LCD_SPU_DMA_START_ADDR_U1);
armada_reg_queue_set(dplane->vbl.regs, idx, sv,
LCD_SPU_DMA_START_ADDR_V1);
val = fb->pitches[0] << 16 | fb->pitches[0];
armada_reg_queue_set(dplane->vbl.regs, idx, val,
LCD_SPU_DMA_PITCH_YC);
val = fb->pitches[1] << 16 | fb->pitches[2];
armada_reg_queue_set(dplane->vbl.regs, idx, val,
LCD_SPU_DMA_PITCH_UV);
}
val = (src_h & 0xffff0000) | src_w >> 16;
if (dplane->src_hw != val) {
dplane->src_hw = val;
armada_reg_queue_set(dplane->vbl.regs, idx, val,
LCD_SPU_DMA_HPXL_VLN);
}
val = crtc_h << 16 | crtc_w;
if (dplane->dst_hw != val) {
dplane->dst_hw = val;
armada_reg_queue_set(dplane->vbl.regs, idx, val,
LCD_SPU_DZM_HPXL_VLN);
}
val = crtc_y << 16 | crtc_x;
if (dplane->dst_yx != val) {
dplane->dst_yx = val;
armada_reg_queue_set(dplane->vbl.regs, idx, val,
LCD_SPU_DMA_OVSA_HPXL_VLN);
}
if (dplane->ctrl0 != ctrl0) {
dplane->ctrl0 = ctrl0;
armada_reg_queue_mod(dplane->vbl.regs, idx, ctrl0,
CFG_CBSH_ENA | CFG_DMAFORMAT | CFG_DMA_FTOGGLE |
CFG_DMA_HSMOOTH | CFG_DMA_TSTMODE |
CFG_DMA_MOD(CFG_SWAPRB | CFG_SWAPUV | CFG_SWAPYU |
CFG_YUV2RGB) | CFG_DMA_ENA,
LCD_SPU_DMA_CTRL0);
}
if (idx) {
armada_reg_queue_end(dplane->vbl.regs, idx);
armada_drm_vbl_event_add(dcrtc, &dplane->vbl.update);
}
return 0;
}
static int armada_plane_disable(struct drm_plane *plane)
{
struct armada_plane *dplane = drm_to_armada_plane(plane);
struct drm_framebuffer *fb;
struct armada_crtc *dcrtc;
if (!dplane->base.crtc)
return 0;
dcrtc = drm_to_armada_crtc(dplane->base.crtc);
dcrtc->plane = NULL;
spin_lock_irq(&dcrtc->irq_lock);
armada_drm_vbl_event_remove(dcrtc, &dplane->vbl.update);
armada_updatel(0, CFG_DMA_ENA, dcrtc->base + LCD_SPU_DMA_CTRL0);
dplane->ctrl0 = 0;
spin_unlock_irq(&dcrtc->irq_lock);
/* Power down the Y/U/V FIFOs */
armada_updatel(CFG_PDWN16x66 | CFG_PDWN32x66, 0,
dcrtc->base + LCD_SPU_SRAM_PARA1);
if (plane->fb)
drm_framebuffer_unreference(plane->fb);
spin_lock_irq(&dplane->lock);
fb = dplane->old_fb;
dplane->old_fb = NULL;
spin_unlock_irq(&dplane->lock);
if (fb)
drm_framebuffer_unreference(fb);
return 0;
}
static void armada_plane_destroy(struct drm_plane *plane)
{
kfree(plane);
}
static int armada_plane_set_property(struct drm_plane *plane,
struct drm_property *property, uint64_t val)
{
struct armada_private *priv = plane->dev->dev_private;
struct armada_plane *dplane = drm_to_armada_plane(plane);
bool update_attr = false;
if (property == priv->colorkey_prop) {
#define CCC(v) ((v) << 24 | (v) << 16 | (v) << 8)
dplane->prop.colorkey_yr = CCC(K2R(val));
dplane->prop.colorkey_ug = CCC(K2G(val));
dplane->prop.colorkey_vb = CCC(K2B(val));
#undef CCC
update_attr = true;
} else if (property == priv->colorkey_min_prop) {
dplane->prop.colorkey_yr &= ~0x00ff0000;
dplane->prop.colorkey_yr |= K2R(val) << 16;
dplane->prop.colorkey_ug &= ~0x00ff0000;
dplane->prop.colorkey_ug |= K2G(val) << 16;
dplane->prop.colorkey_vb &= ~0x00ff0000;
dplane->prop.colorkey_vb |= K2B(val) << 16;
update_attr = true;
} else if (property == priv->colorkey_max_prop) {
dplane->prop.colorkey_yr &= ~0xff000000;
dplane->prop.colorkey_yr |= K2R(val) << 24;
dplane->prop.colorkey_ug &= ~0xff000000;
dplane->prop.colorkey_ug |= K2G(val) << 24;
dplane->prop.colorkey_vb &= ~0xff000000;
dplane->prop.colorkey_vb |= K2B(val) << 24;
update_attr = true;
} else if (property == priv->colorkey_val_prop) {
dplane->prop.colorkey_yr &= ~0x0000ff00;
dplane->prop.colorkey_yr |= K2R(val) << 8;
dplane->prop.colorkey_ug &= ~0x0000ff00;
dplane->prop.colorkey_ug |= K2G(val) << 8;
dplane->prop.colorkey_vb &= ~0x0000ff00;
dplane->prop.colorkey_vb |= K2B(val) << 8;
update_attr = true;
} else if (property == priv->colorkey_alpha_prop) {
dplane->prop.colorkey_yr &= ~0x000000ff;
dplane->prop.colorkey_yr |= K2R(val);
dplane->prop.colorkey_ug &= ~0x000000ff;
dplane->prop.colorkey_ug |= K2G(val);
dplane->prop.colorkey_vb &= ~0x000000ff;
dplane->prop.colorkey_vb |= K2B(val);
update_attr = true;
} else if (property == priv->colorkey_mode_prop) {
dplane->prop.colorkey_mode &= ~CFG_CKMODE_MASK;
dplane->prop.colorkey_mode |= CFG_CKMODE(val);
update_attr = true;
} else if (property == priv->brightness_prop) {
dplane->prop.brightness = val - 256;
update_attr = true;
} else if (property == priv->contrast_prop) {
dplane->prop.contrast = val;
update_attr = true;
} else if (property == priv->saturation_prop) {
dplane->prop.saturation = val;
update_attr = true;
}
if (update_attr && dplane->base.crtc)
armada_ovl_update_attr(&dplane->prop,
drm_to_armada_crtc(dplane->base.crtc));
return 0;
}
static const struct drm_plane_funcs armada_plane_funcs = {
.update_plane = armada_plane_update,
.disable_plane = armada_plane_disable,
.destroy = armada_plane_destroy,
.set_property = armada_plane_set_property,
};
static const uint32_t armada_formats[] = {
DRM_FORMAT_UYVY,
DRM_FORMAT_YUYV,
DRM_FORMAT_YUV420,
DRM_FORMAT_YVU420,
DRM_FORMAT_YUV422,
DRM_FORMAT_YVU422,
DRM_FORMAT_VYUY,
DRM_FORMAT_YVYU,
DRM_FORMAT_ARGB8888,
DRM_FORMAT_ABGR8888,
DRM_FORMAT_XRGB8888,
DRM_FORMAT_XBGR8888,
DRM_FORMAT_RGB888,
DRM_FORMAT_BGR888,
DRM_FORMAT_ARGB1555,
DRM_FORMAT_ABGR1555,
DRM_FORMAT_RGB565,
DRM_FORMAT_BGR565,
};
static struct drm_prop_enum_list armada_drm_colorkey_enum_list[] = {
{ CKMODE_DISABLE, "disabled" },
{ CKMODE_Y, "Y component" },
{ CKMODE_U, "U component" },
{ CKMODE_V, "V component" },
{ CKMODE_RGB, "RGB" },
{ CKMODE_R, "R component" },
{ CKMODE_G, "G component" },
{ CKMODE_B, "B component" },
};
static int armada_overlay_create_properties(struct drm_device *dev)
{
struct armada_private *priv = dev->dev_private;
if (priv->colorkey_prop)
return 0;
priv->colorkey_prop = drm_property_create_range(dev, 0,
"colorkey", 0, 0xffffff);
priv->colorkey_min_prop = drm_property_create_range(dev, 0,
"colorkey_min", 0, 0xffffff);
priv->colorkey_max_prop = drm_property_create_range(dev, 0,
"colorkey_max", 0, 0xffffff);
priv->colorkey_val_prop = drm_property_create_range(dev, 0,
"colorkey_val", 0, 0xffffff);
priv->colorkey_alpha_prop = drm_property_create_range(dev, 0,
"colorkey_alpha", 0, 0xffffff);
priv->colorkey_mode_prop = drm_property_create_enum(dev, 0,
"colorkey_mode",
armada_drm_colorkey_enum_list,
ARRAY_SIZE(armada_drm_colorkey_enum_list));
priv->brightness_prop = drm_property_create_range(dev, 0,
"brightness", 0, 256 + 255);
priv->contrast_prop = drm_property_create_range(dev, 0,
"contrast", 0, 0x7fff);
priv->saturation_prop = drm_property_create_range(dev, 0,
"saturation", 0, 0x7fff);
if (!priv->colorkey_prop)
return -ENOMEM;
return 0;
}
int armada_overlay_plane_create(struct drm_device *dev, unsigned long crtcs)
{
struct armada_private *priv = dev->dev_private;
struct drm_mode_object *mobj;
struct armada_plane *dplane;
int ret;
ret = armada_overlay_create_properties(dev);
if (ret)
return ret;
dplane = kzalloc(sizeof(*dplane), GFP_KERNEL);
if (!dplane)
return -ENOMEM;
spin_lock_init(&dplane->lock);
init_waitqueue_head(&dplane->vbl.wait);
armada_drm_vbl_event_init(&dplane->vbl.update, armada_plane_vbl,
dplane);
drm_plane_init(dev, &dplane->base, crtcs, &armada_plane_funcs,
armada_formats, ARRAY_SIZE(armada_formats), false);
dplane->prop.colorkey_yr = 0xfefefe00;
dplane->prop.colorkey_ug = 0x01010100;
dplane->prop.colorkey_vb = 0x01010100;
dplane->prop.colorkey_mode = CFG_CKMODE(CKMODE_RGB);
dplane->prop.brightness = 0;
dplane->prop.contrast = 0x4000;
dplane->prop.saturation = 0x4000;
mobj = &dplane->base.base;
drm_object_attach_property(mobj, priv->colorkey_prop,
0x0101fe);
drm_object_attach_property(mobj, priv->colorkey_min_prop,
0x0101fe);
drm_object_attach_property(mobj, priv->colorkey_max_prop,
0x0101fe);
drm_object_attach_property(mobj, priv->colorkey_val_prop,
0x0101fe);
drm_object_attach_property(mobj, priv->colorkey_alpha_prop,
0x000000);
drm_object_attach_property(mobj, priv->colorkey_mode_prop,
CKMODE_RGB);
drm_object_attach_property(mobj, priv->brightness_prop, 256);
drm_object_attach_property(mobj, priv->contrast_prop,
dplane->prop.contrast);
drm_object_attach_property(mobj, priv->saturation_prop,
dplane->prop.saturation);
return 0;
}
/*
* Copyright (C) 2012 Russell King
* Rewritten from the dovefb driver, and Armada510 manuals.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder_slave.h>
#include "armada_drm.h"
#include "armada_output.h"
#include "armada_slave.h"
static int armada_drm_slave_get_modes(struct drm_connector *conn)
{
struct drm_encoder *enc = armada_drm_connector_encoder(conn);
int count = 0;
if (enc) {
struct drm_encoder_slave *slave = to_encoder_slave(enc);
count = slave->slave_funcs->get_modes(enc, conn);
}
return count;
}
static void armada_drm_slave_destroy(struct drm_encoder *enc)
{
struct drm_encoder_slave *slave = to_encoder_slave(enc);
struct i2c_client *client = drm_i2c_encoder_get_client(enc);
if (slave->slave_funcs)
slave->slave_funcs->destroy(enc);
if (client)
i2c_put_adapter(client->adapter);
drm_encoder_cleanup(&slave->base);
kfree(slave);
}
static const struct drm_encoder_funcs armada_drm_slave_encoder_funcs = {
.destroy = armada_drm_slave_destroy,
};
static const struct drm_connector_helper_funcs armada_drm_slave_helper_funcs = {
.get_modes = armada_drm_slave_get_modes,
.mode_valid = armada_drm_slave_encoder_mode_valid,
.best_encoder = armada_drm_connector_encoder,
};
static const struct drm_encoder_helper_funcs drm_slave_encoder_helpers = {
.dpms = drm_i2c_encoder_dpms,
.save = drm_i2c_encoder_save,
.restore = drm_i2c_encoder_restore,
.mode_fixup = drm_i2c_encoder_mode_fixup,
.prepare = drm_i2c_encoder_prepare,
.commit = drm_i2c_encoder_commit,
.mode_set = drm_i2c_encoder_mode_set,
.detect = drm_i2c_encoder_detect,
};
static int
armada_drm_conn_slave_create(struct drm_connector *conn, const void *data)
{
const struct armada_drm_slave_config *config = data;
struct drm_encoder_slave *slave;
struct i2c_adapter *adap;
int ret;
conn->interlace_allowed = config->interlace_allowed;
conn->doublescan_allowed = config->doublescan_allowed;
conn->polled = config->polled;
drm_connector_helper_add(conn, &armada_drm_slave_helper_funcs);
slave = kzalloc(sizeof(*slave), GFP_KERNEL);
if (!slave)
return -ENOMEM;
slave->base.possible_crtcs = config->crtcs;
adap = i2c_get_adapter(config->i2c_adapter_id);
if (!adap) {
kfree(slave);
return -EPROBE_DEFER;
}
ret = drm_encoder_init(conn->dev, &slave->base,
&armada_drm_slave_encoder_funcs,
DRM_MODE_ENCODER_TMDS);
if (ret) {
DRM_ERROR("unable to init encoder\n");
i2c_put_adapter(adap);
kfree(slave);
return ret;
}
ret = drm_i2c_encoder_init(conn->dev, slave, adap, &config->info);
i2c_put_adapter(adap);
if (ret) {
DRM_ERROR("unable to init encoder slave\n");
armada_drm_slave_destroy(&slave->base);
return ret;
}
drm_encoder_helper_add(&slave->base, &drm_slave_encoder_helpers);
ret = slave->slave_funcs->create_resources(&slave->base, conn);
if (ret) {
armada_drm_slave_destroy(&slave->base);
return ret;
}
ret = drm_mode_connector_attach_encoder(conn, &slave->base);
if (ret) {
armada_drm_slave_destroy(&slave->base);
return ret;
}
conn->encoder = &slave->base;
return ret;
}
static const struct armada_output_type armada_drm_conn_slave = {
.connector_type = DRM_MODE_CONNECTOR_HDMIA,
.create = armada_drm_conn_slave_create,
.set_property = armada_drm_slave_encoder_set_property,
};
int armada_drm_connector_slave_create(struct drm_device *dev,
const struct armada_drm_slave_config *config)
{
return armada_output_create(dev, &armada_drm_conn_slave, config);
}
/*
* Copyright (C) 2012 Russell King
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef ARMADA_SLAVE_H
#define ARMADA_SLAVE_H
#include <linux/i2c.h>
#include <drm/drmP.h>
struct armada_drm_slave_config {
int i2c_adapter_id;
uint32_t crtcs;
uint8_t polled;
bool interlace_allowed;
bool doublescan_allowed;
struct i2c_board_info info;
};
int armada_drm_connector_slave_create(struct drm_device *dev,
const struct armada_drm_slave_config *);
#endif
...@@ -494,13 +494,12 @@ static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev) ...@@ -494,13 +494,12 @@ static struct drm_encoder *cirrus_encoder_init(struct drm_device *dev)
int cirrus_vga_get_modes(struct drm_connector *connector) int cirrus_vga_get_modes(struct drm_connector *connector)
{ {
/* Just add a static list of modes */ int count;
drm_add_modes_noedid(connector, 640, 480);
drm_add_modes_noedid(connector, 800, 600);
drm_add_modes_noedid(connector, 1024, 768);
drm_add_modes_noedid(connector, 1280, 1024);
return 4; /* Just add a static list of modes */
count = drm_add_modes_noedid(connector, 1280, 1024);
drm_set_preferred_mode(connector, 1024, 768);
return count;
} }
static int cirrus_vga_mode_valid(struct drm_connector *connector, static int cirrus_vga_mode_valid(struct drm_connector *connector,
......
...@@ -1303,7 +1303,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, ...@@ -1303,7 +1303,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
} }
/** /**
* drm_crtc_convert_to_umode - convert a modeinfo into a drm_display_mode * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode
* @out: drm_display_mode to return to the user * @out: drm_display_mode to return to the user
* @in: drm_mode_modeinfo to use * @in: drm_mode_modeinfo to use
* *
...@@ -1557,7 +1557,7 @@ int drm_mode_getcrtc(struct drm_device *dev, ...@@ -1557,7 +1557,7 @@ int drm_mode_getcrtc(struct drm_device *dev,
obj = drm_mode_object_find(dev, crtc_resp->crtc_id, obj = drm_mode_object_find(dev, crtc_resp->crtc_id,
DRM_MODE_OBJECT_CRTC); DRM_MODE_OBJECT_CRTC);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
crtc = obj_to_crtc(obj); crtc = obj_to_crtc(obj);
...@@ -1641,7 +1641,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, ...@@ -1641,7 +1641,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
obj = drm_mode_object_find(dev, out_resp->connector_id, obj = drm_mode_object_find(dev, out_resp->connector_id,
DRM_MODE_OBJECT_CONNECTOR); DRM_MODE_OBJECT_CONNECTOR);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
connector = obj_to_connector(obj); connector = obj_to_connector(obj);
...@@ -1757,7 +1757,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data, ...@@ -1757,7 +1757,7 @@ int drm_mode_getencoder(struct drm_device *dev, void *data,
obj = drm_mode_object_find(dev, enc_resp->encoder_id, obj = drm_mode_object_find(dev, enc_resp->encoder_id,
DRM_MODE_OBJECT_ENCODER); DRM_MODE_OBJECT_ENCODER);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
encoder = obj_to_encoder(obj); encoder = obj_to_encoder(obj);
...@@ -2141,7 +2141,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ...@@ -2141,7 +2141,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
DRM_MODE_OBJECT_CRTC); DRM_MODE_OBJECT_CRTC);
if (!obj) { if (!obj) {
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
crtc = obj_to_crtc(obj); crtc = obj_to_crtc(obj);
...@@ -2164,7 +2164,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ...@@ -2164,7 +2164,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
if (!fb) { if (!fb) {
DRM_DEBUG_KMS("Unknown FB ID%d\n", DRM_DEBUG_KMS("Unknown FB ID%d\n",
crtc_req->fb_id); crtc_req->fb_id);
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
} }
...@@ -2232,7 +2232,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, ...@@ -2232,7 +2232,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
if (!obj) { if (!obj) {
DRM_DEBUG_KMS("Connector id %d unknown\n", DRM_DEBUG_KMS("Connector id %d unknown\n",
out_id); out_id);
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
connector = obj_to_connector(obj); connector = obj_to_connector(obj);
...@@ -2280,7 +2280,7 @@ static int drm_mode_cursor_common(struct drm_device *dev, ...@@ -2280,7 +2280,7 @@ static int drm_mode_cursor_common(struct drm_device *dev,
obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) { if (!obj) {
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id);
return -EINVAL; return -ENOENT;
} }
crtc = obj_to_crtc(obj); crtc = obj_to_crtc(obj);
...@@ -2489,6 +2489,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r) ...@@ -2489,6 +2489,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r)
case DRM_FORMAT_YVU444: case DRM_FORMAT_YVU444:
return 0; return 0;
default: default:
DRM_DEBUG_KMS("invalid pixel format %s\n",
drm_get_format_name(r->pixel_format));
return -EINVAL; return -EINVAL;
} }
} }
...@@ -2654,7 +2656,7 @@ int drm_mode_rmfb(struct drm_device *dev, ...@@ -2654,7 +2656,7 @@ int drm_mode_rmfb(struct drm_device *dev,
mutex_unlock(&dev->mode_config.fb_lock); mutex_unlock(&dev->mode_config.fb_lock);
mutex_unlock(&file_priv->fbs_lock); mutex_unlock(&file_priv->fbs_lock);
return -EINVAL; return -ENOENT;
} }
/** /**
...@@ -2682,7 +2684,7 @@ int drm_mode_getfb(struct drm_device *dev, ...@@ -2682,7 +2684,7 @@ int drm_mode_getfb(struct drm_device *dev,
fb = drm_framebuffer_lookup(dev, r->fb_id); fb = drm_framebuffer_lookup(dev, r->fb_id);
if (!fb) if (!fb)
return -EINVAL; return -ENOENT;
r->height = fb->height; r->height = fb->height;
r->width = fb->width; r->width = fb->width;
...@@ -2727,7 +2729,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev, ...@@ -2727,7 +2729,7 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
fb = drm_framebuffer_lookup(dev, r->fb_id); fb = drm_framebuffer_lookup(dev, r->fb_id);
if (!fb) if (!fb)
return -EINVAL; return -ENOENT;
num_clips = r->num_clips; num_clips = r->num_clips;
clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
...@@ -3059,7 +3061,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev, ...@@ -3059,7 +3061,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto done; goto done;
} }
property = obj_to_property(obj); property = obj_to_property(obj);
...@@ -3188,7 +3190,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev, ...@@ -3188,7 +3190,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto done; goto done;
} }
blob = obj_to_blob(obj); blob = obj_to_blob(obj);
...@@ -3349,7 +3351,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, ...@@ -3349,7 +3351,7 @@ int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data,
obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
if (!obj->properties) { if (!obj->properties) {
...@@ -3402,8 +3404,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, ...@@ -3402,8 +3404,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type);
if (!arg_obj) if (!arg_obj) {
ret = -ENOENT;
goto out; goto out;
}
if (!arg_obj->properties) if (!arg_obj->properties)
goto out; goto out;
...@@ -3416,8 +3420,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, ...@@ -3416,8 +3420,10 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
prop_obj = drm_mode_object_find(dev, arg->prop_id, prop_obj = drm_mode_object_find(dev, arg->prop_id,
DRM_MODE_OBJECT_PROPERTY); DRM_MODE_OBJECT_PROPERTY);
if (!prop_obj) if (!prop_obj) {
ret = -ENOENT;
goto out; goto out;
}
property = obj_to_property(prop_obj); property = obj_to_property(prop_obj);
if (!drm_property_change_is_valid(property, arg->value)) if (!drm_property_change_is_valid(property, arg->value))
...@@ -3502,7 +3508,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev, ...@@ -3502,7 +3508,7 @@ int drm_mode_gamma_set_ioctl(struct drm_device *dev,
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
crtc = obj_to_crtc(obj); crtc = obj_to_crtc(obj);
...@@ -3561,7 +3567,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev, ...@@ -3561,7 +3567,7 @@ int drm_mode_gamma_get_ioctl(struct drm_device *dev,
drm_modeset_lock_all(dev); drm_modeset_lock_all(dev);
obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) { if (!obj) {
ret = -EINVAL; ret = -ENOENT;
goto out; goto out;
} }
crtc = obj_to_crtc(obj); crtc = obj_to_crtc(obj);
...@@ -3615,7 +3621,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, ...@@ -3615,7 +3621,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
if (!obj) if (!obj)
return -EINVAL; return -ENOENT;
crtc = obj_to_crtc(obj); crtc = obj_to_crtc(obj);
mutex_lock(&crtc->mutex); mutex_lock(&crtc->mutex);
...@@ -3632,8 +3638,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev, ...@@ -3632,8 +3638,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
goto out; goto out;
fb = drm_framebuffer_lookup(dev, page_flip->fb_id); fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
if (!fb) if (!fb) {
ret = -ENOENT;
goto out; goto out;
}
ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
if (ret) if (ret)
...@@ -3822,7 +3830,8 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, ...@@ -3822,7 +3830,8 @@ void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
*bpp = 32; *bpp = 32;
break; break;
default: default:
DRM_DEBUG_KMS("unsupported pixel format\n"); DRM_DEBUG_KMS("unsupported pixel format %s\n",
drm_get_format_name(format));
*depth = 0; *depth = 0;
*bpp = 0; *bpp = 0;
break; break;
......
...@@ -405,22 +405,25 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, ...@@ -405,22 +405,25 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
struct drm_framebuffer *old_fb) struct drm_framebuffer *old_fb)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
struct drm_display_mode *adjusted_mode, saved_mode, saved_hwmode; struct drm_display_mode *adjusted_mode, saved_mode;
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
struct drm_encoder_helper_funcs *encoder_funcs; struct drm_encoder_helper_funcs *encoder_funcs;
int saved_x, saved_y; int saved_x, saved_y;
bool saved_enabled;
struct drm_encoder *encoder; struct drm_encoder *encoder;
bool ret = true; bool ret = true;
saved_enabled = crtc->enabled;
crtc->enabled = drm_helper_crtc_in_use(crtc); crtc->enabled = drm_helper_crtc_in_use(crtc);
if (!crtc->enabled) if (!crtc->enabled)
return true; return true;
adjusted_mode = drm_mode_duplicate(dev, mode); adjusted_mode = drm_mode_duplicate(dev, mode);
if (!adjusted_mode) if (!adjusted_mode) {
crtc->enabled = saved_enabled;
return false; return false;
}
saved_hwmode = crtc->hwmode;
saved_mode = crtc->mode; saved_mode = crtc->mode;
saved_x = crtc->x; saved_x = crtc->x;
saved_y = crtc->y; saved_y = crtc->y;
...@@ -539,7 +542,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, ...@@ -539,7 +542,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
done: done:
drm_mode_destroy(dev, adjusted_mode); drm_mode_destroy(dev, adjusted_mode);
if (!ret) { if (!ret) {
crtc->hwmode = saved_hwmode; crtc->enabled = saved_enabled;
crtc->mode = saved_mode; crtc->mode = saved_mode;
crtc->x = saved_x; crtc->x = saved_x;
crtc->y = saved_y; crtc->y = saved_y;
...@@ -567,6 +570,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) ...@@ -567,6 +570,14 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
continue; continue;
connector->encoder = NULL; connector->encoder = NULL;
/*
* drm_helper_disable_unused_functions() ought to be
* doing this, but since we've decoupled the encoder
* from the connector above, the required connection
* between them is henceforth no longer available.
*/
connector->dpms = DRM_MODE_DPMS_OFF;
} }
} }
...@@ -593,9 +604,8 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) ...@@ -593,9 +604,8 @@ drm_crtc_helper_disable(struct drm_crtc *crtc)
int drm_crtc_helper_set_config(struct drm_mode_set *set) int drm_crtc_helper_set_config(struct drm_mode_set *set)
{ {
struct drm_device *dev; struct drm_device *dev;
struct drm_crtc *save_crtcs, *new_crtc, *crtc; struct drm_crtc *new_crtc;
struct drm_encoder *save_encoders, *new_encoder, *encoder; struct drm_encoder *save_encoders, *new_encoder, *encoder;
struct drm_framebuffer *old_fb = NULL;
bool mode_changed = false; /* if true do a full mode set */ bool mode_changed = false; /* if true do a full mode set */
bool fb_changed = false; /* if true and !mode_changed just do a flip */ bool fb_changed = false; /* if true and !mode_changed just do a flip */
struct drm_connector *save_connectors, *connector; struct drm_connector *save_connectors, *connector;
...@@ -631,37 +641,27 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) ...@@ -631,37 +641,27 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
dev = set->crtc->dev; dev = set->crtc->dev;
/* Allocate space for the backup of all (non-pointer) crtc, encoder and /*
* connector data. */ * Allocate space for the backup of all (non-pointer) encoder and
save_crtcs = kzalloc(dev->mode_config.num_crtc * * connector data.
sizeof(struct drm_crtc), GFP_KERNEL); */
if (!save_crtcs)
return -ENOMEM;
save_encoders = kzalloc(dev->mode_config.num_encoder * save_encoders = kzalloc(dev->mode_config.num_encoder *
sizeof(struct drm_encoder), GFP_KERNEL); sizeof(struct drm_encoder), GFP_KERNEL);
if (!save_encoders) { if (!save_encoders)
kfree(save_crtcs);
return -ENOMEM; return -ENOMEM;
}
save_connectors = kzalloc(dev->mode_config.num_connector * save_connectors = kzalloc(dev->mode_config.num_connector *
sizeof(struct drm_connector), GFP_KERNEL); sizeof(struct drm_connector), GFP_KERNEL);
if (!save_connectors) { if (!save_connectors) {
kfree(save_crtcs);
kfree(save_encoders); kfree(save_encoders);
return -ENOMEM; return -ENOMEM;
} }
/* Copy data. Note that driver private data is not affected. /*
* Copy data. Note that driver private data is not affected.
* Should anything bad happen only the expected state is * Should anything bad happen only the expected state is
* restored, not the drivers personal bookkeeping. * restored, not the drivers personal bookkeeping.
*/ */
count = 0;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
save_crtcs[count++] = *crtc;
}
count = 0; count = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
save_encoders[count++] = *encoder; save_encoders[count++] = *encoder;
...@@ -785,19 +785,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) ...@@ -785,19 +785,17 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
mode_changed = true; mode_changed = true;
if (mode_changed) { if (mode_changed) {
set->crtc->enabled = drm_helper_crtc_in_use(set->crtc); if (drm_helper_crtc_in_use(set->crtc)) {
if (set->crtc->enabled) {
DRM_DEBUG_KMS("attempting to set mode from" DRM_DEBUG_KMS("attempting to set mode from"
" userspace\n"); " userspace\n");
drm_mode_debug_printmodeline(set->mode); drm_mode_debug_printmodeline(set->mode);
old_fb = set->crtc->fb;
set->crtc->fb = set->fb; set->crtc->fb = set->fb;
if (!drm_crtc_helper_set_mode(set->crtc, set->mode, if (!drm_crtc_helper_set_mode(set->crtc, set->mode,
set->x, set->y, set->x, set->y,
old_fb)) { save_set.fb)) {
DRM_ERROR("failed to set mode on [CRTC:%d]\n", DRM_ERROR("failed to set mode on [CRTC:%d]\n",
set->crtc->base.id); set->crtc->base.id);
set->crtc->fb = old_fb; set->crtc->fb = save_set.fb;
ret = -EINVAL; ret = -EINVAL;
goto fail; goto fail;
} }
...@@ -812,30 +810,23 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) ...@@ -812,30 +810,23 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
} else if (fb_changed) { } else if (fb_changed) {
set->crtc->x = set->x; set->crtc->x = set->x;
set->crtc->y = set->y; set->crtc->y = set->y;
set->crtc->fb = set->fb;
old_fb = set->crtc->fb;
if (set->crtc->fb != set->fb)
set->crtc->fb = set->fb;
ret = crtc_funcs->mode_set_base(set->crtc, ret = crtc_funcs->mode_set_base(set->crtc,
set->x, set->y, old_fb); set->x, set->y, save_set.fb);
if (ret != 0) { if (ret != 0) {
set->crtc->fb = old_fb; set->crtc->x = save_set.x;
set->crtc->y = save_set.y;
set->crtc->fb = save_set.fb;
goto fail; goto fail;
} }
} }
kfree(save_connectors); kfree(save_connectors);
kfree(save_encoders); kfree(save_encoders);
kfree(save_crtcs);
return 0; return 0;
fail: fail:
/* Restore all previous data. */ /* Restore all previous data. */
count = 0;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
*crtc = save_crtcs[count++];
}
count = 0; count = 0;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
*encoder = save_encoders[count++]; *encoder = save_encoders[count++];
...@@ -854,7 +845,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set) ...@@ -854,7 +845,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
kfree(save_connectors); kfree(save_connectors);
kfree(save_encoders); kfree(save_encoders);
kfree(save_crtcs);
return ret; return ret;
} }
EXPORT_SYMBOL(drm_crtc_helper_set_config); EXPORT_SYMBOL(drm_crtc_helper_set_config);
...@@ -1135,14 +1125,14 @@ void drm_kms_helper_poll_fini(struct drm_device *dev) ...@@ -1135,14 +1125,14 @@ void drm_kms_helper_poll_fini(struct drm_device *dev)
} }
EXPORT_SYMBOL(drm_kms_helper_poll_fini); EXPORT_SYMBOL(drm_kms_helper_poll_fini);
void drm_helper_hpd_irq_event(struct drm_device *dev) bool drm_helper_hpd_irq_event(struct drm_device *dev)
{ {
struct drm_connector *connector; struct drm_connector *connector;
enum drm_connector_status old_status; enum drm_connector_status old_status;
bool changed = false; bool changed = false;
if (!dev->mode_config.poll_enabled) if (!dev->mode_config.poll_enabled)
return; return false;
mutex_lock(&dev->mode_config.mutex); mutex_lock(&dev->mode_config.mutex);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
...@@ -1167,5 +1157,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev) ...@@ -1167,5 +1157,7 @@ void drm_helper_hpd_irq_event(struct drm_device *dev)
if (changed) if (changed)
drm_kms_helper_hotplug_event(dev); drm_kms_helper_hotplug_event(dev);
return changed;
} }
EXPORT_SYMBOL(drm_helper_hpd_irq_event); EXPORT_SYMBOL(drm_helper_hpd_irq_event);
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Initialization, etc. * Initialization, etc.
**************************************************/ **************************************************/
static struct drm_info_list drm_debugfs_list[] = { static const struct drm_info_list drm_debugfs_list[] = {
{"name", drm_name_info, 0}, {"name", drm_name_info, 0},
{"vm", drm_vm_info, 0}, {"vm", drm_vm_info, 0},
{"clients", drm_clients_info, 0}, {"clients", drm_clients_info, 0},
...@@ -84,7 +84,7 @@ static const struct file_operations drm_debugfs_fops = { ...@@ -84,7 +84,7 @@ static const struct file_operations drm_debugfs_fops = {
* Create a given set of debugfs files represented by an array of * Create a given set of debugfs files represented by an array of
* gdm_debugfs_lists in the given root directory. * gdm_debugfs_lists in the given root directory.
*/ */
int drm_debugfs_create_files(struct drm_info_list *files, int count, int drm_debugfs_create_files(const struct drm_info_list *files, int count,
struct dentry *root, struct drm_minor *minor) struct dentry *root, struct drm_minor *minor)
{ {
struct drm_device *dev = minor->dev; struct drm_device *dev = minor->dev;
...@@ -188,7 +188,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id, ...@@ -188,7 +188,7 @@ int drm_debugfs_init(struct drm_minor *minor, int minor_id,
* *
* Remove all debugfs entries created by debugfs_init(). * Remove all debugfs entries created by debugfs_init().
*/ */
int drm_debugfs_remove_files(struct drm_info_list *files, int count, int drm_debugfs_remove_files(const struct drm_info_list *files, int count,
struct drm_minor *minor) struct drm_minor *minor)
{ {
struct list_head *pos, *q; struct list_head *pos, *q;
......
...@@ -403,7 +403,7 @@ long drm_ioctl(struct file *filp, ...@@ -403,7 +403,7 @@ long drm_ioctl(struct file *filp,
err_i1: err_i1:
if (!ioctl) if (!ioctl)
DRM_DEBUG("invalid iotcl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n", DRM_DEBUG("invalid ioctl: pid=%d, dev=0x%lx, auth=%d, cmd=0x%02x, nr=0x%02x\n",
task_pid_nr(current), task_pid_nr(current),
(long)old_encode_dev(file_priv->minor->device), (long)old_encode_dev(file_priv->minor->device),
file_priv->authenticated, cmd, nr); file_priv->authenticated, cmd, nr);
......
...@@ -458,6 +458,15 @@ static const struct drm_display_mode drm_dmt_modes[] = { ...@@ -458,6 +458,15 @@ static const struct drm_display_mode drm_dmt_modes[] = {
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) }, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
}; };
/*
* These more or less come from the DMT spec. The 720x400 modes are
* inferred from historical 80x25 practice. The 640x480@67 and 832x624@75
* modes are old-school Mac modes. The EDID spec says the 1152x864@75 mode
* should be 1152x870, again for the Mac, but instead we use the x864 DMT
* mode.
*
* The DMT modes have been fact-checked; the rest are mild guesses.
*/
static const struct drm_display_mode edid_est_modes[] = { static const struct drm_display_mode edid_est_modes[] = {
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840, { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
968, 1056, 0, 600, 601, 605, 628, 0, 968, 1056, 0, 600, 601, 605, 628, 0,
...@@ -560,7 +569,7 @@ static const struct minimode est3_modes[] = { ...@@ -560,7 +569,7 @@ static const struct minimode est3_modes[] = {
{ 1600, 1200, 75, 0 }, { 1600, 1200, 75, 0 },
{ 1600, 1200, 85, 0 }, { 1600, 1200, 85, 0 },
{ 1792, 1344, 60, 0 }, { 1792, 1344, 60, 0 },
{ 1792, 1344, 85, 0 }, { 1792, 1344, 75, 0 },
{ 1856, 1392, 60, 0 }, { 1856, 1392, 60, 0 },
{ 1856, 1392, 75, 0 }, { 1856, 1392, 75, 0 },
{ 1920, 1200, 60, 1 }, { 1920, 1200, 60, 1 },
...@@ -1320,7 +1329,7 @@ static u32 edid_get_quirks(struct edid *edid) ...@@ -1320,7 +1329,7 @@ static u32 edid_get_quirks(struct edid *edid)
} }
#define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay) #define MODE_SIZE(m) ((m)->hdisplay * (m)->vdisplay)
#define MODE_REFRESH_DIFF(m,r) (abs((m)->vrefresh - target_refresh)) #define MODE_REFRESH_DIFF(c,t) (abs((c) - (t)))
/** /**
* edid_fixup_preferred - set preferred modes based on quirk list * edid_fixup_preferred - set preferred modes based on quirk list
...@@ -1335,6 +1344,7 @@ static void edid_fixup_preferred(struct drm_connector *connector, ...@@ -1335,6 +1344,7 @@ static void edid_fixup_preferred(struct drm_connector *connector,
{ {
struct drm_display_mode *t, *cur_mode, *preferred_mode; struct drm_display_mode *t, *cur_mode, *preferred_mode;
int target_refresh = 0; int target_refresh = 0;
int cur_vrefresh, preferred_vrefresh;
if (list_empty(&connector->probed_modes)) if (list_empty(&connector->probed_modes))
return; return;
...@@ -1357,10 +1367,14 @@ static void edid_fixup_preferred(struct drm_connector *connector, ...@@ -1357,10 +1367,14 @@ static void edid_fixup_preferred(struct drm_connector *connector,
if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode)) if (MODE_SIZE(cur_mode) > MODE_SIZE(preferred_mode))
preferred_mode = cur_mode; preferred_mode = cur_mode;
cur_vrefresh = cur_mode->vrefresh ?
cur_mode->vrefresh : drm_mode_vrefresh(cur_mode);
preferred_vrefresh = preferred_mode->vrefresh ?
preferred_mode->vrefresh : drm_mode_vrefresh(preferred_mode);
/* At a given size, try to get closest to target refresh */ /* At a given size, try to get closest to target refresh */
if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) && if ((MODE_SIZE(cur_mode) == MODE_SIZE(preferred_mode)) &&
MODE_REFRESH_DIFF(cur_mode, target_refresh) < MODE_REFRESH_DIFF(cur_vrefresh, target_refresh) <
MODE_REFRESH_DIFF(preferred_mode, target_refresh)) { MODE_REFRESH_DIFF(preferred_vrefresh, target_refresh)) {
preferred_mode = cur_mode; preferred_mode = cur_mode;
} }
} }
...@@ -2080,7 +2094,7 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing) ...@@ -2080,7 +2094,7 @@ drm_est3_modes(struct drm_connector *connector, struct detailed_timing *timing)
u8 *est = ((u8 *)timing) + 5; u8 *est = ((u8 *)timing) + 5;
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
for (j = 7; j > 0; j--) { for (j = 7; j >= 0; j--) {
m = (i * 8) + (7 - j); m = (i * 8) + (7 - j);
if (m >= ARRAY_SIZE(est3_modes)) if (m >= ARRAY_SIZE(est3_modes))
break; break;
...@@ -3473,6 +3487,19 @@ int drm_add_modes_noedid(struct drm_connector *connector, ...@@ -3473,6 +3487,19 @@ int drm_add_modes_noedid(struct drm_connector *connector,
} }
EXPORT_SYMBOL(drm_add_modes_noedid); EXPORT_SYMBOL(drm_add_modes_noedid);
void drm_set_preferred_mode(struct drm_connector *connector,
int hpref, int vpref)
{
struct drm_display_mode *mode;
list_for_each_entry(mode, &connector->probed_modes, head) {
if (drm_mode_width(mode) == hpref &&
drm_mode_height(mode) == vpref)
mode->type |= DRM_MODE_TYPE_PREFERRED;
}
}
EXPORT_SYMBOL(drm_set_preferred_mode);
/** /**
* drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with * drm_hdmi_avi_infoframe_from_display_mode() - fill an HDMI AVI infoframe with
* data from a DRM display mode * data from a DRM display mode
......
...@@ -239,7 +239,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp, ...@@ -239,7 +239,8 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
priv->ioctl_count = 0; priv->ioctl_count = 0;
/* for compatibility root is always authenticated */ /* for compatibility root is always authenticated */
priv->authenticated = capable(CAP_SYS_ADMIN); priv->always_authenticated = capable(CAP_SYS_ADMIN);
priv->authenticated = priv->always_authenticated;
priv->lock_count = 0; priv->lock_count = 0;
INIT_LIST_HEAD(&priv->lhead); INIT_LIST_HEAD(&priv->lhead);
...@@ -378,8 +379,10 @@ static void drm_events_release(struct drm_file *file_priv) ...@@ -378,8 +379,10 @@ static void drm_events_release(struct drm_file *file_priv)
} }
/* Remove unconsumed events */ /* Remove unconsumed events */
list_for_each_entry_safe(e, et, &file_priv->event_list, link) list_for_each_entry_safe(e, et, &file_priv->event_list, link) {
list_del(&e->link);
e->destroy(e); e->destroy(e);
}
spin_unlock_irqrestore(&dev->event_lock, flags); spin_unlock_irqrestore(&dev->event_lock, flags);
} }
...@@ -531,7 +534,7 @@ int drm_release(struct inode *inode, struct file *filp) ...@@ -531,7 +534,7 @@ int drm_release(struct inode *inode, struct file *filp)
list_for_each_entry(temp, &dev->filelist, lhead) { list_for_each_entry(temp, &dev->filelist, lhead) {
if ((temp->master == file_priv->master) && if ((temp->master == file_priv->master) &&
(temp != file_priv)) (temp != file_priv))
temp->authenticated = 0; temp->authenticated = temp->always_authenticated;
} }
/** /**
......
...@@ -219,7 +219,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs) ...@@ -219,7 +219,7 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
for (i = 0; i < num_crtcs; i++) for (i = 0; i < num_crtcs; i++)
init_waitqueue_head(&dev->vblank[i].queue); init_waitqueue_head(&dev->vblank[i].queue);
DRM_INFO("Supports vblank timestamp caching Rev 1 (10.10.2010).\n"); DRM_INFO("Supports vblank timestamp caching Rev 2 (21.10.2013).\n");
/* Driver specific high-precision vblank timestamping supported? */ /* Driver specific high-precision vblank timestamping supported? */
if (dev->driver->get_vblank_timestamp) if (dev->driver->get_vblank_timestamp)
...@@ -586,24 +586,20 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -586,24 +586,20 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
* code gets preempted or delayed for some reason. * code gets preempted or delayed for some reason.
*/ */
for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) { for (i = 0; i < DRM_TIMESTAMP_MAXRETRIES; i++) {
/* Disable preemption to make it very likely to /*
* succeed in the first iteration even on PREEMPT_RT kernel. * Get vertical and horizontal scanout position vpos, hpos,
* and bounding timestamps stime, etime, pre/post query.
*/ */
preempt_disable(); vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos,
&hpos, &stime, &etime);
/* Get system timestamp before query. */ /*
stime = ktime_get(); * Get correction for CLOCK_MONOTONIC -> CLOCK_REALTIME if
* CLOCK_REALTIME is requested.
/* Get vertical and horizontal scanout pos. vpos, hpos. */ */
vbl_status = dev->driver->get_scanout_position(dev, crtc, &vpos, &hpos);
/* Get system timestamp after query. */
etime = ktime_get();
if (!drm_timestamp_monotonic) if (!drm_timestamp_monotonic)
mono_time_offset = ktime_get_monotonic_offset(); mono_time_offset = ktime_get_monotonic_offset();
preempt_enable();
/* Return as no-op if scanout query unsupported or failed. */ /* Return as no-op if scanout query unsupported or failed. */
if (!(vbl_status & DRM_SCANOUTPOS_VALID)) { if (!(vbl_status & DRM_SCANOUTPOS_VALID)) {
DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n", DRM_DEBUG("crtc %d : scanoutpos query failed [%d].\n",
...@@ -611,6 +607,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc, ...@@ -611,6 +607,7 @@ int drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev, int crtc,
return -EIO; return -EIO;
} }
/* Compute uncertainty in timestamp of scanout position query. */
duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime); duration_ns = ktime_to_ns(etime) - ktime_to_ns(stime);
/* Accept result with < max_error nsecs timing uncertainty. */ /* Accept result with < max_error nsecs timing uncertainty. */
......
...@@ -1041,7 +1041,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector) ...@@ -1041,7 +1041,7 @@ void drm_mode_connector_list_update(struct drm_connector *connector)
/* if equal delete the probed mode */ /* if equal delete the probed mode */
mode->status = pmode->status; mode->status = pmode->status;
/* Merge type bits together */ /* Merge type bits together */
mode->type |= pmode->type; mode->type = pmode->type;
list_del(&pmode->head); list_del(&pmode->head);
drm_mode_destroy(connector->dev, pmode); drm_mode_destroy(connector->dev, pmode);
break; break;
......
...@@ -80,7 +80,7 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali ...@@ -80,7 +80,7 @@ drm_dma_handle_t *drm_pci_alloc(struct drm_device * dev, size_t size, size_t ali
/* Reserve */ /* Reserve */
for (addr = (unsigned long)dmah->vaddr, sz = size; for (addr = (unsigned long)dmah->vaddr, sz = size;
sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
SetPageReserved(virt_to_page(addr)); SetPageReserved(virt_to_page((void *)addr));
} }
return dmah; return dmah;
...@@ -103,7 +103,7 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah) ...@@ -103,7 +103,7 @@ void __drm_pci_free(struct drm_device * dev, drm_dma_handle_t * dmah)
/* Unreserve */ /* Unreserve */
for (addr = (unsigned long)dmah->vaddr, sz = dmah->size; for (addr = (unsigned long)dmah->vaddr, sz = dmah->size;
sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) { sz > 0; addr += PAGE_SIZE, sz -= PAGE_SIZE) {
ClearPageReserved(virt_to_page(addr)); ClearPageReserved(virt_to_page((void *)addr));
} }
dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr, dma_free_coherent(&dev->pdev->dev, dmah->size, dmah->vaddr,
dmah->busaddr); dmah->busaddr);
......
此差异已折叠。
此差异已折叠。
...@@ -301,7 +301,7 @@ static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) ...@@ -301,7 +301,7 @@ static int drm_do_vm_dma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
offset = (unsigned long)vmf->virtual_address - vma->vm_start; /* vm_[pg]off[set] should be 0 */ offset = (unsigned long)vmf->virtual_address - vma->vm_start; /* vm_[pg]off[set] should be 0 */
page_nr = offset >> PAGE_SHIFT; /* page_nr could just be vmf->pgoff */ page_nr = offset >> PAGE_SHIFT; /* page_nr could just be vmf->pgoff */
page = virt_to_page((dma->pagelist[page_nr] + (offset & (~PAGE_MASK)))); page = virt_to_page((void *)dma->pagelist[page_nr]);
get_page(page); get_page(page);
vmf->page = page; vmf->page = page;
......
...@@ -634,6 +634,7 @@ const struct psb_ops cdv_chip_ops = { ...@@ -634,6 +634,7 @@ const struct psb_ops cdv_chip_ops = {
.crtcs = 2, .crtcs = 2,
.hdmi_mask = (1 << 0) | (1 << 1), .hdmi_mask = (1 << 0) | (1 << 1),
.lvds_mask = (1 << 1), .lvds_mask = (1 << 1),
.sdvo_mask = (1 << 0),
.cursor_needs_phys = 0, .cursor_needs_phys = 0,
.sgx_offset = MRST_SGX_OFFSET, .sgx_offset = MRST_SGX_OFFSET,
.chip_setup = cdv_chip_setup, .chip_setup = cdv_chip_setup,
......
...@@ -666,7 +666,7 @@ cdv_intel_dp_i2c_init(struct gma_connector *connector, ...@@ -666,7 +666,7 @@ cdv_intel_dp_i2c_init(struct gma_connector *connector,
strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1); strncpy (intel_dp->adapter.name, name, sizeof(intel_dp->adapter.name) - 1);
intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0'; intel_dp->adapter.name[sizeof(intel_dp->adapter.name) - 1] = '\0';
intel_dp->adapter.algo_data = &intel_dp->algo; intel_dp->adapter.algo_data = &intel_dp->algo;
intel_dp->adapter.dev.parent = &connector->base.kdev; intel_dp->adapter.dev.parent = connector->base.kdev;
if (is_edp(encoder)) if (is_edp(encoder))
cdv_intel_edp_panel_vdd_on(encoder); cdv_intel_edp_panel_vdd_on(encoder);
......
...@@ -714,7 +714,7 @@ static void psb_setup_outputs(struct drm_device *dev) ...@@ -714,7 +714,7 @@ static void psb_setup_outputs(struct drm_device *dev)
clone_mask = (1 << INTEL_OUTPUT_ANALOG); clone_mask = (1 << INTEL_OUTPUT_ANALOG);
break; break;
case INTEL_OUTPUT_SDVO: case INTEL_OUTPUT_SDVO:
crtc_mask = ((1 << 0) | (1 << 1)); crtc_mask = dev_priv->ops->sdvo_mask;
clone_mask = (1 << INTEL_OUTPUT_SDVO); clone_mask = (1 << INTEL_OUTPUT_SDVO);
break; break;
case INTEL_OUTPUT_LVDS: case INTEL_OUTPUT_LVDS:
......
...@@ -40,6 +40,9 @@ static int oaktrail_output_init(struct drm_device *dev) ...@@ -40,6 +40,9 @@ static int oaktrail_output_init(struct drm_device *dev)
dev_err(dev->dev, "DSI is not supported\n"); dev_err(dev->dev, "DSI is not supported\n");
if (dev_priv->hdmi_priv) if (dev_priv->hdmi_priv)
oaktrail_hdmi_init(dev, &dev_priv->mode_dev); oaktrail_hdmi_init(dev, &dev_priv->mode_dev);
psb_intel_sdvo_init(dev, SDVOB);
return 0; return 0;
} }
...@@ -526,6 +529,7 @@ static int oaktrail_chip_setup(struct drm_device *dev) ...@@ -526,6 +529,7 @@ static int oaktrail_chip_setup(struct drm_device *dev)
psb_intel_opregion_init(dev); psb_intel_opregion_init(dev);
psb_intel_init_bios(dev); psb_intel_init_bios(dev);
} }
gma_intel_setup_gmbus(dev);
oaktrail_hdmi_setup(dev); oaktrail_hdmi_setup(dev);
return 0; return 0;
} }
...@@ -534,6 +538,7 @@ static void oaktrail_teardown(struct drm_device *dev) ...@@ -534,6 +538,7 @@ static void oaktrail_teardown(struct drm_device *dev)
{ {
struct drm_psb_private *dev_priv = dev->dev_private; struct drm_psb_private *dev_priv = dev->dev_private;
gma_intel_teardown_gmbus(dev);
oaktrail_hdmi_teardown(dev); oaktrail_hdmi_teardown(dev);
if (!dev_priv->has_gct) if (!dev_priv->has_gct)
psb_intel_destroy_bios(dev); psb_intel_destroy_bios(dev);
...@@ -546,6 +551,7 @@ const struct psb_ops oaktrail_chip_ops = { ...@@ -546,6 +551,7 @@ const struct psb_ops oaktrail_chip_ops = {
.crtcs = 2, .crtcs = 2,
.hdmi_mask = (1 << 1), .hdmi_mask = (1 << 1),
.lvds_mask = (1 << 0), .lvds_mask = (1 << 0),
.sdvo_mask = (1 << 1),
.cursor_needs_phys = 0, .cursor_needs_phys = 0,
.sgx_offset = MRST_SGX_OFFSET, .sgx_offset = MRST_SGX_OFFSET,
......
...@@ -373,6 +373,7 @@ const struct psb_ops psb_chip_ops = { ...@@ -373,6 +373,7 @@ const struct psb_ops psb_chip_ops = {
.crtcs = 2, .crtcs = 2,
.hdmi_mask = (1 << 0), .hdmi_mask = (1 << 0),
.lvds_mask = (1 << 1), .lvds_mask = (1 << 1),
.sdvo_mask = (1 << 0),
.cursor_needs_phys = 1, .cursor_needs_phys = 1,
.sgx_offset = PSB_SGX_OFFSET, .sgx_offset = PSB_SGX_OFFSET,
.chip_setup = psb_chip_setup, .chip_setup = psb_chip_setup,
......
此差异已折叠。
此差异已折叠。
...@@ -572,7 +572,7 @@ int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data, ...@@ -572,7 +572,7 @@ int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
if (!drmmode_obj) { if (!drmmode_obj) {
dev_err(dev->dev, "no such CRTC id\n"); dev_err(dev->dev, "no such CRTC id\n");
return -EINVAL; return -ENOENT;
} }
crtc = to_gma_crtc(obj_to_crtc(drmmode_obj)); crtc = to_gma_crtc(obj_to_crtc(drmmode_obj));
......
此差异已折叠。
此差异已折叠。
...@@ -1486,7 +1486,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -1486,7 +1486,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
spin_lock_init(&dev_priv->irq_lock); spin_lock_init(&dev_priv->irq_lock);
spin_lock_init(&dev_priv->gpu_error.lock); spin_lock_init(&dev_priv->gpu_error.lock);
spin_lock_init(&dev_priv->backlight.lock); spin_lock_init(&dev_priv->backlight_lock);
spin_lock_init(&dev_priv->uncore.lock); spin_lock_init(&dev_priv->uncore.lock);
spin_lock_init(&dev_priv->mm.object_stat_lock); spin_lock_init(&dev_priv->mm.object_stat_lock);
mutex_init(&dev_priv->dpio_lock); mutex_init(&dev_priv->dpio_lock);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册