summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/Kconfig18
-rw-r--r--drivers/gpu/drm/Makefile7
-rw-r--r--drivers/gpu/drm/drm_crtc.c247
-rw-r--r--drivers/gpu/drm/drm_crtc_helper.c10
-rw-r--r--drivers/gpu/drm/drm_drv.c4
-rw-r--r--drivers/gpu/drm/drm_edid.c5
-rw-r--r--drivers/gpu/drm/drm_edid_load.c250
-rw-r--r--drivers/gpu/drm/drm_fops.c8
-rw-r--r--drivers/gpu/drm/drm_gem.c3
-rw-r--r--drivers/gpu/drm/drm_modes.c28
-rw-r--r--drivers/gpu/drm/drm_stub.c26
-rw-r--r--drivers/gpu/drm/drm_sysfs.c7
-rw-r--r--drivers/gpu/drm/drm_usb.c2
-rw-r--r--drivers/gpu/drm/drm_vm.c3
-rw-r--r--drivers/gpu/drm/exynos/Kconfig14
-rw-r--r--drivers/gpu/drm/exynos/Makefile11
-rw-r--r--drivers/gpu/drm/exynos/exynos_ddc.c1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_buf.c191
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_buf.h22
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_connector.c51
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_core.c137
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_crtc.c18
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.c120
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_drv.h42
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.c58
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_encoder.h1
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fb.c6
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fbdev.c160
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_fimd.c54
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.c363
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_gem.h29
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.c115
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_hdmi.h5
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_plane.c8
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.c676
-rw-r--r--drivers/gpu/drm/exynos/exynos_drm_vidi.h36
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.c1437
-rw-r--r--drivers/gpu/drm/exynos/exynos_hdmi.h50
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.c76
-rw-r--r--drivers/gpu/drm/exynos/exynos_mixer.h92
-rw-r--r--drivers/gpu/drm/exynos/regs-hdmi.h488
-rw-r--r--drivers/gpu/drm/gma500/cdv_device.c171
-rw-r--r--drivers/gpu/drm/gma500/framebuffer.c3
-rw-r--r--drivers/gpu/drm/gma500/gtt.c9
-rw-r--r--drivers/gpu/drm/gma500/power.c6
-rw-r--r--drivers/gpu/drm/gma500/psb_drv.h20
-rw-r--r--drivers/gpu/drm/gma500/psb_intel_reg.h9
-rw-r--r--drivers/gpu/drm/i915/i915_drv.h19
-rw-r--r--drivers/gpu/drm/i915/i915_gem.c12
-rw-r--r--drivers/gpu/drm/i915/i915_gem_evict.c19
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h15
-rw-r--r--drivers/gpu/drm/i915/intel_bios.c2
-rw-r--r--drivers/gpu/drm/i915/intel_display.c39
-rw-r--r--drivers/gpu/drm/i915/intel_dp.c10
-rw-r--r--drivers/gpu/drm/i915/intel_i2c.c190
-rw-r--r--drivers/gpu/drm/i915/intel_lvds.c16
-rw-r--r--drivers/gpu/drm/i915/intel_modes.c5
-rw-r--r--drivers/gpu/drm/i915/intel_ringbuffer.c14
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c10
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_display.c3
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_i2c.c8
-rw-r--r--drivers/gpu/drm/nouveau/nv50_dac.c7
-rw-r--r--drivers/gpu/drm/nouveau/nv50_sor.c7
-rw-r--r--drivers/gpu/drm/radeon/atombios_dp.c3
-rw-r--r--drivers/gpu/drm/radeon/evergreen.c1
-rw-r--r--drivers/gpu/drm/radeon/evergreen_cs.c330
-rw-r--r--drivers/gpu/drm/radeon/ni.c1
-rw-r--r--drivers/gpu/drm/radeon/r100.c12
-rw-r--r--drivers/gpu/drm/radeon/r300.c8
-rw-r--r--drivers/gpu/drm/radeon/r420.c8
-rw-r--r--drivers/gpu/drm/radeon/r520.c8
-rw-r--r--drivers/gpu/drm/radeon/r600.c4
-rw-r--r--drivers/gpu/drm/radeon/r600_blit_shaders.c8
-rw-r--r--drivers/gpu/drm/radeon/r600_cs.c368
-rw-r--r--drivers/gpu/drm/radeon/r600d.h1
-rw-r--r--drivers/gpu/drm/radeon/radeon.h3
-rw-r--r--drivers/gpu/drm/radeon/radeon_atombios.c34
-rw-r--r--drivers/gpu/drm/radeon/radeon_connectors.c58
-rw-r--r--drivers/gpu/drm/radeon/radeon_cs.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_cursor.c20
-rw-r--r--drivers/gpu/drm/radeon/radeon_display.c22
-rw-r--r--drivers/gpu/drm/radeon/radeon_encoders.c6
-rw-r--r--drivers/gpu/drm/radeon/radeon_fb.c16
-rw-r--r--drivers/gpu/drm/radeon/radeon_fence.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_gart.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_gem.c26
-rw-r--r--drivers/gpu/drm/radeon/radeon_i2c.c5
-rw-r--r--drivers/gpu/drm/radeon/radeon_legacy_crtc.c4
-rw-r--r--drivers/gpu/drm/radeon/radeon_mode.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.c18
-rw-r--r--drivers/gpu/drm/radeon/radeon_object.h2
-rw-r--r--drivers/gpu/drm/radeon/radeon_ring.c7
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/cayman13
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/evergreen13
-rw-r--r--drivers/gpu/drm/radeon/reg_srcs/r6001
-rw-r--r--drivers/gpu/drm/radeon/rs400.c8
-rw-r--r--drivers/gpu/drm/radeon/rs600.c12
-rw-r--r--drivers/gpu/drm/radeon/rs690.c8
-rw-r--r--drivers/gpu/drm/radeon/rv515.c8
-rw-r--r--drivers/gpu/drm/radeon/rv770.c1
-rw-r--r--drivers/gpu/drm/savage/savage_state.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_agp_backend.c4
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo.c72
-rw-r--r--drivers/gpu/drm/ttm/ttm_bo_vm.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_memory.c12
-rw-r--r--drivers/gpu/drm/ttm/ttm_object.c5
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc.c55
-rw-r--r--drivers/gpu/drm/ttm/ttm_page_alloc_dma.c60
-rw-r--r--drivers/gpu/drm/ttm/ttm_tt.c8
-rw-r--r--drivers/gpu/drm/udl/Kconfig12
-rw-r--r--drivers/gpu/drm/udl/Makefile6
-rw-r--r--drivers/gpu/drm/udl/udl_connector.c141
-rw-r--r--drivers/gpu/drm/udl/udl_drv.c99
-rw-r--r--drivers/gpu/drm/udl/udl_drv.h141
-rw-r--r--drivers/gpu/drm/udl/udl_encoder.c80
-rw-r--r--drivers/gpu/drm/udl/udl_fb.c611
-rw-r--r--drivers/gpu/drm/udl/udl_gem.c227
-rw-r--r--drivers/gpu/drm/udl/udl_main.c338
-rw-r--r--drivers/gpu/drm/udl/udl_modeset.c414
-rw-r--r--drivers/gpu/drm/udl/udl_transfer.c253
120 files changed, 7497 insertions, 1589 deletions
diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 8ea3bca22ee2..cc1148837e24 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -18,6 +18,11 @@ menuconfig DRM
details. You should also select and configure AGP
(/dev/agpgart) support if it is available for your platform.
+config DRM_USB
+ tristate
+ depends on DRM
+ select USB
+
config DRM_KMS_HELPER
tristate
depends on DRM
@@ -27,6 +32,18 @@ config DRM_KMS_HELPER
help
FB and CRTC helpers for KMS drivers.
+config DRM_LOAD_EDID_FIRMWARE
+ bool "Allow to specify an EDID data set instead of probing for it"
+ depends on DRM_KMS_HELPER
+ help
+ Say Y here, if you want to use EDID data to be loaded from the
+ /lib/firmware directory or one of the provided built-in
+ data sets. This may be necessary, if the graphics adapter or
+ monitor are unable to provide appropriate EDID data. Since this
+ feature is provided as a workaround for broken hardware, the
+ default case is N. Details and instructions how to build your own
+ EDID data are given in Documentation/EDID/HOWTO.txt.
+
config DRM_TTM
tristate
depends on DRM
@@ -167,3 +184,4 @@ source "drivers/gpu/drm/vmwgfx/Kconfig"
source "drivers/gpu/drm/gma500/Kconfig"
+source "drivers/gpu/drm/udl/Kconfig"
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
index 0cde1b80fdb1..a858532806ae 100644
--- a/drivers/gpu/drm/Makefile
+++ b/drivers/gpu/drm/Makefile
@@ -12,17 +12,21 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \
drm_crtc.o drm_modes.o drm_edid.o \
drm_info.o drm_debugfs.o drm_encoder_slave.o \
- drm_trace_points.o drm_global.o drm_usb.o
+ drm_trace_points.o drm_global.o
drm-$(CONFIG_COMPAT) += drm_ioc32.o
+drm-usb-y := drm_usb.o
+
drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
+drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
CFLAGS_drm_trace_points.o := -I$(src)
obj-$(CONFIG_DRM) += drm.o
+obj-$(CONFIG_DRM_USB) += drm_usb.o
obj-$(CONFIG_DRM_TTM) += ttm/
obj-$(CONFIG_DRM_TDFX) += tdfx/
obj-$(CONFIG_DRM_R128) += r128/
@@ -37,4 +41,5 @@ obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-$(CONFIG_DRM_GMA500) += gma500/
+obj-$(CONFIG_DRM_UDL) += udl/
obj-y += i2c/
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 6fdaf6fe94eb..d3aaeb6ae236 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -293,9 +293,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
int ret;
ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
- if (ret) {
+ if (ret)
return ret;
- }
fb->dev = dev;
fb->funcs = funcs;
@@ -365,19 +364,31 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
* Caller must hold mode config lock.
*
* Inits a new object created as base part of an driver crtc object.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
*/
-void drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
+int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs)
{
+ int ret;
+
crtc->dev = dev;
crtc->funcs = funcs;
mutex_lock(&dev->mode_config.mutex);
- drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+
+ ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
+ if (ret)
+ goto out;
list_add_tail(&crtc->head, &dev->mode_config.crtc_list);
dev->mode_config.num_crtc++;
+
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_crtc_init);
@@ -453,17 +464,25 @@ EXPORT_SYMBOL(drm_mode_remove);
*
* Initialises a preallocated connector. Connectors should be
* subclassed as part of driver connector objects.
+ *
+ * RETURNS:
+ * Zero on success, error code on failure.
*/
-void drm_connector_init(struct drm_device *dev,
- struct drm_connector *connector,
- const struct drm_connector_funcs *funcs,
- int connector_type)
+int drm_connector_init(struct drm_device *dev,
+ struct drm_connector *connector,
+ const struct drm_connector_funcs *funcs,
+ int connector_type)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
+ ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
+ if (ret)
+ goto out;
+
connector->dev = dev;
connector->funcs = funcs;
- drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR);
connector->connector_type = connector_type;
connector->connector_type_id =
++drm_connector_enum_list[connector_type].count; /* TODO */
@@ -483,7 +502,10 @@ void drm_connector_init(struct drm_device *dev,
drm_connector_attach_property(connector,
dev->mode_config.dpms_property, 0);
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_connector_init);
@@ -518,23 +540,41 @@ void drm_connector_cleanup(struct drm_connector *connector)
}
EXPORT_SYMBOL(drm_connector_cleanup);
-void drm_encoder_init(struct drm_device *dev,
+void drm_connector_unplug_all(struct drm_device *dev)
+{
+ struct drm_connector *connector;
+
+ /* taking the mode config mutex ends up in a clash with sysfs */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ drm_sysfs_connector_remove(connector);
+
+}
+EXPORT_SYMBOL(drm_connector_unplug_all);
+
+int drm_encoder_init(struct drm_device *dev,
struct drm_encoder *encoder,
const struct drm_encoder_funcs *funcs,
int encoder_type)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
- encoder->dev = dev;
+ ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ if (ret)
+ goto out;
- drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER);
+ encoder->dev = dev;
encoder->encoder_type = encoder_type;
encoder->funcs = funcs;
list_add_tail(&encoder->head, &dev->mode_config.encoder_list);
dev->mode_config.num_encoder++;
+ out:
mutex_unlock(&dev->mode_config.mutex);
+
+ return ret;
}
EXPORT_SYMBOL(drm_encoder_init);
@@ -555,18 +595,23 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
const uint32_t *formats, uint32_t format_count,
bool priv)
{
+ int ret;
+
mutex_lock(&dev->mode_config.mutex);
+ ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
+ if (ret)
+ goto out;
+
plane->dev = dev;
- drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
plane->funcs = funcs;
plane->format_types = kmalloc(sizeof(uint32_t) * format_count,
GFP_KERNEL);
if (!plane->format_types) {
DRM_DEBUG_KMS("out of memory when allocating plane\n");
drm_mode_object_put(dev, &plane->base);
- mutex_unlock(&dev->mode_config.mutex);
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto out;
}
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
@@ -584,9 +629,10 @@ int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
INIT_LIST_HEAD(&plane->head);
}
+ out:
mutex_unlock(&dev->mode_config.mutex);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(drm_plane_init);
@@ -626,7 +672,11 @@ struct drm_display_mode *drm_mode_create(struct drm_device *dev)
if (!nmode)
return NULL;
- drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE);
+ if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) {
+ kfree(nmode);
+ return NULL;
+ }
+
return nmode;
}
EXPORT_SYMBOL(drm_mode_create);
@@ -643,6 +693,9 @@ EXPORT_SYMBOL(drm_mode_create);
*/
void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode)
{
+ if (!mode)
+ return;
+
drm_mode_object_put(dev, &mode->base);
kfree(mode);
@@ -933,6 +986,7 @@ int drm_mode_group_init_legacy_group(struct drm_device *dev,
return 0;
}
+EXPORT_SYMBOL(drm_mode_group_init_legacy_group);
/**
* drm_mode_config_cleanup - free up DRM mode_config info
@@ -999,9 +1053,16 @@ EXPORT_SYMBOL(drm_mode_config_cleanup);
* Convert a drm_display_mode into a drm_mode_modeinfo structure to return to
* the user.
*/
-void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
- struct drm_display_mode *in)
+static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
+ const struct drm_display_mode *in)
{
+ WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX ||
+ in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX ||
+ in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX ||
+ in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX ||
+ in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX,
+ "timing values too large for mode info\n");
+
out->clock = in->clock;
out->hdisplay = in->hdisplay;
out->hsync_start = in->hsync_start;
@@ -1030,10 +1091,16 @@ void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
*
* Convert a drm_mode_modeinfo into a drm_display_mode structure to return to
* the caller.
+ *
+ * RETURNS:
+ * Zero on success, errno on failure.
*/
-void drm_crtc_convert_umode(struct drm_display_mode *out,
- struct drm_mode_modeinfo *in)
+static int drm_crtc_convert_umode(struct drm_display_mode *out,
+ const struct drm_mode_modeinfo *in)
{
+ if (in->clock > INT_MAX || in->vrefresh > INT_MAX)
+ return -ERANGE;
+
out->clock = in->clock;
out->hdisplay = in->hdisplay;
out->hsync_start = in->hsync_start;
@@ -1050,6 +1117,8 @@ void drm_crtc_convert_umode(struct drm_display_mode *out,
out->type = in->type;
strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN);
out->name[DRM_DISPLAY_MODE_LEN-1] = 0;
+
+ return 0;
}
/**
@@ -1755,7 +1824,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
struct drm_mode_config *config = &dev->mode_config;
struct drm_mode_crtc *crtc_req = data;
struct drm_mode_object *obj;
- struct drm_crtc *crtc, *crtcfb;
+ struct drm_crtc *crtc;
struct drm_connector **connector_set = NULL, *connector;
struct drm_framebuffer *fb = NULL;
struct drm_display_mode *mode = NULL;
@@ -1767,6 +1836,10 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
+ /* For some reason crtc x/y offsets are signed internally. */
+ if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
+ return -ERANGE;
+
mutex_lock(&dev->mode_config.mutex);
obj = drm_mode_object_find(dev, crtc_req->crtc_id,
DRM_MODE_OBJECT_CRTC);
@@ -1782,14 +1855,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
/* If we have a mode we need a framebuffer. */
/* If we pass -1, set the mode with the currently bound fb */
if (crtc_req->fb_id == -1) {
- list_for_each_entry(crtcfb,
- &dev->mode_config.crtc_list, head) {
- if (crtcfb == crtc) {
- DRM_DEBUG_KMS("Using current fb for "
- "setmode\n");
- fb = crtc->fb;
- }
+ if (!crtc->fb) {
+ DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
+ ret = -EINVAL;
+ goto out;
}
+ fb = crtc->fb;
} else {
obj = drm_mode_object_find(dev, crtc_req->fb_id,
DRM_MODE_OBJECT_FB);
@@ -1803,8 +1874,30 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
}
mode = drm_mode_create(dev);
- drm_crtc_convert_umode(mode, &crtc_req->mode);
+ if (!mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ goto out;
+ }
+
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+
+ if (mode->hdisplay > fb->width ||
+ mode->vdisplay > fb->height ||
+ crtc_req->x > fb->width - mode->hdisplay ||
+ crtc_req->y > fb->height - mode->vdisplay) {
+ DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n",
+ mode->hdisplay, mode->vdisplay,
+ crtc_req->x, crtc_req->y,
+ fb->width, fb->height);
+ ret = -ENOSPC;
+ goto out;
+ }
}
if (crtc_req->count_connectors == 0 && mode) {
@@ -1872,6 +1965,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
out:
kfree(connector_set);
+ drm_mode_destroy(dev, mode);
mutex_unlock(&dev->mode_config.mutex);
return ret;
}
@@ -2370,38 +2464,48 @@ void drm_fb_release(struct drm_file *priv)
*
* Add @mode to @connector's user mode list.
*/
-static int drm_mode_attachmode(struct drm_device *dev,
- struct drm_connector *connector,
- struct drm_display_mode *mode)
+static void drm_mode_attachmode(struct drm_device *dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode)
{
- int ret = 0;
-
list_add_tail(&mode->head, &connector->user_modes);
- return ret;
}
int drm_mode_attachmode_crtc(struct drm_device *dev, struct drm_crtc *crtc,
- struct drm_display_mode *mode)
+ const struct drm_display_mode *mode)
{
struct drm_connector *connector;
int ret = 0;
- struct drm_display_mode *dup_mode;
- int need_dup = 0;
+ struct drm_display_mode *dup_mode, *next;
+ LIST_HEAD(list);
+
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (!connector->encoder)
- break;
+ continue;
if (connector->encoder->crtc == crtc) {
- if (need_dup)
- dup_mode = drm_mode_duplicate(dev, mode);
- else
- dup_mode = mode;
- ret = drm_mode_attachmode(dev, connector, dup_mode);
- if (ret)
- return ret;
- need_dup = 1;
+ dup_mode = drm_mode_duplicate(dev, mode);
+ if (!dup_mode) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ list_add_tail(&dup_mode->head, &list);
}
}
- return 0;
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (!connector->encoder)
+ continue;
+ if (connector->encoder->crtc == crtc)
+ list_move_tail(list.next, &connector->user_modes);
+ }
+
+ WARN_ON(!list_empty(&list));
+
+ out:
+ list_for_each_entry_safe(dup_mode, next, &list, head)
+ drm_mode_destroy(dev, dup_mode);
+
+ return ret;
}
EXPORT_SYMBOL(drm_mode_attachmode_crtc);
@@ -2480,9 +2584,14 @@ int drm_mode_attachmode_ioctl(struct drm_device *dev,
goto out;
}
- drm_crtc_convert_umode(mode, umode);
+ ret = drm_crtc_convert_umode(mode, umode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ drm_mode_destroy(dev, mode);
+ goto out;
+ }
- ret = drm_mode_attachmode(dev, connector, mode);
+ drm_mode_attachmode(dev, connector, mode);
out:
mutex_unlock(&dev->mode_config.mutex);
return ret;
@@ -2523,7 +2632,12 @@ int drm_mode_detachmode_ioctl(struct drm_device *dev,
}
connector = obj_to_connector(obj);
- drm_crtc_convert_umode(&mode, umode);
+ ret = drm_crtc_convert_umode(&mode, umode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ goto out;
+ }
+
ret = drm_mode_detachmode(dev, connector, &mode);
out:
mutex_unlock(&dev->mode_config.mutex);
@@ -2534,6 +2648,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
const char *name, int num_values)
{
struct drm_property *property = NULL;
+ int ret;
property = kzalloc(sizeof(struct drm_property), GFP_KERNEL);
if (!property)
@@ -2545,7 +2660,10 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
goto fail;
}
- drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY);
+ if (ret)
+ goto fail;
+
property->flags = flags;
property->num_values = num_values;
INIT_LIST_HEAD(&property->enum_blob_list);
@@ -2558,6 +2676,7 @@ struct drm_property *drm_property_create(struct drm_device *dev, int flags,
list_add_tail(&property->head, &dev->mode_config.property_list);
return property;
fail:
+ kfree(property->values);
kfree(property);
return NULL;
}
@@ -2821,6 +2940,7 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
void *data)
{
struct drm_property_blob *blob;
+ int ret;
if (!length || !data)
return NULL;
@@ -2829,13 +2949,16 @@ static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev
if (!blob)
return NULL;
- blob->data = (void *)((char *)blob + sizeof(struct drm_property_blob));
+ ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
+ if (ret) {
+ kfree(blob);
+ return NULL;
+ }
+
blob->length = length;
memcpy(blob->data, data, length);
- drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB);
-
list_add_tail(&blob->head, &dev->mode_config.property_blob_list);
return blob;
}
@@ -3171,6 +3294,18 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
goto out;
fb = obj_to_fb(obj);
+ if (crtc->mode.hdisplay > fb->width ||
+ crtc->mode.vdisplay > fb->height ||
+ crtc->x > fb->width - crtc->mode.hdisplay ||
+ crtc->y > fb->height - crtc->mode.vdisplay) {
+ DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n",
+ fb->width, fb->height,
+ crtc->mode.hdisplay, crtc->mode.vdisplay,
+ crtc->x, crtc->y);
+ ret = -ENOSPC;
+ goto out;
+ }
+
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
ret = -ENOMEM;
spin_lock_irqsave(&dev->event_lock, flags);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index d761d1241152..81118893264c 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -37,6 +37,7 @@
#include "drm_fourcc.h"
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
+#include "drm_edid.h"
static bool drm_kms_helper_poll = true;
module_param_named(poll, drm_kms_helper_poll, bool, 0600);
@@ -118,7 +119,12 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
goto prune;
}
- count = (*connector_funcs->get_modes)(connector);
+#ifdef CONFIG_DRM_LOAD_EDID_FIRMWARE
+ count = drm_load_edid_firmware(connector);
+ if (count == 0)
+#endif
+ count = (*connector_funcs->get_modes)(connector);
+
if (count == 0 && connector->status == connector_status_connected)
count = drm_add_modes_noedid(connector, 1024, 768);
if (count == 0)
@@ -352,6 +358,8 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
return true;
adjusted_mode = drm_mode_duplicate(dev, mode);
+ if (!adjusted_mode)
+ return false;
saved_hwmode = crtc->hwmode;
saved_mode = crtc->mode;
diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index d166bd080400..0b65fbc8a630 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -390,6 +390,10 @@ long drm_ioctl(struct file *filp,
unsigned int usize, asize;
dev = file_priv->minor->dev;
+
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
atomic_inc(&dev->ioctl_count);
atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
++file_priv->ioctl_count;
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 7ee7be1e5ce6..5a18b0df8285 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -149,8 +149,7 @@ EXPORT_SYMBOL(drm_edid_header_is_valid);
* Sanity check the EDID block (base or extension). Return 0 if the block
* doesn't check out, or 1 if it's valid.
*/
-static bool
-drm_edid_block_valid(u8 *raw_edid)
+bool drm_edid_block_valid(u8 *raw_edid)
{
int i;
u8 csum = 0;
@@ -203,6 +202,7 @@ bad:
}
return 0;
}
+EXPORT_SYMBOL(drm_edid_block_valid);
/**
* drm_edid_is_valid - sanity check EDID data
@@ -226,7 +226,6 @@ bool drm_edid_is_valid(struct edid *edid)
}
EXPORT_SYMBOL(drm_edid_is_valid);
-#define DDC_ADDR 0x50
#define DDC_SEGMENT_ADDR 0x30
/**
* Get EDID information via I2C.
diff --git a/drivers/gpu/drm/drm_edid_load.c b/drivers/gpu/drm/drm_edid_load.c
new file mode 100644
index 000000000000..da9acba2dd6c
--- /dev/null
+++ b/drivers/gpu/drm/drm_edid_load.c
@@ -0,0 +1,250 @@
+/*
+ drm_edid_load.c: use a built-in EDID data set or load it via the firmware
+ interface
+
+ Copyright (C) 2012 Carsten Emde <C.Emde@osadl.org>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <linux/module.h>
+#include <linux/firmware.h>
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "drm_edid.h"
+
+static char edid_firmware[PATH_MAX];
+module_param_string(edid_firmware, edid_firmware, sizeof(edid_firmware), 0644);
+MODULE_PARM_DESC(edid_firmware, "Do not probe monitor, use specified EDID blob "
+ "from built-in data or /lib/firmware instead. ");
+
+#define GENERIC_EDIDS 4
+static char *generic_edid_name[GENERIC_EDIDS] = {
+ "edid/1024x768.bin",
+ "edid/1280x1024.bin",
+ "edid/1680x1050.bin",
+ "edid/1920x1080.bin",
+};
+
+static u8 generic_edid[GENERIC_EDIDS][128] = {
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x23, 0x1a, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x08, 0x00, 0x61, 0x40,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x64, 0x19,
+ 0x00, 0x40, 0x41, 0x00, 0x26, 0x30, 0x08, 0x90,
+ 0x36, 0x00, 0x63, 0x0a, 0x11, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x2f, 0x31, 0x07, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x58,
+ 0x47, 0x41, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x55,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2c, 0x23, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0x81, 0x80,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x30, 0x2a,
+ 0x00, 0x98, 0x51, 0x00, 0x2a, 0x40, 0x30, 0x70,
+ 0x13, 0x00, 0xbc, 0x63, 0x11, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x3e, 0x40, 0x0b, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x53,
+ 0x58, 0x47, 0x41, 0x0a, 0x20, 0x20, 0x00, 0xa0,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x2b, 0x1b, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xb3, 0x00,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x21, 0x39,
+ 0x90, 0x30, 0x62, 0x1a, 0x27, 0x40, 0x68, 0xb0,
+ 0x36, 0x00, 0xb5, 0x11, 0x11, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x40, 0x42, 0x0f, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x57,
+ 0x53, 0x58, 0x47, 0x41, 0x0a, 0x20, 0x00, 0x26,
+ },
+ {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00,
+ 0x31, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x05, 0x16, 0x01, 0x03, 0x6d, 0x32, 0x1c, 0x78,
+ 0xea, 0x5e, 0xc0, 0xa4, 0x59, 0x4a, 0x98, 0x25,
+ 0x20, 0x50, 0x54, 0x00, 0x00, 0x00, 0xd1, 0xc0,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x3a,
+ 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x45, 0x00, 0xf4, 0x19, 0x11, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0xff, 0x00, 0x4c, 0x69, 0x6e,
+ 0x75, 0x78, 0x20, 0x23, 0x30, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x3b,
+ 0x3d, 0x42, 0x44, 0x0f, 0x00, 0x0a, 0x20, 0x20,
+ 0x20, 0x20, 0x20, 0x20, 0x00, 0x00, 0x00, 0xfc,
+ 0x00, 0x4c, 0x69, 0x6e, 0x75, 0x78, 0x20, 0x46,
+ 0x48, 0x44, 0x0a, 0x20, 0x20, 0x20, 0x00, 0x05,
+ },
+};
+
+static int edid_load(struct drm_connector *connector, char *name,
+ char *connector_name)
+{
+ const struct firmware *fw;
+ struct platform_device *pdev;
+ u8 *fwdata = NULL, *edid;
+ int fwsize, expected;
+ int builtin = 0, err = 0;
+ int i, valid_extensions = 0;
+
+ pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ DRM_ERROR("Failed to register EDID firmware platform device "
+ "for connector \"%s\"\n", connector_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = request_firmware(&fw, name, &pdev->dev);
+ platform_device_unregister(pdev);
+
+ if (err) {
+ i = 0;
+ while (i < GENERIC_EDIDS && strcmp(name, generic_edid_name[i]))
+ i++;
+ if (i < GENERIC_EDIDS) {
+ err = 0;
+ builtin = 1;
+ fwdata = generic_edid[i];
+ fwsize = sizeof(generic_edid[i]);
+ }
+ }
+
+ if (err) {
+ DRM_ERROR("Requesting EDID firmware \"%s\" failed (err=%d)\n",
+ name, err);
+ goto out;
+ }
+
+ if (fwdata == NULL) {
+ fwdata = (u8 *) fw->data;
+ fwsize = fw->size;
+ }
+
+ expected = (fwdata[0x7e] + 1) * EDID_LENGTH;
+ if (expected != fwsize) {
+ DRM_ERROR("Size of EDID firmware \"%s\" is invalid "
+ "(expected %d, got %d)\n", name, expected, (int) fwsize);
+ err = -EINVAL;
+ goto relfw_out;
+ }
+
+ edid = kmalloc(fwsize, GFP_KERNEL);
+ if (edid == NULL) {
+ err = -ENOMEM;
+ goto relfw_out;
+ }
+ memcpy(edid, fwdata, fwsize);
+
+ if (!drm_edid_block_valid(edid)) {
+ DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
+ name);
+ kfree(edid);
+ err = -EINVAL;
+ goto relfw_out;
+ }
+
+ for (i = 1; i <= edid[0x7e]; i++) {
+ if (i != valid_extensions + 1)
+ memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
+ edid + i * EDID_LENGTH, EDID_LENGTH);
+ if (drm_edid_block_valid(edid + i * EDID_LENGTH))
+ valid_extensions++;
+ }
+
+ if (valid_extensions != edid[0x7e]) {
+ edid[EDID_LENGTH-1] += edid[0x7e] - valid_extensions;
+ DRM_INFO("Found %d valid extensions instead of %d in EDID data "
+ "\"%s\" for connector \"%s\"\n", valid_extensions,
+ edid[0x7e], name, connector_name);
+ edid[0x7e] = valid_extensions;
+ edid = krealloc(edid, (valid_extensions + 1) * EDID_LENGTH,
+ GFP_KERNEL);
+ if (edid == NULL) {
+ err = -ENOMEM;
+ goto relfw_out;
+ }
+ }
+
+ connector->display_info.raw_edid = edid;
+ DRM_INFO("Got %s EDID base block and %d extension%s from "
+ "\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
+ "external", valid_extensions, valid_extensions == 1 ? "" : "s",
+ name, connector_name);
+
+relfw_out:
+ release_firmware(fw);
+
+out:
+ return err;
+}
+
+int drm_load_edid_firmware(struct drm_connector *connector)
+{
+ char *connector_name = drm_get_connector_name(connector);
+ char *edidname = edid_firmware, *last, *colon;
+ int ret = 0;
+
+ if (*edidname == '\0')
+ return ret;
+
+ colon = strchr(edidname, ':');
+ if (colon != NULL) {
+ if (strncmp(connector_name, edidname, colon - edidname))
+ return ret;
+ edidname = colon + 1;
+ if (*edidname == '\0')
+ return ret;
+ }
+
+ last = edidname + strlen(edidname) - 1;
+ if (*last == '\n')
+ *last = '\0';
+
+ ret = edid_load(connector, edidname, connector_name);
+ if (ret)
+ return 0;
+
+ drm_mode_connector_update_edid_property(connector,
+ (struct edid *) connector->display_info.raw_edid);
+
+ return drm_add_edid_modes(connector, (struct edid *)
+ connector->display_info.raw_edid);
+}
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index 6263b0147598..7348a3dab250 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -133,6 +133,9 @@ int drm_open(struct inode *inode, struct file *filp)
if (!(dev = minor->dev))
return -ENODEV;
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
retcode = drm_open_helper(inode, filp, dev);
if (!retcode) {
atomic_inc(&dev->counts[_DRM_STAT_OPENS]);
@@ -181,6 +184,9 @@ int drm_stub_open(struct inode *inode, struct file *filp)
if (!(dev = minor->dev))
goto out;
+ if (drm_device_is_unplugged(dev))
+ goto out;
+
old_fops = filp->f_op;
filp->f_op = fops_get(dev->driver->fops);
if (filp->f_op == NULL) {
@@ -579,6 +585,8 @@ int drm_release(struct inode *inode, struct file *filp)
retcode = -EBUSY;
} else
retcode = drm_lastclose(dev);
+ if (drm_device_is_unplugged(dev))
+ drm_put_dev(dev);
}
mutex_unlock(&drm_global_mutex);
diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c
index 3ebe3c8f58b5..0ef358e53245 100644
--- a/drivers/gpu/drm/drm_gem.c
+++ b/drivers/gpu/drm/drm_gem.c
@@ -661,6 +661,9 @@ int drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_hash_item *hash;
int ret = 0;
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
mutex_lock(&dev->struct_mutex);
if (drm_ht_find_item(&mm->offset_hash, vma->vm_pgoff, &hash)) {
diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
index 7ff13bc47ca2..b7adb4a967fd 100644
--- a/drivers/gpu/drm/drm_modes.c
+++ b/drivers/gpu/drm/drm_modes.c
@@ -714,6 +714,27 @@ EXPORT_SYMBOL(drm_mode_set_crtcinfo);
/**
+ * drm_mode_copy - copy the mode
+ * @dst: mode to overwrite
+ * @src: mode to copy
+ *
+ * LOCKING:
+ * None.
+ *
+ * Copy an existing mode into another mode, preserving the object id
+ * of the destination mode.
+ */
+void drm_mode_copy(struct drm_display_mode *dst, const struct drm_display_mode *src)
+{
+ int id = dst->base.id;
+
+ *dst = *src;
+ dst->base.id = id;
+ INIT_LIST_HEAD(&dst->head);
+}
+EXPORT_SYMBOL(drm_mode_copy);
+
+/**
* drm_mode_duplicate - allocate and duplicate an existing mode
* @m: mode to duplicate
*
@@ -727,16 +748,13 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev,
const struct drm_display_mode *mode)
{
struct drm_display_mode *nmode;
- int new_id;
nmode = drm_mode_create(dev);
if (!nmode)
return NULL;
- new_id = nmode->base.id;
- *nmode = *mode;
- nmode->base.id = new_id;
- INIT_LIST_HEAD(&nmode->head);
+ drm_mode_copy(nmode, mode);
+
return nmode;
}
EXPORT_SYMBOL(drm_mode_duplicate);
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index 6d7b083c5b77..aa454f80e109 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -319,6 +319,7 @@ int drm_fill_in_dev(struct drm_device *dev,
drm_lastclose(dev);
return retcode;
}
+EXPORT_SYMBOL(drm_fill_in_dev);
/**
@@ -397,6 +398,7 @@ err_idr:
*minor = NULL;
return ret;
}
+EXPORT_SYMBOL(drm_get_minor);
/**
* Put a secondary minor number.
@@ -428,6 +430,12 @@ int drm_put_minor(struct drm_minor **minor_p)
*minor_p = NULL;
return 0;
}
+EXPORT_SYMBOL(drm_put_minor);
+
+static void drm_unplug_minor(struct drm_minor *minor)
+{
+ drm_sysfs_device_remove(minor);
+}
/**
* Called via drm_exit() at module unload time or when pci device is
@@ -492,3 +500,21 @@ void drm_put_dev(struct drm_device *dev)
kfree(dev);
}
EXPORT_SYMBOL(drm_put_dev);
+
+void drm_unplug_dev(struct drm_device *dev)
+{
+ /* for a USB device */
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ drm_unplug_minor(dev->control);
+ drm_unplug_minor(dev->primary);
+
+ mutex_lock(&drm_global_mutex);
+
+ drm_device_set_unplugged(dev);
+
+ if (dev->open_count == 0) {
+ drm_put_dev(dev);
+ }
+ mutex_unlock(&drm_global_mutex);
+}
+EXPORT_SYMBOL(drm_unplug_dev);
diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c
index 62c3675045ac..5a7bd51fc3d8 100644
--- a/drivers/gpu/drm/drm_sysfs.c
+++ b/drivers/gpu/drm/drm_sysfs.c
@@ -454,6 +454,8 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
{
int i;
+ if (!connector->kdev.parent)
+ return;
DRM_DEBUG("removing \"%s\" from sysfs\n",
drm_get_connector_name(connector));
@@ -461,6 +463,7 @@ void drm_sysfs_connector_remove(struct drm_connector *connector)
device_remove_file(&connector->kdev, &connector_attrs[i]);
sysfs_remove_bin_file(&connector->kdev.kobj, &edid_attr);
device_unregister(&connector->kdev);
+ connector->kdev.parent = NULL;
}
EXPORT_SYMBOL(drm_sysfs_connector_remove);
@@ -533,7 +536,9 @@ err_out:
*/
void drm_sysfs_device_remove(struct drm_minor *minor)
{
- device_unregister(&minor->kdev);
+ if (minor->kdev.parent)
+ device_unregister(&minor->kdev);
+ minor->kdev.parent = NULL;
}
diff --git a/drivers/gpu/drm/drm_usb.c b/drivers/gpu/drm/drm_usb.c
index 445003f4dc93..c8c83dad2ce1 100644
--- a/drivers/gpu/drm/drm_usb.c
+++ b/drivers/gpu/drm/drm_usb.c
@@ -2,7 +2,6 @@
#include <linux/usb.h>
#include <linux/export.h>
-#ifdef CONFIG_USB
int drm_get_usb_dev(struct usb_interface *interface,
const struct usb_device_id *id,
struct drm_driver *driver)
@@ -115,4 +114,3 @@ void drm_usb_exit(struct drm_driver *driver,
usb_deregister(udriver);
}
EXPORT_SYMBOL(drm_usb_exit);
-#endif
diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index 55cd61567812..149561818349 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -680,6 +680,9 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
struct drm_device *dev = priv->minor->dev;
int ret;
+ if (drm_device_is_unplugged(dev))
+ return -ENODEV;
+
mutex_lock(&dev->struct_mutex);
ret = drm_mmap_locked(filp, vma);
mutex_unlock(&dev->struct_mutex);
diff --git a/drivers/gpu/drm/exynos/Kconfig b/drivers/gpu/drm/exynos/Kconfig
index b9e5266c341b..3343ac437fe5 100644
--- a/drivers/gpu/drm/exynos/Kconfig
+++ b/drivers/gpu/drm/exynos/Kconfig
@@ -1,7 +1,6 @@
config DRM_EXYNOS
tristate "DRM Support for Samsung SoC EXYNOS Series"
depends on DRM && PLAT_SAMSUNG
- default n
select DRM_KMS_HELPER
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -12,16 +11,19 @@ config DRM_EXYNOS
If M is selected the module will be called exynosdrm.
config DRM_EXYNOS_FIMD
- tristate "Exynos DRM FIMD"
+ bool "Exynos DRM FIMD"
depends on DRM_EXYNOS && !FB_S3C
- default n
help
Choose this option if you want to use Exynos FIMD for DRM.
- If M is selected, the module will be called exynos_drm_fimd
config DRM_EXYNOS_HDMI
- tristate "Exynos DRM HDMI"
+ bool "Exynos DRM HDMI"
depends on DRM_EXYNOS && !VIDEO_SAMSUNG_S5P_TV
help
Choose this option if you want to use Exynos HDMI for DRM.
- If M is selected, the module will be called exynos_drm_hdmi
+
+config DRM_EXYNOS_VIDI
+ bool "Exynos DRM Virtual Display"
+ depends on DRM_EXYNOS
+ help
+ Choose this option if you want to use Exynos VIDI for DRM.
diff --git a/drivers/gpu/drm/exynos/Makefile b/drivers/gpu/drm/exynos/Makefile
index 395e69c9a96e..9e0bff8badf9 100644
--- a/drivers/gpu/drm/exynos/Makefile
+++ b/drivers/gpu/drm/exynos/Makefile
@@ -8,7 +8,10 @@ exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.o \
exynos_drm_plane.o
-obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
-obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
-obj-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o exynos_ddc.o \
- exynos_hdmiphy.o exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_HDMI) += exynos_hdmi.o exynos_mixer.o \
+ exynos_ddc.o exynos_hdmiphy.o \
+ exynos_drm_hdmi.o
+exynosdrm-$(CONFIG_DRM_EXYNOS_VIDI) += exynos_drm_vidi.o
+
+obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
diff --git a/drivers/gpu/drm/exynos/exynos_ddc.c b/drivers/gpu/drm/exynos/exynos_ddc.c
index 84b614fe26fd..7e1051d07f1f 100644
--- a/drivers/gpu/drm/exynos/exynos_ddc.c
+++ b/drivers/gpu/drm/exynos/exynos_ddc.c
@@ -55,4 +55,3 @@ struct i2c_driver ddc_driver = {
.remove = __devexit_p(s5p_ddc_remove),
.command = NULL,
};
-EXPORT_SYMBOL(ddc_driver);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.c b/drivers/gpu/drm/exynos/exynos_drm_buf.c
index 3cf785c58186..4a3a5f72ed4a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_buf.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_buf.c
@@ -25,45 +25,161 @@
#include "drmP.h"
#include "drm.h"
+#include "exynos_drm.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"
static int lowlevel_buffer_allocate(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
+ unsigned int flags, struct exynos_drm_gem_buf *buf)
{
+ dma_addr_t start_addr, end_addr;
+ unsigned int npages, page_size, i = 0;
+ struct scatterlist *sgl;
+ int ret = 0;
+
DRM_DEBUG_KMS("%s\n", __FILE__);
- buffer->kvaddr = dma_alloc_writecombine(dev->dev, buffer->size,
- &buffer->dma_addr, GFP_KERNEL);
- if (!buffer->kvaddr) {
- DRM_ERROR("failed to allocate buffer.\n");
+ if (flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support allocation type.\n");
+ return -EINVAL;
+ }
+
+ if (buf->dma_addr) {
+ DRM_DEBUG_KMS("already allocated.\n");
+ return 0;
+ }
+
+ if (buf->size >= SZ_1M) {
+ npages = (buf->size >> SECTION_SHIFT) + 1;
+ page_size = SECTION_SIZE;
+ } else if (buf->size >= SZ_64K) {
+ npages = (buf->size >> 16) + 1;
+ page_size = SZ_64K;
+ } else {
+ npages = (buf->size >> PAGE_SHIFT) + 1;
+ page_size = PAGE_SIZE;
+ }
+
+ buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!buf->sgt) {
+ DRM_ERROR("failed to allocate sg table.\n");
return -ENOMEM;
}
- DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
- (unsigned long)buffer->kvaddr,
- (unsigned long)buffer->dma_addr,
- buffer->size);
+ ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize sg table.\n");
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+ return -ENOMEM;
+ }
- return 0;
+ buf->kvaddr = dma_alloc_writecombine(dev->dev, buf->size,
+ &buf->dma_addr, GFP_KERNEL);
+ if (!buf->kvaddr) {
+ DRM_ERROR("failed to allocate buffer.\n");
+ ret = -ENOMEM;
+ goto err1;
+ }
+
+ start_addr = buf->dma_addr;
+ end_addr = buf->dma_addr + buf->size;
+
+ buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
+ if (!buf->pages) {
+ DRM_ERROR("failed to allocate pages.\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ start_addr = buf->dma_addr;
+ end_addr = buf->dma_addr + buf->size;
+
+ buf->pages = kzalloc(sizeof(struct page) * npages, GFP_KERNEL);
+ if (!buf->pages) {
+ DRM_ERROR("failed to allocate pages.\n");
+ ret = -ENOMEM;
+ goto err2;
+ }
+
+ sgl = buf->sgt->sgl;
+
+ while (i < npages) {
+ buf->pages[i] = phys_to_page(start_addr);
+ sg_set_page(sgl, buf->pages[i], page_size, 0);
+ sg_dma_address(sgl) = start_addr;
+ start_addr += page_size;
+ if (end_addr - start_addr < page_size)
+ break;
+ sgl = sg_next(sgl);
+ i++;
+ }
+
+ buf->pages[i] = phys_to_page(start_addr);
+
+ sgl = sg_next(sgl);
+ sg_set_page(sgl, buf->pages[i+1], end_addr - start_addr, 0);
+
+ DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)buf->kvaddr,
+ (unsigned long)buf->dma_addr,
+ buf->size);
+
+ return ret;
+err2:
+ dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
+ (dma_addr_t)buf->dma_addr);
+ buf->dma_addr = (dma_addr_t)NULL;
+err1:
+ sg_free_table(buf->sgt);
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ return ret;
}
static void lowlevel_buffer_deallocate(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
+ unsigned int flags, struct exynos_drm_gem_buf *buf)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
- if (buffer->dma_addr && buffer->size)
- dma_free_writecombine(dev->dev, buffer->size, buffer->kvaddr,
- (dma_addr_t)buffer->dma_addr);
- else
- DRM_DEBUG_KMS("buffer data are invalid.\n");
+ /*
+ * release only physically continuous memory and
+ * non-continuous memory would be released by exynos
+ * gem framework.
+ */
+ if (flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support allocation type.\n");
+ return;
+ }
+
+ if (!buf->dma_addr) {
+ DRM_DEBUG_KMS("dma_addr is invalid.\n");
+ return;
+ }
+
+ DRM_DEBUG_KMS("vaddr(0x%lx), dma_addr(0x%lx), size(0x%lx)\n",
+ (unsigned long)buf->kvaddr,
+ (unsigned long)buf->dma_addr,
+ buf->size);
+
+ sg_free_table(buf->sgt);
+
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ kfree(buf->pages);
+ buf->pages = NULL;
+
+ dma_free_writecombine(dev->dev, buf->size, buf->kvaddr,
+ (dma_addr_t)buf->dma_addr);
+ buf->dma_addr = (dma_addr_t)NULL;
}
-struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
- unsigned int size)
+struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
+ unsigned int size)
{
struct exynos_drm_gem_buf *buffer;
@@ -77,21 +193,11 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
}
buffer->size = size;
-
- /*
- * allocate memory region with size and set the memory information
- * to vaddr and dma_addr of a buffer object.
- */
- if (lowlevel_buffer_allocate(dev, buffer) < 0) {
- kfree(buffer);
- return NULL;
- }
-
return buffer;
}
-void exynos_drm_buf_destroy(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer)
+void exynos_drm_fini_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buffer)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
@@ -100,12 +206,27 @@ void exynos_drm_buf_destroy(struct drm_device *dev,
return;
}
- lowlevel_buffer_deallocate(dev, buffer);
-
kfree(buffer);
buffer = NULL;
}
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module");
-MODULE_LICENSE("GPL");
+int exynos_drm_alloc_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buf, unsigned int flags)
+{
+
+ /*
+ * allocate memory region and set the memory information
+ * to vaddr and dma_addr of a buffer object.
+ */
+ if (lowlevel_buffer_allocate(dev, flags, buf) < 0)
+ return -ENOMEM;
+
+ return 0;
+}
+
+void exynos_drm_free_buf(struct drm_device *dev,
+ unsigned int flags, struct exynos_drm_gem_buf *buffer)
+{
+
+ lowlevel_buffer_deallocate(dev, flags, buffer);
+}
diff --git a/drivers/gpu/drm/exynos/exynos_drm_buf.h b/drivers/gpu/drm/exynos/exynos_drm_buf.h
index c913f2bad760..3388e4eb4ba2 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_buf.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_buf.h
@@ -26,12 +26,22 @@
#ifndef _EXYNOS_DRM_BUF_H_
#define _EXYNOS_DRM_BUF_H_
-/* allocate physical memory. */
-struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
- unsigned int size);
+/* create and initialize buffer object. */
+struct exynos_drm_gem_buf *exynos_drm_init_buf(struct drm_device *dev,
+ unsigned int size);
-/* remove allocated physical memory. */
-void exynos_drm_buf_destroy(struct drm_device *dev,
- struct exynos_drm_gem_buf *buffer);
+/* destroy buffer object. */
+void exynos_drm_fini_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buffer);
+
+/* allocate physical memory region and setup sgt and pages. */
+int exynos_drm_alloc_buf(struct drm_device *dev,
+ struct exynos_drm_gem_buf *buf,
+ unsigned int flags);
+
+/* release physical memory region, sgt and pages. */
+void exynos_drm_free_buf(struct drm_device *dev,
+ unsigned int flags,
+ struct exynos_drm_gem_buf *buffer);
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_connector.c b/drivers/gpu/drm/exynos/exynos_drm_connector.c
index d620b0784257..ee4ff74bc165 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_connector.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_connector.c
@@ -28,6 +28,7 @@
#include "drmP.h"
#include "drm_crtc_helper.h"
+#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
@@ -44,8 +45,9 @@ struct exynos_drm_connector {
/* convert exynos_video_timings to drm_display_mode */
static inline void
convert_to_display_mode(struct drm_display_mode *mode,
- struct fb_videomode *timing)
+ struct exynos_drm_panel_info *panel)
{
+ struct fb_videomode *timing = &panel->timing;
DRM_DEBUG_KMS("%s\n", __FILE__);
mode->clock = timing->pixclock / 1000;
@@ -60,6 +62,8 @@ convert_to_display_mode(struct drm_display_mode *mode,
mode->vsync_start = mode->vdisplay + timing->upper_margin;
mode->vsync_end = mode->vsync_start + timing->vsync_len;
mode->vtotal = mode->vsync_end + timing->lower_margin;
+ mode->width_mm = panel->width_mm;
+ mode->height_mm = panel->height_mm;
if (timing->vmode & FB_VMODE_INTERLACED)
mode->flags |= DRM_MODE_FLAG_INTERLACE;
@@ -148,16 +152,18 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
connector->display_info.raw_edid = edid;
} else {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
- struct fb_videomode *timing;
+ struct exynos_drm_panel_info *panel;
- if (display_ops->get_timing)
- timing = display_ops->get_timing(manager->dev);
+ if (display_ops->get_panel)
+ panel = display_ops->get_panel(manager->dev);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
}
- convert_to_display_mode(mode, timing);
+ convert_to_display_mode(mode, panel);
+ connector->display_info.width_mm = mode->width_mm;
+ connector->display_info.height_mm = mode->height_mm;
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
@@ -219,6 +225,29 @@ static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.best_encoder = exynos_drm_best_encoder,
};
+static int exynos_drm_connector_fill_modes(struct drm_connector *connector,
+ unsigned int max_width, unsigned int max_height)
+{
+ struct exynos_drm_connector *exynos_connector =
+ to_exynos_connector(connector);
+ struct exynos_drm_manager *manager = exynos_connector->manager;
+ struct exynos_drm_manager_ops *ops = manager->ops;
+ unsigned int width, height;
+
+ width = max_width;
+ height = max_height;
+
+ /*
+ * if specific driver want to find desired_mode using maxmum
+ * resolution then get max width and height from that driver.
+ */
+ if (ops && ops->get_max_resol)
+ ops->get_max_resol(manager->dev, &width, &height);
+
+ return drm_helper_probe_single_connector_modes(connector, width,
+ height);
+}
+
/* get detection status of display device. */
static enum drm_connector_status
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
@@ -256,7 +285,7 @@ static void exynos_drm_connector_destroy(struct drm_connector *connector)
static struct drm_connector_funcs exynos_connector_funcs = {
.dpms = drm_helper_connector_dpms,
- .fill_modes = drm_helper_probe_single_connector_modes,
+ .fill_modes = exynos_drm_connector_fill_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
};
@@ -286,6 +315,10 @@ struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
connector->interlace_allowed = true;
connector->polled = DRM_CONNECTOR_POLL_HPD;
break;
+ case EXYNOS_DISPLAY_TYPE_VIDI:
+ type = DRM_MODE_CONNECTOR_VIRTUAL;
+ connector->polled = DRM_CONNECTOR_POLL_HPD;
+ break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
break;
@@ -319,9 +352,3 @@ err_connector:
kfree(exynos_connector);
return NULL;
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_core.c b/drivers/gpu/drm/exynos/exynos_drm_core.c
index 661a03571d0c..411832e8e17a 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_core.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_core.c
@@ -32,7 +32,6 @@
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
-static DEFINE_MUTEX(exynos_drm_mutex);
static LIST_HEAD(exynos_drm_subdrv_list);
static struct drm_device *drm_dev;
@@ -60,6 +59,9 @@ static int exynos_drm_subdrv_probe(struct drm_device *dev,
return ret;
}
+ if (subdrv->is_local)
+ return 0;
+
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, &subdrv->manager,
(1 << MAX_CRTC) - 1);
@@ -116,13 +118,10 @@ int exynos_drm_device_register(struct drm_device *dev)
if (!dev)
return -EINVAL;
- if (drm_dev) {
- DRM_ERROR("Already drm device were registered\n");
- return -EBUSY;
- }
+ drm_dev = dev;
- mutex_lock(&exynos_drm_mutex);
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
+ subdrv->drm_dev = dev;
err = exynos_drm_subdrv_probe(dev, subdrv);
if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n");
@@ -130,9 +129,6 @@ int exynos_drm_device_register(struct drm_device *dev)
}
}
- drm_dev = dev;
- mutex_unlock(&exynos_drm_mutex);
-
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
@@ -143,83 +139,28 @@ int exynos_drm_device_unregister(struct drm_device *dev)
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- if (!dev || dev != drm_dev) {
+ if (!dev) {
WARN(1, "Unexpected drm device unregister!\n");
return -EINVAL;
}
- mutex_lock(&exynos_drm_mutex);
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
exynos_drm_subdrv_remove(dev, subdrv);
drm_dev = NULL;
- mutex_unlock(&exynos_drm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
-static int exynos_drm_mode_group_reinit(struct drm_device *dev)
-{
- struct drm_mode_group *group = &dev->primary->mode_group;
- uint32_t *id_list = group->id_list;
- int ret;
-
- DRM_DEBUG_DRIVER("%s\n", __FILE__);
-
- ret = drm_mode_group_init_legacy_group(dev, group);
- if (ret < 0)
- return ret;
-
- kfree(id_list);
- return 0;
-}
-
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
- int err;
-
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
- mutex_lock(&exynos_drm_mutex);
- if (drm_dev) {
- err = exynos_drm_subdrv_probe(drm_dev, subdrv);
- if (err) {
- DRM_ERROR("failed to probe exynos drm subdrv\n");
- mutex_unlock(&exynos_drm_mutex);
- return err;
- }
-
- /*
- * if any specific driver such as fimd or hdmi driver called
- * exynos_drm_subdrv_register() later than drm_load(),
- * the fb helper should be re-initialized and re-configured.
- */
- err = exynos_drm_fbdev_reinit(drm_dev);
- if (err) {
- DRM_ERROR("failed to reinitialize exynos drm fbdev\n");
- exynos_drm_subdrv_remove(drm_dev, subdrv);
- mutex_unlock(&exynos_drm_mutex);
- return err;
- }
-
- err = exynos_drm_mode_group_reinit(drm_dev);
- if (err) {
- DRM_ERROR("failed to reinitialize mode group\n");
- exynos_drm_fbdev_fini(drm_dev);
- exynos_drm_subdrv_remove(drm_dev, subdrv);
- mutex_unlock(&exynos_drm_mutex);
- return err;
- }
- }
-
- subdrv->drm_dev = drm_dev;
-
list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
- mutex_unlock(&exynos_drm_mutex);
return 0;
}
@@ -227,46 +168,48 @@ EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
- int ret = -EFAULT;
-
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- if (!subdrv) {
- DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n");
- return ret;
- }
+ if (!subdrv)
+ return -EINVAL;
- mutex_lock(&exynos_drm_mutex);
- if (drm_dev) {
- exynos_drm_subdrv_remove(drm_dev, subdrv);
- list_del(&subdrv->list);
+ list_del(&subdrv->list);
- /*
- * fb helper should be updated once a sub driver is released
- * to re-configure crtc and connector and also to re-setup
- * drm framebuffer.
- */
- ret = exynos_drm_fbdev_reinit(drm_dev);
- if (ret < 0) {
- DRM_ERROR("failed fb helper reinit.\n");
- goto fail;
- }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
- ret = exynos_drm_mode_group_reinit(drm_dev);
- if (ret < 0) {
- DRM_ERROR("failed drm mode group reinit.\n");
- goto fail;
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file)
+{
+ struct exynos_drm_subdrv *subdrv;
+ int ret;
+
+ list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
+ if (subdrv->open) {
+ ret = subdrv->open(dev, subdrv->manager.dev, file);
+ if (ret)
+ goto err;
}
}
-fail:
- mutex_unlock(&exynos_drm_mutex);
+ return 0;
+
+err:
+ list_for_each_entry_reverse(subdrv, &subdrv->list, list) {
+ if (subdrv->close)
+ subdrv->close(dev, subdrv->manager.dev, file);
+ }
return ret;
}
-EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_open);
+
+void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file)
+{
+ struct exynos_drm_subdrv *subdrv;
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Core Driver");
-MODULE_LICENSE("GPL");
+ list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list) {
+ if (subdrv->close)
+ subdrv->close(dev, subdrv->manager.dev, file);
+ }
+}
+EXPORT_SYMBOL_GPL(exynos_drm_subdrv_close);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index e3861ac49295..3486ffed0bf0 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -249,7 +249,11 @@ exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
{
DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode;
+ /*
+ * copy the mode data adjusted by mode_fixup() into crtc->mode
+ * so that hardware can be seet to proper mode.
+ */
+ memcpy(&crtc->mode, adjusted_mode, sizeof(*adjusted_mode));
return exynos_drm_crtc_update(crtc);
}
@@ -307,9 +311,6 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
*/
event->pipe = exynos_crtc->pipe;
- list_add_tail(&event->base.link,
- &dev_priv->pageflip_event_list);
-
ret = drm_vblank_get(dev, exynos_crtc->pipe);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter\n");
@@ -318,6 +319,9 @@ static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
goto out;
}
+ list_add_tail(&event->base.link,
+ &dev_priv->pageflip_event_list);
+
crtc->fb = fb;
ret = exynos_drm_crtc_update(crtc);
if (ret) {
@@ -426,9 +430,3 @@ void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank);
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.c b/drivers/gpu/drm/exynos/exynos_drm_drv.c
index 35889ca255e9..3453bdd28a47 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.c
@@ -33,10 +33,12 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
+#include "exynos_drm_encoder.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_plane.h"
+#include "exynos_drm_vidi.h"
#define DRIVER_NAME "exynos"
#define DRIVER_DESC "Samsung SoC DRM"
@@ -99,6 +101,9 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
if (ret)
goto err_vblank;
+ /* setup possible_clones. */
+ exynos_drm_encoder_setup(dev);
+
/*
* create and configure fb helper and also exynos specific
* fbdev object.
@@ -140,17 +145,45 @@ static int exynos_drm_unload(struct drm_device *dev)
return 0;
}
+static int exynos_drm_open(struct drm_device *dev, struct drm_file *file)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ return exynos_drm_subdrv_open(dev, file);
+}
+
static void exynos_drm_preclose(struct drm_device *dev,
- struct drm_file *file_priv)
+ struct drm_file *file)
{
- struct exynos_drm_private *dev_priv = dev->dev_private;
+ struct exynos_drm_private *private = dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ unsigned long flags;
- /*
- * drm framework frees all events at release time,
- * so private event list should be cleared.
- */
- if (!list_empty(&dev_priv->pageflip_event_list))
- INIT_LIST_HEAD(&dev_priv->pageflip_event_list);
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ /* release events of current file */
+ spin_lock_irqsave(&dev->event_lock, flags);
+ list_for_each_entry_safe(e, t, &private->pageflip_event_list,
+ base.link) {
+ if (e->base.file_priv == file) {
+ list_del(&e->base.link);
+ e->base.destroy(&e->base);
+ }
+ }
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ exynos_drm_subdrv_close(dev, file);
+}
+
+static void exynos_drm_postclose(struct drm_device *dev, struct drm_file *file)
+{
+ DRM_DEBUG_DRIVER("%s\n", __FILE__);
+
+ if (!file->driver_priv)
+ return;
+
+ kfree(file->driver_priv);
+ file->driver_priv = NULL;
}
static void exynos_drm_lastclose(struct drm_device *dev)
@@ -176,6 +209,8 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_PLANE_SET_ZPOS, exynos_plane_set_zpos_ioctl,
DRM_UNLOCKED | DRM_AUTH),
+ DRM_IOCTL_DEF_DRV(EXYNOS_VIDI_CONNECTION,
+ vidi_connection_ioctl, DRM_UNLOCKED | DRM_AUTH),
};
static const struct file_operations exynos_drm_driver_fops = {
@@ -193,8 +228,10 @@ static struct drm_driver exynos_drm_driver = {
DRIVER_MODESET | DRIVER_GEM,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
+ .open = exynos_drm_open,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
+ .postclose = exynos_drm_postclose,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
@@ -242,9 +279,60 @@ static struct platform_driver exynos_drm_platform_driver = {
static int __init exynos_drm_init(void)
{
+ int ret;
+
DRM_DEBUG_DRIVER("%s\n", __FILE__);
- return platform_driver_register(&exynos_drm_platform_driver);
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ ret = platform_driver_register(&fimd_driver);
+ if (ret < 0)
+ goto out_fimd;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+ ret = platform_driver_register(&hdmi_driver);
+ if (ret < 0)
+ goto out_hdmi;
+ ret = platform_driver_register(&mixer_driver);
+ if (ret < 0)
+ goto out_mixer;
+ ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
+ if (ret < 0)
+ goto out_common_hdmi;
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ ret = platform_driver_register(&vidi_driver);
+ if (ret < 0)
+ goto out_vidi;
+#endif
+
+ ret = platform_driver_register(&exynos_drm_platform_driver);
+ if (ret < 0)
+ goto out;
+
+ return 0;
+
+out:
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+out_vidi:
+ platform_driver_unregister(&vidi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+ platform_driver_unregister(&exynos_drm_common_hdmi_driver);
+out_common_hdmi:
+ platform_driver_unregister(&mixer_driver);
+out_mixer:
+ platform_driver_unregister(&hdmi_driver);
+out_hdmi:
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ platform_driver_unregister(&fimd_driver);
+out_fimd:
+#endif
+ return ret;
}
static void __exit exynos_drm_exit(void)
@@ -252,6 +340,20 @@ static void __exit exynos_drm_exit(void)
DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_driver_unregister(&exynos_drm_platform_driver);
+
+#ifdef CONFIG_DRM_EXYNOS_HDMI
+ platform_driver_unregister(&exynos_drm_common_hdmi_driver);
+ platform_driver_unregister(&mixer_driver);
+ platform_driver_unregister(&hdmi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+ platform_driver_unregister(&vidi_driver);
+#endif
+
+#ifdef CONFIG_DRM_EXYNOS_FIMD
+ platform_driver_unregister(&fimd_driver);
+#endif
}
module_init(exynos_drm_init);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_drv.h b/drivers/gpu/drm/exynos/exynos_drm_drv.h
index e685e1e33055..fbd0a232c93d 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_drv.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_drv.h
@@ -32,9 +32,9 @@
#include <linux/module.h>
#include "drm.h"
-#define MAX_CRTC 2
+#define MAX_CRTC 3
#define MAX_PLANE 5
-#define MAX_FB_BUFFER 3
+#define MAX_FB_BUFFER 4
#define DEFAULT_ZPOS -1
struct drm_device;
@@ -50,6 +50,8 @@ enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_LCD,
/* HDMI Interface. */
EXYNOS_DISPLAY_TYPE_HDMI,
+ /* Virtual Display Interface. */
+ EXYNOS_DISPLAY_TYPE_VIDI,
};
/*
@@ -136,7 +138,7 @@ struct exynos_drm_overlay {
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @is_connected: check for that display is connected or not.
* @get_edid: get edid modes from display driver.
- * @get_timing: get timing object from display driver.
+ * @get_panel: get panel object from display driver.
* @check_timing: check if timing is valid or not.
* @power_on: display device on or off.
*/
@@ -145,7 +147,7 @@ struct exynos_drm_display_ops {
bool (*is_connected)(struct device *dev);
int (*get_edid)(struct device *dev, struct drm_connector *connector,
u8 *edid, int len);
- void *(*get_timing)(struct device *dev);
+ void *(*get_panel)(struct device *dev);
int (*check_timing)(struct device *dev, void *timing);
int (*power_on)(struct device *dev, int mode);
};
@@ -155,8 +157,10 @@ struct exynos_drm_display_ops {
*
* @dpms: control device power.
* @apply: set timing, vblank and overlay data to registers.
+ * @mode_fixup: fix mode data comparing to hw specific display mode.
* @mode_set: convert drm_display_mode to hw specific display mode and
* would be called by encoder->mode_set().
+ * @get_max_resol: get maximum resolution to specific hardware.
* @commit: set current hw specific display mode to hw.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
@@ -164,7 +168,13 @@ struct exynos_drm_display_ops {
struct exynos_drm_manager_ops {
void (*dpms)(struct device *subdrv_dev, int mode);
void (*apply)(struct device *subdrv_dev);
+ void (*mode_fixup)(struct device *subdrv_dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
void (*mode_set)(struct device *subdrv_dev, void *mode);
+ void (*get_max_resol)(struct device *subdrv_dev, unsigned int *width,
+ unsigned int *height);
void (*commit)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev);
@@ -217,10 +227,13 @@ struct exynos_drm_private {
* @list: sub driver has its own list object to register to exynos drm driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
+ * @is_local: appear encoder and connector disrelated device.
* @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
* by probe callback.
+ * @open: this would be called with drm device file open.
+ * @close: this would be called with drm device file close.
* @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager.
* @encoder: encoder object owned by this sub driver.
@@ -229,9 +242,14 @@ struct exynos_drm_private {
struct exynos_drm_subdrv {
struct list_head list;
struct drm_device *drm_dev;
+ bool is_local;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *dev);
+ int (*open)(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file);
+ void (*close)(struct drm_device *drm_dev, struct device *dev,
+ struct drm_file *file);
struct exynos_drm_manager manager;
struct drm_encoder *encoder;
@@ -254,15 +272,19 @@ int exynos_drm_device_unregister(struct drm_device *dev);
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
* and when a sub driver is registered to exynos drm driver a probe callback
- * of the sub driver is called and creates its own encoder and connector
- * and then fb helper and drm mode group would be re-initialized.
+ * of the sub driver is called and creates its own encoder and connector.
*/
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
-/*
- * this function removes subdrv list from exynos drm driver and fb helper
- * and drm mode group would be re-initialized.
- */
+/* this function removes subdrv list from exynos drm driver */
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
+int exynos_drm_subdrv_open(struct drm_device *dev, struct drm_file *file);
+void exynos_drm_subdrv_close(struct drm_device *dev, struct drm_file *file);
+
+extern struct platform_driver fimd_driver;
+extern struct platform_driver hdmi_driver;
+extern struct platform_driver mixer_driver;
+extern struct platform_driver exynos_drm_common_hdmi_driver;
+extern struct platform_driver vidi_driver;
#endif
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.c b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
index 86b93dde219a..6e9ac7bd1dcf 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.c
@@ -111,9 +111,19 @@ exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
+ struct drm_device *dev = encoder->dev;
+ struct drm_connector *connector;
+ struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
+ struct exynos_drm_manager_ops *manager_ops = manager->ops;
+
DRM_DEBUG_KMS("%s\n", __FILE__);
- /* drm framework doesn't check NULL. */
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder == encoder)
+ if (manager_ops && manager_ops->mode_fixup)
+ manager_ops->mode_fixup(manager->dev, connector,
+ mode, adjusted_mode);
+ }
return true;
}
@@ -132,12 +142,11 @@ static void exynos_drm_encoder_mode_set(struct drm_encoder *encoder,
DRM_DEBUG_KMS("%s\n", __FILE__);
- mode = adjusted_mode;
-
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
if (manager_ops && manager_ops->mode_set)
- manager_ops->mode_set(manager->dev, mode);
+ manager_ops->mode_set(manager->dev,
+ adjusted_mode);
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
@@ -195,6 +204,41 @@ static struct drm_encoder_funcs exynos_encoder_funcs = {
.destroy = exynos_drm_encoder_destroy,
};
+static unsigned int exynos_drm_encoder_clones(struct drm_encoder *encoder)
+{
+ struct drm_encoder *clone;
+ struct drm_device *dev = encoder->dev;
+ struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
+ struct exynos_drm_display_ops *display_ops =
+ exynos_encoder->manager->display_ops;
+ unsigned int clone_mask = 0;
+ int cnt = 0;
+
+ list_for_each_entry(clone, &dev->mode_config.encoder_list, head) {
+ switch (display_ops->type) {
+ case EXYNOS_DISPLAY_TYPE_LCD:
+ case EXYNOS_DISPLAY_TYPE_HDMI:
+ case EXYNOS_DISPLAY_TYPE_VIDI:
+ clone_mask |= (1 << (cnt++));
+ break;
+ default:
+ continue;
+ }
+ }
+
+ return clone_mask;
+}
+
+void exynos_drm_encoder_setup(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ encoder->possible_clones = exynos_drm_encoder_clones(encoder);
+}
+
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *manager,
@@ -399,9 +443,3 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
if (overlay_ops && overlay_ops->disable)
overlay_ops->disable(manager->dev, zpos);
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_encoder.h b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
index 97b087a51cb6..eb7d2316847e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_encoder.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_encoder.h
@@ -30,6 +30,7 @@
struct exynos_drm_manager;
+void exynos_drm_encoder_setup(struct drm_device *dev);
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *mgr,
unsigned int possible_crtcs);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fb.c b/drivers/gpu/drm/exynos/exynos_drm_fb.c
index 3733fe6723d3..c38c8f468fa3 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fb.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fb.c
@@ -211,9 +211,3 @@ void exynos_drm_mode_config_init(struct drm_device *dev)
dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM FB Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
index 5737bc5e6ed2..d5586cc75163 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fbdev.c
@@ -125,7 +125,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
}
size = mode_cmd.pitches[0] * mode_cmd.height;
- exynos_gem_obj = exynos_drm_gem_create(dev, size);
+
+ /* 0 means to allocate physically continuous memory */
+ exynos_gem_obj = exynos_drm_gem_create(dev, 0, size);
if (IS_ERR(exynos_gem_obj)) {
ret = PTR_ERR(exynos_gem_obj);
goto out;
@@ -169,66 +171,6 @@ out:
return ret;
}
-static bool
-exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
- struct drm_fb_helper_surface_size *sizes)
-{
- if (fb->width != sizes->surface_width)
- return false;
- if (fb->height != sizes->surface_height)
- return false;
- if (fb->bits_per_pixel != sizes->surface_bpp)
- return false;
- if (fb->depth != sizes->surface_depth)
- return false;
-
- return true;
-}
-
-static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
- struct drm_fb_helper_surface_size *sizes)
-{
- struct drm_device *dev = helper->dev;
- struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
- struct exynos_drm_gem_obj *exynos_gem_obj;
- struct drm_framebuffer *fb = helper->fb;
- struct drm_mode_fb_cmd2 mode_cmd = { 0 };
- unsigned long size;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- if (exynos_drm_fbdev_is_samefb(fb, sizes))
- return 0;
-
- mode_cmd.width = sizes->surface_width;
- mode_cmd.height = sizes->surface_height;
- mode_cmd.pitches[0] = sizes->surface_width * (sizes->surface_bpp >> 3);
- mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
- sizes->surface_depth);
-
- if (exynos_fbdev->exynos_gem_obj)
- exynos_drm_gem_destroy(exynos_fbdev->exynos_gem_obj);
-
- if (fb->funcs->destroy)
- fb->funcs->destroy(fb);
-
- size = mode_cmd.pitches[0] * mode_cmd.height;
- exynos_gem_obj = exynos_drm_gem_create(dev, size);
- if (IS_ERR(exynos_gem_obj))
- return PTR_ERR(exynos_gem_obj);
-
- exynos_fbdev->exynos_gem_obj = exynos_gem_obj;
-
- helper->fb = exynos_drm_framebuffer_init(dev, &mode_cmd,
- &exynos_gem_obj->base);
- if (IS_ERR_OR_NULL(helper->fb)) {
- DRM_ERROR("failed to create drm framebuffer.\n");
- return PTR_ERR(helper->fb);
- }
-
- return exynos_drm_fbdev_update(helper, helper->fb);
-}
-
static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -236,6 +178,10 @@ static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
DRM_DEBUG_KMS("%s\n", __FILE__);
+ /*
+ * with !helper->fb, it means that this funcion is called first time
+ * and after that, the helper->fb would be used as clone mode.
+ */
if (!helper->fb) {
ret = exynos_drm_fbdev_create(helper, sizes);
if (ret < 0) {
@@ -248,12 +194,6 @@ static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
* because register_framebuffer() should be called.
*/
ret = 1;
- } else {
- ret = exynos_drm_fbdev_recreate(helper, sizes);
- if (ret < 0) {
- DRM_ERROR("failed to reconfigure fbdev\n");
- return ret;
- }
}
return ret;
@@ -376,89 +316,3 @@ void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
drm_fb_helper_restore_fbdev_mode(private->fb_helper);
}
-
-int exynos_drm_fbdev_reinit(struct drm_device *dev)
-{
- struct exynos_drm_private *private = dev->dev_private;
- struct drm_fb_helper *fb_helper;
- int ret;
-
- if (!private)
- return -EINVAL;
-
- /*
- * if all sub drivers were unloaded then num_connector is 0
- * so at this time, the framebuffers also should be destroyed.
- */
- if (!dev->mode_config.num_connector) {
- exynos_drm_fbdev_fini(dev);
- return 0;
- }
-
- fb_helper = private->fb_helper;
-
- if (fb_helper) {
- struct list_head temp_list;
-
- INIT_LIST_HEAD(&temp_list);
-
- /*
- * fb_helper is reintialized but kernel fb is reused
- * so kernel_fb_list need to be backuped and restored
- */
- if (!list_empty(&fb_helper->kernel_fb_list))
- list_replace_init(&fb_helper->kernel_fb_list,
- &temp_list);
-
- drm_fb_helper_fini(fb_helper);
-
- ret = drm_fb_helper_init(dev, fb_helper,
- dev->mode_config.num_crtc, MAX_CONNECTOR);
- if (ret < 0) {
- DRM_ERROR("failed to initialize drm fb helper\n");
- return ret;
- }
-
- if (!list_empty(&temp_list))
- list_replace(&temp_list, &fb_helper->kernel_fb_list);
-
- ret = drm_fb_helper_single_add_all_connectors(fb_helper);
- if (ret < 0) {
- DRM_ERROR("failed to add fb helper to connectors\n");
- goto err;
- }
-
- ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
- if (ret < 0) {
- DRM_ERROR("failed to set up hw configuration.\n");
- goto err;
- }
- } else {
- /*
- * if drm_load() failed whem drm load() was called prior
- * to specific drivers, fb_helper must be NULL and so
- * this fuction should be called again to re-initialize and
- * re-configure the fb helper. it means that this function
- * has been called by the specific drivers.
- */
- ret = exynos_drm_fbdev_init(dev);
- }
-
- return ret;
-
-err:
- /*
- * if drm_load() failed when drm load() was called prior
- * to specific drivers, the fb_helper must be NULL and so check it.
- */
- if (fb_helper)
- drm_fb_helper_fini(fb_helper);
-
- return ret;
-}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
index b6a737d196ae..101d64c7a9be 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c
@@ -89,7 +89,7 @@ struct fimd_context {
bool suspended;
struct mutex lock;
- struct fb_videomode *timing;
+ struct exynos_drm_panel_info *panel;
};
static bool fimd_display_is_connected(struct device *dev)
@@ -101,13 +101,13 @@ static bool fimd_display_is_connected(struct device *dev)
return true;
}
-static void *fimd_get_timing(struct device *dev)
+static void *fimd_get_panel(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
- return ctx->timing;
+ return ctx->panel;
}
static int fimd_check_timing(struct device *dev, void *timing)
@@ -131,7 +131,7 @@ static int fimd_display_power_on(struct device *dev, int mode)
static struct exynos_drm_display_ops fimd_display_ops = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.is_connected = fimd_display_is_connected,
- .get_timing = fimd_get_timing,
+ .get_panel = fimd_get_panel,
.check_timing = fimd_check_timing,
.power_on = fimd_display_power_on,
};
@@ -193,7 +193,8 @@ static void fimd_apply(struct device *subdrv_dev)
static void fimd_commit(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
- struct fb_videomode *timing = ctx->timing;
+ struct exynos_drm_panel_info *panel = ctx->panel;
+ struct fb_videomode *timing = &panel->timing;
u32 val;
if (ctx->suspended)
@@ -604,7 +605,12 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
}
if (is_checked) {
- drm_vblank_put(drm_dev, crtc);
+ /*
+ * call drm_vblank_put only in case that drm_vblank_get was
+ * called.
+ */
+ if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+ drm_vblank_put(drm_dev, crtc);
/*
* don't off vblank if vblank_disable_allowed is 1,
@@ -781,7 +787,7 @@ static int __devinit fimd_probe(struct platform_device *pdev)
struct fimd_context *ctx;
struct exynos_drm_subdrv *subdrv;
struct exynos_drm_fimd_pdata *pdata;
- struct fb_videomode *timing;
+ struct exynos_drm_panel_info *panel;
struct resource *res;
int win;
int ret = -EINVAL;
@@ -794,9 +800,9 @@ static int __devinit fimd_probe(struct platform_device *pdev)
return -EINVAL;
}
- timing = &pdata->timing;
- if (!timing) {
- dev_err(dev, "timing is null.\n");
+ panel = &pdata->panel;
+ if (!panel) {
+ dev_err(dev, "panel is null.\n");
return -EINVAL;
}
@@ -858,16 +864,16 @@ static int __devinit fimd_probe(struct platform_device *pdev)
goto err_req_irq;
}
- ctx->clkdiv = fimd_calc_clkdiv(ctx, timing);
+ ctx->clkdiv = fimd_calc_clkdiv(ctx, &panel->timing);
ctx->vidcon0 = pdata->vidcon0;
ctx->vidcon1 = pdata->vidcon1;
ctx->default_win = pdata->default_win;
- ctx->timing = timing;
+ ctx->panel = panel;
- timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
+ panel->timing.pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n",
- timing->pixclock, ctx->clkdiv);
+ panel->timing.pixclock, ctx->clkdiv);
subdrv = &ctx->subdrv;
@@ -1006,7 +1012,7 @@ static const struct dev_pm_ops fimd_pm_ops = {
SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
};
-static struct platform_driver fimd_driver = {
+struct platform_driver fimd_driver = {
.probe = fimd_probe,
.remove = __devexit_p(fimd_remove),
.driver = {
@@ -1015,21 +1021,3 @@ static struct platform_driver fimd_driver = {
.pm = &fimd_pm_ops,
},
};
-
-static int __init fimd_init(void)
-{
- return platform_driver_register(&fimd_driver);
-}
-
-static void __exit fimd_exit(void)
-{
- platform_driver_unregister(&fimd_driver);
-}
-
-module_init(fimd_init);
-module_exit(fimd_exit);
-
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM FIMD Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c
index 65452512f5b4..fa1aa94a3d8e 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c
@@ -26,6 +26,7 @@
#include "drmP.h"
#include "drm.h"
+#include <linux/shmem_fs.h>
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
@@ -55,6 +56,178 @@ static unsigned int convert_to_vm_err_msg(int msg)
return out_msg;
}
+static unsigned int mask_gem_flags(unsigned int flags)
+{
+ return flags &= EXYNOS_BO_NONCONTIG;
+}
+
+static struct page **exynos_gem_get_pages(struct drm_gem_object *obj,
+ gfp_t gfpmask)
+{
+ struct inode *inode;
+ struct address_space *mapping;
+ struct page *p, **pages;
+ int i, npages;
+
+ /* This is the shared memory object that backs the GEM resource */
+ inode = obj->filp->f_path.dentry->d_inode;
+ mapping = inode->i_mapping;
+
+ npages = obj->size >> PAGE_SHIFT;
+
+ pages = drm_malloc_ab(npages, sizeof(struct page *));
+ if (pages == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ gfpmask |= mapping_gfp_mask(mapping);
+
+ for (i = 0; i < npages; i++) {
+ p = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+ if (IS_ERR(p))
+ goto fail;
+ pages[i] = p;
+ }
+
+ return pages;
+
+fail:
+ while (i--)
+ page_cache_release(pages[i]);
+
+ drm_free_large(pages);
+ return ERR_PTR(PTR_ERR(p));
+}
+
+static void exynos_gem_put_pages(struct drm_gem_object *obj,
+ struct page **pages,
+ bool dirty, bool accessed)
+{
+ int i, npages;
+
+ npages = obj->size >> PAGE_SHIFT;
+
+ for (i = 0; i < npages; i++) {
+ if (dirty)
+ set_page_dirty(pages[i]);
+
+ if (accessed)
+ mark_page_accessed(pages[i]);
+
+ /* Undo the reference we took when populating the table */
+ page_cache_release(pages[i]);
+ }
+
+ drm_free_large(pages);
+}
+
+static int exynos_drm_gem_map_pages(struct drm_gem_object *obj,
+ struct vm_area_struct *vma,
+ unsigned long f_vaddr,
+ pgoff_t page_offset)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+ unsigned long pfn;
+
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ unsigned long usize = buf->size;
+
+ if (!buf->pages)
+ return -EINTR;
+
+ while (usize > 0) {
+ pfn = page_to_pfn(buf->pages[page_offset++]);
+ vm_insert_mixed(vma, f_vaddr, pfn);
+ f_vaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ }
+
+ return 0;
+ }
+
+ pfn = (buf->dma_addr >> PAGE_SHIFT) + page_offset;
+
+ return vm_insert_mixed(vma, f_vaddr, pfn);
+}
+
+static int exynos_drm_gem_get_pages(struct drm_gem_object *obj)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+ struct scatterlist *sgl;
+ struct page **pages;
+ unsigned int npages, i = 0;
+ int ret;
+
+ if (buf->pages) {
+ DRM_DEBUG_KMS("already allocated.\n");
+ return -EINVAL;
+ }
+
+ pages = exynos_gem_get_pages(obj, GFP_KERNEL);
+ if (IS_ERR(pages)) {
+ DRM_ERROR("failed to get pages.\n");
+ return PTR_ERR(pages);
+ }
+
+ npages = obj->size >> PAGE_SHIFT;
+
+ buf->sgt = kzalloc(sizeof(struct sg_table), GFP_KERNEL);
+ if (!buf->sgt) {
+ DRM_ERROR("failed to allocate sg table.\n");
+ ret = -ENOMEM;
+ goto err;
+ }
+
+ ret = sg_alloc_table(buf->sgt, npages, GFP_KERNEL);
+ if (ret < 0) {
+ DRM_ERROR("failed to initialize sg table.\n");
+ ret = -EFAULT;
+ goto err1;
+ }
+
+ sgl = buf->sgt->sgl;
+
+ /* set all pages to sg list. */
+ while (i < npages) {
+ sg_set_page(sgl, pages[i], PAGE_SIZE, 0);
+ sg_dma_address(sgl) = page_to_phys(pages[i]);
+ i++;
+ sgl = sg_next(sgl);
+ }
+
+ /* add some codes for UNCACHED type here. TODO */
+
+ buf->pages = pages;
+ return ret;
+err1:
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+err:
+ exynos_gem_put_pages(obj, pages, true, false);
+ return ret;
+
+}
+
+static void exynos_drm_gem_put_pages(struct drm_gem_object *obj)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
+ struct exynos_drm_gem_buf *buf = exynos_gem_obj->buffer;
+
+ /*
+ * if buffer typs is EXYNOS_BO_NONCONTIG then release all pages
+ * allocated at gem fault handler.
+ */
+ sg_free_table(buf->sgt);
+ kfree(buf->sgt);
+ buf->sgt = NULL;
+
+ exynos_gem_put_pages(obj, buf->pages, true, false);
+ buf->pages = NULL;
+
+ /* add some codes for UNCACHED type here. TODO */
+}
+
static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
struct drm_file *file_priv,
unsigned int *handle)
@@ -90,7 +263,15 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
- exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer);
+ if ((exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) &&
+ exynos_gem_obj->buffer->pages)
+ exynos_drm_gem_put_pages(obj);
+ else
+ exynos_drm_free_buf(obj->dev, exynos_gem_obj->flags,
+ exynos_gem_obj->buffer);
+
+ exynos_drm_fini_buf(obj->dev, exynos_gem_obj->buffer);
+ exynos_gem_obj->buffer = NULL;
if (obj->map_list.map)
drm_gem_free_mmap_offset(obj);
@@ -99,6 +280,7 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
drm_gem_object_release(obj);
kfree(exynos_gem_obj);
+ exynos_gem_obj = NULL;
}
static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
@@ -114,6 +296,7 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
return NULL;
}
+ exynos_gem_obj->size = size;
obj = &exynos_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size);
@@ -129,27 +312,55 @@ static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
}
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
- unsigned long size)
+ unsigned int flags,
+ unsigned long size)
{
- struct exynos_drm_gem_buf *buffer;
struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct exynos_drm_gem_buf *buf;
+ int ret;
size = roundup(size, PAGE_SIZE);
DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
- buffer = exynos_drm_buf_create(dev, size);
- if (!buffer)
+ flags = mask_gem_flags(flags);
+
+ buf = exynos_drm_init_buf(dev, size);
+ if (!buf)
return ERR_PTR(-ENOMEM);
exynos_gem_obj = exynos_drm_gem_init(dev, size);
if (!exynos_gem_obj) {
- exynos_drm_buf_destroy(dev, buffer);
- return ERR_PTR(-ENOMEM);
+ ret = -ENOMEM;
+ goto err;
}
- exynos_gem_obj->buffer = buffer;
+ exynos_gem_obj->buffer = buf;
+
+ /* set memory type and cache attribute from user side. */
+ exynos_gem_obj->flags = flags;
+
+ /*
+ * allocate all pages as desired size if user wants to allocate
+ * physically non-continuous memory.
+ */
+ if (flags & EXYNOS_BO_NONCONTIG) {
+ ret = exynos_drm_gem_get_pages(&exynos_gem_obj->base);
+ if (ret < 0) {
+ drm_gem_object_release(&exynos_gem_obj->base);
+ goto err;
+ }
+ } else {
+ ret = exynos_drm_alloc_buf(dev, buf, flags);
+ if (ret < 0) {
+ drm_gem_object_release(&exynos_gem_obj->base);
+ goto err;
+ }
+ }
return exynos_gem_obj;
+err:
+ exynos_drm_fini_buf(dev, buf);
+ return ERR_PTR(ret);
}
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
@@ -161,7 +372,7 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
DRM_DEBUG_KMS("%s\n", __FILE__);
- exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
+ exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
@@ -175,6 +386,64 @@ int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
return 0;
}
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ exynos_gem_obj = to_exynos_gem_obj(obj);
+
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support NONCONTIG type.\n");
+ drm_gem_object_unreference_unlocked(obj);
+
+ /* TODO */
+ return ERR_PTR(-EINVAL);
+ }
+
+ return &exynos_gem_obj->buffer->dma_addr;
+}
+
+void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv)
+{
+ struct exynos_drm_gem_obj *exynos_gem_obj;
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, gem_handle);
+ if (!obj) {
+ DRM_ERROR("failed to lookup gem object.\n");
+ return;
+ }
+
+ exynos_gem_obj = to_exynos_gem_obj(obj);
+
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ DRM_DEBUG_KMS("not support NONCONTIG type.\n");
+ drm_gem_object_unreference_unlocked(obj);
+
+ /* TODO */
+ return;
+ }
+
+ drm_gem_object_unreference_unlocked(obj);
+
+ /*
+ * decrease obj->refcount one more time because we has already
+ * increased it at exynos_drm_gem_get_dma_addr().
+ */
+ drm_gem_object_unreference_unlocked(obj);
+}
+
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
@@ -200,7 +469,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
struct drm_gem_object *obj = filp->private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_gem_buf *buffer;
- unsigned long pfn, vm_size;
+ unsigned long pfn, vm_size, usize, uaddr = vma->vm_start;
+ int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -209,7 +479,8 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
/* in case of direct mapping, always having non-cachable attribute */
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- vm_size = vma->vm_end - vma->vm_start;
+ vm_size = usize = vma->vm_end - vma->vm_start;
+
/*
* a buffer contains information to physically continuous memory
* allocated by user request or at framebuffer creation.
@@ -220,18 +491,37 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
if (vm_size > buffer->size)
return -EINVAL;
- /*
- * get page frame number to physical memory to be mapped
- * to user space.
- */
- pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >> PAGE_SHIFT;
-
- DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
-
- if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
- vma->vm_page_prot)) {
- DRM_ERROR("failed to remap pfn range.\n");
- return -EAGAIN;
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ int i = 0;
+
+ if (!buffer->pages)
+ return -EINVAL;
+
+ do {
+ ret = vm_insert_page(vma, uaddr, buffer->pages[i++]);
+ if (ret) {
+ DRM_ERROR("failed to remap user space.\n");
+ return ret;
+ }
+
+ uaddr += PAGE_SIZE;
+ usize -= PAGE_SIZE;
+ } while (usize > 0);
+ } else {
+ /*
+ * get page frame number to physical memory to be mapped
+ * to user space.
+ */
+ pfn = ((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
+ PAGE_SHIFT;
+
+ DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
+
+ if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
+ vma->vm_page_prot)) {
+ DRM_ERROR("failed to remap pfn range.\n");
+ return -EAGAIN;
+ }
}
return 0;
@@ -311,9 +601,9 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
*/
args->pitch = args->width * args->bpp >> 3;
- args->size = args->pitch * args->height;
+ args->size = PAGE_ALIGN(args->pitch * args->height);
- exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
+ exynos_gem_obj = exynos_drm_gem_create(dev, args->flags, args->size);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
@@ -397,20 +687,31 @@ int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
struct drm_gem_object *obj = vma->vm_private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct drm_device *dev = obj->dev;
- unsigned long pfn;
+ unsigned long f_vaddr;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
+ f_vaddr = (unsigned long)vmf->virtual_address;
mutex_lock(&dev->struct_mutex);
- pfn = (((unsigned long)exynos_gem_obj->buffer->dma_addr) >>
- PAGE_SHIFT) + page_offset;
+ /*
+ * allocate all pages as desired size if user wants to allocate
+ * physically non-continuous memory.
+ */
+ if (exynos_gem_obj->flags & EXYNOS_BO_NONCONTIG) {
+ ret = exynos_drm_gem_get_pages(obj);
+ if (ret < 0)
+ goto err;
+ }
- ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
+ ret = exynos_drm_gem_map_pages(obj, vma, f_vaddr, page_offset);
+ if (ret < 0)
+ DRM_ERROR("failed to map pages.\n");
+err:
mutex_unlock(&dev->struct_mutex);
return convert_to_vm_err_msg(ret);
@@ -434,7 +735,3 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
return ret;
}
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h
index 67cdc9168708..e40fbad8b705 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_gem.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h
@@ -36,11 +36,15 @@
* @dma_addr: bus address(accessed by dma) to allocated memory region.
* - this address could be physical address without IOMMU and
* device address with IOMMU.
+ * @sgt: sg table to transfer page data.
+ * @pages: contain all pages to allocated memory region.
* @size: size of allocated memory region.
*/
struct exynos_drm_gem_buf {
void __iomem *kvaddr;
dma_addr_t dma_addr;
+ struct sg_table *sgt;
+ struct page **pages;
unsigned long size;
};
@@ -55,6 +59,8 @@ struct exynos_drm_gem_buf {
* by user request or at framebuffer creation.
* continuous memory region allocated by user request
* or at framebuffer creation.
+ * @size: total memory size to physically non-continuous memory region.
+ * @flags: indicate memory type to allocated buffer and cache attruibute.
*
* P.S. this object would be transfered to user as kms_bo.handle so
* user can access the buffer through kms_bo.handle.
@@ -62,6 +68,8 @@ struct exynos_drm_gem_buf {
struct exynos_drm_gem_obj {
struct drm_gem_object base;
struct exynos_drm_gem_buf *buffer;
+ unsigned long size;
+ unsigned int flags;
};
/* destroy a buffer with gem object */
@@ -69,7 +77,8 @@ void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj);
/* create a new buffer with gem object */
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
- unsigned long size);
+ unsigned int flags,
+ unsigned long size);
/*
* request gem object creation and buffer allocation as the size
@@ -79,6 +88,24 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
+/*
+ * get dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be increased.
+ */
+void *exynos_drm_gem_get_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv);
+
+/*
+ * put dma address from gem handle and this function could be used for
+ * other drivers such as 2d/3d acceleration drivers.
+ * with this function call, gem object reference count would be decreased.
+ */
+void exynos_drm_gem_put_dma_addr(struct drm_device *dev,
+ unsigned int gem_handle,
+ struct drm_file *file_priv);
+
/* get buffer offset to map to user space. */
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
index ed8a319ed84b..14eb26b0ba1c 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.c
@@ -38,7 +38,6 @@ struct drm_hdmi_context {
struct exynos_drm_subdrv subdrv;
struct exynos_drm_hdmi_context *hdmi_ctx;
struct exynos_drm_hdmi_context *mixer_ctx;
- struct work_struct work;
};
void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
@@ -49,7 +48,6 @@ void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
if (display_ops)
hdmi_display_ops = display_ops;
}
-EXPORT_SYMBOL(exynos_drm_display_ops_register);
void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
*manager_ops)
@@ -59,7 +57,6 @@ void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
if (manager_ops)
hdmi_manager_ops = manager_ops;
}
-EXPORT_SYMBOL(exynos_drm_manager_ops_register);
void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
*overlay_ops)
@@ -69,7 +66,6 @@ void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
if (overlay_ops)
hdmi_overlay_ops = overlay_ops;
}
-EXPORT_SYMBOL(exynos_drm_overlay_ops_register);
static bool drm_hdmi_is_connected(struct device *dev)
{
@@ -155,6 +151,20 @@ static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx);
}
+static void drm_hdmi_mode_fixup(struct device *subdrv_dev,
+ struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_manager_ops && hdmi_manager_ops->mode_fixup)
+ hdmi_manager_ops->mode_fixup(ctx->hdmi_ctx->ctx, connector,
+ mode, adjusted_mode);
+}
+
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -165,6 +175,18 @@ static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
}
+static void drm_hdmi_get_max_resol(struct device *subdrv_dev,
+ unsigned int *width, unsigned int *height)
+{
+ struct drm_hdmi_context *ctx = to_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (hdmi_manager_ops && hdmi_manager_ops->get_max_resol)
+ hdmi_manager_ops->get_max_resol(ctx->hdmi_ctx->ctx, width,
+ height);
+}
+
static void drm_hdmi_commit(struct device *subdrv_dev)
{
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
@@ -200,7 +222,9 @@ static struct exynos_drm_manager_ops drm_hdmi_manager_ops = {
.dpms = drm_hdmi_dpms,
.enable_vblank = drm_hdmi_enable_vblank,
.disable_vblank = drm_hdmi_disable_vblank,
+ .mode_fixup = drm_hdmi_mode_fixup,
.mode_set = drm_hdmi_mode_set,
+ .get_max_resol = drm_hdmi_get_max_resol,
.commit = drm_hdmi_commit,
};
@@ -249,7 +273,6 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
struct drm_hdmi_context *ctx;
struct platform_device *pdev = to_platform_device(dev);
struct exynos_drm_common_hdmi_pd *pd;
- int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
@@ -270,26 +293,13 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
return -EFAULT;
}
- ret = platform_driver_register(&hdmi_driver);
- if (ret) {
- DRM_DEBUG_KMS("failed to register hdmi driver.\n");
- return ret;
- }
-
- ret = platform_driver_register(&mixer_driver);
- if (ret) {
- DRM_DEBUG_KMS("failed to register mixer driver.\n");
- goto err_hdmidrv;
- }
-
ctx = get_ctx_from_subdrv(subdrv);
ctx->hdmi_ctx = (struct exynos_drm_hdmi_context *)
to_context(pd->hdmi_dev);
if (!ctx->hdmi_ctx) {
DRM_DEBUG_KMS("hdmi context is null.\n");
- ret = -EFAULT;
- goto err_mixerdrv;
+ return -EFAULT;
}
ctx->hdmi_ctx->drm_dev = drm_dev;
@@ -298,42 +308,12 @@ static int hdmi_subdrv_probe(struct drm_device *drm_dev,
to_context(pd->mixer_dev);
if (!ctx->mixer_ctx) {
DRM_DEBUG_KMS("mixer context is null.\n");
- ret = -EFAULT;
- goto err_mixerdrv;
+ return -EFAULT;
}
ctx->mixer_ctx->drm_dev = drm_dev;
return 0;
-
-err_mixerdrv:
- platform_driver_unregister(&mixer_driver);
-err_hdmidrv:
- platform_driver_unregister(&hdmi_driver);
- return ret;
-}
-
-static void hdmi_subdrv_remove(struct drm_device *drm_dev)
-{
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- platform_driver_unregister(&hdmi_driver);
- platform_driver_unregister(&mixer_driver);
-}
-
-static void exynos_drm_hdmi_late_probe(struct work_struct *work)
-{
- struct drm_hdmi_context *ctx = container_of(work,
- struct drm_hdmi_context, work);
-
- /*
- * this function calls subdrv->probe() so this must be called
- * after probe context.
- *
- * PS. subdrv->probe() will call platform_driver_register() to probe
- * hdmi and mixer driver.
- */
- exynos_drm_subdrv_register(&ctx->subdrv);
}
static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
@@ -353,7 +333,6 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
subdrv = &ctx->subdrv;
subdrv->probe = hdmi_subdrv_probe;
- subdrv->remove = hdmi_subdrv_remove;
subdrv->manager.pipe = -1;
subdrv->manager.ops = &drm_hdmi_manager_ops;
subdrv->manager.overlay_ops = &drm_hdmi_overlay_ops;
@@ -362,9 +341,7 @@ static int __devinit exynos_drm_hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, subdrv);
- INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe);
-
- schedule_work(&ctx->work);
+ exynos_drm_subdrv_register(subdrv);
return 0;
}
@@ -400,7 +377,7 @@ static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
return 0;
}
-static struct platform_driver exynos_drm_common_hdmi_driver = {
+struct platform_driver exynos_drm_common_hdmi_driver = {
.probe = exynos_drm_hdmi_probe,
.remove = __devexit_p(exynos_drm_hdmi_remove),
.driver = {
@@ -409,31 +386,3 @@ static struct platform_driver exynos_drm_common_hdmi_driver = {
.pm = &hdmi_pm_ops,
},
};
-
-static int __init exynos_drm_hdmi_init(void)
-{
- int ret;
-
- DRM_DEBUG_KMS("%s\n", __FILE__);
-
- ret = platform_driver_register(&exynos_drm_common_hdmi_driver);
- if (ret) {
- DRM_DEBUG_KMS("failed to register hdmi common driver.\n");
- return ret;
- }
-
- return ret;
-}
-
-static void __exit exynos_drm_hdmi_exit(void)
-{
- platform_driver_unregister(&exynos_drm_common_hdmi_driver);
-}
-
-module_init(exynos_drm_hdmi_init);
-module_exit(exynos_drm_hdmi_exit);
-
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_DESCRIPTION("Samsung SoC DRM HDMI Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
index 3c29f790ee45..44497cfb6c74 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_drm_hdmi.h
@@ -47,7 +47,12 @@ struct exynos_hdmi_display_ops {
};
struct exynos_hdmi_manager_ops {
+ void (*mode_fixup)(void *ctx, struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode);
void (*mode_set)(void *ctx, void *mode);
+ void (*get_max_resol)(void *ctx, unsigned int *width,
+ unsigned int *height);
void (*commit)(void *ctx);
void (*disable)(void *ctx);
};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_plane.c b/drivers/gpu/drm/exynos/exynos_drm_plane.c
index bdcf770aa22e..c277a3a445f5 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_plane.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_plane.c
@@ -22,6 +22,10 @@ struct exynos_plane {
bool enabled;
};
+static const uint32_t formats[] = {
+ DRM_FORMAT_XRGB8888,
+};
+
static int
exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
@@ -115,9 +119,9 @@ int exynos_plane_init(struct drm_device *dev, unsigned int nr)
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
- /* TODO: format */
return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
- &exynos_plane_funcs, NULL, 0, false);
+ &exynos_plane_funcs, formats, ARRAY_SIZE(formats),
+ false);
}
int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
new file mode 100644
index 000000000000..8e1339f9fe1f
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c
@@ -0,0 +1,676 @@
+/* exynos_drm_vidi.c
+ *
+ * Copyright (C) 2012 Samsung Electronics Co.Ltd
+ * Authors:
+ * Inki Dae <inki.dae@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include "drmP.h"
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <drm/exynos_drm.h>
+
+#include "drm_edid.h"
+#include "drm_crtc_helper.h"
+
+#include "exynos_drm_drv.h"
+#include "exynos_drm_crtc.h"
+#include "exynos_drm_encoder.h"
+
+/* vidi has totally three virtual windows. */
+#define WINDOWS_NR 3
+
+#define get_vidi_context(dev) platform_get_drvdata(to_platform_device(dev))
+
+struct vidi_win_data {
+ unsigned int offset_x;
+ unsigned int offset_y;
+ unsigned int ovl_width;
+ unsigned int ovl_height;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int bpp;
+ dma_addr_t dma_addr;
+ void __iomem *vaddr;
+ unsigned int buf_offsize;
+ unsigned int line_size; /* bytes */
+ bool enabled;
+};
+
+struct vidi_context {
+ struct exynos_drm_subdrv subdrv;
+ struct drm_crtc *crtc;
+ struct vidi_win_data win_data[WINDOWS_NR];
+ struct edid *raw_edid;
+ unsigned int clkdiv;
+ unsigned int default_win;
+ unsigned long irq_flags;
+ unsigned int connected;
+ bool vblank_on;
+ bool suspended;
+ struct work_struct work;
+ struct mutex lock;
+};
+
+static const char fake_edid_info[] = {
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x4c, 0x2d, 0x05, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x30, 0x12, 0x01, 0x03, 0x80, 0x10, 0x09, 0x78,
+ 0x0a, 0xee, 0x91, 0xa3, 0x54, 0x4c, 0x99, 0x26, 0x0f, 0x50, 0x54, 0xbd,
+ 0xee, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x66, 0x21, 0x50, 0xb0, 0x51, 0x00,
+ 0x1b, 0x30, 0x40, 0x70, 0x36, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e,
+ 0x01, 0x1d, 0x00, 0x72, 0x51, 0xd0, 0x1e, 0x20, 0x6e, 0x28, 0x55, 0x00,
+ 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0xfd, 0x00, 0x18,
+ 0x4b, 0x1a, 0x44, 0x17, 0x00, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+ 0x00, 0x00, 0x00, 0xfc, 0x00, 0x53, 0x41, 0x4d, 0x53, 0x55, 0x4e, 0x47,
+ 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x01, 0xbc, 0x02, 0x03, 0x1e, 0xf1,
+ 0x46, 0x84, 0x05, 0x03, 0x10, 0x20, 0x22, 0x23, 0x09, 0x07, 0x07, 0x83,
+ 0x01, 0x00, 0x00, 0xe2, 0x00, 0x0f, 0x67, 0x03, 0x0c, 0x00, 0x10, 0x00,
+ 0xb8, 0x2d, 0x01, 0x1d, 0x80, 0x18, 0x71, 0x1c, 0x16, 0x20, 0x58, 0x2c,
+ 0x25, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x9e, 0x8c, 0x0a, 0xd0, 0x8a,
+ 0x20, 0xe0, 0x2d, 0x10, 0x10, 0x3e, 0x96, 0x00, 0xa0, 0x5a, 0x00, 0x00,
+ 0x00, 0x18, 0x02, 0x3a, 0x80, 0x18, 0x71, 0x38, 0x2d, 0x40, 0x58, 0x2c,
+ 0x45, 0x00, 0xa0, 0x5a, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x06
+};
+
+static void vidi_fake_vblank_handler(struct work_struct *work);
+
+static bool vidi_display_is_connected(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * connection request would come from user side
+ * to do hotplug through specific ioctl.
+ */
+ return ctx->connected ? true : false;
+}
+
+static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
+ u8 *edid, int len)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct edid *raw_edid;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * the edid data comes from user side and it would be set
+ * to ctx->raw_edid through specific ioctl.
+ */
+ if (!ctx->raw_edid) {
+ DRM_DEBUG_KMS("raw_edid is null.\n");
+ return -EFAULT;
+ }
+
+ raw_edid = kzalloc(len, GFP_KERNEL);
+ if (!raw_edid) {
+ DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
+ return -ENOMEM;
+ }
+
+ memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
+ * EDID_LENGTH, len));
+
+ /* attach the edid data to connector. */
+ connector->display_info.raw_edid = (char *)raw_edid;
+
+ memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
+ * EDID_LENGTH, len));
+
+ return 0;
+}
+
+static void *vidi_get_panel(struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return NULL;
+}
+
+static int vidi_check_timing(struct device *dev, void *timing)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+
+ return 0;
+}
+
+static int vidi_display_power_on(struct device *dev, int mode)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO */
+
+ return 0;
+}
+
+static struct exynos_drm_display_ops vidi_display_ops = {
+ .type = EXYNOS_DISPLAY_TYPE_VIDI,
+ .is_connected = vidi_display_is_connected,
+ .get_edid = vidi_get_edid,
+ .get_panel = vidi_get_panel,
+ .check_timing = vidi_check_timing,
+ .power_on = vidi_display_power_on,
+};
+
+static void vidi_dpms(struct device *subdrv_dev, int mode)
+{
+ struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+
+ DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
+
+ mutex_lock(&ctx->lock);
+
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ /* TODO. */
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+ /* TODO. */
+ break;
+ default:
+ DRM_DEBUG_KMS("unspecified mode %d\n", mode);
+ break;
+ }
+
+ mutex_unlock(&ctx->lock);
+}
+
+static void vidi_apply(struct device *subdrv_dev)
+{
+ struct vidi_context *ctx = get_vidi_context(subdrv_dev);
+ struct exynos_drm_manager *mgr = &ctx->subdrv.manager;
+ struct exynos_drm_manager_ops *mgr_ops = mgr->ops;
+ struct exynos_drm_overlay_ops *ovl_ops = mgr->overlay_ops;
+ struct vidi_win_data *win_data;
+ int i;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ for (i = 0; i < WINDOWS_NR; i++) {
+ win_data = &ctx->win_data[i];
+ if (win_data->enabled && (ovl_ops && ovl_ops->commit))
+ ovl_ops->commit(subdrv_dev, i);
+ }
+
+ if (mgr_ops && mgr_ops->commit)
+ mgr_ops->commit(subdrv_dev);
+}
+
+static void vidi_commit(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+}
+
+static int vidi_enable_vblank(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return -EPERM;
+
+ if (!test_and_set_bit(0, &ctx->irq_flags))
+ ctx->vblank_on = true;
+
+ return 0;
+}
+
+static void vidi_disable_vblank(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ ctx->vblank_on = false;
+}
+
+static struct exynos_drm_manager_ops vidi_manager_ops = {
+ .dpms = vidi_dpms,
+ .apply = vidi_apply,
+ .commit = vidi_commit,
+ .enable_vblank = vidi_enable_vblank,
+ .disable_vblank = vidi_disable_vblank,
+};
+
+static void vidi_win_mode_set(struct device *dev,
+ struct exynos_drm_overlay *overlay)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_win_data *win_data;
+ int win;
+ unsigned long offset;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!overlay) {
+ dev_err(dev, "overlay is NULL\n");
+ return;
+ }
+
+ win = overlay->zpos;
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ offset = overlay->fb_x * (overlay->bpp >> 3);
+ offset += overlay->fb_y * overlay->pitch;
+
+ DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
+
+ win_data = &ctx->win_data[win];
+
+ win_data->offset_x = overlay->crtc_x;
+ win_data->offset_y = overlay->crtc_y;
+ win_data->ovl_width = overlay->crtc_width;
+ win_data->ovl_height = overlay->crtc_height;
+ win_data->fb_width = overlay->fb_width;
+ win_data->fb_height = overlay->fb_height;
+ win_data->dma_addr = overlay->dma_addr[0] + offset;
+ win_data->vaddr = overlay->vaddr[0] + offset;
+ win_data->bpp = overlay->bpp;
+ win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
+ (overlay->bpp >> 3);
+ win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
+
+ /*
+ * some parts of win_data should be transferred to user side
+ * through specific ioctl.
+ */
+
+ DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
+ win_data->offset_x, win_data->offset_y);
+ DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
+ win_data->ovl_width, win_data->ovl_height);
+ DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
+ (unsigned long)win_data->dma_addr,
+ (unsigned long)win_data->vaddr);
+ DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
+ overlay->fb_width, overlay->crtc_width);
+}
+
+static void vidi_win_commit(struct device *dev, int zpos)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_win_data *win_data;
+ int win = zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (ctx->suspended)
+ return;
+
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+
+ win_data->enabled = true;
+
+ DRM_DEBUG_KMS("dma_addr = 0x%x\n", win_data->dma_addr);
+
+ if (ctx->vblank_on)
+ schedule_work(&ctx->work);
+}
+
+static void vidi_win_disable(struct device *dev, int zpos)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ struct vidi_win_data *win_data;
+ int win = zpos;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (win == DEFAULT_ZPOS)
+ win = ctx->default_win;
+
+ if (win < 0 || win > WINDOWS_NR)
+ return;
+
+ win_data = &ctx->win_data[win];
+ win_data->enabled = false;
+
+ /* TODO. */
+}
+
+static struct exynos_drm_overlay_ops vidi_overlay_ops = {
+ .mode_set = vidi_win_mode_set,
+ .commit = vidi_win_commit,
+ .disable = vidi_win_disable,
+};
+
+static void vidi_finish_pageflip(struct drm_device *drm_dev, int crtc)
+{
+ struct exynos_drm_private *dev_priv = drm_dev->dev_private;
+ struct drm_pending_vblank_event *e, *t;
+ struct timeval now;
+ unsigned long flags;
+ bool is_checked = false;
+
+ spin_lock_irqsave(&drm_dev->event_lock, flags);
+
+ list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
+ base.link) {
+ /* if event's pipe isn't same as crtc then ignore it. */
+ if (crtc != e->pipe)
+ continue;
+
+ is_checked = true;
+
+ do_gettimeofday(&now);
+ e->event.sequence = 0;
+ e->event.tv_sec = now.tv_sec;
+ e->event.tv_usec = now.tv_usec;
+
+ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
+ wake_up_interruptible(&e->base.file_priv->event_wait);
+ }
+
+ if (is_checked) {
+ /*
+ * call drm_vblank_put only in case that drm_vblank_get was
+ * called.
+ */
+ if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+ drm_vblank_put(drm_dev, crtc);
+
+ /*
+ * don't off vblank if vblank_disable_allowed is 1,
+ * because vblank would be off by timer handler.
+ */
+ if (!drm_dev->vblank_disable_allowed)
+ drm_vblank_off(drm_dev, crtc);
+ }
+
+ spin_unlock_irqrestore(&drm_dev->event_lock, flags);
+}
+
+static void vidi_fake_vblank_handler(struct work_struct *work)
+{
+ struct vidi_context *ctx = container_of(work, struct vidi_context,
+ work);
+ struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+ struct exynos_drm_manager *manager = &subdrv->manager;
+
+ if (manager->pipe < 0)
+ return;
+
+ /* refresh rate is about 50Hz. */
+ usleep_range(16000, 20000);
+
+ drm_handle_vblank(subdrv->drm_dev, manager->pipe);
+ vidi_finish_pageflip(subdrv->drm_dev, manager->pipe);
+}
+
+static int vidi_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /*
+ * enable drm irq mode.
+ * - with irq_enabled = 1, we can use the vblank feature.
+ *
+ * P.S. note that we wouldn't use drm irq handler but
+ * just specific driver own one instead because
+ * drm framework supports only one irq handler.
+ */
+ drm_dev->irq_enabled = 1;
+
+ /*
+ * with vblank_disable_allowed = 1, vblank interrupt will be disabled
+ * by drm timer once a current process gives up ownership of
+ * vblank event.(after drm_vblank_put function is called)
+ */
+ drm_dev->vblank_disable_allowed = 1;
+
+ return 0;
+}
+
+static void vidi_subdrv_remove(struct drm_device *drm_dev)
+{
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ /* TODO. */
+}
+
+static int vidi_power_on(struct vidi_context *ctx, bool enable)
+{
+ struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
+ struct device *dev = subdrv->manager.dev;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (enable != false && enable != true)
+ return -EINVAL;
+
+ if (enable) {
+ ctx->suspended = false;
+
+ /* if vblank was enabled status, enable it again. */
+ if (test_and_clear_bit(0, &ctx->irq_flags))
+ vidi_enable_vblank(dev);
+
+ vidi_apply(dev);
+ } else {
+ ctx->suspended = true;
+ }
+
+ return 0;
+}
+
+static int vidi_show_connection(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rc;
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ mutex_lock(&ctx->lock);
+
+ rc = sprintf(buf, "%d\n", ctx->connected);
+
+ mutex_unlock(&ctx->lock);
+
+ return rc;
+}
+
+static int vidi_store_connection(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ret = kstrtoint(buf, 0, &ctx->connected);
+ if (ret)
+ return ret;
+
+ if (ctx->connected > 1)
+ return -EINVAL;
+
+ DRM_DEBUG_KMS("requested connection.\n");
+
+ drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+
+ return len;
+}
+
+static DEVICE_ATTR(connection, 0644, vidi_show_connection,
+ vidi_store_connection);
+
+int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
+ struct drm_file *file_priv)
+{
+ struct vidi_context *ctx = NULL;
+ struct drm_encoder *encoder;
+ struct exynos_drm_manager *manager;
+ struct exynos_drm_display_ops *display_ops;
+ struct drm_exynos_vidi_connection *vidi = data;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ if (!vidi) {
+ DRM_DEBUG_KMS("user data for vidi is null.\n");
+ return -EINVAL;
+ }
+
+ if (!vidi->edid) {
+ DRM_DEBUG_KMS("edid data is null.\n");
+ return -EINVAL;
+ }
+
+ if (vidi->connection > 1) {
+ DRM_DEBUG_KMS("connection should be 0 or 1.\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(encoder, &drm_dev->mode_config.encoder_list,
+ head) {
+ manager = exynos_drm_get_manager(encoder);
+ display_ops = manager->display_ops;
+
+ if (display_ops->type == EXYNOS_DISPLAY_TYPE_VIDI) {
+ ctx = get_vidi_context(manager->dev);
+ break;
+ }
+ }
+
+ if (!ctx) {
+ DRM_DEBUG_KMS("not found virtual device type encoder.\n");
+ return -EINVAL;
+ }
+
+ if (ctx->connected == vidi->connection) {
+ DRM_DEBUG_KMS("same connection request.\n");
+ return -EINVAL;
+ }
+
+ if (vidi->connection)
+ ctx->raw_edid = (struct edid *)vidi->edid;
+
+ ctx->connected = vidi->connection;
+ drm_helper_hpd_irq_event(ctx->subdrv.drm_dev);
+
+ return 0;
+}
+
+static int __devinit vidi_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vidi_context *ctx;
+ struct exynos_drm_subdrv *subdrv;
+ int ret;
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+ if (!ctx)
+ return -ENOMEM;
+
+ ctx->default_win = 0;
+
+ INIT_WORK(&ctx->work, vidi_fake_vblank_handler);
+
+ /* for test */
+ ctx->raw_edid = (struct edid *)fake_edid_info;
+
+ subdrv = &ctx->subdrv;
+ subdrv->probe = vidi_subdrv_probe;
+ subdrv->remove = vidi_subdrv_remove;
+ subdrv->manager.pipe = -1;
+ subdrv->manager.ops = &vidi_manager_ops;
+ subdrv->manager.overlay_ops = &vidi_overlay_ops;
+ subdrv->manager.display_ops = &vidi_display_ops;
+ subdrv->manager.dev = dev;
+
+ mutex_init(&ctx->lock);
+
+ platform_set_drvdata(pdev, ctx);
+
+ ret = device_create_file(&pdev->dev, &dev_attr_connection);
+ if (ret < 0)
+ DRM_INFO("failed to create connection sysfs.\n");
+
+ exynos_drm_subdrv_register(subdrv);
+
+ return 0;
+}
+
+static int __devexit vidi_remove(struct platform_device *pdev)
+{
+ struct vidi_context *ctx = platform_get_drvdata(pdev);
+
+ DRM_DEBUG_KMS("%s\n", __FILE__);
+
+ exynos_drm_subdrv_unregister(&ctx->subdrv);
+
+ kfree(ctx);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int vidi_suspend(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ return vidi_power_on(ctx, false);
+}
+
+static int vidi_resume(struct device *dev)
+{
+ struct vidi_context *ctx = get_vidi_context(dev);
+
+ return vidi_power_on(ctx, true);
+}
+#endif
+
+static const struct dev_pm_ops vidi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(vidi_suspend, vidi_resume)
+};
+
+struct platform_driver vidi_driver = {
+ .probe = vidi_probe,
+ .remove = __devexit_p(vidi_remove),
+ .driver = {
+ .name = "exynos-drm-vidi",
+ .owner = THIS_MODULE,
+ .pm = &vidi_pm_ops,
+ },
+};
diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.h b/drivers/gpu/drm/exynos/exynos_drm_vidi.h
new file mode 100644
index 000000000000..a4babe4e65d7
--- /dev/null
+++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.h
@@ -0,0 +1,36 @@
+/* exynos_drm_vidi.h
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ * Author: Inki Dae <inki.dae@samsung.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _EXYNOS_DRM_VIDI_H_
+#define _EXYNOS_DRM_VIDI_H_
+
+#ifdef CONFIG_DRM_EXYNOS_VIDI
+int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,
+ struct drm_file *file_priv);
+#else
+#define vidi_connection_ioctl NULL
+#endif
+
+#endif
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c
index 3429d3fd93f3..575a8cbd3533 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.c
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.c
@@ -41,44 +41,83 @@
#include "exynos_hdmi.h"
#define HDMI_OVERLAY_NUMBER 3
+#define MAX_WIDTH 1920
+#define MAX_HEIGHT 1080
#define get_hdmi_context(dev) platform_get_drvdata(to_platform_device(dev))
-static const u8 hdmiphy_conf27[32] = {
+struct hdmi_resources {
+ struct clk *hdmi;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_pixel;
+ struct clk *sclk_hdmiphy;
+ struct clk *hdmiphy;
+ struct regulator_bulk_data *regul_bulk;
+ int regul_count;
+};
+
+struct hdmi_context {
+ struct device *dev;
+ struct drm_device *drm_dev;
+ struct fb_videomode *default_timing;
+ unsigned int is_v13:1;
+ unsigned int default_win;
+ unsigned int default_bpp;
+ bool hpd_handle;
+ bool enabled;
+
+ struct resource *regs_res;
+ void __iomem *regs;
+ unsigned int irq;
+ struct workqueue_struct *wq;
+ struct work_struct hotplug_work;
+
+ struct i2c_client *ddc_port;
+ struct i2c_client *hdmiphy_port;
+
+ /* current hdmiphy conf index */
+ int cur_conf;
+
+ struct hdmi_resources res;
+ void *parent_ctx;
+};
+
+/* HDMI Version 1.3 */
+static const u8 hdmiphy_v13_conf27[32] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40,
0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf27_027[32] = {
+static const u8 hdmiphy_v13_conf27_027[32] = {
0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64,
0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf74_175[32] = {
+static const u8 hdmiphy_v13_conf74_175[32] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B,
0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9,
0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf74_25[32] = {
+static const u8 hdmiphy_v13_conf74_25[32] = {
0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40,
0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba,
0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xe0,
0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00,
};
-static const u8 hdmiphy_conf148_5[32] = {
+static const u8 hdmiphy_v13_conf148_5[32] = {
0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40,
0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba,
0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0,
0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00,
};
-struct hdmi_tg_regs {
+struct hdmi_v13_tg_regs {
u8 cmd;
u8 h_fsz_l;
u8 h_fsz_h;
@@ -110,7 +149,7 @@ struct hdmi_tg_regs {
u8 field_bot_hdmi_h;
};
-struct hdmi_core_regs {
+struct hdmi_v13_core_regs {
u8 h_blank[2];
u8 v_blank[3];
u8 h_v_line[3];
@@ -123,12 +162,21 @@ struct hdmi_core_regs {
u8 v_sync_gen3[3];
};
-struct hdmi_preset_conf {
- struct hdmi_core_regs core;
- struct hdmi_tg_regs tg;
+struct hdmi_v13_preset_conf {
+ struct hdmi_v13_core_regs core;
+ struct hdmi_v13_tg_regs tg;
};
-static const struct hdmi_preset_conf hdmi_conf_480p = {
+struct hdmi_v13_conf {
+ int width;
+ int height;
+ int vrefresh;
+ bool interlace;
+ const u8 *hdmiphy_data;
+ const struct hdmi_v13_preset_conf *conf;
+};
+
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_480p = {
.core = {
.h_blank = {0x8a, 0x00},
.v_blank = {0x0d, 0x6a, 0x01},
@@ -154,7 +202,7 @@ static const struct hdmi_preset_conf hdmi_conf_480p = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_720p60 = {
.core = {
.h_blank = {0x72, 0x01},
.v_blank = {0xee, 0xf2, 0x00},
@@ -182,7 +230,7 @@ static const struct hdmi_preset_conf hdmi_conf_720p60 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i50 = {
.core = {
.h_blank = {0xd0, 0x02},
.v_blank = {0x32, 0xB2, 0x00},
@@ -210,7 +258,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p50 = {
.core = {
.h_blank = {0xd0, 0x02},
.v_blank = {0x65, 0x6c, 0x01},
@@ -238,7 +286,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080i60 = {
.core = {
.h_blank = {0x18, 0x01},
.v_blank = {0x32, 0xB2, 0x00},
@@ -266,7 +314,7 @@ static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
},
};
-static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+static const struct hdmi_v13_preset_conf hdmi_v13_conf_1080p60 = {
.core = {
.h_blank = {0x18, 0x01},
.v_blank = {0x65, 0x6c, 0x01},
@@ -294,13 +342,530 @@ static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
},
};
+static const struct hdmi_v13_conf hdmi_v13_confs[] = {
+ { 1280, 720, 60, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 },
+ { 1280, 720, 50, false, hdmiphy_v13_conf74_25, &hdmi_v13_conf_720p60 },
+ { 720, 480, 60, false, hdmiphy_v13_conf27_027, &hdmi_v13_conf_480p },
+ { 1920, 1080, 50, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i50 },
+ { 1920, 1080, 50, false, hdmiphy_v13_conf148_5,
+ &hdmi_v13_conf_1080p50 },
+ { 1920, 1080, 60, true, hdmiphy_v13_conf74_25, &hdmi_v13_conf_1080i60 },
+ { 1920, 1080, 60, false, hdmiphy_v13_conf148_5,
+ &hdmi_v13_conf_1080p60 },
+};
+
+/* HDMI Version 1.4 */
+static const u8 hdmiphy_conf27_027[32] = {
+ 0x01, 0xd1, 0x2d, 0x72, 0x40, 0x64, 0x12, 0x08,
+ 0x43, 0xa0, 0x0e, 0xd9, 0x45, 0xa0, 0xac, 0x80,
+ 0x08, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xe3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00,
+};
+
+static const u8 hdmiphy_conf74_25[32] = {
+ 0x01, 0xd1, 0x1f, 0x10, 0x40, 0x40, 0xf8, 0x08,
+ 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0xa5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00,
+};
+
+static const u8 hdmiphy_conf148_5[32] = {
+ 0x01, 0xd1, 0x1f, 0x00, 0x40, 0x40, 0xf8, 0x08,
+ 0x81, 0xa0, 0xba, 0xd8, 0x45, 0xa0, 0xac, 0x80,
+ 0x3c, 0x80, 0x11, 0x04, 0x02, 0x22, 0x44, 0x86,
+ 0x54, 0x4b, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00,
+};
+
+struct hdmi_tg_regs {
+ u8 cmd;
+ u8 h_fsz_l;
+ u8 h_fsz_h;
+ u8 hact_st_l;
+ u8 hact_st_h;
+ u8 hact_sz_l;
+ u8 hact_sz_h;
+ u8 v_fsz_l;
+ u8 v_fsz_h;
+ u8 vsync_l;
+ u8 vsync_h;
+ u8 vsync2_l;
+ u8 vsync2_h;
+ u8 vact_st_l;
+ u8 vact_st_h;
+ u8 vact_sz_l;
+ u8 vact_sz_h;
+ u8 field_chg_l;
+ u8 field_chg_h;
+ u8 vact_st2_l;
+ u8 vact_st2_h;
+ u8 vact_st3_l;
+ u8 vact_st3_h;
+ u8 vact_st4_l;
+ u8 vact_st4_h;
+ u8 vsync_top_hdmi_l;
+ u8 vsync_top_hdmi_h;
+ u8 vsync_bot_hdmi_l;
+ u8 vsync_bot_hdmi_h;
+ u8 field_top_hdmi_l;
+ u8 field_top_hdmi_h;
+ u8 field_bot_hdmi_l;
+ u8 field_bot_hdmi_h;
+ u8 tg_3d;
+};
+
+struct hdmi_core_regs {
+ u8 h_blank[2];
+ u8 v2_blank[2];
+ u8 v1_blank[2];
+ u8 v_line[2];
+ u8 h_line[2];
+ u8 hsync_pol[1];
+ u8 vsync_pol[1];
+ u8 int_pro_mode[1];
+ u8 v_blank_f0[2];
+ u8 v_blank_f1[2];
+ u8 h_sync_start[2];
+ u8 h_sync_end[2];
+ u8 v_sync_line_bef_2[2];
+ u8 v_sync_line_bef_1[2];
+ u8 v_sync_line_aft_2[2];
+ u8 v_sync_line_aft_1[2];
+ u8 v_sync_line_aft_pxl_2[2];
+ u8 v_sync_line_aft_pxl_1[2];
+ u8 v_blank_f2[2]; /* for 3D mode */
+ u8 v_blank_f3[2]; /* for 3D mode */
+ u8 v_blank_f4[2]; /* for 3D mode */
+ u8 v_blank_f5[2]; /* for 3D mode */
+ u8 v_sync_line_aft_3[2];
+ u8 v_sync_line_aft_4[2];
+ u8 v_sync_line_aft_5[2];
+ u8 v_sync_line_aft_6[2];
+ u8 v_sync_line_aft_pxl_3[2];
+ u8 v_sync_line_aft_pxl_4[2];
+ u8 v_sync_line_aft_pxl_5[2];
+ u8 v_sync_line_aft_pxl_6[2];
+ u8 vact_space_1[2];
+ u8 vact_space_2[2];
+ u8 vact_space_3[2];
+ u8 vact_space_4[2];
+ u8 vact_space_5[2];
+ u8 vact_space_6[2];
+};
+
+struct hdmi_preset_conf {
+ struct hdmi_core_regs core;
+ struct hdmi_tg_regs tg;
+};
+
+struct hdmi_conf {
+ int width;
+ int height;
+ int vrefresh;
+ bool interlace;
+ const u8 *hdmiphy_data;
+ const struct hdmi_preset_conf *conf;
+};
+
+static const struct hdmi_preset_conf hdmi_conf_480p60 = {
+ .core = {
+ .h_blank = {0x8a, 0x00},
+ .v2_blank = {0x0d, 0x02},
+ .v1_blank = {0x2d, 0x00},
+ .v_line = {0x0d, 0x02},
+ .h_line = {0x5a, 0x03},
+ .hsync_pol = {0x01},
+ .vsync_pol = {0x01},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x0e, 0x00},
+ .h_sync_end = {0x4c, 0x00},
+ .v_sync_line_bef_2 = {0x0f, 0x00},
+ .v_sync_line_bef_1 = {0x09, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x5a, 0x03, /* h_fsz */
+ 0x8a, 0x00, 0xd0, 0x02, /* hact */
+ 0x0d, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0xe0, 0x01, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p50 = {
+ .core = {
+ .h_blank = {0xbc, 0x02},
+ .v2_blank = {0xee, 0x02},
+ .v1_blank = {0x1e, 0x00},
+ .v_line = {0xee, 0x02},
+ .h_line = {0xbc, 0x07},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0xb6, 0x01},
+ .h_sync_end = {0xde, 0x01},
+ .v_sync_line_bef_2 = {0x0a, 0x00},
+ .v_sync_line_bef_1 = {0x05, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0xbc, 0x07, /* h_fsz */
+ 0xbc, 0x02, 0x00, 0x05, /* hact */
+ 0xee, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x1e, 0x00, 0xd0, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_720p60 = {
+ .core = {
+ .h_blank = {0x72, 0x01},
+ .v2_blank = {0xee, 0x02},
+ .v1_blank = {0x1e, 0x00},
+ .v_line = {0xee, 0x02},
+ .h_line = {0x72, 0x06},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x6c, 0x00},
+ .h_sync_end = {0x94, 0x00},
+ .v_sync_line_bef_2 = {0x0a, 0x00},
+ .v_sync_line_bef_1 = {0x05, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x72, 0x06, /* h_fsz */
+ 0x72, 0x01, 0x00, 0x05, /* hact */
+ 0xee, 0x02, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x1e, 0x00, 0xd0, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080i50 = {
+ .core = {
+ .h_blank = {0xd0, 0x02},
+ .v2_blank = {0x32, 0x02},
+ .v1_blank = {0x16, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x50, 0x0a},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x01},
+ .v_blank_f0 = {0x49, 0x02},
+ .v_blank_f1 = {0x65, 0x04},
+ .h_sync_start = {0x0e, 0x02},
+ .h_sync_end = {0x3a, 0x02},
+ .v_sync_line_bef_2 = {0x07, 0x00},
+ .v_sync_line_bef_1 = {0x02, 0x00},
+ .v_sync_line_aft_2 = {0x39, 0x02},
+ .v_sync_line_aft_1 = {0x34, 0x02},
+ .v_sync_line_aft_pxl_2 = {0x38, 0x07},
+ .v_sync_line_aft_pxl_1 = {0x38, 0x07},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x50, 0x0a, /* h_fsz */
+ 0xd0, 0x02, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x16, 0x00, 0x1c, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080i60 = {
+ .core = {
+ .h_blank = {0x18, 0x01},
+ .v2_blank = {0x32, 0x02},
+ .v1_blank = {0x16, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x98, 0x08},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x01},
+ .v_blank_f0 = {0x49, 0x02},
+ .v_blank_f1 = {0x65, 0x04},
+ .h_sync_start = {0x56, 0x00},
+ .h_sync_end = {0x82, 0x00},
+ .v_sync_line_bef_2 = {0x07, 0x00},
+ .v_sync_line_bef_1 = {0x02, 0x00},
+ .v_sync_line_aft_2 = {0x39, 0x02},
+ .v_sync_line_aft_1 = {0x34, 0x02},
+ .v_sync_line_aft_pxl_2 = {0xa4, 0x04},
+ .v_sync_line_aft_pxl_1 = {0xa4, 0x04},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x16, 0x00, 0x1c, 0x02, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x49, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p50 = {
+ .core = {
+ .h_blank = {0xd0, 0x02},
+ .v2_blank = {0x65, 0x04},
+ .v1_blank = {0x2d, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x50, 0x0a},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x0e, 0x02},
+ .h_sync_end = {0x3a, 0x02},
+ .v_sync_line_bef_2 = {0x09, 0x00},
+ .v_sync_line_bef_1 = {0x04, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ .vact_space_1 = {0xff, 0xff},
+ .vact_space_2 = {0xff, 0xff},
+ .vact_space_3 = {0xff, 0xff},
+ .vact_space_4 = {0xff, 0xff},
+ .vact_space_5 = {0xff, 0xff},
+ .vact_space_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x50, 0x0a, /* h_fsz */
+ 0xd0, 0x02, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
+static const struct hdmi_preset_conf hdmi_conf_1080p60 = {
+ .core = {
+ .h_blank = {0x18, 0x01},
+ .v2_blank = {0x65, 0x04},
+ .v1_blank = {0x2d, 0x00},
+ .v_line = {0x65, 0x04},
+ .h_line = {0x98, 0x08},
+ .hsync_pol = {0x00},
+ .vsync_pol = {0x00},
+ .int_pro_mode = {0x00},
+ .v_blank_f0 = {0xff, 0xff},
+ .v_blank_f1 = {0xff, 0xff},
+ .h_sync_start = {0x56, 0x00},
+ .h_sync_end = {0x82, 0x00},
+ .v_sync_line_bef_2 = {0x09, 0x00},
+ .v_sync_line_bef_1 = {0x04, 0x00},
+ .v_sync_line_aft_2 = {0xff, 0xff},
+ .v_sync_line_aft_1 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_2 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_1 = {0xff, 0xff},
+ .v_blank_f2 = {0xff, 0xff},
+ .v_blank_f3 = {0xff, 0xff},
+ .v_blank_f4 = {0xff, 0xff},
+ .v_blank_f5 = {0xff, 0xff},
+ .v_sync_line_aft_3 = {0xff, 0xff},
+ .v_sync_line_aft_4 = {0xff, 0xff},
+ .v_sync_line_aft_5 = {0xff, 0xff},
+ .v_sync_line_aft_6 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_3 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_4 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_5 = {0xff, 0xff},
+ .v_sync_line_aft_pxl_6 = {0xff, 0xff},
+ /* other don't care */
+ },
+ .tg = {
+ 0x00, /* cmd */
+ 0x98, 0x08, /* h_fsz */
+ 0x18, 0x01, 0x80, 0x07, /* hact */
+ 0x65, 0x04, /* v_fsz */
+ 0x01, 0x00, 0x33, 0x02, /* vsync */
+ 0x2d, 0x00, 0x38, 0x04, /* vact */
+ 0x33, 0x02, /* field_chg */
+ 0x48, 0x02, /* vact_st2 */
+ 0x00, 0x00, /* vact_st3 */
+ 0x00, 0x00, /* vact_st4 */
+ 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */
+ 0x01, 0x00, 0x33, 0x02, /* field top/bot */
+ 0x00, /* 3d FP */
+ },
+};
+
static const struct hdmi_conf hdmi_confs[] = {
+ { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p60 },
+ { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p50 },
{ 1280, 720, 60, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
- { 1280, 720, 50, false, hdmiphy_conf74_25, &hdmi_conf_720p60 },
- { 720, 480, 60, false, hdmiphy_conf27_027, &hdmi_conf_480p },
{ 1920, 1080, 50, true, hdmiphy_conf74_25, &hdmi_conf_1080i50 },
- { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
{ 1920, 1080, 60, true, hdmiphy_conf74_25, &hdmi_conf_1080i60 },
+ { 1920, 1080, 50, false, hdmiphy_conf148_5, &hdmi_conf_1080p50 },
{ 1920, 1080, 60, false, hdmiphy_conf148_5, &hdmi_conf_1080p60 },
};
@@ -324,7 +889,7 @@ static inline void hdmi_reg_writemask(struct hdmi_context *hdata,
writel(value, hdata->regs + reg_id);
}
-static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
+static void hdmi_v13_regs_dump(struct hdmi_context *hdata, char *prefix)
{
#define DUMPREG(reg_id) \
DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
@@ -333,6 +898,101 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_INTC_FLAG);
DUMPREG(HDMI_INTC_CON);
DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_V13_PHY_RSTOUT);
+ DUMPREG(HDMI_V13_PHY_VPLL);
+ DUMPREG(HDMI_V13_PHY_CMU);
+ DUMPREG(HDMI_V13_CORE_RSTOUT);
+
+ DRM_DEBUG_KMS("%s: ---- CORE REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_CON_0);
+ DUMPREG(HDMI_CON_1);
+ DUMPREG(HDMI_CON_2);
+ DUMPREG(HDMI_SYS_STATUS);
+ DUMPREG(HDMI_V13_PHY_STATUS);
+ DUMPREG(HDMI_STATUS_EN);
+ DUMPREG(HDMI_HPD);
+ DUMPREG(HDMI_MODE_SEL);
+ DUMPREG(HDMI_V13_HPD_GEN);
+ DUMPREG(HDMI_V13_DC_CONTROL);
+ DUMPREG(HDMI_V13_VIDEO_PATTERN_GEN);
+
+ DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_H_BLANK_0);
+ DUMPREG(HDMI_H_BLANK_1);
+ DUMPREG(HDMI_V13_V_BLANK_0);
+ DUMPREG(HDMI_V13_V_BLANK_1);
+ DUMPREG(HDMI_V13_V_BLANK_2);
+ DUMPREG(HDMI_V13_H_V_LINE_0);
+ DUMPREG(HDMI_V13_H_V_LINE_1);
+ DUMPREG(HDMI_V13_H_V_LINE_2);
+ DUMPREG(HDMI_VSYNC_POL);
+ DUMPREG(HDMI_INT_PRO_MODE);
+ DUMPREG(HDMI_V13_V_BLANK_F_0);
+ DUMPREG(HDMI_V13_V_BLANK_F_1);
+ DUMPREG(HDMI_V13_V_BLANK_F_2);
+ DUMPREG(HDMI_V13_H_SYNC_GEN_0);
+ DUMPREG(HDMI_V13_H_SYNC_GEN_1);
+ DUMPREG(HDMI_V13_H_SYNC_GEN_2);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_1_0);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_1_1);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_1_2);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_2_0);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_2_1);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_2_2);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_3_0);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_3_1);
+ DUMPREG(HDMI_V13_V_SYNC_GEN_3_2);
+
+ DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_TG_CMD);
+ DUMPREG(HDMI_TG_H_FSZ_L);
+ DUMPREG(HDMI_TG_H_FSZ_H);
+ DUMPREG(HDMI_TG_HACT_ST_L);
+ DUMPREG(HDMI_TG_HACT_ST_H);
+ DUMPREG(HDMI_TG_HACT_SZ_L);
+ DUMPREG(HDMI_TG_HACT_SZ_H);
+ DUMPREG(HDMI_TG_V_FSZ_L);
+ DUMPREG(HDMI_TG_V_FSZ_H);
+ DUMPREG(HDMI_TG_VSYNC_L);
+ DUMPREG(HDMI_TG_VSYNC_H);
+ DUMPREG(HDMI_TG_VSYNC2_L);
+ DUMPREG(HDMI_TG_VSYNC2_H);
+ DUMPREG(HDMI_TG_VACT_ST_L);
+ DUMPREG(HDMI_TG_VACT_ST_H);
+ DUMPREG(HDMI_TG_VACT_SZ_L);
+ DUMPREG(HDMI_TG_VACT_SZ_H);
+ DUMPREG(HDMI_TG_FIELD_CHG_L);
+ DUMPREG(HDMI_TG_FIELD_CHG_H);
+ DUMPREG(HDMI_TG_VACT_ST2_L);
+ DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
+ DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+#undef DUMPREG
+}
+
+static void hdmi_v14_regs_dump(struct hdmi_context *hdata, char *prefix)
+{
+ int i;
+
+#define DUMPREG(reg_id) \
+ DRM_DEBUG_KMS("%s:" #reg_id " = %08x\n", prefix, \
+ readl(hdata->regs + reg_id))
+
+ DRM_DEBUG_KMS("%s: ---- CONTROL REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_INTC_CON);
+ DUMPREG(HDMI_INTC_FLAG);
+ DUMPREG(HDMI_HPD_STATUS);
+ DUMPREG(HDMI_INTC_CON_1);
+ DUMPREG(HDMI_INTC_FLAG_1);
+ DUMPREG(HDMI_PHY_STATUS_0);
+ DUMPREG(HDMI_PHY_STATUS_PLL);
+ DUMPREG(HDMI_PHY_CON_0);
DUMPREG(HDMI_PHY_RSTOUT);
DUMPREG(HDMI_PHY_VPLL);
DUMPREG(HDMI_PHY_CMU);
@@ -343,40 +1003,93 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_CON_1);
DUMPREG(HDMI_CON_2);
DUMPREG(HDMI_SYS_STATUS);
- DUMPREG(HDMI_PHY_STATUS);
+ DUMPREG(HDMI_PHY_STATUS_0);
DUMPREG(HDMI_STATUS_EN);
DUMPREG(HDMI_HPD);
DUMPREG(HDMI_MODE_SEL);
- DUMPREG(HDMI_HPD_GEN);
+ DUMPREG(HDMI_ENC_EN);
DUMPREG(HDMI_DC_CONTROL);
DUMPREG(HDMI_VIDEO_PATTERN_GEN);
DRM_DEBUG_KMS("%s: ---- CORE SYNC REGISTERS ----\n", prefix);
DUMPREG(HDMI_H_BLANK_0);
DUMPREG(HDMI_H_BLANK_1);
- DUMPREG(HDMI_V_BLANK_0);
- DUMPREG(HDMI_V_BLANK_1);
- DUMPREG(HDMI_V_BLANK_2);
- DUMPREG(HDMI_H_V_LINE_0);
- DUMPREG(HDMI_H_V_LINE_1);
- DUMPREG(HDMI_H_V_LINE_2);
+ DUMPREG(HDMI_V2_BLANK_0);
+ DUMPREG(HDMI_V2_BLANK_1);
+ DUMPREG(HDMI_V1_BLANK_0);
+ DUMPREG(HDMI_V1_BLANK_1);
+ DUMPREG(HDMI_V_LINE_0);
+ DUMPREG(HDMI_V_LINE_1);
+ DUMPREG(HDMI_H_LINE_0);
+ DUMPREG(HDMI_H_LINE_1);
+ DUMPREG(HDMI_HSYNC_POL);
+
DUMPREG(HDMI_VSYNC_POL);
DUMPREG(HDMI_INT_PRO_MODE);
- DUMPREG(HDMI_V_BLANK_F_0);
- DUMPREG(HDMI_V_BLANK_F_1);
- DUMPREG(HDMI_V_BLANK_F_2);
- DUMPREG(HDMI_H_SYNC_GEN_0);
- DUMPREG(HDMI_H_SYNC_GEN_1);
- DUMPREG(HDMI_H_SYNC_GEN_2);
- DUMPREG(HDMI_V_SYNC_GEN_1_0);
- DUMPREG(HDMI_V_SYNC_GEN_1_1);
- DUMPREG(HDMI_V_SYNC_GEN_1_2);
- DUMPREG(HDMI_V_SYNC_GEN_2_0);
- DUMPREG(HDMI_V_SYNC_GEN_2_1);
- DUMPREG(HDMI_V_SYNC_GEN_2_2);
- DUMPREG(HDMI_V_SYNC_GEN_3_0);
- DUMPREG(HDMI_V_SYNC_GEN_3_1);
- DUMPREG(HDMI_V_SYNC_GEN_3_2);
+ DUMPREG(HDMI_V_BLANK_F0_0);
+ DUMPREG(HDMI_V_BLANK_F0_1);
+ DUMPREG(HDMI_V_BLANK_F1_0);
+ DUMPREG(HDMI_V_BLANK_F1_1);
+
+ DUMPREG(HDMI_H_SYNC_START_0);
+ DUMPREG(HDMI_H_SYNC_START_1);
+ DUMPREG(HDMI_H_SYNC_END_0);
+ DUMPREG(HDMI_H_SYNC_END_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_2_0);
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_2_1);
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_1_0);
+ DUMPREG(HDMI_V_SYNC_LINE_BEF_1_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_2_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_2_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_1_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_1_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_2_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_1_1);
+
+ DUMPREG(HDMI_V_BLANK_F2_0);
+ DUMPREG(HDMI_V_BLANK_F2_1);
+ DUMPREG(HDMI_V_BLANK_F3_0);
+ DUMPREG(HDMI_V_BLANK_F3_1);
+ DUMPREG(HDMI_V_BLANK_F4_0);
+ DUMPREG(HDMI_V_BLANK_F4_1);
+ DUMPREG(HDMI_V_BLANK_F5_0);
+ DUMPREG(HDMI_V_BLANK_F5_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_3_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_3_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_4_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_4_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_5_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_5_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_6_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_6_1);
+
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_3_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_4_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_5_1);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_0);
+ DUMPREG(HDMI_V_SYNC_LINE_AFT_PXL_6_1);
+
+ DUMPREG(HDMI_VACT_SPACE_1_0);
+ DUMPREG(HDMI_VACT_SPACE_1_1);
+ DUMPREG(HDMI_VACT_SPACE_2_0);
+ DUMPREG(HDMI_VACT_SPACE_2_1);
+ DUMPREG(HDMI_VACT_SPACE_3_0);
+ DUMPREG(HDMI_VACT_SPACE_3_1);
+ DUMPREG(HDMI_VACT_SPACE_4_0);
+ DUMPREG(HDMI_VACT_SPACE_4_1);
+ DUMPREG(HDMI_VACT_SPACE_5_0);
+ DUMPREG(HDMI_VACT_SPACE_5_1);
+ DUMPREG(HDMI_VACT_SPACE_6_0);
+ DUMPREG(HDMI_VACT_SPACE_6_1);
DRM_DEBUG_KMS("%s: ---- TG REGISTERS ----\n", prefix);
DUMPREG(HDMI_TG_CMD);
@@ -400,6 +1113,10 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_TG_FIELD_CHG_H);
DUMPREG(HDMI_TG_VACT_ST2_L);
DUMPREG(HDMI_TG_VACT_ST2_H);
+ DUMPREG(HDMI_TG_VACT_ST3_L);
+ DUMPREG(HDMI_TG_VACT_ST3_H);
+ DUMPREG(HDMI_TG_VACT_ST4_L);
+ DUMPREG(HDMI_TG_VACT_ST4_H);
DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_L);
DUMPREG(HDMI_TG_VSYNC_TOP_HDMI_H);
DUMPREG(HDMI_TG_VSYNC_BOT_HDMI_L);
@@ -408,10 +1125,49 @@ static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
DUMPREG(HDMI_TG_FIELD_TOP_HDMI_H);
DUMPREG(HDMI_TG_FIELD_BOT_HDMI_L);
DUMPREG(HDMI_TG_FIELD_BOT_HDMI_H);
+ DUMPREG(HDMI_TG_3D);
+
+ DRM_DEBUG_KMS("%s: ---- PACKET REGISTERS ----\n", prefix);
+ DUMPREG(HDMI_AVI_CON);
+ DUMPREG(HDMI_AVI_HEADER0);
+ DUMPREG(HDMI_AVI_HEADER1);
+ DUMPREG(HDMI_AVI_HEADER2);
+ DUMPREG(HDMI_AVI_CHECK_SUM);
+ DUMPREG(HDMI_VSI_CON);
+ DUMPREG(HDMI_VSI_HEADER0);
+ DUMPREG(HDMI_VSI_HEADER1);
+ DUMPREG(HDMI_VSI_HEADER2);
+ for (i = 0; i < 7; ++i)
+ DUMPREG(HDMI_VSI_DATA(i));
+
#undef DUMPREG
}
-static int hdmi_conf_index(struct drm_display_mode *mode)
+static void hdmi_regs_dump(struct hdmi_context *hdata, char *prefix)
+{
+ if (hdata->is_v13)
+ hdmi_v13_regs_dump(hdata, prefix);
+ else
+ hdmi_v14_regs_dump(hdata, prefix);
+}
+
+static int hdmi_v13_conf_index(struct drm_display_mode *mode)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
+ if (hdmi_v13_confs[i].width == mode->hdisplay &&
+ hdmi_v13_confs[i].height == mode->vdisplay &&
+ hdmi_v13_confs[i].vrefresh == mode->vrefresh &&
+ hdmi_v13_confs[i].interlace ==
+ ((mode->flags & DRM_MODE_FLAG_INTERLACE) ?
+ true : false))
+ return i;
+
+ return -EINVAL;
+}
+
+static int hdmi_v14_conf_index(struct drm_display_mode *mode)
{
int i;
@@ -424,7 +1180,16 @@ static int hdmi_conf_index(struct drm_display_mode *mode)
true : false))
return i;
- return -1;
+ return -EINVAL;
+}
+
+static int hdmi_conf_index(struct hdmi_context *hdata,
+ struct drm_display_mode *mode)
+{
+ if (hdata->is_v13)
+ return hdmi_v13_conf_index(mode);
+
+ return hdmi_v14_conf_index(mode);
}
static bool hdmi_is_connected(void *ctx)
@@ -462,29 +1227,69 @@ static int hdmi_get_edid(void *ctx, struct drm_connector *connector,
return 0;
}
-static int hdmi_check_timing(void *ctx, void *timing)
+static int hdmi_v13_check_timing(struct fb_videomode *check_timing)
{
- struct fb_videomode *check_timing = timing;
int i;
- DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+ DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
+ check_timing->xres, check_timing->yres,
+ check_timing->refresh, (check_timing->vmode &
+ FB_VMODE_INTERLACED) ? true : false);
- DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
- check_timing->yres, check_timing->refresh,
- check_timing->vmode);
+ for (i = 0; i < ARRAY_SIZE(hdmi_v13_confs); ++i)
+ if (hdmi_v13_confs[i].width == check_timing->xres &&
+ hdmi_v13_confs[i].height == check_timing->yres &&
+ hdmi_v13_confs[i].vrefresh == check_timing->refresh &&
+ hdmi_v13_confs[i].interlace ==
+ ((check_timing->vmode & FB_VMODE_INTERLACED) ?
+ true : false))
+ return 0;
- for (i = 0; i < ARRAY_SIZE(hdmi_confs); ++i)
+ /* TODO */
+
+ return -EINVAL;
+}
+
+static int hdmi_v14_check_timing(struct fb_videomode *check_timing)
+{
+ int i;
+
+ DRM_DEBUG_KMS("valid mode : xres=%d, yres=%d, refresh=%d, intl=%d\n",
+ check_timing->xres, check_timing->yres,
+ check_timing->refresh, (check_timing->vmode &
+ FB_VMODE_INTERLACED) ? true : false);
+
+ for (i = 0; i < ARRAY_SIZE(hdmi_confs); i++)
if (hdmi_confs[i].width == check_timing->xres &&
hdmi_confs[i].height == check_timing->yres &&
hdmi_confs[i].vrefresh == check_timing->refresh &&
hdmi_confs[i].interlace ==
((check_timing->vmode & FB_VMODE_INTERLACED) ?
true : false))
- return 0;
+ return 0;
+
+ /* TODO */
return -EINVAL;
}
+static int hdmi_check_timing(void *ctx, void *timing)
+{
+ struct hdmi_context *hdata = (struct hdmi_context *)ctx;
+ struct fb_videomode *check_timing = timing;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ DRM_DEBUG_KMS("[%d]x[%d] [%d]Hz [%x]\n", check_timing->xres,
+ check_timing->yres, check_timing->refresh,
+ check_timing->vmode);
+
+ if (hdata->is_v13)
+ return hdmi_v13_check_timing(check_timing);
+ else
+ return hdmi_v14_check_timing(check_timing);
+}
+
static int hdmi_display_power_on(void *ctx, int mode)
{
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
@@ -514,15 +1319,185 @@ static struct exynos_hdmi_display_ops display_ops = {
.power_on = hdmi_display_power_on,
};
+static void hdmi_set_acr(u32 freq, u8 *acr)
+{
+ u32 n, cts;
+
+ switch (freq) {
+ case 32000:
+ n = 4096;
+ cts = 27000;
+ break;
+ case 44100:
+ n = 6272;
+ cts = 30000;
+ break;
+ case 88200:
+ n = 12544;
+ cts = 30000;
+ break;
+ case 176400:
+ n = 25088;
+ cts = 30000;
+ break;
+ case 48000:
+ n = 6144;
+ cts = 27000;
+ break;
+ case 96000:
+ n = 12288;
+ cts = 27000;
+ break;
+ case 192000:
+ n = 24576;
+ cts = 27000;
+ break;
+ default:
+ n = 0;
+ cts = 0;
+ break;
+ }
+
+ acr[1] = cts >> 16;
+ acr[2] = cts >> 8 & 0xff;
+ acr[3] = cts & 0xff;
+
+ acr[4] = n >> 16;
+ acr[5] = n >> 8 & 0xff;
+ acr[6] = n & 0xff;
+}
+
+static void hdmi_reg_acr(struct hdmi_context *hdata, u8 *acr)
+{
+ hdmi_reg_writeb(hdata, HDMI_ACR_N0, acr[6]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_N1, acr[5]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_N2, acr[4]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_MCTS0, acr[3]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_MCTS1, acr[2]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_MCTS2, acr[1]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CTS0, acr[3]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CTS1, acr[2]);
+ hdmi_reg_writeb(hdata, HDMI_ACR_CTS2, acr[1]);
+
+ if (hdata->is_v13)
+ hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 4);
+ else
+ hdmi_reg_writeb(hdata, HDMI_ACR_CON, 4);
+}
+
+static void hdmi_audio_init(struct hdmi_context *hdata)
+{
+ u32 sample_rate, bits_per_sample, frame_size_code;
+ u32 data_num, bit_ch, sample_frq;
+ u32 val;
+ u8 acr[7];
+
+ sample_rate = 44100;
+ bits_per_sample = 16;
+ frame_size_code = 0;
+
+ switch (bits_per_sample) {
+ case 20:
+ data_num = 2;
+ bit_ch = 1;
+ break;
+ case 24:
+ data_num = 3;
+ bit_ch = 1;
+ break;
+ default:
+ data_num = 1;
+ bit_ch = 0;
+ break;
+ }
+
+ hdmi_set_acr(sample_rate, acr);
+ hdmi_reg_acr(hdata, acr);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CON, HDMI_I2S_IN_DISABLE
+ | HDMI_I2S_AUD_I2S | HDMI_I2S_CUV_I2S_ENABLE
+ | HDMI_I2S_MUX_ENABLE);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CH, HDMI_I2S_CH0_EN
+ | HDMI_I2S_CH1_EN | HDMI_I2S_CH2_EN);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_MUX_CUV, HDMI_I2S_CUV_RL_EN);
+
+ sample_frq = (sample_rate == 44100) ? 0 :
+ (sample_rate == 48000) ? 2 :
+ (sample_rate == 32000) ? 3 :
+ (sample_rate == 96000) ? 0xa : 0x0;
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_DIS);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CLK_CON, HDMI_I2S_CLK_EN);
+
+ val = hdmi_reg_read(hdata, HDMI_I2S_DSD_CON) | 0x01;
+ hdmi_reg_writeb(hdata, HDMI_I2S_DSD_CON, val);
+
+ /* Configuration I2S input ports. Configure I2S_PIN_SEL_0~4 */
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_0, HDMI_I2S_SEL_SCLK(5)
+ | HDMI_I2S_SEL_LRCK(6));
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_1, HDMI_I2S_SEL_SDATA1(1)
+ | HDMI_I2S_SEL_SDATA2(4));
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_2, HDMI_I2S_SEL_SDATA3(1)
+ | HDMI_I2S_SEL_SDATA2(2));
+ hdmi_reg_writeb(hdata, HDMI_I2S_PIN_SEL_3, HDMI_I2S_SEL_DSD(0));
+
+ /* I2S_CON_1 & 2 */
+ hdmi_reg_writeb(hdata, HDMI_I2S_CON_1, HDMI_I2S_SCLK_FALLING_EDGE
+ | HDMI_I2S_L_CH_LOW_POL);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CON_2, HDMI_I2S_MSB_FIRST_MODE
+ | HDMI_I2S_SET_BIT_CH(bit_ch)
+ | HDMI_I2S_SET_SDATA_BIT(data_num)
+ | HDMI_I2S_BASIC_FORMAT);
+
+ /* Configure register related to CUV information */
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_0, HDMI_I2S_CH_STATUS_MODE_0
+ | HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH
+ | HDMI_I2S_COPYRIGHT
+ | HDMI_I2S_LINEAR_PCM
+ | HDMI_I2S_CONSUMER_FORMAT);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_1, HDMI_I2S_CD_PLAYER);
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_2, HDMI_I2S_SET_SOURCE_NUM(0));
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_3, HDMI_I2S_CLK_ACCUR_LEVEL_2
+ | HDMI_I2S_SET_SMP_FREQ(sample_frq));
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_4,
+ HDMI_I2S_ORG_SMP_FREQ_44_1
+ | HDMI_I2S_WORD_LEN_MAX24_24BITS
+ | HDMI_I2S_WORD_LEN_MAX_24BITS);
+
+ hdmi_reg_writeb(hdata, HDMI_I2S_CH_ST_CON, HDMI_I2S_CH_STATUS_RELOAD);
+}
+
+static void hdmi_audio_control(struct hdmi_context *hdata, bool onoff)
+{
+ u32 mod;
+
+ mod = hdmi_reg_read(hdata, HDMI_MODE_SEL);
+ if (mod & HDMI_DVI_MODE_EN)
+ return;
+
+ hdmi_reg_writeb(hdata, HDMI_AUI_CON, onoff ? 2 : 0);
+ hdmi_reg_writemask(hdata, HDMI_CON_0, onoff ?
+ HDMI_ASP_EN : HDMI_ASP_DIS, HDMI_ASP_MASK);
+}
+
static void hdmi_conf_reset(struct hdmi_context *hdata)
{
+ u32 reg;
+
/* disable hpd handle for drm */
hdata->hpd_handle = false;
+ if (hdata->is_v13)
+ reg = HDMI_V13_CORE_RSTOUT;
+ else
+ reg = HDMI_CORE_RSTOUT;
+
/* resetting HDMI core */
- hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, 0, HDMI_CORE_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, 0, HDMI_CORE_SW_RSTOUT);
mdelay(10);
- hdmi_reg_writemask(hdata, HDMI_CORE_RSTOUT, ~0, HDMI_CORE_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, ~0, HDMI_CORE_SW_RSTOUT);
mdelay(10);
/* enable hpd handle for drm */
@@ -546,27 +1521,126 @@ static void hdmi_conf_init(struct hdmi_context *hdata)
HDMI_MODE_HDMI_EN, HDMI_MODE_MASK);
/* disable bluescreen */
hdmi_reg_writemask(hdata, HDMI_CON_0, 0, HDMI_BLUE_SCR_EN);
- /* choose bluescreen (fecal) color */
- hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_0, 0x12);
- hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_1, 0x34);
- hdmi_reg_writeb(hdata, HDMI_BLUE_SCREEN_2, 0x56);
- /* enable AVI packet every vsync, fixes purple line problem */
- hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
- /* force RGB, look to CEA-861-D, table 7 for more detail */
- hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(0), 0 << 5);
- hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
-
- hdmi_reg_writeb(hdata, HDMI_SPD_CON, 0x02);
- hdmi_reg_writeb(hdata, HDMI_AUI_CON, 0x02);
- hdmi_reg_writeb(hdata, HDMI_ACR_CON, 0x04);
+
+ if (hdata->is_v13) {
+ /* choose bluescreen (fecal) color */
+ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_0, 0x12);
+ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_1, 0x34);
+ hdmi_reg_writeb(hdata, HDMI_V13_BLUE_SCREEN_2, 0x56);
+
+ /* enable AVI packet every vsync, fixes purple line problem */
+ hdmi_reg_writeb(hdata, HDMI_V13_AVI_CON, 0x02);
+ /* force RGB, look to CEA-861-D, table 7 for more detail */
+ hdmi_reg_writeb(hdata, HDMI_V13_AVI_BYTE(0), 0 << 5);
+ hdmi_reg_writemask(hdata, HDMI_CON_1, 0x10 << 5, 0x11 << 5);
+
+ hdmi_reg_writeb(hdata, HDMI_V13_SPD_CON, 0x02);
+ hdmi_reg_writeb(hdata, HDMI_V13_AUI_CON, 0x02);
+ hdmi_reg_writeb(hdata, HDMI_V13_ACR_CON, 0x04);
+ } else {
+ /* enable AVI packet every vsync, fixes purple line problem */
+ hdmi_reg_writeb(hdata, HDMI_AVI_CON, 0x02);
+ hdmi_reg_writeb(hdata, HDMI_AVI_BYTE(1), 2 << 5);
+ hdmi_reg_writemask(hdata, HDMI_CON_1, 2, 3 << 5);
+ }
/* enable hpd handle for drm */
hdata->hpd_handle = true;
}
-static void hdmi_timing_apply(struct hdmi_context *hdata,
- const struct hdmi_preset_conf *conf)
+static void hdmi_v13_timing_apply(struct hdmi_context *hdata)
{
+ const struct hdmi_v13_preset_conf *conf =
+ hdmi_v13_confs[hdata->cur_conf].conf;
+ const struct hdmi_v13_core_regs *core = &conf->core;
+ const struct hdmi_v13_tg_regs *tg = &conf->tg;
+ int tries;
+
+ /* setting core registers */
+ hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_0, core->v_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_1, core->v_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_2, core->v_blank[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_0, core->h_v_line[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_1, core->h_v_line[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_V_LINE_2, core->h_v_line[2]);
+ hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
+ hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_0, core->v_blank_f[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_1, core->v_blank_f[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_BLANK_F_2, core->v_blank_f[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_0, core->h_sync_gen[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_1, core->h_sync_gen[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_H_SYNC_GEN_2, core->h_sync_gen[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V13_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ /* Timing generator registers */
+ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_L, tg->hact_st_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_ST_H, tg->hact_st_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_L, tg->hact_sz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_HACT_SZ_H, tg->hact_sz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_L, tg->v_fsz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_V_FSZ_H, tg->v_fsz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_L, tg->vsync_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_H, tg->vsync_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_L, tg->vsync2_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC2_H, tg->vsync2_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_L, tg->vact_st_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST_H, tg->vact_st_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_L, tg->vact_sz_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_SZ_H, tg->vact_sz_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_L, tg->field_chg_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+
+ /* waiting for HDMIPHY's PLL to get to steady state */
+ for (tries = 100; tries; --tries) {
+ u32 val = hdmi_reg_read(hdata, HDMI_V13_PHY_STATUS);
+ if (val & HDMI_PHY_STATUS_READY)
+ break;
+ mdelay(1);
+ }
+ /* steady state not achieved */
+ if (tries == 0) {
+ DRM_ERROR("hdmiphy's pll could not reach steady state.\n");
+ hdmi_regs_dump(hdata, "timing apply");
+ }
+
+ clk_disable(hdata->res.sclk_hdmi);
+ clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_hdmiphy);
+ clk_enable(hdata->res.sclk_hdmi);
+
+ /* enable HDMI and timing generator */
+ hdmi_reg_writemask(hdata, HDMI_CON_0, ~0, HDMI_EN);
+ if (core->int_pro_mode[0])
+ hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN |
+ HDMI_FIELD_EN);
+ else
+ hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
+}
+
+static void hdmi_v14_timing_apply(struct hdmi_context *hdata)
+{
+ const struct hdmi_preset_conf *conf = hdmi_confs[hdata->cur_conf].conf;
const struct hdmi_core_regs *core = &conf->core;
const struct hdmi_tg_regs *tg = &conf->tg;
int tries;
@@ -574,29 +1648,102 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
/* setting core registers */
hdmi_reg_writeb(hdata, HDMI_H_BLANK_0, core->h_blank[0]);
hdmi_reg_writeb(hdata, HDMI_H_BLANK_1, core->h_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_0, core->v_blank[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_1, core->v_blank[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_2, core->v_blank[2]);
- hdmi_reg_writeb(hdata, HDMI_H_V_LINE_0, core->h_v_line[0]);
- hdmi_reg_writeb(hdata, HDMI_H_V_LINE_1, core->h_v_line[1]);
- hdmi_reg_writeb(hdata, HDMI_H_V_LINE_2, core->h_v_line[2]);
+ hdmi_reg_writeb(hdata, HDMI_V2_BLANK_0, core->v2_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_V2_BLANK_1, core->v2_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V1_BLANK_0, core->v1_blank[0]);
+ hdmi_reg_writeb(hdata, HDMI_V1_BLANK_1, core->v1_blank[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_LINE_0, core->v_line[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_LINE_1, core->v_line[1]);
+ hdmi_reg_writeb(hdata, HDMI_H_LINE_0, core->h_line[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_LINE_1, core->h_line[1]);
+ hdmi_reg_writeb(hdata, HDMI_HSYNC_POL, core->hsync_pol[0]);
hdmi_reg_writeb(hdata, HDMI_VSYNC_POL, core->vsync_pol[0]);
hdmi_reg_writeb(hdata, HDMI_INT_PRO_MODE, core->int_pro_mode[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_0, core->v_blank_f[0]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_1, core->v_blank_f[1]);
- hdmi_reg_writeb(hdata, HDMI_V_BLANK_F_2, core->v_blank_f[2]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]);
- hdmi_reg_writeb(hdata, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]);
- hdmi_reg_writeb(hdata, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_0, core->v_blank_f0[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F0_1, core->v_blank_f0[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_0, core->v_blank_f1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F1_1, core->v_blank_f1[1]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_0, core->h_sync_start[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_START_1, core->h_sync_start[1]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_0, core->h_sync_end[0]);
+ hdmi_reg_writeb(hdata, HDMI_H_SYNC_END_1, core->h_sync_end[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_0,
+ core->v_sync_line_bef_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_2_1,
+ core->v_sync_line_bef_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_0,
+ core->v_sync_line_bef_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_BEF_1_1,
+ core->v_sync_line_bef_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_0,
+ core->v_sync_line_aft_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_2_1,
+ core->v_sync_line_aft_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_0,
+ core->v_sync_line_aft_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_1_1,
+ core->v_sync_line_aft_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_0,
+ core->v_sync_line_aft_pxl_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_2_1,
+ core->v_sync_line_aft_pxl_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_0,
+ core->v_sync_line_aft_pxl_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_1_1,
+ core->v_sync_line_aft_pxl_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_0, core->v_blank_f2[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F2_1, core->v_blank_f2[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_0, core->v_blank_f3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F3_1, core->v_blank_f3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_0, core->v_blank_f4[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F4_1, core->v_blank_f4[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_0, core->v_blank_f5[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_BLANK_F5_1, core->v_blank_f5[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_0,
+ core->v_sync_line_aft_3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_3_1,
+ core->v_sync_line_aft_3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_0,
+ core->v_sync_line_aft_4[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_4_1,
+ core->v_sync_line_aft_4[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_0,
+ core->v_sync_line_aft_5[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_5_1,
+ core->v_sync_line_aft_5[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_0,
+ core->v_sync_line_aft_6[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_6_1,
+ core->v_sync_line_aft_6[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_0,
+ core->v_sync_line_aft_pxl_3[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_3_1,
+ core->v_sync_line_aft_pxl_3[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_0,
+ core->v_sync_line_aft_pxl_4[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_4_1,
+ core->v_sync_line_aft_pxl_4[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_0,
+ core->v_sync_line_aft_pxl_5[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_5_1,
+ core->v_sync_line_aft_pxl_5[1]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_0,
+ core->v_sync_line_aft_pxl_6[0]);
+ hdmi_reg_writeb(hdata, HDMI_V_SYNC_LINE_AFT_PXL_6_1,
+ core->v_sync_line_aft_pxl_6[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_0, core->vact_space_1[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_1_1, core->vact_space_1[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_0, core->vact_space_2[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_2_1, core->vact_space_2[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_0, core->vact_space_3[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_3_1, core->vact_space_3[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_0, core->vact_space_4[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_4_1, core->vact_space_4[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_0, core->vact_space_5[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_5_1, core->vact_space_5[1]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_0, core->vact_space_6[0]);
+ hdmi_reg_writeb(hdata, HDMI_VACT_SPACE_6_1, core->vact_space_6[1]);
+
/* Timing generator registers */
hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_L, tg->h_fsz_l);
hdmi_reg_writeb(hdata, HDMI_TG_H_FSZ_H, tg->h_fsz_h);
@@ -618,6 +1765,10 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_CHG_H, tg->field_chg_h);
hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_L, tg->vact_st2_l);
hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST2_H, tg->vact_st2_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_L, tg->vact_st3_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST3_H, tg->vact_st3_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_L, tg->vact_st4_l);
+ hdmi_reg_writeb(hdata, HDMI_TG_VACT_ST4_H, tg->vact_st4_h);
hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l);
hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h);
hdmi_reg_writeb(hdata, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l);
@@ -626,10 +1777,11 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h);
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l);
hdmi_reg_writeb(hdata, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h);
+ hdmi_reg_writeb(hdata, HDMI_TG_3D, tg->tg_3d);
/* waiting for HDMIPHY's PLL to get to steady state */
for (tries = 100; tries; --tries) {
- u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS);
+ u32 val = hdmi_reg_read(hdata, HDMI_PHY_STATUS_0);
if (val & HDMI_PHY_STATUS_READY)
break;
mdelay(1);
@@ -653,9 +1805,18 @@ static void hdmi_timing_apply(struct hdmi_context *hdata,
hdmi_reg_writemask(hdata, HDMI_TG_CMD, ~0, HDMI_TG_EN);
}
+static void hdmi_timing_apply(struct hdmi_context *hdata)
+{
+ if (hdata->is_v13)
+ hdmi_v13_timing_apply(hdata);
+ else
+ hdmi_v14_timing_apply(hdata);
+}
+
static void hdmiphy_conf_reset(struct hdmi_context *hdata)
{
u8 buffer[2];
+ u32 reg;
clk_disable(hdata->res.sclk_hdmi);
clk_set_parent(hdata->res.sclk_hdmi, hdata->res.sclk_pixel);
@@ -668,15 +1829,21 @@ static void hdmiphy_conf_reset(struct hdmi_context *hdata)
if (hdata->hdmiphy_port)
i2c_master_send(hdata->hdmiphy_port, buffer, 2);
+ if (hdata->is_v13)
+ reg = HDMI_V13_PHY_RSTOUT;
+ else
+ reg = HDMI_PHY_RSTOUT;
+
/* reset hdmiphy */
- hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, ~0, HDMI_PHY_SW_RSTOUT);
mdelay(10);
- hdmi_reg_writemask(hdata, HDMI_PHY_RSTOUT, 0, HDMI_PHY_SW_RSTOUT);
+ hdmi_reg_writemask(hdata, reg, 0, HDMI_PHY_SW_RSTOUT);
mdelay(10);
}
static void hdmiphy_conf_apply(struct hdmi_context *hdata)
{
+ const u8 *hdmiphy_data;
u8 buffer[32];
u8 operation[2];
u8 read_buffer[32] = {0, };
@@ -689,7 +1856,12 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
}
/* pixel clock */
- memcpy(buffer, hdmi_confs[hdata->cur_conf].hdmiphy_data, 32);
+ if (hdata->is_v13)
+ hdmiphy_data = hdmi_v13_confs[hdata->cur_conf].hdmiphy_data;
+ else
+ hdmiphy_data = hdmi_confs[hdata->cur_conf].hdmiphy_data;
+
+ memcpy(buffer, hdmiphy_data, 32);
ret = i2c_master_send(hdata->hdmiphy_port, buffer, 32);
if (ret != 32) {
DRM_ERROR("failed to configure HDMIPHY via I2C\n");
@@ -721,9 +1893,6 @@ static void hdmiphy_conf_apply(struct hdmi_context *hdata)
static void hdmi_conf_apply(struct hdmi_context *hdata)
{
- const struct hdmi_preset_conf *conf =
- hdmi_confs[hdata->cur_conf].conf;
-
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
hdmiphy_conf_reset(hdata);
@@ -731,13 +1900,55 @@ static void hdmi_conf_apply(struct hdmi_context *hdata)
hdmi_conf_reset(hdata);
hdmi_conf_init(hdata);
+ hdmi_audio_init(hdata);
/* setting core registers */
- hdmi_timing_apply(hdata, conf);
+ hdmi_timing_apply(hdata);
+ hdmi_audio_control(hdata, true);
hdmi_regs_dump(hdata, "start");
}
+static void hdmi_mode_fixup(void *ctx, struct drm_connector *connector,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ struct drm_display_mode *m;
+ struct hdmi_context *hdata = (struct hdmi_context *)ctx;
+ int index;
+
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+ if (hdata->is_v13)
+ index = hdmi_v13_conf_index(adjusted_mode);
+ else
+ index = hdmi_v14_conf_index(adjusted_mode);
+
+ /* just return if user desired mode exists. */
+ if (index >= 0)
+ return;
+
+ /*
+ * otherwise, find the most suitable mode among modes and change it
+ * to adjusted_mode.
+ */
+ list_for_each_entry(m, &connector->modes, head) {
+ if (hdata->is_v13)
+ index = hdmi_v13_conf_index(m);
+ else
+ index = hdmi_v14_conf_index(m);
+
+ if (index >= 0) {
+ DRM_INFO("desired mode doesn't exist so\n");
+ DRM_INFO("use the most suitable mode among modes.\n");
+ memcpy(adjusted_mode, m, sizeof(*m));
+ break;
+ }
+ }
+}
+
static void hdmi_mode_set(void *ctx, void *mode)
{
struct hdmi_context *hdata = (struct hdmi_context *)ctx;
@@ -745,13 +1956,22 @@ static void hdmi_mode_set(void *ctx, void *mode)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
- conf_idx = hdmi_conf_index(mode);
- if (conf_idx >= 0 && conf_idx < ARRAY_SIZE(hdmi_confs))
+ conf_idx = hdmi_conf_index(hdata, mode);
+ if (conf_idx >= 0)
hdata->cur_conf = conf_idx;
else
DRM_DEBUG_KMS("not supported mode\n");
}
+static void hdmi_get_max_resol(void *ctx, unsigned int *width,
+ unsigned int *height)
+{
+ DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
+
+ *width = MAX_WIDTH;
+ *height = MAX_HEIGHT;
+}
+
static void hdmi_commit(void *ctx)
{
struct hdmi_context *hdata = (struct hdmi_context *)ctx;
@@ -770,13 +1990,16 @@ static void hdmi_disable(void *ctx)
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
if (hdata->enabled) {
+ hdmi_audio_control(hdata, false);
hdmiphy_conf_reset(hdata);
hdmi_conf_reset(hdata);
}
}
static struct exynos_hdmi_manager_ops manager_ops = {
+ .mode_fixup = hdmi_mode_fixup,
.mode_set = hdmi_mode_set,
+ .get_max_resol = hdmi_get_max_resol,
.commit = hdmi_commit,
.disable = hdmi_disable,
};
@@ -926,7 +2149,7 @@ static void hdmi_resource_poweron(struct hdmi_context *hdata)
hdmiphy_conf_reset(hdata);
hdmi_conf_reset(hdata);
hdmi_conf_init(hdata);
-
+ hdmi_audio_init(hdata);
}
static void hdmi_resource_poweroff(struct hdmi_context *hdata)
@@ -978,14 +2201,12 @@ void hdmi_attach_ddc_client(struct i2c_client *ddc)
if (ddc)
hdmi_ddc = ddc;
}
-EXPORT_SYMBOL(hdmi_attach_ddc_client);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy)
{
if (hdmiphy)
hdmi_hdmiphy = hdmiphy;
}
-EXPORT_SYMBOL(hdmi_attach_hdmiphy_client);
static int __devinit hdmi_probe(struct platform_device *pdev)
{
@@ -1022,6 +2243,7 @@ static int __devinit hdmi_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, drm_hdmi_ctx);
+ hdata->is_v13 = pdata->is_v13;
hdata->default_win = pdata->default_win;
hdata->default_timing = &pdata->timing;
hdata->default_bpp = pdata->bpp;
@@ -1167,10 +2389,3 @@ struct platform_driver hdmi_driver = {
.pm = &hdmi_pm_ops,
},
};
-EXPORT_SYMBOL(hdmi_driver);
-
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM HDMI core Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.h b/drivers/gpu/drm/exynos/exynos_hdmi.h
index 31d6cf84c1aa..1c3b6d8f1fe7 100644
--- a/drivers/gpu/drm/exynos/exynos_hdmi.h
+++ b/drivers/gpu/drm/exynos/exynos_hdmi.h
@@ -28,56 +28,6 @@
#ifndef _EXYNOS_HDMI_H_
#define _EXYNOS_HDMI_H_
-struct hdmi_conf {
- int width;
- int height;
- int vrefresh;
- bool interlace;
- const u8 *hdmiphy_data;
- const struct hdmi_preset_conf *conf;
-};
-
-struct hdmi_resources {
- struct clk *hdmi;
- struct clk *sclk_hdmi;
- struct clk *sclk_pixel;
- struct clk *sclk_hdmiphy;
- struct clk *hdmiphy;
- struct regulator_bulk_data *regul_bulk;
- int regul_count;
-};
-
-struct hdmi_context {
- struct device *dev;
- struct drm_device *drm_dev;
- struct fb_videomode *default_timing;
- unsigned int default_win;
- unsigned int default_bpp;
- bool hpd_handle;
- bool enabled;
-
- struct resource *regs_res;
- /** base address of HDMI registers */
- void __iomem *regs;
- /** HDMI hotplug interrupt */
- unsigned int irq;
- /** workqueue for delayed work */
- struct workqueue_struct *wq;
- /** hotplug handling work */
- struct work_struct hotplug_work;
-
- struct i2c_client *ddc_port;
- struct i2c_client *hdmiphy_port;
-
- /** current hdmiphy conf index */
- int cur_conf;
- /** other resources */
- struct hdmi_resources res;
-
- void *parent_ctx;
-};
-
-
void hdmi_attach_ddc_client(struct i2c_client *ddc);
void hdmi_attach_hdmiphy_client(struct i2c_client *hdmiphy);
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c
index ac24cff39775..4d5f41e19527 100644
--- a/drivers/gpu/drm/exynos/exynos_mixer.c
+++ b/drivers/gpu/drm/exynos/exynos_mixer.c
@@ -36,11 +36,57 @@
#include "exynos_drm_drv.h"
#include "exynos_drm_hdmi.h"
-#include "exynos_hdmi.h"
-#include "exynos_mixer.h"
+
+#define HDMI_OVERLAY_NUMBER 3
#define get_mixer_context(dev) platform_get_drvdata(to_platform_device(dev))
+struct hdmi_win_data {
+ dma_addr_t dma_addr;
+ void __iomem *vaddr;
+ dma_addr_t chroma_dma_addr;
+ void __iomem *chroma_vaddr;
+ uint32_t pixel_format;
+ unsigned int bpp;
+ unsigned int crtc_x;
+ unsigned int crtc_y;
+ unsigned int crtc_width;
+ unsigned int crtc_height;
+ unsigned int fb_x;
+ unsigned int fb_y;
+ unsigned int fb_width;
+ unsigned int fb_height;
+ unsigned int mode_width;
+ unsigned int mode_height;
+ unsigned int scan_flags;
+};
+
+struct mixer_resources {
+ struct device *dev;
+ int irq;
+ void __iomem *mixer_regs;
+ void __iomem *vp_regs;
+ spinlock_t reg_slock;
+ struct clk *mixer;
+ struct clk *vp;
+ struct clk *sclk_mixer;
+ struct clk *sclk_hdmi;
+ struct clk *sclk_dac;
+};
+
+struct mixer_context {
+ struct fb_videomode *default_timing;
+ unsigned int default_win;
+ unsigned int default_bpp;
+ unsigned int irq;
+ int pipe;
+ bool interlace;
+ bool vp_enabled;
+
+ struct mixer_resources mixer_res;
+ struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER];
+};
+
static const u8 filter_y_horiz_tap8[] = {
0, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, 0, 0, 0,
@@ -712,7 +758,12 @@ static void mixer_finish_pageflip(struct drm_device *drm_dev, int crtc)
}
if (is_checked)
- drm_vblank_put(drm_dev, crtc);
+ /*
+ * call drm_vblank_put only in case that drm_vblank_get was
+ * called.
+ */
+ if (atomic_read(&drm_dev->vblank_refcount[crtc]) > 0)
+ drm_vblank_put(drm_dev, crtc);
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
}
@@ -779,15 +830,15 @@ static void mixer_win_reset(struct mixer_context *ctx)
mixer_reg_writemask(res, MXR_STATUS, MXR_STATUS_16_BURST,
MXR_STATUS_BURST_MASK);
- /* setting default layer priority: layer1 > video > layer0
+ /* setting default layer priority: layer1 > layer0 > video
* because typical usage scenario would be
+ * layer1 - OSD
* layer0 - framebuffer
* video - video overlay
- * layer1 - OSD
*/
- val = MXR_LAYER_CFG_GRP0_VAL(1);
- val |= MXR_LAYER_CFG_VP_VAL(2);
- val |= MXR_LAYER_CFG_GRP1_VAL(3);
+ val = MXR_LAYER_CFG_GRP1_VAL(3);
+ val |= MXR_LAYER_CFG_GRP0_VAL(2);
+ val |= MXR_LAYER_CFG_VP_VAL(1);
mixer_reg_write(res, MXR_LAYER_CFG, val);
/* setting background color */
@@ -1044,7 +1095,7 @@ static int mixer_remove(struct platform_device *pdev)
platform_get_drvdata(pdev);
struct mixer_context *ctx = (struct mixer_context *)drm_hdmi_ctx->ctx;
- dev_info(dev, "remove sucessful\n");
+ dev_info(dev, "remove successful\n");
mixer_resource_poweroff(ctx);
mixer_resources_cleanup(ctx);
@@ -1061,10 +1112,3 @@ struct platform_driver mixer_driver = {
.probe = mixer_probe,
.remove = __devexit_p(mixer_remove),
};
-EXPORT_SYMBOL(mixer_driver);
-
-MODULE_AUTHOR("Seung-Woo Kim, <sw0312.kim@samsung.com>");
-MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
-MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
-MODULE_DESCRIPTION("Samsung DRM HDMI mixer Driver");
-MODULE_LICENSE("GPL");
diff --git a/drivers/gpu/drm/exynos/exynos_mixer.h b/drivers/gpu/drm/exynos/exynos_mixer.h
deleted file mode 100644
index cebacfefc077..000000000000
--- a/drivers/gpu/drm/exynos/exynos_mixer.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- * Authors:
- * Seung-Woo Kim <sw0312.kim@samsung.com>
- * Inki Dae <inki.dae@samsung.com>
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
- * OTHER DEALINGS IN THE SOFTWARE.
- */
-
-#ifndef _EXYNOS_MIXER_H_
-#define _EXYNOS_MIXER_H_
-
-#define HDMI_OVERLAY_NUMBER 3
-
-struct hdmi_win_data {
- dma_addr_t dma_addr;
- void __iomem *vaddr;
- dma_addr_t chroma_dma_addr;
- void __iomem *chroma_vaddr;
- uint32_t pixel_format;
- unsigned int bpp;
- unsigned int crtc_x;
- unsigned int crtc_y;
- unsigned int crtc_width;
- unsigned int crtc_height;
- unsigned int fb_x;
- unsigned int fb_y;
- unsigned int fb_width;
- unsigned int fb_height;
- unsigned int mode_width;
- unsigned int mode_height;
- unsigned int scan_flags;
-};
-
-struct mixer_resources {
- struct device *dev;
- /** interrupt index */
- int irq;
- /** pointer to Mixer registers */
- void __iomem *mixer_regs;
- /** pointer to Video Processor registers */
- void __iomem *vp_regs;
- /** spinlock for protection of registers */
- spinlock_t reg_slock;
- /** other resources */
- struct clk *mixer;
- struct clk *vp;
- struct clk *sclk_mixer;
- struct clk *sclk_hdmi;
- struct clk *sclk_dac;
-};
-
-struct mixer_context {
- unsigned int default_win;
- struct fb_videomode *default_timing;
- unsigned int default_bpp;
-
- /** mixer interrupt */
- unsigned int irq;
- /** current crtc pipe for vblank */
- int pipe;
- /** interlace scan mode */
- bool interlace;
- /** vp enabled status */
- bool vp_enabled;
-
- /** mixer and vp resources */
- struct mixer_resources mixer_res;
-
- /** overlay window data */
- struct hdmi_win_data win_data[HDMI_OVERLAY_NUMBER];
-};
-
-#endif
diff --git a/drivers/gpu/drm/exynos/regs-hdmi.h b/drivers/gpu/drm/exynos/regs-hdmi.h
index 72e6b52be740..3c04bea842ce 100644
--- a/drivers/gpu/drm/exynos/regs-hdmi.h
+++ b/drivers/gpu/drm/exynos/regs-hdmi.h
@@ -19,64 +19,67 @@
* Register part
*/
+/* HDMI Version 1.3 & Common */
#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
+#define HDMI_I2S_BASE(x) ((x) + 0x00040000)
#define HDMI_TG_BASE(x) ((x) + 0x00050000)
/* Control registers */
#define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000)
#define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004)
#define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C)
-#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
-#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0018)
-#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x001C)
-#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
+#define HDMI_V13_PHY_RSTOUT HDMI_CTRL_BASE(0x0014)
+#define HDMI_V13_PHY_VPLL HDMI_CTRL_BASE(0x0018)
+#define HDMI_V13_PHY_CMU HDMI_CTRL_BASE(0x001C)
+#define HDMI_V13_CORE_RSTOUT HDMI_CTRL_BASE(0x0020)
/* Core registers */
#define HDMI_CON_0 HDMI_CORE_BASE(0x0000)
#define HDMI_CON_1 HDMI_CORE_BASE(0x0004)
#define HDMI_CON_2 HDMI_CORE_BASE(0x0008)
#define HDMI_SYS_STATUS HDMI_CORE_BASE(0x0010)
-#define HDMI_PHY_STATUS HDMI_CORE_BASE(0x0014)
+#define HDMI_V13_PHY_STATUS HDMI_CORE_BASE(0x0014)
#define HDMI_STATUS_EN HDMI_CORE_BASE(0x0020)
#define HDMI_HPD HDMI_CORE_BASE(0x0030)
#define HDMI_MODE_SEL HDMI_CORE_BASE(0x0040)
-#define HDMI_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
-#define HDMI_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
-#define HDMI_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
+#define HDMI_ENC_EN HDMI_CORE_BASE(0x0044)
+#define HDMI_V13_BLUE_SCREEN_0 HDMI_CORE_BASE(0x0050)
+#define HDMI_V13_BLUE_SCREEN_1 HDMI_CORE_BASE(0x0054)
+#define HDMI_V13_BLUE_SCREEN_2 HDMI_CORE_BASE(0x0058)
#define HDMI_H_BLANK_0 HDMI_CORE_BASE(0x00A0)
#define HDMI_H_BLANK_1 HDMI_CORE_BASE(0x00A4)
-#define HDMI_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
-#define HDMI_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
-#define HDMI_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
-#define HDMI_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
-#define HDMI_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
-#define HDMI_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
+#define HDMI_V13_V_BLANK_0 HDMI_CORE_BASE(0x00B0)
+#define HDMI_V13_V_BLANK_1 HDMI_CORE_BASE(0x00B4)
+#define HDMI_V13_V_BLANK_2 HDMI_CORE_BASE(0x00B8)
+#define HDMI_V13_H_V_LINE_0 HDMI_CORE_BASE(0x00C0)
+#define HDMI_V13_H_V_LINE_1 HDMI_CORE_BASE(0x00C4)
+#define HDMI_V13_H_V_LINE_2 HDMI_CORE_BASE(0x00C8)
#define HDMI_VSYNC_POL HDMI_CORE_BASE(0x00E4)
#define HDMI_INT_PRO_MODE HDMI_CORE_BASE(0x00E8)
-#define HDMI_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
-#define HDMI_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
-#define HDMI_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
-#define HDMI_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
-#define HDMI_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
-#define HDMI_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
-#define HDMI_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
-#define HDMI_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
-#define HDMI_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
-#define HDMI_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
-#define HDMI_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
-#define HDMI_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
-#define HDMI_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
-#define HDMI_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
-#define HDMI_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
-#define HDMI_ACR_CON HDMI_CORE_BASE(0x0180)
-#define HDMI_AVI_CON HDMI_CORE_BASE(0x0300)
-#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
-#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x05C0)
-#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
-#define HDMI_HPD_GEN HDMI_CORE_BASE(0x05C8)
-#define HDMI_AUI_CON HDMI_CORE_BASE(0x0360)
-#define HDMI_SPD_CON HDMI_CORE_BASE(0x0400)
+#define HDMI_V13_V_BLANK_F_0 HDMI_CORE_BASE(0x0110)
+#define HDMI_V13_V_BLANK_F_1 HDMI_CORE_BASE(0x0114)
+#define HDMI_V13_V_BLANK_F_2 HDMI_CORE_BASE(0x0118)
+#define HDMI_V13_H_SYNC_GEN_0 HDMI_CORE_BASE(0x0120)
+#define HDMI_V13_H_SYNC_GEN_1 HDMI_CORE_BASE(0x0124)
+#define HDMI_V13_H_SYNC_GEN_2 HDMI_CORE_BASE(0x0128)
+#define HDMI_V13_V_SYNC_GEN_1_0 HDMI_CORE_BASE(0x0130)
+#define HDMI_V13_V_SYNC_GEN_1_1 HDMI_CORE_BASE(0x0134)
+#define HDMI_V13_V_SYNC_GEN_1_2 HDMI_CORE_BASE(0x0138)
+#define HDMI_V13_V_SYNC_GEN_2_0 HDMI_CORE_BASE(0x0140)
+#define HDMI_V13_V_SYNC_GEN_2_1 HDMI_CORE_BASE(0x0144)
+#define HDMI_V13_V_SYNC_GEN_2_2 HDMI_CORE_BASE(0x0148)
+#define HDMI_V13_V_SYNC_GEN_3_0 HDMI_CORE_BASE(0x0150)
+#define HDMI_V13_V_SYNC_GEN_3_1 HDMI_CORE_BASE(0x0154)
+#define HDMI_V13_V_SYNC_GEN_3_2 HDMI_CORE_BASE(0x0158)
+#define HDMI_V13_ACR_CON HDMI_CORE_BASE(0x0180)
+#define HDMI_V13_AVI_CON HDMI_CORE_BASE(0x0300)
+#define HDMI_V13_AVI_BYTE(n) HDMI_CORE_BASE(0x0320 + 4 * (n))
+#define HDMI_V13_DC_CONTROL HDMI_CORE_BASE(0x05C0)
+#define HDMI_V13_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x05C4)
+#define HDMI_V13_HPD_GEN HDMI_CORE_BASE(0x05C8)
+#define HDMI_V13_AUI_CON HDMI_CORE_BASE(0x0360)
+#define HDMI_V13_SPD_CON HDMI_CORE_BASE(0x0400)
/* Timing generator registers */
#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
@@ -130,6 +133,9 @@
/* HDMI_CON_0 */
#define HDMI_BLUE_SCR_EN (1 << 5)
+#define HDMI_ASP_EN (1 << 2)
+#define HDMI_ASP_DIS (0 << 2)
+#define HDMI_ASP_MASK (1 << 2)
#define HDMI_EN (1 << 0)
/* HDMI_PHY_STATUS */
@@ -138,10 +144,418 @@
/* HDMI_MODE_SEL */
#define HDMI_MODE_HDMI_EN (1 << 1)
#define HDMI_MODE_DVI_EN (1 << 0)
+#define HDMI_DVI_MODE_EN (1)
+#define HDMI_DVI_MODE_DIS (0)
#define HDMI_MODE_MASK (3 << 0)
/* HDMI_TG_CMD */
#define HDMI_TG_EN (1 << 0)
#define HDMI_FIELD_EN (1 << 1)
+
+/* HDMI Version 1.4 */
+/* Control registers */
+/* #define HDMI_INTC_CON HDMI_CTRL_BASE(0x0000) */
+/* #define HDMI_INTC_FLAG HDMI_CTRL_BASE(0x0004) */
+#define HDMI_HDCP_KEY_LOAD HDMI_CTRL_BASE(0x0008)
+/* #define HDMI_HPD_STATUS HDMI_CTRL_BASE(0x000C) */
+#define HDMI_INTC_CON_1 HDMI_CTRL_BASE(0x0010)
+#define HDMI_INTC_FLAG_1 HDMI_CTRL_BASE(0x0014)
+#define HDMI_PHY_STATUS_0 HDMI_CTRL_BASE(0x0020)
+#define HDMI_PHY_STATUS_CMU HDMI_CTRL_BASE(0x0024)
+#define HDMI_PHY_STATUS_PLL HDMI_CTRL_BASE(0x0028)
+#define HDMI_PHY_CON_0 HDMI_CTRL_BASE(0x0030)
+#define HDMI_HPD_CTRL HDMI_CTRL_BASE(0x0040)
+#define HDMI_HPD_ST HDMI_CTRL_BASE(0x0044)
+#define HDMI_HPD_TH_X HDMI_CTRL_BASE(0x0050)
+#define HDMI_AUDIO_CLKSEL HDMI_CTRL_BASE(0x0070)
+#define HDMI_PHY_RSTOUT HDMI_CTRL_BASE(0x0074)
+#define HDMI_PHY_VPLL HDMI_CTRL_BASE(0x0078)
+#define HDMI_PHY_CMU HDMI_CTRL_BASE(0x007C)
+#define HDMI_CORE_RSTOUT HDMI_CTRL_BASE(0x0080)
+
+/* Video related registers */
+#define HDMI_YMAX HDMI_CORE_BASE(0x0060)
+#define HDMI_YMIN HDMI_CORE_BASE(0x0064)
+#define HDMI_CMAX HDMI_CORE_BASE(0x0068)
+#define HDMI_CMIN HDMI_CORE_BASE(0x006C)
+
+#define HDMI_V2_BLANK_0 HDMI_CORE_BASE(0x00B0)
+#define HDMI_V2_BLANK_1 HDMI_CORE_BASE(0x00B4)
+#define HDMI_V1_BLANK_0 HDMI_CORE_BASE(0x00B8)
+#define HDMI_V1_BLANK_1 HDMI_CORE_BASE(0x00BC)
+
+#define HDMI_V_LINE_0 HDMI_CORE_BASE(0x00C0)
+#define HDMI_V_LINE_1 HDMI_CORE_BASE(0x00C4)
+#define HDMI_H_LINE_0 HDMI_CORE_BASE(0x00C8)
+#define HDMI_H_LINE_1 HDMI_CORE_BASE(0x00CC)
+
+#define HDMI_HSYNC_POL HDMI_CORE_BASE(0x00E0)
+
+#define HDMI_V_BLANK_F0_0 HDMI_CORE_BASE(0x0110)
+#define HDMI_V_BLANK_F0_1 HDMI_CORE_BASE(0x0114)
+#define HDMI_V_BLANK_F1_0 HDMI_CORE_BASE(0x0118)
+#define HDMI_V_BLANK_F1_1 HDMI_CORE_BASE(0x011C)
+
+#define HDMI_H_SYNC_START_0 HDMI_CORE_BASE(0x0120)
+#define HDMI_H_SYNC_START_1 HDMI_CORE_BASE(0x0124)
+#define HDMI_H_SYNC_END_0 HDMI_CORE_BASE(0x0128)
+#define HDMI_H_SYNC_END_1 HDMI_CORE_BASE(0x012C)
+
+#define HDMI_V_SYNC_LINE_BEF_2_0 HDMI_CORE_BASE(0x0130)
+#define HDMI_V_SYNC_LINE_BEF_2_1 HDMI_CORE_BASE(0x0134)
+#define HDMI_V_SYNC_LINE_BEF_1_0 HDMI_CORE_BASE(0x0138)
+#define HDMI_V_SYNC_LINE_BEF_1_1 HDMI_CORE_BASE(0x013C)
+
+#define HDMI_V_SYNC_LINE_AFT_2_0 HDMI_CORE_BASE(0x0140)
+#define HDMI_V_SYNC_LINE_AFT_2_1 HDMI_CORE_BASE(0x0144)
+#define HDMI_V_SYNC_LINE_AFT_1_0 HDMI_CORE_BASE(0x0148)
+#define HDMI_V_SYNC_LINE_AFT_1_1 HDMI_CORE_BASE(0x014C)
+
+#define HDMI_V_SYNC_LINE_AFT_PXL_2_0 HDMI_CORE_BASE(0x0150)
+#define HDMI_V_SYNC_LINE_AFT_PXL_2_1 HDMI_CORE_BASE(0x0154)
+#define HDMI_V_SYNC_LINE_AFT_PXL_1_0 HDMI_CORE_BASE(0x0158)
+#define HDMI_V_SYNC_LINE_AFT_PXL_1_1 HDMI_CORE_BASE(0x015C)
+
+#define HDMI_V_BLANK_F2_0 HDMI_CORE_BASE(0x0160)
+#define HDMI_V_BLANK_F2_1 HDMI_CORE_BASE(0x0164)
+#define HDMI_V_BLANK_F3_0 HDMI_CORE_BASE(0x0168)
+#define HDMI_V_BLANK_F3_1 HDMI_CORE_BASE(0x016C)
+#define HDMI_V_BLANK_F4_0 HDMI_CORE_BASE(0x0170)
+#define HDMI_V_BLANK_F4_1 HDMI_CORE_BASE(0x0174)
+#define HDMI_V_BLANK_F5_0 HDMI_CORE_BASE(0x0178)
+#define HDMI_V_BLANK_F5_1 HDMI_CORE_BASE(0x017C)
+
+#define HDMI_V_SYNC_LINE_AFT_3_0 HDMI_CORE_BASE(0x0180)
+#define HDMI_V_SYNC_LINE_AFT_3_1 HDMI_CORE_BASE(0x0184)
+#define HDMI_V_SYNC_LINE_AFT_4_0 HDMI_CORE_BASE(0x0188)
+#define HDMI_V_SYNC_LINE_AFT_4_1 HDMI_CORE_BASE(0x018C)
+#define HDMI_V_SYNC_LINE_AFT_5_0 HDMI_CORE_BASE(0x0190)
+#define HDMI_V_SYNC_LINE_AFT_5_1 HDMI_CORE_BASE(0x0194)
+#define HDMI_V_SYNC_LINE_AFT_6_0 HDMI_CORE_BASE(0x0198)
+#define HDMI_V_SYNC_LINE_AFT_6_1 HDMI_CORE_BASE(0x019C)
+
+#define HDMI_V_SYNC_LINE_AFT_PXL_3_0 HDMI_CORE_BASE(0x01A0)
+#define HDMI_V_SYNC_LINE_AFT_PXL_3_1 HDMI_CORE_BASE(0x01A4)
+#define HDMI_V_SYNC_LINE_AFT_PXL_4_0 HDMI_CORE_BASE(0x01A8)
+#define HDMI_V_SYNC_LINE_AFT_PXL_4_1 HDMI_CORE_BASE(0x01AC)
+#define HDMI_V_SYNC_LINE_AFT_PXL_5_0 HDMI_CORE_BASE(0x01B0)
+#define HDMI_V_SYNC_LINE_AFT_PXL_5_1 HDMI_CORE_BASE(0x01B4)
+#define HDMI_V_SYNC_LINE_AFT_PXL_6_0 HDMI_CORE_BASE(0x01B8)
+#define HDMI_V_SYNC_LINE_AFT_PXL_6_1 HDMI_CORE_BASE(0x01BC)
+
+#define HDMI_VACT_SPACE_1_0 HDMI_CORE_BASE(0x01C0)
+#define HDMI_VACT_SPACE_1_1 HDMI_CORE_BASE(0x01C4)
+#define HDMI_VACT_SPACE_2_0 HDMI_CORE_BASE(0x01C8)
+#define HDMI_VACT_SPACE_2_1 HDMI_CORE_BASE(0x01CC)
+#define HDMI_VACT_SPACE_3_0 HDMI_CORE_BASE(0x01D0)
+#define HDMI_VACT_SPACE_3_1 HDMI_CORE_BASE(0x01D4)
+#define HDMI_VACT_SPACE_4_0 HDMI_CORE_BASE(0x01D8)
+#define HDMI_VACT_SPACE_4_1 HDMI_CORE_BASE(0x01DC)
+#define HDMI_VACT_SPACE_5_0 HDMI_CORE_BASE(0x01E0)
+#define HDMI_VACT_SPACE_5_1 HDMI_CORE_BASE(0x01E4)
+#define HDMI_VACT_SPACE_6_0 HDMI_CORE_BASE(0x01E8)
+#define HDMI_VACT_SPACE_6_1 HDMI_CORE_BASE(0x01EC)
+
+#define HDMI_GCP_CON HDMI_CORE_BASE(0x0200)
+#define HDMI_GCP_BYTE1 HDMI_CORE_BASE(0x0210)
+#define HDMI_GCP_BYTE2 HDMI_CORE_BASE(0x0214)
+#define HDMI_GCP_BYTE3 HDMI_CORE_BASE(0x0218)
+
+/* Audio related registers */
+#define HDMI_ASP_CON HDMI_CORE_BASE(0x0300)
+#define HDMI_ASP_SP_FLAT HDMI_CORE_BASE(0x0304)
+#define HDMI_ASP_CHCFG0 HDMI_CORE_BASE(0x0310)
+#define HDMI_ASP_CHCFG1 HDMI_CORE_BASE(0x0314)
+#define HDMI_ASP_CHCFG2 HDMI_CORE_BASE(0x0318)
+#define HDMI_ASP_CHCFG3 HDMI_CORE_BASE(0x031C)
+
+#define HDMI_ACR_CON HDMI_CORE_BASE(0x0400)
+#define HDMI_ACR_MCTS0 HDMI_CORE_BASE(0x0410)
+#define HDMI_ACR_MCTS1 HDMI_CORE_BASE(0x0414)
+#define HDMI_ACR_MCTS2 HDMI_CORE_BASE(0x0418)
+#define HDMI_ACR_CTS0 HDMI_CORE_BASE(0x0420)
+#define HDMI_ACR_CTS1 HDMI_CORE_BASE(0x0424)
+#define HDMI_ACR_CTS2 HDMI_CORE_BASE(0x0428)
+#define HDMI_ACR_N0 HDMI_CORE_BASE(0x0430)
+#define HDMI_ACR_N1 HDMI_CORE_BASE(0x0434)
+#define HDMI_ACR_N2 HDMI_CORE_BASE(0x0438)
+
+/* Packet related registers */
+#define HDMI_ACP_CON HDMI_CORE_BASE(0x0500)
+#define HDMI_ACP_TYPE HDMI_CORE_BASE(0x0514)
+#define HDMI_ACP_DATA(n) HDMI_CORE_BASE(0x0520 + 4 * (n))
+
+#define HDMI_ISRC_CON HDMI_CORE_BASE(0x0600)
+#define HDMI_ISRC1_HEADER1 HDMI_CORE_BASE(0x0614)
+#define HDMI_ISRC1_DATA(n) HDMI_CORE_BASE(0x0620 + 4 * (n))
+#define HDMI_ISRC2_DATA(n) HDMI_CORE_BASE(0x06A0 + 4 * (n))
+
+#define HDMI_AVI_CON HDMI_CORE_BASE(0x0700)
+#define HDMI_AVI_HEADER0 HDMI_CORE_BASE(0x0710)
+#define HDMI_AVI_HEADER1 HDMI_CORE_BASE(0x0714)
+#define HDMI_AVI_HEADER2 HDMI_CORE_BASE(0x0718)
+#define HDMI_AVI_CHECK_SUM HDMI_CORE_BASE(0x071C)
+#define HDMI_AVI_BYTE(n) HDMI_CORE_BASE(0x0720 + 4 * (n))
+
+#define HDMI_AUI_CON HDMI_CORE_BASE(0x0800)
+#define HDMI_AUI_HEADER0 HDMI_CORE_BASE(0x0810)
+#define HDMI_AUI_HEADER1 HDMI_CORE_BASE(0x0814)
+#define HDMI_AUI_HEADER2 HDMI_CORE_BASE(0x0818)
+#define HDMI_AUI_CHECK_SUM HDMI_CORE_BASE(0x081C)
+#define HDMI_AUI_BYTE(n) HDMI_CORE_BASE(0x0820 + 4 * (n))
+
+#define HDMI_MPG_CON HDMI_CORE_BASE(0x0900)
+#define HDMI_MPG_CHECK_SUM HDMI_CORE_BASE(0x091C)
+#define HDMI_MPG_DATA(n) HDMI_CORE_BASE(0x0920 + 4 * (n))
+
+#define HDMI_SPD_CON HDMI_CORE_BASE(0x0A00)
+#define HDMI_SPD_HEADER0 HDMI_CORE_BASE(0x0A10)
+#define HDMI_SPD_HEADER1 HDMI_CORE_BASE(0x0A14)
+#define HDMI_SPD_HEADER2 HDMI_CORE_BASE(0x0A18)
+#define HDMI_SPD_DATA(n) HDMI_CORE_BASE(0x0A20 + 4 * (n))
+
+#define HDMI_GAMUT_CON HDMI_CORE_BASE(0x0B00)
+#define HDMI_GAMUT_HEADER0 HDMI_CORE_BASE(0x0B10)
+#define HDMI_GAMUT_HEADER1 HDMI_CORE_BASE(0x0B14)
+#define HDMI_GAMUT_HEADER2 HDMI_CORE_BASE(0x0B18)
+#define HDMI_GAMUT_METADATA(n) HDMI_CORE_BASE(0x0B20 + 4 * (n))
+
+#define HDMI_VSI_CON HDMI_CORE_BASE(0x0C00)
+#define HDMI_VSI_HEADER0 HDMI_CORE_BASE(0x0C10)
+#define HDMI_VSI_HEADER1 HDMI_CORE_BASE(0x0C14)
+#define HDMI_VSI_HEADER2 HDMI_CORE_BASE(0x0C18)
+#define HDMI_VSI_DATA(n) HDMI_CORE_BASE(0x0C20 + 4 * (n))
+
+#define HDMI_DC_CONTROL HDMI_CORE_BASE(0x0D00)
+#define HDMI_VIDEO_PATTERN_GEN HDMI_CORE_BASE(0x0D04)
+
+#define HDMI_AN_SEED_SEL HDMI_CORE_BASE(0x0E48)
+#define HDMI_AN_SEED_0 HDMI_CORE_BASE(0x0E58)
+#define HDMI_AN_SEED_1 HDMI_CORE_BASE(0x0E5C)
+#define HDMI_AN_SEED_2 HDMI_CORE_BASE(0x0E60)
+#define HDMI_AN_SEED_3 HDMI_CORE_BASE(0x0E64)
+
+/* HDCP related registers */
+#define HDMI_HDCP_SHA1(n) HDMI_CORE_BASE(0x7000 + 4 * (n))
+#define HDMI_HDCP_KSV_LIST(n) HDMI_CORE_BASE(0x7050 + 4 * (n))
+
+#define HDMI_HDCP_KSV_LIST_CON HDMI_CORE_BASE(0x7064)
+#define HDMI_HDCP_SHA_RESULT HDMI_CORE_BASE(0x7070)
+#define HDMI_HDCP_CTRL1 HDMI_CORE_BASE(0x7080)
+#define HDMI_HDCP_CTRL2 HDMI_CORE_BASE(0x7084)
+#define HDMI_HDCP_CHECK_RESULT HDMI_CORE_BASE(0x7090)
+#define HDMI_HDCP_BKSV(n) HDMI_CORE_BASE(0x70A0 + 4 * (n))
+#define HDMI_HDCP_AKSV(n) HDMI_CORE_BASE(0x70C0 + 4 * (n))
+#define HDMI_HDCP_AN(n) HDMI_CORE_BASE(0x70E0 + 4 * (n))
+
+#define HDMI_HDCP_BCAPS HDMI_CORE_BASE(0x7100)
+#define HDMI_HDCP_BSTATUS_0 HDMI_CORE_BASE(0x7110)
+#define HDMI_HDCP_BSTATUS_1 HDMI_CORE_BASE(0x7114)
+#define HDMI_HDCP_RI_0 HDMI_CORE_BASE(0x7140)
+#define HDMI_HDCP_RI_1 HDMI_CORE_BASE(0x7144)
+#define HDMI_HDCP_I2C_INT HDMI_CORE_BASE(0x7180)
+#define HDMI_HDCP_AN_INT HDMI_CORE_BASE(0x7190)
+#define HDMI_HDCP_WDT_INT HDMI_CORE_BASE(0x71A0)
+#define HDMI_HDCP_RI_INT HDMI_CORE_BASE(0x71B0)
+#define HDMI_HDCP_RI_COMPARE_0 HDMI_CORE_BASE(0x71D0)
+#define HDMI_HDCP_RI_COMPARE_1 HDMI_CORE_BASE(0x71D4)
+#define HDMI_HDCP_FRAME_COUNT HDMI_CORE_BASE(0x71E0)
+
+#define HDMI_RGB_ROUND_EN HDMI_CORE_BASE(0xD500)
+#define HDMI_VACT_SPACE_R_0 HDMI_CORE_BASE(0xD504)
+#define HDMI_VACT_SPACE_R_1 HDMI_CORE_BASE(0xD508)
+#define HDMI_VACT_SPACE_G_0 HDMI_CORE_BASE(0xD50C)
+#define HDMI_VACT_SPACE_G_1 HDMI_CORE_BASE(0xD510)
+#define HDMI_VACT_SPACE_B_0 HDMI_CORE_BASE(0xD514)
+#define HDMI_VACT_SPACE_B_1 HDMI_CORE_BASE(0xD518)
+
+#define HDMI_BLUE_SCREEN_B_0 HDMI_CORE_BASE(0xD520)
+#define HDMI_BLUE_SCREEN_B_1 HDMI_CORE_BASE(0xD524)
+#define HDMI_BLUE_SCREEN_G_0 HDMI_CORE_BASE(0xD528)
+#define HDMI_BLUE_SCREEN_G_1 HDMI_CORE_BASE(0xD52C)
+#define HDMI_BLUE_SCREEN_R_0 HDMI_CORE_BASE(0xD530)
+#define HDMI_BLUE_SCREEN_R_1 HDMI_CORE_BASE(0xD534)
+
+/* HDMI I2S register */
+#define HDMI_I2S_CLK_CON HDMI_I2S_BASE(0x000)
+#define HDMI_I2S_CON_1 HDMI_I2S_BASE(0x004)
+#define HDMI_I2S_CON_2 HDMI_I2S_BASE(0x008)
+#define HDMI_I2S_PIN_SEL_0 HDMI_I2S_BASE(0x00c)
+#define HDMI_I2S_PIN_SEL_1 HDMI_I2S_BASE(0x010)
+#define HDMI_I2S_PIN_SEL_2 HDMI_I2S_BASE(0x014)
+#define HDMI_I2S_PIN_SEL_3 HDMI_I2S_BASE(0x018)
+#define HDMI_I2S_DSD_CON HDMI_I2S_BASE(0x01c)
+#define HDMI_I2S_MUX_CON HDMI_I2S_BASE(0x020)
+#define HDMI_I2S_CH_ST_CON HDMI_I2S_BASE(0x024)
+#define HDMI_I2S_CH_ST_0 HDMI_I2S_BASE(0x028)
+#define HDMI_I2S_CH_ST_1 HDMI_I2S_BASE(0x02c)
+#define HDMI_I2S_CH_ST_2 HDMI_I2S_BASE(0x030)
+#define HDMI_I2S_CH_ST_3 HDMI_I2S_BASE(0x034)
+#define HDMI_I2S_CH_ST_4 HDMI_I2S_BASE(0x038)
+#define HDMI_I2S_CH_ST_SH_0 HDMI_I2S_BASE(0x03c)
+#define HDMI_I2S_CH_ST_SH_1 HDMI_I2S_BASE(0x040)
+#define HDMI_I2S_CH_ST_SH_2 HDMI_I2S_BASE(0x044)
+#define HDMI_I2S_CH_ST_SH_3 HDMI_I2S_BASE(0x048)
+#define HDMI_I2S_CH_ST_SH_4 HDMI_I2S_BASE(0x04c)
+#define HDMI_I2S_MUX_CH HDMI_I2S_BASE(0x054)
+#define HDMI_I2S_MUX_CUV HDMI_I2S_BASE(0x058)
+
+/* I2S bit definition */
+
+/* I2S_CLK_CON */
+#define HDMI_I2S_CLK_DIS (0)
+#define HDMI_I2S_CLK_EN (1)
+
+/* I2S_CON_1 */
+#define HDMI_I2S_SCLK_FALLING_EDGE (0 << 1)
+#define HDMI_I2S_SCLK_RISING_EDGE (1 << 1)
+#define HDMI_I2S_L_CH_LOW_POL (0)
+#define HDMI_I2S_L_CH_HIGH_POL (1)
+
+/* I2S_CON_2 */
+#define HDMI_I2S_MSB_FIRST_MODE (0 << 6)
+#define HDMI_I2S_LSB_FIRST_MODE (1 << 6)
+#define HDMI_I2S_BIT_CH_32FS (0 << 4)
+#define HDMI_I2S_BIT_CH_48FS (1 << 4)
+#define HDMI_I2S_BIT_CH_RESERVED (2 << 4)
+#define HDMI_I2S_SDATA_16BIT (1 << 2)
+#define HDMI_I2S_SDATA_20BIT (2 << 2)
+#define HDMI_I2S_SDATA_24BIT (3 << 2)
+#define HDMI_I2S_BASIC_FORMAT (0)
+#define HDMI_I2S_L_JUST_FORMAT (2)
+#define HDMI_I2S_R_JUST_FORMAT (3)
+#define HDMI_I2S_CON_2_CLR (~(0xFF))
+#define HDMI_I2S_SET_BIT_CH(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SET_SDATA_BIT(x) (((x) & 0x7) << 2)
+
+/* I2S_PIN_SEL_0 */
+#define HDMI_I2S_SEL_SCLK(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SEL_LRCK(x) ((x) & 0x7)
+
+/* I2S_PIN_SEL_1 */
+#define HDMI_I2S_SEL_SDATA1(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7)
+
+/* I2S_PIN_SEL_2 */
+#define HDMI_I2S_SEL_SDATA3(x) (((x) & 0x7) << 4)
+#define HDMI_I2S_SEL_SDATA2(x) ((x) & 0x7)
+
+/* I2S_PIN_SEL_3 */
+#define HDMI_I2S_SEL_DSD(x) ((x) & 0x7)
+
+/* I2S_DSD_CON */
+#define HDMI_I2S_DSD_CLK_RI_EDGE (1 << 1)
+#define HDMI_I2S_DSD_CLK_FA_EDGE (0 << 1)
+#define HDMI_I2S_DSD_ENABLE (1)
+#define HDMI_I2S_DSD_DISABLE (0)
+
+/* I2S_MUX_CON */
+#define HDMI_I2S_NOISE_FILTER_ZERO (0 << 5)
+#define HDMI_I2S_NOISE_FILTER_2_STAGE (1 << 5)
+#define HDMI_I2S_NOISE_FILTER_3_STAGE (2 << 5)
+#define HDMI_I2S_NOISE_FILTER_4_STAGE (3 << 5)
+#define HDMI_I2S_NOISE_FILTER_5_STAGE (4 << 5)
+#define HDMI_I2S_IN_DISABLE (1 << 4)
+#define HDMI_I2S_IN_ENABLE (0 << 4)
+#define HDMI_I2S_AUD_SPDIF (0 << 2)
+#define HDMI_I2S_AUD_I2S (1 << 2)
+#define HDMI_I2S_AUD_DSD (2 << 2)
+#define HDMI_I2S_CUV_SPDIF_ENABLE (0 << 1)
+#define HDMI_I2S_CUV_I2S_ENABLE (1 << 1)
+#define HDMI_I2S_MUX_DISABLE (0)
+#define HDMI_I2S_MUX_ENABLE (1)
+#define HDMI_I2S_MUX_CON_CLR (~(0xFF))
+
+/* I2S_CH_ST_CON */
+#define HDMI_I2S_CH_STATUS_RELOAD (1)
+#define HDMI_I2S_CH_ST_CON_CLR (~(1))
+
+/* I2S_CH_ST_0 / I2S_CH_ST_SH_0 */
+#define HDMI_I2S_CH_STATUS_MODE_0 (0 << 6)
+#define HDMI_I2S_2AUD_CH_WITHOUT_PREEMPH (0 << 3)
+#define HDMI_I2S_2AUD_CH_WITH_PREEMPH (1 << 3)
+#define HDMI_I2S_DEFAULT_EMPHASIS (0 << 3)
+#define HDMI_I2S_COPYRIGHT (0 << 2)
+#define HDMI_I2S_NO_COPYRIGHT (1 << 2)
+#define HDMI_I2S_LINEAR_PCM (0 << 1)
+#define HDMI_I2S_NO_LINEAR_PCM (1 << 1)
+#define HDMI_I2S_CONSUMER_FORMAT (0)
+#define HDMI_I2S_PROF_FORMAT (1)
+#define HDMI_I2S_CH_ST_0_CLR (~(0xFF))
+
+/* I2S_CH_ST_1 / I2S_CH_ST_SH_1 */
+#define HDMI_I2S_CD_PLAYER (0x00)
+#define HDMI_I2S_DAT_PLAYER (0x03)
+#define HDMI_I2S_DCC_PLAYER (0x43)
+#define HDMI_I2S_MINI_DISC_PLAYER (0x49)
+
+/* I2S_CH_ST_2 / I2S_CH_ST_SH_2 */
+#define HDMI_I2S_CHANNEL_NUM_MASK (0xF << 4)
+#define HDMI_I2S_SOURCE_NUM_MASK (0xF)
+#define HDMI_I2S_SET_CHANNEL_NUM(x) (((x) & (0xF)) << 4)
+#define HDMI_I2S_SET_SOURCE_NUM(x) ((x) & (0xF))
+
+/* I2S_CH_ST_3 / I2S_CH_ST_SH_3 */
+#define HDMI_I2S_CLK_ACCUR_LEVEL_1 (1 << 4)
+#define HDMI_I2S_CLK_ACCUR_LEVEL_2 (0 << 4)
+#define HDMI_I2S_CLK_ACCUR_LEVEL_3 (2 << 4)
+#define HDMI_I2S_SMP_FREQ_44_1 (0x0)
+#define HDMI_I2S_SMP_FREQ_48 (0x2)
+#define HDMI_I2S_SMP_FREQ_32 (0x3)
+#define HDMI_I2S_SMP_FREQ_96 (0xA)
+#define HDMI_I2S_SET_SMP_FREQ(x) ((x) & (0xF))
+
+/* I2S_CH_ST_4 / I2S_CH_ST_SH_4 */
+#define HDMI_I2S_ORG_SMP_FREQ_44_1 (0xF << 4)
+#define HDMI_I2S_ORG_SMP_FREQ_88_2 (0x7 << 4)
+#define HDMI_I2S_ORG_SMP_FREQ_22_05 (0xB << 4)
+#define HDMI_I2S_ORG_SMP_FREQ_176_4 (0x3 << 4)
+#define HDMI_I2S_WORD_LEN_NOT_DEFINE (0x0 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_20BITS (0x1 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_22BITS (0x2 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_23BITS (0x4 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_24BITS (0x5 << 1)
+#define HDMI_I2S_WORD_LEN_MAX24_21BITS (0x6 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_16BITS (0x1 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_18BITS (0x2 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_19BITS (0x4 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_20BITS (0x5 << 1)
+#define HDMI_I2S_WORD_LEN_MAX20_17BITS (0x6 << 1)
+#define HDMI_I2S_WORD_LEN_MAX_24BITS (1)
+#define HDMI_I2S_WORD_LEN_MAX_20BITS (0)
+
+/* I2S_MUX_CH */
+#define HDMI_I2S_CH3_R_EN (1 << 7)
+#define HDMI_I2S_CH3_L_EN (1 << 6)
+#define HDMI_I2S_CH3_EN (3 << 6)
+#define HDMI_I2S_CH2_R_EN (1 << 5)
+#define HDMI_I2S_CH2_L_EN (1 << 4)
+#define HDMI_I2S_CH2_EN (3 << 4)
+#define HDMI_I2S_CH1_R_EN (1 << 3)
+#define HDMI_I2S_CH1_L_EN (1 << 2)
+#define HDMI_I2S_CH1_EN (3 << 2)
+#define HDMI_I2S_CH0_R_EN (1 << 1)
+#define HDMI_I2S_CH0_L_EN (1)
+#define HDMI_I2S_CH0_EN (3)
+#define HDMI_I2S_CH_ALL_EN (0xFF)
+#define HDMI_I2S_MUX_CH_CLR (~HDMI_I2S_CH_ALL_EN)
+
+/* I2S_MUX_CUV */
+#define HDMI_I2S_CUV_R_EN (1 << 1)
+#define HDMI_I2S_CUV_L_EN (1)
+#define HDMI_I2S_CUV_RL_EN (0x03)
+
+/* I2S_CUV_L_R */
+#define HDMI_I2S_CUV_R_DATA_MASK (0x7 << 4)
+#define HDMI_I2S_CUV_L_DATA_MASK (0x7)
+
+/* Timing generator registers */
+/* TG configure/status registers */
+#define HDMI_TG_VACT_ST3_L HDMI_TG_BASE(0x0068)
+#define HDMI_TG_VACT_ST3_H HDMI_TG_BASE(0x006c)
+#define HDMI_TG_VACT_ST4_L HDMI_TG_BASE(0x0070)
+#define HDMI_TG_VACT_ST4_H HDMI_TG_BASE(0x0074)
+#define HDMI_TG_3D HDMI_TG_BASE(0x00F0)
+
#endif /* SAMSUNG_REGS_HDMI_H */
diff --git a/drivers/gpu/drm/gma500/cdv_device.c b/drivers/gpu/drm/gma500/cdv_device.c
index 4a5b099c3bc5..a54cc738926a 100644
--- a/drivers/gpu/drm/gma500/cdv_device.c
+++ b/drivers/gpu/drm/gma500/cdv_device.c
@@ -202,13 +202,12 @@ static inline void CDV_MSG_WRITE32(uint port, uint offset, u32 value)
pci_dev_put(pci_root);
}
-#define PSB_APM_CMD 0x0
-#define PSB_APM_STS 0x04
#define PSB_PM_SSC 0x20
#define PSB_PM_SSS 0x30
-#define PSB_PWRGT_GFX_MASK 0x3
-#define CDV_PWRGT_DISPLAY_CNTR 0x000fc00c
-#define CDV_PWRGT_DISPLAY_STS 0x000fc00c
+#define PSB_PWRGT_GFX_ON 0x02
+#define PSB_PWRGT_GFX_OFF 0x01
+#define PSB_PWRGT_GFX_D0 0x00
+#define PSB_PWRGT_GFX_D3 0x03
static void cdv_init_pm(struct drm_device *dev)
{
@@ -221,26 +220,22 @@ static void cdv_init_pm(struct drm_device *dev)
dev_priv->ospm_base = CDV_MSG_READ32(PSB_PUNIT_PORT,
PSB_OSPMBA) & 0xFFFF;
- /* Force power on for now */
+ /* Power status */
pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
- pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ /* Enable the GPU */
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_cnt |= PSB_PWRGT_GFX_ON;
outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
+
+ /* Wait for the GPU power */
for (i = 0; i < 5; i++) {
u32 pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
if ((pwr_sts & PSB_PWRGT_GFX_MASK) == 0)
- break;
- udelay(10);
- }
- pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC);
- pwr_cnt &= ~CDV_PWRGT_DISPLAY_CNTR;
- outl(pwr_cnt, dev_priv->ospm_base + PSB_PM_SSC);
- for (i = 0; i < 5; i++) {
- u32 pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS);
- if ((pwr_sts & CDV_PWRGT_DISPLAY_STS) == 0)
- break;
+ return;
udelay(10);
}
+ dev_err(dev->dev, "GPU: power management timed out.\n");
}
/**
@@ -249,11 +244,50 @@ static void cdv_init_pm(struct drm_device *dev)
*
* Save the state we need in order to be able to restore the interface
* upon resume from suspend
- *
- * FIXME: review
*/
static int cdv_save_display_registers(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_save_area *regs = &dev_priv->regs;
+ struct drm_connector *connector;
+
+ dev_info(dev->dev, "Saving GPU registers.\n");
+
+ pci_read_config_byte(dev->pdev, 0xF4, &regs->cdv.saveLBB);
+
+ regs->cdv.saveDSPCLK_GATE_D = REG_READ(DSPCLK_GATE_D);
+ regs->cdv.saveRAMCLK_GATE_D = REG_READ(RAMCLK_GATE_D);
+
+ regs->cdv.saveDSPARB = REG_READ(DSPARB);
+ regs->cdv.saveDSPFW[0] = REG_READ(DSPFW1);
+ regs->cdv.saveDSPFW[1] = REG_READ(DSPFW2);
+ regs->cdv.saveDSPFW[2] = REG_READ(DSPFW3);
+ regs->cdv.saveDSPFW[3] = REG_READ(DSPFW4);
+ regs->cdv.saveDSPFW[4] = REG_READ(DSPFW5);
+ regs->cdv.saveDSPFW[5] = REG_READ(DSPFW6);
+
+ regs->cdv.saveADPA = REG_READ(ADPA);
+
+ regs->cdv.savePP_CONTROL = REG_READ(PP_CONTROL);
+ regs->cdv.savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS);
+ regs->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
+ regs->saveBLC_PWM_CTL2 = REG_READ(BLC_PWM_CTL2);
+ regs->cdv.saveLVDS = REG_READ(LVDS);
+
+ regs->cdv.savePFIT_CONTROL = REG_READ(PFIT_CONTROL);
+
+ regs->cdv.savePP_ON_DELAYS = REG_READ(PP_ON_DELAYS);
+ regs->cdv.savePP_OFF_DELAYS = REG_READ(PP_OFF_DELAYS);
+ regs->cdv.savePP_CYCLE = REG_READ(PP_CYCLE);
+
+ regs->cdv.saveVGACNTRL = REG_READ(VGACNTRL);
+
+ regs->cdv.saveIER = REG_READ(PSB_INT_ENABLE_R);
+ regs->cdv.saveIMR = REG_READ(PSB_INT_MASK_R);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_OFF);
+
return 0;
}
@@ -267,16 +301,113 @@ static int cdv_save_display_registers(struct drm_device *dev)
*/
static int cdv_restore_display_registers(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ struct psb_save_area *regs = &dev_priv->regs;
+ struct drm_connector *connector;
+ u32 temp;
+
+ pci_write_config_byte(dev->pdev, 0xF4, regs->cdv.saveLBB);
+
+ REG_WRITE(DSPCLK_GATE_D, regs->cdv.saveDSPCLK_GATE_D);
+ REG_WRITE(RAMCLK_GATE_D, regs->cdv.saveRAMCLK_GATE_D);
+
+ /* BIOS does below anyway */
+ REG_WRITE(DPIO_CFG, 0);
+ REG_WRITE(DPIO_CFG, DPIO_MODE_SELECT_0 | DPIO_CMN_RESET_N);
+
+ temp = REG_READ(DPLL_A);
+ if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) {
+ REG_WRITE(DPLL_A, temp | DPLL_SYNCLOCK_ENABLE);
+ REG_READ(DPLL_A);
+ }
+
+ temp = REG_READ(DPLL_B);
+ if ((temp & DPLL_SYNCLOCK_ENABLE) == 0) {
+ REG_WRITE(DPLL_B, temp | DPLL_SYNCLOCK_ENABLE);
+ REG_READ(DPLL_B);
+ }
+
+ udelay(500);
+
+ REG_WRITE(DSPFW1, regs->cdv.saveDSPFW[0]);
+ REG_WRITE(DSPFW2, regs->cdv.saveDSPFW[1]);
+ REG_WRITE(DSPFW3, regs->cdv.saveDSPFW[2]);
+ REG_WRITE(DSPFW4, regs->cdv.saveDSPFW[3]);
+ REG_WRITE(DSPFW5, regs->cdv.saveDSPFW[4]);
+ REG_WRITE(DSPFW6, regs->cdv.saveDSPFW[5]);
+
+ REG_WRITE(DSPARB, regs->cdv.saveDSPARB);
+ REG_WRITE(ADPA, regs->cdv.saveADPA);
+
+ REG_WRITE(BLC_PWM_CTL2, regs->saveBLC_PWM_CTL2);
+ REG_WRITE(LVDS, regs->cdv.saveLVDS);
+ REG_WRITE(PFIT_CONTROL, regs->cdv.savePFIT_CONTROL);
+ REG_WRITE(PFIT_PGM_RATIOS, regs->cdv.savePFIT_PGM_RATIOS);
+ REG_WRITE(BLC_PWM_CTL, regs->saveBLC_PWM_CTL);
+ REG_WRITE(PP_ON_DELAYS, regs->cdv.savePP_ON_DELAYS);
+ REG_WRITE(PP_OFF_DELAYS, regs->cdv.savePP_OFF_DELAYS);
+ REG_WRITE(PP_CYCLE, regs->cdv.savePP_CYCLE);
+ REG_WRITE(PP_CONTROL, regs->cdv.savePP_CONTROL);
+
+ REG_WRITE(VGACNTRL, regs->cdv.saveVGACNTRL);
+
+ REG_WRITE(PSB_INT_ENABLE_R, regs->cdv.saveIER);
+ REG_WRITE(PSB_INT_MASK_R, regs->cdv.saveIMR);
+
+ /* Fix arbitration bug */
+ CDV_MSG_WRITE32(3, 0x30, 0x08027108);
+
+ drm_mode_config_reset(dev);
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ connector->funcs->dpms(connector, DRM_MODE_DPMS_ON);
+
+ /* Resume the modeset for every activated CRTC */
+ drm_helper_resume_force_mode(dev);
return 0;
}
static int cdv_power_down(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 pwr_cnt, pwr_mask, pwr_sts;
+ int tries = 5;
+
+ pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_cnt |= PSB_PWRGT_GFX_OFF;
+ pwr_mask = PSB_PWRGT_GFX_MASK;
+
+ outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
+
+ while (tries--) {
+ pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
+ if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D3)
+ return 0;
+ udelay(10);
+ }
return 0;
}
static int cdv_power_up(struct drm_device *dev)
{
+ struct drm_psb_private *dev_priv = dev->dev_private;
+ u32 pwr_cnt, pwr_mask, pwr_sts;
+ int tries = 5;
+
+ pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
+ pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
+ pwr_cnt |= PSB_PWRGT_GFX_ON;
+ pwr_mask = PSB_PWRGT_GFX_MASK;
+
+ outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
+
+ while (tries--) {
+ pwr_sts = inl(dev_priv->apm_base + PSB_APM_STS);
+ if ((pwr_sts & pwr_mask) == PSB_PWRGT_GFX_D0)
+ return 0;
+ udelay(10);
+ }
return 0;
}
@@ -321,6 +452,8 @@ static int cdv_chip_setup(struct drm_device *dev)
cdv_get_core_freq(dev);
gma_intel_opregion_init(dev);
psb_intel_init_bios(dev);
+ REG_WRITE(PORT_HOTPLUG_EN, 0);
+ REG_WRITE(PORT_HOTPLUG_STAT, REG_READ(PORT_HOTPLUG_STAT));
return 0;
}
diff --git a/drivers/gpu/drm/gma500/framebuffer.c b/drivers/gpu/drm/gma500/framebuffer.c
index 98006308bab2..8ea202f1ba50 100644
--- a/drivers/gpu/drm/gma500/framebuffer.c
+++ b/drivers/gpu/drm/gma500/framebuffer.c
@@ -125,7 +125,7 @@ static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
unsigned long phys_addr = (unsigned long)dev_priv->stolen_base;
page_num = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
- address = (unsigned long)vmf->virtual_address;
+ address = (unsigned long)vmf->virtual_address - (vmf->pgoff << PAGE_SHIFT);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
@@ -214,7 +214,6 @@ static struct fb_ops psbfb_roll_ops = {
.fb_imageblit = cfb_imageblit,
.fb_pan_display = psbfb_pan,
.fb_mmap = psbfb_mmap,
- .fb_sync = psbfb_sync,
.fb_ioctl = psbfb_ioctl,
};
diff --git a/drivers/gpu/drm/gma500/gtt.c b/drivers/gpu/drm/gma500/gtt.c
index 574bc9794a0e..c6465b40090f 100644
--- a/drivers/gpu/drm/gma500/gtt.c
+++ b/drivers/gpu/drm/gma500/gtt.c
@@ -446,10 +446,9 @@ int psb_gtt_init(struct drm_device *dev, int resume)
pg->gtt_start = pci_resource_start(dev->pdev, PSB_GTT_RESOURCE);
gtt_pages = pci_resource_len(dev->pdev, PSB_GTT_RESOURCE)
>> PAGE_SHIFT;
- /* Some CDV firmware doesn't report this currently. In which case the
- system has 64 gtt pages */
+ /* CDV doesn't report this. In which case the system has 64 gtt pages */
if (pg->gtt_start == 0 || gtt_pages == 0) {
- dev_err(dev->dev, "GTT PCI BAR not initialized.\n");
+ dev_dbg(dev->dev, "GTT PCI BAR not initialized.\n");
gtt_pages = 64;
pg->gtt_start = dev_priv->pge_ctl;
}
@@ -461,10 +460,10 @@ int psb_gtt_init(struct drm_device *dev, int resume)
if (pg->gatt_pages == 0 || pg->gatt_start == 0) {
static struct resource fudge; /* Preferably peppermint */
- /* This can occur on CDV SDV systems. Fudge it in this case.
+ /* This can occur on CDV systems. Fudge it in this case.
We really don't care what imaginary space is being allocated
at this point */
- dev_err(dev->dev, "GATT PCI BAR not initialized.\n");
+ dev_dbg(dev->dev, "GATT PCI BAR not initialized.\n");
pg->gatt_start = 0x40000000;
pg->gatt_pages = (128 * 1024 * 1024) >> PAGE_SHIFT;
/* This is a little confusing but in fact the GTT is providing
diff --git a/drivers/gpu/drm/gma500/power.c b/drivers/gpu/drm/gma500/power.c
index 994f66935499..889b854751da 100644
--- a/drivers/gpu/drm/gma500/power.c
+++ b/drivers/gpu/drm/gma500/power.c
@@ -102,9 +102,6 @@ static void gma_resume_display(struct pci_dev *pdev)
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_psb_private *dev_priv = dev->dev_private;
- if (dev_priv->suspended == false)
- return;
-
/* turn on the display power island */
dev_priv->ops->power_up(dev);
dev_priv->suspended = false;
@@ -196,6 +193,7 @@ int gma_power_suspend(struct device *_dev)
if (!dev_priv->suspended) {
if (dev_priv->display_count) {
mutex_unlock(&power_mutex);
+ dev_err(dev->dev, "GPU hardware busy, cannot suspend\n");
return -EBUSY;
}
psb_irq_uninstall(dev);
@@ -303,7 +301,7 @@ int psb_runtime_suspend(struct device *dev)
int psb_runtime_resume(struct device *dev)
{
- return gma_power_resume(dev);;
+ return gma_power_resume(dev);
}
int psb_runtime_idle(struct device *dev)
diff --git a/drivers/gpu/drm/gma500/psb_drv.h b/drivers/gpu/drm/gma500/psb_drv.h
index af1c99752000..40ce2c9bc2e4 100644
--- a/drivers/gpu/drm/gma500/psb_drv.h
+++ b/drivers/gpu/drm/gma500/psb_drv.h
@@ -456,12 +456,32 @@ struct medfield_state {
uint32_t saveHDMIB_CONTROL;
};
+struct cdv_state {
+ uint32_t saveDSPCLK_GATE_D;
+ uint32_t saveRAMCLK_GATE_D;
+ uint32_t saveDSPARB;
+ uint32_t saveDSPFW[6];
+ uint32_t saveADPA;
+ uint32_t savePP_CONTROL;
+ uint32_t savePFIT_PGM_RATIOS;
+ uint32_t saveLVDS;
+ uint32_t savePFIT_CONTROL;
+ uint32_t savePP_ON_DELAYS;
+ uint32_t savePP_OFF_DELAYS;
+ uint32_t savePP_CYCLE;
+ uint32_t saveVGACNTRL;
+ uint32_t saveIER;
+ uint32_t saveIMR;
+ u8 saveLBB;
+};
+
struct psb_save_area {
uint32_t saveBSM;
uint32_t saveVBT;
union {
struct psb_state psb;
struct medfield_state mdfld;
+ struct cdv_state cdv;
};
uint32_t saveBLC_PWM_CTL2;
uint32_t saveBLC_PWM_CTL;
diff --git a/drivers/gpu/drm/gma500/psb_intel_reg.h b/drivers/gpu/drm/gma500/psb_intel_reg.h
index fcc0af03d685..e89d3a2e8fdc 100644
--- a/drivers/gpu/drm/gma500/psb_intel_reg.h
+++ b/drivers/gpu/drm/gma500/psb_intel_reg.h
@@ -177,6 +177,9 @@
#define LVDSPP_OFF 0x6120c
#define PP_CYCLE 0x61210
+#define PP_ON_DELAYS 0x61208 /* Cedartrail */
+#define PP_OFF_DELAYS 0x6120c /* Cedartrail */
+
#define PFIT_CONTROL 0x61230
#define PFIT_ENABLE (1 << 31)
#define PFIT_PIPE_MASK (3 << 29)
@@ -1252,6 +1255,12 @@ No status bits are changed.
# define SB_BYTE_ENABLE_SHIFT 4
# define SB_BUSY (1 << 0)
+#define DSPCLK_GATE_D 0x6200
+# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* Fixed value on CDV */
+# define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11)
+# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6)
+
+#define RAMCLK_GATE_D 0x6210
/* 32-bit value read/written from the DPIO reg. */
#define SB_DATA 0x02104 /* cedarview */
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index b8397282153b..c0f19f572004 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -35,6 +35,7 @@
#include "intel_ringbuffer.h"
#include <linux/io-mapping.h>
#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
#include <drm/intel-gtt.h>
#include <linux/backlight.h>
@@ -199,7 +200,7 @@ struct drm_i915_error_state {
u32 tiling:2;
u32 dirty:1;
u32 purgeable:1;
- u32 ring:4;
+ s32 ring:4;
u32 cache_level:2;
} *active_bo, *pinned_bo;
u32 active_bo_count, pinned_bo_count;
@@ -298,6 +299,16 @@ enum intel_pch {
struct intel_fbdev;
struct intel_fbc_work;
+struct intel_gmbus {
+ struct i2c_adapter adapter;
+ bool force_bit;
+ bool has_gpio;
+ u32 reg0;
+ u32 gpio_reg;
+ struct i2c_algo_bit_data bit_algo;
+ struct drm_i915_private *dev_priv;
+};
+
typedef struct drm_i915_private {
struct drm_device *dev;
@@ -315,11 +326,7 @@ typedef struct drm_i915_private {
/** gt_lock is also taken in irq contexts. */
struct spinlock gt_lock;
- struct intel_gmbus {
- struct i2c_adapter adapter;
- struct i2c_adapter *force_bit;
- u32 reg0;
- } *gmbus;
+ struct intel_gmbus *gmbus;
/** gmbus_mutex protects against concurrent usage of the single hw gmbus
* controller on different i2c buses. */
diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
index 19a06c280b12..1f441f5c2405 100644
--- a/drivers/gpu/drm/i915/i915_gem.c
+++ b/drivers/gpu/drm/i915/i915_gem.c
@@ -1985,11 +1985,6 @@ i915_wait_request(struct intel_ring_buffer *ring,
if (atomic_read(&dev_priv->mm.wedged))
ret = -EAGAIN;
- if (ret && ret != -ERESTARTSYS)
- DRM_ERROR("%s returns %d (awaiting %d at %d, next %d)\n",
- __func__, ret, seqno, ring->get_seqno(ring),
- dev_priv->next_seqno);
-
/* Directly dispatch request retiring. While we have the work queue
* to handle this, the waiter on a request often wants an associated
* buffer to have made it to the inactive list, and we would need
@@ -3069,10 +3064,13 @@ i915_gem_object_finish_gpu(struct drm_i915_gem_object *obj)
return ret;
}
+ ret = i915_gem_object_wait_rendering(obj);
+ if (ret)
+ return ret;
+
/* Ensure that we invalidate the GPU's caches and TLBs. */
obj->base.read_domains &= ~I915_GEM_GPU_DOMAINS;
-
- return i915_gem_object_wait_rendering(obj);
+ return 0;
}
/**
diff --git a/drivers/gpu/drm/i915/i915_gem_evict.c b/drivers/gpu/drm/i915/i915_gem_evict.c
index 097119caa36a..21a82710f4b2 100644
--- a/drivers/gpu/drm/i915/i915_gem_evict.c
+++ b/drivers/gpu/drm/i915/i915_gem_evict.c
@@ -36,7 +36,6 @@ static bool
mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
{
list_add(&obj->exec_list, unwind);
- drm_gem_object_reference(&obj->base);
return drm_mm_scan_add_block(obj->gtt_space);
}
@@ -49,21 +48,6 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
struct drm_i915_gem_object *obj;
int ret = 0;
- i915_gem_retire_requests(dev);
-
- /* Re-check for free space after retiring requests */
- if (mappable) {
- if (drm_mm_search_free_in_range(&dev_priv->mm.gtt_space,
- min_size, alignment, 0,
- dev_priv->mm.gtt_mappable_end,
- 0))
- return 0;
- } else {
- if (drm_mm_search_free(&dev_priv->mm.gtt_space,
- min_size, alignment, 0))
- return 0;
- }
-
trace_i915_gem_evict(dev, min_size, alignment, mappable);
/*
@@ -139,7 +123,6 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
BUG_ON(ret);
list_del_init(&obj->exec_list);
- drm_gem_object_unreference(&obj->base);
}
/* We expect the caller to unpin, evict all and try again, or give up.
@@ -158,10 +141,10 @@ found:
exec_list);
if (drm_mm_scan_remove_block(obj->gtt_space)) {
list_move(&obj->exec_list, &eviction_list);
+ drm_gem_object_reference(&obj->base);
continue;
}
list_del_init(&obj->exec_list);
- drm_gem_object_unreference(&obj->base);
}
/* Unbinding will emit any required flushes */
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 52a06be1d98d..f59cd3a1f4c6 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -3111,6 +3111,20 @@
#define DISP_TILE_SURFACE_SWIZZLING (1<<13)
#define DISP_FBC_WM_DIS (1<<15)
+/* GEN7 chicken */
+#define GEN7_COMMON_SLICE_CHICKEN1 0x7010
+# define GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC ((1<<10) | (1<<26))
+
+#define GEN7_L3CNTLREG1 0xB01C
+#define GEN7_WA_FOR_GEN7_L3_CONTROL 0x3C4FFF8C
+
+#define GEN7_L3_CHICKEN_MODE_REGISTER 0xB030
+#define GEN7_WA_L3_CHICKEN_MODE 0x20000000
+
+/* WaCatErrorRejectionIssue */
+#define GEN7_SQ_CHICKEN_MBCUNIT_CONFIG 0x9030
+#define GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB (1<<11)
+
/* PCH */
/* south display engine interrupt */
@@ -3714,6 +3728,7 @@
#define GT_FIFO_NUM_RESERVED_ENTRIES 20
#define GEN6_UCGCTL2 0x9404
+# define GEN6_RCZUNIT_CLOCK_GATE_DISABLE (1 << 13)
# define GEN6_RCPBUNIT_CLOCK_GATE_DISABLE (1 << 12)
# define GEN6_RCCUNIT_CLOCK_GATE_DISABLE (1 << 11)
diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
index 50656339d922..8168d8f8a634 100644
--- a/drivers/gpu/drm/i915/intel_bios.c
+++ b/drivers/gpu/drm/i915/intel_bios.c
@@ -669,7 +669,7 @@ intel_parse_bios(struct drm_device *dev)
}
if (!vbt) {
- DRM_ERROR("VBT signature missing\n");
+ DRM_DEBUG_DRIVER("VBT signature missing\n");
pci_unmap_rom(pdev, bios);
return -1;
}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index b65dc04f1289..72b292ae8796 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4737,8 +4737,17 @@ sandybridge_compute_sprite_srwm(struct drm_device *dev, int plane,
crtc = intel_get_crtc_for_plane(dev, plane);
clock = crtc->mode.clock;
+ if (!clock) {
+ *sprite_wm = 0;
+ return false;
+ }
line_time_us = (sprite_width * 1000) / clock;
+ if (!line_time_us) {
+ *sprite_wm = 0;
+ return false;
+ }
+
line_count = (latency_ns / line_time_us + 1000) / 1000;
line_size = sprite_width * pixel_size;
@@ -6268,7 +6277,7 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
int i;
/* The clocks have to be on to load the palette. */
- if (!crtc->enabled)
+ if (!crtc->enabled || !intel_crtc->active)
return;
/* use legacy palette for Ironlake */
@@ -6654,7 +6663,7 @@ intel_framebuffer_create_for_mode(struct drm_device *dev,
mode_cmd.height = mode->vdisplay;
mode_cmd.pitches[0] = intel_framebuffer_pitch_for_width(mode_cmd.width,
bpp);
- mode_cmd.pixel_format = 0;
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
return intel_framebuffer_create(dev, &mode_cmd, obj);
}
@@ -8275,8 +8284,8 @@ void gen6_enable_rps(struct drm_i915_private *dev_priv)
I915_WRITE(GEN6_RC6pp_THRESHOLD, 64000); /* unused */
if (intel_enable_rc6(dev_priv->dev))
- rc6_mask = GEN6_RC_CTL_RC6p_ENABLE |
- GEN6_RC_CTL_RC6_ENABLE;
+ rc6_mask = GEN6_RC_CTL_RC6_ENABLE |
+ ((IS_GEN7(dev_priv->dev)) ? GEN6_RC_CTL_RC6p_ENABLE : 0);
I915_WRITE(GEN6_RC_CONTROL,
rc6_mask |
@@ -8554,12 +8563,32 @@ static void ivybridge_init_clock_gating(struct drm_device *dev)
I915_WRITE(WM2_LP_ILK, 0);
I915_WRITE(WM1_LP_ILK, 0);
+ /* According to the spec, bit 13 (RCZUNIT) must be set on IVB.
+ * This implements the WaDisableRCZUnitClockGating workaround.
+ */
+ I915_WRITE(GEN6_UCGCTL2, GEN6_RCZUNIT_CLOCK_GATE_DISABLE);
+
I915_WRITE(ILK_DSPCLK_GATE, IVB_VRHUNIT_CLK_GATE);
I915_WRITE(IVB_CHICKEN3,
CHICKEN3_DGMG_REQ_OUT_FIX_DISABLE |
CHICKEN3_DGMG_DONE_FIX_DISABLE);
+ /* Apply the WaDisableRHWOOptimizationForRenderHang workaround. */
+ I915_WRITE(GEN7_COMMON_SLICE_CHICKEN1,
+ GEN7_CSC1_RHWO_OPT_DISABLE_IN_RCC);
+
+ /* WaApplyL3ControlAndL3ChickenMode requires those two on Ivy Bridge */
+ I915_WRITE(GEN7_L3CNTLREG1,
+ GEN7_WA_FOR_GEN7_L3_CONTROL);
+ I915_WRITE(GEN7_L3_CHICKEN_MODE_REGISTER,
+ GEN7_WA_L3_CHICKEN_MODE);
+
+ /* This is required by WaCatErrorRejectionIssue */
+ I915_WRITE(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG,
+ I915_READ(GEN7_SQ_CHICKEN_MBCUNIT_CONFIG) |
+ GEN7_SQ_CHICKEN_MBCUNIT_SQINTMOB);
+
for_each_pipe(pipe) {
I915_WRITE(DSPCNTR(pipe),
I915_READ(DSPCNTR(pipe)) |
@@ -9020,8 +9049,6 @@ struct intel_quirk {
};
struct intel_quirk intel_quirks[] = {
- /* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */
- { 0x2a42, 0x103c, 0x30eb, quirk_pipea_force },
/* HP Mini needs pipe A force quirk (LP: #322104) */
{ 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
index 39eccf908a69..110552ff302c 100644
--- a/drivers/gpu/drm/i915/intel_dp.c
+++ b/drivers/gpu/drm/i915/intel_dp.c
@@ -49,7 +49,7 @@ struct intel_dp {
uint32_t DP;
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
bool has_audio;
- int force_audio;
+ enum hdmi_force_audio force_audio;
uint32_t color_range;
int dpms_mode;
uint8_t link_bw;
@@ -2116,8 +2116,8 @@ intel_dp_detect(struct drm_connector *connector, bool force)
if (status != connector_status_connected)
return status;
- if (intel_dp->force_audio) {
- intel_dp->has_audio = intel_dp->force_audio > 0;
+ if (intel_dp->force_audio != HDMI_AUDIO_AUTO) {
+ intel_dp->has_audio = (intel_dp->force_audio == HDMI_AUDIO_ON);
} else {
edid = intel_dp_get_edid(connector, &intel_dp->adapter);
if (edid) {
@@ -2217,10 +2217,10 @@ intel_dp_set_property(struct drm_connector *connector,
intel_dp->force_audio = i;
- if (i == 0)
+ if (i == HDMI_AUDIO_AUTO)
has_audio = intel_dp_detect_audio(connector);
else
- has_audio = i > 0;
+ has_audio = (i == HDMI_AUDIO_ON);
if (has_audio == intel_dp->has_audio)
return 0;
diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
index 068617f7256a..601c86e664af 100644
--- a/drivers/gpu/drm/i915/intel_i2c.c
+++ b/drivers/gpu/drm/i915/intel_i2c.c
@@ -45,13 +45,6 @@ to_intel_gmbus(struct i2c_adapter *i2c)
return container_of(i2c, struct intel_gmbus, adapter);
}
-struct intel_gpio {
- struct i2c_adapter adapter;
- struct i2c_algo_bit_data algo;
- struct drm_i915_private *dev_priv;
- u32 reg;
-};
-
void
intel_i2c_reset(struct drm_device *dev)
{
@@ -78,15 +71,15 @@ static void intel_i2c_quirk_set(struct drm_i915_private *dev_priv, bool enable)
I915_WRITE(DSPCLK_GATE_D, val);
}
-static u32 get_reserved(struct intel_gpio *gpio)
+static u32 get_reserved(struct intel_gmbus *bus)
{
- struct drm_i915_private *dev_priv = gpio->dev_priv;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
struct drm_device *dev = dev_priv->dev;
u32 reserved = 0;
/* On most chips, these bits must be preserved in software. */
if (!IS_I830(dev) && !IS_845G(dev))
- reserved = I915_READ_NOTRACE(gpio->reg) &
+ reserved = I915_READ_NOTRACE(bus->gpio_reg) &
(GPIO_DATA_PULLUP_DISABLE |
GPIO_CLOCK_PULLUP_DISABLE);
@@ -95,29 +88,29 @@ static u32 get_reserved(struct intel_gpio *gpio)
static int get_clock(void *data)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
- I915_WRITE_NOTRACE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK);
- I915_WRITE_NOTRACE(gpio->reg, reserved);
- return (I915_READ_NOTRACE(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0;
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_CLOCK_DIR_MASK);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+ return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_CLOCK_VAL_IN) != 0;
}
static int get_data(void *data)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
- I915_WRITE_NOTRACE(gpio->reg, reserved | GPIO_DATA_DIR_MASK);
- I915_WRITE_NOTRACE(gpio->reg, reserved);
- return (I915_READ_NOTRACE(gpio->reg) & GPIO_DATA_VAL_IN) != 0;
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | GPIO_DATA_DIR_MASK);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved);
+ return (I915_READ_NOTRACE(bus->gpio_reg) & GPIO_DATA_VAL_IN) != 0;
}
static void set_clock(void *data, int state_high)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
u32 clock_bits;
if (state_high)
@@ -126,15 +119,15 @@ static void set_clock(void *data, int state_high)
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
GPIO_CLOCK_VAL_MASK;
- I915_WRITE_NOTRACE(gpio->reg, reserved | clock_bits);
- POSTING_READ(gpio->reg);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | clock_bits);
+ POSTING_READ(bus->gpio_reg);
}
static void set_data(void *data, int state_high)
{
- struct intel_gpio *gpio = data;
- struct drm_i915_private *dev_priv = gpio->dev_priv;
- u32 reserved = get_reserved(gpio);
+ struct intel_gmbus *bus = data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
+ u32 reserved = get_reserved(bus);
u32 data_bits;
if (state_high)
@@ -143,13 +136,14 @@ static void set_data(void *data, int state_high)
data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
GPIO_DATA_VAL_MASK;
- I915_WRITE_NOTRACE(gpio->reg, reserved | data_bits);
- POSTING_READ(gpio->reg);
+ I915_WRITE_NOTRACE(bus->gpio_reg, reserved | data_bits);
+ POSTING_READ(bus->gpio_reg);
}
-static struct i2c_adapter *
-intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin)
+static bool
+intel_gpio_setup(struct intel_gmbus *bus, u32 pin)
{
+ struct drm_i915_private *dev_priv = bus->dev_priv;
static const int map_pin_to_reg[] = {
0,
GPIOB,
@@ -160,65 +154,48 @@ intel_gpio_create(struct drm_i915_private *dev_priv, u32 pin)
0,
GPIOF,
};
- struct intel_gpio *gpio;
+ struct i2c_algo_bit_data *algo;
if (pin >= ARRAY_SIZE(map_pin_to_reg) || !map_pin_to_reg[pin])
- return NULL;
+ return false;
- gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL);
- if (gpio == NULL)
- return NULL;
+ algo = &bus->bit_algo;
- gpio->reg = map_pin_to_reg[pin];
+ bus->gpio_reg = map_pin_to_reg[pin];
if (HAS_PCH_SPLIT(dev_priv->dev))
- gpio->reg += PCH_GPIOA - GPIOA;
- gpio->dev_priv = dev_priv;
-
- snprintf(gpio->adapter.name, sizeof(gpio->adapter.name),
- "i915 GPIO%c", "?BACDE?F"[pin]);
- gpio->adapter.owner = THIS_MODULE;
- gpio->adapter.algo_data = &gpio->algo;
- gpio->adapter.dev.parent = &dev_priv->dev->pdev->dev;
- gpio->algo.setsda = set_data;
- gpio->algo.setscl = set_clock;
- gpio->algo.getsda = get_data;
- gpio->algo.getscl = get_clock;
- gpio->algo.udelay = I2C_RISEFALL_TIME;
- gpio->algo.timeout = usecs_to_jiffies(2200);
- gpio->algo.data = gpio;
-
- if (i2c_bit_add_bus(&gpio->adapter))
- goto out_free;
-
- return &gpio->adapter;
-
-out_free:
- kfree(gpio);
- return NULL;
+ bus->gpio_reg += PCH_GPIOA - GPIOA;
+
+ bus->adapter.algo_data = algo;
+ algo->setsda = set_data;
+ algo->setscl = set_clock;
+ algo->getsda = get_data;
+ algo->getscl = get_clock;
+ algo->udelay = I2C_RISEFALL_TIME;
+ algo->timeout = usecs_to_jiffies(2200);
+ algo->data = bus;
+
+ return true;
}
static int
-intel_i2c_quirk_xfer(struct drm_i915_private *dev_priv,
- struct i2c_adapter *adapter,
+intel_i2c_quirk_xfer(struct intel_gmbus *bus,
struct i2c_msg *msgs,
int num)
{
- struct intel_gpio *gpio = container_of(adapter,
- struct intel_gpio,
- adapter);
+ struct drm_i915_private *dev_priv = bus->dev_priv;
int ret;
intel_i2c_reset(dev_priv->dev);
intel_i2c_quirk_set(dev_priv, true);
- set_data(gpio, 1);
- set_clock(gpio, 1);
+ set_data(bus, 1);
+ set_clock(bus, 1);
udelay(I2C_RISEFALL_TIME);
- ret = adapter->algo->master_xfer(adapter, msgs, num);
+ ret = i2c_bit_algo.master_xfer(&bus->adapter, msgs, num);
- set_data(gpio, 1);
- set_clock(gpio, 1);
+ set_data(bus, 1);
+ set_clock(bus, 1);
intel_i2c_quirk_set(dev_priv, false);
return ret;
@@ -232,14 +209,13 @@ gmbus_xfer(struct i2c_adapter *adapter,
struct intel_gmbus *bus = container_of(adapter,
struct intel_gmbus,
adapter);
- struct drm_i915_private *dev_priv = adapter->algo_data;
+ struct drm_i915_private *dev_priv = bus->dev_priv;
int i, reg_offset, ret;
mutex_lock(&dev_priv->gmbus_mutex);
if (bus->force_bit) {
- ret = intel_i2c_quirk_xfer(dev_priv,
- bus->force_bit, msgs, num);
+ ret = intel_i2c_quirk_xfer(bus, msgs, num);
goto out;
}
@@ -253,7 +229,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
if (msgs[i].flags & I2C_M_RD) {
I915_WRITE(GMBUS1 + reg_offset,
- GMBUS_CYCLE_WAIT | (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
+ GMBUS_CYCLE_WAIT |
+ (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
(len << GMBUS_BYTE_COUNT_SHIFT) |
(msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
GMBUS_SLAVE_READ | GMBUS_SW_RDY);
@@ -282,7 +259,8 @@ gmbus_xfer(struct i2c_adapter *adapter,
I915_WRITE(GMBUS3 + reg_offset, val);
I915_WRITE(GMBUS1 + reg_offset,
- (i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) |
+ GMBUS_CYCLE_WAIT |
+ (i + 1 == num ? GMBUS_CYCLE_STOP : 0) |
(msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) |
(msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
@@ -321,9 +299,12 @@ clear_err:
I915_WRITE(GMBUS1 + reg_offset, 0);
done:
- /* Mark the GMBUS interface as disabled. We will re-enable it at the
- * start of the next xfer, till then let it sleep.
+ /* Mark the GMBUS interface as disabled after waiting for idle.
+ * We will re-enable it at the start of the next xfer,
+ * till then let it sleep.
*/
+ if (wait_for((I915_READ(GMBUS2 + reg_offset) & GMBUS_ACTIVE) == 0, 10))
+ DRM_INFO("GMBUS timed out waiting for idle\n");
I915_WRITE(GMBUS0 + reg_offset, 0);
ret = i;
goto out;
@@ -334,11 +315,12 @@ timeout:
I915_WRITE(GMBUS0 + reg_offset, 0);
/* Hardware may not support GMBUS over these pins? Try GPIO bitbanging instead. */
- bus->force_bit = intel_gpio_create(dev_priv, bus->reg0 & 0xff);
- if (!bus->force_bit)
- ret = -ENOMEM;
- else
- ret = intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num);
+ if (!bus->has_gpio) {
+ ret = -EIO;
+ } else {
+ bus->force_bit = true;
+ ret = intel_i2c_quirk_xfer(bus, msgs, num);
+ }
out:
mutex_unlock(&dev_priv->gmbus_mutex);
return ret;
@@ -346,14 +328,8 @@ out:
static u32 gmbus_func(struct i2c_adapter *adapter)
{
- struct intel_gmbus *bus = container_of(adapter,
- struct intel_gmbus,
- adapter);
-
- if (bus->force_bit)
- bus->force_bit->algo->functionality(bus->force_bit);
-
- return (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+ return i2c_bit_algo.functionality(adapter) &
+ (I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
/* I2C_FUNC_10BIT_ADDR | */
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
I2C_FUNC_SMBUS_BLOCK_PROC_CALL);
@@ -401,7 +377,7 @@ int intel_setup_gmbus(struct drm_device *dev)
names[i]);
bus->adapter.dev.parent = &dev->pdev->dev;
- bus->adapter.algo_data = dev_priv;
+ bus->dev_priv = dev_priv;
bus->adapter.algo = &gmbus_algorithm;
ret = i2c_add_adapter(&bus->adapter);
@@ -411,8 +387,11 @@ int intel_setup_gmbus(struct drm_device *dev)
/* By default use a conservative clock rate */
bus->reg0 = i | GMBUS_RATE_100KHZ;
+ bus->has_gpio = intel_gpio_setup(bus, i);
+
/* XXX force bit banging until GMBUS is fully debugged */
- bus->force_bit = intel_gpio_create(dev_priv, i);
+ if (bus->has_gpio && IS_GEN2(dev))
+ bus->force_bit = true;
}
intel_i2c_reset(dev_priv->dev);
@@ -440,19 +419,8 @@ void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)
{
struct intel_gmbus *bus = to_intel_gmbus(adapter);
- if (force_bit) {
- if (bus->force_bit == NULL) {
- struct drm_i915_private *dev_priv = adapter->algo_data;
- bus->force_bit = intel_gpio_create(dev_priv,
- bus->reg0 & 0xff);
- }
- } else {
- if (bus->force_bit) {
- i2c_del_adapter(bus->force_bit);
- kfree(bus->force_bit);
- bus->force_bit = NULL;
- }
- }
+ if (bus->has_gpio)
+ bus->force_bit = force_bit;
}
void intel_teardown_gmbus(struct drm_device *dev)
@@ -465,10 +433,6 @@ void intel_teardown_gmbus(struct drm_device *dev)
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
struct intel_gmbus *bus = &dev_priv->gmbus[i];
- if (bus->force_bit) {
- i2c_del_adapter(bus->force_bit);
- kfree(bus->force_bit);
- }
i2c_del_adapter(&bus->adapter);
}
diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
index b103c3b2bfee..c5c0973af8a1 100644
--- a/drivers/gpu/drm/i915/intel_lvds.c
+++ b/drivers/gpu/drm/i915/intel_lvds.c
@@ -739,6 +739,22 @@ static const struct dmi_system_id intel_no_lvds[] = {
DMI_MATCH(DMI_BOARD_NAME, "AT5NM10T-I"),
},
},
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Hewlett-Packard t5745",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_BOARD_NAME, "hp t5745"),
+ },
+ },
+ {
+ .callback = intel_no_lvds_dmi_callback,
+ .ident = "Hewlett-Packard st5747",
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_BOARD_NAME, "hp st5747"),
+ },
+ },
{ } /* terminating entry */
};
diff --git a/drivers/gpu/drm/i915/intel_modes.c b/drivers/gpu/drm/i915/intel_modes.c
index 2978a3f61b58..d1928e79d9b6 100644
--- a/drivers/gpu/drm/i915/intel_modes.c
+++ b/drivers/gpu/drm/i915/intel_modes.c
@@ -28,6 +28,7 @@
#include <linux/fb.h>
#include <drm/drm_edid.h>
#include "drmP.h"
+#include "drm_edid.h"
#include "intel_drv.h"
#include "i915_drv.h"
@@ -42,13 +43,13 @@ bool intel_ddc_probe(struct intel_encoder *intel_encoder, int ddc_bus)
u8 buf[2];
struct i2c_msg msgs[] = {
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = 1,
.buf = buf,
diff --git a/drivers/gpu/drm/i915/intel_ringbuffer.c b/drivers/gpu/drm/i915/intel_ringbuffer.c
index ca3972f2c6f5..fc66af6a9448 100644
--- a/drivers/gpu/drm/i915/intel_ringbuffer.c
+++ b/drivers/gpu/drm/i915/intel_ringbuffer.c
@@ -287,7 +287,7 @@ static int init_ring_common(struct intel_ring_buffer *ring)
I915_WRITE_CTL(ring,
((ring->size - PAGE_SIZE) & RING_NR_PAGES)
- | RING_REPORT_64K | RING_VALID);
+ | RING_VALID);
/* If the head is still not zero, the ring is dead */
if ((I915_READ_CTL(ring) & RING_VALID) == 0 ||
@@ -1191,18 +1191,6 @@ int intel_wait_ring_buffer(struct intel_ring_buffer *ring, int n)
struct drm_i915_private *dev_priv = dev->dev_private;
unsigned long end;
int ret;
- u32 head;
-
- /* If the reported head position has wrapped or hasn't advanced,
- * fallback to the slow and accurate path.
- */
- head = intel_read_status_page(ring, 4);
- if (head > ring->head) {
- ring->head = head;
- ring->space = ring_space(ring);
- if (ring->space >= n)
- return 0;
- }
ret = intel_ring_wait_request(ring, n);
if (ret != -ENOSPC)
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index 724190ec496b..e36b171c1e7d 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -148,7 +148,7 @@ struct intel_sdvo_connector {
/* Mark the type of connector */
uint16_t output_flag;
- int force_audio;
+ enum hdmi_force_audio force_audio;
/* This contains all current supported TV format */
u8 tv_format_supported[TV_FORMAT_NUM];
@@ -1309,8 +1309,8 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
if (status == connector_status_connected) {
struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector);
- if (intel_sdvo_connector->force_audio)
- intel_sdvo->has_hdmi_audio = intel_sdvo_connector->force_audio > 0;
+ if (intel_sdvo_connector->force_audio != HDMI_AUDIO_AUTO)
+ intel_sdvo->has_hdmi_audio = (intel_sdvo_connector->force_audio == HDMI_AUDIO_ON);
}
return status;
@@ -1683,10 +1683,10 @@ intel_sdvo_set_property(struct drm_connector *connector,
intel_sdvo_connector->force_audio = i;
- if (i == 0)
+ if (i == HDMI_AUDIO_AUTO)
has_audio = intel_sdvo_detect_hdmi_audio(connector);
else
- has_audio = i > 0;
+ has_audio = (i == HDMI_AUDIO_ON);
if (has_audio == intel_sdvo->has_hdmi_audio)
return 0;
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index ab447275da28..a85e112863d1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -317,6 +317,9 @@ nouveau_display_create(struct drm_device *dev)
dev->mode_config.max_height = 8192;
}
+ dev->mode_config.preferred_depth = 24;
+ dev->mode_config.prefer_shadow = 1;
+
drm_kms_helper_poll_init(dev);
drm_kms_helper_poll_disable(dev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
index 820ae7f52044..8f4f914d9eab 100644
--- a/drivers/gpu/drm/nouveau/nouveau_i2c.c
+++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
@@ -277,7 +277,7 @@ i2c_bit_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-const struct i2c_algorithm i2c_bit_algo = {
+const struct i2c_algorithm nouveau_i2c_bit_algo = {
.master_xfer = i2c_bit_xfer,
.functionality = i2c_bit_func
};
@@ -384,12 +384,12 @@ nouveau_i2c_init(struct drm_device *dev)
case 0: /* NV04:NV50 */
port->drive = entry[0];
port->sense = entry[1];
- port->adapter.algo = &i2c_bit_algo;
+ port->adapter.algo = &nouveau_i2c_bit_algo;
break;
case 4: /* NV4E */
port->drive = 0x600800 + entry[1];
port->sense = port->drive;
- port->adapter.algo = &i2c_bit_algo;
+ port->adapter.algo = &nouveau_i2c_bit_algo;
break;
case 5: /* NV50- */
port->drive = entry[0] & 0x0f;
@@ -402,7 +402,7 @@ nouveau_i2c_init(struct drm_device *dev)
port->drive = 0x00d014 + (port->drive * 0x20);
port->sense = port->drive;
}
- port->adapter.algo = &i2c_bit_algo;
+ port->adapter.algo = &nouveau_i2c_bit_algo;
break;
case 6: /* NV50- DP AUX */
port->drive = entry[0];
diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
index a0f2bebf49e3..55c56330be6d 100644
--- a/drivers/gpu/drm/nouveau/nv50_dac.c
+++ b/drivers/gpu/drm/nouveau/nv50_dac.c
@@ -190,11 +190,8 @@ nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
- connector->native_mode) {
- int id = adjusted_mode->base.id;
- *adjusted_mode = *connector->native_mode;
- adjusted_mode->base.id = id;
- }
+ connector->native_mode)
+ drm_mode_copy(adjusted_mode, connector->native_mode);
return true;
}
diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
index 9a5c2c98658c..a7844ab6a50c 100644
--- a/drivers/gpu/drm/nouveau/nv50_sor.c
+++ b/drivers/gpu/drm/nouveau/nv50_sor.c
@@ -342,11 +342,8 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
}
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
- connector->native_mode) {
- int id = adjusted_mode->base.id;
- *adjusted_mode = *connector->native_mode;
- adjusted_mode->base.id = id;
- }
+ connector->native_mode)
+ drm_mode_copy(adjusted_mode, connector->native_mode);
return true;
}
diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
index 552b436451fd..191218ad92e7 100644
--- a/drivers/gpu/drm/radeon/atombios_dp.c
+++ b/drivers/gpu/drm/radeon/atombios_dp.c
@@ -746,7 +746,8 @@ static int radeon_dp_link_train_init(struct radeon_dp_link_train_info *dp_info)
/* set the lane count on the sink */
tmp = dp_info->dp_lane_count;
- if (dp_info->dpcd[0] >= 0x11)
+ if (dp_info->dpcd[DP_DPCD_REV] >= 0x11 &&
+ dp_info->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)
tmp |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
radeon_write_dpcd_reg(dp_info->radeon_connector, DP_LANE_COUNT_SET, tmp);
diff --git a/drivers/gpu/drm/radeon/evergreen.c b/drivers/gpu/drm/radeon/evergreen.c
index 450b01d915e2..466db4115cd5 100644
--- a/drivers/gpu/drm/radeon/evergreen.c
+++ b/drivers/gpu/drm/radeon/evergreen.c
@@ -3273,6 +3273,7 @@ int evergreen_resume(struct radeon_device *rdev)
r = evergreen_startup(rdev);
if (r) {
DRM_ERROR("evergreen startup failed on resume\n");
+ rdev->accel_working = false;
return r;
}
diff --git a/drivers/gpu/drm/radeon/evergreen_cs.c b/drivers/gpu/drm/radeon/evergreen_cs.c
index 49203b67b81b..a58b37a2e65a 100644
--- a/drivers/gpu/drm/radeon/evergreen_cs.c
+++ b/drivers/gpu/drm/radeon/evergreen_cs.c
@@ -43,48 +43,43 @@ struct evergreen_cs_track {
u32 npipes;
u32 row_size;
/* value we track */
- u32 nsamples;
- u32 cb_color_base_last[12];
+ u32 nsamples; /* unused */
struct radeon_bo *cb_color_bo[12];
u32 cb_color_bo_offset[12];
- struct radeon_bo *cb_color_fmask_bo[8];
- struct radeon_bo *cb_color_cmask_bo[8];
+ struct radeon_bo *cb_color_fmask_bo[8]; /* unused */
+ struct radeon_bo *cb_color_cmask_bo[8]; /* unused */
u32 cb_color_info[12];
u32 cb_color_view[12];
- u32 cb_color_pitch_idx[12];
- u32 cb_color_slice_idx[12];
- u32 cb_color_dim_idx[12];
- u32 cb_color_dim[12];
u32 cb_color_pitch[12];
u32 cb_color_slice[12];
u32 cb_color_attrib[12];
- u32 cb_color_cmask_slice[8];
- u32 cb_color_fmask_slice[8];
+ u32 cb_color_cmask_slice[8];/* unused */
+ u32 cb_color_fmask_slice[8];/* unused */
u32 cb_target_mask;
- u32 cb_shader_mask;
+ u32 cb_shader_mask; /* unused */
u32 vgt_strmout_config;
u32 vgt_strmout_buffer_config;
struct radeon_bo *vgt_strmout_bo[4];
- u64 vgt_strmout_bo_mc[4];
u32 vgt_strmout_bo_offset[4];
u32 vgt_strmout_size[4];
u32 db_depth_control;
u32 db_depth_view;
u32 db_depth_slice;
u32 db_depth_size;
- u32 db_depth_size_idx;
u32 db_z_info;
- u32 db_z_idx;
u32 db_z_read_offset;
u32 db_z_write_offset;
struct radeon_bo *db_z_read_bo;
struct radeon_bo *db_z_write_bo;
u32 db_s_info;
- u32 db_s_idx;
u32 db_s_read_offset;
u32 db_s_write_offset;
struct radeon_bo *db_s_read_bo;
struct radeon_bo *db_s_write_bo;
+ bool sx_misc_kill_all_prims;
+ bool cb_dirty;
+ bool db_dirty;
+ bool streamout_dirty;
};
static u32 evergreen_cs_get_aray_mode(u32 tiling_flags)
@@ -124,44 +119,39 @@ static void evergreen_cs_track_init(struct evergreen_cs_track *track)
}
for (i = 0; i < 12; i++) {
- track->cb_color_base_last[i] = 0;
track->cb_color_bo[i] = NULL;
track->cb_color_bo_offset[i] = 0xFFFFFFFF;
track->cb_color_info[i] = 0;
track->cb_color_view[i] = 0xFFFFFFFF;
- track->cb_color_pitch_idx[i] = 0;
- track->cb_color_slice_idx[i] = 0;
- track->cb_color_dim[i] = 0;
track->cb_color_pitch[i] = 0;
track->cb_color_slice[i] = 0;
- track->cb_color_dim[i] = 0;
}
track->cb_target_mask = 0xFFFFFFFF;
track->cb_shader_mask = 0xFFFFFFFF;
+ track->cb_dirty = true;
track->db_depth_view = 0xFFFFC000;
track->db_depth_size = 0xFFFFFFFF;
- track->db_depth_size_idx = 0;
track->db_depth_control = 0xFFFFFFFF;
track->db_z_info = 0xFFFFFFFF;
- track->db_z_idx = 0xFFFFFFFF;
track->db_z_read_offset = 0xFFFFFFFF;
track->db_z_write_offset = 0xFFFFFFFF;
track->db_z_read_bo = NULL;
track->db_z_write_bo = NULL;
track->db_s_info = 0xFFFFFFFF;
- track->db_s_idx = 0xFFFFFFFF;
track->db_s_read_offset = 0xFFFFFFFF;
track->db_s_write_offset = 0xFFFFFFFF;
track->db_s_read_bo = NULL;
track->db_s_write_bo = NULL;
+ track->db_dirty = true;
for (i = 0; i < 4; i++) {
track->vgt_strmout_size[i] = 0;
track->vgt_strmout_bo[i] = NULL;
track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF;
- track->vgt_strmout_bo_mc[i] = 0xFFFFFFFF;
}
+ track->streamout_dirty = true;
+ track->sx_misc_kill_all_prims = false;
}
struct eg_surface {
@@ -306,8 +296,8 @@ static int evergreen_surface_check(struct radeon_cs_parser *p,
case ARRAY_2D_TILED_THIN1:
return evergreen_surface_check_2d(p, surf, prefix);
default:
- dev_warn(p->dev, "%s:%d invalid array mode %d\n",
- __func__, __LINE__, surf->mode);
+ dev_warn(p->dev, "%s:%d %s invalid array mode %d\n",
+ __func__, __LINE__, prefix, surf->mode);
return -EINVAL;
}
return -EINVAL;
@@ -325,8 +315,8 @@ static int evergreen_surface_value_conv_check(struct radeon_cs_parser *p,
case ARRAY_1D_TILED_THIN1:
return 0;
default:
- dev_warn(p->dev, "%s:%d invalid array mode %d\n",
- __func__, __LINE__, surf->mode);
+ dev_warn(p->dev, "%s:%d %s invalid array mode %d\n",
+ __func__, __LINE__, prefix, surf->mode);
return -EINVAL;
}
@@ -795,62 +785,77 @@ static int evergreen_cs_track_validate_texture(struct radeon_cs_parser *p,
static int evergreen_cs_track_check(struct radeon_cs_parser *p)
{
struct evergreen_cs_track *track = p->track;
- unsigned tmp, i, j;
+ unsigned tmp, i;
int r;
+ unsigned buffer_mask = 0;
/* check streamout */
- for (i = 0; i < 4; i++) {
- if (track->vgt_strmout_config & (1 << i)) {
- for (j = 0; j < 4; j++) {
- if ((track->vgt_strmout_buffer_config >> (i * 4)) & (1 << j)) {
- if (track->vgt_strmout_bo[j]) {
- u64 offset = (u64)track->vgt_strmout_bo_offset[j] +
- (u64)track->vgt_strmout_size[j];
- if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) {
- DRM_ERROR("streamout %d bo too small: 0x%llx, 0x%lx\n",
- j, offset,
- radeon_bo_size(track->vgt_strmout_bo[j]));
- return -EINVAL;
- }
- } else {
- dev_warn(p->dev, "No buffer for streamout %d\n", j);
+ if (track->streamout_dirty && track->vgt_strmout_config) {
+ for (i = 0; i < 4; i++) {
+ if (track->vgt_strmout_config & (1 << i)) {
+ buffer_mask |= (track->vgt_strmout_buffer_config >> (i * 4)) & 0xf;
+ }
+ }
+
+ for (i = 0; i < 4; i++) {
+ if (buffer_mask & (1 << i)) {
+ if (track->vgt_strmout_bo[i]) {
+ u64 offset = (u64)track->vgt_strmout_bo_offset[i] +
+ (u64)track->vgt_strmout_size[i];
+ if (offset > radeon_bo_size(track->vgt_strmout_bo[i])) {
+ DRM_ERROR("streamout %d bo too small: 0x%llx, 0x%lx\n",
+ i, offset,
+ radeon_bo_size(track->vgt_strmout_bo[i]));
return -EINVAL;
}
+ } else {
+ dev_warn(p->dev, "No buffer for streamout %d\n", i);
+ return -EINVAL;
}
}
}
+ track->streamout_dirty = false;
}
+ if (track->sx_misc_kill_all_prims)
+ return 0;
+
/* check that we have a cb for each enabled target
*/
- tmp = track->cb_target_mask;
- for (i = 0; i < 8; i++) {
- if ((tmp >> (i * 4)) & 0xF) {
- /* at least one component is enabled */
- if (track->cb_color_bo[i] == NULL) {
- dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
- __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
- return -EINVAL;
- }
- /* check cb */
- r = evergreen_cs_track_validate_cb(p, i);
- if (r) {
- return r;
+ if (track->cb_dirty) {
+ tmp = track->cb_target_mask;
+ for (i = 0; i < 8; i++) {
+ if ((tmp >> (i * 4)) & 0xF) {
+ /* at least one component is enabled */
+ if (track->cb_color_bo[i] == NULL) {
+ dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
+ __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
+ return -EINVAL;
+ }
+ /* check cb */
+ r = evergreen_cs_track_validate_cb(p, i);
+ if (r) {
+ return r;
+ }
}
}
+ track->cb_dirty = false;
}
- /* Check stencil buffer */
- if (G_028800_STENCIL_ENABLE(track->db_depth_control)) {
- r = evergreen_cs_track_validate_stencil(p);
- if (r)
- return r;
- }
- /* Check depth buffer */
- if (G_028800_Z_WRITE_ENABLE(track->db_depth_control)) {
- r = evergreen_cs_track_validate_depth(p);
- if (r)
- return r;
+ if (track->db_dirty) {
+ /* Check stencil buffer */
+ if (G_028800_STENCIL_ENABLE(track->db_depth_control)) {
+ r = evergreen_cs_track_validate_stencil(p);
+ if (r)
+ return r;
+ }
+ /* Check depth buffer */
+ if (G_028800_Z_WRITE_ENABLE(track->db_depth_control)) {
+ r = evergreen_cs_track_validate_depth(p);
+ if (r)
+ return r;
+ }
+ track->db_dirty = false;
}
return 0;
@@ -1184,6 +1189,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
break;
case DB_DEPTH_CONTROL:
track->db_depth_control = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case CAYMAN_DB_EQAA:
if (p->rdev->family < CHIP_CAYMAN) {
@@ -1225,19 +1231,23 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
DB_MACRO_TILE_ASPECT(mtaspect);
}
}
+ track->db_dirty = true;
break;
case DB_STENCIL_INFO:
track->db_s_info = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case DB_DEPTH_VIEW:
track->db_depth_view = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case DB_DEPTH_SIZE:
track->db_depth_size = radeon_get_ib_value(p, idx);
- track->db_depth_size_idx = idx;
+ track->db_dirty = true;
break;
case R_02805C_DB_DEPTH_SLICE:
track->db_depth_slice = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case DB_Z_READ_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -1249,6 +1259,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_z_read_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_z_read_bo = reloc->robj;
+ track->db_dirty = true;
break;
case DB_Z_WRITE_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -1260,6 +1271,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_z_write_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_z_write_bo = reloc->robj;
+ track->db_dirty = true;
break;
case DB_STENCIL_READ_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -1271,6 +1283,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_s_read_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_s_read_bo = reloc->robj;
+ track->db_dirty = true;
break;
case DB_STENCIL_WRITE_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -1282,12 +1295,15 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->db_s_write_offset = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_s_write_bo = reloc->robj;
+ track->db_dirty = true;
break;
case VGT_STRMOUT_CONFIG:
track->vgt_strmout_config = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_CONFIG:
track->vgt_strmout_buffer_config = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_BASE_0:
case VGT_STRMOUT_BUFFER_BASE_1:
@@ -1303,7 +1319,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->vgt_strmout_bo_offset[tmp] = radeon_get_ib_value(p, idx) << 8;
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->vgt_strmout_bo[tmp] = reloc->robj;
- track->vgt_strmout_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_SIZE_0:
case VGT_STRMOUT_BUFFER_SIZE_1:
@@ -1312,6 +1328,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16;
/* size in register is DWs, convert to bytes */
track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4;
+ track->streamout_dirty = true;
break;
case CP_COHER_BASE:
r = evergreen_cs_packet_next_reloc(p, &reloc);
@@ -1323,9 +1340,11 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
case CB_TARGET_MASK:
track->cb_target_mask = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case CB_SHADER_MASK:
track->cb_shader_mask = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case PA_SC_AA_CONFIG:
if (p->rdev->family >= CHIP_CAYMAN) {
@@ -1355,6 +1374,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR7_VIEW:
tmp = (reg - CB_COLOR0_VIEW) / 0x3c;
track->cb_color_view[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case CB_COLOR8_VIEW:
case CB_COLOR9_VIEW:
@@ -1362,6 +1382,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR11_VIEW:
tmp = ((reg - CB_COLOR8_VIEW) / 0x1c) + 8;
track->cb_color_view[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case CB_COLOR0_INFO:
case CB_COLOR1_INFO:
@@ -1383,6 +1404,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
}
+ track->cb_dirty = true;
break;
case CB_COLOR8_INFO:
case CB_COLOR9_INFO:
@@ -1400,6 +1422,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
track->cb_color_info[tmp] |= CB_ARRAY_MODE(evergreen_cs_get_aray_mode(reloc->lobj.tiling_flags));
}
+ track->cb_dirty = true;
break;
case CB_COLOR0_PITCH:
case CB_COLOR1_PITCH:
@@ -1411,7 +1434,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR7_PITCH:
tmp = (reg - CB_COLOR0_PITCH) / 0x3c;
track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_pitch_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR8_PITCH:
case CB_COLOR9_PITCH:
@@ -1419,7 +1442,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR11_PITCH:
tmp = ((reg - CB_COLOR8_PITCH) / 0x1c) + 8;
track->cb_color_pitch[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_pitch_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR0_SLICE:
case CB_COLOR1_SLICE:
@@ -1431,7 +1454,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR7_SLICE:
tmp = (reg - CB_COLOR0_SLICE) / 0x3c;
track->cb_color_slice[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_slice_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR8_SLICE:
case CB_COLOR9_SLICE:
@@ -1439,7 +1462,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case CB_COLOR11_SLICE:
tmp = ((reg - CB_COLOR8_SLICE) / 0x1c) + 8;
track->cb_color_slice[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_slice_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR0_ATTRIB:
case CB_COLOR1_ATTRIB:
@@ -1471,6 +1494,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = ((reg - CB_COLOR0_ATTRIB) / 0x3c);
track->cb_color_attrib[tmp] = ib[idx];
+ track->cb_dirty = true;
break;
case CB_COLOR8_ATTRIB:
case CB_COLOR9_ATTRIB:
@@ -1498,26 +1522,7 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
tmp = ((reg - CB_COLOR8_ATTRIB) / 0x1c) + 8;
track->cb_color_attrib[tmp] = ib[idx];
- break;
- case CB_COLOR0_DIM:
- case CB_COLOR1_DIM:
- case CB_COLOR2_DIM:
- case CB_COLOR3_DIM:
- case CB_COLOR4_DIM:
- case CB_COLOR5_DIM:
- case CB_COLOR6_DIM:
- case CB_COLOR7_DIM:
- tmp = (reg - CB_COLOR0_DIM) / 0x3c;
- track->cb_color_dim[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_dim_idx[tmp] = idx;
- break;
- case CB_COLOR8_DIM:
- case CB_COLOR9_DIM:
- case CB_COLOR10_DIM:
- case CB_COLOR11_DIM:
- tmp = ((reg - CB_COLOR8_DIM) / 0x1c) + 8;
- track->cb_color_dim[tmp] = radeon_get_ib_value(p, idx);
- track->cb_color_dim_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
case CB_COLOR0_FMASK:
case CB_COLOR1_FMASK:
@@ -1592,8 +1597,8 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - CB_COLOR0_BASE) / 0x3c;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
- track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
+ track->cb_dirty = true;
break;
case CB_COLOR8_BASE:
case CB_COLOR9_BASE:
@@ -1608,8 +1613,8 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = ((reg - CB_COLOR8_BASE) / 0x1c) + 8;
track->cb_color_bo_offset[tmp] = radeon_get_ib_value(p, idx);
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
- track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
+ track->cb_dirty = true;
break;
case CB_IMMED0_BASE:
case CB_IMMED1_BASE:
@@ -1748,6 +1753,9 @@ static int evergreen_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
+ case SX_MISC:
+ track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0;
+ break;
default:
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
@@ -1803,6 +1811,8 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
{
int pred_op;
int tmp;
+ uint64_t offset;
+
if (pkt->count != 1) {
DRM_ERROR("bad SET PREDICATION\n");
return -EINVAL;
@@ -1826,8 +1836,12 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- ib[idx + 0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx + 1] = tmp + (upper_32_bits(reloc->lobj.gpu_offset) & 0xff);
+ offset = reloc->lobj.gpu_offset +
+ (idx_value & 0xfffffff0) +
+ ((u64)(tmp & 0xff) << 32);
+
+ ib[idx + 0] = offset;
+ ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff);
}
break;
case PACKET3_CONTEXT_CONTROL:
@@ -1855,6 +1869,9 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
break;
case PACKET3_INDEX_BASE:
+ {
+ uint64_t offset;
+
if (pkt->count != 1) {
DRM_ERROR("bad INDEX_BASE\n");
return -EINVAL;
@@ -1864,15 +1881,24 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad INDEX_BASE\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ idx_value +
+ ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
+
+ ib[idx+0] = offset;
+ ib[idx+1] = upper_32_bits(offset) & 0xff;
+
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX:
+ {
+ uint64_t offset;
if (pkt->count != 3) {
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
@@ -1882,15 +1908,25 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ idx_value +
+ ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
+
+ ib[idx+0] = offset;
+ ib[idx+1] = upper_32_bits(offset) & 0xff;
+
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX_2:
+ {
+ uint64_t offset;
+
if (pkt->count != 4) {
DRM_ERROR("bad DRAW_INDEX_2\n");
return -EINVAL;
@@ -1900,14 +1936,21 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DRAW_INDEX_2\n");
return -EINVAL;
}
- ib[idx+1] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ radeon_get_ib_value(p, idx+1) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
+
r = evergreen_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX_AUTO:
if (pkt->count != 1) {
DRM_ERROR("bad DRAW_INDEX_AUTO\n");
@@ -1998,13 +2041,20 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
}
/* bit 4 is reg (0) or mem (1) */
if (idx_value & 0x10) {
+ uint64_t offset;
+
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad WAIT_REG_MEM\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffffc);
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_SURFACE_SYNC:
@@ -2029,16 +2079,25 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
if (pkt->count) {
+ uint64_t offset;
+
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffff8;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_EVENT_WRITE_EOP:
+ {
+ uint64_t offset;
+
if (pkt->count != 4) {
DRM_ERROR("bad EVENT_WRITE_EOP\n");
return -EINVAL;
@@ -2048,10 +2107,19 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE_EOP\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffffc;
+ ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
break;
+ }
case PACKET3_EVENT_WRITE_EOS:
+ {
+ uint64_t offset;
+
if (pkt->count != 3) {
DRM_ERROR("bad EVENT_WRITE_EOS\n");
return -EINVAL;
@@ -2061,9 +2129,15 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE_EOS\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffffc;
+ ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
break;
+ }
case PACKET3_SET_CONFIG_REG:
start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_START;
end_reg = 4 * pkt->count + start_reg - 4;
@@ -2156,6 +2230,8 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
ib[idx+1+(i*8)+3] += moffset;
break;
case SQ_TEX_VTX_VALID_BUFFER:
+ {
+ uint64_t offset64;
/* vtx base */
r = evergreen_cs_packet_next_reloc(p, &reloc);
if (r) {
@@ -2167,11 +2243,15 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
if (p->rdev && (size + offset) > radeon_bo_size(reloc->robj)) {
/* force size to size of the buffer */
dev_warn(p->dev, "vbo resource seems too big for the bo\n");
- ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj);
+ ib[idx+1+(i*8)+1] = radeon_bo_size(reloc->robj) - offset;
}
- ib[idx+1+(i*8)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
- ib[idx+1+(i*8)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset64 = reloc->lobj.gpu_offset + offset;
+ ib[idx+1+(i*8)+0] = offset64;
+ ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
+ (upper_32_bits(offset64) & 0xff);
break;
+ }
case SQ_TEX_VTX_INVALID_TEXTURE:
case SQ_TEX_VTX_INVALID_BUFFER:
default:
@@ -2247,8 +2327,9 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
/* Reading data from SRC_ADDRESS. */
if (((idx_value >> 1) & 0x3) == 2) {
@@ -2265,8 +2346,9 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+3] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+4] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_COPY_DW:
@@ -2289,8 +2371,9 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
} else {
/* SRC is a reg. */
reg = radeon_get_ib_value(p, idx+1) << 2;
@@ -2312,8 +2395,9 @@ static int evergreen_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+3] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+4] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
} else {
/* DST is a reg. */
reg = radeon_get_ib_value(p, idx+3) << 2;
diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c
index 8ce7f9973a5c..160799c14b91 100644
--- a/drivers/gpu/drm/radeon/ni.c
+++ b/drivers/gpu/drm/radeon/ni.c
@@ -1547,6 +1547,7 @@ int cayman_resume(struct radeon_device *rdev)
r = cayman_startup(rdev);
if (r) {
DRM_ERROR("cayman startup failed on resume\n");
+ rdev->accel_working = false;
return r;
}
return r;
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index cee3c8a28dbd..81801c176aa5 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -827,9 +827,7 @@ int r100_irq_process(struct radeon_device *rdev)
WREG32(RADEON_AIC_CNTL, msi_rearm | RS400_MSI_REARM);
break;
default:
- msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN;
- WREG32(RADEON_MSI_REARM_EN, msi_rearm);
- WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN);
+ WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN);
break;
}
}
@@ -3979,6 +3977,8 @@ static int r100_startup(struct radeon_device *rdev)
int r100_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
if (rdev->flags & RADEON_IS_PCI)
r100_pci_gart_disable(rdev);
@@ -3998,7 +3998,11 @@ int r100_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return r100_startup(rdev);
+ r = r100_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int r100_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
index 9b36e581b18d..fa14383f9ca0 100644
--- a/drivers/gpu/drm/radeon/r300.c
+++ b/drivers/gpu/drm/radeon/r300.c
@@ -1430,6 +1430,8 @@ static int r300_startup(struct radeon_device *rdev)
int r300_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -1451,7 +1453,11 @@ int r300_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return r300_startup(rdev);
+ r = r300_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int r300_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
index d0f2c0363c27..f3fcaacfea01 100644
--- a/drivers/gpu/drm/radeon/r420.c
+++ b/drivers/gpu/drm/radeon/r420.c
@@ -291,6 +291,8 @@ static int r420_startup(struct radeon_device *rdev)
int r420_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -316,7 +318,11 @@ int r420_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return r420_startup(rdev);
+ r = r420_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int r420_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
index 422923ce70b6..ebcc15b03c9f 100644
--- a/drivers/gpu/drm/radeon/r520.c
+++ b/drivers/gpu/drm/radeon/r520.c
@@ -218,6 +218,8 @@ static int r520_startup(struct radeon_device *rdev)
int r520_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -237,7 +239,11 @@ int r520_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return r520_startup(rdev);
+ r = r520_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int r520_init(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
index 8a6d68c028d3..5eb23829353f 100644
--- a/drivers/gpu/drm/radeon/r600.c
+++ b/drivers/gpu/drm/radeon/r600.c
@@ -2362,6 +2362,9 @@ void r600_semaphore_ring_emit(struct radeon_device *rdev,
uint64_t addr = semaphore->gpu_addr;
unsigned sel = emit_wait ? PACKET3_SEM_SEL_WAIT : PACKET3_SEM_SEL_SIGNAL;
+ if (rdev->family < CHIP_CAYMAN)
+ sel |= PACKET3_SEM_WAIT_ON_SIGNAL;
+
radeon_ring_write(ring, PACKET3(PACKET3_MEM_SEMAPHORE, 1));
radeon_ring_write(ring, addr & 0xffffffff);
radeon_ring_write(ring, (upper_32_bits(addr) & 0xff) | sel);
@@ -2529,6 +2532,7 @@ int r600_resume(struct radeon_device *rdev)
r = r600_startup(rdev);
if (r) {
DRM_ERROR("r600 startup failed on resume\n");
+ rdev->accel_working = false;
return r;
}
diff --git a/drivers/gpu/drm/radeon/r600_blit_shaders.c b/drivers/gpu/drm/radeon/r600_blit_shaders.c
index 2d1f6c5ee2a7..73e2c7c6edbc 100644
--- a/drivers/gpu/drm/radeon/r600_blit_shaders.c
+++ b/drivers/gpu/drm/radeon/r600_blit_shaders.c
@@ -314,6 +314,10 @@ const u32 r6xx_default_state[] =
0x00000000, /* VGT_VTX_CNT_EN */
0xc0016900,
+ 0x000000d4,
+ 0x00000000, /* SX_MISC */
+
+ 0xc0016900,
0x000002c8,
0x00000000, /* VGT_STRMOUT_BUFFER_EN */
@@ -626,6 +630,10 @@ const u32 r7xx_default_state[] =
0x00000000, /* VGT_VTX_CNT_EN */
0xc0016900,
+ 0x000000d4,
+ 0x00000000, /* SX_MISC */
+
+ 0xc0016900,
0x000002c8,
0x00000000, /* VGT_STRMOUT_BUFFER_EN */
diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
index 5cbe948ef16e..0ec3f205f9c4 100644
--- a/drivers/gpu/drm/radeon/r600_cs.c
+++ b/drivers/gpu/drm/radeon/r600_cs.c
@@ -52,18 +52,18 @@ struct r600_cs_track {
struct radeon_bo *cb_color_bo[8];
u64 cb_color_bo_mc[8];
u32 cb_color_bo_offset[8];
- struct radeon_bo *cb_color_frag_bo[8];
- struct radeon_bo *cb_color_tile_bo[8];
+ struct radeon_bo *cb_color_frag_bo[8]; /* unused */
+ struct radeon_bo *cb_color_tile_bo[8]; /* unused */
u32 cb_color_info[8];
u32 cb_color_view[8];
- u32 cb_color_size_idx[8];
+ u32 cb_color_size_idx[8]; /* unused */
u32 cb_target_mask;
- u32 cb_shader_mask;
+ u32 cb_shader_mask; /* unused */
u32 cb_color_size[8];
u32 vgt_strmout_en;
u32 vgt_strmout_buffer_en;
struct radeon_bo *vgt_strmout_bo[4];
- u64 vgt_strmout_bo_mc[4];
+ u64 vgt_strmout_bo_mc[4]; /* unused */
u32 vgt_strmout_bo_offset[4];
u32 vgt_strmout_size[4];
u32 db_depth_control;
@@ -74,6 +74,10 @@ struct r600_cs_track {
u32 db_offset;
struct radeon_bo *db_bo;
u64 db_bo_mc;
+ bool sx_misc_kill_all_prims;
+ bool cb_dirty;
+ bool db_dirty;
+ bool streamout_dirty;
};
#define FMT_8_BIT(fmt, vc) [fmt] = { 1, 1, 1, vc, CHIP_R600 }
@@ -307,6 +311,7 @@ static void r600_cs_track_init(struct r600_cs_track *track)
}
track->cb_target_mask = 0xFFFFFFFF;
track->cb_shader_mask = 0xFFFFFFFF;
+ track->cb_dirty = true;
track->db_bo = NULL;
track->db_bo_mc = 0xFFFFFFFF;
/* assume the biggest format and that htile is enabled */
@@ -315,6 +320,7 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->db_depth_size = 0xFFFFFFFF;
track->db_depth_size_idx = 0;
track->db_depth_control = 0xFFFFFFFF;
+ track->db_dirty = true;
for (i = 0; i < 4; i++) {
track->vgt_strmout_size[i] = 0;
@@ -322,6 +328,8 @@ static void r600_cs_track_init(struct r600_cs_track *track)
track->vgt_strmout_bo_offset[i] = 0xFFFFFFFF;
track->vgt_strmout_bo_mc[i] = 0xFFFFFFFF;
}
+ track->streamout_dirty = true;
+ track->sx_misc_kill_all_prims = false;
}
static int r600_cs_track_validate_cb(struct radeon_cs_parser *p, int i)
@@ -459,7 +467,7 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
return 0;
/* check streamout */
- if (track->vgt_strmout_en) {
+ if (track->streamout_dirty && track->vgt_strmout_en) {
for (i = 0; i < 4; i++) {
if (track->vgt_strmout_buffer_en & (1 << i)) {
if (track->vgt_strmout_bo[i]) {
@@ -477,140 +485,151 @@ static int r600_cs_track_check(struct radeon_cs_parser *p)
}
}
}
+ track->streamout_dirty = false;
}
+ if (track->sx_misc_kill_all_prims)
+ return 0;
+
/* check that we have a cb for each enabled target, we don't check
* shader_mask because it seems mesa isn't always setting it :(
*/
- tmp = track->cb_target_mask;
- for (i = 0; i < 8; i++) {
- if ((tmp >> (i * 4)) & 0xF) {
- /* at least one component is enabled */
- if (track->cb_color_bo[i] == NULL) {
- dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
- __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
- return -EINVAL;
+ if (track->cb_dirty) {
+ tmp = track->cb_target_mask;
+ for (i = 0; i < 8; i++) {
+ if ((tmp >> (i * 4)) & 0xF) {
+ /* at least one component is enabled */
+ if (track->cb_color_bo[i] == NULL) {
+ dev_warn(p->dev, "%s:%d mask 0x%08X | 0x%08X no cb for %d\n",
+ __func__, __LINE__, track->cb_target_mask, track->cb_shader_mask, i);
+ return -EINVAL;
+ }
+ /* perform rewrite of CB_COLOR[0-7]_SIZE */
+ r = r600_cs_track_validate_cb(p, i);
+ if (r)
+ return r;
}
- /* perform rewrite of CB_COLOR[0-7]_SIZE */
- r = r600_cs_track_validate_cb(p, i);
- if (r)
- return r;
}
+ track->cb_dirty = false;
}
- /* Check depth buffer */
- if (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
- G_028800_Z_ENABLE(track->db_depth_control)) {
- u32 nviews, bpe, ntiles, size, slice_tile_max;
- u32 height, height_align, pitch, pitch_align, depth_align;
- u64 base_offset, base_align;
- struct array_mode_checker array_check;
- int array_mode;
-
- if (track->db_bo == NULL) {
- dev_warn(p->dev, "z/stencil with no depth buffer\n");
- return -EINVAL;
- }
- if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) {
- dev_warn(p->dev, "this kernel doesn't support z/stencil htile\n");
- return -EINVAL;
- }
- switch (G_028010_FORMAT(track->db_depth_info)) {
- case V_028010_DEPTH_16:
- bpe = 2;
- break;
- case V_028010_DEPTH_X8_24:
- case V_028010_DEPTH_8_24:
- case V_028010_DEPTH_X8_24_FLOAT:
- case V_028010_DEPTH_8_24_FLOAT:
- case V_028010_DEPTH_32_FLOAT:
- bpe = 4;
- break;
- case V_028010_DEPTH_X24_8_32_FLOAT:
- bpe = 8;
- break;
- default:
- dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info));
- return -EINVAL;
- }
- if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) {
- if (!track->db_depth_size_idx) {
- dev_warn(p->dev, "z/stencil buffer size not set\n");
+
+ if (track->db_dirty) {
+ /* Check depth buffer */
+ if (G_028800_STENCIL_ENABLE(track->db_depth_control) ||
+ G_028800_Z_ENABLE(track->db_depth_control)) {
+ u32 nviews, bpe, ntiles, size, slice_tile_max;
+ u32 height, height_align, pitch, pitch_align, depth_align;
+ u64 base_offset, base_align;
+ struct array_mode_checker array_check;
+ int array_mode;
+
+ if (track->db_bo == NULL) {
+ dev_warn(p->dev, "z/stencil with no depth buffer\n");
return -EINVAL;
}
- tmp = radeon_bo_size(track->db_bo) - track->db_offset;
- tmp = (tmp / bpe) >> 6;
- if (!tmp) {
- dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %ld)\n",
- track->db_depth_size, bpe, track->db_offset,
- radeon_bo_size(track->db_bo));
+ if (G_028010_TILE_SURFACE_ENABLE(track->db_depth_info)) {
+ dev_warn(p->dev, "this kernel doesn't support z/stencil htile\n");
return -EINVAL;
}
- ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF);
- } else {
- size = radeon_bo_size(track->db_bo);
- /* pitch in pixels */
- pitch = (G_028000_PITCH_TILE_MAX(track->db_depth_size) + 1) * 8;
- slice_tile_max = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
- slice_tile_max *= 64;
- height = slice_tile_max / pitch;
- if (height > 8192)
- height = 8192;
- base_offset = track->db_bo_mc + track->db_offset;
- array_mode = G_028010_ARRAY_MODE(track->db_depth_info);
- array_check.array_mode = array_mode;
- array_check.group_size = track->group_size;
- array_check.nbanks = track->nbanks;
- array_check.npipes = track->npipes;
- array_check.nsamples = track->nsamples;
- array_check.blocksize = bpe;
- if (r600_get_array_mode_alignment(&array_check,
- &pitch_align, &height_align, &depth_align, &base_align)) {
- dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
- G_028010_ARRAY_MODE(track->db_depth_info),
- track->db_depth_info);
- return -EINVAL;
- }
- switch (array_mode) {
- case V_028010_ARRAY_1D_TILED_THIN1:
- /* don't break userspace */
- height &= ~0x7;
+ switch (G_028010_FORMAT(track->db_depth_info)) {
+ case V_028010_DEPTH_16:
+ bpe = 2;
+ break;
+ case V_028010_DEPTH_X8_24:
+ case V_028010_DEPTH_8_24:
+ case V_028010_DEPTH_X8_24_FLOAT:
+ case V_028010_DEPTH_8_24_FLOAT:
+ case V_028010_DEPTH_32_FLOAT:
+ bpe = 4;
break;
- case V_028010_ARRAY_2D_TILED_THIN1:
+ case V_028010_DEPTH_X24_8_32_FLOAT:
+ bpe = 8;
break;
default:
- dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
- G_028010_ARRAY_MODE(track->db_depth_info),
- track->db_depth_info);
+ dev_warn(p->dev, "z/stencil with invalid format %d\n", G_028010_FORMAT(track->db_depth_info));
return -EINVAL;
}
+ if ((track->db_depth_size & 0xFFFFFC00) == 0xFFFFFC00) {
+ if (!track->db_depth_size_idx) {
+ dev_warn(p->dev, "z/stencil buffer size not set\n");
+ return -EINVAL;
+ }
+ tmp = radeon_bo_size(track->db_bo) - track->db_offset;
+ tmp = (tmp / bpe) >> 6;
+ if (!tmp) {
+ dev_warn(p->dev, "z/stencil buffer too small (0x%08X %d %d %ld)\n",
+ track->db_depth_size, bpe, track->db_offset,
+ radeon_bo_size(track->db_bo));
+ return -EINVAL;
+ }
+ ib[track->db_depth_size_idx] = S_028000_SLICE_TILE_MAX(tmp - 1) | (track->db_depth_size & 0x3FF);
+ } else {
+ size = radeon_bo_size(track->db_bo);
+ /* pitch in pixels */
+ pitch = (G_028000_PITCH_TILE_MAX(track->db_depth_size) + 1) * 8;
+ slice_tile_max = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
+ slice_tile_max *= 64;
+ height = slice_tile_max / pitch;
+ if (height > 8192)
+ height = 8192;
+ base_offset = track->db_bo_mc + track->db_offset;
+ array_mode = G_028010_ARRAY_MODE(track->db_depth_info);
+ array_check.array_mode = array_mode;
+ array_check.group_size = track->group_size;
+ array_check.nbanks = track->nbanks;
+ array_check.npipes = track->npipes;
+ array_check.nsamples = track->nsamples;
+ array_check.blocksize = bpe;
+ if (r600_get_array_mode_alignment(&array_check,
+ &pitch_align, &height_align, &depth_align, &base_align)) {
+ dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
+ G_028010_ARRAY_MODE(track->db_depth_info),
+ track->db_depth_info);
+ return -EINVAL;
+ }
+ switch (array_mode) {
+ case V_028010_ARRAY_1D_TILED_THIN1:
+ /* don't break userspace */
+ height &= ~0x7;
+ break;
+ case V_028010_ARRAY_2D_TILED_THIN1:
+ break;
+ default:
+ dev_warn(p->dev, "%s invalid tiling %d (0x%08X)\n", __func__,
+ G_028010_ARRAY_MODE(track->db_depth_info),
+ track->db_depth_info);
+ return -EINVAL;
+ }
- if (!IS_ALIGNED(pitch, pitch_align)) {
- dev_warn(p->dev, "%s:%d db pitch (%d, 0x%x, %d) invalid\n",
- __func__, __LINE__, pitch, pitch_align, array_mode);
- return -EINVAL;
- }
- if (!IS_ALIGNED(height, height_align)) {
- dev_warn(p->dev, "%s:%d db height (%d, 0x%x, %d) invalid\n",
- __func__, __LINE__, height, height_align, array_mode);
- return -EINVAL;
- }
- if (!IS_ALIGNED(base_offset, base_align)) {
- dev_warn(p->dev, "%s offset[%d] 0x%llx, 0x%llx, %d not aligned\n", __func__, i,
- base_offset, base_align, array_mode);
- return -EINVAL;
- }
+ if (!IS_ALIGNED(pitch, pitch_align)) {
+ dev_warn(p->dev, "%s:%d db pitch (%d, 0x%x, %d) invalid\n",
+ __func__, __LINE__, pitch, pitch_align, array_mode);
+ return -EINVAL;
+ }
+ if (!IS_ALIGNED(height, height_align)) {
+ dev_warn(p->dev, "%s:%d db height (%d, 0x%x, %d) invalid\n",
+ __func__, __LINE__, height, height_align, array_mode);
+ return -EINVAL;
+ }
+ if (!IS_ALIGNED(base_offset, base_align)) {
+ dev_warn(p->dev, "%s offset[%d] 0x%llx, 0x%llx, %d not aligned\n", __func__, i,
+ base_offset, base_align, array_mode);
+ return -EINVAL;
+ }
- ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
- nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
- tmp = ntiles * bpe * 64 * nviews;
- if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
- dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n",
- array_mode,
- track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset,
- radeon_bo_size(track->db_bo));
- return -EINVAL;
+ ntiles = G_028000_SLICE_TILE_MAX(track->db_depth_size) + 1;
+ nviews = G_028004_SLICE_MAX(track->db_depth_view) + 1;
+ tmp = ntiles * bpe * 64 * nviews;
+ if ((tmp + track->db_offset) > radeon_bo_size(track->db_bo)) {
+ dev_warn(p->dev, "z/stencil buffer (%d) too small (0x%08X %d %d %d -> %u have %lu)\n",
+ array_mode,
+ track->db_depth_size, ntiles, nviews, bpe, tmp + track->db_offset,
+ radeon_bo_size(track->db_bo));
+ return -EINVAL;
+ }
}
}
+ track->db_dirty = false;
}
return 0;
}
@@ -983,6 +1002,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
break;
case R_028800_DB_DEPTH_CONTROL:
track->db_depth_control = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case R_028010_DB_DEPTH_INFO:
if (!(p->cs_flags & RADEON_CS_KEEP_TILING_FLAGS) &&
@@ -1003,21 +1023,27 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1);
track->db_depth_info |= S_028010_ARRAY_MODE(V_028010_ARRAY_1D_TILED_THIN1);
}
- } else
+ } else {
track->db_depth_info = radeon_get_ib_value(p, idx);
+ }
+ track->db_dirty = true;
break;
case R_028004_DB_DEPTH_VIEW:
track->db_depth_view = radeon_get_ib_value(p, idx);
+ track->db_dirty = true;
break;
case R_028000_DB_DEPTH_SIZE:
track->db_depth_size = radeon_get_ib_value(p, idx);
track->db_depth_size_idx = idx;
+ track->db_dirty = true;
break;
case R_028AB0_VGT_STRMOUT_EN:
track->vgt_strmout_en = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
case R_028B20_VGT_STRMOUT_BUFFER_EN:
track->vgt_strmout_buffer_en = radeon_get_ib_value(p, idx);
+ track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_BASE_0:
case VGT_STRMOUT_BUFFER_BASE_1:
@@ -1034,6 +1060,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->vgt_strmout_bo[tmp] = reloc->robj;
track->vgt_strmout_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->streamout_dirty = true;
break;
case VGT_STRMOUT_BUFFER_SIZE_0:
case VGT_STRMOUT_BUFFER_SIZE_1:
@@ -1042,6 +1069,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - VGT_STRMOUT_BUFFER_SIZE_0) / 16;
/* size in register is DWs, convert to bytes */
track->vgt_strmout_size[tmp] = radeon_get_ib_value(p, idx) * 4;
+ track->streamout_dirty = true;
break;
case CP_COHER_BASE:
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -1054,6 +1082,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
break;
case R_028238_CB_TARGET_MASK:
track->cb_target_mask = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case R_02823C_CB_SHADER_MASK:
track->cb_shader_mask = radeon_get_ib_value(p, idx);
@@ -1061,6 +1090,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case R_028C04_PA_SC_AA_CONFIG:
tmp = G_028C04_MSAA_NUM_SAMPLES(radeon_get_ib_value(p, idx));
track->nsamples = 1 << tmp;
+ track->cb_dirty = true;
break;
case R_0280A0_CB_COLOR0_INFO:
case R_0280A4_CB_COLOR1_INFO:
@@ -1090,6 +1120,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - R_0280A0_CB_COLOR0_INFO) / 4;
track->cb_color_info[tmp] = radeon_get_ib_value(p, idx);
}
+ track->cb_dirty = true;
break;
case R_028080_CB_COLOR0_VIEW:
case R_028084_CB_COLOR1_VIEW:
@@ -1101,6 +1132,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
case R_02809C_CB_COLOR7_VIEW:
tmp = (reg - R_028080_CB_COLOR0_VIEW) / 4;
track->cb_color_view[tmp] = radeon_get_ib_value(p, idx);
+ track->cb_dirty = true;
break;
case R_028060_CB_COLOR0_SIZE:
case R_028064_CB_COLOR1_SIZE:
@@ -1113,6 +1145,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
tmp = (reg - R_028060_CB_COLOR0_SIZE) / 4;
track->cb_color_size[tmp] = radeon_get_ib_value(p, idx);
track->cb_color_size_idx[tmp] = idx;
+ track->cb_dirty = true;
break;
/* This register were added late, there is userspace
* which does provide relocation for those but set
@@ -1195,6 +1228,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
track->cb_color_base_last[tmp] = ib[idx];
track->cb_color_bo[tmp] = reloc->robj;
track->cb_color_bo_mc[tmp] = reloc->lobj.gpu_offset;
+ track->cb_dirty = true;
break;
case DB_DEPTH_BASE:
r = r600_cs_packet_next_reloc(p, &reloc);
@@ -1207,6 +1241,7 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
track->db_bo = reloc->robj;
track->db_bo_mc = reloc->lobj.gpu_offset;
+ track->db_dirty = true;
break;
case DB_HTILE_DATA_BASE:
case SQ_PGM_START_FS:
@@ -1279,6 +1314,9 @@ static int r600_cs_check_reg(struct radeon_cs_parser *p, u32 reg, u32 idx)
}
ib[idx] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
break;
+ case SX_MISC:
+ track->sx_misc_kill_all_prims = (radeon_get_ib_value(p, idx) & 0x1) != 0;
+ break;
default:
dev_warn(p->dev, "forbidden register 0x%08x at %d\n", reg, idx);
return -EINVAL;
@@ -1392,6 +1430,7 @@ static int r600_check_texture_resource(struct radeon_cs_parser *p, u32 idx,
h0 = G_038004_TEX_HEIGHT(word1) + 1;
d0 = G_038004_TEX_DEPTH(word1);
nfaces = 1;
+ array = 0;
switch (G_038000_DIM(word0)) {
case V_038000_SQ_TEX_DIM_1D:
case V_038000_SQ_TEX_DIM_2D:
@@ -1529,6 +1568,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
{
int pred_op;
int tmp;
+ uint64_t offset;
+
if (pkt->count != 1) {
DRM_ERROR("bad SET PREDICATION\n");
return -EINVAL;
@@ -1552,8 +1593,12 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
- ib[idx + 0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx + 1] = tmp + (upper_32_bits(reloc->lobj.gpu_offset) & 0xff);
+ offset = reloc->lobj.gpu_offset +
+ (idx_value & 0xfffffff0) +
+ ((u64)(tmp & 0xff) << 32);
+
+ ib[idx + 0] = offset;
+ ib[idx + 1] = (tmp & 0xffffff00) | (upper_32_bits(offset) & 0xff);
}
break;
@@ -1577,6 +1622,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
}
break;
case PACKET3_DRAW_INDEX:
+ {
+ uint64_t offset;
if (pkt->count != 3) {
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
@@ -1586,14 +1633,21 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad DRAW_INDEX\n");
return -EINVAL;
}
- ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ idx_value +
+ ((u64)(radeon_get_ib_value(p, idx+1) & 0xff) << 32);
+
+ ib[idx+0] = offset;
+ ib[idx+1] = upper_32_bits(offset) & 0xff;
+
r = r600_cs_track_check(p);
if (r) {
dev_warn(p->dev, "%s:%d invalid cmd stream\n", __func__, __LINE__);
return r;
}
break;
+ }
case PACKET3_DRAW_INDEX_AUTO:
if (pkt->count != 1) {
DRM_ERROR("bad DRAW_INDEX_AUTO\n");
@@ -1624,13 +1678,20 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
}
/* bit 4 is reg (0) or mem (1) */
if (idx_value & 0x10) {
+ uint64_t offset;
+
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad WAIT_REG_MEM\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffff0) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = (ib[idx+1] & 0x3) | (offset & 0xfffffff0);
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_SURFACE_SYNC:
@@ -1655,16 +1716,25 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
return -EINVAL;
}
if (pkt->count) {
+ uint64_t offset;
+
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffff8) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffff8;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_EVENT_WRITE_EOP:
+ {
+ uint64_t offset;
+
if (pkt->count != 4) {
DRM_ERROR("bad EVENT_WRITE_EOP\n");
return -EINVAL;
@@ -1674,9 +1744,15 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
DRM_ERROR("bad EVENT_WRITE\n");
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset = reloc->lobj.gpu_offset +
+ (radeon_get_ib_value(p, idx+1) & 0xfffffffc) +
+ ((u64)(radeon_get_ib_value(p, idx+2) & 0xff) << 32);
+
+ ib[idx+1] = offset & 0xfffffffc;
+ ib[idx+2] = (ib[idx+2] & 0xffffff00) | (upper_32_bits(offset) & 0xff);
break;
+ }
case PACKET3_SET_CONFIG_REG:
start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_OFFSET;
end_reg = 4 * pkt->count + start_reg - 4;
@@ -1761,6 +1837,8 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
ib[idx+1+(i*7)+3] += mip_offset;
break;
case SQ_TEX_VTX_VALID_BUFFER:
+ {
+ uint64_t offset64;
/* vtx base */
r = r600_cs_packet_next_reloc(p, &reloc);
if (r) {
@@ -1773,11 +1851,15 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
/* force size to size of the buffer */
dev_warn(p->dev, "vbo resource seems too big (%d) for the bo (%ld)\n",
size + offset, radeon_bo_size(reloc->robj));
- ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj);
+ ib[idx+1+(i*7)+1] = radeon_bo_size(reloc->robj) - offset;
}
- ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
- ib[idx+1+(i*7)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+
+ offset64 = reloc->lobj.gpu_offset + offset;
+ ib[idx+1+(i*8)+0] = offset64;
+ ib[idx+1+(i*8)+2] = (ib[idx+1+(i*8)+2] & 0xffffff00) |
+ (upper_32_bits(offset64) & 0xff);
break;
+ }
case SQ_TEX_VTX_INVALID_TEXTURE:
case SQ_TEX_VTX_INVALID_BUFFER:
default:
@@ -1872,8 +1954,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
}
/* Reading data from SRC_ADDRESS. */
if (((idx_value >> 1) & 0x3) == 2) {
@@ -1890,8 +1973,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+3] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+4] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
}
break;
case PACKET3_COPY_DW:
@@ -1914,8 +1998,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+1] = offset;
+ ib[idx+2] = upper_32_bits(offset) & 0xff;
} else {
/* SRC is a reg. */
reg = radeon_get_ib_value(p, idx+1) << 2;
@@ -1937,8 +2022,9 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
offset + 4, radeon_bo_size(reloc->robj));
return -EINVAL;
}
- ib[idx+3] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
- ib[idx+4] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+ offset += reloc->lobj.gpu_offset;
+ ib[idx+3] = offset;
+ ib[idx+4] = upper_32_bits(offset) & 0xff;
} else {
/* DST is a reg. */
reg = radeon_get_ib_value(p, idx+3) << 2;
diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
index 2ba460b5b62f..8ae328ff5fdd 100644
--- a/drivers/gpu/drm/radeon/r600d.h
+++ b/drivers/gpu/drm/radeon/r600d.h
@@ -850,6 +850,7 @@
#define PACKET3_STRMOUT_BUFFER_UPDATE 0x34
#define PACKET3_INDIRECT_BUFFER_MP 0x38
#define PACKET3_MEM_SEMAPHORE 0x39
+# define PACKET3_SEM_WAIT_ON_SIGNAL (0x1 << 12)
# define PACKET3_SEM_SEL_SIGNAL (0x6 << 29)
# define PACKET3_SEM_SEL_WAIT (0x7 << 29)
#define PACKET3_MPEG_INDEX 0x3A
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 6923fa14308e..d2870a014ec0 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -414,9 +414,6 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
int alignment, int initial_domain,
bool discardable, bool kernel,
struct drm_gem_object **obj);
-int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
- uint64_t *gpu_addr);
-void radeon_gem_object_unpin(struct drm_gem_object *obj);
int radeon_mode_dumb_create(struct drm_file *file_priv,
struct drm_device *dev,
diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
index 5082d17d14dc..73541373bf56 100644
--- a/drivers/gpu/drm/radeon/radeon_atombios.c
+++ b/drivers/gpu/drm/radeon/radeon_atombios.c
@@ -442,6 +442,20 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
struct radeon_device *rdev = dev->dev_private;
*i2c_bus = radeon_lookup_i2c_gpio(rdev, 0x93);
}
+
+ /* Fujitsu D3003-S2 board lists DVI-I as DVI-D and VGA */
+ if ((dev->pdev->device == 0x9802) &&
+ (dev->pdev->subsystem_vendor == 0x1734) &&
+ (dev->pdev->subsystem_device == 0x11bd)) {
+ if (*connector_type == DRM_MODE_CONNECTOR_VGA) {
+ *connector_type = DRM_MODE_CONNECTOR_DVII;
+ *line_mux = 0x3103;
+ } else if (*connector_type == DRM_MODE_CONNECTOR_DVID) {
+ *connector_type = DRM_MODE_CONNECTOR_DVII;
+ }
+ }
+
+
return true;
}
@@ -2931,6 +2945,20 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP5;
}
}
+ if ((radeon_encoder->devices & ATOM_DEVICE_DFP6_SUPPORT) &&
+ (radeon_connector->devices & ATOM_DEVICE_DFP6_SUPPORT)) {
+ if (connected) {
+ DRM_DEBUG_KMS("DFP6 connected\n");
+ bios_0_scratch |= ATOM_S0_DFP6;
+ bios_3_scratch |= ATOM_S3_DFP6_ACTIVE;
+ bios_6_scratch |= ATOM_S6_ACC_REQ_DFP6;
+ } else {
+ DRM_DEBUG_KMS("DFP6 disconnected\n");
+ bios_0_scratch &= ~ATOM_S0_DFP6;
+ bios_3_scratch &= ~ATOM_S3_DFP6_ACTIVE;
+ bios_6_scratch &= ~ATOM_S6_ACC_REQ_DFP6;
+ }
+ }
if (rdev->family >= CHIP_R600) {
WREG32(R600_BIOS_0_SCRATCH, bios_0_scratch);
@@ -2951,6 +2979,9 @@ radeon_atombios_encoder_crtc_scratch_regs(struct drm_encoder *encoder, int crtc)
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
uint32_t bios_3_scratch;
+ if (ASIC_IS_DCE4(rdev))
+ return;
+
if (rdev->family >= CHIP_R600)
bios_3_scratch = RREG32(R600_BIOS_3_SCRATCH);
else
@@ -3003,6 +3034,9 @@ radeon_atombios_encoder_dpms_scratch_regs(struct drm_encoder *encoder, bool on)
struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
uint32_t bios_2_scratch;
+ if (ASIC_IS_DCE4(rdev))
+ return;
+
if (rdev->family >= CHIP_R600)
bios_2_scratch = RREG32(R600_BIOS_2_SCRATCH);
else
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index e7cb3ab09243..64774ac94449 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -827,6 +827,27 @@ static int radeon_dvi_get_modes(struct drm_connector *connector)
return ret;
}
+static bool radeon_check_hpd_status_unchanged(struct drm_connector *connector)
+{
+ struct drm_device *dev = connector->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ enum drm_connector_status status;
+
+ /* We only trust HPD on R600 and newer ASICS. */
+ if (rdev->family >= CHIP_R600
+ && radeon_connector->hpd.hpd != RADEON_HPD_NONE) {
+ if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
+ status = connector_status_connected;
+ else
+ status = connector_status_disconnected;
+ if (connector->status == status)
+ return true;
+ }
+
+ return false;
+}
+
/*
* DVI is complicated
* Do a DDC probe, if DDC probe passes, get the full EDID so
@@ -851,6 +872,9 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
enum drm_connector_status ret = connector_status_disconnected;
bool dret = false;
+ if (!force && radeon_check_hpd_status_unchanged(connector))
+ return connector->status;
+
if (radeon_connector->ddc_bus)
dret = radeon_ddc_probe(radeon_connector);
if (dret) {
@@ -946,6 +970,10 @@ radeon_dvi_detect(struct drm_connector *connector, bool force)
encoder = obj_to_encoder(obj);
+ if (encoder->encoder_type != DRM_MODE_ENCODER_DAC ||
+ encoder->encoder_type != DRM_MODE_ENCODER_TVDAC)
+ continue;
+
encoder_funcs = encoder->helper_private;
if (encoder_funcs->detect) {
if (ret != connector_status_connected) {
@@ -1057,7 +1085,7 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector,
(radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B))
return MODE_OK;
else if (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_A) {
- if (ASIC_IS_DCE3(rdev)) {
+ if (0) {
/* HDMI 1.3+ supports max clock of 340 Mhz */
if (mode->clock > 340000)
return MODE_CLOCK_HIGH;
@@ -1117,13 +1145,23 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
(connector->connector_type == DRM_MODE_CONNECTOR_LVDS)) {
struct drm_display_mode *mode;
- if (!radeon_dig_connector->edp_on)
- atombios_set_edp_panel_power(connector,
- ATOM_TRANSMITTER_ACTION_POWER_ON);
- ret = radeon_ddc_get_modes(radeon_connector);
- if (!radeon_dig_connector->edp_on)
- atombios_set_edp_panel_power(connector,
- ATOM_TRANSMITTER_ACTION_POWER_OFF);
+ if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+ if (!radeon_dig_connector->edp_on)
+ atombios_set_edp_panel_power(connector,
+ ATOM_TRANSMITTER_ACTION_POWER_ON);
+ ret = radeon_ddc_get_modes(radeon_connector);
+ if (!radeon_dig_connector->edp_on)
+ atombios_set_edp_panel_power(connector,
+ ATOM_TRANSMITTER_ACTION_POWER_OFF);
+ } else {
+ /* need to setup ddc on the bridge */
+ if (radeon_connector_encoder_get_dp_bridge_encoder_id(connector) !=
+ ENCODER_OBJECT_ID_NONE) {
+ if (encoder)
+ radeon_atom_ext_encoder_setup_ddc(encoder);
+ }
+ ret = radeon_ddc_get_modes(radeon_connector);
+ }
if (ret > 0) {
if (encoder) {
@@ -1134,7 +1172,6 @@ static int radeon_dp_get_modes(struct drm_connector *connector)
return ret;
}
- encoder = radeon_best_single_encoder(connector);
if (!encoder)
return 0;
@@ -1241,6 +1278,9 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
+ if (!force && radeon_check_hpd_status_unchanged(connector))
+ return connector->status;
+
if (radeon_connector->edid) {
kfree(radeon_connector->edid);
radeon_connector->edid = NULL;
diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
index dc79d08c1f97..d9d9f5a59c42 100644
--- a/drivers/gpu/drm/radeon/radeon_cs.c
+++ b/drivers/gpu/drm/radeon/radeon_cs.c
@@ -243,20 +243,11 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
if ((p->cs_flags & RADEON_CS_USE_VM) &&
!p->rdev->vm_manager.enabled) {
DRM_ERROR("VM not active on asic!\n");
- if (p->chunk_relocs_idx != -1)
- kfree(p->chunks[p->chunk_relocs_idx].kdata);
- if (p->chunk_flags_idx != -1)
- kfree(p->chunks[p->chunk_flags_idx].kdata);
return -EINVAL;
}
- if (radeon_cs_get_ring(p, ring, priority)) {
- if (p->chunk_relocs_idx != -1)
- kfree(p->chunks[p->chunk_relocs_idx].kdata);
- if (p->chunk_flags_idx != -1)
- kfree(p->chunks[p->chunk_flags_idx].kdata);
+ if (radeon_cs_get_ring(p, ring, priority))
return -EINVAL;
- }
/* deal with non-vm */
@@ -271,11 +262,8 @@ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
p->chunks[p->chunk_ib_idx].kpage[0] = kmalloc(PAGE_SIZE, GFP_KERNEL);
p->chunks[p->chunk_ib_idx].kpage[1] = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (p->chunks[p->chunk_ib_idx].kpage[0] == NULL ||
- p->chunks[p->chunk_ib_idx].kpage[1] == NULL) {
- kfree(p->chunks[p->chunk_ib_idx].kpage[0]);
- kfree(p->chunks[p->chunk_ib_idx].kpage[1]);
+ p->chunks[p->chunk_ib_idx].kpage[1] == NULL)
return -ENOMEM;
- }
p->chunks[p->chunk_ib_idx].kpage_idx[0] = -1;
p->chunks[p->chunk_ib_idx].kpage_idx[1] = -1;
p->chunks[p->chunk_ib_idx].last_copied_page = -1;
@@ -460,6 +448,10 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
int r;
radeon_mutex_lock(&rdev->cs_mutex);
+ if (!rdev->accel_working) {
+ radeon_mutex_unlock(&rdev->cs_mutex);
+ return -EBUSY;
+ }
/* initialize parser */
memset(&parser, 0, sizeof(struct radeon_cs_parser));
parser.filp = filp;
diff --git a/drivers/gpu/drm/radeon/radeon_cursor.c b/drivers/gpu/drm/radeon/radeon_cursor.c
index fde25c0d65a0..42acc6449dd6 100644
--- a/drivers/gpu/drm/radeon/radeon_cursor.c
+++ b/drivers/gpu/drm/radeon/radeon_cursor.c
@@ -151,7 +151,9 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
uint32_t height)
{
struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_device *rdev = crtc->dev->dev_private;
struct drm_gem_object *obj;
+ struct radeon_bo *robj;
uint64_t gpu_addr;
int ret;
@@ -173,7 +175,15 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
return -ENOENT;
}
- ret = radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
+ robj = gem_to_radeon_bo(obj);
+ ret = radeon_bo_reserve(robj, false);
+ if (unlikely(ret != 0))
+ goto fail;
+ /* Only 27 bit offset for legacy cursor */
+ ret = radeon_bo_pin_restricted(robj, RADEON_GEM_DOMAIN_VRAM,
+ ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
+ &gpu_addr);
+ radeon_bo_unreserve(robj);
if (ret)
goto fail;
@@ -181,14 +191,18 @@ int radeon_crtc_cursor_set(struct drm_crtc *crtc,
radeon_crtc->cursor_height = height;
radeon_lock_cursor(crtc, true);
- /* XXX only 27 bit offset for legacy cursor */
radeon_set_cursor(crtc, obj, gpu_addr);
radeon_show_cursor(crtc);
radeon_lock_cursor(crtc, false);
unpin:
if (radeon_crtc->cursor_bo) {
- radeon_gem_object_unpin(radeon_crtc->cursor_bo);
+ robj = gem_to_radeon_bo(radeon_crtc->cursor_bo);
+ ret = radeon_bo_reserve(robj, false);
+ if (likely(ret == 0)) {
+ radeon_bo_unpin(robj);
+ radeon_bo_unreserve(robj);
+ }
drm_gem_object_unreference_unlocked(radeon_crtc->cursor_bo);
}
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 1f98e5fbc733..1ebcef25b915 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -402,7 +402,9 @@ static int radeon_crtc_page_flip(struct drm_crtc *crtc,
DRM_ERROR("failed to reserve new rbo buffer before flip\n");
goto pflip_cleanup;
}
- r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+ /* Only 27 bit offset for legacy CRTC */
+ r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
+ ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27, &base);
if (unlikely(r != 0)) {
radeon_bo_unreserve(rbo);
r = -EINVAL;
@@ -1087,15 +1089,21 @@ static const struct drm_framebuffer_funcs radeon_fb_funcs = {
.create_handle = radeon_user_framebuffer_create_handle,
};
-void
+int
radeon_framebuffer_init(struct drm_device *dev,
struct radeon_framebuffer *rfb,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj)
{
+ int ret;
rfb->obj = obj;
- drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs);
+ ret = drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs);
+ if (ret) {
+ rfb->obj = NULL;
+ return ret;
+ }
drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd);
+ return 0;
}
static struct drm_framebuffer *
@@ -1105,6 +1113,7 @@ radeon_user_framebuffer_create(struct drm_device *dev,
{
struct drm_gem_object *obj;
struct radeon_framebuffer *radeon_fb;
+ int ret;
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
if (obj == NULL) {
@@ -1117,7 +1126,12 @@ radeon_user_framebuffer_create(struct drm_device *dev,
if (radeon_fb == NULL)
return ERR_PTR(-ENOMEM);
- radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj);
+ ret = radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj);
+ if (ret) {
+ kfree(radeon_fb);
+ drm_gem_object_unreference_unlocked(obj);
+ return NULL;
+ }
return &radeon_fb->base;
}
diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
index 9419c51bcf50..26e92708d114 100644
--- a/drivers/gpu/drm/radeon/radeon_encoders.c
+++ b/drivers/gpu/drm/radeon/radeon_encoders.c
@@ -307,8 +307,6 @@ void radeon_panel_mode_fixup(struct drm_encoder *encoder,
bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
u32 pixel_clock)
{
- struct drm_device *dev = encoder->dev;
- struct radeon_device *rdev = dev->dev_private;
struct drm_connector *connector;
struct radeon_connector *radeon_connector;
struct radeon_connector_atom_dig *dig_connector;
@@ -326,7 +324,7 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
case DRM_MODE_CONNECTOR_HDMIB:
if (radeon_connector->use_digital) {
/* HDMI 1.3 supports up to 340 Mhz over single link */
- if (ASIC_IS_DCE3(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (0 && drm_detect_hdmi_monitor(radeon_connector->edid)) {
if (pixel_clock > 340000)
return true;
else
@@ -348,7 +346,7 @@ bool radeon_dig_monitor_is_duallink(struct drm_encoder *encoder,
return false;
else {
/* HDMI 1.3 supports up to 340 Mhz over single link */
- if (ASIC_IS_DCE3(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) {
+ if (0 && drm_detect_hdmi_monitor(radeon_connector->edid)) {
if (pixel_clock > 340000)
return true;
else
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index a5692d5f415d..5906914a78bc 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -164,7 +164,10 @@ static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
ret = radeon_bo_reserve(rbo, false);
if (unlikely(ret != 0))
goto out_unref;
- ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, NULL);
+ /* Only 27 bit offset for legacy CRTC */
+ ret = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM,
+ ASIC_IS_AVIVO(rdev) ? 0 : 1 << 27,
+ NULL);
if (ret) {
radeon_bo_unreserve(rbo);
goto out_unref;
@@ -209,6 +212,11 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
sizes->surface_depth);
ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
+ if (ret) {
+ DRM_ERROR("failed to create fbcon object %d\n", ret);
+ return ret;
+ }
+
rbo = gem_to_radeon_bo(gobj);
/* okay we have an object now allocate the framebuffer */
@@ -220,7 +228,11 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
info->par = rfbdev;
- radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
+ ret = radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
+ if (ret) {
+ DRM_ERROR("failed to initalise framebuffer %d\n", ret);
+ goto out_unref;
+ }
fb = &rfbdev->rfb.base;
diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
index 64ea3dd9e6ff..4bd36a354fbe 100644
--- a/drivers/gpu/drm/radeon/radeon_fence.c
+++ b/drivers/gpu/drm/radeon/radeon_fence.c
@@ -364,8 +364,10 @@ int radeon_fence_count_emitted(struct radeon_device *rdev, int ring)
int not_processed = 0;
read_lock_irqsave(&rdev->fence_lock, irq_flags);
- if (!rdev->fence_drv[ring].initialized)
+ if (!rdev->fence_drv[ring].initialized) {
+ read_unlock_irqrestore(&rdev->fence_lock, irq_flags);
return 0;
+ }
if (!list_empty(&rdev->fence_drv[ring].emitted)) {
struct list_head *ptr;
diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
index 010dad8b66ae..c58a036233fb 100644
--- a/drivers/gpu/drm/radeon/radeon_gart.c
+++ b/drivers/gpu/drm/radeon/radeon_gart.c
@@ -597,13 +597,13 @@ int radeon_vm_bo_rmv(struct radeon_device *rdev,
if (bo_va == NULL)
return 0;
- list_del(&bo_va->bo_list);
mutex_lock(&vm->mutex);
radeon_mutex_lock(&rdev->cs_mutex);
radeon_vm_bo_update_pte(rdev, vm, bo, NULL);
radeon_mutex_unlock(&rdev->cs_mutex);
list_del(&bo_va->vm_list);
mutex_unlock(&vm->mutex);
+ list_del(&bo_va->bo_list);
kfree(bo_va);
return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index 7337850af2fa..c7008b5210f7 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -75,32 +75,6 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
return 0;
}
-int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
- uint64_t *gpu_addr)
-{
- struct radeon_bo *robj = gem_to_radeon_bo(obj);
- int r;
-
- r = radeon_bo_reserve(robj, false);
- if (unlikely(r != 0))
- return r;
- r = radeon_bo_pin(robj, pin_domain, gpu_addr);
- radeon_bo_unreserve(robj);
- return r;
-}
-
-void radeon_gem_object_unpin(struct drm_gem_object *obj)
-{
- struct radeon_bo *robj = gem_to_radeon_bo(obj);
- int r;
-
- r = radeon_bo_reserve(robj, false);
- if (likely(r == 0)) {
- radeon_bo_unpin(robj);
- radeon_bo_unreserve(robj);
- }
-}
-
int radeon_gem_set_domain(struct drm_gem_object *gobj,
uint32_t rdomain, uint32_t wdomain)
{
diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
index 3265a7a57977..85bcfc8923a7 100644
--- a/drivers/gpu/drm/radeon/radeon_i2c.c
+++ b/drivers/gpu/drm/radeon/radeon_i2c.c
@@ -26,6 +26,7 @@
#include <linux/export.h>
#include "drmP.h"
+#include "drm_edid.h"
#include "radeon_drm.h"
#include "radeon.h"
#include "atom.h"
@@ -45,13 +46,13 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
int ret;
struct i2c_msg msgs[] = {
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = 0,
.len = 1,
.buf = &out,
},
{
- .addr = 0x50,
+ .addr = DDC_ADDR,
.flags = I2C_M_RD,
.len = 8,
.buf = buf,
diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
index 25a19c483075..210317c7045e 100644
--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
@@ -419,7 +419,9 @@ int radeon_crtc_do_set_base(struct drm_crtc *crtc,
r = radeon_bo_reserve(rbo, false);
if (unlikely(r != 0))
return r;
- r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
+ /* Only 27 bit offset for legacy CRTC */
+ r = radeon_bo_pin_restricted(rbo, RADEON_GEM_DOMAIN_VRAM, 1 << 27,
+ &base);
if (unlikely(r != 0)) {
radeon_bo_unreserve(rbo);
return -EINVAL;
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 4330e3253573..8a85598fb242 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -649,7 +649,7 @@ extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno);
extern void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno);
-void radeon_framebuffer_init(struct drm_device *dev,
+int radeon_framebuffer_init(struct drm_device *dev,
struct radeon_framebuffer *rfb,
struct drm_mode_fb_cmd2 *mode_cmd,
struct drm_gem_object *obj);
diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
index 342deaccc152..91541e63d582 100644
--- a/drivers/gpu/drm/radeon/radeon_object.c
+++ b/drivers/gpu/drm/radeon/radeon_object.c
@@ -224,7 +224,8 @@ void radeon_bo_unref(struct radeon_bo **bo)
*bo = NULL;
}
-int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
+int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain, u64 max_offset,
+ u64 *gpu_addr)
{
int r, i;
@@ -232,6 +233,7 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
bo->pin_count++;
if (gpu_addr)
*gpu_addr = radeon_bo_gpu_offset(bo);
+ WARN_ON_ONCE(max_offset != 0);
return 0;
}
radeon_ttm_placement_from_domain(bo, domain);
@@ -239,6 +241,15 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
/* force to pin into visible video ram */
bo->placement.lpfn = bo->rdev->mc.visible_vram_size >> PAGE_SHIFT;
}
+ if (max_offset) {
+ u64 lpfn = max_offset >> PAGE_SHIFT;
+
+ if (!bo->placement.lpfn)
+ bo->placement.lpfn = bo->rdev->mc.gtt_size >> PAGE_SHIFT;
+
+ if (lpfn < bo->placement.lpfn)
+ bo->placement.lpfn = lpfn;
+ }
for (i = 0; i < bo->placement.num_placement; i++)
bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false, false);
@@ -252,6 +263,11 @@ int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
return r;
}
+int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
+{
+ return radeon_bo_pin_restricted(bo, domain, 0, gpu_addr);
+}
+
int radeon_bo_unpin(struct radeon_bo *bo)
{
int r, i;
diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
index cde430308870..f9104be88d7c 100644
--- a/drivers/gpu/drm/radeon/radeon_object.h
+++ b/drivers/gpu/drm/radeon/radeon_object.h
@@ -118,6 +118,8 @@ extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr);
extern void radeon_bo_kunmap(struct radeon_bo *bo);
extern void radeon_bo_unref(struct radeon_bo **bo);
extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr);
+extern int radeon_bo_pin_restricted(struct radeon_bo *bo, u32 domain,
+ u64 max_offset, u64 *gpu_addr);
extern int radeon_bo_unpin(struct radeon_bo *bo);
extern int radeon_bo_evict_vram(struct radeon_device *rdev);
extern void radeon_bo_force_delete(struct radeon_device *rdev);
diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
index 32c83d8bad34..30566201dffb 100644
--- a/drivers/gpu/drm/radeon/radeon_ring.c
+++ b/drivers/gpu/drm/radeon/radeon_ring.c
@@ -503,8 +503,11 @@ static unsigned radeon_debugfs_ib_idx[RADEON_IB_POOL_SIZE];
int radeon_debugfs_ring_init(struct radeon_device *rdev)
{
#if defined(CONFIG_DEBUG_FS)
- return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list,
- ARRAY_SIZE(radeon_debugfs_ring_info_list));
+ if (rdev->family >= CHIP_CAYMAN)
+ return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list,
+ ARRAY_SIZE(radeon_debugfs_ring_info_list));
+ else
+ return radeon_debugfs_add_files(rdev, radeon_debugfs_ring_info_list, 1);
#else
return 0;
#endif
diff --git a/drivers/gpu/drm/radeon/reg_srcs/cayman b/drivers/gpu/drm/radeon/reg_srcs/cayman
index 7b526d3ceac1..aea63c415852 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/cayman
+++ b/drivers/gpu/drm/radeon/reg_srcs/cayman
@@ -208,7 +208,6 @@ cayman 0x9400
0x00028344 PA_SC_VPORT_ZMAX_14
0x00028348 PA_SC_VPORT_ZMIN_15
0x0002834C PA_SC_VPORT_ZMAX_15
-0x00028350 SX_MISC
0x00028354 SX_SURFACE_SYNC
0x0002835C SX_SCATTER_EXPORT_SIZE
0x00028380 SQ_VTX_SEMANTIC_0
@@ -560,6 +559,18 @@ cayman 0x9400
0x00028C34 PA_SC_AA_SAMPLE_LOCS_PIXEL_X1_Y1_3
0x00028C38 PA_SC_AA_MASK_X0_Y0_X1_Y0
0x00028C3C PA_SC_AA_MASK_X0_Y1_X1_Y1
+0x00028C78 CB_COLOR0_DIM
+0x00028CB4 CB_COLOR1_DIM
+0x00028CF0 CB_COLOR2_DIM
+0x00028D2C CB_COLOR3_DIM
+0x00028D68 CB_COLOR4_DIM
+0x00028DA4 CB_COLOR5_DIM
+0x00028DE0 CB_COLOR6_DIM
+0x00028E1C CB_COLOR7_DIM
+0x00028E58 CB_COLOR8_DIM
+0x00028E74 CB_COLOR9_DIM
+0x00028E90 CB_COLOR10_DIM
+0x00028EAC CB_COLOR11_DIM
0x00028C8C CB_COLOR0_CLEAR_WORD0
0x00028C90 CB_COLOR0_CLEAR_WORD1
0x00028C94 CB_COLOR0_CLEAR_WORD2
diff --git a/drivers/gpu/drm/radeon/reg_srcs/evergreen b/drivers/gpu/drm/radeon/reg_srcs/evergreen
index 7f4339463e31..77c37202376f 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/evergreen
+++ b/drivers/gpu/drm/radeon/reg_srcs/evergreen
@@ -224,7 +224,6 @@ evergreen 0x9400
0x00028344 PA_SC_VPORT_ZMAX_14
0x00028348 PA_SC_VPORT_ZMIN_15
0x0002834C PA_SC_VPORT_ZMAX_15
-0x00028350 SX_MISC
0x00028354 SX_SURFACE_SYNC
0x00028380 SQ_VTX_SEMANTIC_0
0x00028384 SQ_VTX_SEMANTIC_1
@@ -563,6 +562,18 @@ evergreen 0x9400
0x00028C34 PA_SC_AA_SAMPLE_LOCS_6
0x00028C38 PA_SC_AA_SAMPLE_LOCS_7
0x00028C3C PA_SC_AA_MASK
+0x00028C78 CB_COLOR0_DIM
+0x00028CB4 CB_COLOR1_DIM
+0x00028CF0 CB_COLOR2_DIM
+0x00028D2C CB_COLOR3_DIM
+0x00028D68 CB_COLOR4_DIM
+0x00028DA4 CB_COLOR5_DIM
+0x00028DE0 CB_COLOR6_DIM
+0x00028E1C CB_COLOR7_DIM
+0x00028E58 CB_COLOR8_DIM
+0x00028E74 CB_COLOR9_DIM
+0x00028E90 CB_COLOR10_DIM
+0x00028EAC CB_COLOR11_DIM
0x00028C8C CB_COLOR0_CLEAR_WORD0
0x00028C90 CB_COLOR0_CLEAR_WORD1
0x00028C94 CB_COLOR0_CLEAR_WORD2
diff --git a/drivers/gpu/drm/radeon/reg_srcs/r600 b/drivers/gpu/drm/radeon/reg_srcs/r600
index 79d245527ba8..626c24ea0b56 100644
--- a/drivers/gpu/drm/radeon/reg_srcs/r600
+++ b/drivers/gpu/drm/radeon/reg_srcs/r600
@@ -438,7 +438,6 @@ r600 0x9400
0x00028638 SPI_VS_OUT_ID_9
0x00028438 SX_ALPHA_REF
0x00028410 SX_ALPHA_TEST_CONTROL
-0x00028350 SX_MISC
0x00028354 SX_SURFACE_SYNC
0x00009014 SX_MEMORY_EXPORT_SIZE
0x00009604 TC_INVALIDATE
diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
index 5280c87d5955..4cf381b3a6d8 100644
--- a/drivers/gpu/drm/radeon/rs400.c
+++ b/drivers/gpu/drm/radeon/rs400.c
@@ -442,6 +442,8 @@ static int rs400_startup(struct radeon_device *rdev)
int rs400_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
rs400_gart_disable(rdev);
/* Resume clock before doing reset */
@@ -462,7 +464,11 @@ int rs400_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return rs400_startup(rdev);
+ r = rs400_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int rs400_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 8c2a3d4d638c..d25cf869d08d 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -703,9 +703,7 @@ int rs600_irq_process(struct radeon_device *rdev)
WREG32(RADEON_BUS_CNTL, msi_rearm | RS600_MSI_REARM);
break;
default:
- msi_rearm = RREG32(RADEON_MSI_REARM_EN) & ~RV370_MSI_REARM_EN;
- WREG32(RADEON_MSI_REARM_EN, msi_rearm);
- WREG32(RADEON_MSI_REARM_EN, msi_rearm | RV370_MSI_REARM_EN);
+ WREG32(RADEON_MSI_REARM_EN, RV370_MSI_REARM_EN);
break;
}
}
@@ -897,6 +895,8 @@ static int rs600_startup(struct radeon_device *rdev)
int rs600_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
rs600_gart_disable(rdev);
/* Resume clock before doing reset */
@@ -915,7 +915,11 @@ int rs600_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return rs600_startup(rdev);
+ r = rs600_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int rs600_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
index 6aa65032d3cb..f2c3b9d75f18 100644
--- a/drivers/gpu/drm/radeon/rs690.c
+++ b/drivers/gpu/drm/radeon/rs690.c
@@ -659,6 +659,8 @@ static int rs690_startup(struct radeon_device *rdev)
int rs690_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
rs400_gart_disable(rdev);
/* Resume clock before doing reset */
@@ -677,7 +679,11 @@ int rs690_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return rs690_startup(rdev);
+ r = rs690_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int rs690_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
index 9e1b159bbb7f..c0549b146bae 100644
--- a/drivers/gpu/drm/radeon/rv515.c
+++ b/drivers/gpu/drm/radeon/rv515.c
@@ -423,6 +423,8 @@ static int rv515_startup(struct radeon_device *rdev)
int rv515_resume(struct radeon_device *rdev)
{
+ int r;
+
/* Make sur GART are not working */
if (rdev->flags & RADEON_IS_PCIE)
rv370_pcie_gart_disable(rdev);
@@ -442,7 +444,11 @@ int rv515_resume(struct radeon_device *rdev)
radeon_surface_init(rdev);
rdev->accel_working = true;
- return rv515_startup(rdev);
+ r = rv515_startup(rdev);
+ if (r) {
+ rdev->accel_working = false;
+ }
+ return r;
}
int rv515_suspend(struct radeon_device *rdev)
diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
index 6f2cbfb18292..c62ae4be3845 100644
--- a/drivers/gpu/drm/radeon/rv770.c
+++ b/drivers/gpu/drm/radeon/rv770.c
@@ -1139,6 +1139,7 @@ int rv770_resume(struct radeon_device *rdev)
r = rv770_startup(rdev);
if (r) {
DRM_ERROR("r600 startup failed on resume\n");
+ rdev->accel_working = false;
return r;
}
diff --git a/drivers/gpu/drm/savage/savage_state.c b/drivers/gpu/drm/savage/savage_state.c
index 8a3e31599c94..031aaaf79ac2 100644
--- a/drivers/gpu/drm/savage/savage_state.c
+++ b/drivers/gpu/drm/savage/savage_state.c
@@ -1057,7 +1057,8 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_
DRM_ERROR("indexed drawing command extends "
"beyond end of command buffer\n");
DMA_FLUSH();
- return -EINVAL;
+ ret = -EINVAL;
+ goto done;
}
/* fall through */
case SAVAGE_CMD_DMA_PRIM:
@@ -1076,7 +1077,7 @@ int savage_bci_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_
cmdbuf->vb_stride,
cmdbuf->nbox, cmdbuf->box_addr);
if (ret != 0)
- return ret;
+ goto done;
first_draw_cmd = NULL;
}
}
diff --git a/drivers/gpu/drm/ttm/ttm_agp_backend.c b/drivers/gpu/drm/ttm/ttm_agp_backend.c
index 747c1413fc95..4a8728291361 100644
--- a/drivers/gpu/drm/ttm/ttm_agp_backend.c
+++ b/drivers/gpu/drm/ttm/ttm_agp_backend.c
@@ -29,6 +29,8 @@
* Keith Packard.
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_module.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_page_alloc.h"
@@ -74,7 +76,7 @@ static int ttm_agp_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem)
ret = agp_bind_memory(mem, node->start);
if (ret)
- printk(KERN_ERR TTM_PFX "AGP Bind memory failed.\n");
+ pr_err("AGP Bind memory failed\n");
return ret;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
index 7c3a57de8187..1f5c67c579cf 100644
--- a/drivers/gpu/drm/ttm/ttm_bo.c
+++ b/drivers/gpu/drm/ttm/ttm_bo.c
@@ -28,6 +28,8 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_module.h"
#include "ttm/ttm_bo_driver.h"
#include "ttm/ttm_placement.h"
@@ -68,15 +70,13 @@ static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type)
{
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
- printk(KERN_ERR TTM_PFX " has_type: %d\n", man->has_type);
- printk(KERN_ERR TTM_PFX " use_type: %d\n", man->use_type);
- printk(KERN_ERR TTM_PFX " flags: 0x%08X\n", man->flags);
- printk(KERN_ERR TTM_PFX " gpu_offset: 0x%08lX\n", man->gpu_offset);
- printk(KERN_ERR TTM_PFX " size: %llu\n", man->size);
- printk(KERN_ERR TTM_PFX " available_caching: 0x%08X\n",
- man->available_caching);
- printk(KERN_ERR TTM_PFX " default_caching: 0x%08X\n",
- man->default_caching);
+ pr_err(" has_type: %d\n", man->has_type);
+ pr_err(" use_type: %d\n", man->use_type);
+ pr_err(" flags: 0x%08X\n", man->flags);
+ pr_err(" gpu_offset: 0x%08lX\n", man->gpu_offset);
+ pr_err(" size: %llu\n", man->size);
+ pr_err(" available_caching: 0x%08X\n", man->available_caching);
+ pr_err(" default_caching: 0x%08X\n", man->default_caching);
if (mem_type != TTM_PL_SYSTEM)
(*man->func->debug)(man, TTM_PFX);
}
@@ -86,16 +86,16 @@ static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
{
int i, ret, mem_type;
- printk(KERN_ERR TTM_PFX "No space for %p (%lu pages, %luK, %luM)\n",
- bo, bo->mem.num_pages, bo->mem.size >> 10,
- bo->mem.size >> 20);
+ pr_err("No space for %p (%lu pages, %luK, %luM)\n",
+ bo, bo->mem.num_pages, bo->mem.size >> 10,
+ bo->mem.size >> 20);
for (i = 0; i < placement->num_placement; i++) {
ret = ttm_mem_type_from_flags(placement->placement[i],
&mem_type);
if (ret)
return;
- printk(KERN_ERR TTM_PFX " placement[%d]=0x%08X (%d)\n",
- i, placement->placement[i], mem_type);
+ pr_err(" placement[%d]=0x%08X (%d)\n",
+ i, placement->placement[i], mem_type);
ttm_mem_type_debug(bo->bdev, mem_type);
}
}
@@ -344,7 +344,7 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
ret = -ENOMEM;
break;
default:
- printk(KERN_ERR TTM_PFX "Illegal buffer object type\n");
+ pr_err("Illegal buffer object type\n");
ret = -EINVAL;
break;
}
@@ -432,7 +432,7 @@ moved:
if (bo->evicted) {
ret = bdev->driver->invalidate_caches(bdev, bo->mem.placement);
if (ret)
- printk(KERN_ERR TTM_PFX "Can not flush read caches\n");
+ pr_err("Can not flush read caches\n");
bo->evicted = false;
}
@@ -734,9 +734,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
if (unlikely(ret != 0)) {
if (ret != -ERESTARTSYS) {
- printk(KERN_ERR TTM_PFX
- "Failed to expire sync object before "
- "buffer eviction.\n");
+ pr_err("Failed to expire sync object before buffer eviction\n");
}
goto out;
}
@@ -757,9 +755,8 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
no_wait_reserve, no_wait_gpu);
if (ret) {
if (ret != -ERESTARTSYS) {
- printk(KERN_ERR TTM_PFX
- "Failed to find memory space for "
- "buffer 0x%p eviction.\n", bo);
+ pr_err("Failed to find memory space for buffer 0x%p eviction\n",
+ bo);
ttm_bo_mem_space_debug(bo, &placement);
}
goto out;
@@ -769,7 +766,7 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
no_wait_reserve, no_wait_gpu);
if (ret) {
if (ret != -ERESTARTSYS)
- printk(KERN_ERR TTM_PFX "Buffer eviction failed\n");
+ pr_err("Buffer eviction failed\n");
ttm_bo_mem_put(bo, &evict_mem);
goto out;
}
@@ -1180,7 +1177,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false);
if (ret) {
- printk(KERN_ERR TTM_PFX "Out of kernel memory.\n");
+ pr_err("Out of kernel memory\n");
if (destroy)
(*destroy)(bo);
else
@@ -1191,7 +1188,7 @@ int ttm_bo_init(struct ttm_bo_device *bdev,
size += buffer_start & ~PAGE_MASK;
num_pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
if (num_pages == 0) {
- printk(KERN_ERR TTM_PFX "Illegal buffer object size.\n");
+ pr_err("Illegal buffer object size\n");
if (destroy)
(*destroy)(bo);
else
@@ -1342,8 +1339,7 @@ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
if (allow_errors) {
return ret;
} else {
- printk(KERN_ERR TTM_PFX
- "Cleanup eviction failed\n");
+ pr_err("Cleanup eviction failed\n");
}
}
spin_lock(&glob->lru_lock);
@@ -1358,14 +1354,14 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
int ret = -EINVAL;
if (mem_type >= TTM_NUM_MEM_TYPES) {
- printk(KERN_ERR TTM_PFX "Illegal memory type %d\n", mem_type);
+ pr_err("Illegal memory type %d\n", mem_type);
return ret;
}
man = &bdev->man[mem_type];
if (!man->has_type) {
- printk(KERN_ERR TTM_PFX "Trying to take down uninitialized "
- "memory manager type %u\n", mem_type);
+ pr_err("Trying to take down uninitialized memory manager type %u\n",
+ mem_type);
return ret;
}
@@ -1388,16 +1384,12 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type)
struct ttm_mem_type_manager *man = &bdev->man[mem_type];
if (mem_type == 0 || mem_type >= TTM_NUM_MEM_TYPES) {
- printk(KERN_ERR TTM_PFX
- "Illegal memory manager memory type %u.\n",
- mem_type);
+ pr_err("Illegal memory manager memory type %u\n", mem_type);
return -EINVAL;
}
if (!man->has_type) {
- printk(KERN_ERR TTM_PFX
- "Memory type %u has not been initialized.\n",
- mem_type);
+ pr_err("Memory type %u has not been initialized\n", mem_type);
return 0;
}
@@ -1482,8 +1474,7 @@ int ttm_bo_global_init(struct drm_global_reference *ref)
ttm_mem_init_shrink(&glob->shrink, ttm_bo_swapout);
ret = ttm_mem_register_shrink(glob->mem_glob, &glob->shrink);
if (unlikely(ret != 0)) {
- printk(KERN_ERR TTM_PFX
- "Could not register buffer object swapout.\n");
+ pr_err("Could not register buffer object swapout\n");
goto out_no_shrink;
}
@@ -1516,9 +1507,8 @@ int ttm_bo_device_release(struct ttm_bo_device *bdev)
man->use_type = false;
if ((i != TTM_PL_SYSTEM) && ttm_bo_clean_mm(bdev, i)) {
ret = -EBUSY;
- printk(KERN_ERR TTM_PFX
- "DRM memory manager type %d "
- "is not clean.\n", i);
+ pr_err("DRM memory manager type %d is not clean\n",
+ i);
}
man->has_type = false;
}
diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
index 54412848de88..a877813571a4 100644
--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
+++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
@@ -28,6 +28,8 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <ttm/ttm_module.h>
#include <ttm/ttm_bo_driver.h>
#include <ttm/ttm_placement.h>
@@ -262,8 +264,7 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
read_unlock(&bdev->vm_lock);
if (unlikely(bo == NULL)) {
- printk(KERN_ERR TTM_PFX
- "Could not find buffer object to map.\n");
+ pr_err("Could not find buffer object to map\n");
return -EINVAL;
}
diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c
index 9eba8e9a4e9c..23d2ecbaed59 100644
--- a/drivers/gpu/drm/ttm/ttm_memory.c
+++ b/drivers/gpu/drm/ttm/ttm_memory.c
@@ -25,6 +25,8 @@
*
**************************************************************************/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_memory.h"
#include "ttm/ttm_module.h"
#include "ttm/ttm_page_alloc.h"
@@ -74,9 +76,8 @@ static void ttm_mem_zone_kobj_release(struct kobject *kobj)
struct ttm_mem_zone *zone =
container_of(kobj, struct ttm_mem_zone, kobj);
- printk(KERN_INFO TTM_PFX
- "Zone %7s: Used memory at exit: %llu kiB.\n",
- zone->name, (unsigned long long) zone->used_mem >> 10);
+ pr_info("Zone %7s: Used memory at exit: %llu kiB\n",
+ zone->name, (unsigned long long)zone->used_mem >> 10);
kfree(zone);
}
@@ -390,9 +391,8 @@ int ttm_mem_global_init(struct ttm_mem_global *glob)
#endif
for (i = 0; i < glob->num_zones; ++i) {
zone = glob->zones[i];
- printk(KERN_INFO TTM_PFX
- "Zone %7s: Available graphics memory: %llu kiB.\n",
- zone->name, (unsigned long long) zone->max_mem >> 10);
+ pr_info("Zone %7s: Available graphics memory: %llu kiB\n",
+ zone->name, (unsigned long long)zone->max_mem >> 10);
}
ttm_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
ttm_dma_page_alloc_init(glob, glob->zone_kernel->max_mem/(2*PAGE_SIZE));
diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
index 93577f2e2954..68daca412cbd 100644
--- a/drivers/gpu/drm/ttm/ttm_object.c
+++ b/drivers/gpu/drm/ttm/ttm_object.c
@@ -49,6 +49,8 @@
* for fast lookup of ref objects given a base object.
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include "ttm/ttm_object.h"
#include "ttm/ttm_module.h"
#include <linux/list.h>
@@ -232,8 +234,7 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
return NULL;
if (tfile != base->tfile && !base->shareable) {
- printk(KERN_ERR TTM_PFX
- "Attempted access of non-shareable object.\n");
+ pr_err("Attempted access of non-shareable object\n");
ttm_base_object_unref(&base);
return NULL;
}
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc.c b/drivers/gpu/drm/ttm/ttm_page_alloc.c
index 499debda791e..ebc6fac96e36 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc.c
@@ -30,6 +30,9 @@
* - Use page->lru to keep a free list
* - doesn't track currently in use pages
*/
+
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/highmem.h>
@@ -167,18 +170,13 @@ static ssize_t ttm_pool_store(struct kobject *kobj,
m->options.small = val;
else if (attr == &ttm_page_pool_alloc_size) {
if (val > NUM_PAGES_TO_ALLOC*8) {
- printk(KERN_ERR TTM_PFX
- "Setting allocation size to %lu "
- "is not allowed. Recommended size is "
- "%lu\n",
+ pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n",
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7),
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
return size;
} else if (val > NUM_PAGES_TO_ALLOC) {
- printk(KERN_WARNING TTM_PFX
- "Setting allocation size to "
- "larger than %lu is not recommended.\n",
- NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
+ pr_warn("Setting allocation size to larger than %lu is not recommended\n",
+ NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
}
m->options.alloc_size = val;
}
@@ -279,8 +277,7 @@ static void ttm_pages_put(struct page *pages[], unsigned npages)
{
unsigned i;
if (set_pages_array_wb(pages, npages))
- printk(KERN_ERR TTM_PFX "Failed to set %d pages to wb!\n",
- npages);
+ pr_err("Failed to set %d pages to wb!\n", npages);
for (i = 0; i < npages; ++i)
__free_page(pages[i]);
}
@@ -315,8 +312,7 @@ static int ttm_page_pool_free(struct ttm_page_pool *pool, unsigned nr_free)
pages_to_free = kmalloc(npages_to_free * sizeof(struct page *),
GFP_KERNEL);
if (!pages_to_free) {
- printk(KERN_ERR TTM_PFX
- "Failed to allocate memory for pool free operation.\n");
+ pr_err("Failed to allocate memory for pool free operation\n");
return 0;
}
@@ -438,16 +434,12 @@ static int ttm_set_pages_caching(struct page **pages,
case tt_uncached:
r = set_pages_array_uc(pages, cpages);
if (r)
- printk(KERN_ERR TTM_PFX
- "Failed to set %d pages to uc!\n",
- cpages);
+ pr_err("Failed to set %d pages to uc!\n", cpages);
break;
case tt_wc:
r = set_pages_array_wc(pages, cpages);
if (r)
- printk(KERN_ERR TTM_PFX
- "Failed to set %d pages to wc!\n",
- cpages);
+ pr_err("Failed to set %d pages to wc!\n", cpages);
break;
default:
break;
@@ -492,8 +484,7 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags,
caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL);
if (!caching_array) {
- printk(KERN_ERR TTM_PFX
- "Unable to allocate table for new pages.");
+ pr_err("Unable to allocate table for new pages\n");
return -ENOMEM;
}
@@ -501,7 +492,7 @@ static int ttm_alloc_new_pages(struct list_head *pages, gfp_t gfp_flags,
p = alloc_page(gfp_flags);
if (!p) {
- printk(KERN_ERR TTM_PFX "Unable to get page %u.\n", i);
+ pr_err("Unable to get page %u\n", i);
/* store already allocated pages in the pool after
* setting the caching state */
@@ -599,8 +590,7 @@ static void ttm_page_pool_fill_locked(struct ttm_page_pool *pool,
++pool->nrefills;
pool->npages += alloc_size;
} else {
- printk(KERN_ERR TTM_PFX
- "Failed to fill pool (%p).", pool);
+ pr_err("Failed to fill pool (%p)\n", pool);
/* If we have any pages left put them to the pool. */
list_for_each_entry(p, &pool->list, lru) {
++cpages;
@@ -675,9 +665,7 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags,
for (i = 0; i < npages; i++) {
if (pages[i]) {
if (page_count(pages[i]) != 1)
- printk(KERN_ERR TTM_PFX
- "Erroneous page count. "
- "Leaking pages.\n");
+ pr_err("Erroneous page count. Leaking pages.\n");
__free_page(pages[i]);
pages[i] = NULL;
}
@@ -689,9 +677,7 @@ static void ttm_put_pages(struct page **pages, unsigned npages, int flags,
for (i = 0; i < npages; i++) {
if (pages[i]) {
if (page_count(pages[i]) != 1)
- printk(KERN_ERR TTM_PFX
- "Erroneous page count. "
- "Leaking pages.\n");
+ pr_err("Erroneous page count. Leaking pages.\n");
list_add_tail(&pages[i]->lru, &pool->list);
pages[i] = NULL;
pool->npages++;
@@ -740,8 +726,7 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags,
p = alloc_page(gfp_flags);
if (!p) {
- printk(KERN_ERR TTM_PFX
- "Unable to allocate page.");
+ pr_err("Unable to allocate page\n");
return -ENOMEM;
}
@@ -781,9 +766,7 @@ static int ttm_get_pages(struct page **pages, unsigned npages, int flags,
if (r) {
/* If there is any pages in the list put them back to
* the pool. */
- printk(KERN_ERR TTM_PFX
- "Failed to allocate extra pages "
- "for large request.");
+ pr_err("Failed to allocate extra pages for large request\n");
ttm_put_pages(pages, count, flags, cstate);
return r;
}
@@ -809,7 +792,7 @@ int ttm_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages)
WARN_ON(_manager);
- printk(KERN_INFO TTM_PFX "Initializing pool allocator.\n");
+ pr_info("Initializing pool allocator\n");
_manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
@@ -844,7 +827,7 @@ void ttm_page_alloc_fini(void)
{
int i;
- printk(KERN_INFO TTM_PFX "Finalizing pool allocator.\n");
+ pr_info("Finalizing pool allocator\n");
ttm_pool_mm_shrink_fini(_manager);
for (i = 0; i < NUM_POOLS; ++i)
diff --git a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
index 0c46d8cdc6ea..4f9e548b2eec 100644
--- a/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
+++ b/drivers/gpu/drm/ttm/ttm_page_alloc_dma.c
@@ -33,6 +33,8 @@
* when freed).
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <linux/dma-mapping.h>
#include <linux/list.h>
#include <linux/seq_file.h> /* for seq_printf */
@@ -221,18 +223,13 @@ static ssize_t ttm_pool_store(struct kobject *kobj, struct attribute *attr,
m->options.small = val;
else if (attr == &ttm_page_pool_alloc_size) {
if (val > NUM_PAGES_TO_ALLOC*8) {
- printk(KERN_ERR TTM_PFX
- "Setting allocation size to %lu "
- "is not allowed. Recommended size is "
- "%lu\n",
+ pr_err("Setting allocation size to %lu is not allowed. Recommended size is %lu\n",
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 7),
NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
return size;
} else if (val > NUM_PAGES_TO_ALLOC) {
- printk(KERN_WARNING TTM_PFX
- "Setting allocation size to "
- "larger than %lu is not recommended.\n",
- NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
+ pr_warn("Setting allocation size to larger than %lu is not recommended\n",
+ NUM_PAGES_TO_ALLOC*(PAGE_SIZE >> 10));
}
m->options.alloc_size = val;
}
@@ -313,15 +310,13 @@ static int ttm_set_pages_caching(struct dma_pool *pool,
if (pool->type & IS_UC) {
r = set_pages_array_uc(pages, cpages);
if (r)
- pr_err(TTM_PFX
- "%s: Failed to set %d pages to uc!\n",
+ pr_err("%s: Failed to set %d pages to uc!\n",
pool->dev_name, cpages);
}
if (pool->type & IS_WC) {
r = set_pages_array_wc(pages, cpages);
if (r)
- pr_err(TTM_PFX
- "%s: Failed to set %d pages to wc!\n",
+ pr_err("%s: Failed to set %d pages to wc!\n",
pool->dev_name, cpages);
}
return r;
@@ -387,8 +382,8 @@ static void ttm_dma_pages_put(struct dma_pool *pool, struct list_head *d_pages,
/* Don't set WB on WB page pool. */
if (npages && !(pool->type & IS_CACHED) &&
set_pages_array_wb(pages, npages))
- pr_err(TTM_PFX "%s: Failed to set %d pages to wb!\n",
- pool->dev_name, npages);
+ pr_err("%s: Failed to set %d pages to wb!\n",
+ pool->dev_name, npages);
list_for_each_entry_safe(d_page, tmp, d_pages, page_list) {
list_del(&d_page->page_list);
@@ -400,8 +395,8 @@ static void ttm_dma_page_put(struct dma_pool *pool, struct dma_page *d_page)
{
/* Don't set WB on WB page pool. */
if (!(pool->type & IS_CACHED) && set_pages_array_wb(&d_page->p, 1))
- pr_err(TTM_PFX "%s: Failed to set %d pages to wb!\n",
- pool->dev_name, 1);
+ pr_err("%s: Failed to set %d pages to wb!\n",
+ pool->dev_name, 1);
list_del(&d_page->page_list);
__ttm_dma_free_page(pool, d_page);
@@ -430,17 +425,16 @@ static unsigned ttm_dma_page_pool_free(struct dma_pool *pool, unsigned nr_free)
#if 0
if (nr_free > 1) {
pr_debug("%s: (%s:%d) Attempting to free %d (%d) pages\n",
- pool->dev_name, pool->name, current->pid,
- npages_to_free, nr_free);
+ pool->dev_name, pool->name, current->pid,
+ npages_to_free, nr_free);
}
#endif
pages_to_free = kmalloc(npages_to_free * sizeof(struct page *),
GFP_KERNEL);
if (!pages_to_free) {
- pr_err(TTM_PFX
- "%s: Failed to allocate memory for pool free operation.\n",
- pool->dev_name);
+ pr_err("%s: Failed to allocate memory for pool free operation\n",
+ pool->dev_name);
return 0;
}
INIT_LIST_HEAD(&d_pages);
@@ -723,23 +717,21 @@ static int ttm_dma_pool_alloc_new_pages(struct dma_pool *pool,
caching_array = kmalloc(max_cpages*sizeof(struct page *), GFP_KERNEL);
if (!caching_array) {
- pr_err(TTM_PFX
- "%s: Unable to allocate table for new pages.",
- pool->dev_name);
+ pr_err("%s: Unable to allocate table for new pages\n",
+ pool->dev_name);
return -ENOMEM;
}
if (count > 1) {
pr_debug("%s: (%s:%d) Getting %d pages\n",
- pool->dev_name, pool->name, current->pid,
- count);
+ pool->dev_name, pool->name, current->pid, count);
}
for (i = 0, cpages = 0; i < count; ++i) {
dma_p = __ttm_dma_alloc_page(pool);
if (!dma_p) {
- pr_err(TTM_PFX "%s: Unable to get page %u.\n",
- pool->dev_name, i);
+ pr_err("%s: Unable to get page %u\n",
+ pool->dev_name, i);
/* store already allocated pages in the pool after
* setting the caching state */
@@ -821,8 +813,8 @@ static int ttm_dma_page_pool_fill_locked(struct dma_pool *pool,
struct dma_page *d_page;
unsigned cpages = 0;
- pr_err(TTM_PFX "%s: Failed to fill %s pool (r:%d)!\n",
- pool->dev_name, pool->name, r);
+ pr_err("%s: Failed to fill %s pool (r:%d)!\n",
+ pool->dev_name, pool->name, r);
list_for_each_entry(d_page, &d_pages, page_list) {
cpages++;
@@ -1038,8 +1030,8 @@ static int ttm_dma_pool_mm_shrink(struct shrinker *shrink,
nr_free = shrink_pages;
shrink_pages = ttm_dma_page_pool_free(p->pool, nr_free);
pr_debug("%s: (%s:%d) Asked to shrink %d, have %d more to go\n",
- p->pool->dev_name, p->pool->name, current->pid, nr_free,
- shrink_pages);
+ p->pool->dev_name, p->pool->name, current->pid,
+ nr_free, shrink_pages);
}
mutex_unlock(&_manager->lock);
/* return estimated number of unused pages in pool */
@@ -1064,7 +1056,7 @@ int ttm_dma_page_alloc_init(struct ttm_mem_global *glob, unsigned max_pages)
WARN_ON(_manager);
- printk(KERN_INFO TTM_PFX "Initializing DMA pool allocator.\n");
+ pr_info("Initializing DMA pool allocator\n");
_manager = kzalloc(sizeof(*_manager), GFP_KERNEL);
if (!_manager)
@@ -1097,7 +1089,7 @@ void ttm_dma_page_alloc_fini(void)
{
struct device_pools *p, *t;
- printk(KERN_INFO TTM_PFX "Finalizing DMA pool allocator.\n");
+ pr_info("Finalizing DMA pool allocator\n");
ttm_dma_pool_mm_shrink_fini(_manager);
list_for_each_entry_safe_reverse(p, t, &_manager->pools, pools) {
diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
index 2f75d203a2bf..8aafeef40ca3 100644
--- a/drivers/gpu/drm/ttm/ttm_tt.c
+++ b/drivers/gpu/drm/ttm/ttm_tt.c
@@ -28,6 +28,8 @@
* Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
*/
+#define pr_fmt(fmt) "[TTM] " fmt
+
#include <linux/sched.h>
#include <linux/highmem.h>
#include <linux/pagemap.h>
@@ -196,7 +198,7 @@ int ttm_tt_init(struct ttm_tt *ttm, struct ttm_bo_device *bdev,
ttm_tt_alloc_page_directory(ttm);
if (!ttm->pages) {
ttm_tt_destroy(ttm);
- printk(KERN_ERR TTM_PFX "Failed allocating page table\n");
+ pr_err("Failed allocating page table\n");
return -ENOMEM;
}
return 0;
@@ -229,7 +231,7 @@ int ttm_dma_tt_init(struct ttm_dma_tt *ttm_dma, struct ttm_bo_device *bdev,
ttm_dma_tt_alloc_page_directory(ttm_dma);
if (!ttm->pages || !ttm_dma->dma_address) {
ttm_tt_destroy(ttm);
- printk(KERN_ERR TTM_PFX "Failed allocating page table\n");
+ pr_err("Failed allocating page table\n");
return -ENOMEM;
}
return 0;
@@ -347,7 +349,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistent_swap_storage)
ttm->num_pages << PAGE_SHIFT,
0);
if (unlikely(IS_ERR(swap_storage))) {
- printk(KERN_ERR "Failed allocating swap storage.\n");
+ pr_err("Failed allocating swap storage\n");
return PTR_ERR(swap_storage);
}
} else
diff --git a/drivers/gpu/drm/udl/Kconfig b/drivers/gpu/drm/udl/Kconfig
new file mode 100644
index 000000000000..0b5e096d39a6
--- /dev/null
+++ b/drivers/gpu/drm/udl/Kconfig
@@ -0,0 +1,12 @@
+config DRM_UDL
+ tristate "DisplayLink"
+ depends on DRM && EXPERIMENTAL
+ select DRM_USB
+ select FB_SYS_FILLRECT
+ select FB_SYS_COPYAREA
+ select FB_SYS_IMAGEBLIT
+ select FB_DEFERRED_IO
+ select DRM_KMS_HELPER
+ help
+ This is a KMS driver for the USB displaylink video adapters.
+ Say M/Y to add support for these devices via drm/kms interfaces.
diff --git a/drivers/gpu/drm/udl/Makefile b/drivers/gpu/drm/udl/Makefile
new file mode 100644
index 000000000000..05c7481bfd40
--- /dev/null
+++ b/drivers/gpu/drm/udl/Makefile
@@ -0,0 +1,6 @@
+
+ccflags-y := -Iinclude/drm
+
+udl-y := udl_drv.o udl_modeset.o udl_connector.o udl_encoder.o udl_main.o udl_fb.o udl_transfer.o udl_gem.o
+
+obj-$(CONFIG_DRM_UDL) := udl.o
diff --git a/drivers/gpu/drm/udl/udl_connector.c b/drivers/gpu/drm/udl/udl_connector.c
new file mode 100644
index 000000000000..ba055e9ca007
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_connector.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_edid.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+/* dummy connector to just get EDID,
+ all UDL appear to have a DVI-D */
+
+static u8 *udl_get_edid(struct udl_device *udl)
+{
+ u8 *block;
+ char rbuf[3];
+ int ret, i;
+
+ block = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (block == NULL)
+ return NULL;
+
+ for (i = 0; i < EDID_LENGTH; i++) {
+ ret = usb_control_msg(udl->ddev->usbdev,
+ usb_rcvctrlpipe(udl->ddev->usbdev, 0), (0x02),
+ (0x80 | (0x02 << 5)), i << 8, 0xA1, rbuf, 2,
+ HZ);
+ if (ret < 1) {
+ DRM_ERROR("Read EDID byte %d failed err %x\n", i, ret);
+ i--;
+ goto error;
+ }
+ block[i] = rbuf[1];
+ }
+
+ return block;
+
+error:
+ kfree(block);
+ return NULL;
+}
+
+static int udl_get_modes(struct drm_connector *connector)
+{
+ struct udl_device *udl = connector->dev->dev_private;
+ struct edid *edid;
+ int ret;
+
+ edid = (struct edid *)udl_get_edid(udl);
+
+ connector->display_info.raw_edid = (char *)edid;
+
+ drm_mode_connector_update_edid_property(connector, edid);
+ ret = drm_add_edid_modes(connector, edid);
+ connector->display_info.raw_edid = NULL;
+ kfree(edid);
+ return ret;
+}
+
+static int udl_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+{
+ return 0;
+}
+
+static enum drm_connector_status
+udl_detect(struct drm_connector *connector, bool force)
+{
+ if (drm_device_is_unplugged(connector->dev))
+ return connector_status_disconnected;
+ return connector_status_connected;
+}
+
+struct drm_encoder *udl_best_single_encoder(struct drm_connector *connector)
+{
+ int enc_id = connector->encoder_ids[0];
+ struct drm_mode_object *obj;
+ struct drm_encoder *encoder;
+
+ obj = drm_mode_object_find(connector->dev, enc_id, DRM_MODE_OBJECT_ENCODER);
+ if (!obj)
+ return NULL;
+ encoder = obj_to_encoder(obj);
+ return encoder;
+}
+
+int udl_connector_set_property(struct drm_connector *connector, struct drm_property *property,
+ uint64_t val)
+{
+ return 0;
+}
+
+static void udl_connector_destroy(struct drm_connector *connector)
+{
+ drm_sysfs_connector_remove(connector);
+ drm_connector_cleanup(connector);
+ kfree(connector);
+}
+
+struct drm_connector_helper_funcs udl_connector_helper_funcs = {
+ .get_modes = udl_get_modes,
+ .mode_valid = udl_mode_valid,
+ .best_encoder = udl_best_single_encoder,
+};
+
+struct drm_connector_funcs udl_connector_funcs = {
+ .dpms = drm_helper_connector_dpms,
+ .detect = udl_detect,
+ .fill_modes = drm_helper_probe_single_connector_modes,
+ .destroy = udl_connector_destroy,
+ .set_property = udl_connector_set_property,
+};
+
+int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder)
+{
+ struct drm_connector *connector;
+
+ connector = kzalloc(sizeof(struct drm_connector), GFP_KERNEL);
+ if (!connector)
+ return -ENOMEM;
+
+ drm_connector_init(dev, connector, &udl_connector_funcs, DRM_MODE_CONNECTOR_DVII);
+ drm_connector_helper_add(connector, &udl_connector_helper_funcs);
+
+ drm_sysfs_connector_add(connector);
+ drm_mode_connector_attach_encoder(connector, encoder);
+
+ drm_connector_attach_property(connector,
+ dev->mode_config.dirty_info_property,
+ 1);
+ return 0;
+}
diff --git a/drivers/gpu/drm/udl/udl_drv.c b/drivers/gpu/drm/udl/udl_drv.c
new file mode 100644
index 000000000000..5340c5f3987b
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_drv.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include "drm_usb.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+static struct drm_driver driver;
+
+static struct usb_device_id id_table[] = {
+ {.idVendor = 0x17e9, .match_flags = USB_DEVICE_ID_MATCH_VENDOR,},
+ {},
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+MODULE_LICENSE("GPL");
+
+static int udl_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ return drm_get_usb_dev(interface, id, &driver);
+}
+
+static void udl_usb_disconnect(struct usb_interface *interface)
+{
+ struct drm_device *dev = usb_get_intfdata(interface);
+
+ drm_kms_helper_poll_disable(dev);
+ drm_connector_unplug_all(dev);
+ udl_fbdev_unplug(dev);
+ udl_drop_usb(dev);
+ drm_unplug_dev(dev);
+}
+
+static struct vm_operations_struct udl_gem_vm_ops = {
+ .fault = udl_gem_fault,
+ .open = drm_gem_vm_open,
+ .close = drm_gem_vm_close,
+};
+
+static const struct file_operations udl_driver_fops = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .mmap = drm_gem_mmap,
+ .poll = drm_poll,
+ .read = drm_read,
+ .unlocked_ioctl = drm_ioctl,
+ .release = drm_release,
+ .fasync = drm_fasync,
+ .llseek = noop_llseek,
+};
+
+static struct drm_driver driver = {
+ .driver_features = DRIVER_MODESET | DRIVER_GEM,
+ .load = udl_driver_load,
+ .unload = udl_driver_unload,
+
+ /* gem hooks */
+ .gem_init_object = udl_gem_init_object,
+ .gem_free_object = udl_gem_free_object,
+ .gem_vm_ops = &udl_gem_vm_ops,
+
+ .dumb_create = udl_dumb_create,
+ .dumb_map_offset = udl_gem_mmap,
+ .dumb_destroy = udl_dumb_destroy,
+ .fops = &udl_driver_fops,
+ .name = DRIVER_NAME,
+ .desc = DRIVER_DESC,
+ .date = DRIVER_DATE,
+ .major = DRIVER_MAJOR,
+ .minor = DRIVER_MINOR,
+ .patchlevel = DRIVER_PATCHLEVEL,
+};
+
+static struct usb_driver udl_driver = {
+ .name = "udl",
+ .probe = udl_usb_probe,
+ .disconnect = udl_usb_disconnect,
+ .id_table = id_table,
+};
+
+static int __init udl_init(void)
+{
+ return drm_usb_init(&driver, &udl_driver);
+}
+
+static void __exit udl_exit(void)
+{
+ drm_usb_exit(&driver, &udl_driver);
+}
+
+module_init(udl_init);
+module_exit(udl_exit);
diff --git a/drivers/gpu/drm/udl/udl_drv.h b/drivers/gpu/drm/udl/udl_drv.h
new file mode 100644
index 000000000000..1612954a5bc4
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_drv.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#ifndef UDL_DRV_H
+#define UDL_DRV_H
+
+#include <linux/usb.h>
+
+#define DRIVER_NAME "udl"
+#define DRIVER_DESC "DisplayLink"
+#define DRIVER_DATE "20120220"
+
+#define DRIVER_MAJOR 0
+#define DRIVER_MINOR 0
+#define DRIVER_PATCHLEVEL 1
+
+struct udl_device;
+
+struct urb_node {
+ struct list_head entry;
+ struct udl_device *dev;
+ struct delayed_work release_urb_work;
+ struct urb *urb;
+};
+
+struct urb_list {
+ struct list_head list;
+ spinlock_t lock;
+ struct semaphore limit_sem;
+ int available;
+ int count;
+ size_t size;
+};
+
+struct udl_fbdev;
+
+struct udl_device {
+ struct device *dev;
+ struct drm_device *ddev;
+
+ int sku_pixel_limit;
+
+ struct urb_list urbs;
+ atomic_t lost_pixels; /* 1 = a render op failed. Need screen refresh */
+
+ struct udl_fbdev *fbdev;
+ char mode_buf[1024];
+ uint32_t mode_buf_len;
+ atomic_t bytes_rendered; /* raw pixel-bytes driver asked to render */
+ atomic_t bytes_identical; /* saved effort with backbuffer comparison */
+ atomic_t bytes_sent; /* to usb, after compression including overhead */
+ atomic_t cpu_kcycles_used; /* transpired during pixel processing */
+};
+
+struct udl_gem_object {
+ struct drm_gem_object base;
+ struct page **pages;
+ void *vmapping;
+};
+
+#define to_udl_bo(x) container_of(x, struct udl_gem_object, base)
+
+struct udl_framebuffer {
+ struct drm_framebuffer base;
+ struct udl_gem_object *obj;
+ bool active_16; /* active on the 16-bit channel */
+};
+
+#define to_udl_fb(x) container_of(x, struct udl_framebuffer, base)
+
+/* modeset */
+int udl_modeset_init(struct drm_device *dev);
+void udl_modeset_cleanup(struct drm_device *dev);
+int udl_connector_init(struct drm_device *dev, struct drm_encoder *encoder);
+
+struct drm_encoder *udl_encoder_init(struct drm_device *dev);
+
+struct urb *udl_get_urb(struct drm_device *dev);
+
+int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len);
+void udl_urb_completion(struct urb *urb);
+
+int udl_driver_load(struct drm_device *dev, unsigned long flags);
+int udl_driver_unload(struct drm_device *dev);
+
+int udl_fbdev_init(struct drm_device *dev);
+void udl_fbdev_cleanup(struct drm_device *dev);
+void udl_fbdev_unplug(struct drm_device *dev);
+struct drm_framebuffer *
+udl_fb_user_fb_create(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_mode_fb_cmd2 *mode_cmd);
+
+int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr,
+ const char *front, char **urb_buf_ptr,
+ u32 byte_offset, u32 byte_width,
+ int *ident_ptr, int *sent_ptr);
+
+int udl_dumb_create(struct drm_file *file_priv,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args);
+int udl_gem_mmap(struct drm_file *file_priv, struct drm_device *dev,
+ uint32_t handle, uint64_t *offset);
+int udl_dumb_destroy(struct drm_file *file_priv, struct drm_device *dev,
+ uint32_t handle);
+
+int udl_gem_init_object(struct drm_gem_object *obj);
+void udl_gem_free_object(struct drm_gem_object *gem_obj);
+struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev,
+ size_t size);
+
+int udl_gem_vmap(struct udl_gem_object *obj);
+void udl_gem_vunmap(struct udl_gem_object *obj);
+int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+
+int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
+ int width, int height);
+
+int udl_drop_usb(struct drm_device *dev);
+
+#define CMD_WRITE_RAW8 "\xAF\x60" /**< 8 bit raw write command. */
+#define CMD_WRITE_RL8 "\xAF\x61" /**< 8 bit run length command. */
+#define CMD_WRITE_COPY8 "\xAF\x62" /**< 8 bit copy command. */
+#define CMD_WRITE_RLX8 "\xAF\x63" /**< 8 bit extended run length command. */
+
+#define CMD_WRITE_RAW16 "\xAF\x68" /**< 16 bit raw write command. */
+#define CMD_WRITE_RL16 "\xAF\x69" /**< 16 bit run length command. */
+#define CMD_WRITE_COPY16 "\xAF\x6A" /**< 16 bit copy command. */
+#define CMD_WRITE_RLX16 "\xAF\x6B" /**< 16 bit extended run length command. */
+
+#endif
diff --git a/drivers/gpu/drm/udl/udl_encoder.c b/drivers/gpu/drm/udl/udl_encoder.c
new file mode 100644
index 000000000000..56e75f0f1df5
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_encoder.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+/* dummy encoder */
+void udl_enc_destroy(struct drm_encoder *encoder)
+{
+ drm_encoder_cleanup(encoder);
+ kfree(encoder);
+}
+
+static void udl_encoder_disable(struct drm_encoder *encoder)
+{
+}
+
+static bool udl_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+ return true;
+}
+
+static void udl_encoder_prepare(struct drm_encoder *encoder)
+{
+}
+
+static void udl_encoder_commit(struct drm_encoder *encoder)
+{
+}
+
+static void udl_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+{
+}
+
+static void
+udl_encoder_dpms(struct drm_encoder *encoder, int mode)
+{
+}
+
+static const struct drm_encoder_helper_funcs udl_helper_funcs = {
+ .dpms = udl_encoder_dpms,
+ .mode_fixup = udl_mode_fixup,
+ .prepare = udl_encoder_prepare,
+ .mode_set = udl_encoder_mode_set,
+ .commit = udl_encoder_commit,
+ .disable = udl_encoder_disable,
+};
+
+static const struct drm_encoder_funcs udl_enc_funcs = {
+ .destroy = udl_enc_destroy,
+};
+
+struct drm_encoder *udl_encoder_init(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+
+ encoder = kzalloc(sizeof(struct drm_encoder), GFP_KERNEL);
+ if (!encoder)
+ return NULL;
+
+ drm_encoder_init(dev, encoder, &udl_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &udl_helper_funcs);
+ encoder->possible_crtcs = 1;
+ return encoder;
+}
diff --git a/drivers/gpu/drm/udl/udl_fb.c b/drivers/gpu/drm/udl/udl_fb.c
new file mode 100644
index 000000000000..4d9c3a5d8a45
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_fb.c
@@ -0,0 +1,611 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+
+#include "drmP.h"
+#include "drm.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+#include "drm_fb_helper.h"
+
+#define DL_DEFIO_WRITE_DELAY 5 /* fb_deferred_io.delay in jiffies */
+
+static int fb_defio = 1; /* Optionally enable experimental fb_defio mmap support */
+static int fb_bpp = 16;
+
+module_param(fb_bpp, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+module_param(fb_defio, int, S_IWUSR | S_IRUSR | S_IWGRP | S_IRGRP);
+
+struct udl_fbdev {
+ struct drm_fb_helper helper;
+ struct udl_framebuffer ufb;
+ struct list_head fbdev_list;
+ int fb_count;
+};
+
+#define DL_ALIGN_UP(x, a) ALIGN(x, a)
+#define DL_ALIGN_DOWN(x, a) ALIGN(x-(a-1), a)
+
+/** Read the red component (0..255) of a 32 bpp colour. */
+#define DLO_RGB_GETRED(col) (uint8_t)((col) & 0xFF)
+
+/** Read the green component (0..255) of a 32 bpp colour. */
+#define DLO_RGB_GETGRN(col) (uint8_t)(((col) >> 8) & 0xFF)
+
+/** Read the blue component (0..255) of a 32 bpp colour. */
+#define DLO_RGB_GETBLU(col) (uint8_t)(((col) >> 16) & 0xFF)
+
+/** Return red/green component of a 16 bpp colour number. */
+#define DLO_RG16(red, grn) (uint8_t)((((red) & 0xF8) | ((grn) >> 5)) & 0xFF)
+
+/** Return green/blue component of a 16 bpp colour number. */
+#define DLO_GB16(grn, blu) (uint8_t)(((((grn) & 0x1C) << 3) | ((blu) >> 3)) & 0xFF)
+
+/** Return 8 bpp colour number from red, green and blue components. */
+#define DLO_RGB8(red, grn, blu) ((((red) << 5) | (((grn) & 3) << 3) | ((blu) & 7)) & 0xFF)
+
+#if 0
+static uint8_t rgb8(uint32_t col)
+{
+ uint8_t red = DLO_RGB_GETRED(col);
+ uint8_t grn = DLO_RGB_GETGRN(col);
+ uint8_t blu = DLO_RGB_GETBLU(col);
+
+ return DLO_RGB8(red, grn, blu);
+}
+
+static uint16_t rgb16(uint32_t col)
+{
+ uint8_t red = DLO_RGB_GETRED(col);
+ uint8_t grn = DLO_RGB_GETGRN(col);
+ uint8_t blu = DLO_RGB_GETBLU(col);
+
+ return (DLO_RG16(red, grn) << 8) + DLO_GB16(grn, blu);
+}
+#endif
+
+/*
+ * NOTE: fb_defio.c is holding info->fbdefio.mutex
+ * Touching ANY framebuffer memory that triggers a page fault
+ * in fb_defio will cause a deadlock, when it also tries to
+ * grab the same mutex.
+ */
+static void udlfb_dpy_deferred_io(struct fb_info *info,
+ struct list_head *pagelist)
+{
+ struct page *cur;
+ struct fb_deferred_io *fbdefio = info->fbdefio;
+ struct udl_fbdev *ufbdev = info->par;
+ struct drm_device *dev = ufbdev->ufb.base.dev;
+ struct udl_device *udl = dev->dev_private;
+ struct urb *urb;
+ char *cmd;
+ cycles_t start_cycles, end_cycles;
+ int bytes_sent = 0;
+ int bytes_identical = 0;
+ int bytes_rendered = 0;
+
+ if (!fb_defio)
+ return;
+
+ start_cycles = get_cycles();
+
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return;
+
+ cmd = urb->transfer_buffer;
+
+ /* walk the written page list and render each to device */
+ list_for_each_entry(cur, &fbdefio->pagelist, lru) {
+
+ if (udl_render_hline(dev, (ufbdev->ufb.base.bits_per_pixel / 8),
+ &urb, (char *) info->fix.smem_start,
+ &cmd, cur->index << PAGE_SHIFT,
+ PAGE_SIZE, &bytes_identical, &bytes_sent))
+ goto error;
+ bytes_rendered += PAGE_SIZE;
+ }
+
+ if (cmd > (char *) urb->transfer_buffer) {
+ /* Send partial buffer remaining before exiting */
+ int len = cmd - (char *) urb->transfer_buffer;
+ udl_submit_urb(dev, urb, len);
+ bytes_sent += len;
+ } else
+ udl_urb_completion(urb);
+
+error:
+ atomic_add(bytes_sent, &udl->bytes_sent);
+ atomic_add(bytes_identical, &udl->bytes_identical);
+ atomic_add(bytes_rendered, &udl->bytes_rendered);
+ end_cycles = get_cycles();
+ atomic_add(((unsigned int) ((end_cycles - start_cycles)
+ >> 10)), /* Kcycles */
+ &udl->cpu_kcycles_used);
+}
+
+int udl_handle_damage(struct udl_framebuffer *fb, int x, int y,
+ int width, int height)
+{
+ struct drm_device *dev = fb->base.dev;
+ struct udl_device *udl = dev->dev_private;
+ int i, ret;
+ char *cmd;
+ cycles_t start_cycles, end_cycles;
+ int bytes_sent = 0;
+ int bytes_identical = 0;
+ struct urb *urb;
+ int aligned_x;
+ int bpp = (fb->base.bits_per_pixel / 8);
+
+ if (!fb->active_16)
+ return 0;
+
+ if (!fb->obj->vmapping)
+ udl_gem_vmap(fb->obj);
+
+ start_cycles = get_cycles();
+
+ aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long));
+ width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long));
+ x = aligned_x;
+
+ if ((width <= 0) ||
+ (x + width > fb->base.width) ||
+ (y + height > fb->base.height))
+ return -EINVAL;
+
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return 0;
+ cmd = urb->transfer_buffer;
+
+ for (i = y; i < y + height ; i++) {
+ const int line_offset = fb->base.pitches[0] * i;
+ const int byte_offset = line_offset + (x * bpp);
+
+ if (udl_render_hline(dev, bpp, &urb,
+ (char *) fb->obj->vmapping,
+ &cmd, byte_offset, width * bpp,
+ &bytes_identical, &bytes_sent))
+ goto error;
+ }
+
+ if (cmd > (char *) urb->transfer_buffer) {
+ /* Send partial buffer remaining before exiting */
+ int len = cmd - (char *) urb->transfer_buffer;
+ ret = udl_submit_urb(dev, urb, len);
+ bytes_sent += len;
+ } else
+ udl_urb_completion(urb);
+
+error:
+ atomic_add(bytes_sent, &udl->bytes_sent);
+ atomic_add(bytes_identical, &udl->bytes_identical);
+ atomic_add(width*height*bpp, &udl->bytes_rendered);
+ end_cycles = get_cycles();
+ atomic_add(((unsigned int) ((end_cycles - start_cycles)
+ >> 10)), /* Kcycles */
+ &udl->cpu_kcycles_used);
+
+ return 0;
+}
+
+static int udl_fb_mmap(struct fb_info *info, struct vm_area_struct *vma)
+{
+ unsigned long start = vma->vm_start;
+ unsigned long size = vma->vm_end - vma->vm_start;
+ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
+ unsigned long page, pos;
+
+ if (offset + size > info->fix.smem_len)
+ return -EINVAL;
+
+ pos = (unsigned long)info->fix.smem_start + offset;
+
+ pr_notice("mmap() framebuffer addr:%lu size:%lu\n",
+ pos, size);
+
+ while (size > 0) {
+ page = vmalloc_to_pfn((void *)pos);
+ if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
+ return -EAGAIN;
+
+ start += PAGE_SIZE;
+ pos += PAGE_SIZE;
+ if (size > PAGE_SIZE)
+ size -= PAGE_SIZE;
+ else
+ size = 0;
+ }
+
+ vma->vm_flags |= VM_RESERVED; /* avoid to swap out this VMA */
+ return 0;
+}
+
+static void udl_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ sys_fillrect(info, rect);
+
+ udl_handle_damage(&ufbdev->ufb, rect->dx, rect->dy, rect->width,
+ rect->height);
+}
+
+static void udl_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ sys_copyarea(info, region);
+
+ udl_handle_damage(&ufbdev->ufb, region->dx, region->dy, region->width,
+ region->height);
+}
+
+static void udl_fb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ sys_imageblit(info, image);
+
+ udl_handle_damage(&ufbdev->ufb, image->dx, image->dy, image->width,
+ image->height);
+}
+
+/*
+ * It's common for several clients to have framebuffer open simultaneously.
+ * e.g. both fbcon and X. Makes things interesting.
+ * Assumes caller is holding info->lock (for open and release at least)
+ */
+static int udl_fb_open(struct fb_info *info, int user)
+{
+ struct udl_fbdev *ufbdev = info->par;
+ struct drm_device *dev = ufbdev->ufb.base.dev;
+ struct udl_device *udl = dev->dev_private;
+
+ /* If the USB device is gone, we don't accept new opens */
+ if (drm_device_is_unplugged(udl->ddev))
+ return -ENODEV;
+
+ ufbdev->fb_count++;
+
+ if (fb_defio && (info->fbdefio == NULL)) {
+ /* enable defio at last moment if not disabled by client */
+
+ struct fb_deferred_io *fbdefio;
+
+ fbdefio = kmalloc(sizeof(struct fb_deferred_io), GFP_KERNEL);
+
+ if (fbdefio) {
+ fbdefio->delay = DL_DEFIO_WRITE_DELAY;
+ fbdefio->deferred_io = udlfb_dpy_deferred_io;
+ }
+
+ info->fbdefio = fbdefio;
+ fb_deferred_io_init(info);
+ }
+
+ pr_notice("open /dev/fb%d user=%d fb_info=%p count=%d\n",
+ info->node, user, info, ufbdev->fb_count);
+
+ return 0;
+}
+
+
+/*
+ * Assumes caller is holding info->lock mutex (for open and release at least)
+ */
+static int udl_fb_release(struct fb_info *info, int user)
+{
+ struct udl_fbdev *ufbdev = info->par;
+
+ ufbdev->fb_count--;
+
+ if ((ufbdev->fb_count == 0) && (info->fbdefio)) {
+ fb_deferred_io_cleanup(info);
+ kfree(info->fbdefio);
+ info->fbdefio = NULL;
+ info->fbops->fb_mmap = udl_fb_mmap;
+ }
+
+ pr_warn("released /dev/fb%d user=%d count=%d\n",
+ info->node, user, ufbdev->fb_count);
+
+ return 0;
+}
+
+static struct fb_ops udlfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_check_var = drm_fb_helper_check_var,
+ .fb_set_par = drm_fb_helper_set_par,
+ .fb_fillrect = udl_fb_fillrect,
+ .fb_copyarea = udl_fb_copyarea,
+ .fb_imageblit = udl_fb_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,
+ .fb_mmap = udl_fb_mmap,
+ .fb_open = udl_fb_open,
+ .fb_release = udl_fb_release,
+};
+
+void udl_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno)
+{
+}
+
+void udl_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, int regno)
+{
+ *red = 0;
+ *green = 0;
+ *blue = 0;
+}
+
+static int udl_user_framebuffer_dirty(struct drm_framebuffer *fb,
+ struct drm_file *file,
+ unsigned flags, unsigned color,
+ struct drm_clip_rect *clips,
+ unsigned num_clips)
+{
+ struct udl_framebuffer *ufb = to_udl_fb(fb);
+ int i;
+
+ if (!ufb->active_16)
+ return 0;
+
+ for (i = 0; i < num_clips; i++) {
+ udl_handle_damage(ufb, clips[i].x1, clips[i].y1,
+ clips[i].x2 - clips[i].x1,
+ clips[i].y2 - clips[i].y1);
+ }
+ return 0;
+}
+
+static void udl_user_framebuffer_destroy(struct drm_framebuffer *fb)
+{
+ struct udl_framebuffer *ufb = to_udl_fb(fb);
+
+ if (ufb->obj)
+ drm_gem_object_unreference_unlocked(&ufb->obj->base);
+
+ drm_framebuffer_cleanup(fb);
+ kfree(ufb);
+}
+
+static const struct drm_framebuffer_funcs udlfb_funcs = {
+ .destroy = udl_user_framebuffer_destroy,
+ .dirty = udl_user_framebuffer_dirty,
+ .create_handle = NULL,
+};
+
+
+static int
+udl_framebuffer_init(struct drm_device *dev,
+ struct udl_framebuffer *ufb,
+ struct drm_mode_fb_cmd2 *mode_cmd,
+ struct udl_gem_object *obj)
+{
+ int ret;
+
+ ufb->obj = obj;
+ ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs);
+ drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd);
+ return ret;
+}
+
+
+static int udlfb_create(struct udl_fbdev *ufbdev,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct drm_device *dev = ufbdev->helper.dev;
+ struct fb_info *info;
+ struct device *device = &dev->usbdev->dev;
+ struct drm_framebuffer *fb;
+ struct drm_mode_fb_cmd2 mode_cmd;
+ struct udl_gem_object *obj;
+ uint32_t size;
+ int ret = 0;
+
+ if (sizes->surface_bpp == 24)
+ sizes->surface_bpp = 32;
+
+ mode_cmd.width = sizes->surface_width;
+ mode_cmd.height = sizes->surface_height;
+ mode_cmd.pitches[0] = mode_cmd.width * ((sizes->surface_bpp + 7) / 8);
+
+ mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
+ sizes->surface_depth);
+
+ size = mode_cmd.pitches[0] * mode_cmd.height;
+ size = ALIGN(size, PAGE_SIZE);
+
+ obj = udl_gem_alloc_object(dev, size);
+ if (!obj)
+ goto out;
+
+ ret = udl_gem_vmap(obj);
+ if (ret) {
+ DRM_ERROR("failed to vmap fb\n");
+ goto out_gfree;
+ }
+
+ info = framebuffer_alloc(0, device);
+ if (!info) {
+ ret = -ENOMEM;
+ goto out_gfree;
+ }
+ info->par = ufbdev;
+
+ ret = udl_framebuffer_init(dev, &ufbdev->ufb, &mode_cmd, obj);
+ if (ret)
+ goto out_gfree;
+
+ fb = &ufbdev->ufb.base;
+
+ ufbdev->helper.fb = fb;
+ ufbdev->helper.fbdev = info;
+
+ strcpy(info->fix.id, "udldrmfb");
+
+ info->screen_base = ufbdev->ufb.obj->vmapping;
+ info->fix.smem_len = size;
+ info->fix.smem_start = (unsigned long)ufbdev->ufb.obj->vmapping;
+
+ info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT;
+ info->fbops = &udlfb_ops;
+ drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
+ drm_fb_helper_fill_var(info, &ufbdev->helper, sizes->fb_width, sizes->fb_height);
+
+ ret = fb_alloc_cmap(&info->cmap, 256, 0);
+ if (ret) {
+ ret = -ENOMEM;
+ goto out_gfree;
+ }
+
+
+ DRM_DEBUG_KMS("allocated %dx%d vmal %p\n",
+ fb->width, fb->height,
+ ufbdev->ufb.obj->vmapping);
+
+ return ret;
+out_gfree:
+ drm_gem_object_unreference(&ufbdev->ufb.obj->base);
+out:
+ return ret;
+}
+
+static int udl_fb_find_or_create_single(struct drm_fb_helper *helper,
+ struct drm_fb_helper_surface_size *sizes)
+{
+ struct udl_fbdev *ufbdev = (struct udl_fbdev *)helper;
+ int new_fb = 0;
+ int ret;
+
+ if (!helper->fb) {
+ ret = udlfb_create(ufbdev, sizes);
+ if (ret)
+ return ret;
+
+ new_fb = 1;
+ }
+ return new_fb;
+}
+
+static struct drm_fb_helper_funcs udl_fb_helper_funcs = {
+ .gamma_set = udl_crtc_fb_gamma_set,
+ .gamma_get = udl_crtc_fb_gamma_get,
+ .fb_probe = udl_fb_find_or_create_single,
+};
+
+static void udl_fbdev_destroy(struct drm_device *dev,
+ struct udl_fbdev *ufbdev)
+{
+ struct fb_info *info;
+ if (ufbdev->helper.fbdev) {
+ info = ufbdev->helper.fbdev;
+ unregister_framebuffer(info);
+ if (info->cmap.len)
+ fb_dealloc_cmap(&info->cmap);
+ framebuffer_release(info);
+ }
+ drm_fb_helper_fini(&ufbdev->helper);
+ drm_framebuffer_cleanup(&ufbdev->ufb.base);
+ drm_gem_object_unreference_unlocked(&ufbdev->ufb.obj->base);
+}
+
+int udl_fbdev_init(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ int bpp_sel = fb_bpp;
+ struct udl_fbdev *ufbdev;
+ int ret;
+
+ ufbdev = kzalloc(sizeof(struct udl_fbdev), GFP_KERNEL);
+ if (!ufbdev)
+ return -ENOMEM;
+
+ udl->fbdev = ufbdev;
+ ufbdev->helper.funcs = &udl_fb_helper_funcs;
+
+ ret = drm_fb_helper_init(dev, &ufbdev->helper,
+ 1, 1);
+ if (ret) {
+ kfree(ufbdev);
+ return ret;
+
+ }
+
+ drm_fb_helper_single_add_all_connectors(&ufbdev->helper);
+ drm_fb_helper_initial_config(&ufbdev->helper, bpp_sel);
+ return 0;
+}
+
+void udl_fbdev_cleanup(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ if (!udl->fbdev)
+ return;
+
+ udl_fbdev_destroy(dev, udl->fbdev);
+ kfree(udl->fbdev);
+ udl->fbdev = NULL;
+}
+
+void udl_fbdev_unplug(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ struct udl_fbdev *ufbdev;
+ if (!udl->fbdev)
+ return;
+
+ ufbdev = udl->fbdev;
+ if (ufbdev->helper.fbdev) {
+ struct fb_info *info;
+ info = ufbdev->helper.fbdev;
+ unlink_framebuffer(info);
+ }
+}
+
+struct drm_framebuffer *
+udl_fb_user_fb_create(struct drm_device *dev,
+ struct drm_file *file,
+ struct drm_mode_fb_cmd2 *mode_cmd)
+{
+ struct drm_gem_object *obj;
+ struct udl_framebuffer *ufb;
+ int ret;
+
+ obj = drm_gem_object_lookup(dev, file, mode_cmd->handles[0]);
+ if (obj == NULL)
+ return ERR_PTR(-ENOENT);
+
+ ufb = kzalloc(sizeof(*ufb), GFP_KERNEL);
+ if (ufb == NULL)
+ return ERR_PTR(-ENOMEM);
+
+ ret = udl_framebuffer_init(dev, ufb, mode_cmd, to_udl_bo(obj));
+ if (ret) {
+ kfree(ufb);
+ return ERR_PTR(-EINVAL);
+ }
+ return &ufb->base;
+}
diff --git a/drivers/gpu/drm/udl/udl_gem.c b/drivers/gpu/drm/udl/udl_gem.c
new file mode 100644
index 000000000000..852642dc1187
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_gem.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "udl_drv.h"
+#include <linux/shmem_fs.h>
+
+struct udl_gem_object *udl_gem_alloc_object(struct drm_device *dev,
+ size_t size)
+{
+ struct udl_gem_object *obj;
+
+ obj = kzalloc(sizeof(*obj), GFP_KERNEL);
+ if (obj == NULL)
+ return NULL;
+
+ if (drm_gem_object_init(dev, &obj->base, size) != 0) {
+ kfree(obj);
+ return NULL;
+ }
+
+ return obj;
+}
+
+static int
+udl_gem_create(struct drm_file *file,
+ struct drm_device *dev,
+ uint64_t size,
+ uint32_t *handle_p)
+{
+ struct udl_gem_object *obj;
+ int ret;
+ u32 handle;
+
+ size = roundup(size, PAGE_SIZE);
+
+ obj = udl_gem_alloc_object(dev, size);
+ if (obj == NULL)
+ return -ENOMEM;
+
+ ret = drm_gem_handle_create(file, &obj->base, &handle);
+ if (ret) {
+ drm_gem_object_release(&obj->base);
+ kfree(obj);
+ return ret;
+ }
+
+ drm_gem_object_unreference(&obj->base);
+ *handle_p = handle;
+ return 0;
+}
+
+int udl_dumb_create(struct drm_file *file,
+ struct drm_device *dev,
+ struct drm_mode_create_dumb *args)
+{
+ args->pitch = args->width * ((args->bpp + 1) / 8);
+ args->size = args->pitch * args->height;
+ return udl_gem_create(file, dev,
+ args->size, &args->handle);
+}
+
+int udl_dumb_destroy(struct drm_file *file, struct drm_device *dev,
+ uint32_t handle)
+{
+ return drm_gem_handle_delete(file, handle);
+}
+
+int udl_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+{
+ struct udl_gem_object *obj = to_udl_bo(vma->vm_private_data);
+ struct page *page;
+ unsigned int page_offset;
+ int ret = 0;
+
+ page_offset = ((unsigned long)vmf->virtual_address - vma->vm_start) >>
+ PAGE_SHIFT;
+
+ if (!obj->pages)
+ return VM_FAULT_SIGBUS;
+
+ page = obj->pages[page_offset];
+ ret = vm_insert_page(vma, (unsigned long)vmf->virtual_address, page);
+ switch (ret) {
+ case -EAGAIN:
+ set_need_resched();
+ case 0:
+ case -ERESTARTSYS:
+ return VM_FAULT_NOPAGE;
+ case -ENOMEM:
+ return VM_FAULT_OOM;
+ default:
+ return VM_FAULT_SIGBUS;
+ }
+}
+
+int udl_gem_init_object(struct drm_gem_object *obj)
+{
+ BUG();
+
+ return 0;
+}
+
+static int udl_gem_get_pages(struct udl_gem_object *obj, gfp_t gfpmask)
+{
+ int page_count, i;
+ struct page *page;
+ struct inode *inode;
+ struct address_space *mapping;
+
+ if (obj->pages)
+ return 0;
+
+ page_count = obj->base.size / PAGE_SIZE;
+ BUG_ON(obj->pages != NULL);
+ obj->pages = drm_malloc_ab(page_count, sizeof(struct page *));
+ if (obj->pages == NULL)
+ return -ENOMEM;
+
+ inode = obj->base.filp->f_path.dentry->d_inode;
+ mapping = inode->i_mapping;
+ gfpmask |= mapping_gfp_mask(mapping);
+
+ for (i = 0; i < page_count; i++) {
+ page = shmem_read_mapping_page_gfp(mapping, i, gfpmask);
+ if (IS_ERR(page))
+ goto err_pages;
+ obj->pages[i] = page;
+ }
+
+ return 0;
+err_pages:
+ while (i--)
+ page_cache_release(obj->pages[i]);
+ drm_free_large(obj->pages);
+ obj->pages = NULL;
+ return PTR_ERR(page);
+}
+
+static void udl_gem_put_pages(struct udl_gem_object *obj)
+{
+ int page_count = obj->base.size / PAGE_SIZE;
+ int i;
+
+ for (i = 0; i < page_count; i++)
+ page_cache_release(obj->pages[i]);
+
+ drm_free_large(obj->pages);
+ obj->pages = NULL;
+}
+
+int udl_gem_vmap(struct udl_gem_object *obj)
+{
+ int page_count = obj->base.size / PAGE_SIZE;
+ int ret;
+
+ ret = udl_gem_get_pages(obj, GFP_KERNEL);
+ if (ret)
+ return ret;
+
+ obj->vmapping = vmap(obj->pages, page_count, 0, PAGE_KERNEL);
+ if (!obj->vmapping)
+ return -ENOMEM;
+ return 0;
+}
+
+void udl_gem_vunmap(struct udl_gem_object *obj)
+{
+ if (obj->vmapping)
+ vunmap(obj->vmapping);
+
+ udl_gem_put_pages(obj);
+}
+
+void udl_gem_free_object(struct drm_gem_object *gem_obj)
+{
+ struct udl_gem_object *obj = to_udl_bo(gem_obj);
+
+ if (obj->vmapping)
+ udl_gem_vunmap(obj);
+
+ if (obj->pages)
+ udl_gem_put_pages(obj);
+
+ if (gem_obj->map_list.map)
+ drm_gem_free_mmap_offset(gem_obj);
+}
+
+/* the dumb interface doesn't work with the GEM straight MMAP
+ interface, it expects to do MMAP on the drm fd, like normal */
+int udl_gem_mmap(struct drm_file *file, struct drm_device *dev,
+ uint32_t handle, uint64_t *offset)
+{
+ struct udl_gem_object *gobj;
+ struct drm_gem_object *obj;
+ int ret = 0;
+
+ mutex_lock(&dev->struct_mutex);
+ obj = drm_gem_object_lookup(dev, file, handle);
+ if (obj == NULL) {
+ ret = -ENOENT;
+ goto unlock;
+ }
+ gobj = to_udl_bo(obj);
+
+ ret = udl_gem_get_pages(gobj, GFP_KERNEL);
+ if (ret)
+ return ret;
+ if (!gobj->base.map_list.map) {
+ ret = drm_gem_create_mmap_offset(obj);
+ if (ret)
+ goto out;
+ }
+
+ *offset = (u64)gobj->base.map_list.hash.key << PAGE_SHIFT;
+
+out:
+ drm_gem_object_unreference(&gobj->base);
+unlock:
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+}
diff --git a/drivers/gpu/drm/udl/udl_main.c b/drivers/gpu/drm/udl/udl_main.c
new file mode 100644
index 000000000000..a8d5f09428c7
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_main.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+#include "drmP.h"
+#include "udl_drv.h"
+
+/* -BULK_SIZE as per usb-skeleton. Can we get full page and avoid overhead? */
+#define BULK_SIZE 512
+
+#define MAX_TRANSFER (PAGE_SIZE*16 - BULK_SIZE)
+#define WRITES_IN_FLIGHT (4)
+#define MAX_VENDOR_DESCRIPTOR_SIZE 256
+
+#define GET_URB_TIMEOUT HZ
+#define FREE_URB_TIMEOUT (HZ*2)
+
+static int udl_parse_vendor_descriptor(struct drm_device *dev,
+ struct usb_device *usbdev)
+{
+ struct udl_device *udl = dev->dev_private;
+ char *desc;
+ char *buf;
+ char *desc_end;
+
+ u8 total_len = 0;
+
+ buf = kzalloc(MAX_VENDOR_DESCRIPTOR_SIZE, GFP_KERNEL);
+ if (!buf)
+ return false;
+ desc = buf;
+
+ total_len = usb_get_descriptor(usbdev, 0x5f, /* vendor specific */
+ 0, desc, MAX_VENDOR_DESCRIPTOR_SIZE);
+ if (total_len > 5) {
+ DRM_INFO("vendor descriptor length:%x data:%02x %02x %02x %02x" \
+ "%02x %02x %02x %02x %02x %02x %02x\n",
+ total_len, desc[0],
+ desc[1], desc[2], desc[3], desc[4], desc[5], desc[6],
+ desc[7], desc[8], desc[9], desc[10]);
+
+ if ((desc[0] != total_len) || /* descriptor length */
+ (desc[1] != 0x5f) || /* vendor descriptor type */
+ (desc[2] != 0x01) || /* version (2 bytes) */
+ (desc[3] != 0x00) ||
+ (desc[4] != total_len - 2)) /* length after type */
+ goto unrecognized;
+
+ desc_end = desc + total_len;
+ desc += 5; /* the fixed header we've already parsed */
+
+ while (desc < desc_end) {
+ u8 length;
+ u16 key;
+
+ key = *((u16 *) desc);
+ desc += sizeof(u16);
+ length = *desc;
+ desc++;
+
+ switch (key) {
+ case 0x0200: { /* max_area */
+ u32 max_area;
+ max_area = le32_to_cpu(*((u32 *)desc));
+ DRM_DEBUG("DL chip limited to %d pixel modes\n",
+ max_area);
+ udl->sku_pixel_limit = max_area;
+ break;
+ }
+ default:
+ break;
+ }
+ desc += length;
+ }
+ }
+
+ goto success;
+
+unrecognized:
+ /* allow udlfb to load for now even if firmware unrecognized */
+ DRM_ERROR("Unrecognized vendor firmware descriptor\n");
+
+success:
+ kfree(buf);
+ return true;
+}
+
+static void udl_release_urb_work(struct work_struct *work)
+{
+ struct urb_node *unode = container_of(work, struct urb_node,
+ release_urb_work.work);
+
+ up(&unode->dev->urbs.limit_sem);
+}
+
+void udl_urb_completion(struct urb *urb)
+{
+ struct urb_node *unode = urb->context;
+ struct udl_device *udl = unode->dev;
+ unsigned long flags;
+
+ /* sync/async unlink faults aren't errors */
+ if (urb->status) {
+ if (!(urb->status == -ENOENT ||
+ urb->status == -ECONNRESET ||
+ urb->status == -ESHUTDOWN)) {
+ DRM_ERROR("%s - nonzero write bulk status received: %d\n",
+ __func__, urb->status);
+ atomic_set(&udl->lost_pixels, 1);
+ }
+ }
+
+ urb->transfer_buffer_length = udl->urbs.size; /* reset to actual */
+
+ spin_lock_irqsave(&udl->urbs.lock, flags);
+ list_add_tail(&unode->entry, &udl->urbs.list);
+ udl->urbs.available++;
+ spin_unlock_irqrestore(&udl->urbs.lock, flags);
+
+#if 0
+ /*
+ * When using fb_defio, we deadlock if up() is called
+ * while another is waiting. So queue to another process.
+ */
+ if (fb_defio)
+ schedule_delayed_work(&unode->release_urb_work, 0);
+ else
+#endif
+ up(&udl->urbs.limit_sem);
+}
+
+static void udl_free_urb_list(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ int count = udl->urbs.count;
+ struct list_head *node;
+ struct urb_node *unode;
+ struct urb *urb;
+ int ret;
+ unsigned long flags;
+
+ DRM_DEBUG("Waiting for completes and freeing all render urbs\n");
+
+ /* keep waiting and freeing, until we've got 'em all */
+ while (count--) {
+
+ /* Getting interrupted means a leak, but ok at shutdown*/
+ ret = down_interruptible(&udl->urbs.limit_sem);
+ if (ret)
+ break;
+
+ spin_lock_irqsave(&udl->urbs.lock, flags);
+
+ node = udl->urbs.list.next; /* have reserved one with sem */
+ list_del_init(node);
+
+ spin_unlock_irqrestore(&udl->urbs.lock, flags);
+
+ unode = list_entry(node, struct urb_node, entry);
+ urb = unode->urb;
+
+ /* Free each separately allocated piece */
+ usb_free_coherent(urb->dev, udl->urbs.size,
+ urb->transfer_buffer, urb->transfer_dma);
+ usb_free_urb(urb);
+ kfree(node);
+ }
+ udl->urbs.count = 0;
+}
+
+static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size)
+{
+ struct udl_device *udl = dev->dev_private;
+ int i = 0;
+ struct urb *urb;
+ struct urb_node *unode;
+ char *buf;
+
+ spin_lock_init(&udl->urbs.lock);
+
+ udl->urbs.size = size;
+ INIT_LIST_HEAD(&udl->urbs.list);
+
+ while (i < count) {
+ unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL);
+ if (!unode)
+ break;
+ unode->dev = udl;
+
+ INIT_DELAYED_WORK(&unode->release_urb_work,
+ udl_release_urb_work);
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ kfree(unode);
+ break;
+ }
+ unode->urb = urb;
+
+ buf = usb_alloc_coherent(udl->ddev->usbdev, MAX_TRANSFER, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ kfree(unode);
+ usb_free_urb(urb);
+ break;
+ }
+
+ /* urb->transfer_buffer_length set to actual before submit */
+ usb_fill_bulk_urb(urb, udl->ddev->usbdev, usb_sndbulkpipe(udl->ddev->usbdev, 1),
+ buf, size, udl_urb_completion, unode);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ list_add_tail(&unode->entry, &udl->urbs.list);
+
+ i++;
+ }
+
+ sema_init(&udl->urbs.limit_sem, i);
+ udl->urbs.count = i;
+ udl->urbs.available = i;
+
+ DRM_DEBUG("allocated %d %d byte urbs\n", i, (int) size);
+
+ return i;
+}
+
+struct urb *udl_get_urb(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+ int ret = 0;
+ struct list_head *entry;
+ struct urb_node *unode;
+ struct urb *urb = NULL;
+ unsigned long flags;
+
+ /* Wait for an in-flight buffer to complete and get re-queued */
+ ret = down_timeout(&udl->urbs.limit_sem, GET_URB_TIMEOUT);
+ if (ret) {
+ atomic_set(&udl->lost_pixels, 1);
+ DRM_INFO("wait for urb interrupted: %x available: %d\n",
+ ret, udl->urbs.available);
+ goto error;
+ }
+
+ spin_lock_irqsave(&udl->urbs.lock, flags);
+
+ BUG_ON(list_empty(&udl->urbs.list)); /* reserved one with limit_sem */
+ entry = udl->urbs.list.next;
+ list_del_init(entry);
+ udl->urbs.available--;
+
+ spin_unlock_irqrestore(&udl->urbs.lock, flags);
+
+ unode = list_entry(entry, struct urb_node, entry);
+ urb = unode->urb;
+
+error:
+ return urb;
+}
+
+int udl_submit_urb(struct drm_device *dev, struct urb *urb, size_t len)
+{
+ struct udl_device *udl = dev->dev_private;
+ int ret;
+
+ BUG_ON(len > udl->urbs.size);
+
+ urb->transfer_buffer_length = len; /* set to actual payload len */
+ ret = usb_submit_urb(urb, GFP_ATOMIC);
+ if (ret) {
+ udl_urb_completion(urb); /* because no one else will */
+ atomic_set(&udl->lost_pixels, 1);
+ DRM_ERROR("usb_submit_urb error %x\n", ret);
+ }
+ return ret;
+}
+
+int udl_driver_load(struct drm_device *dev, unsigned long flags)
+{
+ struct udl_device *udl;
+ int ret;
+
+ DRM_DEBUG("\n");
+ udl = kzalloc(sizeof(struct udl_device), GFP_KERNEL);
+ if (!udl)
+ return -ENOMEM;
+
+ udl->ddev = dev;
+ dev->dev_private = udl;
+
+ if (!udl_parse_vendor_descriptor(dev, dev->usbdev)) {
+ DRM_ERROR("firmware not recognized. Assume incompatible device\n");
+ goto err;
+ }
+
+ if (!udl_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
+ ret = -ENOMEM;
+ DRM_ERROR("udl_alloc_urb_list failed\n");
+ goto err;
+ }
+
+ DRM_DEBUG("\n");
+ ret = udl_modeset_init(dev);
+
+ ret = udl_fbdev_init(dev);
+ return 0;
+err:
+ kfree(udl);
+ DRM_ERROR("%d\n", ret);
+ return ret;
+}
+
+int udl_drop_usb(struct drm_device *dev)
+{
+ udl_free_urb_list(dev);
+ return 0;
+}
+
+int udl_driver_unload(struct drm_device *dev)
+{
+ struct udl_device *udl = dev->dev_private;
+
+ if (udl->urbs.count)
+ udl_free_urb_list(dev);
+
+ udl_fbdev_cleanup(dev);
+ udl_modeset_cleanup(dev);
+ kfree(udl);
+ return 0;
+}
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
new file mode 100644
index 000000000000..b3ecb3d12a1d
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -0,0 +1,414 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ *
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include "drmP.h"
+#include "drm_crtc.h"
+#include "drm_crtc_helper.h"
+#include "udl_drv.h"
+
+/*
+ * All DisplayLink bulk operations start with 0xAF, followed by specific code
+ * All operations are written to buffers which then later get sent to device
+ */
+static char *udl_set_register(char *buf, u8 reg, u8 val)
+{
+ *buf++ = 0xAF;
+ *buf++ = 0x20;
+ *buf++ = reg;
+ *buf++ = val;
+ return buf;
+}
+
+static char *udl_vidreg_lock(char *buf)
+{
+ return udl_set_register(buf, 0xFF, 0x00);
+}
+
+static char *udl_vidreg_unlock(char *buf)
+{
+ return udl_set_register(buf, 0xFF, 0xFF);
+}
+
+/*
+ * On/Off for driving the DisplayLink framebuffer to the display
+ * 0x00 H and V sync on
+ * 0x01 H and V sync off (screen blank but powered)
+ * 0x07 DPMS powerdown (requires modeset to come back)
+ */
+static char *udl_enable_hvsync(char *buf, bool enable)
+{
+ if (enable)
+ return udl_set_register(buf, 0x1F, 0x00);
+ else
+ return udl_set_register(buf, 0x1F, 0x07);
+}
+
+static char *udl_set_color_depth(char *buf, u8 selection)
+{
+ return udl_set_register(buf, 0x00, selection);
+}
+
+static char *udl_set_base16bpp(char *wrptr, u32 base)
+{
+ /* the base pointer is 16 bits wide, 0x20 is hi byte. */
+ wrptr = udl_set_register(wrptr, 0x20, base >> 16);
+ wrptr = udl_set_register(wrptr, 0x21, base >> 8);
+ return udl_set_register(wrptr, 0x22, base);
+}
+
+/*
+ * DisplayLink HW has separate 16bpp and 8bpp framebuffers.
+ * In 24bpp modes, the low 323 RGB bits go in the 8bpp framebuffer
+ */
+static char *udl_set_base8bpp(char *wrptr, u32 base)
+{
+ wrptr = udl_set_register(wrptr, 0x26, base >> 16);
+ wrptr = udl_set_register(wrptr, 0x27, base >> 8);
+ return udl_set_register(wrptr, 0x28, base);
+}
+
+static char *udl_set_register_16(char *wrptr, u8 reg, u16 value)
+{
+ wrptr = udl_set_register(wrptr, reg, value >> 8);
+ return udl_set_register(wrptr, reg+1, value);
+}
+
+/*
+ * This is kind of weird because the controller takes some
+ * register values in a different byte order than other registers.
+ */
+static char *udl_set_register_16be(char *wrptr, u8 reg, u16 value)
+{
+ wrptr = udl_set_register(wrptr, reg, value);
+ return udl_set_register(wrptr, reg+1, value >> 8);
+}
+
+/*
+ * LFSR is linear feedback shift register. The reason we have this is
+ * because the display controller needs to minimize the clock depth of
+ * various counters used in the display path. So this code reverses the
+ * provided value into the lfsr16 value by counting backwards to get
+ * the value that needs to be set in the hardware comparator to get the
+ * same actual count. This makes sense once you read above a couple of
+ * times and think about it from a hardware perspective.
+ */
+static u16 udl_lfsr16(u16 actual_count)
+{
+ u32 lv = 0xFFFF; /* This is the lfsr value that the hw starts with */
+
+ while (actual_count--) {
+ lv = ((lv << 1) |
+ (((lv >> 15) ^ (lv >> 4) ^ (lv >> 2) ^ (lv >> 1)) & 1))
+ & 0xFFFF;
+ }
+
+ return (u16) lv;
+}
+
+/*
+ * This does LFSR conversion on the value that is to be written.
+ * See LFSR explanation above for more detail.
+ */
+static char *udl_set_register_lfsr16(char *wrptr, u8 reg, u16 value)
+{
+ return udl_set_register_16(wrptr, reg, udl_lfsr16(value));
+}
+
+/*
+ * This takes a standard fbdev screeninfo struct and all of its monitor mode
+ * details and converts them into the DisplayLink equivalent register commands.
+ ERR(vreg(dev, 0x00, (color_depth == 16) ? 0 : 1));
+ ERR(vreg_lfsr16(dev, 0x01, xDisplayStart));
+ ERR(vreg_lfsr16(dev, 0x03, xDisplayEnd));
+ ERR(vreg_lfsr16(dev, 0x05, yDisplayStart));
+ ERR(vreg_lfsr16(dev, 0x07, yDisplayEnd));
+ ERR(vreg_lfsr16(dev, 0x09, xEndCount));
+ ERR(vreg_lfsr16(dev, 0x0B, hSyncStart));
+ ERR(vreg_lfsr16(dev, 0x0D, hSyncEnd));
+ ERR(vreg_big_endian(dev, 0x0F, hPixels));
+ ERR(vreg_lfsr16(dev, 0x11, yEndCount));
+ ERR(vreg_lfsr16(dev, 0x13, vSyncStart));
+ ERR(vreg_lfsr16(dev, 0x15, vSyncEnd));
+ ERR(vreg_big_endian(dev, 0x17, vPixels));
+ ERR(vreg_little_endian(dev, 0x1B, pixelClock5KHz));
+
+ ERR(vreg(dev, 0x1F, 0));
+
+ ERR(vbuf(dev, WRITE_VIDREG_UNLOCK, DSIZEOF(WRITE_VIDREG_UNLOCK)));
+ */
+static char *udl_set_vid_cmds(char *wrptr, struct drm_display_mode *mode)
+{
+ u16 xds, yds;
+ u16 xde, yde;
+ u16 yec;
+
+ /* x display start */
+ xds = mode->crtc_htotal - mode->crtc_hsync_start;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x01, xds);
+ /* x display end */
+ xde = xds + mode->crtc_hdisplay;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x03, xde);
+
+ /* y display start */
+ yds = mode->crtc_vtotal - mode->crtc_vsync_start;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x05, yds);
+ /* y display end */
+ yde = yds + mode->crtc_vdisplay;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x07, yde);
+
+ /* x end count is active + blanking - 1 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x09,
+ mode->crtc_htotal - 1);
+
+ /* libdlo hardcodes hsync start to 1 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x0B, 1);
+
+ /* hsync end is width of sync pulse + 1 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x0D,
+ mode->crtc_hsync_end - mode->crtc_hsync_start + 1);
+
+ /* hpixels is active pixels */
+ wrptr = udl_set_register_16(wrptr, 0x0F, mode->hdisplay);
+
+ /* yendcount is vertical active + vertical blanking */
+ yec = mode->crtc_vtotal;
+ wrptr = udl_set_register_lfsr16(wrptr, 0x11, yec);
+
+ /* libdlo hardcodes vsync start to 0 */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x13, 0);
+
+ /* vsync end is width of vsync pulse */
+ wrptr = udl_set_register_lfsr16(wrptr, 0x15, mode->crtc_vsync_end - mode->crtc_vsync_start);
+
+ /* vpixels is active pixels */
+ wrptr = udl_set_register_16(wrptr, 0x17, mode->crtc_vdisplay);
+
+ wrptr = udl_set_register_16be(wrptr, 0x1B,
+ mode->clock / 5);
+
+ return wrptr;
+}
+
+static int udl_crtc_write_mode_to_hw(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct udl_device *udl = dev->dev_private;
+ struct urb *urb;
+ char *buf;
+ int retval;
+
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return -ENOMEM;
+
+ buf = (char *)urb->transfer_buffer;
+
+ memcpy(buf, udl->mode_buf, udl->mode_buf_len);
+ retval = udl_submit_urb(dev, urb, udl->mode_buf_len);
+ DRM_INFO("write mode info %d\n", udl->mode_buf_len);
+ return retval;
+}
+
+
+static void udl_crtc_dpms(struct drm_crtc *crtc, int mode)
+{
+ struct drm_device *dev = crtc->dev;
+ struct udl_device *udl = dev->dev_private;
+ int retval;
+
+ if (mode == DRM_MODE_DPMS_OFF) {
+ char *buf;
+ struct urb *urb;
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return;
+
+ buf = (char *)urb->transfer_buffer;
+ buf = udl_vidreg_lock(buf);
+ buf = udl_enable_hvsync(buf, false);
+ buf = udl_vidreg_unlock(buf);
+
+ retval = udl_submit_urb(dev, urb, buf - (char *)
+ urb->transfer_buffer);
+ } else {
+ if (udl->mode_buf_len == 0) {
+ DRM_ERROR("Trying to enable DPMS with no mode\n");
+ return;
+ }
+ udl_crtc_write_mode_to_hw(crtc);
+ }
+
+}
+
+static bool udl_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+
+{
+ return true;
+}
+
+#if 0
+static int
+udl_pipe_set_base_atomic(struct drm_crtc *crtc, struct drm_framebuffer *fb,
+ int x, int y, enum mode_set_atomic state)
+{
+ return 0;
+}
+
+static int
+udl_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+{
+ return 0;
+}
+#endif
+
+static int udl_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+ int x, int y,
+ struct drm_framebuffer *old_fb)
+
+{
+ struct drm_device *dev = crtc->dev;
+ struct udl_framebuffer *ufb = to_udl_fb(crtc->fb);
+ struct udl_device *udl = dev->dev_private;
+ char *buf;
+ char *wrptr;
+ int color_depth = 0;
+
+ buf = (char *)udl->mode_buf;
+
+ /* for now we just clip 24 -> 16 - if we fix that fix this */
+ /*if (crtc->fb->bits_per_pixel != 16)
+ color_depth = 1; */
+
+ /* This first section has to do with setting the base address on the
+ * controller * associated with the display. There are 2 base
+ * pointers, currently, we only * use the 16 bpp segment.
+ */
+ wrptr = udl_vidreg_lock(buf);
+ wrptr = udl_set_color_depth(wrptr, color_depth);
+ /* set base for 16bpp segment to 0 */
+ wrptr = udl_set_base16bpp(wrptr, 0);
+ /* set base for 8bpp segment to end of fb */
+ wrptr = udl_set_base8bpp(wrptr, 2 * mode->vdisplay * mode->hdisplay);
+
+ wrptr = udl_set_vid_cmds(wrptr, adjusted_mode);
+ wrptr = udl_enable_hvsync(wrptr, true);
+ wrptr = udl_vidreg_unlock(wrptr);
+
+ ufb->active_16 = true;
+ if (old_fb) {
+ struct udl_framebuffer *uold_fb = to_udl_fb(old_fb);
+ uold_fb->active_16 = false;
+ }
+ udl->mode_buf_len = wrptr - buf;
+
+ /* damage all of it */
+ udl_handle_damage(ufb, 0, 0, ufb->base.width, ufb->base.height);
+ return 0;
+}
+
+
+static void udl_crtc_disable(struct drm_crtc *crtc)
+{
+
+
+}
+
+static void udl_crtc_destroy(struct drm_crtc *crtc)
+{
+ drm_crtc_cleanup(crtc);
+ kfree(crtc);
+}
+
+static void udl_load_lut(struct drm_crtc *crtc)
+{
+}
+
+static void udl_crtc_prepare(struct drm_crtc *crtc)
+{
+}
+
+static void udl_crtc_commit(struct drm_crtc *crtc)
+{
+ udl_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+}
+
+static struct drm_crtc_helper_funcs udl_helper_funcs = {
+ .dpms = udl_crtc_dpms,
+ .mode_fixup = udl_crtc_mode_fixup,
+ .mode_set = udl_crtc_mode_set,
+ .prepare = udl_crtc_prepare,
+ .commit = udl_crtc_commit,
+ .disable = udl_crtc_disable,
+ .load_lut = udl_load_lut,
+};
+
+static const struct drm_crtc_funcs udl_crtc_funcs = {
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = udl_crtc_destroy,
+};
+
+int udl_crtc_init(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+
+ crtc = kzalloc(sizeof(struct drm_crtc) + sizeof(struct drm_connector *), GFP_KERNEL);
+ if (crtc == NULL)
+ return -ENOMEM;
+
+ drm_crtc_init(dev, crtc, &udl_crtc_funcs);
+ drm_crtc_helper_add(crtc, &udl_helper_funcs);
+
+ return 0;
+}
+
+static const struct drm_mode_config_funcs udl_mode_funcs = {
+ .fb_create = udl_fb_user_fb_create,
+ .output_poll_changed = NULL,
+};
+
+int udl_modeset_init(struct drm_device *dev)
+{
+ struct drm_encoder *encoder;
+ drm_mode_config_init(dev);
+
+ dev->mode_config.min_width = 640;
+ dev->mode_config.min_height = 480;
+
+ dev->mode_config.max_width = 2048;
+ dev->mode_config.max_height = 2048;
+
+ dev->mode_config.prefer_shadow = 0;
+ dev->mode_config.preferred_depth = 24;
+
+ dev->mode_config.funcs = (void *)&udl_mode_funcs;
+
+ drm_mode_create_dirty_info_property(dev);
+
+ udl_crtc_init(dev);
+
+ encoder = udl_encoder_init(dev);
+
+ udl_connector_init(dev, encoder);
+
+ return 0;
+}
+
+void udl_modeset_cleanup(struct drm_device *dev)
+{
+ drm_mode_config_cleanup(dev);
+}
diff --git a/drivers/gpu/drm/udl/udl_transfer.c b/drivers/gpu/drm/udl/udl_transfer.c
new file mode 100644
index 000000000000..b9320e2608dd
--- /dev/null
+++ b/drivers/gpu/drm/udl/udl_transfer.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2012 Red Hat
+ * based in parts on udlfb.c:
+ * Copyright (C) 2009 Roberto De Ioris <roberto@unbit.it>
+ * Copyright (C) 2009 Jaya Kumar <jayakumar.lkml@gmail.com>
+ * Copyright (C) 2009 Bernie Thompson <bernie@plugable.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License v2. See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/fb.h>
+#include <linux/prefetch.h>
+
+#include "drmP.h"
+#include "udl_drv.h"
+
+#define MAX_CMD_PIXELS 255
+
+#define RLX_HEADER_BYTES 7
+#define MIN_RLX_PIX_BYTES 4
+#define MIN_RLX_CMD_BYTES (RLX_HEADER_BYTES + MIN_RLX_PIX_BYTES)
+
+#define RLE_HEADER_BYTES 6
+#define MIN_RLE_PIX_BYTES 3
+#define MIN_RLE_CMD_BYTES (RLE_HEADER_BYTES + MIN_RLE_PIX_BYTES)
+
+#define RAW_HEADER_BYTES 6
+#define MIN_RAW_PIX_BYTES 2
+#define MIN_RAW_CMD_BYTES (RAW_HEADER_BYTES + MIN_RAW_PIX_BYTES)
+
+/*
+ * Trims identical data from front and back of line
+ * Sets new front buffer address and width
+ * And returns byte count of identical pixels
+ * Assumes CPU natural alignment (unsigned long)
+ * for back and front buffer ptrs and width
+ */
+#if 0
+static int udl_trim_hline(const u8 *bback, const u8 **bfront, int *width_bytes)
+{
+ int j, k;
+ const unsigned long *back = (const unsigned long *) bback;
+ const unsigned long *front = (const unsigned long *) *bfront;
+ const int width = *width_bytes / sizeof(unsigned long);
+ int identical = width;
+ int start = width;
+ int end = width;
+
+ prefetch((void *) front);
+ prefetch((void *) back);
+
+ for (j = 0; j < width; j++) {
+ if (back[j] != front[j]) {
+ start = j;
+ break;
+ }
+ }
+
+ for (k = width - 1; k > j; k--) {
+ if (back[k] != front[k]) {
+ end = k+1;
+ break;
+ }
+ }
+
+ identical = start + (width - end);
+ *bfront = (u8 *) &front[start];
+ *width_bytes = (end - start) * sizeof(unsigned long);
+
+ return identical * sizeof(unsigned long);
+}
+#endif
+
+static inline u16 pixel32_to_be16p(const uint8_t *pixel)
+{
+ uint32_t pix = *(uint32_t *)pixel;
+ u16 retval;
+
+ retval = (((pix >> 3) & 0x001f) |
+ ((pix >> 5) & 0x07e0) |
+ ((pix >> 8) & 0xf800));
+ return retval;
+}
+
+/*
+ * Render a command stream for an encoded horizontal line segment of pixels.
+ *
+ * A command buffer holds several commands.
+ * It always begins with a fresh command header
+ * (the protocol doesn't require this, but we enforce it to allow
+ * multiple buffers to be potentially encoded and sent in parallel).
+ * A single command encodes one contiguous horizontal line of pixels
+ *
+ * The function relies on the client to do all allocation, so that
+ * rendering can be done directly to output buffers (e.g. USB URBs).
+ * The function fills the supplied command buffer, providing information
+ * on where it left off, so the client may call in again with additional
+ * buffers if the line will take several buffers to complete.
+ *
+ * A single command can transmit a maximum of 256 pixels,
+ * regardless of the compression ratio (protocol design limit).
+ * To the hardware, 0 for a size byte means 256
+ *
+ * Rather than 256 pixel commands which are either rl or raw encoded,
+ * the rlx command simply assumes alternating raw and rl spans within one cmd.
+ * This has a slightly larger header overhead, but produces more even results.
+ * It also processes all data (read and write) in a single pass.
+ * Performance benchmarks of common cases show it having just slightly better
+ * compression than 256 pixel raw or rle commands, with similar CPU consumpion.
+ * But for very rl friendly data, will compress not quite as well.
+ */
+static void udl_compress_hline16(
+ const u8 **pixel_start_ptr,
+ const u8 *const pixel_end,
+ uint32_t *device_address_ptr,
+ uint8_t **command_buffer_ptr,
+ const uint8_t *const cmd_buffer_end, int bpp)
+{
+ const u8 *pixel = *pixel_start_ptr;
+ uint32_t dev_addr = *device_address_ptr;
+ uint8_t *cmd = *command_buffer_ptr;
+
+ while ((pixel_end > pixel) &&
+ (cmd_buffer_end - MIN_RLX_CMD_BYTES > cmd)) {
+ uint8_t *raw_pixels_count_byte = 0;
+ uint8_t *cmd_pixels_count_byte = 0;
+ const u8 *raw_pixel_start = 0;
+ const u8 *cmd_pixel_start, *cmd_pixel_end = 0;
+
+ prefetchw((void *) cmd); /* pull in one cache line at least */
+
+ *cmd++ = 0xaf;
+ *cmd++ = 0x6b;
+ *cmd++ = (uint8_t) ((dev_addr >> 16) & 0xFF);
+ *cmd++ = (uint8_t) ((dev_addr >> 8) & 0xFF);
+ *cmd++ = (uint8_t) ((dev_addr) & 0xFF);
+
+ cmd_pixels_count_byte = cmd++; /* we'll know this later */
+ cmd_pixel_start = pixel;
+
+ raw_pixels_count_byte = cmd++; /* we'll know this later */
+ raw_pixel_start = pixel;
+
+ cmd_pixel_end = pixel + (min(MAX_CMD_PIXELS + 1,
+ min((int)(pixel_end - pixel) / bpp,
+ (int)(cmd_buffer_end - cmd) / 2))) * bpp;
+
+ prefetch_range((void *) pixel, (cmd_pixel_end - pixel) * bpp);
+
+ while (pixel < cmd_pixel_end) {
+ const u8 * const repeating_pixel = pixel;
+
+ if (bpp == 2)
+ *(uint16_t *)cmd = cpu_to_be16p((uint16_t *)pixel);
+ else if (bpp == 4)
+ *(uint16_t *)cmd = cpu_to_be16(pixel32_to_be16p(pixel));
+
+ cmd += 2;
+ pixel += bpp;
+
+ if (unlikely((pixel < cmd_pixel_end) &&
+ (!memcmp(pixel, repeating_pixel, bpp)))) {
+ /* go back and fill in raw pixel count */
+ *raw_pixels_count_byte = (((repeating_pixel -
+ raw_pixel_start) / bpp) + 1) & 0xFF;
+
+ while ((pixel < cmd_pixel_end)
+ && (!memcmp(pixel, repeating_pixel, bpp))) {
+ pixel += bpp;
+ }
+
+ /* immediately after raw data is repeat byte */
+ *cmd++ = (((pixel - repeating_pixel) / bpp) - 1) & 0xFF;
+
+ /* Then start another raw pixel span */
+ raw_pixel_start = pixel;
+ raw_pixels_count_byte = cmd++;
+ }
+ }
+
+ if (pixel > raw_pixel_start) {
+ /* finalize last RAW span */
+ *raw_pixels_count_byte = ((pixel-raw_pixel_start) / bpp) & 0xFF;
+ }
+
+ *cmd_pixels_count_byte = ((pixel - cmd_pixel_start) / bpp) & 0xFF;
+ dev_addr += ((pixel - cmd_pixel_start) / bpp) * 2;
+ }
+
+ if (cmd_buffer_end <= MIN_RLX_CMD_BYTES + cmd) {
+ /* Fill leftover bytes with no-ops */
+ if (cmd_buffer_end > cmd)
+ memset(cmd, 0xAF, cmd_buffer_end - cmd);
+ cmd = (uint8_t *) cmd_buffer_end;
+ }
+
+ *command_buffer_ptr = cmd;
+ *pixel_start_ptr = pixel;
+ *device_address_ptr = dev_addr;
+
+ return;
+}
+
+/*
+ * There are 3 copies of every pixel: The front buffer that the fbdev
+ * client renders to, the actual framebuffer across the USB bus in hardware
+ * (that we can only write to, slowly, and can never read), and (optionally)
+ * our shadow copy that tracks what's been sent to that hardware buffer.
+ */
+int udl_render_hline(struct drm_device *dev, int bpp, struct urb **urb_ptr,
+ const char *front, char **urb_buf_ptr,
+ u32 byte_offset, u32 byte_width,
+ int *ident_ptr, int *sent_ptr)
+{
+ const u8 *line_start, *line_end, *next_pixel;
+ u32 base16 = 0 + (byte_offset / bpp) * 2;
+ struct urb *urb = *urb_ptr;
+ u8 *cmd = *urb_buf_ptr;
+ u8 *cmd_end = (u8 *) urb->transfer_buffer + urb->transfer_buffer_length;
+
+ line_start = (u8 *) (front + byte_offset);
+ next_pixel = line_start;
+ line_end = next_pixel + byte_width;
+
+ while (next_pixel < line_end) {
+
+ udl_compress_hline16(&next_pixel,
+ line_end, &base16,
+ (u8 **) &cmd, (u8 *) cmd_end, bpp);
+
+ if (cmd >= cmd_end) {
+ int len = cmd - (u8 *) urb->transfer_buffer;
+ if (udl_submit_urb(dev, urb, len))
+ return 1; /* lost pixels is set */
+ *sent_ptr += len;
+ urb = udl_get_urb(dev);
+ if (!urb)
+ return 1; /* lost_pixels is set */
+ *urb_ptr = urb;
+ cmd = urb->transfer_buffer;
+ cmd_end = &cmd[urb->transfer_buffer_length];
+ }
+ }
+
+ *urb_buf_ptr = cmd;
+
+ return 0;
+}
+