Merge branch 'drm-core-next' of git://people.freedesktop.org/~airlied/linux
* 'drm-core-next' of git://people.freedesktop.org/~airlied/linux: (307 commits) drm/nouveau/pm: fix build with HWMON off gma500: silence gcc warnings in mid_get_vbt_data() drm/ttm: fix condition (and vs or) drm/radeon: double lock typo in radeon_vm_bo_rmv() drm/radeon: use after free in radeon_vm_bo_add() drm/sis|via: don't return stack garbage from free_mem ioctl drm/radeon/kms: remove pointless CS flags priority struct drm/radeon/kms: check if vm is supported in VA ioctl drm: introduce drm_can_sleep and use in intel/radeon drivers. (v2) radeon: Fix disabling PCI bus mastering on big endian hosts. ttm: fix agp since ttm tt rework agp: Fix multi-line warning message whitespace drm/ttm/dma: Fix accounting error when calling ttm_mem_global_free_page and don't try to free freed pages. drm/ttm/dma: Only call set_pages_array_wb when the page is not in WB pool. drm/radeon/kms: sync across multiple rings when doing bo moves v3 drm/radeon/kms: Add support for multi-ring sync in CS ioctl (v2) drm/radeon: GPU virtual memory support v22 drm: make DRM_UNLOCKED ioctls with their own mutex drm: no need to hold global mutex for static data drm/radeon/benchmark: common modes sweep ignores 640x480@32 ... Fix up trivial conflicts in radeon/evergreen.c and vmwgfx/vmwgfx_kms.c
This commit is contained in:
commit
1a464cbb3d
|
@ -514,12 +514,12 @@ static void agp_v2_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_
|
|||
switch (*bridge_agpstat & 7) {
|
||||
case 4:
|
||||
*bridge_agpstat |= (AGPSTAT2_2X | AGPSTAT2_1X);
|
||||
printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate"
|
||||
printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x4 rate. "
|
||||
"Fixing up support for x2 & x1\n");
|
||||
break;
|
||||
case 2:
|
||||
*bridge_agpstat |= AGPSTAT2_1X;
|
||||
printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate"
|
||||
printk(KERN_INFO PFX "BIOS bug. AGP bridge claims to only support x2 rate. "
|
||||
"Fixing up support for x1\n");
|
||||
break;
|
||||
default:
|
||||
|
@ -693,7 +693,7 @@ static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_
|
|||
*bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
|
||||
*vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
|
||||
} else {
|
||||
printk(KERN_INFO PFX "Fell back to AGPx4 mode because");
|
||||
printk(KERN_INFO PFX "Fell back to AGPx4 mode because ");
|
||||
if (!(*bridge_agpstat & AGPSTAT3_8X)) {
|
||||
printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n",
|
||||
*bridge_agpstat, origbridge);
|
||||
|
@ -956,7 +956,7 @@ int agp_generic_create_gatt_table(struct agp_bridge_data *bridge)
|
|||
bridge->driver->cache_flush();
|
||||
#ifdef CONFIG_X86
|
||||
if (set_memory_uc((unsigned long)table, 1 << page_order))
|
||||
printk(KERN_WARNING "Could not set GATT table memory to UC!");
|
||||
printk(KERN_WARNING "Could not set GATT table memory to UC!\n");
|
||||
|
||||
bridge->gatt_table = (void *)table;
|
||||
#else
|
||||
|
|
|
@ -162,3 +162,6 @@ config DRM_SAVAGE
|
|||
source "drivers/gpu/drm/exynos/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/vmwgfx/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/gma500/Kconfig"
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
|
|||
drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \
|
||||
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
|
||||
drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
|
||||
drm_platform.o drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.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
|
||||
|
@ -36,4 +36,5 @@ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
|
|||
obj-$(CONFIG_DRM_VIA) +=via/
|
||||
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
|
||||
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
||||
obj-$(CONFIG_DRM_GMA500) += gma500/
|
||||
obj-y += i2c/
|
||||
|
|
|
@ -154,8 +154,6 @@ int drm_getsareactx(struct drm_device *dev, void *data,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
request->handle = NULL;
|
||||
list_for_each_entry(_entry, &dev->maplist, head) {
|
||||
if (_entry->map == map) {
|
||||
|
@ -164,6 +162,9 @@ int drm_getsareactx(struct drm_device *dev, void *data,
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (request->handle == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "drmP.h"
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_edid.h"
|
||||
#include "drm_fourcc.h"
|
||||
|
||||
struct drm_prop_enum_list {
|
||||
int type;
|
||||
|
@ -324,6 +325,7 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
|
|||
{
|
||||
struct drm_device *dev = fb->dev;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_plane *plane;
|
||||
struct drm_mode_set set;
|
||||
int ret;
|
||||
|
||||
|
@ -340,6 +342,18 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
|
|||
}
|
||||
}
|
||||
|
||||
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
|
||||
if (plane->fb == fb) {
|
||||
/* should turn off the crtc */
|
||||
ret = plane->funcs->disable_plane(plane);
|
||||
if (ret)
|
||||
DRM_ERROR("failed to disable plane with busy fb\n");
|
||||
/* disconnect the plane from the fb and crtc: */
|
||||
plane->fb = NULL;
|
||||
plane->crtc = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
drm_mode_object_put(dev, &fb->base);
|
||||
list_del(&fb->head);
|
||||
dev->mode_config.num_fb--;
|
||||
|
@ -540,6 +554,63 @@ void drm_encoder_cleanup(struct drm_encoder *encoder)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_encoder_cleanup);
|
||||
|
||||
int drm_plane_init(struct drm_device *dev, struct drm_plane *plane,
|
||||
unsigned long possible_crtcs,
|
||||
const struct drm_plane_funcs *funcs,
|
||||
const uint32_t *formats, uint32_t format_count,
|
||||
bool priv)
|
||||
{
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
memcpy(plane->format_types, formats, format_count * sizeof(uint32_t));
|
||||
plane->format_count = format_count;
|
||||
plane->possible_crtcs = possible_crtcs;
|
||||
|
||||
/* private planes are not exposed to userspace, but depending on
|
||||
* display hardware, might be convenient to allow sharing programming
|
||||
* for the scanout engine with the crtc implementation.
|
||||
*/
|
||||
if (!priv) {
|
||||
list_add_tail(&plane->head, &dev->mode_config.plane_list);
|
||||
dev->mode_config.num_plane++;
|
||||
} else {
|
||||
INIT_LIST_HEAD(&plane->head);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_plane_init);
|
||||
|
||||
void drm_plane_cleanup(struct drm_plane *plane)
|
||||
{
|
||||
struct drm_device *dev = plane->dev;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
kfree(plane->format_types);
|
||||
drm_mode_object_put(dev, &plane->base);
|
||||
/* if not added to a list, it must be a private plane */
|
||||
if (!list_empty(&plane->head)) {
|
||||
list_del(&plane->head);
|
||||
dev->mode_config.num_plane--;
|
||||
}
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_plane_cleanup);
|
||||
|
||||
/**
|
||||
* drm_mode_create - create a new display mode
|
||||
* @dev: DRM device
|
||||
|
@ -871,6 +942,7 @@ void drm_mode_config_init(struct drm_device *dev)
|
|||
INIT_LIST_HEAD(&dev->mode_config.encoder_list);
|
||||
INIT_LIST_HEAD(&dev->mode_config.property_list);
|
||||
INIT_LIST_HEAD(&dev->mode_config.property_blob_list);
|
||||
INIT_LIST_HEAD(&dev->mode_config.plane_list);
|
||||
idr_init(&dev->mode_config.crtc_idr);
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
@ -947,6 +1019,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
|
|||
struct drm_encoder *encoder, *enct;
|
||||
struct drm_framebuffer *fb, *fbt;
|
||||
struct drm_property *property, *pt;
|
||||
struct drm_plane *plane, *plt;
|
||||
|
||||
list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list,
|
||||
head) {
|
||||
|
@ -971,6 +1044,10 @@ void drm_mode_config_cleanup(struct drm_device *dev)
|
|||
crtc->funcs->destroy(crtc);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
|
||||
head) {
|
||||
plane->funcs->destroy(plane);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_config_cleanup);
|
||||
|
||||
|
@ -1379,7 +1456,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
|
|||
*/
|
||||
if ((out_resp->count_modes >= mode_count) && mode_count) {
|
||||
copied = 0;
|
||||
mode_ptr = (struct drm_mode_modeinfo *)(unsigned long)out_resp->modes_ptr;
|
||||
mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr;
|
||||
list_for_each_entry(mode, &connector->modes, head) {
|
||||
drm_crtc_convert_to_umode(&u_mode, mode);
|
||||
if (copy_to_user(mode_ptr + copied,
|
||||
|
@ -1394,8 +1471,8 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
|
|||
|
||||
if ((out_resp->count_props >= props_count) && props_count) {
|
||||
copied = 0;
|
||||
prop_ptr = (uint32_t *)(unsigned long)(out_resp->props_ptr);
|
||||
prop_values = (uint64_t *)(unsigned long)(out_resp->prop_values_ptr);
|
||||
prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr);
|
||||
prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr);
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_PROPERTY; i++) {
|
||||
if (connector->property_ids[i] != 0) {
|
||||
if (put_user(connector->property_ids[i],
|
||||
|
@ -1417,7 +1494,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data,
|
|||
|
||||
if ((out_resp->count_encoders >= encoders_count) && encoders_count) {
|
||||
copied = 0;
|
||||
encoder_ptr = (uint32_t *)(unsigned long)(out_resp->encoders_ptr);
|
||||
encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr);
|
||||
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
|
||||
if (connector->encoder_ids[i] != 0) {
|
||||
if (put_user(connector->encoder_ids[i],
|
||||
|
@ -1470,6 +1547,245 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_getplane_res - get plane info
|
||||
* @dev: DRM device
|
||||
* @data: ioctl data
|
||||
* @file_priv: DRM file info
|
||||
*
|
||||
* Return an plane count and set of IDs.
|
||||
*/
|
||||
int drm_mode_getplane_res(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_mode_get_plane_res *plane_resp = data;
|
||||
struct drm_mode_config *config;
|
||||
struct drm_plane *plane;
|
||||
uint32_t __user *plane_ptr;
|
||||
int copied = 0, ret = 0;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
config = &dev->mode_config;
|
||||
|
||||
/*
|
||||
* This ioctl is called twice, once to determine how much space is
|
||||
* needed, and the 2nd time to fill it.
|
||||
*/
|
||||
if (config->num_plane &&
|
||||
(plane_resp->count_planes >= config->num_plane)) {
|
||||
plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr;
|
||||
|
||||
list_for_each_entry(plane, &config->plane_list, head) {
|
||||
if (put_user(plane->base.id, plane_ptr + copied)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
copied++;
|
||||
}
|
||||
}
|
||||
plane_resp->count_planes = config->num_plane;
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_getplane - get plane info
|
||||
* @dev: DRM device
|
||||
* @data: ioctl data
|
||||
* @file_priv: DRM file info
|
||||
*
|
||||
* Return plane info, including formats supported, gamma size, any
|
||||
* current fb, etc.
|
||||
*/
|
||||
int drm_mode_getplane(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_mode_get_plane *plane_resp = data;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_plane *plane;
|
||||
uint32_t __user *format_ptr;
|
||||
int ret = 0;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
obj = drm_mode_object_find(dev, plane_resp->plane_id,
|
||||
DRM_MODE_OBJECT_PLANE);
|
||||
if (!obj) {
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
plane = obj_to_plane(obj);
|
||||
|
||||
if (plane->crtc)
|
||||
plane_resp->crtc_id = plane->crtc->base.id;
|
||||
else
|
||||
plane_resp->crtc_id = 0;
|
||||
|
||||
if (plane->fb)
|
||||
plane_resp->fb_id = plane->fb->base.id;
|
||||
else
|
||||
plane_resp->fb_id = 0;
|
||||
|
||||
plane_resp->plane_id = plane->base.id;
|
||||
plane_resp->possible_crtcs = plane->possible_crtcs;
|
||||
plane_resp->gamma_size = plane->gamma_size;
|
||||
|
||||
/*
|
||||
* This ioctl is called twice, once to determine how much space is
|
||||
* needed, and the 2nd time to fill it.
|
||||
*/
|
||||
if (plane->format_count &&
|
||||
(plane_resp->count_format_types >= plane->format_count)) {
|
||||
format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr;
|
||||
if (copy_to_user(format_ptr,
|
||||
plane->format_types,
|
||||
sizeof(uint32_t) * plane->format_count)) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
plane_resp->count_format_types = plane->format_count;
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_setplane - set up or tear down an plane
|
||||
* @dev: DRM device
|
||||
* @data: ioctl data*
|
||||
* @file_prive: DRM file info
|
||||
*
|
||||
* Set plane info, including placement, fb, scaling, and other factors.
|
||||
* Or pass a NULL fb to disable.
|
||||
*/
|
||||
int drm_mode_setplane(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_mode_set_plane *plane_req = data;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_plane *plane;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_framebuffer *fb;
|
||||
int ret = 0;
|
||||
unsigned int fb_width, fb_height;
|
||||
int i;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
/*
|
||||
* First, find the plane, crtc, and fb objects. If not available,
|
||||
* we don't bother to call the driver.
|
||||
*/
|
||||
obj = drm_mode_object_find(dev, plane_req->plane_id,
|
||||
DRM_MODE_OBJECT_PLANE);
|
||||
if (!obj) {
|
||||
DRM_DEBUG_KMS("Unknown plane ID %d\n",
|
||||
plane_req->plane_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
plane = obj_to_plane(obj);
|
||||
|
||||
/* No fb means shut it down */
|
||||
if (!plane_req->fb_id) {
|
||||
plane->funcs->disable_plane(plane);
|
||||
plane->crtc = NULL;
|
||||
plane->fb = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
obj = drm_mode_object_find(dev, plane_req->crtc_id,
|
||||
DRM_MODE_OBJECT_CRTC);
|
||||
if (!obj) {
|
||||
DRM_DEBUG_KMS("Unknown crtc ID %d\n",
|
||||
plane_req->crtc_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
crtc = obj_to_crtc(obj);
|
||||
|
||||
obj = drm_mode_object_find(dev, plane_req->fb_id,
|
||||
DRM_MODE_OBJECT_FB);
|
||||
if (!obj) {
|
||||
DRM_DEBUG_KMS("Unknown framebuffer ID %d\n",
|
||||
plane_req->fb_id);
|
||||
ret = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
fb = obj_to_fb(obj);
|
||||
|
||||
/* Check whether this plane supports the fb pixel format. */
|
||||
for (i = 0; i < plane->format_count; i++)
|
||||
if (fb->pixel_format == plane->format_types[i])
|
||||
break;
|
||||
if (i == plane->format_count) {
|
||||
DRM_DEBUG_KMS("Invalid pixel format 0x%08x\n", fb->pixel_format);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
fb_width = fb->width << 16;
|
||||
fb_height = fb->height << 16;
|
||||
|
||||
/* Make sure source coordinates are inside the fb. */
|
||||
if (plane_req->src_w > fb_width ||
|
||||
plane_req->src_x > fb_width - plane_req->src_w ||
|
||||
plane_req->src_h > fb_height ||
|
||||
plane_req->src_y > fb_height - plane_req->src_h) {
|
||||
DRM_DEBUG_KMS("Invalid source coordinates "
|
||||
"%u.%06ux%u.%06u+%u.%06u+%u.%06u\n",
|
||||
plane_req->src_w >> 16,
|
||||
((plane_req->src_w & 0xffff) * 15625) >> 10,
|
||||
plane_req->src_h >> 16,
|
||||
((plane_req->src_h & 0xffff) * 15625) >> 10,
|
||||
plane_req->src_x >> 16,
|
||||
((plane_req->src_x & 0xffff) * 15625) >> 10,
|
||||
plane_req->src_y >> 16,
|
||||
((plane_req->src_y & 0xffff) * 15625) >> 10);
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Give drivers some help against integer overflows */
|
||||
if (plane_req->crtc_w > INT_MAX ||
|
||||
plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w ||
|
||||
plane_req->crtc_h > INT_MAX ||
|
||||
plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) {
|
||||
DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n",
|
||||
plane_req->crtc_w, plane_req->crtc_h,
|
||||
plane_req->crtc_x, plane_req->crtc_y);
|
||||
ret = -ERANGE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = plane->funcs->update_plane(plane, crtc, fb,
|
||||
plane_req->crtc_x, plane_req->crtc_y,
|
||||
plane_req->crtc_w, plane_req->crtc_h,
|
||||
plane_req->src_x, plane_req->src_y,
|
||||
plane_req->src_w, plane_req->src_h);
|
||||
if (!ret) {
|
||||
plane->crtc = crtc;
|
||||
plane->fb = fb;
|
||||
}
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_setcrtc - set CRTC configuration
|
||||
* @inode: inode from the ioctl
|
||||
|
@ -1576,7 +1892,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
|||
}
|
||||
|
||||
for (i = 0; i < crtc_req->count_connectors; i++) {
|
||||
set_connectors_ptr = (uint32_t *)(unsigned long)crtc_req->set_connectors_ptr;
|
||||
set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
|
||||
if (get_user(out_id, &set_connectors_ptr[i])) {
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
|
@ -1625,10 +1941,8 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
|
|||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
if (!req->flags) {
|
||||
DRM_ERROR("no operation set\n");
|
||||
if (!req->flags)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC);
|
||||
|
@ -1641,7 +1955,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
|
|||
|
||||
if (req->flags & DRM_MODE_CURSOR_BO) {
|
||||
if (!crtc->funcs->cursor_set) {
|
||||
DRM_ERROR("crtc does not support cursor\n");
|
||||
ret = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1654,7 +1967,6 @@ int drm_mode_cursor_ioctl(struct drm_device *dev,
|
|||
if (crtc->funcs->cursor_move) {
|
||||
ret = crtc->funcs->cursor_move(crtc, req->x, req->y);
|
||||
} else {
|
||||
DRM_ERROR("crtc does not support cursor\n");
|
||||
ret = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1664,6 +1976,42 @@ out:
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Original addfb only supported RGB formats, so figure out which one */
|
||||
uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth)
|
||||
{
|
||||
uint32_t fmt;
|
||||
|
||||
switch (bpp) {
|
||||
case 8:
|
||||
fmt = DRM_FORMAT_RGB332;
|
||||
break;
|
||||
case 16:
|
||||
if (depth == 15)
|
||||
fmt = DRM_FORMAT_XRGB1555;
|
||||
else
|
||||
fmt = DRM_FORMAT_RGB565;
|
||||
break;
|
||||
case 24:
|
||||
fmt = DRM_FORMAT_RGB888;
|
||||
break;
|
||||
case 32:
|
||||
if (depth == 24)
|
||||
fmt = DRM_FORMAT_XRGB8888;
|
||||
else if (depth == 30)
|
||||
fmt = DRM_FORMAT_XRGB2101010;
|
||||
else
|
||||
fmt = DRM_FORMAT_ARGB8888;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n");
|
||||
fmt = DRM_FORMAT_XRGB8888;
|
||||
break;
|
||||
}
|
||||
|
||||
return fmt;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_mode_legacy_fb_format);
|
||||
|
||||
/**
|
||||
* drm_mode_addfb - add an FB to the graphics configuration
|
||||
* @inode: inode from the ioctl
|
||||
|
@ -1684,7 +2032,140 @@ out:
|
|||
int drm_mode_addfb(struct drm_device *dev,
|
||||
void *data, struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_mode_fb_cmd *r = data;
|
||||
struct drm_mode_fb_cmd *or = data;
|
||||
struct drm_mode_fb_cmd2 r = {};
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
struct drm_framebuffer *fb;
|
||||
int ret = 0;
|
||||
|
||||
/* Use new struct with format internally */
|
||||
r.fb_id = or->fb_id;
|
||||
r.width = or->width;
|
||||
r.height = or->height;
|
||||
r.pitches[0] = or->pitch;
|
||||
r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth);
|
||||
r.handles[0] = or->handle;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
if ((config->min_width > r.width) || (r.width > config->max_width))
|
||||
return -EINVAL;
|
||||
|
||||
if ((config->min_height > r.height) || (r.height > config->max_height))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
/* TODO check buffer is sufficiently large */
|
||||
/* TODO setup destructor callback */
|
||||
|
||||
fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r);
|
||||
if (IS_ERR(fb)) {
|
||||
DRM_ERROR("could not create framebuffer\n");
|
||||
ret = PTR_ERR(fb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
or->fb_id = fb->base.id;
|
||||
list_add(&fb->filp_head, &file_priv->fbs);
|
||||
DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int format_check(struct drm_mode_fb_cmd2 *r)
|
||||
{
|
||||
uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN;
|
||||
|
||||
switch (format) {
|
||||
case DRM_FORMAT_C8:
|
||||
case DRM_FORMAT_RGB332:
|
||||
case DRM_FORMAT_BGR233:
|
||||
case DRM_FORMAT_XRGB4444:
|
||||
case DRM_FORMAT_XBGR4444:
|
||||
case DRM_FORMAT_RGBX4444:
|
||||
case DRM_FORMAT_BGRX4444:
|
||||
case DRM_FORMAT_ARGB4444:
|
||||
case DRM_FORMAT_ABGR4444:
|
||||
case DRM_FORMAT_RGBA4444:
|
||||
case DRM_FORMAT_BGRA4444:
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
case DRM_FORMAT_RGBX5551:
|
||||
case DRM_FORMAT_BGRX5551:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
case DRM_FORMAT_BGRA5551:
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_BGR565:
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_BGR888:
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
case DRM_FORMAT_XBGR2101010:
|
||||
case DRM_FORMAT_RGBX1010102:
|
||||
case DRM_FORMAT_BGRX1010102:
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
case DRM_FORMAT_ABGR2101010:
|
||||
case DRM_FORMAT_RGBA1010102:
|
||||
case DRM_FORMAT_BGRA1010102:
|
||||
case DRM_FORMAT_YUYV:
|
||||
case DRM_FORMAT_YVYU:
|
||||
case DRM_FORMAT_UYVY:
|
||||
case DRM_FORMAT_VYUY:
|
||||
case DRM_FORMAT_AYUV:
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_YUV410:
|
||||
case DRM_FORMAT_YVU410:
|
||||
case DRM_FORMAT_YUV411:
|
||||
case DRM_FORMAT_YVU411:
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_YVU420:
|
||||
case DRM_FORMAT_YUV422:
|
||||
case DRM_FORMAT_YVU422:
|
||||
case DRM_FORMAT_YUV444:
|
||||
case DRM_FORMAT_YVU444:
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_mode_addfb2 - add an FB to the graphics configuration
|
||||
* @inode: inode from the ioctl
|
||||
* @filp: file * from the ioctl
|
||||
* @cmd: cmd from ioctl
|
||||
* @arg: arg from ioctl
|
||||
*
|
||||
* LOCKING:
|
||||
* Takes mode config lock.
|
||||
*
|
||||
* Add a new FB to the specified CRTC, given a user request with format.
|
||||
*
|
||||
* Called by the user via ioctl.
|
||||
*
|
||||
* RETURNS:
|
||||
* Zero on success, errno on failure.
|
||||
*/
|
||||
int drm_mode_addfb2(struct drm_device *dev,
|
||||
void *data, struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_mode_fb_cmd2 *r = data;
|
||||
struct drm_mode_config *config = &dev->mode_config;
|
||||
struct drm_framebuffer *fb;
|
||||
int ret = 0;
|
||||
|
@ -1693,18 +2174,23 @@ int drm_mode_addfb(struct drm_device *dev,
|
|||
return -EINVAL;
|
||||
|
||||
if ((config->min_width > r->width) || (r->width > config->max_width)) {
|
||||
DRM_ERROR("mode new framebuffer width not within limits\n");
|
||||
DRM_ERROR("bad framebuffer width %d, should be >= %d && <= %d\n",
|
||||
r->width, config->min_width, config->max_width);
|
||||
return -EINVAL;
|
||||
}
|
||||
if ((config->min_height > r->height) || (r->height > config->max_height)) {
|
||||
DRM_ERROR("mode new framebuffer height not within limits\n");
|
||||
DRM_ERROR("bad framebuffer height %d, should be >= %d && <= %d\n",
|
||||
r->height, config->min_height, config->max_height);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
ret = format_check(r);
|
||||
if (ret) {
|
||||
DRM_ERROR("bad framebuffer format 0x%08x\n", r->pixel_format);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* TODO check buffer is sufficiently large */
|
||||
/* TODO setup destructor callback */
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
fb = dev->mode_config.funcs->fb_create(dev, file_priv, r);
|
||||
if (IS_ERR(fb)) {
|
||||
|
@ -1756,7 +2242,6 @@ int drm_mode_rmfb(struct drm_device *dev,
|
|||
obj = drm_mode_object_find(dev, *id, DRM_MODE_OBJECT_FB);
|
||||
/* TODO check that we really get a framebuffer back. */
|
||||
if (!obj) {
|
||||
DRM_ERROR("mode invalid framebuffer id\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1767,7 +2252,6 @@ int drm_mode_rmfb(struct drm_device *dev,
|
|||
found = 1;
|
||||
|
||||
if (!found) {
|
||||
DRM_ERROR("tried to remove a fb that we didn't own\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1814,7 +2298,6 @@ int drm_mode_getfb(struct drm_device *dev,
|
|||
mutex_lock(&dev->mode_config.mutex);
|
||||
obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
|
||||
if (!obj) {
|
||||
DRM_ERROR("invalid framebuffer id\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -1824,7 +2307,7 @@ int drm_mode_getfb(struct drm_device *dev,
|
|||
r->width = fb->width;
|
||||
r->depth = fb->depth;
|
||||
r->bpp = fb->bits_per_pixel;
|
||||
r->pitch = fb->pitch;
|
||||
r->pitch = fb->pitches[0];
|
||||
fb->funcs->create_handle(fb, file_priv, &r->handle);
|
||||
|
||||
out:
|
||||
|
@ -1850,14 +2333,13 @@ int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
|
|||
mutex_lock(&dev->mode_config.mutex);
|
||||
obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
|
||||
if (!obj) {
|
||||
DRM_ERROR("invalid framebuffer id\n");
|
||||
ret = -EINVAL;
|
||||
goto out_err1;
|
||||
}
|
||||
fb = obj_to_fb(obj);
|
||||
|
||||
num_clips = r->num_clips;
|
||||
clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr;
|
||||
clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr;
|
||||
|
||||
if (!num_clips != !clips_ptr) {
|
||||
ret = -EINVAL;
|
||||
|
@ -2253,7 +2735,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
|
|||
struct drm_property_enum *prop_enum;
|
||||
struct drm_mode_property_enum __user *enum_ptr;
|
||||
struct drm_property_blob *prop_blob;
|
||||
uint32_t *blob_id_ptr;
|
||||
uint32_t __user *blob_id_ptr;
|
||||
uint64_t __user *values_ptr;
|
||||
uint32_t __user *blob_length_ptr;
|
||||
|
||||
|
@ -2283,7 +2765,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
|
|||
out_resp->flags = property->flags;
|
||||
|
||||
if ((out_resp->count_values >= value_count) && value_count) {
|
||||
values_ptr = (uint64_t *)(unsigned long)out_resp->values_ptr;
|
||||
values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr;
|
||||
for (i = 0; i < value_count; i++) {
|
||||
if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) {
|
||||
ret = -EFAULT;
|
||||
|
@ -2296,7 +2778,7 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
|
|||
if (property->flags & DRM_MODE_PROP_ENUM) {
|
||||
if ((out_resp->count_enum_blobs >= enum_count) && enum_count) {
|
||||
copied = 0;
|
||||
enum_ptr = (struct drm_mode_property_enum *)(unsigned long)out_resp->enum_blob_ptr;
|
||||
enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr;
|
||||
list_for_each_entry(prop_enum, &property->enum_blob_list, head) {
|
||||
|
||||
if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) {
|
||||
|
@ -2318,8 +2800,8 @@ int drm_mode_getproperty_ioctl(struct drm_device *dev,
|
|||
if (property->flags & DRM_MODE_PROP_BLOB) {
|
||||
if ((out_resp->count_enum_blobs >= blob_count) && blob_count) {
|
||||
copied = 0;
|
||||
blob_id_ptr = (uint32_t *)(unsigned long)out_resp->enum_blob_ptr;
|
||||
blob_length_ptr = (uint32_t *)(unsigned long)out_resp->values_ptr;
|
||||
blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr;
|
||||
blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr;
|
||||
|
||||
list_for_each_entry(prop_blob, &property->enum_blob_list, head) {
|
||||
if (put_user(prop_blob->base.id, blob_id_ptr + copied)) {
|
||||
|
@ -2380,7 +2862,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
|
|||
struct drm_mode_get_blob *out_resp = data;
|
||||
struct drm_property_blob *blob;
|
||||
int ret = 0;
|
||||
void *blob_ptr;
|
||||
void __user *blob_ptr;
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
@ -2394,7 +2876,7 @@ int drm_mode_getblob_ioctl(struct drm_device *dev,
|
|||
blob = obj_to_blob(obj);
|
||||
|
||||
if (out_resp->length == blob->length) {
|
||||
blob_ptr = (void *)(unsigned long)out_resp->data;
|
||||
blob_ptr = (void __user *)(unsigned long)out_resp->data;
|
||||
if (copy_to_user(blob_ptr, blob->data, blob->length)){
|
||||
ret = -EFAULT;
|
||||
goto done;
|
||||
|
@ -2788,3 +3270,71 @@ int drm_mode_destroy_dumb_ioctl(struct drm_device *dev,
|
|||
|
||||
return dev->driver->dumb_destroy(file_priv, dev, args->handle);
|
||||
}
|
||||
|
||||
/*
|
||||
* Just need to support RGB formats here for compat with code that doesn't
|
||||
* use pixel formats directly yet.
|
||||
*/
|
||||
void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth,
|
||||
int *bpp)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_RGB332:
|
||||
case DRM_FORMAT_BGR233:
|
||||
*depth = 8;
|
||||
*bpp = 8;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB1555:
|
||||
case DRM_FORMAT_XBGR1555:
|
||||
case DRM_FORMAT_RGBX5551:
|
||||
case DRM_FORMAT_BGRX5551:
|
||||
case DRM_FORMAT_ARGB1555:
|
||||
case DRM_FORMAT_ABGR1555:
|
||||
case DRM_FORMAT_RGBA5551:
|
||||
case DRM_FORMAT_BGRA5551:
|
||||
*depth = 15;
|
||||
*bpp = 16;
|
||||
break;
|
||||
case DRM_FORMAT_RGB565:
|
||||
case DRM_FORMAT_BGR565:
|
||||
*depth = 16;
|
||||
*bpp = 16;
|
||||
break;
|
||||
case DRM_FORMAT_RGB888:
|
||||
case DRM_FORMAT_BGR888:
|
||||
*depth = 24;
|
||||
*bpp = 24;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB8888:
|
||||
case DRM_FORMAT_XBGR8888:
|
||||
case DRM_FORMAT_RGBX8888:
|
||||
case DRM_FORMAT_BGRX8888:
|
||||
*depth = 24;
|
||||
*bpp = 32;
|
||||
break;
|
||||
case DRM_FORMAT_XRGB2101010:
|
||||
case DRM_FORMAT_XBGR2101010:
|
||||
case DRM_FORMAT_RGBX1010102:
|
||||
case DRM_FORMAT_BGRX1010102:
|
||||
case DRM_FORMAT_ARGB2101010:
|
||||
case DRM_FORMAT_ABGR2101010:
|
||||
case DRM_FORMAT_RGBA1010102:
|
||||
case DRM_FORMAT_BGRA1010102:
|
||||
*depth = 30;
|
||||
*bpp = 32;
|
||||
break;
|
||||
case DRM_FORMAT_ARGB8888:
|
||||
case DRM_FORMAT_ABGR8888:
|
||||
case DRM_FORMAT_RGBA8888:
|
||||
case DRM_FORMAT_BGRA8888:
|
||||
*depth = 32;
|
||||
*bpp = 32;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("unsupported pixel format\n");
|
||||
*depth = 0;
|
||||
*bpp = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_get_bpp_depth);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
|
||||
#include "drmP.h"
|
||||
#include "drm_crtc.h"
|
||||
#include "drm_fourcc.h"
|
||||
#include "drm_crtc_helper.h"
|
||||
#include "drm_fb_helper.h"
|
||||
|
||||
|
@ -710,7 +711,7 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
|
|||
for (i = 0; i < set->num_connectors; i++) {
|
||||
DRM_DEBUG_KMS("\t[CONNECTOR:%d:%s] set DPMS on\n", set->connectors[i]->base.id,
|
||||
drm_get_connector_name(set->connectors[i]));
|
||||
set->connectors[i]->dpms = DRM_MODE_DPMS_ON;
|
||||
set->connectors[i]->funcs->dpms(set->connectors[i], DRM_MODE_DPMS_ON);
|
||||
}
|
||||
}
|
||||
drm_helper_disable_unused_functions(dev);
|
||||
|
@ -847,13 +848,19 @@ void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
|
|||
EXPORT_SYMBOL(drm_helper_connector_dpms);
|
||||
|
||||
int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
|
||||
struct drm_mode_fb_cmd *mode_cmd)
|
||||
struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
fb->width = mode_cmd->width;
|
||||
fb->height = mode_cmd->height;
|
||||
fb->pitch = mode_cmd->pitch;
|
||||
fb->bits_per_pixel = mode_cmd->bpp;
|
||||
fb->depth = mode_cmd->depth;
|
||||
for (i = 0; i < 4; i++) {
|
||||
fb->pitches[i] = mode_cmd->pitches[i];
|
||||
fb->offsets[i] = mode_cmd->offsets[i];
|
||||
}
|
||||
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &fb->depth,
|
||||
&fb->bits_per_pixel);
|
||||
fb->pixel_format = mode_cmd->pixel_format;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1008,3 +1015,36 @@ void drm_helper_hpd_irq_event(struct drm_device *dev)
|
|||
queue_delayed_work(system_nrt_wq, &dev->mode_config.output_poll_work, 0);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_helper_hpd_irq_event);
|
||||
|
||||
|
||||
/**
|
||||
* drm_format_num_planes - get the number of planes for format
|
||||
* @format: pixel format (DRM_FORMAT_*)
|
||||
*
|
||||
* RETURNS:
|
||||
* The number of planes used by the specified pixel format.
|
||||
*/
|
||||
int drm_format_num_planes(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_YUV410:
|
||||
case DRM_FORMAT_YVU410:
|
||||
case DRM_FORMAT_YUV411:
|
||||
case DRM_FORMAT_YVU411:
|
||||
case DRM_FORMAT_YUV420:
|
||||
case DRM_FORMAT_YVU420:
|
||||
case DRM_FORMAT_YUV422:
|
||||
case DRM_FORMAT_YVU422:
|
||||
case DRM_FORMAT_YUV444:
|
||||
case DRM_FORMAT_YVU444:
|
||||
return 3;
|
||||
case DRM_FORMAT_NV12:
|
||||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(drm_format_num_planes);
|
||||
|
|
|
@ -61,14 +61,14 @@ static int drm_version(struct drm_device *dev, void *data,
|
|||
|
||||
/** Ioctl table */
|
||||
static struct drm_ioctl_desc drm_ioctls[] = {
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_VERSION, drm_version, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_UNIQUE, drm_getunique, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAGIC, drm_getmagic, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_IRQ_BUSID, drm_irq_by_busid, DRM_MASTER|DRM_ROOT_ONLY),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, 0),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_MAP, drm_getmap, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_CLIENT, drm_getclient, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_STATS, drm_getstats, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_GET_CAP, drm_getcap, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_SET_VERSION, drm_setversion, DRM_MASTER),
|
||||
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_SET_UNIQUE, drm_setunique, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
|
||||
|
@ -136,8 +136,11 @@ static struct drm_ioctl_desc drm_ioctls[] = {
|
|||
DRM_IOCTL_DEF(DRM_IOCTL_GEM_OPEN, drm_gem_open_ioctl, DRM_AUTH|DRM_UNLOCKED),
|
||||
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETRESOURCES, drm_mode_getresources, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_MASTER|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETGAMMA, drm_mode_gamma_set_ioctl, DRM_MASTER|DRM_UNLOCKED),
|
||||
|
@ -150,6 +153,7 @@ static struct drm_ioctl_desc drm_ioctls[] = {
|
|||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
|
|
|
@ -508,25 +508,10 @@ static void
|
|||
cea_for_each_detailed_block(u8 *ext, detailed_cb *cb, void *closure)
|
||||
{
|
||||
int i, n = 0;
|
||||
u8 rev = ext[0x01], d = ext[0x02];
|
||||
u8 d = ext[0x02];
|
||||
u8 *det_base = ext + d;
|
||||
|
||||
switch (rev) {
|
||||
case 0:
|
||||
/* can't happen */
|
||||
return;
|
||||
case 1:
|
||||
/* have to infer how many blocks we have, check pixel clock */
|
||||
for (i = 0; i < 6; i++)
|
||||
if (det_base[18*i] || det_base[18*i+1])
|
||||
n++;
|
||||
break;
|
||||
default:
|
||||
/* explicit count */
|
||||
n = min(ext[0x03] & 0x0f, 6);
|
||||
break;
|
||||
}
|
||||
|
||||
n = (127 - d) / 18;
|
||||
for (i = 0; i < n; i++)
|
||||
cb((struct detailed_timing *)(det_base + 18 * i), closure);
|
||||
}
|
||||
|
@ -1319,6 +1304,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
|
|||
|
||||
#define HDMI_IDENTIFIER 0x000C03
|
||||
#define AUDIO_BLOCK 0x01
|
||||
#define VIDEO_BLOCK 0x02
|
||||
#define VENDOR_BLOCK 0x03
|
||||
#define SPEAKER_BLOCK 0x04
|
||||
#define EDID_BASIC_AUDIO (1 << 6)
|
||||
|
@ -1349,6 +1335,47 @@ u8 *drm_find_cea_extension(struct edid *edid)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_find_cea_extension);
|
||||
|
||||
static int
|
||||
do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
u8 * mode, cea_mode;
|
||||
int modes = 0;
|
||||
|
||||
for (mode = db; mode < db + len; mode++) {
|
||||
cea_mode = (*mode & 127) - 1; /* CEA modes are numbered 1..127 */
|
||||
if (cea_mode < drm_num_cea_modes) {
|
||||
struct drm_display_mode *newmode;
|
||||
newmode = drm_mode_duplicate(dev,
|
||||
&edid_cea_modes[cea_mode]);
|
||||
if (newmode) {
|
||||
drm_mode_probed_add(connector, newmode);
|
||||
modes++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
static int
|
||||
add_cea_modes(struct drm_connector *connector, struct edid *edid)
|
||||
{
|
||||
u8 * cea = drm_find_cea_extension(edid);
|
||||
u8 * db, dbl;
|
||||
int modes = 0;
|
||||
|
||||
if (cea && cea[1] >= 3) {
|
||||
for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
|
||||
dbl = db[0] & 0x1f;
|
||||
if (((db[0] & 0xe0) >> 5) == VIDEO_BLOCK)
|
||||
modes += do_cea_modes (connector, db+1, dbl);
|
||||
}
|
||||
}
|
||||
|
||||
return modes;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
|
||||
{
|
||||
|
@ -1432,26 +1459,29 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
|
|||
eld[18] = edid->prod_code[0];
|
||||
eld[19] = edid->prod_code[1];
|
||||
|
||||
for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
|
||||
dbl = db[0] & 0x1f;
|
||||
|
||||
switch ((db[0] & 0xe0) >> 5) {
|
||||
case AUDIO_BLOCK: /* Audio Data Block, contains SADs */
|
||||
sad_count = dbl / 3;
|
||||
memcpy(eld + 20 + mnl, &db[1], dbl);
|
||||
break;
|
||||
case SPEAKER_BLOCK: /* Speaker Allocation Data Block */
|
||||
eld[7] = db[1];
|
||||
break;
|
||||
case VENDOR_BLOCK:
|
||||
/* HDMI Vendor-Specific Data Block */
|
||||
if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
|
||||
parse_hdmi_vsdb(connector, db);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
if (cea[1] >= 3)
|
||||
for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
|
||||
dbl = db[0] & 0x1f;
|
||||
|
||||
switch ((db[0] & 0xe0) >> 5) {
|
||||
case AUDIO_BLOCK:
|
||||
/* Audio Data Block, contains SADs */
|
||||
sad_count = dbl / 3;
|
||||
memcpy(eld + 20 + mnl, &db[1], dbl);
|
||||
break;
|
||||
case SPEAKER_BLOCK:
|
||||
/* Speaker Allocation Data Block */
|
||||
eld[7] = db[1];
|
||||
break;
|
||||
case VENDOR_BLOCK:
|
||||
/* HDMI Vendor-Specific Data Block */
|
||||
if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
|
||||
parse_hdmi_vsdb(connector, db);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
eld[5] |= sad_count << 4;
|
||||
eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
|
||||
|
||||
|
@ -1722,6 +1752,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
|
|||
num_modes += add_standard_modes(connector, edid);
|
||||
num_modes += add_established_modes(connector, edid);
|
||||
num_modes += add_inferred_modes(connector, edid);
|
||||
num_modes += add_cea_modes(connector, edid);
|
||||
|
||||
if (quirks & (EDID_QUIRK_PREFER_LARGE_60 | EDID_QUIRK_PREFER_LARGE_75))
|
||||
edid_fixup_preferred(connector, quirks);
|
||||
|
|
|
@ -378,3 +378,287 @@ static const struct {
|
|||
{ 1920, 1440, 75, 0 },
|
||||
};
|
||||
static const int num_est3_modes = sizeof(est3_modes) / sizeof(est3_modes[0]);
|
||||
|
||||
/*
|
||||
* Probably taken from CEA-861 spec.
|
||||
* This table is converted from xorg's hw/xfree86/modes/xf86EdidModes.c.
|
||||
*/
|
||||
static const struct drm_display_mode edid_cea_modes[] = {
|
||||
/* 640x480@60Hz */
|
||||
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
|
||||
752, 800, 0, 480, 490, 492, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x480@60Hz */
|
||||
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
|
||||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x480@60Hz */
|
||||
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000, 720, 736,
|
||||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1280x720@60Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1390,
|
||||
1430, 1650, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080i@60Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x480i@60Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x480i@60Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x240@60Hz */
|
||||
{ DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
1602, 1716, 0, 240, 244, 247, 262, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x240@60Hz */
|
||||
{ DRM_MODE("1440x240", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
1602, 1716, 0, 240, 244, 247, 262, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x480i@60Hz */
|
||||
{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
3204, 3432, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 2880x480i@60Hz */
|
||||
{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
3204, 3432, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 2880x240@60Hz */
|
||||
{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
3204, 3432, 0, 240, 244, 247, 262, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x240@60Hz */
|
||||
{ DRM_MODE("2880x240", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
3204, 3432, 0, 240, 244, 247, 262, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x480@60Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
|
||||
1596, 1716, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x480@60Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1472,
|
||||
1596, 1716, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1920x1080@60Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 720x576@50Hz */
|
||||
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
|
||||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x576@50Hz */
|
||||
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000, 720, 732,
|
||||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1280x720@50Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 1720,
|
||||
1760, 1980, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080i@50Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x576i@50Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x576i@50Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x288@50Hz */
|
||||
{ DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
1590, 1728, 0, 288, 290, 293, 312, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x288@50Hz */
|
||||
{ DRM_MODE("1440x288", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
1590, 1728, 0, 288, 290, 293, 312, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x576i@50Hz */
|
||||
{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
3180, 3456, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 2880x576i@50Hz */
|
||||
{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
3180, 3456, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 2880x288@50Hz */
|
||||
{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
3180, 3456, 0, 288, 290, 293, 312, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x288@50Hz */
|
||||
{ DRM_MODE("2880x288", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
3180, 3456, 0, 288, 290, 293, 312, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x576@50Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
|
||||
1592, 1728, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x576@50Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
|
||||
1592, 1728, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1920x1080@50Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080@24Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2558,
|
||||
2602, 2750, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080@25Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080@30Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 2880x480@60Hz */
|
||||
{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
|
||||
3192, 3432, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x480@60Hz */
|
||||
{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2944,
|
||||
3192, 3432, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x576@50Hz */
|
||||
{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
|
||||
3184, 3456, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 2880x576@50Hz */
|
||||
{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 108000, 2880, 2928,
|
||||
3184, 3456, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1920x1080i@50Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
|
||||
2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1920x1080i@100Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1280x720@100Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1720,
|
||||
1760, 1980, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 720x576@100Hz */
|
||||
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
|
||||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x576@100Hz */
|
||||
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 54000, 720, 732,
|
||||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x576i@100Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x576i@100Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1920x1080i@120Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1280x720@120Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 148500, 1280, 1390,
|
||||
1430, 1650, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 720x480@120Hz */
|
||||
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
|
||||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x480@120Hz */
|
||||
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 54000, 720, 736,
|
||||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x480i@120Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x480i@120Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 720x576@200Hz */
|
||||
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
|
||||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x576@200Hz */
|
||||
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 108000, 720, 732,
|
||||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x576i@200Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x576i@200Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 720x480@240Hz */
|
||||
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
|
||||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 720x480@240Hz */
|
||||
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 108000, 720, 736,
|
||||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 1440x480i@240 */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1440x480i@240 */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 1280x720@24Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 59400, 1280, 3040,
|
||||
3080, 3300, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1280x720@25Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3700,
|
||||
3740, 3960, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1280x720@30Hz */
|
||||
{ DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250, 1280, 3040,
|
||||
3080, 3300, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080@120Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1089, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1920x1080@100Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 297000, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
};
|
||||
static const int drm_num_cea_modes =
|
||||
sizeof (edid_cea_modes) / sizeof (edid_cea_modes[0]);
|
||||
|
|
|
@ -255,6 +255,13 @@ bool drm_fb_helper_force_kernel_mode(void)
|
|||
int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
|
||||
void *panic_str)
|
||||
{
|
||||
/*
|
||||
* It's a waste of time and effort to switch back to text console
|
||||
* if the kernel should reboot before panic messages can be seen.
|
||||
*/
|
||||
if (panic_timeout < 0)
|
||||
return 0;
|
||||
|
||||
printk(KERN_ERR "panic occurred, switching back to text console\n");
|
||||
return drm_fb_helper_force_kernel_mode();
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ int drm_stub_open(struct inode *inode, struct file *filp)
|
|||
goto out;
|
||||
|
||||
old_fops = filp->f_op;
|
||||
filp->f_op = fops_get(&dev->driver->fops);
|
||||
filp->f_op = fops_get(dev->driver->fops);
|
||||
if (filp->f_op == NULL) {
|
||||
filp->f_op = old_fops;
|
||||
goto out;
|
||||
|
|
|
@ -158,14 +158,11 @@ int drm_getmap(struct drm_device *dev, void *data,
|
|||
int i;
|
||||
|
||||
idx = map->offset;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
if (idx < 0) {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
if (idx < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
list_for_each(list, &dev->maplist) {
|
||||
if (i == idx) {
|
||||
r_list = list_entry(list, struct drm_map_list, head);
|
||||
|
@ -211,9 +208,9 @@ int drm_getclient(struct drm_device *dev, void *data,
|
|||
int i;
|
||||
|
||||
idx = client->idx;
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
i = 0;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
list_for_each_entry(pt, &dev->filelist, lhead) {
|
||||
if (i++ >= idx) {
|
||||
client->auth = pt->authenticated;
|
||||
|
@ -249,8 +246,6 @@ int drm_getstats(struct drm_device *dev, void *data,
|
|||
|
||||
memset(stats, 0, sizeof(*stats));
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
for (i = 0; i < dev->counters; i++) {
|
||||
if (dev->types[i] == _DRM_STAT_LOCK)
|
||||
stats->data[i].value =
|
||||
|
@ -262,8 +257,6 @@ int drm_getstats(struct drm_device *dev, void *data,
|
|||
|
||||
stats->count = dev->counters;
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include "drmP.h"
|
||||
|
||||
static int drm_notifier(void *priv);
|
||||
|
@ -345,6 +346,7 @@ void drm_idlelock_take(struct drm_lock_data *lock_data)
|
|||
}
|
||||
spin_unlock_bh(&lock_data->spinlock);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_idlelock_take);
|
||||
|
||||
void drm_idlelock_release(struct drm_lock_data *lock_data)
|
||||
{
|
||||
|
@ -364,6 +366,7 @@ void drm_idlelock_release(struct drm_lock_data *lock_data)
|
|||
}
|
||||
spin_unlock_bh(&lock_data->spinlock);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_idlelock_release);
|
||||
|
||||
int drm_i_have_hw_lock(struct drm_device *dev, struct drm_file *file_priv)
|
||||
{
|
||||
|
|
|
@ -1,351 +0,0 @@
|
|||
/**************************************************************************
|
||||
*
|
||||
* Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDERS, AUTHORS 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.
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the
|
||||
* next paragraph) shall be included in all copies or substantial portions
|
||||
* of the Software.
|
||||
*
|
||||
*
|
||||
**************************************************************************/
|
||||
/*
|
||||
* Simple memory manager interface that keeps track on allocate regions on a
|
||||
* per "owner" basis. All regions associated with an "owner" can be released
|
||||
* with a simple call. Typically if the "owner" exists. The owner is any
|
||||
* "unsigned long" identifier. Can typically be a pointer to a file private
|
||||
* struct or a context identifier.
|
||||
*
|
||||
* Authors:
|
||||
* Thomas Hellström <thomas-at-tungstengraphics-dot-com>
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include "drm_sman.h"
|
||||
|
||||
struct drm_owner_item {
|
||||
struct drm_hash_item owner_hash;
|
||||
struct list_head sman_list;
|
||||
struct list_head mem_blocks;
|
||||
};
|
||||
|
||||
void drm_sman_takedown(struct drm_sman * sman)
|
||||
{
|
||||
drm_ht_remove(&sman->user_hash_tab);
|
||||
drm_ht_remove(&sman->owner_hash_tab);
|
||||
kfree(sman->mm);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_takedown);
|
||||
|
||||
int
|
||||
drm_sman_init(struct drm_sman * sman, unsigned int num_managers,
|
||||
unsigned int user_order, unsigned int owner_order)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
sman->mm = kcalloc(num_managers, sizeof(*sman->mm), GFP_KERNEL);
|
||||
if (!sman->mm) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sman->num_managers = num_managers;
|
||||
INIT_LIST_HEAD(&sman->owner_items);
|
||||
ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
|
||||
if (ret)
|
||||
goto out1;
|
||||
ret = drm_ht_create(&sman->user_hash_tab, user_order);
|
||||
if (!ret)
|
||||
goto out;
|
||||
|
||||
drm_ht_remove(&sman->owner_hash_tab);
|
||||
out1:
|
||||
kfree(sman->mm);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_init);
|
||||
|
||||
static void *drm_sman_mm_allocate(void *private, unsigned long size,
|
||||
unsigned alignment)
|
||||
{
|
||||
struct drm_mm *mm = (struct drm_mm *) private;
|
||||
struct drm_mm_node *tmp;
|
||||
|
||||
tmp = drm_mm_search_free(mm, size, alignment, 1);
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
tmp = drm_mm_get_block(tmp, size, alignment);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static void drm_sman_mm_free(void *private, void *ref)
|
||||
{
|
||||
struct drm_mm_node *node = (struct drm_mm_node *) ref;
|
||||
|
||||
drm_mm_put_block(node);
|
||||
}
|
||||
|
||||
static void drm_sman_mm_destroy(void *private)
|
||||
{
|
||||
struct drm_mm *mm = (struct drm_mm *) private;
|
||||
drm_mm_takedown(mm);
|
||||
kfree(mm);
|
||||
}
|
||||
|
||||
static unsigned long drm_sman_mm_offset(void *private, void *ref)
|
||||
{
|
||||
struct drm_mm_node *node = (struct drm_mm_node *) ref;
|
||||
return node->start;
|
||||
}
|
||||
|
||||
int
|
||||
drm_sman_set_range(struct drm_sman * sman, unsigned int manager,
|
||||
unsigned long start, unsigned long size)
|
||||
{
|
||||
struct drm_sman_mm *sman_mm;
|
||||
struct drm_mm *mm;
|
||||
int ret;
|
||||
|
||||
BUG_ON(manager >= sman->num_managers);
|
||||
|
||||
sman_mm = &sman->mm[manager];
|
||||
mm = kzalloc(sizeof(*mm), GFP_KERNEL);
|
||||
if (!mm) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
sman_mm->private = mm;
|
||||
ret = drm_mm_init(mm, start, size);
|
||||
|
||||
if (ret) {
|
||||
kfree(mm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
sman_mm->allocate = drm_sman_mm_allocate;
|
||||
sman_mm->free = drm_sman_mm_free;
|
||||
sman_mm->destroy = drm_sman_mm_destroy;
|
||||
sman_mm->offset = drm_sman_mm_offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_set_range);
|
||||
|
||||
int
|
||||
drm_sman_set_manager(struct drm_sman * sman, unsigned int manager,
|
||||
struct drm_sman_mm * allocator)
|
||||
{
|
||||
BUG_ON(manager >= sman->num_managers);
|
||||
sman->mm[manager] = *allocator;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_sman_set_manager);
|
||||
|
||||
static struct drm_owner_item *drm_sman_get_owner_item(struct drm_sman * sman,
|
||||
unsigned long owner)
|
||||
{
|
||||
int ret;
|
||||
struct drm_hash_item *owner_hash_item;
|
||||
struct drm_owner_item *owner_item;
|
||||
|
||||
ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item);
|
||||
if (!ret) {
|
||||
return drm_hash_entry(owner_hash_item, struct drm_owner_item,
|
||||
owner_hash);
|
||||
}
|
||||
|
||||
owner_item = kzalloc(sizeof(*owner_item), GFP_KERNEL);
|
||||
if (!owner_item)
|
||||
goto out;
|
||||
|
||||
INIT_LIST_HEAD(&owner_item->mem_blocks);
|
||||
owner_item->owner_hash.key = owner;
|
||||
if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
|
||||
goto out1;
|
||||
|
||||
list_add_tail(&owner_item->sman_list, &sman->owner_items);
|
||||
return owner_item;
|
||||
|
||||
out1:
|
||||
kfree(owner_item);
|
||||
out:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct drm_memblock_item *drm_sman_alloc(struct drm_sman *sman, unsigned int manager,
|
||||
unsigned long size, unsigned alignment,
|
||||
unsigned long owner)
|
||||
{
|
||||
void *tmp;
|
||||
struct drm_sman_mm *sman_mm;
|
||||
struct drm_owner_item *owner_item;
|
||||
struct drm_memblock_item *memblock;
|
||||
|
||||
BUG_ON(manager >= sman->num_managers);
|
||||
|
||||
sman_mm = &sman->mm[manager];
|
||||
tmp = sman_mm->allocate(sman_mm->private, size, alignment);
|
||||
|
||||
if (!tmp) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
memblock = kzalloc(sizeof(*memblock), GFP_KERNEL);
|
||||
|
||||
if (!memblock)
|
||||
goto out;
|
||||
|
||||
memblock->mm_info = tmp;
|
||||
memblock->mm = sman_mm;
|
||||
memblock->sman = sman;
|
||||
|
||||
if (drm_ht_just_insert_please
|
||||
(&sman->user_hash_tab, &memblock->user_hash,
|
||||
(unsigned long)memblock, 32, 0, 0))
|
||||
goto out1;
|
||||
|
||||
owner_item = drm_sman_get_owner_item(sman, owner);
|
||||
if (!owner_item)
|
||||
goto out2;
|
||||
|
||||
list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
|
||||
|
||||
return memblock;
|
||||
|
||||
out2:
|
||||
drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
|
||||
out1:
|
||||
kfree(memblock);
|
||||
out:
|
||||
sman_mm->free(sman_mm->private, tmp);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_alloc);
|
||||
|
||||
static void drm_sman_free(struct drm_memblock_item *item)
|
||||
{
|
||||
struct drm_sman *sman = item->sman;
|
||||
|
||||
list_del(&item->owner_list);
|
||||
drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
|
||||
item->mm->free(item->mm->private, item->mm_info);
|
||||
kfree(item);
|
||||
}
|
||||
|
||||
int drm_sman_free_key(struct drm_sman *sman, unsigned int key)
|
||||
{
|
||||
struct drm_hash_item *hash_item;
|
||||
struct drm_memblock_item *memblock_item;
|
||||
|
||||
if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
|
||||
return -EINVAL;
|
||||
|
||||
memblock_item = drm_hash_entry(hash_item, struct drm_memblock_item,
|
||||
user_hash);
|
||||
drm_sman_free(memblock_item);
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_free_key);
|
||||
|
||||
static void drm_sman_remove_owner(struct drm_sman *sman,
|
||||
struct drm_owner_item *owner_item)
|
||||
{
|
||||
list_del(&owner_item->sman_list);
|
||||
drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
|
||||
kfree(owner_item);
|
||||
}
|
||||
|
||||
int drm_sman_owner_clean(struct drm_sman *sman, unsigned long owner)
|
||||
{
|
||||
|
||||
struct drm_hash_item *hash_item;
|
||||
struct drm_owner_item *owner_item;
|
||||
|
||||
if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
|
||||
if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
|
||||
drm_sman_remove_owner(sman, owner_item);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_owner_clean);
|
||||
|
||||
static void drm_sman_do_owner_cleanup(struct drm_sman *sman,
|
||||
struct drm_owner_item *owner_item)
|
||||
{
|
||||
struct drm_memblock_item *entry, *next;
|
||||
|
||||
list_for_each_entry_safe(entry, next, &owner_item->mem_blocks,
|
||||
owner_list) {
|
||||
drm_sman_free(entry);
|
||||
}
|
||||
drm_sman_remove_owner(sman, owner_item);
|
||||
}
|
||||
|
||||
void drm_sman_owner_cleanup(struct drm_sman *sman, unsigned long owner)
|
||||
{
|
||||
|
||||
struct drm_hash_item *hash_item;
|
||||
struct drm_owner_item *owner_item;
|
||||
|
||||
if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
owner_item = drm_hash_entry(hash_item, struct drm_owner_item, owner_hash);
|
||||
drm_sman_do_owner_cleanup(sman, owner_item);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_owner_cleanup);
|
||||
|
||||
void drm_sman_cleanup(struct drm_sman *sman)
|
||||
{
|
||||
struct drm_owner_item *entry, *next;
|
||||
unsigned int i;
|
||||
struct drm_sman_mm *sman_mm;
|
||||
|
||||
list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
|
||||
drm_sman_do_owner_cleanup(sman, entry);
|
||||
}
|
||||
if (sman->mm) {
|
||||
for (i = 0; i < sman->num_managers; ++i) {
|
||||
sman_mm = &sman->mm[i];
|
||||
if (sman_mm->private) {
|
||||
sman_mm->destroy(sman_mm->private);
|
||||
sman_mm->private = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(drm_sman_cleanup);
|
|
@ -18,3 +18,10 @@ config DRM_EXYNOS_FIMD
|
|||
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"
|
||||
depends on DRM_EXYNOS
|
||||
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
|
||||
|
|
|
@ -5,7 +5,10 @@
|
|||
ccflags-y := -Iinclude/drm -Idrivers/gpu/drm/exynos
|
||||
exynosdrm-y := exynos_drm_drv.o exynos_drm_encoder.o exynos_drm_connector.o \
|
||||
exynos_drm_crtc.o exynos_drm_fbdev.o exynos_drm_fb.o \
|
||||
exynos_drm_buf.o exynos_drm_gem.o exynos_drm_core.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
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors:
|
||||
* Seung-Woo Kim <sw0312.kim@samsung.com>
|
||||
* 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/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_hdmi.h"
|
||||
|
||||
static int s5p_ddc_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *dev_id)
|
||||
{
|
||||
hdmi_attach_ddc_client(client);
|
||||
|
||||
dev_info(&client->adapter->dev, "attached s5p_ddc "
|
||||
"into i2c adapter successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s5p_ddc_remove(struct i2c_client *client)
|
||||
{
|
||||
dev_info(&client->adapter->dev, "detached s5p_ddc "
|
||||
"from i2c adapter successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_device_id ddc_idtable[] = {
|
||||
{"s5p_ddc", 0},
|
||||
{ },
|
||||
};
|
||||
|
||||
struct i2c_driver ddc_driver = {
|
||||
.driver = {
|
||||
.name = "s5p_ddc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = ddc_idtable,
|
||||
.probe = s5p_ddc_probe,
|
||||
.remove = __devexit_p(s5p_ddc_remove),
|
||||
.command = NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(ddc_driver);
|
|
@ -73,7 +73,7 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
|
|||
buffer = kzalloc(sizeof(*buffer), GFP_KERNEL);
|
||||
if (!buffer) {
|
||||
DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
buffer->size = size;
|
||||
|
@ -84,8 +84,7 @@ struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
|
|||
*/
|
||||
if (lowlevel_buffer_allocate(dev, buffer) < 0) {
|
||||
kfree(buffer);
|
||||
buffer = NULL;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
|
|
|
@ -30,9 +30,6 @@
|
|||
struct exynos_drm_gem_buf *exynos_drm_buf_create(struct drm_device *dev,
|
||||
unsigned int size);
|
||||
|
||||
/* get memory information of a drm framebuffer. */
|
||||
struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb);
|
||||
|
||||
/* remove allocated physical memory. */
|
||||
void exynos_drm_buf_destroy(struct drm_device *dev,
|
||||
struct exynos_drm_gem_buf *buffer);
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_encoder.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
|
||||
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
|
||||
drm_crtc)
|
||||
|
@ -52,11 +51,13 @@
|
|||
* drm framework doesn't support multiple irq yet.
|
||||
* we can refer to the crtc to current hardware interrupt occured through
|
||||
* this pipe value.
|
||||
* @dpms: store the crtc dpms value
|
||||
*/
|
||||
struct exynos_drm_crtc {
|
||||
struct drm_crtc drm_crtc;
|
||||
struct exynos_drm_overlay overlay;
|
||||
unsigned int pipe;
|
||||
unsigned int dpms;
|
||||
};
|
||||
|
||||
static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
|
||||
|
@ -78,20 +79,24 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
|
|||
struct exynos_drm_gem_buf *buffer;
|
||||
unsigned int actual_w;
|
||||
unsigned int actual_h;
|
||||
int nr = exynos_drm_format_num_buffers(fb->pixel_format);
|
||||
int i;
|
||||
|
||||
buffer = exynos_drm_fb_get_buf(fb);
|
||||
if (!buffer) {
|
||||
DRM_LOG_KMS("buffer is null.\n");
|
||||
return -EFAULT;
|
||||
for (i = 0; i < nr; i++) {
|
||||
buffer = exynos_drm_fb_buffer(fb, i);
|
||||
if (!buffer) {
|
||||
DRM_LOG_KMS("buffer is null\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
overlay->dma_addr[i] = buffer->dma_addr;
|
||||
overlay->vaddr[i] = buffer->kvaddr;
|
||||
|
||||
DRM_DEBUG_KMS("buffer: %d, vaddr = 0x%lx, dma_addr = 0x%lx\n",
|
||||
i, (unsigned long)overlay->vaddr[i],
|
||||
(unsigned long)overlay->dma_addr[i]);
|
||||
}
|
||||
|
||||
overlay->dma_addr = buffer->dma_addr;
|
||||
overlay->vaddr = buffer->kvaddr;
|
||||
|
||||
DRM_DEBUG_KMS("vaddr = 0x%lx, dma_addr = 0x%lx\n",
|
||||
(unsigned long)overlay->vaddr,
|
||||
(unsigned long)overlay->dma_addr);
|
||||
|
||||
actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
|
||||
actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
|
||||
|
||||
|
@ -101,7 +106,8 @@ int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
|
|||
overlay->fb_width = fb->width;
|
||||
overlay->fb_height = fb->height;
|
||||
overlay->bpp = fb->bits_per_pixel;
|
||||
overlay->pitch = fb->pitch;
|
||||
overlay->pitch = fb->pitches[0];
|
||||
overlay->pixel_format = fb->pixel_format;
|
||||
|
||||
/* set overlay range to be displayed. */
|
||||
overlay->crtc_x = pos->crtc_x;
|
||||
|
@ -153,26 +159,37 @@ static int exynos_drm_crtc_update(struct drm_crtc *crtc)
|
|||
|
||||
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
|
||||
|
||||
DRM_DEBUG_KMS("crtc[%d] mode[%d]\n", crtc->base.id, mode);
|
||||
|
||||
if (exynos_crtc->dpms == mode) {
|
||||
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
|
||||
exynos_drm_encoder_crtc_commit);
|
||||
exynos_drm_fn_encoder(crtc, &mode,
|
||||
exynos_drm_encoder_crtc_dpms);
|
||||
exynos_crtc->dpms = mode;
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
/* TODO */
|
||||
exynos_drm_fn_encoder(crtc, NULL,
|
||||
exynos_drm_encoder_crtc_disable);
|
||||
exynos_drm_fn_encoder(crtc, &mode,
|
||||
exynos_drm_encoder_crtc_dpms);
|
||||
exynos_crtc->dpms = mode;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
|
||||
DRM_ERROR("unspecified mode %d\n", mode);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
|
||||
|
@ -188,6 +205,28 @@ static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
|
|||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/*
|
||||
* when set_crtc is requested from user or at booting time,
|
||||
* crtc->commit would be called without dpms call so if dpms is
|
||||
* no power on then crtc->dpms should be called
|
||||
* with DRM_MODE_DPMS_ON for the hardware power to be on.
|
||||
*/
|
||||
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON) {
|
||||
int mode = DRM_MODE_DPMS_ON;
|
||||
|
||||
/*
|
||||
* enable hardware(power on) to all encoders hdmi connected
|
||||
* to current crtc.
|
||||
*/
|
||||
exynos_drm_crtc_dpms(crtc, mode);
|
||||
/*
|
||||
* enable dma to all encoders connected to current crtc and
|
||||
* lcd panel.
|
||||
*/
|
||||
exynos_drm_fn_encoder(crtc, &mode,
|
||||
exynos_drm_encoder_dpms_from_crtc);
|
||||
}
|
||||
|
||||
exynos_drm_fn_encoder(crtc, &exynos_crtc->pipe,
|
||||
exynos_drm_encoder_crtc_commit);
|
||||
}
|
||||
|
@ -344,6 +383,8 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
|
|||
}
|
||||
|
||||
exynos_crtc->pipe = nr;
|
||||
exynos_crtc->dpms = DRM_MODE_DPMS_OFF;
|
||||
exynos_crtc->overlay.zpos = DEFAULT_ZPOS;
|
||||
crtc = &exynos_crtc->drm_crtc;
|
||||
|
||||
private->crtc[nr] = crtc;
|
||||
|
@ -357,9 +398,14 @@ int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
|
|||
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct exynos_drm_private *private = dev->dev_private;
|
||||
struct exynos_drm_crtc *exynos_crtc =
|
||||
to_exynos_crtc(private->crtc[crtc]);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
|
||||
return -EPERM;
|
||||
|
||||
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
|
||||
exynos_drm_enable_vblank);
|
||||
|
||||
|
@ -369,9 +415,14 @@ int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
|
|||
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct exynos_drm_private *private = dev->dev_private;
|
||||
struct exynos_drm_crtc *exynos_crtc =
|
||||
to_exynos_crtc(private->crtc[crtc]);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (exynos_crtc->dpms != DRM_MODE_DPMS_ON)
|
||||
return;
|
||||
|
||||
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
|
||||
exynos_drm_disable_vblank);
|
||||
}
|
||||
|
|
|
@ -36,13 +36,16 @@
|
|||
#include "exynos_drm_fbdev.h"
|
||||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
#include "exynos_drm_plane.h"
|
||||
|
||||
#define DRIVER_NAME "exynos-drm"
|
||||
#define DRIVER_NAME "exynos"
|
||||
#define DRIVER_DESC "Samsung SoC DRM"
|
||||
#define DRIVER_DATE "20110530"
|
||||
#define DRIVER_MAJOR 1
|
||||
#define DRIVER_MINOR 0
|
||||
|
||||
#define VBLANK_OFF_DELAY 50000
|
||||
|
||||
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
struct exynos_drm_private *private;
|
||||
|
@ -77,6 +80,12 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
|||
goto err_crtc;
|
||||
}
|
||||
|
||||
for (nr = 0; nr < MAX_PLANE; nr++) {
|
||||
ret = exynos_plane_init(dev, nr);
|
||||
if (ret)
|
||||
goto err_crtc;
|
||||
}
|
||||
|
||||
ret = drm_vblank_init(dev, MAX_CRTC);
|
||||
if (ret)
|
||||
goto err_crtc;
|
||||
|
@ -100,6 +109,8 @@ static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
|
|||
goto err_drm_device;
|
||||
}
|
||||
|
||||
drm_vblank_offdelay = VBLANK_OFF_DELAY;
|
||||
|
||||
return 0;
|
||||
|
||||
err_drm_device:
|
||||
|
@ -163,6 +174,18 @@ static struct drm_ioctl_desc exynos_ioctls[] = {
|
|||
DRM_AUTH),
|
||||
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP,
|
||||
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),
|
||||
};
|
||||
|
||||
static const struct file_operations exynos_drm_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.mmap = exynos_drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.release = drm_release,
|
||||
};
|
||||
|
||||
static struct drm_driver exynos_drm_driver = {
|
||||
|
@ -182,15 +205,7 @@ static struct drm_driver exynos_drm_driver = {
|
|||
.dumb_map_offset = exynos_drm_gem_dumb_map_offset,
|
||||
.dumb_destroy = exynos_drm_gem_dumb_destroy,
|
||||
.ioctls = exynos_ioctls,
|
||||
.fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.mmap = exynos_drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.read = drm_read,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.release = drm_release,
|
||||
},
|
||||
.fops = &exynos_drm_driver_fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
|
|
|
@ -33,11 +33,16 @@
|
|||
#include "drm.h"
|
||||
|
||||
#define MAX_CRTC 2
|
||||
#define MAX_PLANE 5
|
||||
#define MAX_FB_BUFFER 3
|
||||
#define DEFAULT_ZPOS -1
|
||||
|
||||
struct drm_device;
|
||||
struct exynos_drm_overlay;
|
||||
struct drm_connector;
|
||||
|
||||
extern unsigned int drm_vblank_offdelay;
|
||||
|
||||
/* this enumerates display type. */
|
||||
enum exynos_drm_output_type {
|
||||
EXYNOS_DISPLAY_TYPE_NONE,
|
||||
|
@ -57,8 +62,8 @@ enum exynos_drm_output_type {
|
|||
struct exynos_drm_overlay_ops {
|
||||
void (*mode_set)(struct device *subdrv_dev,
|
||||
struct exynos_drm_overlay *overlay);
|
||||
void (*commit)(struct device *subdrv_dev);
|
||||
void (*disable)(struct device *subdrv_dev);
|
||||
void (*commit)(struct device *subdrv_dev, int zpos);
|
||||
void (*disable)(struct device *subdrv_dev, int zpos);
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -80,9 +85,11 @@ struct exynos_drm_overlay_ops {
|
|||
* @scan_flag: interlace or progressive way.
|
||||
* (it could be DRM_MODE_FLAG_*)
|
||||
* @bpp: pixel size.(in bit)
|
||||
* @dma_addr: bus(accessed by dma) address to the memory region allocated
|
||||
* for a overlay.
|
||||
* @vaddr: virtual memory addresss to this overlay.
|
||||
* @pixel_format: fourcc pixel format of this overlay
|
||||
* @dma_addr: array of bus(accessed by dma) address to the memory region
|
||||
* allocated for a overlay.
|
||||
* @vaddr: array of virtual memory addresss to this overlay.
|
||||
* @zpos: order of overlay layer(z position).
|
||||
* @default_win: a window to be enabled.
|
||||
* @color_key: color key on or off.
|
||||
* @index_color: if using color key feature then this value would be used
|
||||
|
@ -109,8 +116,10 @@ struct exynos_drm_overlay {
|
|||
unsigned int scan_flag;
|
||||
unsigned int bpp;
|
||||
unsigned int pitch;
|
||||
dma_addr_t dma_addr;
|
||||
void __iomem *vaddr;
|
||||
uint32_t pixel_format;
|
||||
dma_addr_t dma_addr[MAX_FB_BUFFER];
|
||||
void __iomem *vaddr[MAX_FB_BUFFER];
|
||||
int zpos;
|
||||
|
||||
bool default_win;
|
||||
bool color_key;
|
||||
|
@ -144,17 +153,19 @@ struct exynos_drm_display_ops {
|
|||
/*
|
||||
* Exynos drm manager ops
|
||||
*
|
||||
* @dpms: control device power.
|
||||
* @apply: set timing, vblank and overlay data to registers.
|
||||
* @mode_set: convert drm_display_mode to hw specific display mode and
|
||||
* would be called by encoder->mode_set().
|
||||
* @commit: set current hw specific display mode to hw.
|
||||
* @disable: disable hardware specific display mode.
|
||||
* @enable_vblank: specific driver callback for enabling vblank interrupt.
|
||||
* @disable_vblank: specific driver callback for disabling vblank interrupt.
|
||||
*/
|
||||
struct exynos_drm_manager_ops {
|
||||
void (*dpms)(struct device *subdrv_dev, int mode);
|
||||
void (*apply)(struct device *subdrv_dev);
|
||||
void (*mode_set)(struct device *subdrv_dev, void *mode);
|
||||
void (*commit)(struct device *subdrv_dev);
|
||||
void (*disable)(struct device *subdrv_dev);
|
||||
int (*enable_vblank)(struct device *subdrv_dev);
|
||||
void (*disable_vblank)(struct device *subdrv_dev);
|
||||
};
|
||||
|
|
|
@ -42,37 +42,19 @@
|
|||
* @drm_encoder: encoder object.
|
||||
* @manager: specific encoder has its own manager to control a hardware
|
||||
* appropriately and we can access a hardware drawing on this manager.
|
||||
* @dpms: store the encoder dpms value.
|
||||
*/
|
||||
struct exynos_drm_encoder {
|
||||
struct drm_encoder drm_encoder;
|
||||
struct exynos_drm_manager *manager;
|
||||
int dpms;
|
||||
};
|
||||
|
||||
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
static void exynos_drm_display_power(struct drm_encoder *encoder, int 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, encoder dpms: %d\n", __FILE__, mode);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
if (manager_ops && manager_ops->commit)
|
||||
manager_ops->commit(manager->dev);
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
/* TODO */
|
||||
if (manager_ops && manager_ops->disable)
|
||||
manager_ops->disable(manager->dev);
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("unspecified mode %d\n", mode);
|
||||
break;
|
||||
}
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->encoder == encoder) {
|
||||
|
@ -87,6 +69,43 @@ static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
|
|||
}
|
||||
}
|
||||
|
||||
static void exynos_drm_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
|
||||
struct exynos_drm_manager_ops *manager_ops = manager->ops;
|
||||
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
|
||||
|
||||
DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
|
||||
|
||||
if (exynos_encoder->dpms == mode) {
|
||||
DRM_DEBUG_KMS("desired dpms mode is same as previous one.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
if (manager_ops && manager_ops->apply)
|
||||
manager_ops->apply(manager->dev);
|
||||
exynos_drm_display_power(encoder, mode);
|
||||
exynos_encoder->dpms = mode;
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
exynos_drm_display_power(encoder, mode);
|
||||
exynos_encoder->dpms = mode;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("unspecified mode %d\n", mode);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
||||
static bool
|
||||
exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
|
@ -169,7 +188,6 @@ static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
|
|||
exynos_encoder->manager->pipe = -1;
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
encoder->dev->mode_config.num_encoder--;
|
||||
kfree(exynos_encoder);
|
||||
}
|
||||
|
||||
|
@ -199,6 +217,7 @@ exynos_drm_encoder_create(struct drm_device *dev,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
exynos_encoder->dpms = DRM_MODE_DPMS_OFF;
|
||||
exynos_encoder->manager = manager;
|
||||
encoder = &exynos_encoder->drm_encoder;
|
||||
encoder->possible_crtcs = possible_crtcs;
|
||||
|
@ -275,12 +294,27 @@ void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
|
|||
manager_ops->disable_vblank(manager->dev);
|
||||
}
|
||||
|
||||
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
|
||||
void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
|
||||
void *data)
|
||||
{
|
||||
struct exynos_drm_manager *manager =
|
||||
to_exynos_encoder(encoder)->manager;
|
||||
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
|
||||
int zpos = DEFAULT_ZPOS;
|
||||
|
||||
if (data)
|
||||
zpos = *(int *)data;
|
||||
|
||||
if (overlay_ops && overlay_ops->commit)
|
||||
overlay_ops->commit(manager->dev, zpos);
|
||||
}
|
||||
|
||||
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
|
||||
{
|
||||
struct exynos_drm_manager *manager =
|
||||
to_exynos_encoder(encoder)->manager;
|
||||
int crtc = *(int *)data;
|
||||
int zpos = DEFAULT_ZPOS;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
|
@ -290,8 +324,53 @@ void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data)
|
|||
*/
|
||||
manager->pipe = crtc;
|
||||
|
||||
if (overlay_ops && overlay_ops->commit)
|
||||
overlay_ops->commit(manager->dev);
|
||||
exynos_drm_encoder_crtc_plane_commit(encoder, &zpos);
|
||||
}
|
||||
|
||||
void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder, void *data)
|
||||
{
|
||||
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
|
||||
int mode = *(int *)data;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
exynos_drm_encoder_dpms(encoder, mode);
|
||||
|
||||
exynos_encoder->dpms = mode;
|
||||
}
|
||||
|
||||
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct exynos_drm_encoder *exynos_encoder = to_exynos_encoder(encoder);
|
||||
struct exynos_drm_manager *manager = exynos_encoder->manager;
|
||||
struct exynos_drm_manager_ops *manager_ops = manager->ops;
|
||||
struct drm_connector *connector;
|
||||
int mode = *(int *)data;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (manager_ops && manager_ops->dpms)
|
||||
manager_ops->dpms(manager->dev, mode);
|
||||
|
||||
/*
|
||||
* set current dpms mode to the connector connected to
|
||||
* current encoder. connector->dpms would be checked
|
||||
* at drm_helper_connector_dpms()
|
||||
*/
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
|
||||
if (connector->encoder == encoder)
|
||||
connector->dpms = mode;
|
||||
|
||||
/*
|
||||
* if this condition is ok then it means that the crtc is already
|
||||
* detached from encoder and last function for detaching is properly
|
||||
* done, so clear pipe from manager to prevent repeated call.
|
||||
*/
|
||||
if (mode > DRM_MODE_DPMS_ON) {
|
||||
if (!encoder->crtc)
|
||||
manager->pipe = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data)
|
||||
|
@ -310,19 +389,15 @@ void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data)
|
|||
struct exynos_drm_manager *manager =
|
||||
to_exynos_encoder(encoder)->manager;
|
||||
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
|
||||
int zpos = DEFAULT_ZPOS;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
if (overlay_ops && overlay_ops->disable)
|
||||
overlay_ops->disable(manager->dev);
|
||||
if (data)
|
||||
zpos = *(int *)data;
|
||||
|
||||
/*
|
||||
* crtc is already detached from encoder and last
|
||||
* function for detaching is properly done, so
|
||||
* clear pipe from manager to prevent repeated call
|
||||
*/
|
||||
if (!encoder->crtc)
|
||||
manager->pipe = -1;
|
||||
if (overlay_ops && overlay_ops->disable)
|
||||
overlay_ops->disable(manager->dev, zpos);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
|
||||
|
|
|
@ -39,7 +39,12 @@ void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
|
|||
void (*fn)(struct drm_encoder *, void *));
|
||||
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data);
|
||||
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data);
|
||||
void exynos_drm_encoder_crtc_plane_commit(struct drm_encoder *encoder,
|
||||
void *data);
|
||||
void exynos_drm_encoder_crtc_commit(struct drm_encoder *encoder, void *data);
|
||||
void exynos_drm_encoder_dpms_from_crtc(struct drm_encoder *encoder,
|
||||
void *data);
|
||||
void exynos_drm_encoder_crtc_dpms(struct drm_encoder *encoder, void *data);
|
||||
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
|
||||
void exynos_drm_encoder_crtc_disable(struct drm_encoder *encoder, void *data);
|
||||
|
||||
|
|
|
@ -33,7 +33,6 @@
|
|||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
|
||||
#define to_exynos_fb(x) container_of(x, struct exynos_drm_fb, fb)
|
||||
|
@ -42,15 +41,11 @@
|
|||
* exynos specific framebuffer structure.
|
||||
*
|
||||
* @fb: drm framebuffer obejct.
|
||||
* @exynos_gem_obj: exynos specific gem object containing a gem object.
|
||||
* @buffer: pointer to exynos_drm_gem_buffer object.
|
||||
* - contain the memory information to memory region allocated
|
||||
* at default framebuffer creation.
|
||||
* @exynos_gem_obj: array of exynos specific gem object containing a gem object.
|
||||
*/
|
||||
struct exynos_drm_fb {
|
||||
struct drm_framebuffer fb;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj[MAX_FB_BUFFER];
|
||||
};
|
||||
|
||||
static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
|
||||
|
@ -61,13 +56,6 @@ static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
|
|||
|
||||
drm_framebuffer_cleanup(fb);
|
||||
|
||||
/*
|
||||
* default framebuffer has no gem object so
|
||||
* a buffer of the default framebuffer should be released at here.
|
||||
*/
|
||||
if (!exynos_fb->exynos_gem_obj && exynos_fb->buffer)
|
||||
exynos_drm_buf_destroy(fb->dev, exynos_fb->buffer);
|
||||
|
||||
kfree(exynos_fb);
|
||||
exynos_fb = NULL;
|
||||
}
|
||||
|
@ -81,7 +69,7 @@ static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
|
|||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
return drm_gem_handle_create(file_priv,
|
||||
&exynos_fb->exynos_gem_obj->base, handle);
|
||||
&exynos_fb->exynos_gem_obj[0]->base, handle);
|
||||
}
|
||||
|
||||
static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
|
||||
|
@ -102,134 +90,88 @@ static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
|
|||
.dirty = exynos_drm_fb_dirty,
|
||||
};
|
||||
|
||||
static struct drm_framebuffer *
|
||||
exynos_drm_fb_init(struct drm_file *file_priv, struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd *mode_cmd)
|
||||
struct drm_framebuffer *
|
||||
exynos_drm_framebuffer_init(struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct drm_gem_object *obj)
|
||||
{
|
||||
struct exynos_drm_fb *exynos_fb;
|
||||
struct drm_framebuffer *fb;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj = NULL;
|
||||
struct drm_gem_object *obj;
|
||||
unsigned int size;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
mode_cmd->pitch = max(mode_cmd->pitch,
|
||||
mode_cmd->width * (mode_cmd->bpp >> 3));
|
||||
|
||||
DRM_LOG_KMS("drm fb create(%dx%d)\n",
|
||||
mode_cmd->width, mode_cmd->height);
|
||||
|
||||
exynos_fb = kzalloc(sizeof(*exynos_fb), GFP_KERNEL);
|
||||
if (!exynos_fb) {
|
||||
DRM_ERROR("failed to allocate exynos drm framebuffer.\n");
|
||||
DRM_ERROR("failed to allocate exynos drm framebuffer\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
fb = &exynos_fb->fb;
|
||||
ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs);
|
||||
ret = drm_framebuffer_init(dev, &exynos_fb->fb, &exynos_drm_fb_funcs);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to initialize framebuffer.\n");
|
||||
goto err_init;
|
||||
DRM_ERROR("failed to initialize framebuffer\n");
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
DRM_LOG_KMS("create: fb id: %d\n", fb->base.id);
|
||||
drm_helper_mode_fill_fb_struct(&exynos_fb->fb, mode_cmd);
|
||||
exynos_fb->exynos_gem_obj[0] = to_exynos_gem_obj(obj);
|
||||
|
||||
size = mode_cmd->pitch * mode_cmd->height;
|
||||
|
||||
/*
|
||||
* mode_cmd->handle could be NULL at booting time or
|
||||
* with user request. if NULL, a new buffer or a gem object
|
||||
* would be allocated.
|
||||
*/
|
||||
if (!mode_cmd->handle) {
|
||||
if (!file_priv) {
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
|
||||
/*
|
||||
* in case that file_priv is NULL, it allocates
|
||||
* only buffer and this buffer would be used
|
||||
* for default framebuffer.
|
||||
*/
|
||||
buffer = exynos_drm_buf_create(dev, size);
|
||||
if (IS_ERR(buffer)) {
|
||||
ret = PTR_ERR(buffer);
|
||||
goto err_buffer;
|
||||
}
|
||||
|
||||
exynos_fb->buffer = buffer;
|
||||
|
||||
DRM_LOG_KMS("default: dma_addr = 0x%lx, size = 0x%x\n",
|
||||
(unsigned long)buffer->dma_addr, size);
|
||||
|
||||
goto out;
|
||||
} else {
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
|
||||
&mode_cmd->handle,
|
||||
size);
|
||||
if (IS_ERR(exynos_gem_obj)) {
|
||||
ret = PTR_ERR(exynos_gem_obj);
|
||||
goto err_buffer;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object.\n");
|
||||
goto err_buffer;
|
||||
}
|
||||
|
||||
exynos_gem_obj = to_exynos_gem_obj(obj);
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* if got a exynos_gem_obj from either a handle or
|
||||
* a new creation then exynos_fb->exynos_gem_obj is NULL
|
||||
* so that default framebuffer has no its own gem object,
|
||||
* only its own buffer object.
|
||||
*/
|
||||
exynos_fb->buffer = exynos_gem_obj->buffer;
|
||||
|
||||
DRM_LOG_KMS("dma_addr = 0x%lx, size = 0x%x, gem object = 0x%x\n",
|
||||
(unsigned long)exynos_fb->buffer->dma_addr, size,
|
||||
(unsigned int)&exynos_gem_obj->base);
|
||||
|
||||
out:
|
||||
exynos_fb->exynos_gem_obj = exynos_gem_obj;
|
||||
|
||||
drm_helper_mode_fill_fb_struct(fb, mode_cmd);
|
||||
|
||||
return fb;
|
||||
|
||||
err_buffer:
|
||||
drm_framebuffer_cleanup(fb);
|
||||
|
||||
err_init:
|
||||
kfree(exynos_fb);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
return &exynos_fb->fb;
|
||||
}
|
||||
|
||||
struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
struct drm_mode_fb_cmd *mode_cmd)
|
||||
static struct drm_framebuffer *
|
||||
exynos_user_fb_create(struct drm_device *dev, struct drm_file *file_priv,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct drm_framebuffer *fb;
|
||||
struct exynos_drm_fb *exynos_fb;
|
||||
int nr;
|
||||
int i;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
return exynos_drm_fb_init(file_priv, dev, mode_cmd);
|
||||
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[0]);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object\n");
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
fb = exynos_drm_framebuffer_init(dev, mode_cmd, obj);
|
||||
if (IS_ERR(fb))
|
||||
return fb;
|
||||
|
||||
exynos_fb = to_exynos_fb(fb);
|
||||
nr = exynos_drm_format_num_buffers(fb->pixel_format);
|
||||
|
||||
for (i = 1; i < nr; i++) {
|
||||
obj = drm_gem_object_lookup(dev, file_priv,
|
||||
mode_cmd->handles[i]);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object\n");
|
||||
exynos_drm_fb_destroy(fb);
|
||||
return ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
exynos_fb->exynos_gem_obj[i] = to_exynos_gem_obj(obj);
|
||||
}
|
||||
|
||||
return fb;
|
||||
}
|
||||
|
||||
struct exynos_drm_gem_buf *exynos_drm_fb_get_buf(struct drm_framebuffer *fb)
|
||||
struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
|
||||
int index)
|
||||
{
|
||||
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
buffer = exynos_fb->buffer;
|
||||
if (index >= MAX_FB_BUFFER)
|
||||
return NULL;
|
||||
|
||||
buffer = exynos_fb->exynos_gem_obj[index]->buffer;
|
||||
if (!buffer)
|
||||
return NULL;
|
||||
|
||||
|
@ -250,7 +192,7 @@ static void exynos_drm_output_poll_changed(struct drm_device *dev)
|
|||
}
|
||||
|
||||
static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
|
||||
.fb_create = exynos_drm_fb_create,
|
||||
.fb_create = exynos_user_fb_create,
|
||||
.output_poll_changed = exynos_drm_output_poll_changed,
|
||||
};
|
||||
|
||||
|
|
|
@ -28,9 +28,27 @@
|
|||
#ifndef _EXYNOS_DRM_FB_H_
|
||||
#define _EXYNOS_DRM_FB_H
|
||||
|
||||
struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
|
||||
struct drm_file *filp,
|
||||
struct drm_mode_fb_cmd *mode_cmd);
|
||||
static inline int exynos_drm_format_num_buffers(uint32_t format)
|
||||
{
|
||||
switch (format) {
|
||||
case DRM_FORMAT_NV12M:
|
||||
case DRM_FORMAT_NV12MT:
|
||||
return 2;
|
||||
case DRM_FORMAT_YUV420M:
|
||||
return 3;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
struct drm_framebuffer *
|
||||
exynos_drm_framebuffer_init(struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct drm_gem_object *obj);
|
||||
|
||||
/* get memory information of a drm framebuffer */
|
||||
struct exynos_drm_gem_buf *exynos_drm_fb_buffer(struct drm_framebuffer *fb,
|
||||
int index);
|
||||
|
||||
void exynos_drm_mode_config_init(struct drm_device *dev);
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_fb.h"
|
||||
#include "exynos_drm_gem.h"
|
||||
#include "exynos_drm_buf.h"
|
||||
|
||||
#define MAX_CONNECTOR 4
|
||||
#define PREFERRED_BPP 32
|
||||
|
@ -43,8 +42,8 @@
|
|||
drm_fb_helper)
|
||||
|
||||
struct exynos_drm_fbdev {
|
||||
struct drm_fb_helper drm_fb_helper;
|
||||
struct drm_framebuffer *fb;
|
||||
struct drm_fb_helper drm_fb_helper;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
};
|
||||
|
||||
static int exynos_drm_fbdev_set_par(struct fb_info *info)
|
||||
|
@ -90,26 +89,24 @@ static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
|
|||
{
|
||||
struct fb_info *fbi = helper->fbdev;
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper);
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
unsigned int size = fb->width * fb->height * (fb->bits_per_pixel >> 3);
|
||||
unsigned long offset;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
exynos_fb->fb = fb;
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
|
||||
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
|
||||
|
||||
buffer = exynos_drm_fb_get_buf(fb);
|
||||
/* RGB formats use only one buffer */
|
||||
buffer = exynos_drm_fb_buffer(fb, 0);
|
||||
if (!buffer) {
|
||||
DRM_LOG_KMS("buffer is null.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
|
||||
offset += fbi->var.yoffset * fb->pitch;
|
||||
offset += fbi->var.yoffset * fb->pitches[0];
|
||||
|
||||
dev->mode_config.fb_base = (resource_size_t)buffer->dma_addr;
|
||||
fbi->screen_base = buffer->kvaddr + offset;
|
||||
|
@ -124,10 +121,12 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
|
|||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct fb_info *fbi;
|
||||
struct drm_mode_fb_cmd mode_cmd = { 0 };
|
||||
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
|
||||
struct platform_device *pdev = dev->platformdev;
|
||||
unsigned long size;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
@ -138,8 +137,9 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
|
|||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
mode_cmd.bpp = sizes->surface_bpp;
|
||||
mode_cmd.depth = sizes->surface_depth;
|
||||
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);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
|
@ -150,14 +150,23 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
|
|||
goto out;
|
||||
}
|
||||
|
||||
exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
|
||||
if (IS_ERR_OR_NULL(exynos_fbdev->fb)) {
|
||||
DRM_ERROR("failed to create drm framebuffer.\n");
|
||||
ret = PTR_ERR(exynos_fbdev->fb);
|
||||
size = mode_cmd.pitches[0] * mode_cmd.height;
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, size);
|
||||
if (IS_ERR(exynos_gem_obj)) {
|
||||
ret = PTR_ERR(exynos_gem_obj);
|
||||
goto out;
|
||||
}
|
||||
|
||||
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");
|
||||
ret = PTR_ERR(helper->fb);
|
||||
goto out;
|
||||
}
|
||||
|
||||
helper->fb = exynos_fbdev->fb;
|
||||
helper->fbdev = fbi;
|
||||
|
||||
fbi->par = helper;
|
||||
|
@ -171,8 +180,10 @@ static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
|
|||
}
|
||||
|
||||
ret = exynos_drm_fbdev_update(helper, helper->fb);
|
||||
if (ret < 0)
|
||||
if (ret < 0) {
|
||||
fb_dealloc_cmap(&fbi->cmap);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* if failed, all resources allocated above would be released by
|
||||
|
@ -205,34 +216,42 @@ static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
|
|||
{
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
|
||||
struct drm_framebuffer *fb = exynos_fbdev->fb;
|
||||
struct drm_mode_fb_cmd mode_cmd = { 0 };
|
||||
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 (helper->fb != fb) {
|
||||
DRM_ERROR("drm framebuffer is different\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (exynos_drm_fbdev_is_samefb(fb, sizes))
|
||||
return 0;
|
||||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
mode_cmd.bpp = sizes->surface_bpp;
|
||||
mode_cmd.depth = sizes->surface_depth;
|
||||
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);
|
||||
|
||||
exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
|
||||
if (IS_ERR(exynos_fbdev->fb)) {
|
||||
DRM_ERROR("failed to allocate fb.\n");
|
||||
return PTR_ERR(exynos_fbdev->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);
|
||||
}
|
||||
|
||||
helper->fb = exynos_fbdev->fb;
|
||||
return exynos_drm_fbdev_update(helper, helper->fb);
|
||||
}
|
||||
|
||||
|
@ -366,6 +385,9 @@ void exynos_drm_fbdev_fini(struct drm_device *dev)
|
|||
|
||||
fbdev = to_exynos_fbdev(private->fb_helper);
|
||||
|
||||
if (fbdev->exynos_gem_obj)
|
||||
exynos_drm_gem_destroy(fbdev->exynos_gem_obj);
|
||||
|
||||
exynos_drm_fbdev_destroy(dev, private->fb_helper);
|
||||
kfree(fbdev);
|
||||
private->fb_helper = NULL;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/exynos_drm.h>
|
||||
#include <plat/regs-fb-v4.h>
|
||||
|
@ -68,6 +69,7 @@ struct fimd_win_data {
|
|||
void __iomem *vaddr;
|
||||
unsigned int buf_offsize;
|
||||
unsigned int line_size; /* bytes */
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
struct fimd_context {
|
||||
|
@ -84,6 +86,8 @@ struct fimd_context {
|
|||
unsigned long irq_flags;
|
||||
u32 vidcon0;
|
||||
u32 vidcon1;
|
||||
bool suspended;
|
||||
struct mutex lock;
|
||||
|
||||
struct fb_videomode *timing;
|
||||
};
|
||||
|
@ -119,7 +123,7 @@ static int fimd_display_power_on(struct device *dev, int mode)
|
|||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/* TODO. */
|
||||
/* TODO */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -132,12 +136,68 @@ static struct exynos_drm_display_ops fimd_display_ops = {
|
|||
.power_on = fimd_display_power_on,
|
||||
};
|
||||
|
||||
static void fimd_dpms(struct device *subdrv_dev, int mode)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s, %d\n", __FILE__, mode);
|
||||
|
||||
mutex_lock(&ctx->lock);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
/*
|
||||
* enable fimd hardware only if suspended status.
|
||||
*
|
||||
* P.S. fimd_dpms function would be called at booting time so
|
||||
* clk_enable could be called double time.
|
||||
*/
|
||||
if (ctx->suspended)
|
||||
pm_runtime_get_sync(subdrv_dev);
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
pm_runtime_put_sync(subdrv_dev);
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("unspecified mode %d\n", mode);
|
||||
break;
|
||||
}
|
||||
|
||||
mutex_unlock(&ctx->lock);
|
||||
}
|
||||
|
||||
static void fimd_apply(struct device *subdrv_dev)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_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 fimd_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 fimd_commit(struct device *dev)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
struct fb_videomode *timing = ctx->timing;
|
||||
u32 val;
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/* setup polarity values from machine code. */
|
||||
|
@ -177,40 +237,6 @@ static void fimd_commit(struct device *dev)
|
|||
writel(val, ctx->regs + VIDCON0);
|
||||
}
|
||||
|
||||
static void fimd_disable(struct device *dev)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
|
||||
struct drm_device *drm_dev = subdrv->drm_dev;
|
||||
struct exynos_drm_manager *manager = &subdrv->manager;
|
||||
u32 val;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/* fimd dma off */
|
||||
val = readl(ctx->regs + VIDCON0);
|
||||
val &= ~(VIDCON0_ENVID | VIDCON0_ENVID_F);
|
||||
writel(val, ctx->regs + VIDCON0);
|
||||
|
||||
/*
|
||||
* if vblank is enabled status with dma off then
|
||||
* it disables vsync interrupt.
|
||||
*/
|
||||
if (drm_dev->vblank_enabled[manager->pipe] &&
|
||||
atomic_read(&drm_dev->vblank_refcount[manager->pipe])) {
|
||||
drm_vblank_put(drm_dev, manager->pipe);
|
||||
|
||||
/*
|
||||
* if vblank_disable_allowed is 0 then disable
|
||||
* vsync interrupt right now else the vsync interrupt
|
||||
* would be disabled by drm timer once a current process
|
||||
* gives up ownershop of vblank event.
|
||||
*/
|
||||
if (!drm_dev->vblank_disable_allowed)
|
||||
drm_vblank_off(drm_dev, manager->pipe);
|
||||
}
|
||||
}
|
||||
|
||||
static int fimd_enable_vblank(struct device *dev)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
|
@ -218,6 +244,9 @@ static int fimd_enable_vblank(struct device *dev)
|
|||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (ctx->suspended)
|
||||
return -EPERM;
|
||||
|
||||
if (!test_and_set_bit(0, &ctx->irq_flags)) {
|
||||
val = readl(ctx->regs + VIDINTCON0);
|
||||
|
||||
|
@ -242,6 +271,9 @@ static void fimd_disable_vblank(struct device *dev)
|
|||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (ctx->suspended)
|
||||
return;
|
||||
|
||||
if (test_and_clear_bit(0, &ctx->irq_flags)) {
|
||||
val = readl(ctx->regs + VIDINTCON0);
|
||||
|
||||
|
@ -253,8 +285,9 @@ static void fimd_disable_vblank(struct device *dev)
|
|||
}
|
||||
|
||||
static struct exynos_drm_manager_ops fimd_manager_ops = {
|
||||
.dpms = fimd_dpms,
|
||||
.apply = fimd_apply,
|
||||
.commit = fimd_commit,
|
||||
.disable = fimd_disable,
|
||||
.enable_vblank = fimd_enable_vblank,
|
||||
.disable_vblank = fimd_disable_vblank,
|
||||
};
|
||||
|
@ -264,6 +297,7 @@ static void fimd_win_mode_set(struct device *dev,
|
|||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
struct fimd_win_data *win_data;
|
||||
int win;
|
||||
unsigned long offset;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
@ -273,12 +307,19 @@ static void fimd_win_mode_set(struct device *dev,
|
|||
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[ctx->default_win];
|
||||
win_data = &ctx->win_data[win];
|
||||
|
||||
win_data->offset_x = overlay->crtc_x;
|
||||
win_data->offset_y = overlay->crtc_y;
|
||||
|
@ -286,8 +327,8 @@ static void fimd_win_mode_set(struct device *dev,
|
|||
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 + offset;
|
||||
win_data->vaddr = overlay->vaddr + offset;
|
||||
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);
|
||||
|
@ -381,15 +422,21 @@ static void fimd_win_set_colkey(struct device *dev, unsigned int win)
|
|||
writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
|
||||
}
|
||||
|
||||
static void fimd_win_commit(struct device *dev)
|
||||
static void fimd_win_commit(struct device *dev, int zpos)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
struct fimd_win_data *win_data;
|
||||
int win = ctx->default_win;
|
||||
int win = zpos;
|
||||
unsigned long val, alpha, size;
|
||||
|
||||
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;
|
||||
|
||||
|
@ -472,24 +519,37 @@ static void fimd_win_commit(struct device *dev)
|
|||
if (win != 0)
|
||||
fimd_win_set_colkey(dev, win);
|
||||
|
||||
/* wincon */
|
||||
val = readl(ctx->regs + WINCON(win));
|
||||
val |= WINCONx_ENWIN;
|
||||
writel(val, ctx->regs + WINCON(win));
|
||||
|
||||
/* Enable DMA channel and unprotect windows */
|
||||
val = readl(ctx->regs + SHADOWCON);
|
||||
val |= SHADOWCON_CHx_ENABLE(win);
|
||||
val &= ~SHADOWCON_WINx_PROTECT(win);
|
||||
writel(val, ctx->regs + SHADOWCON);
|
||||
|
||||
win_data->enabled = true;
|
||||
}
|
||||
|
||||
static void fimd_win_disable(struct device *dev)
|
||||
static void fimd_win_disable(struct device *dev, int zpos)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
int win = ctx->default_win;
|
||||
struct fimd_win_data *win_data;
|
||||
int win = zpos;
|
||||
u32 val;
|
||||
|
||||
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];
|
||||
|
||||
/* protect windows */
|
||||
val = readl(ctx->regs + SHADOWCON);
|
||||
val |= SHADOWCON_WINx_PROTECT(win);
|
||||
|
@ -505,6 +565,8 @@ static void fimd_win_disable(struct device *dev)
|
|||
val &= ~SHADOWCON_CHx_ENABLE(win);
|
||||
val &= ~SHADOWCON_WINx_PROTECT(win);
|
||||
writel(val, ctx->regs + SHADOWCON);
|
||||
|
||||
win_data->enabled = false;
|
||||
}
|
||||
|
||||
static struct exynos_drm_overlay_ops fimd_overlay_ops = {
|
||||
|
@ -540,9 +602,17 @@ static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
|
|||
wake_up_interruptible(&e->base.file_priv->event_wait);
|
||||
}
|
||||
|
||||
if (is_checked)
|
||||
if (is_checked) {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -560,19 +630,14 @@ static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
|
|||
/* VSYNC interrupt */
|
||||
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
|
||||
|
||||
/*
|
||||
* in case that vblank_disable_allowed is 1, it could induce
|
||||
* the problem that manager->pipe could be -1 because with
|
||||
* disable callback, vsync interrupt isn't disabled and at this moment,
|
||||
* vsync interrupt could occur. the vsync interrupt would be disabled
|
||||
* by timer handler later.
|
||||
*/
|
||||
if (manager->pipe == -1)
|
||||
return IRQ_HANDLED;
|
||||
/* check the crtc is detached already from encoder */
|
||||
if (manager->pipe < 0)
|
||||
goto out;
|
||||
|
||||
drm_handle_vblank(drm_dev, manager->pipe);
|
||||
fimd_finish_pageflip(drm_dev, manager->pipe);
|
||||
|
||||
out:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
|
@ -590,6 +655,13 @@ static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
|
|||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -739,9 +811,6 @@ static int __devinit fimd_probe(struct platform_device *pdev)
|
|||
|
||||
ctx->irq = res->start;
|
||||
|
||||
for (win = 0; win < WINDOWS_NR; win++)
|
||||
fimd_clear_win(ctx, win);
|
||||
|
||||
ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "irq request failed.\n");
|
||||
|
@ -769,7 +838,17 @@ static int __devinit fimd_probe(struct platform_device *pdev)
|
|||
subdrv->manager.display_ops = &fimd_display_ops;
|
||||
subdrv->manager.dev = dev;
|
||||
|
||||
mutex_init(&ctx->lock);
|
||||
|
||||
platform_set_drvdata(pdev, ctx);
|
||||
|
||||
pm_runtime_set_active(dev);
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_get_sync(dev);
|
||||
|
||||
for (win = 0; win < WINDOWS_NR; win++)
|
||||
fimd_clear_win(ctx, win);
|
||||
|
||||
exynos_drm_subdrv_register(subdrv);
|
||||
|
||||
return 0;
|
||||
|
@ -797,14 +876,25 @@ err_clk_get:
|
|||
|
||||
static int __devexit fimd_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct fimd_context *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
exynos_drm_subdrv_unregister(&ctx->subdrv);
|
||||
|
||||
if (ctx->suspended)
|
||||
goto out;
|
||||
|
||||
clk_disable(ctx->lcd_clk);
|
||||
clk_disable(ctx->bus_clk);
|
||||
|
||||
pm_runtime_set_suspended(dev);
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
out:
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
clk_put(ctx->lcd_clk);
|
||||
clk_put(ctx->bus_clk);
|
||||
|
||||
|
@ -818,12 +908,102 @@ static int __devexit fimd_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int fimd_suspend(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (pm_runtime_suspended(dev))
|
||||
return 0;
|
||||
|
||||
ret = pm_runtime_suspend(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fimd_resume(struct device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pm_runtime_resume(dev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to resume runtime pm.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
ret = pm_runtime_set_active(dev);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to active runtime pm.\n");
|
||||
pm_runtime_enable(dev);
|
||||
pm_runtime_suspend(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pm_runtime_enable(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_RUNTIME
|
||||
static int fimd_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
clk_disable(ctx->lcd_clk);
|
||||
clk_disable(ctx->bus_clk);
|
||||
|
||||
ctx->suspended = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int fimd_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct fimd_context *ctx = get_fimd_context(dev);
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
ret = clk_enable(ctx->bus_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_enable(ctx->lcd_clk);
|
||||
if (ret < 0) {
|
||||
clk_disable(ctx->bus_clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ctx->suspended = false;
|
||||
|
||||
/* if vblank was enabled status, enable it again. */
|
||||
if (test_and_clear_bit(0, &ctx->irq_flags))
|
||||
fimd_enable_vblank(dev);
|
||||
|
||||
fimd_apply(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct dev_pm_ops fimd_pm_ops = {
|
||||
SET_SYSTEM_SLEEP_PM_OPS(fimd_suspend, fimd_resume)
|
||||
SET_RUNTIME_PM_OPS(fimd_runtime_suspend, fimd_runtime_resume, NULL)
|
||||
};
|
||||
|
||||
static struct platform_driver fimd_driver = {
|
||||
.probe = fimd_probe,
|
||||
.remove = __devexit_p(fimd_remove),
|
||||
.driver = {
|
||||
.name = "exynos4-fb",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &fimd_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -55,93 +55,96 @@ static unsigned int convert_to_vm_err_msg(int msg)
|
|||
return out_msg;
|
||||
}
|
||||
|
||||
static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
|
||||
static int exynos_drm_gem_handle_create(struct drm_gem_object *obj,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle)
|
||||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static struct exynos_drm_gem_obj
|
||||
*exynos_drm_gem_init(struct drm_device *drm_dev,
|
||||
struct drm_file *file_priv, unsigned int *handle,
|
||||
unsigned int size)
|
||||
{
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
|
||||
if (!exynos_gem_obj) {
|
||||
DRM_ERROR("failed to allocate exynos gem object.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
obj = &exynos_gem_obj->base;
|
||||
|
||||
ret = drm_gem_object_init(drm_dev, obj, size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initialize gem object.\n");
|
||||
ret = -EINVAL;
|
||||
goto err_object_init;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
|
||||
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to allocate mmap offset.\n");
|
||||
goto err_create_mmap_offset;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate a id of idr table where the obj is registered
|
||||
* and handle has the id what user can see.
|
||||
*/
|
||||
ret = drm_gem_handle_create(file_priv, obj, handle);
|
||||
if (ret)
|
||||
goto err_handle_create;
|
||||
return ret;
|
||||
|
||||
DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
|
||||
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
|
||||
return exynos_gem_obj;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_handle_create:
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
void exynos_drm_gem_destroy(struct exynos_drm_gem_obj *exynos_gem_obj)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
err_create_mmap_offset:
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (!exynos_gem_obj)
|
||||
return;
|
||||
|
||||
obj = &exynos_gem_obj->base;
|
||||
|
||||
DRM_DEBUG_KMS("handle count = %d\n", atomic_read(&obj->handle_count));
|
||||
|
||||
exynos_drm_buf_destroy(obj->dev, exynos_gem_obj->buffer);
|
||||
|
||||
if (obj->map_list.map)
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
|
||||
/* release file pointer to gem object. */
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
err_object_init:
|
||||
kfree(exynos_gem_obj);
|
||||
}
|
||||
|
||||
return ERR_PTR(ret);
|
||||
static struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,
|
||||
unsigned long size)
|
||||
{
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct drm_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
|
||||
if (!exynos_gem_obj) {
|
||||
DRM_ERROR("failed to allocate exynos gem object\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
obj = &exynos_gem_obj->base;
|
||||
|
||||
ret = drm_gem_object_init(dev, obj, size);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to initialize gem object\n");
|
||||
kfree(exynos_gem_obj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
|
||||
|
||||
return exynos_gem_obj;
|
||||
}
|
||||
|
||||
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle, unsigned long size)
|
||||
unsigned long size)
|
||||
{
|
||||
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj = NULL;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
|
||||
size = roundup(size, PAGE_SIZE);
|
||||
|
||||
DRM_DEBUG_KMS("%s: size = 0x%lx\n", __FILE__, size);
|
||||
|
||||
buffer = exynos_drm_buf_create(dev, size);
|
||||
if (IS_ERR(buffer)) {
|
||||
return ERR_CAST(buffer);
|
||||
}
|
||||
if (!buffer)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
exynos_gem_obj = exynos_drm_gem_init(dev, file_priv, handle, size);
|
||||
if (IS_ERR(exynos_gem_obj)) {
|
||||
exynos_gem_obj = exynos_drm_gem_init(dev, size);
|
||||
if (!exynos_gem_obj) {
|
||||
exynos_drm_buf_destroy(dev, buffer);
|
||||
return exynos_gem_obj;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
exynos_gem_obj->buffer = buffer;
|
||||
|
@ -150,23 +153,30 @@ 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)
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_exynos_gem_create *args = data;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj = NULL;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv,
|
||||
&args->handle, args->size);
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
|
||||
if (IS_ERR(exynos_gem_obj))
|
||||
return PTR_ERR(exynos_gem_obj);
|
||||
|
||||
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
|
||||
&args->handle);
|
||||
if (ret) {
|
||||
exynos_drm_gem_destroy(exynos_gem_obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_exynos_gem_map_off *args = data;
|
||||
|
||||
|
@ -185,7 +195,7 @@ int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
|
|||
}
|
||||
|
||||
static int exynos_drm_gem_mmap_buffer(struct file *filp,
|
||||
struct vm_area_struct *vma)
|
||||
struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_object *obj = filp->private_data;
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
|
||||
|
@ -196,6 +206,7 @@ static int exynos_drm_gem_mmap_buffer(struct file *filp,
|
|||
|
||||
vma->vm_flags |= (VM_IO | VM_RESERVED);
|
||||
|
||||
/* in case of direct mapping, always having non-cachable attribute */
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
vma->vm_file = filp;
|
||||
|
||||
|
@ -232,7 +243,7 @@ static const struct file_operations exynos_drm_gem_fops = {
|
|||
};
|
||||
|
||||
int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_exynos_gem_mmap *args = data;
|
||||
struct drm_gem_object *obj;
|
||||
|
@ -278,32 +289,19 @@ int exynos_drm_gem_init_object(struct drm_gem_object *obj)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj)
|
||||
void exynos_drm_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
DRM_DEBUG_KMS("handle count = %d\n",
|
||||
atomic_read(&gem_obj->handle_count));
|
||||
|
||||
if (gem_obj->map_list.map)
|
||||
drm_gem_free_mmap_offset(gem_obj);
|
||||
|
||||
/* release file pointer to gem object. */
|
||||
drm_gem_object_release(gem_obj);
|
||||
|
||||
exynos_gem_obj = to_exynos_gem_obj(gem_obj);
|
||||
|
||||
exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->buffer);
|
||||
|
||||
kfree(exynos_gem_obj);
|
||||
exynos_drm_gem_destroy(to_exynos_gem_obj(obj));
|
||||
}
|
||||
|
||||
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev, struct drm_mode_create_dumb *args)
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
|
@ -316,19 +314,27 @@ int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
|
|||
args->pitch = args->width * args->bpp >> 3;
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, file_priv, &args->handle,
|
||||
args->size);
|
||||
exynos_gem_obj = exynos_drm_gem_create(dev, args->size);
|
||||
if (IS_ERR(exynos_gem_obj))
|
||||
return PTR_ERR(exynos_gem_obj);
|
||||
|
||||
ret = exynos_drm_gem_handle_create(&exynos_gem_obj->base, file_priv,
|
||||
&args->handle);
|
||||
if (ret) {
|
||||
exynos_drm_gem_destroy(exynos_gem_obj);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||
struct drm_device *dev, uint32_t handle, uint64_t *offset)
|
||||
struct drm_device *dev, uint32_t handle,
|
||||
uint64_t *offset)
|
||||
{
|
||||
struct exynos_drm_gem_obj *exynos_gem_obj;
|
||||
struct drm_gem_object *obj;
|
||||
int ret = 0;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
|
@ -343,19 +349,46 @@ int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
|
|||
obj = drm_gem_object_lookup(dev, file_priv, handle);
|
||||
if (!obj) {
|
||||
DRM_ERROR("failed to lookup gem object.\n");
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return -EINVAL;
|
||||
ret = -EINVAL;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
exynos_gem_obj = to_exynos_gem_obj(obj);
|
||||
|
||||
*offset = get_gem_mmap_offset(&exynos_gem_obj->base);
|
||||
|
||||
drm_gem_object_unreference(obj);
|
||||
if (!exynos_gem_obj->base.map_list.map) {
|
||||
ret = drm_gem_create_mmap_offset(&exynos_gem_obj->base);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
*offset = (u64)exynos_gem_obj->base.map_list.hash.key << PAGE_SHIFT;
|
||||
DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
|
||||
|
||||
out:
|
||||
drm_gem_object_unreference(obj);
|
||||
unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
|
||||
struct drm_device *dev,
|
||||
unsigned int handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/*
|
||||
* obj->refcount and obj->handle_count are decreased and
|
||||
* if both them are 0 then exynos_drm_gem_free_object()
|
||||
* would be called by callback to release resources.
|
||||
*/
|
||||
ret = drm_gem_handle_delete(file_priv, handle);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to delete drm_gem_handle.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -403,28 +436,6 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
|
||||
struct drm_device *dev, unsigned int handle)
|
||||
{
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
/*
|
||||
* obj->refcount and obj->handle_count are decreased and
|
||||
* if both them are 0 then exynos_drm_gem_free_object()
|
||||
* would be called by callback to release resources.
|
||||
*/
|
||||
ret = drm_gem_handle_delete(file_priv, handle);
|
||||
if (ret < 0) {
|
||||
DRM_ERROR("failed to delete drm_gem_handle.\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
|
||||
MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -60,14 +60,16 @@ struct exynos_drm_gem_buf {
|
|||
* user can access the buffer through kms_bo.handle.
|
||||
*/
|
||||
struct exynos_drm_gem_obj {
|
||||
struct drm_gem_object base;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
struct drm_gem_object base;
|
||||
struct exynos_drm_gem_buf *buffer;
|
||||
};
|
||||
|
||||
/* create a new buffer and get a new gem handle. */
|
||||
/* destroy a buffer with gem object */
|
||||
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,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle, unsigned long size);
|
||||
unsigned long size);
|
||||
|
||||
/*
|
||||
* request gem object creation and buffer allocation as the size
|
||||
|
@ -75,15 +77,18 @@ struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_device *dev,
|
|||
* height and bpp.
|
||||
*/
|
||||
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
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);
|
||||
struct drm_file *file_priv);
|
||||
|
||||
/* unmap a buffer from user space. */
|
||||
int exynos_drm_gem_munmap_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
/*
|
||||
* mmap the physically continuous memory that a gem object contains
|
||||
* to user space.
|
||||
*/
|
||||
int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
|
||||
/* initialize gem object. */
|
||||
int exynos_drm_gem_init_object(struct drm_gem_object *obj);
|
||||
|
@ -93,24 +98,13 @@ void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj);
|
|||
|
||||
/* create memory region for drm framebuffer. */
|
||||
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev, struct drm_mode_create_dumb *args);
|
||||
struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
|
||||
/* map memory region for drm framebuffer to user space. */
|
||||
int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
|
||||
struct drm_device *dev, uint32_t handle, uint64_t *offset);
|
||||
|
||||
/* page fault handler and mmap fault address(virtual) to physical memory. */
|
||||
int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||
|
||||
/*
|
||||
* mmap the physically continuous memory that a gem object contains
|
||||
* to user space.
|
||||
*/
|
||||
int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
|
||||
/* set vm_flags and we can change the vm attribute to other one at here. */
|
||||
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
struct drm_device *dev, uint32_t handle,
|
||||
uint64_t *offset);
|
||||
|
||||
/*
|
||||
* destroy memory region allocated.
|
||||
|
@ -118,6 +112,13 @@ int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
|||
* would be released by drm_gem_handle_delete().
|
||||
*/
|
||||
int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
|
||||
struct drm_device *dev, unsigned int handle);
|
||||
struct drm_device *dev,
|
||||
unsigned int handle);
|
||||
|
||||
/* page fault handler and mmap fault address(virtual) to physical memory. */
|
||||
int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||
|
||||
/* set vm_flags and we can change the vm attribute to other one at here. */
|
||||
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,439 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors:
|
||||
* Inki Dae <inki.dae@samsung.com>
|
||||
* Seung-Woo Kim <sw0312.kim@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/wait.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/exynos_drm.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_hdmi.h"
|
||||
|
||||
#define to_context(dev) platform_get_drvdata(to_platform_device(dev))
|
||||
#define to_subdrv(dev) to_context(dev)
|
||||
#define get_ctx_from_subdrv(subdrv) container_of(subdrv,\
|
||||
struct drm_hdmi_context, subdrv);
|
||||
|
||||
/* these callback points shoud be set by specific drivers. */
|
||||
static struct exynos_hdmi_display_ops *hdmi_display_ops;
|
||||
static struct exynos_hdmi_manager_ops *hdmi_manager_ops;
|
||||
static struct exynos_hdmi_overlay_ops *hdmi_overlay_ops;
|
||||
|
||||
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
|
||||
*display_ops)
|
||||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
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)
|
||||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
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)
|
||||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (overlay_ops)
|
||||
hdmi_overlay_ops = overlay_ops;
|
||||
}
|
||||
EXPORT_SYMBOL(exynos_drm_overlay_ops_register);
|
||||
|
||||
static bool drm_hdmi_is_connected(struct device *dev)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_display_ops && hdmi_display_ops->is_connected)
|
||||
return hdmi_display_ops->is_connected(ctx->hdmi_ctx->ctx);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int drm_hdmi_get_edid(struct device *dev,
|
||||
struct drm_connector *connector, u8 *edid, int len)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_display_ops && hdmi_display_ops->get_edid)
|
||||
return hdmi_display_ops->get_edid(ctx->hdmi_ctx->ctx,
|
||||
connector, edid, len);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_hdmi_check_timing(struct device *dev, void *timing)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_display_ops && hdmi_display_ops->check_timing)
|
||||
return hdmi_display_ops->check_timing(ctx->hdmi_ctx->ctx,
|
||||
timing);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int drm_hdmi_power_on(struct device *dev, int mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_display_ops && hdmi_display_ops->power_on)
|
||||
return hdmi_display_ops->power_on(ctx->hdmi_ctx->ctx, mode);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct exynos_drm_display_ops drm_hdmi_display_ops = {
|
||||
.type = EXYNOS_DISPLAY_TYPE_HDMI,
|
||||
.is_connected = drm_hdmi_is_connected,
|
||||
.get_edid = drm_hdmi_get_edid,
|
||||
.check_timing = drm_hdmi_check_timing,
|
||||
.power_on = drm_hdmi_power_on,
|
||||
};
|
||||
|
||||
static int drm_hdmi_enable_vblank(struct device *subdrv_dev)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
|
||||
struct exynos_drm_manager *manager = &subdrv->manager;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_overlay_ops && hdmi_overlay_ops->enable_vblank)
|
||||
return hdmi_overlay_ops->enable_vblank(ctx->mixer_ctx->ctx,
|
||||
manager->pipe);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void drm_hdmi_disable_vblank(struct device *subdrv_dev)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_overlay_ops && hdmi_overlay_ops->disable_vblank)
|
||||
return hdmi_overlay_ops->disable_vblank(ctx->mixer_ctx->ctx);
|
||||
}
|
||||
|
||||
static void drm_hdmi_mode_set(struct device *subdrv_dev, void *mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_manager_ops && hdmi_manager_ops->mode_set)
|
||||
hdmi_manager_ops->mode_set(ctx->hdmi_ctx->ctx, mode);
|
||||
}
|
||||
|
||||
static void drm_hdmi_commit(struct device *subdrv_dev)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_manager_ops && hdmi_manager_ops->commit)
|
||||
hdmi_manager_ops->commit(ctx->hdmi_ctx->ctx);
|
||||
}
|
||||
|
||||
static void drm_hdmi_dpms(struct device *subdrv_dev, int mode)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
if (hdmi_manager_ops && hdmi_manager_ops->disable)
|
||||
hdmi_manager_ops->disable(ctx->hdmi_ctx->ctx);
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_KMS("unkown dps mode: %d\n", mode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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_set = drm_hdmi_mode_set,
|
||||
.commit = drm_hdmi_commit,
|
||||
};
|
||||
|
||||
static void drm_mixer_mode_set(struct device *subdrv_dev,
|
||||
struct exynos_drm_overlay *overlay)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_overlay_ops && hdmi_overlay_ops->win_mode_set)
|
||||
hdmi_overlay_ops->win_mode_set(ctx->mixer_ctx->ctx, overlay);
|
||||
}
|
||||
|
||||
static void drm_mixer_commit(struct device *subdrv_dev, int zpos)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_overlay_ops && hdmi_overlay_ops->win_commit)
|
||||
hdmi_overlay_ops->win_commit(ctx->mixer_ctx->ctx, zpos);
|
||||
}
|
||||
|
||||
static void drm_mixer_disable(struct device *subdrv_dev, int zpos)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = to_context(subdrv_dev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
if (hdmi_overlay_ops && hdmi_overlay_ops->win_disable)
|
||||
hdmi_overlay_ops->win_disable(ctx->mixer_ctx->ctx, zpos);
|
||||
}
|
||||
|
||||
static struct exynos_drm_overlay_ops drm_hdmi_overlay_ops = {
|
||||
.mode_set = drm_mixer_mode_set,
|
||||
.commit = drm_mixer_commit,
|
||||
.disable = drm_mixer_disable,
|
||||
};
|
||||
|
||||
|
||||
static int hdmi_subdrv_probe(struct drm_device *drm_dev,
|
||||
struct device *dev)
|
||||
{
|
||||
struct exynos_drm_subdrv *subdrv = to_subdrv(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__);
|
||||
|
||||
pd = pdev->dev.platform_data;
|
||||
|
||||
if (!pd) {
|
||||
DRM_DEBUG_KMS("platform data is null.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!pd->hdmi_dev) {
|
||||
DRM_DEBUG_KMS("hdmi device is null.\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!pd->mixer_dev) {
|
||||
DRM_DEBUG_KMS("mixer device is null.\n");
|
||||
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;
|
||||
}
|
||||
|
||||
ctx->hdmi_ctx->drm_dev = drm_dev;
|
||||
|
||||
ctx->mixer_ctx = (struct exynos_drm_hdmi_context *)
|
||||
to_context(pd->mixer_dev);
|
||||
if (!ctx->mixer_ctx) {
|
||||
DRM_DEBUG_KMS("mixer context is null.\n");
|
||||
ret = -EFAULT;
|
||||
goto err_mixerdrv;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct exynos_drm_subdrv *subdrv;
|
||||
struct drm_hdmi_context *ctx;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
||||
if (!ctx) {
|
||||
DRM_LOG_KMS("failed to alloc common hdmi context.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
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;
|
||||
subdrv->manager.display_ops = &drm_hdmi_display_ops;
|
||||
subdrv->manager.dev = dev;
|
||||
|
||||
platform_set_drvdata(pdev, subdrv);
|
||||
|
||||
INIT_WORK(&ctx->work, exynos_drm_hdmi_late_probe);
|
||||
|
||||
schedule_work(&ctx->work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_runtime_suspend(struct device *dev)
|
||||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmi_runtime_resume(struct device *dev)
|
||||
{
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops hdmi_pm_ops = {
|
||||
.runtime_suspend = hdmi_runtime_suspend,
|
||||
.runtime_resume = hdmi_runtime_resume,
|
||||
};
|
||||
|
||||
static int __devexit exynos_drm_hdmi_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct drm_hdmi_context *ctx = platform_get_drvdata(pdev);
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
exynos_drm_subdrv_unregister(&ctx->subdrv);
|
||||
kfree(ctx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver exynos_drm_common_hdmi_driver = {
|
||||
.probe = exynos_drm_hdmi_probe,
|
||||
.remove = __devexit_p(exynos_drm_hdmi_remove),
|
||||
.driver = {
|
||||
.name = "exynos-drm-hdmi",
|
||||
.owner = THIS_MODULE,
|
||||
.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");
|
|
@ -0,0 +1,73 @@
|
|||
/* exynos_drm_hdmi.h
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* Authoer: 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_HDMI_H_
|
||||
#define _EXYNOS_DRM_HDMI_H_
|
||||
|
||||
/*
|
||||
* exynos hdmi common context structure.
|
||||
*
|
||||
* @drm_dev: pointer to drm_device.
|
||||
* @ctx: pointer to the context of specific device driver.
|
||||
* this context should be hdmi_context or mixer_context.
|
||||
*/
|
||||
struct exynos_drm_hdmi_context {
|
||||
struct drm_device *drm_dev;
|
||||
void *ctx;
|
||||
};
|
||||
|
||||
struct exynos_hdmi_display_ops {
|
||||
bool (*is_connected)(void *ctx);
|
||||
int (*get_edid)(void *ctx, struct drm_connector *connector,
|
||||
u8 *edid, int len);
|
||||
int (*check_timing)(void *ctx, void *timing);
|
||||
int (*power_on)(void *ctx, int mode);
|
||||
};
|
||||
|
||||
struct exynos_hdmi_manager_ops {
|
||||
void (*mode_set)(void *ctx, void *mode);
|
||||
void (*commit)(void *ctx);
|
||||
void (*disable)(void *ctx);
|
||||
};
|
||||
|
||||
struct exynos_hdmi_overlay_ops {
|
||||
int (*enable_vblank)(void *ctx, int pipe);
|
||||
void (*disable_vblank)(void *ctx);
|
||||
void (*win_mode_set)(void *ctx, struct exynos_drm_overlay *overlay);
|
||||
void (*win_commit)(void *ctx, int zpos);
|
||||
void (*win_disable)(void *ctx, int zpos);
|
||||
};
|
||||
|
||||
extern struct platform_driver hdmi_driver;
|
||||
extern struct platform_driver mixer_driver;
|
||||
|
||||
void exynos_drm_display_ops_register(struct exynos_hdmi_display_ops
|
||||
*display_ops);
|
||||
void exynos_drm_manager_ops_register(struct exynos_hdmi_manager_ops
|
||||
*manager_ops);
|
||||
void exynos_drm_overlay_ops_register(struct exynos_hdmi_overlay_ops
|
||||
*overlay_ops);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors: Joonyoung Shim <jy0922.shim@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 "exynos_drm.h"
|
||||
#include "exynos_drm_crtc.h"
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_drm_encoder.h"
|
||||
|
||||
struct exynos_plane {
|
||||
struct drm_plane base;
|
||||
struct exynos_drm_overlay overlay;
|
||||
bool enabled;
|
||||
};
|
||||
|
||||
static int
|
||||
exynos_update_plane(struct drm_plane *plane, struct drm_crtc *crtc,
|
||||
struct drm_framebuffer *fb, int crtc_x, int crtc_y,
|
||||
unsigned int crtc_w, unsigned int crtc_h,
|
||||
uint32_t src_x, uint32_t src_y,
|
||||
uint32_t src_w, uint32_t src_h)
|
||||
{
|
||||
struct exynos_plane *exynos_plane =
|
||||
container_of(plane, struct exynos_plane, base);
|
||||
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
|
||||
struct exynos_drm_crtc_pos pos;
|
||||
unsigned int x = src_x >> 16;
|
||||
unsigned int y = src_y >> 16;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
|
||||
pos.crtc_x = crtc_x;
|
||||
pos.crtc_y = crtc_y;
|
||||
pos.crtc_w = crtc_w;
|
||||
pos.crtc_h = crtc_h;
|
||||
|
||||
pos.fb_x = x;
|
||||
pos.fb_y = y;
|
||||
|
||||
/* TODO: scale feature */
|
||||
ret = exynos_drm_overlay_update(overlay, fb, &crtc->mode, &pos);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
exynos_drm_fn_encoder(crtc, overlay,
|
||||
exynos_drm_encoder_crtc_mode_set);
|
||||
exynos_drm_fn_encoder(crtc, &overlay->zpos,
|
||||
exynos_drm_encoder_crtc_plane_commit);
|
||||
|
||||
exynos_plane->enabled = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_disable_plane(struct drm_plane *plane)
|
||||
{
|
||||
struct exynos_plane *exynos_plane =
|
||||
container_of(plane, struct exynos_plane, base);
|
||||
struct exynos_drm_overlay *overlay = &exynos_plane->overlay;
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
if (!exynos_plane->enabled)
|
||||
return 0;
|
||||
|
||||
exynos_drm_fn_encoder(plane->crtc, &overlay->zpos,
|
||||
exynos_drm_encoder_crtc_disable);
|
||||
|
||||
exynos_plane->enabled = false;
|
||||
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void exynos_plane_destroy(struct drm_plane *plane)
|
||||
{
|
||||
struct exynos_plane *exynos_plane =
|
||||
container_of(plane, struct exynos_plane, base);
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
exynos_disable_plane(plane);
|
||||
drm_plane_cleanup(plane);
|
||||
kfree(exynos_plane);
|
||||
}
|
||||
|
||||
static struct drm_plane_funcs exynos_plane_funcs = {
|
||||
.update_plane = exynos_update_plane,
|
||||
.disable_plane = exynos_disable_plane,
|
||||
.destroy = exynos_plane_destroy,
|
||||
};
|
||||
|
||||
int exynos_plane_init(struct drm_device *dev, unsigned int nr)
|
||||
{
|
||||
struct exynos_plane *exynos_plane;
|
||||
uint32_t possible_crtcs;
|
||||
|
||||
exynos_plane = kzalloc(sizeof(struct exynos_plane), GFP_KERNEL);
|
||||
if (!exynos_plane)
|
||||
return -ENOMEM;
|
||||
|
||||
/* all CRTCs are available */
|
||||
possible_crtcs = (1 << MAX_CRTC) - 1;
|
||||
|
||||
exynos_plane->overlay.zpos = DEFAULT_ZPOS;
|
||||
|
||||
/* TODO: format */
|
||||
return drm_plane_init(dev, &exynos_plane->base, possible_crtcs,
|
||||
&exynos_plane_funcs, NULL, 0, false);
|
||||
}
|
||||
|
||||
int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_exynos_plane_set_zpos *zpos_req = data;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_plane *plane;
|
||||
struct exynos_plane *exynos_plane;
|
||||
int ret = 0;
|
||||
|
||||
DRM_DEBUG_KMS("[%d] %s\n", __LINE__, __func__);
|
||||
|
||||
if (!drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -EINVAL;
|
||||
|
||||
if (zpos_req->zpos < 0 || zpos_req->zpos >= MAX_PLANE) {
|
||||
if (zpos_req->zpos != DEFAULT_ZPOS) {
|
||||
DRM_ERROR("zpos not within limits\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
obj = drm_mode_object_find(dev, zpos_req->plane_id,
|
||||
DRM_MODE_OBJECT_PLANE);
|
||||
if (!obj) {
|
||||
DRM_DEBUG_KMS("Unknown plane ID %d\n",
|
||||
zpos_req->plane_id);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
plane = obj_to_plane(obj);
|
||||
exynos_plane = container_of(plane, struct exynos_plane, base);
|
||||
|
||||
exynos_plane->overlay.zpos = zpos_req->zpos;
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors: Joonyoung Shim <jy0922.shim@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.
|
||||
*
|
||||
*/
|
||||
|
||||
int exynos_plane_init(struct drm_device *dev, unsigned int nr);
|
||||
int exynos_plane_set_zpos_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
* Authors:
|
||||
* Inki Dae <inki.dae@samsung.com>
|
||||
* Seung-Woo Kim <sw0312.kim@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_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);
|
||||
|
||||
extern struct i2c_driver hdmiphy_driver;
|
||||
extern struct i2c_driver ddc_driver;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (C) 2011 Samsung Electronics Co.Ltd
|
||||
* Authors:
|
||||
* Seung-Woo Kim <sw0312.kim@samsung.com>
|
||||
* 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/i2c.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
#include "exynos_drm_drv.h"
|
||||
#include "exynos_hdmi.h"
|
||||
|
||||
|
||||
static int hdmiphy_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
hdmi_attach_hdmiphy_client(client);
|
||||
|
||||
dev_info(&client->adapter->dev, "attached s5p_hdmiphy "
|
||||
"into i2c adapter successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hdmiphy_remove(struct i2c_client *client)
|
||||
{
|
||||
dev_info(&client->adapter->dev, "detached s5p_hdmiphy "
|
||||
"from i2c adapter successfully\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id hdmiphy_id[] = {
|
||||
{ "s5p_hdmiphy", 0 },
|
||||
{ },
|
||||
};
|
||||
|
||||
struct i2c_driver hdmiphy_driver = {
|
||||
.driver = {
|
||||
.name = "s5p-hdmiphy",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = hdmiphy_id,
|
||||
.probe = hdmiphy_probe,
|
||||
.remove = __devexit_p(hdmiphy_remove),
|
||||
.command = NULL,
|
||||
};
|
||||
EXPORT_SYMBOL(hdmiphy_driver);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
*
|
||||
* 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
|
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
*
|
||||
* Cloned from drivers/media/video/s5p-tv/regs-hdmi.h
|
||||
*
|
||||
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* HDMI register header file for Samsung TVOUT driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef SAMSUNG_REGS_HDMI_H
|
||||
#define SAMSUNG_REGS_HDMI_H
|
||||
|
||||
/*
|
||||
* Register part
|
||||
*/
|
||||
|
||||
#define HDMI_CTRL_BASE(x) ((x) + 0x00000000)
|
||||
#define HDMI_CORE_BASE(x) ((x) + 0x00010000)
|
||||
#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)
|
||||
|
||||
/* 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_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_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_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)
|
||||
|
||||
/* Timing generator registers */
|
||||
#define HDMI_TG_CMD HDMI_TG_BASE(0x0000)
|
||||
#define HDMI_TG_H_FSZ_L HDMI_TG_BASE(0x0018)
|
||||
#define HDMI_TG_H_FSZ_H HDMI_TG_BASE(0x001C)
|
||||
#define HDMI_TG_HACT_ST_L HDMI_TG_BASE(0x0020)
|
||||
#define HDMI_TG_HACT_ST_H HDMI_TG_BASE(0x0024)
|
||||
#define HDMI_TG_HACT_SZ_L HDMI_TG_BASE(0x0028)
|
||||
#define HDMI_TG_HACT_SZ_H HDMI_TG_BASE(0x002C)
|
||||
#define HDMI_TG_V_FSZ_L HDMI_TG_BASE(0x0030)
|
||||
#define HDMI_TG_V_FSZ_H HDMI_TG_BASE(0x0034)
|
||||
#define HDMI_TG_VSYNC_L HDMI_TG_BASE(0x0038)
|
||||
#define HDMI_TG_VSYNC_H HDMI_TG_BASE(0x003C)
|
||||
#define HDMI_TG_VSYNC2_L HDMI_TG_BASE(0x0040)
|
||||
#define HDMI_TG_VSYNC2_H HDMI_TG_BASE(0x0044)
|
||||
#define HDMI_TG_VACT_ST_L HDMI_TG_BASE(0x0048)
|
||||
#define HDMI_TG_VACT_ST_H HDMI_TG_BASE(0x004C)
|
||||
#define HDMI_TG_VACT_SZ_L HDMI_TG_BASE(0x0050)
|
||||
#define HDMI_TG_VACT_SZ_H HDMI_TG_BASE(0x0054)
|
||||
#define HDMI_TG_FIELD_CHG_L HDMI_TG_BASE(0x0058)
|
||||
#define HDMI_TG_FIELD_CHG_H HDMI_TG_BASE(0x005C)
|
||||
#define HDMI_TG_VACT_ST2_L HDMI_TG_BASE(0x0060)
|
||||
#define HDMI_TG_VACT_ST2_H HDMI_TG_BASE(0x0064)
|
||||
#define HDMI_TG_VSYNC_TOP_HDMI_L HDMI_TG_BASE(0x0078)
|
||||
#define HDMI_TG_VSYNC_TOP_HDMI_H HDMI_TG_BASE(0x007C)
|
||||
#define HDMI_TG_VSYNC_BOT_HDMI_L HDMI_TG_BASE(0x0080)
|
||||
#define HDMI_TG_VSYNC_BOT_HDMI_H HDMI_TG_BASE(0x0084)
|
||||
#define HDMI_TG_FIELD_TOP_HDMI_L HDMI_TG_BASE(0x0088)
|
||||
#define HDMI_TG_FIELD_TOP_HDMI_H HDMI_TG_BASE(0x008C)
|
||||
#define HDMI_TG_FIELD_BOT_HDMI_L HDMI_TG_BASE(0x0090)
|
||||
#define HDMI_TG_FIELD_BOT_HDMI_H HDMI_TG_BASE(0x0094)
|
||||
|
||||
/*
|
||||
* Bit definition part
|
||||
*/
|
||||
|
||||
/* HDMI_INTC_CON */
|
||||
#define HDMI_INTC_EN_GLOBAL (1 << 6)
|
||||
#define HDMI_INTC_EN_HPD_PLUG (1 << 3)
|
||||
#define HDMI_INTC_EN_HPD_UNPLUG (1 << 2)
|
||||
|
||||
/* HDMI_INTC_FLAG */
|
||||
#define HDMI_INTC_FLAG_HPD_PLUG (1 << 3)
|
||||
#define HDMI_INTC_FLAG_HPD_UNPLUG (1 << 2)
|
||||
|
||||
/* HDMI_PHY_RSTOUT */
|
||||
#define HDMI_PHY_SW_RSTOUT (1 << 0)
|
||||
|
||||
/* HDMI_CORE_RSTOUT */
|
||||
#define HDMI_CORE_SW_RSTOUT (1 << 0)
|
||||
|
||||
/* HDMI_CON_0 */
|
||||
#define HDMI_BLUE_SCR_EN (1 << 5)
|
||||
#define HDMI_EN (1 << 0)
|
||||
|
||||
/* HDMI_PHY_STATUS */
|
||||
#define HDMI_PHY_STATUS_READY (1 << 0)
|
||||
|
||||
/* HDMI_MODE_SEL */
|
||||
#define HDMI_MODE_HDMI_EN (1 << 1)
|
||||
#define HDMI_MODE_DVI_EN (1 << 0)
|
||||
#define HDMI_MODE_MASK (3 << 0)
|
||||
|
||||
/* HDMI_TG_CMD */
|
||||
#define HDMI_TG_EN (1 << 0)
|
||||
#define HDMI_FIELD_EN (1 << 1)
|
||||
|
||||
#endif /* SAMSUNG_REGS_HDMI_H */
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
*
|
||||
* Cloned from drivers/media/video/s5p-tv/regs-mixer.h
|
||||
*
|
||||
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* Mixer register header file for Samsung Mixer driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
#ifndef SAMSUNG_REGS_MIXER_H
|
||||
#define SAMSUNG_REGS_MIXER_H
|
||||
|
||||
/*
|
||||
* Register part
|
||||
*/
|
||||
#define MXR_STATUS 0x0000
|
||||
#define MXR_CFG 0x0004
|
||||
#define MXR_INT_EN 0x0008
|
||||
#define MXR_INT_STATUS 0x000C
|
||||
#define MXR_LAYER_CFG 0x0010
|
||||
#define MXR_VIDEO_CFG 0x0014
|
||||
#define MXR_GRAPHIC0_CFG 0x0020
|
||||
#define MXR_GRAPHIC0_BASE 0x0024
|
||||
#define MXR_GRAPHIC0_SPAN 0x0028
|
||||
#define MXR_GRAPHIC0_SXY 0x002C
|
||||
#define MXR_GRAPHIC0_WH 0x0030
|
||||
#define MXR_GRAPHIC0_DXY 0x0034
|
||||
#define MXR_GRAPHIC0_BLANK 0x0038
|
||||
#define MXR_GRAPHIC1_CFG 0x0040
|
||||
#define MXR_GRAPHIC1_BASE 0x0044
|
||||
#define MXR_GRAPHIC1_SPAN 0x0048
|
||||
#define MXR_GRAPHIC1_SXY 0x004C
|
||||
#define MXR_GRAPHIC1_WH 0x0050
|
||||
#define MXR_GRAPHIC1_DXY 0x0054
|
||||
#define MXR_GRAPHIC1_BLANK 0x0058
|
||||
#define MXR_BG_CFG 0x0060
|
||||
#define MXR_BG_COLOR0 0x0064
|
||||
#define MXR_BG_COLOR1 0x0068
|
||||
#define MXR_BG_COLOR2 0x006C
|
||||
#define MXR_CM_COEFF_Y 0x0080
|
||||
#define MXR_CM_COEFF_CB 0x0084
|
||||
#define MXR_CM_COEFF_CR 0x0088
|
||||
#define MXR_GRAPHIC0_BASE_S 0x2024
|
||||
#define MXR_GRAPHIC1_BASE_S 0x2044
|
||||
|
||||
/* for parametrized access to layer registers */
|
||||
#define MXR_GRAPHIC_CFG(i) (0x0020 + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_BASE(i) (0x0024 + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_SPAN(i) (0x0028 + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_SXY(i) (0x002C + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_WH(i) (0x0030 + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_DXY(i) (0x0034 + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_BLANK(i) (0x0038 + (i) * 0x20)
|
||||
#define MXR_GRAPHIC_BASE_S(i) (0x2024 + (i) * 0x20)
|
||||
|
||||
/*
|
||||
* Bit definition part
|
||||
*/
|
||||
|
||||
/* generates mask for range of bits */
|
||||
#define MXR_MASK(high_bit, low_bit) \
|
||||
(((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
|
||||
|
||||
#define MXR_MASK_VAL(val, high_bit, low_bit) \
|
||||
(((val) << (low_bit)) & MXR_MASK(high_bit, low_bit))
|
||||
|
||||
/* bits for MXR_STATUS */
|
||||
#define MXR_STATUS_16_BURST (1 << 7)
|
||||
#define MXR_STATUS_BURST_MASK (1 << 7)
|
||||
#define MXR_STATUS_BIG_ENDIAN (1 << 3)
|
||||
#define MXR_STATUS_ENDIAN_MASK (1 << 3)
|
||||
#define MXR_STATUS_SYNC_ENABLE (1 << 2)
|
||||
#define MXR_STATUS_REG_RUN (1 << 0)
|
||||
|
||||
/* bits for MXR_CFG */
|
||||
#define MXR_CFG_RGB601_0_255 (0 << 9)
|
||||
#define MXR_CFG_RGB601_16_235 (1 << 9)
|
||||
#define MXR_CFG_RGB709_0_255 (2 << 9)
|
||||
#define MXR_CFG_RGB709_16_235 (3 << 9)
|
||||
#define MXR_CFG_RGB_FMT_MASK 0x600
|
||||
#define MXR_CFG_OUT_YUV444 (0 << 8)
|
||||
#define MXR_CFG_OUT_RGB888 (1 << 8)
|
||||
#define MXR_CFG_OUT_MASK (1 << 8)
|
||||
#define MXR_CFG_DST_SDO (0 << 7)
|
||||
#define MXR_CFG_DST_HDMI (1 << 7)
|
||||
#define MXR_CFG_DST_MASK (1 << 7)
|
||||
#define MXR_CFG_SCAN_HD_720 (0 << 6)
|
||||
#define MXR_CFG_SCAN_HD_1080 (1 << 6)
|
||||
#define MXR_CFG_GRP1_ENABLE (1 << 5)
|
||||
#define MXR_CFG_GRP0_ENABLE (1 << 4)
|
||||
#define MXR_CFG_VP_ENABLE (1 << 3)
|
||||
#define MXR_CFG_SCAN_INTERLACE (0 << 2)
|
||||
#define MXR_CFG_SCAN_PROGRASSIVE (1 << 2)
|
||||
#define MXR_CFG_SCAN_NTSC (0 << 1)
|
||||
#define MXR_CFG_SCAN_PAL (1 << 1)
|
||||
#define MXR_CFG_SCAN_SD (0 << 0)
|
||||
#define MXR_CFG_SCAN_HD (1 << 0)
|
||||
#define MXR_CFG_SCAN_MASK 0x47
|
||||
|
||||
/* bits for MXR_GRAPHICn_CFG */
|
||||
#define MXR_GRP_CFG_COLOR_KEY_DISABLE (1 << 21)
|
||||
#define MXR_GRP_CFG_BLEND_PRE_MUL (1 << 20)
|
||||
#define MXR_GRP_CFG_WIN_BLEND_EN (1 << 17)
|
||||
#define MXR_GRP_CFG_PIXEL_BLEND_EN (1 << 16)
|
||||
#define MXR_GRP_CFG_FORMAT_VAL(x) MXR_MASK_VAL(x, 11, 8)
|
||||
#define MXR_GRP_CFG_FORMAT_MASK MXR_GRP_CFG_FORMAT_VAL(~0)
|
||||
#define MXR_GRP_CFG_ALPHA_VAL(x) MXR_MASK_VAL(x, 7, 0)
|
||||
|
||||
/* bits for MXR_GRAPHICn_WH */
|
||||
#define MXR_GRP_WH_H_SCALE(x) MXR_MASK_VAL(x, 28, 28)
|
||||
#define MXR_GRP_WH_V_SCALE(x) MXR_MASK_VAL(x, 12, 12)
|
||||
#define MXR_GRP_WH_WIDTH(x) MXR_MASK_VAL(x, 26, 16)
|
||||
#define MXR_GRP_WH_HEIGHT(x) MXR_MASK_VAL(x, 10, 0)
|
||||
|
||||
/* bits for MXR_GRAPHICn_SXY */
|
||||
#define MXR_GRP_SXY_SX(x) MXR_MASK_VAL(x, 26, 16)
|
||||
#define MXR_GRP_SXY_SY(x) MXR_MASK_VAL(x, 10, 0)
|
||||
|
||||
/* bits for MXR_GRAPHICn_DXY */
|
||||
#define MXR_GRP_DXY_DX(x) MXR_MASK_VAL(x, 26, 16)
|
||||
#define MXR_GRP_DXY_DY(x) MXR_MASK_VAL(x, 10, 0)
|
||||
|
||||
/* bits for MXR_INT_EN */
|
||||
#define MXR_INT_EN_VSYNC (1 << 11)
|
||||
#define MXR_INT_EN_ALL (0x0f << 8)
|
||||
|
||||
/* bit for MXR_INT_STATUS */
|
||||
#define MXR_INT_CLEAR_VSYNC (1 << 11)
|
||||
#define MXR_INT_STATUS_VSYNC (1 << 0)
|
||||
|
||||
/* bit for MXR_LAYER_CFG */
|
||||
#define MXR_LAYER_CFG_GRP1_VAL(x) MXR_MASK_VAL(x, 11, 8)
|
||||
#define MXR_LAYER_CFG_GRP0_VAL(x) MXR_MASK_VAL(x, 7, 4)
|
||||
#define MXR_LAYER_CFG_VP_VAL(x) MXR_MASK_VAL(x, 3, 0)
|
||||
|
||||
#endif /* SAMSUNG_REGS_MIXER_H */
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
*
|
||||
* Cloned from drivers/media/video/s5p-tv/regs-vp.h
|
||||
*
|
||||
* Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
|
||||
* http://www.samsung.com/
|
||||
*
|
||||
* Video processor register header file for Samsung Mixer driver
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#ifndef SAMSUNG_REGS_VP_H
|
||||
#define SAMSUNG_REGS_VP_H
|
||||
|
||||
/*
|
||||
* Register part
|
||||
*/
|
||||
|
||||
#define VP_ENABLE 0x0000
|
||||
#define VP_SRESET 0x0004
|
||||
#define VP_SHADOW_UPDATE 0x0008
|
||||
#define VP_FIELD_ID 0x000C
|
||||
#define VP_MODE 0x0010
|
||||
#define VP_IMG_SIZE_Y 0x0014
|
||||
#define VP_IMG_SIZE_C 0x0018
|
||||
#define VP_PER_RATE_CTRL 0x001C
|
||||
#define VP_TOP_Y_PTR 0x0028
|
||||
#define VP_BOT_Y_PTR 0x002C
|
||||
#define VP_TOP_C_PTR 0x0030
|
||||
#define VP_BOT_C_PTR 0x0034
|
||||
#define VP_ENDIAN_MODE 0x03CC
|
||||
#define VP_SRC_H_POSITION 0x0044
|
||||
#define VP_SRC_V_POSITION 0x0048
|
||||
#define VP_SRC_WIDTH 0x004C
|
||||
#define VP_SRC_HEIGHT 0x0050
|
||||
#define VP_DST_H_POSITION 0x0054
|
||||
#define VP_DST_V_POSITION 0x0058
|
||||
#define VP_DST_WIDTH 0x005C
|
||||
#define VP_DST_HEIGHT 0x0060
|
||||
#define VP_H_RATIO 0x0064
|
||||
#define VP_V_RATIO 0x0068
|
||||
#define VP_POLY8_Y0_LL 0x006C
|
||||
#define VP_POLY4_Y0_LL 0x00EC
|
||||
#define VP_POLY4_C0_LL 0x012C
|
||||
|
||||
/*
|
||||
* Bit definition part
|
||||
*/
|
||||
|
||||
/* generates mask for range of bits */
|
||||
|
||||
#define VP_MASK(high_bit, low_bit) \
|
||||
(((2 << ((high_bit) - (low_bit))) - 1) << (low_bit))
|
||||
|
||||
#define VP_MASK_VAL(val, high_bit, low_bit) \
|
||||
(((val) << (low_bit)) & VP_MASK(high_bit, low_bit))
|
||||
|
||||
/* VP_ENABLE */
|
||||
#define VP_ENABLE_ON (1 << 0)
|
||||
|
||||
/* VP_SRESET */
|
||||
#define VP_SRESET_PROCESSING (1 << 0)
|
||||
|
||||
/* VP_SHADOW_UPDATE */
|
||||
#define VP_SHADOW_UPDATE_ENABLE (1 << 0)
|
||||
|
||||
/* VP_MODE */
|
||||
#define VP_MODE_NV12 (0 << 6)
|
||||
#define VP_MODE_NV21 (1 << 6)
|
||||
#define VP_MODE_LINE_SKIP (1 << 5)
|
||||
#define VP_MODE_MEM_LINEAR (0 << 4)
|
||||
#define VP_MODE_MEM_TILED (1 << 4)
|
||||
#define VP_MODE_FMT_MASK (5 << 4)
|
||||
#define VP_MODE_FIELD_ID_AUTO_TOGGLING (1 << 2)
|
||||
#define VP_MODE_2D_IPC (1 << 1)
|
||||
|
||||
/* VP_IMG_SIZE_Y */
|
||||
/* VP_IMG_SIZE_C */
|
||||
#define VP_IMG_HSIZE(x) VP_MASK_VAL(x, 29, 16)
|
||||
#define VP_IMG_VSIZE(x) VP_MASK_VAL(x, 13, 0)
|
||||
|
||||
/* VP_SRC_H_POSITION */
|
||||
#define VP_SRC_H_POSITION_VAL(x) VP_MASK_VAL(x, 14, 4)
|
||||
|
||||
/* VP_ENDIAN_MODE */
|
||||
#define VP_ENDIAN_MODE_LITTLE (1 << 0)
|
||||
|
||||
#endif /* SAMSUNG_REGS_VP_H */
|
|
@ -0,0 +1,27 @@
|
|||
config DRM_GMA500
|
||||
tristate "Intel GMA5/600 KMS Framebuffer"
|
||||
depends on DRM && PCI && X86 && EXPERIMENTAL
|
||||
select FB_CFB_COPYAREA
|
||||
select FB_CFB_FILLRECT
|
||||
select FB_CFB_IMAGEBLIT
|
||||
select DRM_KMS_HELPER
|
||||
select DRM_TTM
|
||||
help
|
||||
Say yes for an experimental 2D KMS framebuffer driver for the
|
||||
Intel GMA500 ('Poulsbo') and other Intel IMG based graphics
|
||||
devices.
|
||||
|
||||
config DRM_GMA600
|
||||
bool "Intel GMA600 support (Experimental)"
|
||||
depends on DRM_GMA500
|
||||
help
|
||||
Say yes to include support for GMA600 (Intel Moorestown/Oaktrail)
|
||||
platforms with LVDS ports. HDMI and MIPI are not currently
|
||||
supported.
|
||||
|
||||
config DRM_GMA3600
|
||||
bool "Intel GMA3600/3650 support (Experimental)"
|
||||
depends on DRM_GMA500
|
||||
help
|
||||
Say yes to include basic support for Intel GMA3600/3650 (Intel
|
||||
Cedar Trail) platforms.
|
|
@ -0,0 +1,40 @@
|
|||
#
|
||||
# KMS driver for the GMA500
|
||||
#
|
||||
ccflags-y += -Iinclude/drm
|
||||
|
||||
gma500_gfx-y += gem_glue.o \
|
||||
accel_2d.o \
|
||||
backlight.o \
|
||||
framebuffer.o \
|
||||
gem.o \
|
||||
gtt.o \
|
||||
intel_bios.o \
|
||||
intel_i2c.o \
|
||||
intel_gmbus.o \
|
||||
intel_opregion.o \
|
||||
mmu.o \
|
||||
power.o \
|
||||
psb_drv.o \
|
||||
psb_intel_display.o \
|
||||
psb_intel_lvds.o \
|
||||
psb_intel_modes.o \
|
||||
psb_intel_sdvo.o \
|
||||
psb_lid.o \
|
||||
psb_irq.o \
|
||||
psb_device.o \
|
||||
mid_bios.o
|
||||
|
||||
gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \
|
||||
cdv_intel_crt.o \
|
||||
cdv_intel_display.o \
|
||||
cdv_intel_hdmi.o \
|
||||
cdv_intel_lvds.o
|
||||
|
||||
gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \
|
||||
oaktrail_crtc.o \
|
||||
oaktrail_lvds.o \
|
||||
oaktrail_hdmi.o \
|
||||
oaktrail_hdmi_i2c.o
|
||||
|
||||
obj-$(CONFIG_DRM_GMA500) += gma500_gfx.o
|
|
@ -0,0 +1,364 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
|
||||
* develop this driver.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "framebuffer.h"
|
||||
|
||||
/**
|
||||
* psb_spank - reset the 2D engine
|
||||
* @dev_priv: our PSB DRM device
|
||||
*
|
||||
* Soft reset the graphics engine and then reload the necessary registers.
|
||||
* We use this at initialisation time but it will become relevant for
|
||||
* accelerated X later
|
||||
*/
|
||||
void psb_spank(struct drm_psb_private *dev_priv)
|
||||
{
|
||||
PSB_WSGX32(_PSB_CS_RESET_BIF_RESET | _PSB_CS_RESET_DPM_RESET |
|
||||
_PSB_CS_RESET_TA_RESET | _PSB_CS_RESET_USE_RESET |
|
||||
_PSB_CS_RESET_ISP_RESET | _PSB_CS_RESET_TSP_RESET |
|
||||
_PSB_CS_RESET_TWOD_RESET, PSB_CR_SOFT_RESET);
|
||||
PSB_RSGX32(PSB_CR_SOFT_RESET);
|
||||
|
||||
msleep(1);
|
||||
|
||||
PSB_WSGX32(0, PSB_CR_SOFT_RESET);
|
||||
wmb();
|
||||
PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_CB_CTRL_CLEAR_FAULT,
|
||||
PSB_CR_BIF_CTRL);
|
||||
wmb();
|
||||
(void) PSB_RSGX32(PSB_CR_BIF_CTRL);
|
||||
|
||||
msleep(1);
|
||||
PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) & ~_PSB_CB_CTRL_CLEAR_FAULT,
|
||||
PSB_CR_BIF_CTRL);
|
||||
(void) PSB_RSGX32(PSB_CR_BIF_CTRL);
|
||||
PSB_WSGX32(dev_priv->gtt.gatt_start, PSB_CR_BIF_TWOD_REQ_BASE);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb2_2d_wait_available - wait for FIFO room
|
||||
* @dev_priv: our DRM device
|
||||
* @size: size (in dwords) of the command we want to issue
|
||||
*
|
||||
* Wait until there is room to load the FIFO with our data. If the
|
||||
* device is not responding then reset it
|
||||
*/
|
||||
static int psb_2d_wait_available(struct drm_psb_private *dev_priv,
|
||||
unsigned size)
|
||||
{
|
||||
uint32_t avail = PSB_RSGX32(PSB_CR_2D_SOCIF);
|
||||
unsigned long t = jiffies + HZ;
|
||||
|
||||
while (avail < size) {
|
||||
avail = PSB_RSGX32(PSB_CR_2D_SOCIF);
|
||||
if (time_after(jiffies, t)) {
|
||||
psb_spank(dev_priv);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_2d_submit - submit a 2D command
|
||||
* @dev_priv: our DRM device
|
||||
* @cmdbuf: command to issue
|
||||
* @size: length (in dwords)
|
||||
*
|
||||
* Issue one or more 2D commands to the accelerator. This needs to be
|
||||
* serialized later when we add the GEM interfaces for acceleration
|
||||
*/
|
||||
static int psbfb_2d_submit(struct drm_psb_private *dev_priv, uint32_t *cmdbuf,
|
||||
unsigned size)
|
||||
{
|
||||
int ret = 0;
|
||||
int i;
|
||||
unsigned submit_size;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->lock_2d, flags);
|
||||
while (size > 0) {
|
||||
submit_size = (size < 0x60) ? size : 0x60;
|
||||
size -= submit_size;
|
||||
ret = psb_2d_wait_available(dev_priv, submit_size);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
submit_size <<= 2;
|
||||
|
||||
for (i = 0; i < submit_size; i += 4)
|
||||
PSB_WSGX32(*cmdbuf++, PSB_SGX_2D_SLAVE_PORT + i);
|
||||
|
||||
(void)PSB_RSGX32(PSB_SGX_2D_SLAVE_PORT + i - 4);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev_priv->lock_2d, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* psb_accel_2d_copy_direction - compute blit order
|
||||
* @xdir: X direction of move
|
||||
* @ydir: Y direction of move
|
||||
*
|
||||
* Compute the correct order setings to ensure that an overlapping blit
|
||||
* correctly copies all the pixels.
|
||||
*/
|
||||
static u32 psb_accel_2d_copy_direction(int xdir, int ydir)
|
||||
{
|
||||
if (xdir < 0)
|
||||
return (ydir < 0) ? PSB_2D_COPYORDER_BR2TL :
|
||||
PSB_2D_COPYORDER_TR2BL;
|
||||
else
|
||||
return (ydir < 0) ? PSB_2D_COPYORDER_BL2TR :
|
||||
PSB_2D_COPYORDER_TL2BR;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_accel_2d_copy - accelerated 2D copy
|
||||
* @dev_priv: our DRM device
|
||||
* @src_offset in bytes
|
||||
* @src_stride in bytes
|
||||
* @src_format psb 2D format defines
|
||||
* @dst_offset in bytes
|
||||
* @dst_stride in bytes
|
||||
* @dst_format psb 2D format defines
|
||||
* @src_x offset in pixels
|
||||
* @src_y offset in pixels
|
||||
* @dst_x offset in pixels
|
||||
* @dst_y offset in pixels
|
||||
* @size_x of the copied area
|
||||
* @size_y of the copied area
|
||||
*
|
||||
* Format and issue a 2D accelerated copy command.
|
||||
*/
|
||||
static int psb_accel_2d_copy(struct drm_psb_private *dev_priv,
|
||||
uint32_t src_offset, uint32_t src_stride,
|
||||
uint32_t src_format, uint32_t dst_offset,
|
||||
uint32_t dst_stride, uint32_t dst_format,
|
||||
uint16_t src_x, uint16_t src_y,
|
||||
uint16_t dst_x, uint16_t dst_y,
|
||||
uint16_t size_x, uint16_t size_y)
|
||||
{
|
||||
uint32_t blit_cmd;
|
||||
uint32_t buffer[10];
|
||||
uint32_t *buf;
|
||||
uint32_t direction;
|
||||
|
||||
buf = buffer;
|
||||
|
||||
direction =
|
||||
psb_accel_2d_copy_direction(src_x - dst_x, src_y - dst_y);
|
||||
|
||||
if (direction == PSB_2D_COPYORDER_BR2TL ||
|
||||
direction == PSB_2D_COPYORDER_TR2BL) {
|
||||
src_x += size_x - 1;
|
||||
dst_x += size_x - 1;
|
||||
}
|
||||
if (direction == PSB_2D_COPYORDER_BR2TL ||
|
||||
direction == PSB_2D_COPYORDER_BL2TR) {
|
||||
src_y += size_y - 1;
|
||||
dst_y += size_y - 1;
|
||||
}
|
||||
|
||||
blit_cmd =
|
||||
PSB_2D_BLIT_BH |
|
||||
PSB_2D_ROT_NONE |
|
||||
PSB_2D_DSTCK_DISABLE |
|
||||
PSB_2D_SRCCK_DISABLE |
|
||||
PSB_2D_USE_PAT | PSB_2D_ROP3_SRCCOPY | direction;
|
||||
|
||||
*buf++ = PSB_2D_FENCE_BH;
|
||||
*buf++ =
|
||||
PSB_2D_DST_SURF_BH | dst_format | (dst_stride <<
|
||||
PSB_2D_DST_STRIDE_SHIFT);
|
||||
*buf++ = dst_offset;
|
||||
*buf++ =
|
||||
PSB_2D_SRC_SURF_BH | src_format | (src_stride <<
|
||||
PSB_2D_SRC_STRIDE_SHIFT);
|
||||
*buf++ = src_offset;
|
||||
*buf++ =
|
||||
PSB_2D_SRC_OFF_BH | (src_x << PSB_2D_SRCOFF_XSTART_SHIFT) |
|
||||
(src_y << PSB_2D_SRCOFF_YSTART_SHIFT);
|
||||
*buf++ = blit_cmd;
|
||||
*buf++ =
|
||||
(dst_x << PSB_2D_DST_XSTART_SHIFT) | (dst_y <<
|
||||
PSB_2D_DST_YSTART_SHIFT);
|
||||
*buf++ =
|
||||
(size_x << PSB_2D_DST_XSIZE_SHIFT) | (size_y <<
|
||||
PSB_2D_DST_YSIZE_SHIFT);
|
||||
*buf++ = PSB_2D_FLUSH_BH;
|
||||
|
||||
return psbfb_2d_submit(dev_priv, buffer, buf - buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* psbfb_copyarea_accel - copyarea acceleration for /dev/fb
|
||||
* @info: our framebuffer
|
||||
* @a: copyarea parameters from the framebuffer core
|
||||
*
|
||||
* Perform a 2D copy via the accelerator
|
||||
*/
|
||||
static void psbfb_copyarea_accel(struct fb_info *info,
|
||||
const struct fb_copyarea *a)
|
||||
{
|
||||
struct psb_fbdev *fbdev = info->par;
|
||||
struct psb_framebuffer *psbfb = &fbdev->pfb;
|
||||
struct drm_device *dev = psbfb->base.dev;
|
||||
struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
uint32_t offset;
|
||||
uint32_t stride;
|
||||
uint32_t src_format;
|
||||
uint32_t dst_format;
|
||||
|
||||
if (!fb)
|
||||
return;
|
||||
|
||||
offset = psbfb->gtt->offset;
|
||||
stride = fb->pitches[0];
|
||||
|
||||
switch (fb->depth) {
|
||||
case 8:
|
||||
src_format = PSB_2D_SRC_332RGB;
|
||||
dst_format = PSB_2D_DST_332RGB;
|
||||
break;
|
||||
case 15:
|
||||
src_format = PSB_2D_SRC_555RGB;
|
||||
dst_format = PSB_2D_DST_555RGB;
|
||||
break;
|
||||
case 16:
|
||||
src_format = PSB_2D_SRC_565RGB;
|
||||
dst_format = PSB_2D_DST_565RGB;
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
/* this is wrong but since we don't do blending its okay */
|
||||
src_format = PSB_2D_SRC_8888ARGB;
|
||||
dst_format = PSB_2D_DST_8888ARGB;
|
||||
break;
|
||||
default:
|
||||
/* software fallback */
|
||||
cfb_copyarea(info, a);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gma_power_begin(dev, false)) {
|
||||
cfb_copyarea(info, a);
|
||||
return;
|
||||
}
|
||||
psb_accel_2d_copy(dev_priv,
|
||||
offset, stride, src_format,
|
||||
offset, stride, dst_format,
|
||||
a->sx, a->sy, a->dx, a->dy, a->width, a->height);
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* psbfb_copyarea - 2D copy interface
|
||||
* @info: our framebuffer
|
||||
* @region: region to copy
|
||||
*
|
||||
* Copy an area of the framebuffer console either by the accelerator
|
||||
* or directly using the cfb helpers according to the request
|
||||
*/
|
||||
void psbfb_copyarea(struct fb_info *info,
|
||||
const struct fb_copyarea *region)
|
||||
{
|
||||
if (unlikely(info->state != FBINFO_STATE_RUNNING))
|
||||
return;
|
||||
|
||||
/* Avoid the 8 pixel erratum */
|
||||
if (region->width == 8 || region->height == 8 ||
|
||||
(info->flags & FBINFO_HWACCEL_DISABLED))
|
||||
return cfb_copyarea(info, region);
|
||||
|
||||
psbfb_copyarea_accel(info, region);
|
||||
}
|
||||
|
||||
/**
|
||||
* psbfb_sync - synchronize 2D
|
||||
* @info: our framebuffer
|
||||
*
|
||||
* Wait for the 2D engine to quiesce so that we can do CPU
|
||||
* access to the framebuffer again
|
||||
*/
|
||||
int psbfb_sync(struct fb_info *info)
|
||||
{
|
||||
struct psb_fbdev *fbdev = info->par;
|
||||
struct psb_framebuffer *psbfb = &fbdev->pfb;
|
||||
struct drm_device *dev = psbfb->base.dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long _end = jiffies + DRM_HZ;
|
||||
int busy = 0;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->lock_2d, flags);
|
||||
/*
|
||||
* First idle the 2D engine.
|
||||
*/
|
||||
|
||||
if ((PSB_RSGX32(PSB_CR_2D_SOCIF) == _PSB_C2_SOCIF_EMPTY) &&
|
||||
((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) & _PSB_C2B_STATUS_BUSY) == 0))
|
||||
goto out;
|
||||
|
||||
do {
|
||||
busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY);
|
||||
cpu_relax();
|
||||
} while (busy && !time_after_eq(jiffies, _end));
|
||||
|
||||
if (busy)
|
||||
busy = (PSB_RSGX32(PSB_CR_2D_SOCIF) != _PSB_C2_SOCIF_EMPTY);
|
||||
if (busy)
|
||||
goto out;
|
||||
|
||||
do {
|
||||
busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
|
||||
_PSB_C2B_STATUS_BUSY) != 0);
|
||||
cpu_relax();
|
||||
} while (busy && !time_after_eq(jiffies, _end));
|
||||
if (busy)
|
||||
busy = ((PSB_RSGX32(PSB_CR_2D_BLIT_STATUS) &
|
||||
_PSB_C2B_STATUS_BUSY) != 0);
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&dev_priv->lock_2d, flags);
|
||||
return (busy) ? -EBUSY : 0;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* GMA500 Backlight Interface
|
||||
*
|
||||
* Copyright (c) 2009-2011, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors: Eric Knopp
|
||||
*
|
||||
*/
|
||||
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "intel_bios.h"
|
||||
#include "power.h"
|
||||
|
||||
int gma_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
return dev_priv->ops->backlight_init(dev);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void gma_backlight_exit(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
if (dev_priv->backlight_device) {
|
||||
dev_priv->backlight_device->props.brightness = 0;
|
||||
backlight_update_status(dev_priv->backlight_device);
|
||||
backlight_device_unregister(dev_priv->backlight_device);
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,351 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/backlight.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "intel_bios.h"
|
||||
#include "cdv_device.h"
|
||||
|
||||
#define VGA_SR_INDEX 0x3c4
|
||||
#define VGA_SR_DATA 0x3c5
|
||||
|
||||
static void cdv_disable_vga(struct drm_device *dev)
|
||||
{
|
||||
u8 sr1;
|
||||
u32 vga_reg;
|
||||
|
||||
vga_reg = VGACNTRL;
|
||||
|
||||
outb(1, VGA_SR_INDEX);
|
||||
sr1 = inb(VGA_SR_DATA);
|
||||
outb(sr1 | 1<<5, VGA_SR_DATA);
|
||||
udelay(300);
|
||||
|
||||
REG_WRITE(vga_reg, VGA_DISP_DISABLE);
|
||||
REG_READ(vga_reg);
|
||||
}
|
||||
|
||||
static int cdv_output_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
cdv_disable_vga(dev);
|
||||
|
||||
cdv_intel_crt_init(dev, &dev_priv->mode_dev);
|
||||
cdv_intel_lvds_init(dev, &dev_priv->mode_dev);
|
||||
|
||||
/* These bits indicate HDMI not SDVO on CDV, but we don't yet support
|
||||
the HDMI interface */
|
||||
if (REG_READ(SDVOB) & SDVO_DETECTED)
|
||||
cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB);
|
||||
if (REG_READ(SDVOC) & SDVO_DETECTED)
|
||||
cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
|
||||
/*
|
||||
* Poulsbo Backlight Interfaces
|
||||
*/
|
||||
|
||||
#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */
|
||||
#define BLC_PWM_FREQ_CALC_CONSTANT 32
|
||||
#define MHz 1000000
|
||||
|
||||
#define PSB_BLC_PWM_PRECISION_FACTOR 10
|
||||
#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE
|
||||
#define PSB_BLC_MIN_PWM_REG_FREQ 0x2
|
||||
|
||||
#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
|
||||
#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16)
|
||||
|
||||
static int cdv_brightness;
|
||||
static struct backlight_device *cdv_backlight_device;
|
||||
|
||||
static int cdv_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
/* return locally cached var instead of HW read (due to DPST etc.) */
|
||||
/* FIXME: ideally return actual value in case firmware fiddled with
|
||||
it */
|
||||
return cdv_brightness;
|
||||
}
|
||||
|
||||
|
||||
static int cdv_backlight_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long core_clock;
|
||||
/* u32 bl_max_freq; */
|
||||
/* unsigned long value; */
|
||||
u16 bl_max_freq;
|
||||
uint32_t value;
|
||||
uint32_t blc_pwm_precision_factor;
|
||||
|
||||
/* get bl_max_freq and pol from dev_priv*/
|
||||
if (!dev_priv->lvds_bl) {
|
||||
dev_err(dev->dev, "Has no valid LVDS backlight info\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
bl_max_freq = dev_priv->lvds_bl->freq;
|
||||
blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR;
|
||||
|
||||
core_clock = dev_priv->core_freq;
|
||||
|
||||
value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT;
|
||||
value *= blc_pwm_precision_factor;
|
||||
value /= bl_max_freq;
|
||||
value /= blc_pwm_precision_factor;
|
||||
|
||||
if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ ||
|
||||
value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ)
|
||||
return -ERANGE;
|
||||
else {
|
||||
/* FIXME */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdv_set_brightness(struct backlight_device *bd)
|
||||
{
|
||||
int level = bd->props.brightness;
|
||||
|
||||
/* Percentage 1-100% being valid */
|
||||
if (level < 1)
|
||||
level = 1;
|
||||
|
||||
/*cdv_intel_lvds_set_brightness(dev, level); FIXME */
|
||||
cdv_brightness = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops cdv_ops = {
|
||||
.get_brightness = cdv_get_brightness,
|
||||
.update_status = cdv_set_brightness,
|
||||
};
|
||||
|
||||
static int cdv_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
struct backlight_properties props;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 100;
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
|
||||
cdv_backlight_device = backlight_device_register("psb-bl",
|
||||
NULL, (void *)dev, &cdv_ops, &props);
|
||||
if (IS_ERR(cdv_backlight_device))
|
||||
return PTR_ERR(cdv_backlight_device);
|
||||
|
||||
ret = cdv_backlight_setup(dev);
|
||||
if (ret < 0) {
|
||||
backlight_device_unregister(cdv_backlight_device);
|
||||
cdv_backlight_device = NULL;
|
||||
return ret;
|
||||
}
|
||||
cdv_backlight_device->props.brightness = 100;
|
||||
cdv_backlight_device->props.max_brightness = 100;
|
||||
backlight_update_status(cdv_backlight_device);
|
||||
dev_priv->backlight_device = cdv_backlight_device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provide the Cedarview specific chip logic and low level methods
|
||||
* for power management
|
||||
*
|
||||
* FIXME: we need to implement the apm/ospm base management bits
|
||||
* for this and the MID devices.
|
||||
*/
|
||||
|
||||
static inline u32 CDV_MSG_READ32(uint port, uint offset)
|
||||
{
|
||||
int mcr = (0x10<<24) | (port << 16) | (offset << 8);
|
||||
uint32_t ret_val = 0;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
pci_write_config_dword(pci_root, 0xD0, mcr);
|
||||
pci_read_config_dword(pci_root, 0xD4, &ret_val);
|
||||
pci_dev_put(pci_root);
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
static inline void CDV_MSG_WRITE32(uint port, uint offset, u32 value)
|
||||
{
|
||||
int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
pci_write_config_dword(pci_root, 0xD4, value);
|
||||
pci_write_config_dword(pci_root, 0xD0, mcr);
|
||||
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
|
||||
|
||||
static void cdv_init_pm(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pwr_cnt;
|
||||
int i;
|
||||
|
||||
dev_priv->apm_base = CDV_MSG_READ32(PSB_PUNIT_PORT,
|
||||
PSB_APMBA) & 0xFFFF;
|
||||
dev_priv->ospm_base = CDV_MSG_READ32(PSB_PUNIT_PORT,
|
||||
PSB_OSPMBA) & 0xFFFF;
|
||||
|
||||
/* Force power on for now */
|
||||
pwr_cnt = inl(dev_priv->apm_base + PSB_APM_CMD);
|
||||
pwr_cnt &= ~PSB_PWRGT_GFX_MASK;
|
||||
|
||||
outl(pwr_cnt, dev_priv->apm_base + PSB_APM_CMD);
|
||||
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;
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* cdv_save_display_registers - save registers lost on suspend
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdv_restore_display_registers - restore lost register state
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Restore register state that was lost during suspend and resume.
|
||||
*
|
||||
* FIXME: review
|
||||
*/
|
||||
static int cdv_restore_display_registers(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdv_power_down(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cdv_power_up(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* FIXME ? - shared with Poulsbo */
|
||||
static void cdv_get_core_freq(struct drm_device *dev)
|
||||
{
|
||||
uint32_t clock;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
pci_write_config_dword(pci_root, 0xD0, 0xD0050300);
|
||||
pci_read_config_dword(pci_root, 0xD4, &clock);
|
||||
pci_dev_put(pci_root);
|
||||
|
||||
switch (clock & 0x07) {
|
||||
case 0:
|
||||
dev_priv->core_freq = 100;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->core_freq = 133;
|
||||
break;
|
||||
case 2:
|
||||
dev_priv->core_freq = 150;
|
||||
break;
|
||||
case 3:
|
||||
dev_priv->core_freq = 178;
|
||||
break;
|
||||
case 4:
|
||||
dev_priv->core_freq = 200;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
dev_priv->core_freq = 266;
|
||||
default:
|
||||
dev_priv->core_freq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int cdv_chip_setup(struct drm_device *dev)
|
||||
{
|
||||
cdv_get_core_freq(dev);
|
||||
gma_intel_opregion_init(dev);
|
||||
psb_intel_init_bios(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* CDV is much like Poulsbo but has MID like SGX offsets and PM */
|
||||
|
||||
const struct psb_ops cdv_chip_ops = {
|
||||
.name = "GMA3600/3650",
|
||||
.accel_2d = 0,
|
||||
.pipes = 2,
|
||||
.crtcs = 2,
|
||||
.sgx_offset = MRST_SGX_OFFSET,
|
||||
.chip_setup = cdv_chip_setup,
|
||||
|
||||
.crtc_helper = &cdv_intel_helper_funcs,
|
||||
.crtc_funcs = &cdv_intel_crtc_funcs,
|
||||
|
||||
.output_init = cdv_output_init,
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
.backlight_init = cdv_backlight_init,
|
||||
#endif
|
||||
|
||||
.init_pm = cdv_init_pm,
|
||||
.save_regs = cdv_save_display_registers,
|
||||
.restore_regs = cdv_restore_display_registers,
|
||||
.power_down = cdv_power_down,
|
||||
.power_up = cdv_power_up,
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright © 2011 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*/
|
||||
|
||||
extern const struct drm_crtc_helper_funcs cdv_intel_helper_funcs;
|
||||
extern const struct drm_crtc_funcs cdv_intel_crtc_funcs;
|
||||
extern void cdv_intel_crt_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
extern void cdv_intel_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
extern void cdv_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev,
|
||||
int reg);
|
||||
extern struct drm_display_mode *cdv_intel_crtc_mode_get(struct drm_device *dev,
|
||||
struct drm_crtc *crtc);
|
||||
|
||||
extern inline void cdv_intel_wait_for_vblank(struct drm_device *dev)
|
||||
{
|
||||
/* Wait for 20ms, i.e. one cycle at 50hz. */
|
||||
/* FIXME: msleep ?? */
|
||||
mdelay(20);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,333 @@
|
|||
/*
|
||||
* Copyright © 2006-2007 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "power.h"
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
|
||||
static void cdv_intel_crt_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
u32 temp, reg;
|
||||
reg = ADPA;
|
||||
|
||||
temp = REG_READ(reg);
|
||||
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
|
||||
temp &= ~ADPA_DAC_ENABLE;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
temp |= ADPA_DAC_ENABLE;
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
temp |= ADPA_DAC_ENABLE | ADPA_HSYNC_CNTL_DISABLE;
|
||||
break;
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
temp |= ADPA_DAC_ENABLE | ADPA_VSYNC_CNTL_DISABLE;
|
||||
break;
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
temp |= ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE;
|
||||
break;
|
||||
}
|
||||
|
||||
REG_WRITE(reg, temp);
|
||||
}
|
||||
|
||||
static int cdv_intel_crt_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
int max_clock = 0;
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
/* The lowest clock for CDV is 20000KHz */
|
||||
if (mode->clock < 20000)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
/* The max clock for CDV is 355 instead of 400 */
|
||||
max_clock = 355000;
|
||||
if (mode->clock > max_clock)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
if (mode->hdisplay > 1680 || mode->vdisplay > 1050)
|
||||
return MODE_PANEL;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static bool cdv_intel_crt_mode_fixup(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cdv_intel_crt_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
struct psb_intel_crtc *psb_intel_crtc =
|
||||
to_psb_intel_crtc(crtc);
|
||||
int dpll_md_reg;
|
||||
u32 adpa, dpll_md;
|
||||
u32 adpa_reg;
|
||||
|
||||
if (psb_intel_crtc->pipe == 0)
|
||||
dpll_md_reg = DPLL_A_MD;
|
||||
else
|
||||
dpll_md_reg = DPLL_B_MD;
|
||||
|
||||
adpa_reg = ADPA;
|
||||
|
||||
/*
|
||||
* Disable separate mode multiplier used when cloning SDVO to CRT
|
||||
* XXX this needs to be adjusted when we really are cloning
|
||||
*/
|
||||
{
|
||||
dpll_md = REG_READ(dpll_md_reg);
|
||||
REG_WRITE(dpll_md_reg,
|
||||
dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
|
||||
}
|
||||
|
||||
adpa = 0;
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
adpa |= ADPA_HSYNC_ACTIVE_HIGH;
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
adpa |= ADPA_VSYNC_ACTIVE_HIGH;
|
||||
|
||||
if (psb_intel_crtc->pipe == 0)
|
||||
adpa |= ADPA_PIPE_A_SELECT;
|
||||
else
|
||||
adpa |= ADPA_PIPE_B_SELECT;
|
||||
|
||||
REG_WRITE(adpa_reg, adpa);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
|
||||
*
|
||||
* \return true if CRT is connected.
|
||||
* \return false if CRT is disconnected.
|
||||
*/
|
||||
static bool cdv_intel_crt_detect_hotplug(struct drm_connector *connector,
|
||||
bool force)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
u32 hotplug_en;
|
||||
int i, tries = 0, ret = false;
|
||||
u32 adpa_orig;
|
||||
|
||||
/* disable the DAC when doing the hotplug detection */
|
||||
|
||||
adpa_orig = REG_READ(ADPA);
|
||||
|
||||
REG_WRITE(ADPA, adpa_orig & ~(ADPA_DAC_ENABLE));
|
||||
|
||||
/*
|
||||
* On a CDV thep, CRT detect sequence need to be done twice
|
||||
* to get a reliable result.
|
||||
*/
|
||||
tries = 2;
|
||||
|
||||
hotplug_en = REG_READ(PORT_HOTPLUG_EN);
|
||||
hotplug_en &= ~(CRT_HOTPLUG_DETECT_MASK);
|
||||
hotplug_en |= CRT_HOTPLUG_FORCE_DETECT;
|
||||
|
||||
hotplug_en |= CRT_HOTPLUG_ACTIVATION_PERIOD_64;
|
||||
hotplug_en |= CRT_HOTPLUG_VOLTAGE_COMPARE_50;
|
||||
|
||||
for (i = 0; i < tries ; i++) {
|
||||
unsigned long timeout;
|
||||
/* turn on the FORCE_DETECT */
|
||||
REG_WRITE(PORT_HOTPLUG_EN, hotplug_en);
|
||||
timeout = jiffies + msecs_to_jiffies(1000);
|
||||
/* wait for FORCE_DETECT to go off */
|
||||
do {
|
||||
if (!(REG_READ(PORT_HOTPLUG_EN) &
|
||||
CRT_HOTPLUG_FORCE_DETECT))
|
||||
break;
|
||||
msleep(1);
|
||||
} while (time_after(timeout, jiffies));
|
||||
}
|
||||
|
||||
if ((REG_READ(PORT_HOTPLUG_STAT) & CRT_HOTPLUG_MONITOR_MASK) !=
|
||||
CRT_HOTPLUG_MONITOR_NONE)
|
||||
ret = true;
|
||||
|
||||
/* Restore the saved ADPA */
|
||||
REG_WRITE(ADPA, adpa_orig);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static enum drm_connector_status cdv_intel_crt_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
if (cdv_intel_crt_detect_hotplug(connector, force))
|
||||
return connector_status_connected;
|
||||
else
|
||||
return connector_status_disconnected;
|
||||
}
|
||||
|
||||
static void cdv_intel_crt_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
|
||||
psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
static int cdv_intel_crt_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
return psb_intel_ddc_get_modes(connector, &psb_intel_encoder->ddc_bus->adapter);
|
||||
}
|
||||
|
||||
static int cdv_intel_crt_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
uint64_t value)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Routines for controlling stuff on the analog port
|
||||
*/
|
||||
|
||||
static const struct drm_encoder_helper_funcs cdv_intel_crt_helper_funcs = {
|
||||
.dpms = cdv_intel_crt_dpms,
|
||||
.mode_fixup = cdv_intel_crt_mode_fixup,
|
||||
.prepare = psb_intel_encoder_prepare,
|
||||
.commit = psb_intel_encoder_commit,
|
||||
.mode_set = cdv_intel_crt_mode_set,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs cdv_intel_crt_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = cdv_intel_crt_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = cdv_intel_crt_destroy,
|
||||
.set_property = cdv_intel_crt_set_property,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
cdv_intel_crt_connector_helper_funcs = {
|
||||
.mode_valid = cdv_intel_crt_mode_valid,
|
||||
.get_modes = cdv_intel_crt_get_modes,
|
||||
.best_encoder = psb_intel_best_encoder,
|
||||
};
|
||||
|
||||
static void cdv_intel_crt_enc_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs cdv_intel_crt_enc_funcs = {
|
||||
.destroy = cdv_intel_crt_enc_destroy,
|
||||
};
|
||||
|
||||
void cdv_intel_crt_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
|
||||
struct psb_intel_connector *psb_intel_connector;
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
u32 i2c_reg;
|
||||
|
||||
psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL);
|
||||
if (!psb_intel_encoder)
|
||||
return;
|
||||
|
||||
psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL);
|
||||
if (!psb_intel_connector)
|
||||
goto failed_connector;
|
||||
|
||||
connector = &psb_intel_connector->base;
|
||||
drm_connector_init(dev, connector,
|
||||
&cdv_intel_crt_connector_funcs, DRM_MODE_CONNECTOR_VGA);
|
||||
|
||||
encoder = &psb_intel_encoder->base;
|
||||
drm_encoder_init(dev, encoder,
|
||||
&cdv_intel_crt_enc_funcs, DRM_MODE_ENCODER_DAC);
|
||||
|
||||
psb_intel_connector_attach_encoder(psb_intel_connector,
|
||||
psb_intel_encoder);
|
||||
|
||||
/* Set up the DDC bus. */
|
||||
i2c_reg = GPIOA;
|
||||
/* Remove the following code for CDV */
|
||||
/*
|
||||
if (dev_priv->crt_ddc_bus != 0)
|
||||
i2c_reg = dev_priv->crt_ddc_bus;
|
||||
}*/
|
||||
psb_intel_encoder->ddc_bus = psb_intel_i2c_create(dev,
|
||||
i2c_reg, "CRTDDC_A");
|
||||
if (!psb_intel_encoder->ddc_bus) {
|
||||
dev_printk(KERN_ERR, &dev->pdev->dev, "DDC bus registration "
|
||||
"failed.\n");
|
||||
goto failed_ddc;
|
||||
}
|
||||
|
||||
psb_intel_encoder->type = INTEL_OUTPUT_ANALOG;
|
||||
/*
|
||||
psb_intel_output->clone_mask = (1 << INTEL_ANALOG_CLONE_BIT);
|
||||
psb_intel_output->crtc_mask = (1 << 0) | (1 << 1);
|
||||
*/
|
||||
connector->interlace_allowed = 0;
|
||||
connector->doublescan_allowed = 0;
|
||||
|
||||
drm_encoder_helper_add(encoder, &cdv_intel_crt_helper_funcs);
|
||||
drm_connector_helper_add(connector,
|
||||
&cdv_intel_crt_connector_helper_funcs);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
||||
return;
|
||||
failed_ddc:
|
||||
drm_encoder_cleanup(&psb_intel_encoder->base);
|
||||
drm_connector_cleanup(&psb_intel_connector->base);
|
||||
kfree(psb_intel_connector);
|
||||
failed_connector:
|
||||
kfree(psb_intel_encoder);
|
||||
return;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,394 @@
|
|||
/*
|
||||
* Copyright © 2006-2011 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* jim liu <jim.liu@intel.com>
|
||||
*
|
||||
* FIXME:
|
||||
* We should probably make this generic and share it with Medfield
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_edid.h>
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* hdmi control bits */
|
||||
#define HDMI_NULL_PACKETS_DURING_VSYNC (1 << 9)
|
||||
#define HDMI_BORDER_ENABLE (1 << 7)
|
||||
#define HDMI_AUDIO_ENABLE (1 << 6)
|
||||
#define HDMI_VSYNC_ACTIVE_HIGH (1 << 4)
|
||||
#define HDMI_HSYNC_ACTIVE_HIGH (1 << 3)
|
||||
/* hdmi-b control bits */
|
||||
#define HDMIB_PIPE_B_SELECT (1 << 30)
|
||||
|
||||
|
||||
struct mid_intel_hdmi_priv {
|
||||
u32 hdmi_reg;
|
||||
u32 save_HDMIB;
|
||||
bool has_hdmi_sink;
|
||||
bool has_hdmi_audio;
|
||||
/* Should set this when detect hotplug */
|
||||
bool hdmi_device_connected;
|
||||
struct mdfld_hdmi_i2c *i2c_bus;
|
||||
struct i2c_adapter *hdmi_i2c_adapter; /* for control functions */
|
||||
struct drm_device *dev;
|
||||
};
|
||||
|
||||
static void cdv_hdmi_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct psb_intel_encoder *psb_intel_encoder = to_psb_intel_encoder(encoder);
|
||||
struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
|
||||
u32 hdmib;
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
struct psb_intel_crtc *intel_crtc = to_psb_intel_crtc(crtc);
|
||||
|
||||
hdmib = (2 << 10);
|
||||
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
hdmib |= HDMI_VSYNC_ACTIVE_HIGH;
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
hdmib |= HDMI_HSYNC_ACTIVE_HIGH;
|
||||
|
||||
if (intel_crtc->pipe == 1)
|
||||
hdmib |= HDMIB_PIPE_B_SELECT;
|
||||
|
||||
if (hdmi_priv->has_hdmi_audio) {
|
||||
hdmib |= HDMI_AUDIO_ENABLE;
|
||||
hdmib |= HDMI_NULL_PACKETS_DURING_VSYNC;
|
||||
}
|
||||
|
||||
REG_WRITE(hdmi_priv->hdmi_reg, hdmib);
|
||||
REG_READ(hdmi_priv->hdmi_reg);
|
||||
}
|
||||
|
||||
static bool cdv_hdmi_mode_fixup(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cdv_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
to_psb_intel_encoder(encoder);
|
||||
struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
|
||||
u32 hdmib;
|
||||
|
||||
hdmib = REG_READ(hdmi_priv->hdmi_reg);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
REG_WRITE(hdmi_priv->hdmi_reg, hdmib & ~HDMIB_PORT_EN);
|
||||
else
|
||||
REG_WRITE(hdmi_priv->hdmi_reg, hdmib | HDMIB_PORT_EN);
|
||||
REG_READ(hdmi_priv->hdmi_reg);
|
||||
}
|
||||
|
||||
static void cdv_hdmi_save(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
|
||||
|
||||
hdmi_priv->save_HDMIB = REG_READ(hdmi_priv->hdmi_reg);
|
||||
}
|
||||
|
||||
static void cdv_hdmi_restore(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
|
||||
|
||||
REG_WRITE(hdmi_priv->hdmi_reg, hdmi_priv->save_HDMIB);
|
||||
REG_READ(hdmi_priv->hdmi_reg);
|
||||
}
|
||||
|
||||
static enum drm_connector_status cdv_hdmi_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_connector *psb_intel_connector =
|
||||
to_psb_intel_connector(connector);
|
||||
struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
|
||||
struct edid *edid = NULL;
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
|
||||
edid = drm_get_edid(connector, &psb_intel_encoder->i2c_bus->adapter);
|
||||
|
||||
hdmi_priv->has_hdmi_sink = false;
|
||||
hdmi_priv->has_hdmi_audio = false;
|
||||
if (edid) {
|
||||
if (edid->input & DRM_EDID_INPUT_DIGITAL) {
|
||||
status = connector_status_connected;
|
||||
hdmi_priv->has_hdmi_sink =
|
||||
drm_detect_hdmi_monitor(edid);
|
||||
hdmi_priv->has_hdmi_audio =
|
||||
drm_detect_monitor_audio(edid);
|
||||
}
|
||||
|
||||
psb_intel_connector->base.display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static int cdv_hdmi_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
uint64_t value)
|
||||
{
|
||||
struct drm_encoder *encoder = connector->encoder;
|
||||
|
||||
if (!strcmp(property->name, "scaling mode") && encoder) {
|
||||
struct psb_intel_crtc *crtc = to_psb_intel_crtc(encoder->crtc);
|
||||
bool centre;
|
||||
uint64_t curValue;
|
||||
|
||||
if (!crtc)
|
||||
return -1;
|
||||
|
||||
switch (value) {
|
||||
case DRM_MODE_SCALE_FULLSCREEN:
|
||||
break;
|
||||
case DRM_MODE_SCALE_NO_SCALE:
|
||||
break;
|
||||
case DRM_MODE_SCALE_ASPECT:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (drm_connector_property_get_value(connector,
|
||||
property, &curValue))
|
||||
return -1;
|
||||
|
||||
if (curValue == value)
|
||||
return 0;
|
||||
|
||||
if (drm_connector_property_set_value(connector,
|
||||
property, value))
|
||||
return -1;
|
||||
|
||||
centre = (curValue == DRM_MODE_SCALE_NO_SCALE) ||
|
||||
(value == DRM_MODE_SCALE_NO_SCALE);
|
||||
|
||||
if (crtc->saved_mode.hdisplay != 0 &&
|
||||
crtc->saved_mode.vdisplay != 0) {
|
||||
if (centre) {
|
||||
if (!drm_crtc_helper_set_mode(encoder->crtc, &crtc->saved_mode,
|
||||
encoder->crtc->x, encoder->crtc->y, encoder->crtc->fb))
|
||||
return -1;
|
||||
} else {
|
||||
struct drm_encoder_helper_funcs *helpers
|
||||
= encoder->helper_private;
|
||||
helpers->mode_set(encoder, &crtc->saved_mode,
|
||||
&crtc->saved_adjusted_mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the list of HDMI DDC modes if available.
|
||||
*/
|
||||
static int cdv_hdmi_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct edid *edid = NULL;
|
||||
int ret = 0;
|
||||
|
||||
edid = drm_get_edid(connector, &psb_intel_encoder->i2c_bus->adapter);
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cdv_hdmi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
|
||||
if (mode->clock > 165000)
|
||||
return MODE_CLOCK_HIGH;
|
||||
if (mode->clock < 20000)
|
||||
return MODE_CLOCK_HIGH;
|
||||
|
||||
/* just in case */
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
/* just in case */
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
||||
/*
|
||||
* FIXME: for now we limit the size to 1680x1050 on CDV, otherwise it
|
||||
* will go beyond the stolen memory size allocated to the framebuffer
|
||||
*/
|
||||
if (mode->hdisplay > 1680)
|
||||
return MODE_PANEL;
|
||||
if (mode->vdisplay > 1050)
|
||||
return MODE_PANEL;
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static void cdv_hdmi_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
|
||||
if (psb_intel_encoder->i2c_bus)
|
||||
psb_intel_i2c_destroy(psb_intel_encoder->i2c_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs cdv_hdmi_helper_funcs = {
|
||||
.dpms = cdv_hdmi_dpms,
|
||||
.mode_fixup = cdv_hdmi_mode_fixup,
|
||||
.prepare = psb_intel_encoder_prepare,
|
||||
.mode_set = cdv_hdmi_mode_set,
|
||||
.commit = psb_intel_encoder_commit,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
cdv_hdmi_connector_helper_funcs = {
|
||||
.get_modes = cdv_hdmi_get_modes,
|
||||
.mode_valid = cdv_hdmi_mode_valid,
|
||||
.best_encoder = psb_intel_best_encoder,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs cdv_hdmi_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.save = cdv_hdmi_save,
|
||||
.restore = cdv_hdmi_restore,
|
||||
.detect = cdv_hdmi_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = cdv_hdmi_set_property,
|
||||
.destroy = cdv_hdmi_destroy,
|
||||
};
|
||||
|
||||
void cdv_hdmi_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev, int reg)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
struct psb_intel_connector *psb_intel_connector;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct mid_intel_hdmi_priv *hdmi_priv;
|
||||
int ddc_bus;
|
||||
|
||||
psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!psb_intel_encoder)
|
||||
return;
|
||||
|
||||
psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector),
|
||||
GFP_KERNEL);
|
||||
|
||||
if (!psb_intel_connector)
|
||||
goto err_connector;
|
||||
|
||||
hdmi_priv = kzalloc(sizeof(struct mid_intel_hdmi_priv), GFP_KERNEL);
|
||||
|
||||
if (!hdmi_priv)
|
||||
goto err_priv;
|
||||
|
||||
connector = &psb_intel_connector->base;
|
||||
encoder = &psb_intel_encoder->base;
|
||||
drm_connector_init(dev, connector,
|
||||
&cdv_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DVID);
|
||||
|
||||
drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
|
||||
psb_intel_connector_attach_encoder(psb_intel_connector,
|
||||
psb_intel_encoder);
|
||||
psb_intel_encoder->type = INTEL_OUTPUT_HDMI;
|
||||
hdmi_priv->hdmi_reg = reg;
|
||||
hdmi_priv->has_hdmi_sink = false;
|
||||
psb_intel_encoder->dev_priv = hdmi_priv;
|
||||
|
||||
drm_encoder_helper_add(encoder, &cdv_hdmi_helper_funcs);
|
||||
drm_connector_helper_add(connector,
|
||||
&cdv_hdmi_connector_helper_funcs);
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
||||
drm_connector_attach_property(connector,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
DRM_MODE_SCALE_FULLSCREEN);
|
||||
|
||||
switch (reg) {
|
||||
case SDVOB:
|
||||
ddc_bus = GPIOE;
|
||||
break;
|
||||
case SDVOC:
|
||||
ddc_bus = GPIOD;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("unknown reg 0x%x for HDMI\n", reg);
|
||||
goto failed_ddc;
|
||||
break;
|
||||
}
|
||||
|
||||
psb_intel_encoder->i2c_bus = psb_intel_i2c_create(dev,
|
||||
ddc_bus, (reg == SDVOB) ? "HDMIB" : "HDMIC");
|
||||
|
||||
if (!psb_intel_encoder->i2c_bus) {
|
||||
dev_err(dev->dev, "No ddc adapter available!\n");
|
||||
goto failed_ddc;
|
||||
}
|
||||
|
||||
hdmi_priv->hdmi_i2c_adapter =
|
||||
&(psb_intel_encoder->i2c_bus->adapter);
|
||||
hdmi_priv->dev = dev;
|
||||
drm_sysfs_connector_add(connector);
|
||||
return;
|
||||
|
||||
failed_ddc:
|
||||
drm_encoder_cleanup(encoder);
|
||||
drm_connector_cleanup(connector);
|
||||
err_priv:
|
||||
kfree(psb_intel_connector);
|
||||
err_connector:
|
||||
kfree(psb_intel_encoder);
|
||||
}
|
|
@ -0,0 +1,732 @@
|
|||
/*
|
||||
* Copyright © 2006-2011 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Dave Airlie <airlied@linux.ie>
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "power.h"
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "cdv_device.h"
|
||||
|
||||
/**
|
||||
* LVDS I2C backlight control macros
|
||||
*/
|
||||
#define BRIGHTNESS_MAX_LEVEL 100
|
||||
#define BRIGHTNESS_MASK 0xFF
|
||||
#define BLC_I2C_TYPE 0x01
|
||||
#define BLC_PWM_TYPT 0x02
|
||||
|
||||
#define BLC_POLARITY_NORMAL 0
|
||||
#define BLC_POLARITY_INVERSE 1
|
||||
|
||||
#define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE)
|
||||
#define PSB_BLC_MIN_PWM_REG_FREQ (0x2)
|
||||
#define PSB_BLC_PWM_PRECISION_FACTOR (10)
|
||||
#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16)
|
||||
#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
|
||||
|
||||
struct cdv_intel_lvds_priv {
|
||||
/**
|
||||
* Saved LVDO output states
|
||||
*/
|
||||
uint32_t savePP_ON;
|
||||
uint32_t savePP_OFF;
|
||||
uint32_t saveLVDS;
|
||||
uint32_t savePP_CONTROL;
|
||||
uint32_t savePP_CYCLE;
|
||||
uint32_t savePFIT_CONTROL;
|
||||
uint32_t savePFIT_PGM_RATIOS;
|
||||
uint32_t saveBLC_PWM_CTL;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the maximum level of the backlight duty cycle field.
|
||||
*/
|
||||
static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 retval;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
retval = ((REG_READ(BLC_PWM_CTL) &
|
||||
BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
|
||||
|
||||
gma_power_end(dev);
|
||||
} else
|
||||
retval = ((dev_priv->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set LVDS backlight level by I2C command
|
||||
*/
|
||||
static int cdv_lvds_i2c_set_brightness(struct drm_device *dev,
|
||||
unsigned int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus;
|
||||
u8 out_buf[2];
|
||||
unsigned int blc_i2c_brightness;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = lvds_i2c_bus->slave_addr,
|
||||
.flags = 0,
|
||||
.len = 2,
|
||||
.buf = out_buf,
|
||||
}
|
||||
};
|
||||
|
||||
blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level *
|
||||
BRIGHTNESS_MASK /
|
||||
BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE)
|
||||
blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness;
|
||||
|
||||
out_buf[0] = dev_priv->lvds_bl->brightnesscmd;
|
||||
out_buf[1] = (u8)blc_i2c_brightness;
|
||||
|
||||
if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1)
|
||||
return 0;
|
||||
|
||||
DRM_ERROR("I2C transfer error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int cdv_lvds_pwm_set_brightness(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
u32 max_pwm_blc;
|
||||
u32 blc_pwm_duty_cycle;
|
||||
|
||||
max_pwm_blc = cdv_intel_lvds_get_max_backlight(dev);
|
||||
|
||||
/*BLC_PWM_CTL Should be initiated while backlight device init*/
|
||||
BUG_ON((max_pwm_blc & PSB_BLC_MAX_PWM_REG_FREQ) == 0);
|
||||
|
||||
blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL;
|
||||
|
||||
if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE)
|
||||
blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle;
|
||||
|
||||
blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR;
|
||||
REG_WRITE(BLC_PWM_CTL,
|
||||
(max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) |
|
||||
(blc_pwm_duty_cycle));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set LVDS backlight level either by I2C or PWM
|
||||
*/
|
||||
void cdv_intel_lvds_set_brightness(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!dev_priv->lvds_bl) {
|
||||
DRM_ERROR("NO LVDS Backlight Info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev_priv->lvds_bl->type == BLC_I2C_TYPE)
|
||||
cdv_lvds_i2c_set_brightness(dev, level);
|
||||
else
|
||||
cdv_lvds_pwm_set_brightness(dev, level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the backlight level.
|
||||
*
|
||||
* level backlight level, from 0 to cdv_intel_lvds_get_max_backlight().
|
||||
*/
|
||||
static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 blc_pwm_ctl;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
blc_pwm_ctl =
|
||||
REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
REG_WRITE(BLC_PWM_CTL,
|
||||
(blc_pwm_ctl |
|
||||
(level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
|
||||
gma_power_end(dev);
|
||||
} else {
|
||||
blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL &
|
||||
~BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
|
||||
(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the power state for the panel.
|
||||
*/
|
||||
static void cdv_intel_lvds_set_power(struct drm_device *dev,
|
||||
struct drm_encoder *encoder, bool on)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pp_status;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
|
||||
POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & PP_ON) == 0);
|
||||
|
||||
cdv_intel_lvds_set_backlight(dev,
|
||||
dev_priv->mode_dev.backlight_duty_cycle);
|
||||
} else {
|
||||
cdv_intel_lvds_set_backlight(dev, 0);
|
||||
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
|
||||
~POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while (pp_status & PP_ON);
|
||||
}
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void cdv_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
cdv_intel_lvds_set_power(dev, encoder, true);
|
||||
else
|
||||
cdv_intel_lvds_set_power(dev, encoder, false);
|
||||
/* XXX: We never power down the LVDS pairs. */
|
||||
}
|
||||
|
||||
static void cdv_intel_lvds_save(struct drm_connector *connector)
|
||||
{
|
||||
}
|
||||
|
||||
static void cdv_intel_lvds_restore(struct drm_connector *connector)
|
||||
{
|
||||
}
|
||||
|
||||
int cdv_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_display_mode *fixed_mode =
|
||||
dev_priv->mode_dev.panel_fixed_mode;
|
||||
|
||||
/* just in case */
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
/* just in case */
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
||||
if (fixed_mode) {
|
||||
if (mode->hdisplay > fixed_mode->hdisplay)
|
||||
return MODE_PANEL;
|
||||
if (mode->vdisplay > fixed_mode->vdisplay)
|
||||
return MODE_PANEL;
|
||||
}
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
bool cdv_intel_lvds_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_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
struct drm_encoder *tmp_encoder;
|
||||
struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode;
|
||||
|
||||
/* Should never happen!! */
|
||||
list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list,
|
||||
head) {
|
||||
if (tmp_encoder != encoder
|
||||
&& tmp_encoder->crtc == encoder->crtc) {
|
||||
printk(KERN_ERR "Can't enable LVDS and another "
|
||||
"encoder on the same pipe\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have timings from the BIOS for the panel, put them in
|
||||
* to the adjusted mode. The CRTC will be set up for this mode,
|
||||
* with the panel scaling set up to source from the H/VDisplay
|
||||
* of the original mode.
|
||||
*/
|
||||
if (panel_fixed_mode != NULL) {
|
||||
adjusted_mode->hdisplay = panel_fixed_mode->hdisplay;
|
||||
adjusted_mode->hsync_start = panel_fixed_mode->hsync_start;
|
||||
adjusted_mode->hsync_end = panel_fixed_mode->hsync_end;
|
||||
adjusted_mode->htotal = panel_fixed_mode->htotal;
|
||||
adjusted_mode->vdisplay = panel_fixed_mode->vdisplay;
|
||||
adjusted_mode->vsync_start = panel_fixed_mode->vsync_start;
|
||||
adjusted_mode->vsync_end = panel_fixed_mode->vsync_end;
|
||||
adjusted_mode->vtotal = panel_fixed_mode->vtotal;
|
||||
adjusted_mode->clock = panel_fixed_mode->clock;
|
||||
drm_mode_set_crtcinfo(adjusted_mode,
|
||||
CRTC_INTERLACE_HALVE_V);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: It would be nice to support lower refresh rates on the
|
||||
* panels to reduce power consumption, and perhaps match the
|
||||
* user's requested refresh rate.
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void cdv_intel_lvds_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
|
||||
mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_DUTY_CYCLE_MASK);
|
||||
|
||||
cdv_intel_lvds_set_power(dev, encoder, false);
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void cdv_intel_lvds_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
|
||||
if (mode_dev->backlight_duty_cycle == 0)
|
||||
mode_dev->backlight_duty_cycle =
|
||||
cdv_intel_lvds_get_max_backlight(dev);
|
||||
|
||||
cdv_intel_lvds_set_power(dev, encoder, true);
|
||||
}
|
||||
|
||||
static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pfit_control;
|
||||
|
||||
/*
|
||||
* The LVDS pin pair will already have been turned on in the
|
||||
* cdv_intel_crtc_mode_set since it has a large impact on the DPLL
|
||||
* settings.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Enable automatic panel scaling so that non-native modes fill the
|
||||
* screen. Should be enabled before the pipe is enabled, according to
|
||||
* register description and PRM.
|
||||
*/
|
||||
if (mode->hdisplay != adjusted_mode->hdisplay ||
|
||||
mode->vdisplay != adjusted_mode->vdisplay)
|
||||
pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
|
||||
HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
|
||||
HORIZ_INTERP_BILINEAR);
|
||||
else
|
||||
pfit_control = 0;
|
||||
|
||||
if (dev_priv->lvds_dither)
|
||||
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
|
||||
|
||||
REG_WRITE(PFIT_CONTROL, pfit_control);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect the LVDS connection.
|
||||
*
|
||||
* This always returns CONNECTOR_STATUS_CONNECTED.
|
||||
* This connector should only have
|
||||
* been set up if the LVDS was actually connected anyway.
|
||||
*/
|
||||
static enum drm_connector_status cdv_intel_lvds_detect(
|
||||
struct drm_connector *connector, bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
|
||||
*/
|
||||
static int cdv_intel_lvds_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
int ret;
|
||||
|
||||
ret = psb_intel_ddc_get_modes(connector, &psb_intel_encoder->i2c_bus->adapter);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Didn't get an EDID, so
|
||||
* Set wide sync ranges so we get all modes
|
||||
* handed to valid_mode for checking
|
||||
*/
|
||||
connector->display_info.min_vfreq = 0;
|
||||
connector->display_info.max_vfreq = 200;
|
||||
connector->display_info.min_hfreq = 0;
|
||||
connector->display_info.max_hfreq = 200;
|
||||
if (mode_dev->panel_fixed_mode != NULL) {
|
||||
struct drm_display_mode *mode =
|
||||
drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cdv_intel_lvds_destroy - unregister and free LVDS structures
|
||||
* @connector: connector to free
|
||||
*
|
||||
* Unregister the DDC bus for this connector then free the driver private
|
||||
* structure.
|
||||
*/
|
||||
void cdv_intel_lvds_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
|
||||
if (psb_intel_encoder->i2c_bus)
|
||||
psb_intel_i2c_destroy(psb_intel_encoder->i2c_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
int cdv_intel_lvds_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
uint64_t value)
|
||||
{
|
||||
struct drm_encoder *encoder = connector->encoder;
|
||||
|
||||
if (!strcmp(property->name, "scaling mode") && encoder) {
|
||||
struct psb_intel_crtc *crtc =
|
||||
to_psb_intel_crtc(encoder->crtc);
|
||||
uint64_t curValue;
|
||||
|
||||
if (!crtc)
|
||||
return -1;
|
||||
|
||||
switch (value) {
|
||||
case DRM_MODE_SCALE_FULLSCREEN:
|
||||
break;
|
||||
case DRM_MODE_SCALE_NO_SCALE:
|
||||
break;
|
||||
case DRM_MODE_SCALE_ASPECT:
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (drm_connector_property_get_value(connector,
|
||||
property,
|
||||
&curValue))
|
||||
return -1;
|
||||
|
||||
if (curValue == value)
|
||||
return 0;
|
||||
|
||||
if (drm_connector_property_set_value(connector,
|
||||
property,
|
||||
value))
|
||||
return -1;
|
||||
|
||||
if (crtc->saved_mode.hdisplay != 0 &&
|
||||
crtc->saved_mode.vdisplay != 0) {
|
||||
if (!drm_crtc_helper_set_mode(encoder->crtc,
|
||||
&crtc->saved_mode,
|
||||
encoder->crtc->x,
|
||||
encoder->crtc->y,
|
||||
encoder->crtc->fb))
|
||||
return -1;
|
||||
}
|
||||
} else if (!strcmp(property->name, "backlight") && encoder) {
|
||||
if (drm_connector_property_set_value(connector,
|
||||
property,
|
||||
value))
|
||||
return -1;
|
||||
else {
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv =
|
||||
encoder->dev->dev_private;
|
||||
struct backlight_device *bd =
|
||||
dev_priv->backlight_device;
|
||||
bd->props.brightness = value;
|
||||
backlight_update_status(bd);
|
||||
#endif
|
||||
}
|
||||
} else if (!strcmp(property->name, "DPMS") && encoder) {
|
||||
struct drm_encoder_helper_funcs *helpers =
|
||||
encoder->helper_private;
|
||||
helpers->dpms(encoder, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs
|
||||
cdv_intel_lvds_helper_funcs = {
|
||||
.dpms = cdv_intel_lvds_encoder_dpms,
|
||||
.mode_fixup = cdv_intel_lvds_mode_fixup,
|
||||
.prepare = cdv_intel_lvds_prepare,
|
||||
.mode_set = cdv_intel_lvds_mode_set,
|
||||
.commit = cdv_intel_lvds_commit,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
cdv_intel_lvds_connector_helper_funcs = {
|
||||
.get_modes = cdv_intel_lvds_get_modes,
|
||||
.mode_valid = cdv_intel_lvds_mode_valid,
|
||||
.best_encoder = psb_intel_best_encoder,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.save = cdv_intel_lvds_save,
|
||||
.restore = cdv_intel_lvds_restore,
|
||||
.detect = cdv_intel_lvds_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = cdv_intel_lvds_set_property,
|
||||
.destroy = cdv_intel_lvds_destroy,
|
||||
};
|
||||
|
||||
|
||||
static void cdv_intel_lvds_enc_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
const struct drm_encoder_funcs cdv_intel_lvds_enc_funcs = {
|
||||
.destroy = cdv_intel_lvds_enc_destroy,
|
||||
};
|
||||
|
||||
/**
|
||||
* cdv_intel_lvds_init - setup LVDS connectors on this device
|
||||
* @dev: drm device
|
||||
*
|
||||
* Create the connector, register the LVDS DDC bus, and try to figure out what
|
||||
* modes we can display on the LVDS panel (if present).
|
||||
*/
|
||||
void cdv_intel_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
struct psb_intel_connector *psb_intel_connector;
|
||||
struct cdv_intel_lvds_priv *lvds_priv;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_display_mode *scan;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 lvds;
|
||||
int pipe;
|
||||
|
||||
psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder),
|
||||
GFP_KERNEL);
|
||||
if (!psb_intel_encoder)
|
||||
return;
|
||||
|
||||
psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector),
|
||||
GFP_KERNEL);
|
||||
if (!psb_intel_connector)
|
||||
goto failed_connector;
|
||||
|
||||
lvds_priv = kzalloc(sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL);
|
||||
if (!lvds_priv)
|
||||
goto failed_lvds_priv;
|
||||
|
||||
psb_intel_encoder->dev_priv = lvds_priv;
|
||||
|
||||
connector = &psb_intel_connector->base;
|
||||
encoder = &psb_intel_encoder->base;
|
||||
|
||||
|
||||
drm_connector_init(dev, connector,
|
||||
&cdv_intel_lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
|
||||
drm_encoder_init(dev, encoder,
|
||||
&cdv_intel_lvds_enc_funcs,
|
||||
DRM_MODE_ENCODER_LVDS);
|
||||
|
||||
|
||||
psb_intel_connector_attach_encoder(psb_intel_connector,
|
||||
psb_intel_encoder);
|
||||
psb_intel_encoder->type = INTEL_OUTPUT_LVDS;
|
||||
|
||||
drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs);
|
||||
drm_connector_helper_add(connector,
|
||||
&cdv_intel_lvds_connector_helper_funcs);
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
||||
/*Attach connector properties*/
|
||||
drm_connector_attach_property(connector,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
DRM_MODE_SCALE_FULLSCREEN);
|
||||
drm_connector_attach_property(connector,
|
||||
dev_priv->backlight_property,
|
||||
BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
/**
|
||||
* Set up I2C bus
|
||||
* FIXME: distroy i2c_bus when exit
|
||||
*/
|
||||
psb_intel_encoder->i2c_bus = psb_intel_i2c_create(dev,
|
||||
GPIOB,
|
||||
"LVDSBLC_B");
|
||||
if (!psb_intel_encoder->i2c_bus) {
|
||||
dev_printk(KERN_ERR,
|
||||
&dev->pdev->dev, "I2C bus registration failed.\n");
|
||||
goto failed_blc_i2c;
|
||||
}
|
||||
psb_intel_encoder->i2c_bus->slave_addr = 0x2C;
|
||||
dev_priv->lvds_i2c_bus = psb_intel_encoder->i2c_bus;
|
||||
|
||||
/*
|
||||
* LVDS discovery:
|
||||
* 1) check for EDID on DDC
|
||||
* 2) check for VBT data
|
||||
* 3) check to see if LVDS is already on
|
||||
* if none of the above, no panel
|
||||
* 4) make sure lid is open
|
||||
* if closed, act like it's not there for now
|
||||
*/
|
||||
|
||||
/* Set up the DDC bus. */
|
||||
psb_intel_encoder->ddc_bus = psb_intel_i2c_create(dev,
|
||||
GPIOC,
|
||||
"LVDSDDC_C");
|
||||
if (!psb_intel_encoder->ddc_bus) {
|
||||
dev_printk(KERN_ERR, &dev->pdev->dev,
|
||||
"DDC bus registration " "failed.\n");
|
||||
goto failed_ddc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to get the fixed panel mode from DDC. Assume that the
|
||||
* preferred mode is the right one.
|
||||
*/
|
||||
psb_intel_ddc_get_modes(connector,
|
||||
&psb_intel_encoder->ddc_bus->adapter);
|
||||
list_for_each_entry(scan, &connector->probed_modes, head) {
|
||||
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, scan);
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to get EDID, what about VBT? do we need this?*/
|
||||
if (dev_priv->lfp_lvds_vbt_mode) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode);
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |=
|
||||
DRM_MODE_TYPE_PREFERRED;
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we didn't get EDID, try checking if the panel is already turned
|
||||
* on. If so, assume that whatever is currently programmed is the
|
||||
* correct mode.
|
||||
*/
|
||||
lvds = REG_READ(LVDS);
|
||||
pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
|
||||
crtc = psb_intel_get_crtc_from_pipe(dev, pipe);
|
||||
|
||||
if (crtc && (lvds & LVDS_PORT_EN)) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
cdv_intel_crtc_mode_get(dev, crtc);
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |=
|
||||
DRM_MODE_TYPE_PREFERRED;
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
|
||||
/* If we still don't have a mode after all that, give up. */
|
||||
if (!mode_dev->panel_fixed_mode) {
|
||||
DRM_DEBUG
|
||||
("Found no modes on the lvds, ignoring the LVDS\n");
|
||||
goto failed_find;
|
||||
}
|
||||
|
||||
out:
|
||||
drm_sysfs_connector_add(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
printk(KERN_ERR "Failed find\n");
|
||||
if (psb_intel_encoder->ddc_bus)
|
||||
psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus);
|
||||
failed_ddc:
|
||||
printk(KERN_ERR "Failed DDC\n");
|
||||
if (psb_intel_encoder->i2c_bus)
|
||||
psb_intel_i2c_destroy(psb_intel_encoder->i2c_bus);
|
||||
failed_blc_i2c:
|
||||
printk(KERN_ERR "Failed BLC\n");
|
||||
drm_encoder_cleanup(encoder);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(lvds_priv);
|
||||
failed_lvds_priv:
|
||||
kfree(psb_intel_connector);
|
||||
failed_connector:
|
||||
kfree(psb_intel_encoder);
|
||||
}
|
|
@ -0,0 +1,831 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "framebuffer.h"
|
||||
#include "gtt.h"
|
||||
|
||||
static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb);
|
||||
static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle);
|
||||
|
||||
static const struct drm_framebuffer_funcs psb_fb_funcs = {
|
||||
.destroy = psb_user_framebuffer_destroy,
|
||||
.create_handle = psb_user_framebuffer_create_handle,
|
||||
};
|
||||
|
||||
#define CMAP_TOHW(_val, _width) ((((_val) << (_width)) + 0x7FFF - (_val)) >> 16)
|
||||
|
||||
static int psbfb_setcolreg(unsigned regno, unsigned red, unsigned green,
|
||||
unsigned blue, unsigned transp,
|
||||
struct fb_info *info)
|
||||
{
|
||||
struct psb_fbdev *fbdev = info->par;
|
||||
struct drm_framebuffer *fb = fbdev->psb_fb_helper.fb;
|
||||
uint32_t v;
|
||||
|
||||
if (!fb)
|
||||
return -ENOMEM;
|
||||
|
||||
if (regno > 255)
|
||||
return 1;
|
||||
|
||||
red = CMAP_TOHW(red, info->var.red.length);
|
||||
blue = CMAP_TOHW(blue, info->var.blue.length);
|
||||
green = CMAP_TOHW(green, info->var.green.length);
|
||||
transp = CMAP_TOHW(transp, info->var.transp.length);
|
||||
|
||||
v = (red << info->var.red.offset) |
|
||||
(green << info->var.green.offset) |
|
||||
(blue << info->var.blue.offset) |
|
||||
(transp << info->var.transp.offset);
|
||||
|
||||
if (regno < 16) {
|
||||
switch (fb->bits_per_pixel) {
|
||||
case 16:
|
||||
((uint32_t *) info->pseudo_palette)[regno] = v;
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
((uint32_t *) info->pseudo_palette)[regno] = v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psbfb_pan(struct fb_var_screeninfo *var, struct fb_info *info)
|
||||
{
|
||||
struct psb_fbdev *fbdev = info->par;
|
||||
struct psb_framebuffer *psbfb = &fbdev->pfb;
|
||||
struct drm_device *dev = psbfb->base.dev;
|
||||
|
||||
/*
|
||||
* We have to poke our nose in here. The core fb code assumes
|
||||
* panning is part of the hardware that can be invoked before
|
||||
* the actual fb is mapped. In our case that isn't quite true.
|
||||
*/
|
||||
if (psbfb->gtt->npage) {
|
||||
/* GTT roll shifts in 4K pages, we need to shift the right
|
||||
number of pages */
|
||||
int pages = info->fix.line_length >> 12;
|
||||
psb_gtt_roll(dev, psbfb->gtt, var->yoffset * pages);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psbfb_suspend(struct drm_device *dev)
|
||||
{
|
||||
struct drm_framebuffer *fb = 0;
|
||||
struct psb_framebuffer *psbfb = to_psb_fb(fb);
|
||||
|
||||
console_lock();
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
|
||||
struct fb_info *info = psbfb->fbdev;
|
||||
fb_set_suspend(info, 1);
|
||||
drm_fb_helper_blank(FB_BLANK_POWERDOWN, info);
|
||||
}
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
console_unlock();
|
||||
}
|
||||
|
||||
void psbfb_resume(struct drm_device *dev)
|
||||
{
|
||||
struct drm_framebuffer *fb = 0;
|
||||
struct psb_framebuffer *psbfb = to_psb_fb(fb);
|
||||
|
||||
console_lock();
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(fb, &dev->mode_config.fb_list, head) {
|
||||
struct fb_info *info = psbfb->fbdev;
|
||||
fb_set_suspend(info, 0);
|
||||
drm_fb_helper_blank(FB_BLANK_UNBLANK, info);
|
||||
}
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
console_unlock();
|
||||
drm_helper_disable_unused_functions(dev);
|
||||
}
|
||||
|
||||
static int psbfb_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
struct psb_framebuffer *psbfb = vma->vm_private_data;
|
||||
struct drm_device *dev = psbfb->base.dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int page_num;
|
||||
int i;
|
||||
unsigned long address;
|
||||
int ret;
|
||||
unsigned long pfn;
|
||||
/* FIXME: assumes fb at stolen base which may not be true */
|
||||
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;
|
||||
|
||||
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
||||
|
||||
for (i = 0; i < page_num; i++) {
|
||||
pfn = (phys_addr >> PAGE_SHIFT);
|
||||
|
||||
ret = vm_insert_mixed(vma, address, pfn);
|
||||
if (unlikely((ret == -EBUSY) || (ret != 0 && i > 0)))
|
||||
break;
|
||||
else if (unlikely(ret != 0)) {
|
||||
ret = (ret == -ENOMEM) ? VM_FAULT_OOM : VM_FAULT_SIGBUS;
|
||||
return ret;
|
||||
}
|
||||
address += PAGE_SIZE;
|
||||
phys_addr += PAGE_SIZE;
|
||||
}
|
||||
return VM_FAULT_NOPAGE;
|
||||
}
|
||||
|
||||
static void psbfb_vm_open(struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
static void psbfb_vm_close(struct vm_area_struct *vma)
|
||||
{
|
||||
}
|
||||
|
||||
static struct vm_operations_struct psbfb_vm_ops = {
|
||||
.fault = psbfb_vm_fault,
|
||||
.open = psbfb_vm_open,
|
||||
.close = psbfb_vm_close
|
||||
};
|
||||
|
||||
static int psbfb_mmap(struct fb_info *info, struct vm_area_struct *vma)
|
||||
{
|
||||
struct psb_fbdev *fbdev = info->par;
|
||||
struct psb_framebuffer *psbfb = &fbdev->pfb;
|
||||
|
||||
if (vma->vm_pgoff != 0)
|
||||
return -EINVAL;
|
||||
if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT))
|
||||
return -EINVAL;
|
||||
|
||||
if (!psbfb->addr_space)
|
||||
psbfb->addr_space = vma->vm_file->f_mapping;
|
||||
/*
|
||||
* If this is a GEM object then info->screen_base is the virtual
|
||||
* kernel remapping of the object. FIXME: Review if this is
|
||||
* suitable for our mmap work
|
||||
*/
|
||||
vma->vm_ops = &psbfb_vm_ops;
|
||||
vma->vm_private_data = (void *)psbfb;
|
||||
vma->vm_flags |= VM_RESERVED | VM_IO |
|
||||
VM_MIXEDMAP | VM_DONTEXPAND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psbfb_ioctl(struct fb_info *info, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static struct fb_ops psbfb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_check_var = drm_fb_helper_check_var,
|
||||
.fb_set_par = drm_fb_helper_set_par,
|
||||
.fb_blank = drm_fb_helper_blank,
|
||||
.fb_setcolreg = psbfb_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = psbfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
.fb_mmap = psbfb_mmap,
|
||||
.fb_sync = psbfb_sync,
|
||||
.fb_ioctl = psbfb_ioctl,
|
||||
};
|
||||
|
||||
static struct fb_ops psbfb_roll_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_check_var = drm_fb_helper_check_var,
|
||||
.fb_set_par = drm_fb_helper_set_par,
|
||||
.fb_blank = drm_fb_helper_blank,
|
||||
.fb_setcolreg = psbfb_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
.fb_pan_display = psbfb_pan,
|
||||
.fb_mmap = psbfb_mmap,
|
||||
.fb_sync = psbfb_sync,
|
||||
.fb_ioctl = psbfb_ioctl,
|
||||
};
|
||||
|
||||
static struct fb_ops psbfb_unaccel_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_check_var = drm_fb_helper_check_var,
|
||||
.fb_set_par = drm_fb_helper_set_par,
|
||||
.fb_blank = drm_fb_helper_blank,
|
||||
.fb_setcolreg = psbfb_setcolreg,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
.fb_mmap = psbfb_mmap,
|
||||
.fb_ioctl = psbfb_ioctl,
|
||||
};
|
||||
|
||||
/**
|
||||
* psb_framebuffer_init - initialize a framebuffer
|
||||
* @dev: our DRM device
|
||||
* @fb: framebuffer to set up
|
||||
* @mode_cmd: mode description
|
||||
* @gt: backing object
|
||||
*
|
||||
* Configure and fill in the boilerplate for our frame buffer. Return
|
||||
* 0 on success or an error code if we fail.
|
||||
*/
|
||||
static int psb_framebuffer_init(struct drm_device *dev,
|
||||
struct psb_framebuffer *fb,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct gtt_range *gt)
|
||||
{
|
||||
u32 bpp, depth;
|
||||
int ret;
|
||||
|
||||
drm_fb_get_bpp_depth(mode_cmd->pixel_format, &depth, &bpp);
|
||||
|
||||
if (mode_cmd->pitches[0] & 63)
|
||||
return -EINVAL;
|
||||
switch (bpp) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 24:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
ret = drm_framebuffer_init(dev, &fb->base, &psb_fb_funcs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "framebuffer init failed: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
|
||||
fb->gtt = gt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_framebuffer_create - create a framebuffer backed by gt
|
||||
* @dev: our DRM device
|
||||
* @mode_cmd: the description of the requested mode
|
||||
* @gt: the backing object
|
||||
*
|
||||
* Create a framebuffer object backed by the gt, and fill in the
|
||||
* boilerplate required
|
||||
*
|
||||
* TODO: review object references
|
||||
*/
|
||||
|
||||
static struct drm_framebuffer *psb_framebuffer_create
|
||||
(struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd,
|
||||
struct gtt_range *gt)
|
||||
{
|
||||
struct psb_framebuffer *fb;
|
||||
int ret;
|
||||
|
||||
fb = kzalloc(sizeof(*fb), GFP_KERNEL);
|
||||
if (!fb)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = psb_framebuffer_init(dev, fb, mode_cmd, gt);
|
||||
if (ret) {
|
||||
kfree(fb);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
return &fb->base;
|
||||
}
|
||||
|
||||
/**
|
||||
* psbfb_alloc - allocate frame buffer memory
|
||||
* @dev: the DRM device
|
||||
* @aligned_size: space needed
|
||||
* @force: fall back to GEM buffers if need be
|
||||
*
|
||||
* Allocate the frame buffer. In the usual case we get a GTT range that
|
||||
* is stolen memory backed and life is simple. If there isn't sufficient
|
||||
* we fail as we don't have the virtual mapping space to really vmap it
|
||||
* and the kernel console code can't handle non linear framebuffers.
|
||||
*
|
||||
* Re-address this as and if the framebuffer layer grows this ability.
|
||||
*/
|
||||
static struct gtt_range *psbfb_alloc(struct drm_device *dev, int aligned_size)
|
||||
{
|
||||
struct gtt_range *backing;
|
||||
/* Begin by trying to use stolen memory backing */
|
||||
backing = psb_gtt_alloc_range(dev, aligned_size, "fb", 1);
|
||||
if (backing) {
|
||||
if (drm_gem_private_object_init(dev,
|
||||
&backing->gem, aligned_size) == 0)
|
||||
return backing;
|
||||
psb_gtt_free_range(dev, backing);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* psbfb_create - create a framebuffer
|
||||
* @fbdev: the framebuffer device
|
||||
* @sizes: specification of the layout
|
||||
*
|
||||
* Create a framebuffer to the specifications provided
|
||||
*/
|
||||
static int psbfb_create(struct psb_fbdev *fbdev,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct drm_device *dev = fbdev->psb_fb_helper.dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct fb_info *info;
|
||||
struct drm_framebuffer *fb;
|
||||
struct psb_framebuffer *psbfb = &fbdev->pfb;
|
||||
struct drm_mode_fb_cmd2 mode_cmd;
|
||||
struct device *device = &dev->pdev->dev;
|
||||
int size;
|
||||
int ret;
|
||||
struct gtt_range *backing;
|
||||
u32 bpp, depth;
|
||||
int gtt_roll = 0;
|
||||
int pitch_lines = 0;
|
||||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
bpp = sizes->surface_bpp;
|
||||
|
||||
/* No 24bit packed */
|
||||
if (bpp == 24)
|
||||
bpp = 32;
|
||||
|
||||
do {
|
||||
/*
|
||||
* Acceleration via the GTT requires pitch to be
|
||||
* power of two aligned. Preferably page but less
|
||||
* is ok with some fonts
|
||||
*/
|
||||
mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 4096 >> pitch_lines);
|
||||
depth = sizes->surface_depth;
|
||||
|
||||
size = mode_cmd.pitches[0] * mode_cmd.height;
|
||||
size = ALIGN(size, PAGE_SIZE);
|
||||
|
||||
/* Allocate the fb in the GTT with stolen page backing */
|
||||
backing = psbfb_alloc(dev, size);
|
||||
|
||||
if (pitch_lines)
|
||||
pitch_lines *= 2;
|
||||
else
|
||||
pitch_lines = 1;
|
||||
gtt_roll++;
|
||||
} while (backing == NULL && pitch_lines <= 16);
|
||||
|
||||
/* The final pitch we accepted if we succeeded */
|
||||
pitch_lines /= 2;
|
||||
|
||||
if (backing == NULL) {
|
||||
/*
|
||||
* We couldn't get the space we wanted, fall back to the
|
||||
* display engine requirement instead. The HW requires
|
||||
* the pitch to be 64 byte aligned
|
||||
*/
|
||||
|
||||
gtt_roll = 0; /* Don't use GTT accelerated scrolling */
|
||||
pitch_lines = 64;
|
||||
|
||||
mode_cmd.pitches[0] = ALIGN(mode_cmd.width * ((bpp + 7) / 8), 64);
|
||||
|
||||
size = mode_cmd.pitches[0] * mode_cmd.height;
|
||||
size = ALIGN(size, PAGE_SIZE);
|
||||
|
||||
/* Allocate the framebuffer in the GTT with stolen page backing */
|
||||
backing = psbfb_alloc(dev, size);
|
||||
if (backing == NULL)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
info = framebuffer_alloc(0, device);
|
||||
if (!info) {
|
||||
ret = -ENOMEM;
|
||||
goto out_err1;
|
||||
}
|
||||
info->par = fbdev;
|
||||
|
||||
mode_cmd.pixel_format = drm_mode_legacy_fb_format(bpp, depth);
|
||||
|
||||
ret = psb_framebuffer_init(dev, psbfb, &mode_cmd, backing);
|
||||
if (ret)
|
||||
goto out_unref;
|
||||
|
||||
fb = &psbfb->base;
|
||||
psbfb->fbdev = info;
|
||||
|
||||
fbdev->psb_fb_helper.fb = fb;
|
||||
fbdev->psb_fb_helper.fbdev = info;
|
||||
|
||||
strcpy(info->fix.id, "psbfb");
|
||||
|
||||
info->flags = FBINFO_DEFAULT;
|
||||
if (dev_priv->ops->accel_2d && pitch_lines > 8) /* 2D engine */
|
||||
info->fbops = &psbfb_ops;
|
||||
else if (gtt_roll) { /* GTT rolling seems best */
|
||||
info->fbops = &psbfb_roll_ops;
|
||||
info->flags |= FBINFO_HWACCEL_YPAN;
|
||||
} else /* Software */
|
||||
info->fbops = &psbfb_unaccel_ops;
|
||||
|
||||
ret = fb_alloc_cmap(&info->cmap, 256, 0);
|
||||
if (ret) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unref;
|
||||
}
|
||||
|
||||
info->fix.smem_start = dev->mode_config.fb_base;
|
||||
info->fix.smem_len = size;
|
||||
info->fix.ywrapstep = gtt_roll;
|
||||
info->fix.ypanstep = 0;
|
||||
|
||||
/* Accessed stolen memory directly */
|
||||
info->screen_base = (char *)dev_priv->vram_addr +
|
||||
backing->offset;
|
||||
info->screen_size = size;
|
||||
|
||||
if (dev_priv->gtt.stolen_size) {
|
||||
info->apertures = alloc_apertures(1);
|
||||
if (!info->apertures) {
|
||||
ret = -ENOMEM;
|
||||
goto out_unref;
|
||||
}
|
||||
info->apertures->ranges[0].base = dev->mode_config.fb_base;
|
||||
info->apertures->ranges[0].size = dev_priv->gtt.stolen_size;
|
||||
}
|
||||
|
||||
drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth);
|
||||
drm_fb_helper_fill_var(info, &fbdev->psb_fb_helper,
|
||||
sizes->fb_width, sizes->fb_height);
|
||||
|
||||
info->fix.mmio_start = pci_resource_start(dev->pdev, 0);
|
||||
info->fix.mmio_len = pci_resource_len(dev->pdev, 0);
|
||||
|
||||
info->pixmap.size = 64 * 1024;
|
||||
info->pixmap.buf_align = 8;
|
||||
info->pixmap.access_align = 32;
|
||||
info->pixmap.flags = FB_PIXMAP_SYSTEM;
|
||||
info->pixmap.scan_align = 1;
|
||||
|
||||
dev_info(dev->dev, "allocated %dx%d fb\n",
|
||||
psbfb->base.width, psbfb->base.height);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return 0;
|
||||
out_unref:
|
||||
if (backing->stolen)
|
||||
psb_gtt_free_range(dev, backing);
|
||||
else
|
||||
drm_gem_object_unreference(&backing->gem);
|
||||
out_err1:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
psb_gtt_free_range(dev, backing);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_user_framebuffer_create - create framebuffer
|
||||
* @dev: our DRM device
|
||||
* @filp: client file
|
||||
* @cmd: mode request
|
||||
*
|
||||
* Create a new framebuffer backed by a userspace GEM object
|
||||
*/
|
||||
static struct drm_framebuffer *psb_user_framebuffer_create
|
||||
(struct drm_device *dev, struct drm_file *filp,
|
||||
struct drm_mode_fb_cmd2 *cmd)
|
||||
{
|
||||
struct gtt_range *r;
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
/*
|
||||
* Find the GEM object and thus the gtt range object that is
|
||||
* to back this space
|
||||
*/
|
||||
obj = drm_gem_object_lookup(dev, filp, cmd->handles[0]);
|
||||
if (obj == NULL)
|
||||
return ERR_PTR(-ENOENT);
|
||||
|
||||
/* Let the core code do all the work */
|
||||
r = container_of(obj, struct gtt_range, gem);
|
||||
return psb_framebuffer_create(dev, cmd, r);
|
||||
}
|
||||
|
||||
static void psbfb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
|
||||
u16 blue, int regno)
|
||||
{
|
||||
}
|
||||
|
||||
static void psbfb_gamma_get(struct drm_crtc *crtc, u16 *red,
|
||||
u16 *green, u16 *blue, int regno)
|
||||
{
|
||||
}
|
||||
|
||||
static int psbfb_probe(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct psb_fbdev *psb_fbdev = (struct psb_fbdev *)helper;
|
||||
int new_fb = 0;
|
||||
int ret;
|
||||
|
||||
if (!helper->fb) {
|
||||
ret = psbfb_create(psb_fbdev, sizes);
|
||||
if (ret)
|
||||
return ret;
|
||||
new_fb = 1;
|
||||
}
|
||||
return new_fb;
|
||||
}
|
||||
|
||||
struct drm_fb_helper_funcs psb_fb_helper_funcs = {
|
||||
.gamma_set = psbfb_gamma_set,
|
||||
.gamma_get = psbfb_gamma_get,
|
||||
.fb_probe = psbfb_probe,
|
||||
};
|
||||
|
||||
int psb_fbdev_destroy(struct drm_device *dev, struct psb_fbdev *fbdev)
|
||||
{
|
||||
struct fb_info *info;
|
||||
struct psb_framebuffer *psbfb = &fbdev->pfb;
|
||||
|
||||
if (fbdev->psb_fb_helper.fbdev) {
|
||||
info = fbdev->psb_fb_helper.fbdev;
|
||||
unregister_framebuffer(info);
|
||||
if (info->cmap.len)
|
||||
fb_dealloc_cmap(&info->cmap);
|
||||
framebuffer_release(info);
|
||||
}
|
||||
drm_fb_helper_fini(&fbdev->psb_fb_helper);
|
||||
drm_framebuffer_cleanup(&psbfb->base);
|
||||
|
||||
if (psbfb->gtt)
|
||||
drm_gem_object_unreference(&psbfb->gtt->gem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int psb_fbdev_init(struct drm_device *dev)
|
||||
{
|
||||
struct psb_fbdev *fbdev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
fbdev = kzalloc(sizeof(struct psb_fbdev), GFP_KERNEL);
|
||||
if (!fbdev) {
|
||||
dev_err(dev->dev, "no memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dev_priv->fbdev = fbdev;
|
||||
fbdev->psb_fb_helper.funcs = &psb_fb_helper_funcs;
|
||||
|
||||
drm_fb_helper_init(dev, &fbdev->psb_fb_helper, dev_priv->ops->crtcs,
|
||||
INTELFB_CONN_LIMIT);
|
||||
|
||||
drm_fb_helper_single_add_all_connectors(&fbdev->psb_fb_helper);
|
||||
drm_fb_helper_initial_config(&fbdev->psb_fb_helper, 32);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psb_fbdev_fini(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!dev_priv->fbdev)
|
||||
return;
|
||||
|
||||
psb_fbdev_destroy(dev, dev_priv->fbdev);
|
||||
kfree(dev_priv->fbdev);
|
||||
dev_priv->fbdev = NULL;
|
||||
}
|
||||
|
||||
static void psbfb_output_poll_changed(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_fbdev *fbdev = (struct psb_fbdev *)dev_priv->fbdev;
|
||||
drm_fb_helper_hotplug_event(&fbdev->psb_fb_helper);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_user_framebuffer_create_handle - add hamdle to a framebuffer
|
||||
* @fb: framebuffer
|
||||
* @file_priv: our DRM file
|
||||
* @handle: returned handle
|
||||
*
|
||||
* Our framebuffer object is a GTT range which also contains a GEM
|
||||
* object. We need to turn it into a handle for userspace. GEM will do
|
||||
* the work for us
|
||||
*/
|
||||
static int psb_user_framebuffer_create_handle(struct drm_framebuffer *fb,
|
||||
struct drm_file *file_priv,
|
||||
unsigned int *handle)
|
||||
{
|
||||
struct psb_framebuffer *psbfb = to_psb_fb(fb);
|
||||
struct gtt_range *r = psbfb->gtt;
|
||||
return drm_gem_handle_create(file_priv, &r->gem, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_user_framebuffer_destroy - destruct user created fb
|
||||
* @fb: framebuffer
|
||||
*
|
||||
* User framebuffers are backed by GEM objects so all we have to do is
|
||||
* clean up a bit and drop the reference, GEM will handle the fallout
|
||||
*/
|
||||
static void psb_user_framebuffer_destroy(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct psb_framebuffer *psbfb = to_psb_fb(fb);
|
||||
struct gtt_range *r = psbfb->gtt;
|
||||
struct drm_device *dev = fb->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_fbdev *fbdev = dev_priv->fbdev;
|
||||
struct drm_crtc *crtc;
|
||||
int reset = 0;
|
||||
|
||||
/* Should never get stolen memory for a user fb */
|
||||
WARN_ON(r->stolen);
|
||||
|
||||
/* Check if we are erroneously live */
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
||||
if (crtc->fb == fb)
|
||||
reset = 1;
|
||||
|
||||
if (reset)
|
||||
/*
|
||||
* Now force a sane response before we permit the DRM CRTC
|
||||
* layer to do stupid things like blank the display. Instead
|
||||
* we reset this framebuffer as if the user had forced a reset.
|
||||
* We must do this before the cleanup so that the DRM layer
|
||||
* doesn't get a chance to stick its oar in where it isn't
|
||||
* wanted.
|
||||
*/
|
||||
drm_fb_helper_restore_fbdev_mode(&fbdev->psb_fb_helper);
|
||||
|
||||
/* Let DRM do its clean up */
|
||||
drm_framebuffer_cleanup(fb);
|
||||
/* We are no longer using the resource in GEM */
|
||||
drm_gem_object_unreference_unlocked(&r->gem);
|
||||
kfree(fb);
|
||||
}
|
||||
|
||||
static const struct drm_mode_config_funcs psb_mode_funcs = {
|
||||
.fb_create = psb_user_framebuffer_create,
|
||||
.output_poll_changed = psbfb_output_poll_changed,
|
||||
};
|
||||
|
||||
static int psb_create_backlight_property(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_property *backlight;
|
||||
|
||||
if (dev_priv->backlight_property)
|
||||
return 0;
|
||||
|
||||
backlight = drm_property_create(dev, DRM_MODE_PROP_RANGE,
|
||||
"backlight", 2);
|
||||
backlight->values[0] = 0;
|
||||
backlight->values[1] = 100;
|
||||
|
||||
dev_priv->backlight_property = backlight;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void psb_setup_outputs(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_connector *connector;
|
||||
|
||||
drm_mode_create_scaling_mode_property(dev);
|
||||
psb_create_backlight_property(dev);
|
||||
|
||||
dev_priv->ops->output_init(dev);
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list,
|
||||
head) {
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct drm_encoder *encoder = &psb_intel_encoder->base;
|
||||
int crtc_mask = 0, clone_mask = 0;
|
||||
|
||||
/* valid crtcs */
|
||||
switch (psb_intel_encoder->type) {
|
||||
case INTEL_OUTPUT_ANALOG:
|
||||
crtc_mask = (1 << 0);
|
||||
clone_mask = (1 << INTEL_OUTPUT_ANALOG);
|
||||
break;
|
||||
case INTEL_OUTPUT_SDVO:
|
||||
crtc_mask = ((1 << 0) | (1 << 1));
|
||||
clone_mask = (1 << INTEL_OUTPUT_SDVO);
|
||||
break;
|
||||
case INTEL_OUTPUT_LVDS:
|
||||
if (IS_MRST(dev))
|
||||
crtc_mask = (1 << 0);
|
||||
else
|
||||
crtc_mask = (1 << 1);
|
||||
clone_mask = (1 << INTEL_OUTPUT_LVDS);
|
||||
break;
|
||||
case INTEL_OUTPUT_MIPI:
|
||||
crtc_mask = (1 << 0);
|
||||
clone_mask = (1 << INTEL_OUTPUT_MIPI);
|
||||
break;
|
||||
case INTEL_OUTPUT_MIPI2:
|
||||
crtc_mask = (1 << 2);
|
||||
clone_mask = (1 << INTEL_OUTPUT_MIPI2);
|
||||
break;
|
||||
case INTEL_OUTPUT_HDMI:
|
||||
if (IS_MFLD(dev))
|
||||
crtc_mask = (1 << 1);
|
||||
else
|
||||
crtc_mask = (1 << 0);
|
||||
clone_mask = (1 << INTEL_OUTPUT_HDMI);
|
||||
break;
|
||||
}
|
||||
encoder->possible_crtcs = crtc_mask;
|
||||
encoder->possible_clones =
|
||||
psb_intel_connector_clones(dev, clone_mask);
|
||||
}
|
||||
}
|
||||
|
||||
void psb_modeset_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
int i;
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
|
||||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
|
||||
dev->mode_config.funcs = (void *) &psb_mode_funcs;
|
||||
|
||||
/* set memory base */
|
||||
/* Oaktrail and Poulsbo should use BAR 2*/
|
||||
pci_read_config_dword(dev->pdev, PSB_BSM, (u32 *)
|
||||
&(dev->mode_config.fb_base));
|
||||
|
||||
/* num pipes is 2 for PSB but 1 for Mrst */
|
||||
for (i = 0; i < dev_priv->num_pipe; i++)
|
||||
psb_intel_crtc_init(dev, i, mode_dev);
|
||||
|
||||
dev->mode_config.max_width = 2048;
|
||||
dev->mode_config.max_height = 2048;
|
||||
|
||||
psb_setup_outputs(dev);
|
||||
}
|
||||
|
||||
void psb_modeset_cleanup(struct drm_device *dev)
|
||||
{
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
psb_fbdev_fini(dev);
|
||||
drm_mode_config_cleanup(dev);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* Copyright (c) 2008-2011, Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _FRAMEBUFFER_H_
|
||||
#define _FRAMEBUFFER_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
|
||||
#include "psb_drv.h"
|
||||
|
||||
struct psb_framebuffer {
|
||||
struct drm_framebuffer base;
|
||||
struct address_space *addr_space;
|
||||
struct fb_info *fbdev;
|
||||
struct gtt_range *gtt;
|
||||
};
|
||||
|
||||
struct psb_fbdev {
|
||||
struct drm_fb_helper psb_fb_helper;
|
||||
struct psb_framebuffer pfb;
|
||||
};
|
||||
|
||||
#define to_psb_fb(x) container_of(x, struct psb_framebuffer, base)
|
||||
|
||||
extern int psb_intel_connector_clones(struct drm_device *dev, int type_mask);
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
* psb GEM interface
|
||||
*
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors: Alan Cox
|
||||
*
|
||||
* TODO:
|
||||
* - we need to work out if the MMU is relevant (eg for
|
||||
* accelerated operations on a GEM object)
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
|
||||
int psb_gem_init_object(struct drm_gem_object *obj)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void psb_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
|
||||
drm_gem_object_release_wrap(obj);
|
||||
/* This must occur last as it frees up the memory of the GEM object */
|
||||
psb_gtt_free_range(obj->dev, gtt);
|
||||
}
|
||||
|
||||
int psb_gem_get_aperture(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gem_dumb_map_gtt - buffer mapping for dumb interface
|
||||
* @file: our drm client file
|
||||
* @dev: drm device
|
||||
* @handle: GEM handle to the object (from dumb_create)
|
||||
*
|
||||
* Do the necessary setup to allow the mapping of the frame buffer
|
||||
* into user memory. We don't have to do much here at the moment.
|
||||
*/
|
||||
int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle, uint64_t *offset)
|
||||
{
|
||||
int ret = 0;
|
||||
struct drm_gem_object *obj;
|
||||
|
||||
if (!(dev->driver->driver_features & DRIVER_GEM))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
/* GEM does all our handle to object mapping */
|
||||
obj = drm_gem_object_lookup(dev, file, handle);
|
||||
if (obj == NULL) {
|
||||
ret = -ENOENT;
|
||||
goto unlock;
|
||||
}
|
||||
/* What validation is needed here ? */
|
||||
|
||||
/* Make it mmapable */
|
||||
if (!obj->map_list.map) {
|
||||
ret = gem_create_mmap_offset(obj);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
/* GEM should really work out the hash offsets for us */
|
||||
*offset = (u64)obj->map_list.hash.key << PAGE_SHIFT;
|
||||
out:
|
||||
drm_gem_object_unreference(obj);
|
||||
unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gem_create - create a mappable object
|
||||
* @file: the DRM file of the client
|
||||
* @dev: our device
|
||||
* @size: the size requested
|
||||
* @handlep: returned handle (opaque number)
|
||||
*
|
||||
* Create a GEM object, fill in the boilerplate and attach a handle to
|
||||
* it so that userspace can speak about it. This does the core work
|
||||
* for the various methods that do/will create GEM objects for things
|
||||
*/
|
||||
static int psb_gem_create(struct drm_file *file,
|
||||
struct drm_device *dev, uint64_t size, uint32_t *handlep)
|
||||
{
|
||||
struct gtt_range *r;
|
||||
int ret;
|
||||
u32 handle;
|
||||
|
||||
size = roundup(size, PAGE_SIZE);
|
||||
|
||||
/* Allocate our object - for now a direct gtt range which is not
|
||||
stolen memory backed */
|
||||
r = psb_gtt_alloc_range(dev, size, "gem", 0);
|
||||
if (r == NULL) {
|
||||
dev_err(dev->dev, "no memory for %lld byte GEM object\n", size);
|
||||
return -ENOSPC;
|
||||
}
|
||||
/* Initialize the extra goodies GEM needs to do all the hard work */
|
||||
if (drm_gem_object_init(dev, &r->gem, size) != 0) {
|
||||
psb_gtt_free_range(dev, r);
|
||||
/* GEM doesn't give an error code so use -ENOMEM */
|
||||
dev_err(dev->dev, "GEM init failed for %lld\n", size);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* Give the object a handle so we can carry it more easily */
|
||||
ret = drm_gem_handle_create(file, &r->gem, &handle);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "GEM handle failed for %p, %lld\n",
|
||||
&r->gem, size);
|
||||
drm_gem_object_release(&r->gem);
|
||||
psb_gtt_free_range(dev, r);
|
||||
return ret;
|
||||
}
|
||||
/* We have the initial and handle reference but need only one now */
|
||||
drm_gem_object_unreference(&r->gem);
|
||||
*handlep = handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gem_dumb_create - create a dumb buffer
|
||||
* @drm_file: our client file
|
||||
* @dev: our device
|
||||
* @args: the requested arguments copied from userspace
|
||||
*
|
||||
* Allocate a buffer suitable for use for a frame buffer of the
|
||||
* form described by user space. Give userspace a handle by which
|
||||
* to reference it.
|
||||
*/
|
||||
int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args)
|
||||
{
|
||||
args->pitch = ALIGN(args->width * ((args->bpp + 7) / 8), 64);
|
||||
args->size = args->pitch * args->height;
|
||||
return psb_gem_create(file, dev, args->size, &args->handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gem_dumb_destroy - destroy a dumb buffer
|
||||
* @file: client file
|
||||
* @dev: our DRM device
|
||||
* @handle: the object handle
|
||||
*
|
||||
* Destroy a handle that was created via psb_gem_dumb_create, at least
|
||||
* we hope it was created that way. i915 seems to assume the caller
|
||||
* does the checking but that might be worth review ! FIXME
|
||||
*/
|
||||
int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle)
|
||||
{
|
||||
/* No special work needed, drop the reference and see what falls out */
|
||||
return drm_gem_handle_delete(file, handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gem_fault - pagefault handler for GEM objects
|
||||
* @vma: the VMA of the GEM object
|
||||
* @vmf: fault detail
|
||||
*
|
||||
* Invoked when a fault occurs on an mmap of a GEM managed area. GEM
|
||||
* does most of the work for us including the actual map/unmap calls
|
||||
* but we need to do the actual page work.
|
||||
*
|
||||
* This code eventually needs to handle faulting objects in and out
|
||||
* of the GTT and repacking it when we run out of space. We can put
|
||||
* that off for now and for our simple uses
|
||||
*
|
||||
* The VMA was set up by GEM. In doing so it also ensured that the
|
||||
* vma->vm_private_data points to the GEM object that is backing this
|
||||
* mapping.
|
||||
*/
|
||||
int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||
{
|
||||
struct drm_gem_object *obj;
|
||||
struct gtt_range *r;
|
||||
int ret;
|
||||
unsigned long pfn;
|
||||
pgoff_t page_offset;
|
||||
struct drm_device *dev;
|
||||
struct drm_psb_private *dev_priv;
|
||||
|
||||
obj = vma->vm_private_data; /* GEM object */
|
||||
dev = obj->dev;
|
||||
dev_priv = dev->dev_private;
|
||||
|
||||
r = container_of(obj, struct gtt_range, gem); /* Get the gtt range */
|
||||
|
||||
/* Make sure we don't parallel update on a fault, nor move or remove
|
||||
something from beneath our feet */
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
|
||||
/* For now the mmap pins the object and it stays pinned. As things
|
||||
stand that will do us no harm */
|
||||
if (r->mmapping == 0) {
|
||||
ret = psb_gtt_pin(r);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "gma500: pin failed: %d\n", ret);
|
||||
goto fail;
|
||||
}
|
||||
r->mmapping = 1;
|
||||
}
|
||||
|
||||
/* Page relative to the VMA start - we must calculate this ourselves
|
||||
because vmf->pgoff is the fake GEM offset */
|
||||
page_offset = ((unsigned long) vmf->virtual_address - vma->vm_start)
|
||||
>> PAGE_SHIFT;
|
||||
|
||||
/* CPU view of the page, don't go via the GART for CPU writes */
|
||||
if (r->stolen)
|
||||
pfn = (dev_priv->stolen_base + r->offset) >> PAGE_SHIFT;
|
||||
else
|
||||
pfn = page_to_pfn(r->pages[page_offset]);
|
||||
ret = vm_insert_pfn(vma, (unsigned long)vmf->virtual_address, pfn);
|
||||
|
||||
fail:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
case -ERESTARTSYS:
|
||||
case -EINTR:
|
||||
return VM_FAULT_NOPAGE;
|
||||
case -ENOMEM:
|
||||
return VM_FAULT_OOM;
|
||||
default:
|
||||
return VM_FAULT_SIGBUS;
|
||||
}
|
||||
}
|
||||
|
||||
static int psb_gem_create_stolen(struct drm_file *file, struct drm_device *dev,
|
||||
int size, u32 *handle)
|
||||
{
|
||||
struct gtt_range *gtt = psb_gtt_alloc_range(dev, size, "gem", 1);
|
||||
if (gtt == NULL)
|
||||
return -ENOMEM;
|
||||
if (drm_gem_private_object_init(dev, >t->gem, size) != 0)
|
||||
goto free_gtt;
|
||||
if (drm_gem_handle_create(file, >t->gem, handle) == 0)
|
||||
return 0;
|
||||
free_gtt:
|
||||
psb_gtt_free_range(dev, gtt);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* GEM interfaces for our specific client
|
||||
*/
|
||||
int psb_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_psb_gem_create *args = data;
|
||||
int ret;
|
||||
if (args->flags & GMA_GEM_CREATE_STOLEN) {
|
||||
ret = psb_gem_create_stolen(file, dev, args->size,
|
||||
&args->handle);
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
/* Fall throguh */
|
||||
args->flags &= ~GMA_GEM_CREATE_STOLEN;
|
||||
}
|
||||
return psb_gem_create(file, dev, args->size, &args->handle);
|
||||
}
|
||||
|
||||
int psb_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file)
|
||||
{
|
||||
struct drm_psb_gem_mmap *args = data;
|
||||
return dev->driver->dumb_map_offset(file, dev,
|
||||
args->handle, &args->offset);
|
||||
}
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
|
||||
void drm_gem_object_release_wrap(struct drm_gem_object *obj)
|
||||
{
|
||||
/* Remove the list map if one is present */
|
||||
if (obj->map_list.map) {
|
||||
struct drm_gem_mm *mm = obj->dev->mm_private;
|
||||
struct drm_map_list *list = &obj->map_list;
|
||||
drm_ht_remove_item(&mm->offset_hash, &list->hash);
|
||||
drm_mm_put_block(list->file_offset_node);
|
||||
kfree(list->map);
|
||||
list->map = NULL;
|
||||
}
|
||||
drm_gem_object_release(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* gem_create_mmap_offset - invent an mmap offset
|
||||
* @obj: our object
|
||||
*
|
||||
* Standard implementation of offset generation for mmap as is
|
||||
* duplicated in several drivers. This belongs in GEM.
|
||||
*/
|
||||
int gem_create_mmap_offset(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct drm_gem_mm *mm = dev->mm_private;
|
||||
struct drm_map_list *list;
|
||||
struct drm_local_map *map;
|
||||
int ret;
|
||||
|
||||
list = &obj->map_list;
|
||||
list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
|
||||
if (list->map == NULL)
|
||||
return -ENOMEM;
|
||||
map = list->map;
|
||||
map->type = _DRM_GEM;
|
||||
map->size = obj->size;
|
||||
map->handle = obj;
|
||||
|
||||
list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
|
||||
obj->size / PAGE_SIZE, 0, 0);
|
||||
if (!list->file_offset_node) {
|
||||
dev_err(dev->dev, "failed to allocate offset for bo %d\n",
|
||||
obj->name);
|
||||
ret = -ENOSPC;
|
||||
goto free_it;
|
||||
}
|
||||
list->file_offset_node = drm_mm_get_block(list->file_offset_node,
|
||||
obj->size / PAGE_SIZE, 0);
|
||||
if (!list->file_offset_node) {
|
||||
ret = -ENOMEM;
|
||||
goto free_it;
|
||||
}
|
||||
list->hash.key = list->file_offset_node->start;
|
||||
ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to add to map hash\n");
|
||||
goto free_mm;
|
||||
}
|
||||
return 0;
|
||||
|
||||
free_mm:
|
||||
drm_mm_put_block(list->file_offset_node);
|
||||
free_it:
|
||||
kfree(list->map);
|
||||
list->map = NULL;
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
extern void drm_gem_object_release_wrap(struct drm_gem_object *obj);
|
||||
extern int gem_create_mmap_offset(struct drm_gem_object *obj);
|
|
@ -0,0 +1,553 @@
|
|||
/*
|
||||
* Copyright (c) 2007, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors: Thomas Hellstrom <thomas-at-tungstengraphics.com>
|
||||
* Alan Cox <alan@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "psb_drv.h"
|
||||
|
||||
|
||||
/*
|
||||
* GTT resource allocator - manage page mappings in GTT space
|
||||
*/
|
||||
|
||||
/**
|
||||
* psb_gtt_mask_pte - generate GTT pte entry
|
||||
* @pfn: page number to encode
|
||||
* @type: type of memory in the GTT
|
||||
*
|
||||
* Set the GTT entry for the appropriate memory type.
|
||||
*/
|
||||
static inline uint32_t psb_gtt_mask_pte(uint32_t pfn, int type)
|
||||
{
|
||||
uint32_t mask = PSB_PTE_VALID;
|
||||
|
||||
if (type & PSB_MMU_CACHED_MEMORY)
|
||||
mask |= PSB_PTE_CACHED;
|
||||
if (type & PSB_MMU_RO_MEMORY)
|
||||
mask |= PSB_PTE_RO;
|
||||
if (type & PSB_MMU_WO_MEMORY)
|
||||
mask |= PSB_PTE_WO;
|
||||
|
||||
return (pfn << PAGE_SHIFT) | mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_entry - find the GTT entries for a gtt_range
|
||||
* @dev: our DRM device
|
||||
* @r: our GTT range
|
||||
*
|
||||
* Given a gtt_range object return the GTT offset of the page table
|
||||
* entries for this gtt_range
|
||||
*/
|
||||
u32 *psb_gtt_entry(struct drm_device *dev, struct gtt_range *r)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long offset;
|
||||
|
||||
offset = r->resource.start - dev_priv->gtt_mem->start;
|
||||
|
||||
return dev_priv->gtt_map + (offset >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_insert - put an object into the GTT
|
||||
* @dev: our DRM device
|
||||
* @r: our GTT range
|
||||
*
|
||||
* Take our preallocated GTT range and insert the GEM object into
|
||||
* the GTT. This is protected via the gtt mutex which the caller
|
||||
* must hold.
|
||||
*/
|
||||
static int psb_gtt_insert(struct drm_device *dev, struct gtt_range *r)
|
||||
{
|
||||
u32 *gtt_slot, pte;
|
||||
struct page **pages;
|
||||
int i;
|
||||
|
||||
if (r->pages == NULL) {
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
WARN_ON(r->stolen); /* refcount these maybe ? */
|
||||
|
||||
gtt_slot = psb_gtt_entry(dev, r);
|
||||
pages = r->pages;
|
||||
|
||||
/* Make sure changes are visible to the GPU */
|
||||
set_pages_array_uc(pages, r->npage);
|
||||
|
||||
/* Write our page entries into the GTT itself */
|
||||
for (i = r->roll; i < r->npage; i++) {
|
||||
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
|
||||
iowrite32(pte, gtt_slot++);
|
||||
}
|
||||
for (i = 0; i < r->roll; i++) {
|
||||
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
|
||||
iowrite32(pte, gtt_slot++);
|
||||
}
|
||||
/* Make sure all the entries are set before we return */
|
||||
ioread32(gtt_slot - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_remove - remove an object from the GTT
|
||||
* @dev: our DRM device
|
||||
* @r: our GTT range
|
||||
*
|
||||
* Remove a preallocated GTT range from the GTT. Overwrite all the
|
||||
* page table entries with the dummy page. This is protected via the gtt
|
||||
* mutex which the caller must hold.
|
||||
*/
|
||||
static void psb_gtt_remove(struct drm_device *dev, struct gtt_range *r)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 *gtt_slot, pte;
|
||||
int i;
|
||||
|
||||
WARN_ON(r->stolen);
|
||||
|
||||
gtt_slot = psb_gtt_entry(dev, r);
|
||||
pte = psb_gtt_mask_pte(page_to_pfn(dev_priv->scratch_page), 0);
|
||||
|
||||
for (i = 0; i < r->npage; i++)
|
||||
iowrite32(pte, gtt_slot++);
|
||||
ioread32(gtt_slot - 1);
|
||||
set_pages_array_wb(r->pages, r->npage);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_roll - set scrolling position
|
||||
* @dev: our DRM device
|
||||
* @r: the gtt mapping we are using
|
||||
* @roll: roll offset
|
||||
*
|
||||
* Roll an existing pinned mapping by moving the pages through the GTT.
|
||||
* This allows us to implement hardware scrolling on the consoles without
|
||||
* a 2D engine
|
||||
*/
|
||||
void psb_gtt_roll(struct drm_device *dev, struct gtt_range *r, int roll)
|
||||
{
|
||||
u32 *gtt_slot, pte;
|
||||
int i;
|
||||
|
||||
if (roll >= r->npage) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
r->roll = roll;
|
||||
|
||||
/* Not currently in the GTT - no worry we will write the mapping at
|
||||
the right position when it gets pinned */
|
||||
if (!r->stolen && !r->in_gart)
|
||||
return;
|
||||
|
||||
gtt_slot = psb_gtt_entry(dev, r);
|
||||
|
||||
for (i = r->roll; i < r->npage; i++) {
|
||||
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
|
||||
iowrite32(pte, gtt_slot++);
|
||||
}
|
||||
for (i = 0; i < r->roll; i++) {
|
||||
pte = psb_gtt_mask_pte(page_to_pfn(r->pages[i]), 0);
|
||||
iowrite32(pte, gtt_slot++);
|
||||
}
|
||||
ioread32(gtt_slot - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_attach_pages - attach and pin GEM pages
|
||||
* @gt: the gtt range
|
||||
*
|
||||
* Pin and build an in kernel list of the pages that back our GEM object.
|
||||
* While we hold this the pages cannot be swapped out. This is protected
|
||||
* via the gtt mutex which the caller must hold.
|
||||
*/
|
||||
static int psb_gtt_attach_pages(struct gtt_range *gt)
|
||||
{
|
||||
struct inode *inode;
|
||||
struct address_space *mapping;
|
||||
int i;
|
||||
struct page *p;
|
||||
int pages = gt->gem.size / PAGE_SIZE;
|
||||
|
||||
WARN_ON(gt->pages);
|
||||
|
||||
/* This is the shared memory object that backs the GEM resource */
|
||||
inode = gt->gem.filp->f_path.dentry->d_inode;
|
||||
mapping = inode->i_mapping;
|
||||
|
||||
gt->pages = kmalloc(pages * sizeof(struct page *), GFP_KERNEL);
|
||||
if (gt->pages == NULL)
|
||||
return -ENOMEM;
|
||||
gt->npage = pages;
|
||||
|
||||
for (i = 0; i < pages; i++) {
|
||||
/* FIXME: needs updating as per mail from Hugh Dickins */
|
||||
p = read_cache_page_gfp(mapping, i,
|
||||
__GFP_COLD | GFP_KERNEL);
|
||||
if (IS_ERR(p))
|
||||
goto err;
|
||||
gt->pages[i] = p;
|
||||
}
|
||||
return 0;
|
||||
|
||||
err:
|
||||
while (i--)
|
||||
page_cache_release(gt->pages[i]);
|
||||
kfree(gt->pages);
|
||||
gt->pages = NULL;
|
||||
return PTR_ERR(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_detach_pages - attach and pin GEM pages
|
||||
* @gt: the gtt range
|
||||
*
|
||||
* Undo the effect of psb_gtt_attach_pages. At this point the pages
|
||||
* must have been removed from the GTT as they could now be paged out
|
||||
* and move bus address. This is protected via the gtt mutex which the
|
||||
* caller must hold.
|
||||
*/
|
||||
static void psb_gtt_detach_pages(struct gtt_range *gt)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < gt->npage; i++) {
|
||||
/* FIXME: do we need to force dirty */
|
||||
set_page_dirty(gt->pages[i]);
|
||||
page_cache_release(gt->pages[i]);
|
||||
}
|
||||
kfree(gt->pages);
|
||||
gt->pages = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_pin - pin pages into the GTT
|
||||
* @gt: range to pin
|
||||
*
|
||||
* Pin a set of pages into the GTT. The pins are refcounted so that
|
||||
* multiple pins need multiple unpins to undo.
|
||||
*
|
||||
* Non GEM backed objects treat this as a no-op as they are always GTT
|
||||
* backed objects.
|
||||
*/
|
||||
int psb_gtt_pin(struct gtt_range *gt)
|
||||
{
|
||||
int ret = 0;
|
||||
struct drm_device *dev = gt->gem.dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
mutex_lock(&dev_priv->gtt_mutex);
|
||||
|
||||
if (gt->in_gart == 0 && gt->stolen == 0) {
|
||||
ret = psb_gtt_attach_pages(gt);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
ret = psb_gtt_insert(dev, gt);
|
||||
if (ret < 0) {
|
||||
psb_gtt_detach_pages(gt);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
gt->in_gart++;
|
||||
out:
|
||||
mutex_unlock(&dev_priv->gtt_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_unpin - Drop a GTT pin requirement
|
||||
* @gt: range to pin
|
||||
*
|
||||
* Undoes the effect of psb_gtt_pin. On the last drop the GEM object
|
||||
* will be removed from the GTT which will also drop the page references
|
||||
* and allow the VM to clean up or page stuff.
|
||||
*
|
||||
* Non GEM backed objects treat this as a no-op as they are always GTT
|
||||
* backed objects.
|
||||
*/
|
||||
void psb_gtt_unpin(struct gtt_range *gt)
|
||||
{
|
||||
struct drm_device *dev = gt->gem.dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
mutex_lock(&dev_priv->gtt_mutex);
|
||||
|
||||
WARN_ON(!gt->in_gart);
|
||||
|
||||
gt->in_gart--;
|
||||
if (gt->in_gart == 0 && gt->stolen == 0) {
|
||||
psb_gtt_remove(dev, gt);
|
||||
psb_gtt_detach_pages(gt);
|
||||
}
|
||||
mutex_unlock(&dev_priv->gtt_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* GTT resource allocator - allocate and manage GTT address space
|
||||
*/
|
||||
|
||||
/**
|
||||
* psb_gtt_alloc_range - allocate GTT address space
|
||||
* @dev: Our DRM device
|
||||
* @len: length (bytes) of address space required
|
||||
* @name: resource name
|
||||
* @backed: resource should be backed by stolen pages
|
||||
*
|
||||
* Ask the kernel core to find us a suitable range of addresses
|
||||
* to use for a GTT mapping.
|
||||
*
|
||||
* Returns a gtt_range structure describing the object, or NULL on
|
||||
* error. On successful return the resource is both allocated and marked
|
||||
* as in use.
|
||||
*/
|
||||
struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
|
||||
const char *name, int backed)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct gtt_range *gt;
|
||||
struct resource *r = dev_priv->gtt_mem;
|
||||
int ret;
|
||||
unsigned long start, end;
|
||||
|
||||
if (backed) {
|
||||
/* The start of the GTT is the stolen pages */
|
||||
start = r->start;
|
||||
end = r->start + dev_priv->gtt.stolen_size - 1;
|
||||
} else {
|
||||
/* The rest we will use for GEM backed objects */
|
||||
start = r->start + dev_priv->gtt.stolen_size;
|
||||
end = r->end;
|
||||
}
|
||||
|
||||
gt = kzalloc(sizeof(struct gtt_range), GFP_KERNEL);
|
||||
if (gt == NULL)
|
||||
return NULL;
|
||||
gt->resource.name = name;
|
||||
gt->stolen = backed;
|
||||
gt->in_gart = backed;
|
||||
gt->roll = 0;
|
||||
/* Ensure this is set for non GEM objects */
|
||||
gt->gem.dev = dev;
|
||||
ret = allocate_resource(dev_priv->gtt_mem, >->resource,
|
||||
len, start, end, PAGE_SIZE, NULL, NULL);
|
||||
if (ret == 0) {
|
||||
gt->offset = gt->resource.start - r->start;
|
||||
return gt;
|
||||
}
|
||||
kfree(gt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_gtt_free_range - release GTT address space
|
||||
* @dev: our DRM device
|
||||
* @gt: a mapping created with psb_gtt_alloc_range
|
||||
*
|
||||
* Release a resource that was allocated with psb_gtt_alloc_range. If the
|
||||
* object has been pinned by mmap users we clean this up here currently.
|
||||
*/
|
||||
void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt)
|
||||
{
|
||||
/* Undo the mmap pin if we are destroying the object */
|
||||
if (gt->mmapping) {
|
||||
psb_gtt_unpin(gt);
|
||||
gt->mmapping = 0;
|
||||
}
|
||||
WARN_ON(gt->in_gart && !gt->stolen);
|
||||
release_resource(>->resource);
|
||||
kfree(gt);
|
||||
}
|
||||
|
||||
void psb_gtt_alloc(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
init_rwsem(&dev_priv->gtt.sem);
|
||||
}
|
||||
|
||||
void psb_gtt_takedown(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (dev_priv->gtt_map) {
|
||||
iounmap(dev_priv->gtt_map);
|
||||
dev_priv->gtt_map = NULL;
|
||||
}
|
||||
if (dev_priv->gtt_initialized) {
|
||||
pci_write_config_word(dev->pdev, PSB_GMCH_CTRL,
|
||||
dev_priv->gmch_ctrl);
|
||||
PSB_WVDC32(dev_priv->pge_ctl, PSB_PGETBL_CTL);
|
||||
(void) PSB_RVDC32(PSB_PGETBL_CTL);
|
||||
}
|
||||
if (dev_priv->vram_addr)
|
||||
iounmap(dev_priv->gtt_map);
|
||||
}
|
||||
|
||||
int psb_gtt_init(struct drm_device *dev, int resume)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned gtt_pages;
|
||||
unsigned long stolen_size, vram_stolen_size;
|
||||
unsigned i, num_pages;
|
||||
unsigned pfn_base;
|
||||
uint32_t vram_pages;
|
||||
uint32_t dvmt_mode = 0;
|
||||
struct psb_gtt *pg;
|
||||
|
||||
int ret = 0;
|
||||
uint32_t pte;
|
||||
|
||||
mutex_init(&dev_priv->gtt_mutex);
|
||||
|
||||
psb_gtt_alloc(dev);
|
||||
pg = &dev_priv->gtt;
|
||||
|
||||
/* Enable the GTT */
|
||||
pci_read_config_word(dev->pdev, PSB_GMCH_CTRL, &dev_priv->gmch_ctrl);
|
||||
pci_write_config_word(dev->pdev, PSB_GMCH_CTRL,
|
||||
dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED);
|
||||
|
||||
dev_priv->pge_ctl = PSB_RVDC32(PSB_PGETBL_CTL);
|
||||
PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL);
|
||||
(void) PSB_RVDC32(PSB_PGETBL_CTL);
|
||||
|
||||
/* The root resource we allocate address space from */
|
||||
dev_priv->gtt_initialized = 1;
|
||||
|
||||
pg->gtt_phys_start = dev_priv->pge_ctl & PAGE_MASK;
|
||||
|
||||
/*
|
||||
* The video mmu has a hw bug when accessing 0x0D0000000.
|
||||
* Make gatt start at 0x0e000,0000. This doesn't actually
|
||||
* matter for us but may do if the video acceleration ever
|
||||
* gets opened up.
|
||||
*/
|
||||
pg->mmu_gatt_start = 0xE0000000;
|
||||
|
||||
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 */
|
||||
if (pg->gtt_start == 0 || gtt_pages == 0) {
|
||||
dev_err(dev->dev, "GTT PCI BAR not initialized.\n");
|
||||
gtt_pages = 64;
|
||||
pg->gtt_start = dev_priv->pge_ctl;
|
||||
}
|
||||
|
||||
pg->gatt_start = pci_resource_start(dev->pdev, PSB_GATT_RESOURCE);
|
||||
pg->gatt_pages = pci_resource_len(dev->pdev, PSB_GATT_RESOURCE)
|
||||
>> PAGE_SHIFT;
|
||||
dev_priv->gtt_mem = &dev->pdev->resource[PSB_GATT_RESOURCE];
|
||||
|
||||
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.
|
||||
We really don't care what imaginary space is being allocated
|
||||
at this point */
|
||||
dev_err(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
|
||||
a view from the GPU into memory and not vice versa. As such
|
||||
this is really allocating space that is not the same as the
|
||||
CPU address space on CDV */
|
||||
fudge.start = 0x40000000;
|
||||
fudge.end = 0x40000000 + 128 * 1024 * 1024 - 1;
|
||||
fudge.name = "fudge";
|
||||
fudge.flags = IORESOURCE_MEM;
|
||||
dev_priv->gtt_mem = &fudge;
|
||||
}
|
||||
|
||||
pci_read_config_dword(dev->pdev, PSB_BSM, &dev_priv->stolen_base);
|
||||
vram_stolen_size = pg->gtt_phys_start - dev_priv->stolen_base
|
||||
- PAGE_SIZE;
|
||||
|
||||
stolen_size = vram_stolen_size;
|
||||
|
||||
printk(KERN_INFO "Stolen memory information\n");
|
||||
printk(KERN_INFO " base in RAM: 0x%x\n", dev_priv->stolen_base);
|
||||
printk(KERN_INFO " size: %luK, calculated by (GTT RAM base) - (Stolen base), seems wrong\n",
|
||||
vram_stolen_size/1024);
|
||||
dvmt_mode = (dev_priv->gmch_ctrl >> 4) & 0x7;
|
||||
printk(KERN_INFO " the correct size should be: %dM(dvmt mode=%d)\n",
|
||||
(dvmt_mode == 1) ? 1 : (2 << (dvmt_mode - 1)), dvmt_mode);
|
||||
|
||||
if (resume && (gtt_pages != pg->gtt_pages) &&
|
||||
(stolen_size != pg->stolen_size)) {
|
||||
dev_err(dev->dev, "GTT resume error.\n");
|
||||
ret = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
pg->gtt_pages = gtt_pages;
|
||||
pg->stolen_size = stolen_size;
|
||||
dev_priv->vram_stolen_size = vram_stolen_size;
|
||||
|
||||
/*
|
||||
* Map the GTT and the stolen memory area
|
||||
*/
|
||||
dev_priv->gtt_map = ioremap_nocache(pg->gtt_phys_start,
|
||||
gtt_pages << PAGE_SHIFT);
|
||||
if (!dev_priv->gtt_map) {
|
||||
dev_err(dev->dev, "Failure to map gtt.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
dev_priv->vram_addr = ioremap_wc(dev_priv->stolen_base, stolen_size);
|
||||
if (!dev_priv->vram_addr) {
|
||||
dev_err(dev->dev, "Failure to map stolen base.\n");
|
||||
ret = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert vram stolen pages into the GTT
|
||||
*/
|
||||
|
||||
pfn_base = dev_priv->stolen_base >> PAGE_SHIFT;
|
||||
vram_pages = num_pages = vram_stolen_size >> PAGE_SHIFT;
|
||||
printk(KERN_INFO"Set up %d stolen pages starting at 0x%08x, GTT offset %dK\n",
|
||||
num_pages, pfn_base << PAGE_SHIFT, 0);
|
||||
for (i = 0; i < num_pages; ++i) {
|
||||
pte = psb_gtt_mask_pte(pfn_base + i, 0);
|
||||
iowrite32(pte, dev_priv->gtt_map + i);
|
||||
}
|
||||
|
||||
/*
|
||||
* Init rest of GTT to the scratch page to avoid accidents or scribbles
|
||||
*/
|
||||
|
||||
pfn_base = page_to_pfn(dev_priv->scratch_page);
|
||||
pte = psb_gtt_mask_pte(pfn_base, 0);
|
||||
for (; i < gtt_pages; ++i)
|
||||
iowrite32(pte, dev_priv->gtt_map + i);
|
||||
|
||||
(void) ioread32(dev_priv->gtt_map + i - 1);
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
psb_gtt_takedown(dev);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007-2008, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _PSB_GTT_H_
|
||||
#define _PSB_GTT_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
/* This wants cleaning up with respect to the psb_dev and un-needed stuff */
|
||||
struct psb_gtt {
|
||||
uint32_t gatt_start;
|
||||
uint32_t mmu_gatt_start;
|
||||
uint32_t gtt_start;
|
||||
uint32_t gtt_phys_start;
|
||||
unsigned gtt_pages;
|
||||
unsigned gatt_pages;
|
||||
unsigned long stolen_size;
|
||||
unsigned long vram_stolen_size;
|
||||
struct rw_semaphore sem;
|
||||
};
|
||||
|
||||
/* Exported functions */
|
||||
extern int psb_gtt_init(struct drm_device *dev, int resume);
|
||||
extern void psb_gtt_takedown(struct drm_device *dev);
|
||||
|
||||
/* Each gtt_range describes an allocation in the GTT area */
|
||||
struct gtt_range {
|
||||
struct resource resource; /* Resource for our allocation */
|
||||
u32 offset; /* GTT offset of our object */
|
||||
struct drm_gem_object gem; /* GEM high level stuff */
|
||||
int in_gart; /* Currently in the GART (ref ct) */
|
||||
bool stolen; /* Backed from stolen RAM */
|
||||
bool mmapping; /* Is mmappable */
|
||||
struct page **pages; /* Backing pages if present */
|
||||
int npage; /* Number of backing pages */
|
||||
int roll; /* Roll applied to the GTT entries */
|
||||
};
|
||||
|
||||
extern struct gtt_range *psb_gtt_alloc_range(struct drm_device *dev, int len,
|
||||
const char *name, int backed);
|
||||
extern void psb_gtt_kref_put(struct gtt_range *gt);
|
||||
extern void psb_gtt_free_range(struct drm_device *dev, struct gtt_range *gt);
|
||||
extern int psb_gtt_pin(struct gtt_range *gt);
|
||||
extern void psb_gtt_unpin(struct gtt_range *gt);
|
||||
extern void psb_gtt_roll(struct drm_device *dev,
|
||||
struct gtt_range *gt, int roll);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,303 @@
|
|||
/*
|
||||
* Copyright (c) 2006 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*
|
||||
*/
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "intel_bios.h"
|
||||
|
||||
|
||||
static void *find_section(struct bdb_header *bdb, int section_id)
|
||||
{
|
||||
u8 *base = (u8 *)bdb;
|
||||
int index = 0;
|
||||
u16 total, current_size;
|
||||
u8 current_id;
|
||||
|
||||
/* skip to first section */
|
||||
index += bdb->header_size;
|
||||
total = bdb->bdb_size;
|
||||
|
||||
/* walk the sections looking for section_id */
|
||||
while (index < total) {
|
||||
current_id = *(base + index);
|
||||
index++;
|
||||
current_size = *((u16 *)(base + index));
|
||||
index += 2;
|
||||
if (current_id == section_id)
|
||||
return base + index;
|
||||
index += current_size;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
|
||||
struct lvds_dvo_timing *dvo_timing)
|
||||
{
|
||||
panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
|
||||
dvo_timing->hactive_lo;
|
||||
panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
|
||||
((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
|
||||
panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
|
||||
dvo_timing->hsync_pulse_width;
|
||||
panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
|
||||
((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
|
||||
|
||||
panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
|
||||
dvo_timing->vactive_lo;
|
||||
panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
|
||||
dvo_timing->vsync_off;
|
||||
panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
|
||||
dvo_timing->vsync_pulse_width;
|
||||
panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
|
||||
((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
|
||||
panel_fixed_mode->clock = dvo_timing->clock * 10;
|
||||
panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
|
||||
|
||||
/* Some VBTs have bogus h/vtotal values */
|
||||
if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal)
|
||||
panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1;
|
||||
if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal)
|
||||
panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1;
|
||||
|
||||
drm_mode_set_name(panel_fixed_mode);
|
||||
}
|
||||
|
||||
static void parse_backlight_data(struct drm_psb_private *dev_priv,
|
||||
struct bdb_header *bdb)
|
||||
{
|
||||
struct bdb_lvds_backlight *vbt_lvds_bl = NULL;
|
||||
struct bdb_lvds_backlight *lvds_bl;
|
||||
u8 p_type = 0;
|
||||
void *bl_start = NULL;
|
||||
struct bdb_lvds_options *lvds_opts
|
||||
= find_section(bdb, BDB_LVDS_OPTIONS);
|
||||
|
||||
dev_priv->lvds_bl = NULL;
|
||||
|
||||
if (lvds_opts)
|
||||
p_type = lvds_opts->panel_type;
|
||||
else
|
||||
return;
|
||||
|
||||
bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT);
|
||||
vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type;
|
||||
|
||||
lvds_bl = kzalloc(sizeof(*vbt_lvds_bl), GFP_KERNEL);
|
||||
if (!lvds_bl) {
|
||||
dev_err(dev_priv->dev->dev, "out of memory for backlight data\n");
|
||||
return;
|
||||
}
|
||||
memcpy(lvds_bl, vbt_lvds_bl, sizeof(*vbt_lvds_bl));
|
||||
dev_priv->lvds_bl = lvds_bl;
|
||||
}
|
||||
|
||||
/* Try to find integrated panel data */
|
||||
static void parse_lfp_panel_data(struct drm_psb_private *dev_priv,
|
||||
struct bdb_header *bdb)
|
||||
{
|
||||
struct bdb_lvds_options *lvds_options;
|
||||
struct bdb_lvds_lfp_data *lvds_lfp_data;
|
||||
struct bdb_lvds_lfp_data_entry *entry;
|
||||
struct lvds_dvo_timing *dvo_timing;
|
||||
struct drm_display_mode *panel_fixed_mode;
|
||||
|
||||
/* Defaults if we can't find VBT info */
|
||||
dev_priv->lvds_dither = 0;
|
||||
dev_priv->lvds_vbt = 0;
|
||||
|
||||
lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
|
||||
if (!lvds_options)
|
||||
return;
|
||||
|
||||
dev_priv->lvds_dither = lvds_options->pixel_dither;
|
||||
if (lvds_options->panel_type == 0xff)
|
||||
return;
|
||||
|
||||
lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
|
||||
if (!lvds_lfp_data)
|
||||
return;
|
||||
|
||||
|
||||
entry = &lvds_lfp_data->data[lvds_options->panel_type];
|
||||
dvo_timing = &entry->dvo_timing;
|
||||
|
||||
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode),
|
||||
GFP_KERNEL);
|
||||
if (panel_fixed_mode == NULL) {
|
||||
dev_err(dev_priv->dev->dev, "out of memory for fixed panel mode\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dev_priv->lvds_vbt = 1;
|
||||
fill_detail_timing_data(panel_fixed_mode, dvo_timing);
|
||||
|
||||
if (panel_fixed_mode->htotal > 0 && panel_fixed_mode->vtotal > 0) {
|
||||
dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
|
||||
drm_mode_debug_printmodeline(panel_fixed_mode);
|
||||
} else {
|
||||
dev_dbg(dev_priv->dev->dev, "ignoring invalid LVDS VBT\n");
|
||||
dev_priv->lvds_vbt = 0;
|
||||
kfree(panel_fixed_mode);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Try to find sdvo panel data */
|
||||
static void parse_sdvo_panel_data(struct drm_psb_private *dev_priv,
|
||||
struct bdb_header *bdb)
|
||||
{
|
||||
struct bdb_sdvo_lvds_options *sdvo_lvds_options;
|
||||
struct lvds_dvo_timing *dvo_timing;
|
||||
struct drm_display_mode *panel_fixed_mode;
|
||||
|
||||
dev_priv->sdvo_lvds_vbt_mode = NULL;
|
||||
|
||||
sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS);
|
||||
if (!sdvo_lvds_options)
|
||||
return;
|
||||
|
||||
dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS);
|
||||
if (!dvo_timing)
|
||||
return;
|
||||
|
||||
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
|
||||
|
||||
if (!panel_fixed_mode)
|
||||
return;
|
||||
|
||||
fill_detail_timing_data(panel_fixed_mode,
|
||||
dvo_timing + sdvo_lvds_options->panel_type);
|
||||
|
||||
dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void parse_general_features(struct drm_psb_private *dev_priv,
|
||||
struct bdb_header *bdb)
|
||||
{
|
||||
struct bdb_general_features *general;
|
||||
|
||||
/* Set sensible defaults in case we can't find the general block */
|
||||
dev_priv->int_tv_support = 1;
|
||||
dev_priv->int_crt_support = 1;
|
||||
|
||||
general = find_section(bdb, BDB_GENERAL_FEATURES);
|
||||
if (general) {
|
||||
dev_priv->int_tv_support = general->int_tv_support;
|
||||
dev_priv->int_crt_support = general->int_crt_support;
|
||||
dev_priv->lvds_use_ssc = general->enable_ssc;
|
||||
|
||||
if (dev_priv->lvds_use_ssc) {
|
||||
dev_priv->lvds_ssc_freq
|
||||
= general->ssc_freq ? 100 : 96;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_intel_init_bios - initialize VBIOS settings & find VBT
|
||||
* @dev: DRM device
|
||||
*
|
||||
* Loads the Video BIOS and checks that the VBT exists. Sets scratch registers
|
||||
* to appropriate values.
|
||||
*
|
||||
* VBT existence is a sanity check that is relied on by other i830_bios.c code.
|
||||
* Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
|
||||
* feed an updated VBT back through that, compared to what we'll fetch using
|
||||
* this method of groping around in the BIOS data.
|
||||
*
|
||||
* Returns 0 on success, nonzero on failure.
|
||||
*/
|
||||
bool psb_intel_init_bios(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct pci_dev *pdev = dev->pdev;
|
||||
struct vbt_header *vbt = NULL;
|
||||
struct bdb_header *bdb;
|
||||
u8 __iomem *bios;
|
||||
size_t size;
|
||||
int i;
|
||||
|
||||
bios = pci_map_rom(pdev, &size);
|
||||
if (!bios)
|
||||
return -1;
|
||||
|
||||
/* Scour memory looking for the VBT signature */
|
||||
for (i = 0; i + 4 < size; i++) {
|
||||
if (!memcmp(bios + i, "$VBT", 4)) {
|
||||
vbt = (struct vbt_header *)(bios + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vbt) {
|
||||
dev_err(dev->dev, "VBT signature missing\n");
|
||||
pci_unmap_rom(pdev, bios);
|
||||
return -1;
|
||||
}
|
||||
|
||||
bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
|
||||
|
||||
/* Grab useful general definitions */
|
||||
parse_general_features(dev_priv, bdb);
|
||||
parse_lfp_panel_data(dev_priv, bdb);
|
||||
parse_sdvo_panel_data(dev_priv, bdb);
|
||||
parse_backlight_data(dev_priv, bdb);
|
||||
|
||||
pci_unmap_rom(pdev, bios);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy and free VBT data
|
||||
*/
|
||||
void psb_intel_destroy_bios(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_display_mode *sdvo_lvds_vbt_mode =
|
||||
dev_priv->sdvo_lvds_vbt_mode;
|
||||
struct drm_display_mode *lfp_lvds_vbt_mode =
|
||||
dev_priv->lfp_lvds_vbt_mode;
|
||||
struct bdb_lvds_backlight *lvds_bl =
|
||||
dev_priv->lvds_bl;
|
||||
|
||||
/*free sdvo panel mode*/
|
||||
if (sdvo_lvds_vbt_mode) {
|
||||
dev_priv->sdvo_lvds_vbt_mode = NULL;
|
||||
kfree(sdvo_lvds_vbt_mode);
|
||||
}
|
||||
|
||||
if (lfp_lvds_vbt_mode) {
|
||||
dev_priv->lfp_lvds_vbt_mode = NULL;
|
||||
kfree(lfp_lvds_vbt_mode);
|
||||
}
|
||||
|
||||
if (lvds_bl) {
|
||||
dev_priv->lvds_bl = NULL;
|
||||
kfree(lvds_bl);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* Copyright (c) 2006 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _I830_BIOS_H_
|
||||
#define _I830_BIOS_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
struct vbt_header {
|
||||
u8 signature[20]; /**< Always starts with 'VBT$' */
|
||||
u16 version; /**< decimal */
|
||||
u16 header_size; /**< in bytes */
|
||||
u16 vbt_size; /**< in bytes */
|
||||
u8 vbt_checksum;
|
||||
u8 reserved0;
|
||||
u32 bdb_offset; /**< from beginning of VBT */
|
||||
u32 aim_offset[4]; /**< from beginning of VBT */
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
struct bdb_header {
|
||||
u8 signature[16]; /**< Always 'BIOS_DATA_BLOCK' */
|
||||
u16 version; /**< decimal */
|
||||
u16 header_size; /**< in bytes */
|
||||
u16 bdb_size; /**< in bytes */
|
||||
};
|
||||
|
||||
/* strictly speaking, this is a "skip" block, but it has interesting info */
|
||||
struct vbios_data {
|
||||
u8 type; /* 0 == desktop, 1 == mobile */
|
||||
u8 relstage;
|
||||
u8 chipset;
|
||||
u8 lvds_present:1;
|
||||
u8 tv_present:1;
|
||||
u8 rsvd2:6; /* finish byte */
|
||||
u8 rsvd3[4];
|
||||
u8 signon[155];
|
||||
u8 copyright[61];
|
||||
u16 code_segment;
|
||||
u8 dos_boot_mode;
|
||||
u8 bandwidth_percent;
|
||||
u8 rsvd4; /* popup memory size */
|
||||
u8 resize_pci_bios;
|
||||
u8 rsvd5; /* is crt already on ddc2 */
|
||||
} __attribute__((packed));
|
||||
|
||||
/*
|
||||
* There are several types of BIOS data blocks (BDBs), each block has
|
||||
* an ID and size in the first 3 bytes (ID in first, size in next 2).
|
||||
* Known types are listed below.
|
||||
*/
|
||||
#define BDB_GENERAL_FEATURES 1
|
||||
#define BDB_GENERAL_DEFINITIONS 2
|
||||
#define BDB_OLD_TOGGLE_LIST 3
|
||||
#define BDB_MODE_SUPPORT_LIST 4
|
||||
#define BDB_GENERIC_MODE_TABLE 5
|
||||
#define BDB_EXT_MMIO_REGS 6
|
||||
#define BDB_SWF_IO 7
|
||||
#define BDB_SWF_MMIO 8
|
||||
#define BDB_DOT_CLOCK_TABLE 9
|
||||
#define BDB_MODE_REMOVAL_TABLE 10
|
||||
#define BDB_CHILD_DEVICE_TABLE 11
|
||||
#define BDB_DRIVER_FEATURES 12
|
||||
#define BDB_DRIVER_PERSISTENCE 13
|
||||
#define BDB_EXT_TABLE_PTRS 14
|
||||
#define BDB_DOT_CLOCK_OVERRIDE 15
|
||||
#define BDB_DISPLAY_SELECT 16
|
||||
/* 17 rsvd */
|
||||
#define BDB_DRIVER_ROTATION 18
|
||||
#define BDB_DISPLAY_REMOVE 19
|
||||
#define BDB_OEM_CUSTOM 20
|
||||
#define BDB_EFP_LIST 21 /* workarounds for VGA hsync/vsync */
|
||||
#define BDB_SDVO_LVDS_OPTIONS 22
|
||||
#define BDB_SDVO_PANEL_DTDS 23
|
||||
#define BDB_SDVO_LVDS_PNP_IDS 24
|
||||
#define BDB_SDVO_LVDS_POWER_SEQ 25
|
||||
#define BDB_TV_OPTIONS 26
|
||||
#define BDB_LVDS_OPTIONS 40
|
||||
#define BDB_LVDS_LFP_DATA_PTRS 41
|
||||
#define BDB_LVDS_LFP_DATA 42
|
||||
#define BDB_LVDS_BACKLIGHT 43
|
||||
#define BDB_LVDS_POWER 44
|
||||
#define BDB_SKIP 254 /* VBIOS private block, ignore */
|
||||
|
||||
struct bdb_general_features {
|
||||
/* bits 1 */
|
||||
u8 panel_fitting:2;
|
||||
u8 flexaim:1;
|
||||
u8 msg_enable:1;
|
||||
u8 clear_screen:3;
|
||||
u8 color_flip:1;
|
||||
|
||||
/* bits 2 */
|
||||
u8 download_ext_vbt:1;
|
||||
u8 enable_ssc:1;
|
||||
u8 ssc_freq:1;
|
||||
u8 enable_lfp_on_override:1;
|
||||
u8 disable_ssc_ddt:1;
|
||||
u8 rsvd8:3; /* finish byte */
|
||||
|
||||
/* bits 3 */
|
||||
u8 disable_smooth_vision:1;
|
||||
u8 single_dvi:1;
|
||||
u8 rsvd9:6; /* finish byte */
|
||||
|
||||
/* bits 4 */
|
||||
u8 legacy_monitor_detect;
|
||||
|
||||
/* bits 5 */
|
||||
u8 int_crt_support:1;
|
||||
u8 int_tv_support:1;
|
||||
u8 rsvd11:6; /* finish byte */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bdb_general_definitions {
|
||||
/* DDC GPIO */
|
||||
u8 crt_ddc_gmbus_pin;
|
||||
|
||||
/* DPMS bits */
|
||||
u8 dpms_acpi:1;
|
||||
u8 skip_boot_crt_detect:1;
|
||||
u8 dpms_aim:1;
|
||||
u8 rsvd1:5; /* finish byte */
|
||||
|
||||
/* boot device bits */
|
||||
u8 boot_display[2];
|
||||
u8 child_dev_size;
|
||||
|
||||
/* device info */
|
||||
u8 tv_or_lvds_info[33];
|
||||
u8 dev1[33];
|
||||
u8 dev2[33];
|
||||
u8 dev3[33];
|
||||
u8 dev4[33];
|
||||
/* may be another device block here on some platforms */
|
||||
};
|
||||
|
||||
struct bdb_lvds_options {
|
||||
u8 panel_type;
|
||||
u8 rsvd1;
|
||||
/* LVDS capabilities, stored in a dword */
|
||||
u8 pfit_mode:2;
|
||||
u8 pfit_text_mode_enhanced:1;
|
||||
u8 pfit_gfx_mode_enhanced:1;
|
||||
u8 pfit_ratio_auto:1;
|
||||
u8 pixel_dither:1;
|
||||
u8 lvds_edid:1;
|
||||
u8 rsvd2:1;
|
||||
u8 rsvd4;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bdb_lvds_backlight {
|
||||
u8 type:2;
|
||||
u8 pol:1;
|
||||
u8 gpio:3;
|
||||
u8 gmbus:2;
|
||||
u16 freq;
|
||||
u8 minbrightness;
|
||||
u8 i2caddr;
|
||||
u8 brightnesscmd;
|
||||
/*FIXME: more...*/
|
||||
} __attribute__((packed));
|
||||
|
||||
/* LFP pointer table contains entries to the struct below */
|
||||
struct bdb_lvds_lfp_data_ptr {
|
||||
u16 fp_timing_offset; /* offsets are from start of bdb */
|
||||
u8 fp_table_size;
|
||||
u16 dvo_timing_offset;
|
||||
u8 dvo_table_size;
|
||||
u16 panel_pnp_id_offset;
|
||||
u8 pnp_table_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bdb_lvds_lfp_data_ptrs {
|
||||
u8 lvds_entries; /* followed by one or more lvds_data_ptr structs */
|
||||
struct bdb_lvds_lfp_data_ptr ptr[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* LFP data has 3 blocks per entry */
|
||||
struct lvds_fp_timing {
|
||||
u16 x_res;
|
||||
u16 y_res;
|
||||
u32 lvds_reg;
|
||||
u32 lvds_reg_val;
|
||||
u32 pp_on_reg;
|
||||
u32 pp_on_reg_val;
|
||||
u32 pp_off_reg;
|
||||
u32 pp_off_reg_val;
|
||||
u32 pp_cycle_reg;
|
||||
u32 pp_cycle_reg_val;
|
||||
u32 pfit_reg;
|
||||
u32 pfit_reg_val;
|
||||
u16 terminator;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct lvds_dvo_timing {
|
||||
u16 clock; /**< In 10khz */
|
||||
u8 hactive_lo;
|
||||
u8 hblank_lo;
|
||||
u8 hblank_hi:4;
|
||||
u8 hactive_hi:4;
|
||||
u8 vactive_lo;
|
||||
u8 vblank_lo;
|
||||
u8 vblank_hi:4;
|
||||
u8 vactive_hi:4;
|
||||
u8 hsync_off_lo;
|
||||
u8 hsync_pulse_width;
|
||||
u8 vsync_pulse_width:4;
|
||||
u8 vsync_off:4;
|
||||
u8 rsvd0:6;
|
||||
u8 hsync_off_hi:2;
|
||||
u8 h_image;
|
||||
u8 v_image;
|
||||
u8 max_hv;
|
||||
u8 h_border;
|
||||
u8 v_border;
|
||||
u8 rsvd1:3;
|
||||
u8 digital:2;
|
||||
u8 vsync_positive:1;
|
||||
u8 hsync_positive:1;
|
||||
u8 rsvd2:1;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct lvds_pnp_id {
|
||||
u16 mfg_name;
|
||||
u16 product_code;
|
||||
u32 serial;
|
||||
u8 mfg_week;
|
||||
u8 mfg_year;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bdb_lvds_lfp_data_entry {
|
||||
struct lvds_fp_timing fp_timing;
|
||||
struct lvds_dvo_timing dvo_timing;
|
||||
struct lvds_pnp_id pnp_id;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bdb_lvds_lfp_data {
|
||||
struct bdb_lvds_lfp_data_entry data[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct aimdb_header {
|
||||
char signature[16];
|
||||
char oem_device[20];
|
||||
u16 aimdb_version;
|
||||
u16 aimdb_header_size;
|
||||
u16 aimdb_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct aimdb_block {
|
||||
u8 aimdb_id;
|
||||
u16 aimdb_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vch_panel_data {
|
||||
u16 fp_timing_offset;
|
||||
u8 fp_timing_size;
|
||||
u16 dvo_timing_offset;
|
||||
u8 dvo_timing_size;
|
||||
u16 text_fitting_offset;
|
||||
u8 text_fitting_size;
|
||||
u16 graphics_fitting_offset;
|
||||
u8 graphics_fitting_size;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vch_bdb_22 {
|
||||
struct aimdb_block aimdb_block;
|
||||
struct vch_panel_data panels[16];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct bdb_sdvo_lvds_options {
|
||||
u8 panel_backlight;
|
||||
u8 h40_set_panel_type;
|
||||
u8 panel_type;
|
||||
u8 ssc_clk_freq;
|
||||
u16 als_low_trip;
|
||||
u16 als_high_trip;
|
||||
u8 sclalarcoeff_tab_row_num;
|
||||
u8 sclalarcoeff_tab_row_size;
|
||||
u8 coefficient[8];
|
||||
u8 panel_misc_bits_1;
|
||||
u8 panel_misc_bits_2;
|
||||
u8 panel_misc_bits_3;
|
||||
u8 panel_misc_bits_4;
|
||||
} __attribute__((packed));
|
||||
|
||||
|
||||
extern bool psb_intel_init_bios(struct drm_device *dev);
|
||||
extern void psb_intel_destroy_bios(struct drm_device *dev);
|
||||
|
||||
/*
|
||||
* Driver<->VBIOS interaction occurs through scratch bits in
|
||||
* GR18 & SWF*.
|
||||
*/
|
||||
|
||||
/* GR18 bits are set on display switch and hotkey events */
|
||||
#define GR18_DRIVER_SWITCH_EN (1<<7) /* 0: VBIOS control, 1: driver control */
|
||||
#define GR18_HOTKEY_MASK 0x78 /* See also SWF4 15:0 */
|
||||
#define GR18_HK_NONE (0x0<<3)
|
||||
#define GR18_HK_LFP_STRETCH (0x1<<3)
|
||||
#define GR18_HK_TOGGLE_DISP (0x2<<3)
|
||||
#define GR18_HK_DISP_SWITCH (0x4<<3) /* see SWF14 15:0 for what to enable */
|
||||
#define GR18_HK_POPUP_DISABLED (0x6<<3)
|
||||
#define GR18_HK_POPUP_ENABLED (0x7<<3)
|
||||
#define GR18_HK_PFIT (0x8<<3)
|
||||
#define GR18_HK_APM_CHANGE (0xa<<3)
|
||||
#define GR18_HK_MULTIPLE (0xc<<3)
|
||||
#define GR18_USER_INT_EN (1<<2)
|
||||
#define GR18_A0000_FLUSH_EN (1<<1)
|
||||
#define GR18_SMM_EN (1<<0)
|
||||
|
||||
/* Set by driver, cleared by VBIOS */
|
||||
#define SWF00_YRES_SHIFT 16
|
||||
#define SWF00_XRES_SHIFT 0
|
||||
#define SWF00_RES_MASK 0xffff
|
||||
|
||||
/* Set by VBIOS at boot time and driver at runtime */
|
||||
#define SWF01_TV2_FORMAT_SHIFT 8
|
||||
#define SWF01_TV1_FORMAT_SHIFT 0
|
||||
#define SWF01_TV_FORMAT_MASK 0xffff
|
||||
|
||||
#define SWF10_VBIOS_BLC_I2C_EN (1<<29)
|
||||
#define SWF10_GTT_OVERRIDE_EN (1<<28)
|
||||
#define SWF10_LFP_DPMS_OVR (1<<27) /* override DPMS on display switch */
|
||||
#define SWF10_ACTIVE_TOGGLE_LIST_MASK (7<<24)
|
||||
#define SWF10_OLD_TOGGLE 0x0
|
||||
#define SWF10_TOGGLE_LIST_1 0x1
|
||||
#define SWF10_TOGGLE_LIST_2 0x2
|
||||
#define SWF10_TOGGLE_LIST_3 0x3
|
||||
#define SWF10_TOGGLE_LIST_4 0x4
|
||||
#define SWF10_PANNING_EN (1<<23)
|
||||
#define SWF10_DRIVER_LOADED (1<<22)
|
||||
#define SWF10_EXTENDED_DESKTOP (1<<21)
|
||||
#define SWF10_EXCLUSIVE_MODE (1<<20)
|
||||
#define SWF10_OVERLAY_EN (1<<19)
|
||||
#define SWF10_PLANEB_HOLDOFF (1<<18)
|
||||
#define SWF10_PLANEA_HOLDOFF (1<<17)
|
||||
#define SWF10_VGA_HOLDOFF (1<<16)
|
||||
#define SWF10_ACTIVE_DISP_MASK 0xffff
|
||||
#define SWF10_PIPEB_LFP2 (1<<15)
|
||||
#define SWF10_PIPEB_EFP2 (1<<14)
|
||||
#define SWF10_PIPEB_TV2 (1<<13)
|
||||
#define SWF10_PIPEB_CRT2 (1<<12)
|
||||
#define SWF10_PIPEB_LFP (1<<11)
|
||||
#define SWF10_PIPEB_EFP (1<<10)
|
||||
#define SWF10_PIPEB_TV (1<<9)
|
||||
#define SWF10_PIPEB_CRT (1<<8)
|
||||
#define SWF10_PIPEA_LFP2 (1<<7)
|
||||
#define SWF10_PIPEA_EFP2 (1<<6)
|
||||
#define SWF10_PIPEA_TV2 (1<<5)
|
||||
#define SWF10_PIPEA_CRT2 (1<<4)
|
||||
#define SWF10_PIPEA_LFP (1<<3)
|
||||
#define SWF10_PIPEA_EFP (1<<2)
|
||||
#define SWF10_PIPEA_TV (1<<1)
|
||||
#define SWF10_PIPEA_CRT (1<<0)
|
||||
|
||||
#define SWF11_MEMORY_SIZE_SHIFT 16
|
||||
#define SWF11_SV_TEST_EN (1<<15)
|
||||
#define SWF11_IS_AGP (1<<14)
|
||||
#define SWF11_DISPLAY_HOLDOFF (1<<13)
|
||||
#define SWF11_DPMS_REDUCED (1<<12)
|
||||
#define SWF11_IS_VBE_MODE (1<<11)
|
||||
#define SWF11_PIPEB_ACCESS (1<<10) /* 0 here means pipe a */
|
||||
#define SWF11_DPMS_MASK 0x07
|
||||
#define SWF11_DPMS_OFF (1<<2)
|
||||
#define SWF11_DPMS_SUSPEND (1<<1)
|
||||
#define SWF11_DPMS_STANDBY (1<<0)
|
||||
#define SWF11_DPMS_ON 0
|
||||
|
||||
#define SWF14_GFX_PFIT_EN (1<<31)
|
||||
#define SWF14_TEXT_PFIT_EN (1<<30)
|
||||
#define SWF14_LID_STATUS_CLOSED (1<<29) /* 0 here means open */
|
||||
#define SWF14_POPUP_EN (1<<28)
|
||||
#define SWF14_DISPLAY_HOLDOFF (1<<27)
|
||||
#define SWF14_DISP_DETECT_EN (1<<26)
|
||||
#define SWF14_DOCKING_STATUS_DOCKED (1<<25) /* 0 here means undocked */
|
||||
#define SWF14_DRIVER_STATUS (1<<24)
|
||||
#define SWF14_OS_TYPE_WIN9X (1<<23)
|
||||
#define SWF14_OS_TYPE_WINNT (1<<22)
|
||||
/* 21:19 rsvd */
|
||||
#define SWF14_PM_TYPE_MASK 0x00070000
|
||||
#define SWF14_PM_ACPI_VIDEO (0x4 << 16)
|
||||
#define SWF14_PM_ACPI (0x3 << 16)
|
||||
#define SWF14_PM_APM_12 (0x2 << 16)
|
||||
#define SWF14_PM_APM_11 (0x1 << 16)
|
||||
#define SWF14_HK_REQUEST_MASK 0x0000ffff /* see GR18 6:3 for event type */
|
||||
/* if GR18 indicates a display switch */
|
||||
#define SWF14_DS_PIPEB_LFP2_EN (1<<15)
|
||||
#define SWF14_DS_PIPEB_EFP2_EN (1<<14)
|
||||
#define SWF14_DS_PIPEB_TV2_EN (1<<13)
|
||||
#define SWF14_DS_PIPEB_CRT2_EN (1<<12)
|
||||
#define SWF14_DS_PIPEB_LFP_EN (1<<11)
|
||||
#define SWF14_DS_PIPEB_EFP_EN (1<<10)
|
||||
#define SWF14_DS_PIPEB_TV_EN (1<<9)
|
||||
#define SWF14_DS_PIPEB_CRT_EN (1<<8)
|
||||
#define SWF14_DS_PIPEA_LFP2_EN (1<<7)
|
||||
#define SWF14_DS_PIPEA_EFP2_EN (1<<6)
|
||||
#define SWF14_DS_PIPEA_TV2_EN (1<<5)
|
||||
#define SWF14_DS_PIPEA_CRT2_EN (1<<4)
|
||||
#define SWF14_DS_PIPEA_LFP_EN (1<<3)
|
||||
#define SWF14_DS_PIPEA_EFP_EN (1<<2)
|
||||
#define SWF14_DS_PIPEA_TV_EN (1<<1)
|
||||
#define SWF14_DS_PIPEA_CRT_EN (1<<0)
|
||||
/* if GR18 indicates a panel fitting request */
|
||||
#define SWF14_PFIT_EN (1<<0) /* 0 means disable */
|
||||
/* if GR18 indicates an APM change request */
|
||||
#define SWF14_APM_HIBERNATE 0x4
|
||||
#define SWF14_APM_SUSPEND 0x3
|
||||
#define SWF14_APM_STANDBY 0x1
|
||||
#define SWF14_APM_RESTORE 0x0
|
||||
|
||||
#endif /* _I830_BIOS_H_ */
|
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
* Copyright (c) 2006 Dave Airlie <airlied@linux.ie>
|
||||
* Copyright © 2006-2008,2010 Intel Corporation
|
||||
* Jesse Barnes <jesse.barnes@intel.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
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Chris Wilson <chris@chris-wilson.co.uk>
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include "drmP.h"
|
||||
#include "drm.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
|
||||
#define _wait_for(COND, MS, W) ({ \
|
||||
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
|
||||
int ret__ = 0; \
|
||||
while (! (COND)) { \
|
||||
if (time_after(jiffies, timeout__)) { \
|
||||
ret__ = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
if (W && !(in_atomic() || in_dbg_master())) msleep(W); \
|
||||
} \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define wait_for(COND, MS) _wait_for(COND, MS, 1)
|
||||
#define wait_for_atomic(COND, MS) _wait_for(COND, MS, 0)
|
||||
|
||||
/* Intel GPIO access functions */
|
||||
|
||||
#define I2C_RISEFALL_TIME 20
|
||||
|
||||
static inline struct intel_gmbus *
|
||||
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_psb_private *dev_priv;
|
||||
u32 reg;
|
||||
};
|
||||
|
||||
void
|
||||
gma_intel_i2c_reset(struct drm_device *dev)
|
||||
{
|
||||
REG_WRITE(GMBUS0, 0);
|
||||
}
|
||||
|
||||
static void intel_i2c_quirk_set(struct drm_psb_private *dev_priv, bool enable)
|
||||
{
|
||||
/* When using bit bashing for I2C, this bit needs to be set to 1 */
|
||||
/* FIXME: We are never Pineview, right?
|
||||
|
||||
u32 val;
|
||||
|
||||
if (!IS_PINEVIEW(dev_priv->dev))
|
||||
return;
|
||||
|
||||
val = REG_READ(DSPCLK_GATE_D);
|
||||
if (enable)
|
||||
val |= DPCUNIT_CLOCK_GATE_DISABLE;
|
||||
else
|
||||
val &= ~DPCUNIT_CLOCK_GATE_DISABLE;
|
||||
REG_WRITE(DSPCLK_GATE_D, val);
|
||||
|
||||
return;
|
||||
*/
|
||||
}
|
||||
|
||||
static u32 get_reserved(struct intel_gpio *gpio)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = gpio->dev_priv;
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
u32 reserved = 0;
|
||||
|
||||
/* On most chips, these bits must be preserved in software. */
|
||||
reserved = REG_READ(gpio->reg) &
|
||||
(GPIO_DATA_PULLUP_DISABLE |
|
||||
GPIO_CLOCK_PULLUP_DISABLE);
|
||||
|
||||
return reserved;
|
||||
}
|
||||
|
||||
static int get_clock(void *data)
|
||||
{
|
||||
struct intel_gpio *gpio = data;
|
||||
struct drm_psb_private *dev_priv = gpio->dev_priv;
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
u32 reserved = get_reserved(gpio);
|
||||
REG_WRITE(gpio->reg, reserved | GPIO_CLOCK_DIR_MASK);
|
||||
REG_WRITE(gpio->reg, reserved);
|
||||
return (REG_READ(gpio->reg) & GPIO_CLOCK_VAL_IN) != 0;
|
||||
}
|
||||
|
||||
static int get_data(void *data)
|
||||
{
|
||||
struct intel_gpio *gpio = data;
|
||||
struct drm_psb_private *dev_priv = gpio->dev_priv;
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
u32 reserved = get_reserved(gpio);
|
||||
REG_WRITE(gpio->reg, reserved | GPIO_DATA_DIR_MASK);
|
||||
REG_WRITE(gpio->reg, reserved);
|
||||
return (REG_READ(gpio->reg) & GPIO_DATA_VAL_IN) != 0;
|
||||
}
|
||||
|
||||
static void set_clock(void *data, int state_high)
|
||||
{
|
||||
struct intel_gpio *gpio = data;
|
||||
struct drm_psb_private *dev_priv = gpio->dev_priv;
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
u32 reserved = get_reserved(gpio);
|
||||
u32 clock_bits;
|
||||
|
||||
if (state_high)
|
||||
clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
|
||||
else
|
||||
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
|
||||
GPIO_CLOCK_VAL_MASK;
|
||||
|
||||
REG_WRITE(gpio->reg, reserved | clock_bits);
|
||||
REG_READ(gpio->reg); /* Posting */
|
||||
}
|
||||
|
||||
static void set_data(void *data, int state_high)
|
||||
{
|
||||
struct intel_gpio *gpio = data;
|
||||
struct drm_psb_private *dev_priv = gpio->dev_priv;
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
u32 reserved = get_reserved(gpio);
|
||||
u32 data_bits;
|
||||
|
||||
if (state_high)
|
||||
data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
|
||||
else
|
||||
data_bits = GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
|
||||
GPIO_DATA_VAL_MASK;
|
||||
|
||||
REG_WRITE(gpio->reg, reserved | data_bits);
|
||||
REG_READ(gpio->reg);
|
||||
}
|
||||
|
||||
static struct i2c_adapter *
|
||||
intel_gpio_create(struct drm_psb_private *dev_priv, u32 pin)
|
||||
{
|
||||
static const int map_pin_to_reg[] = {
|
||||
0,
|
||||
GPIOB,
|
||||
GPIOA,
|
||||
GPIOC,
|
||||
GPIOD,
|
||||
GPIOE,
|
||||
0,
|
||||
GPIOF,
|
||||
};
|
||||
struct intel_gpio *gpio;
|
||||
|
||||
if (pin >= ARRAY_SIZE(map_pin_to_reg) || !map_pin_to_reg[pin])
|
||||
return NULL;
|
||||
|
||||
gpio = kzalloc(sizeof(struct intel_gpio), GFP_KERNEL);
|
||||
if (gpio == NULL)
|
||||
return NULL;
|
||||
|
||||
gpio->reg = map_pin_to_reg[pin];
|
||||
gpio->dev_priv = dev_priv;
|
||||
|
||||
snprintf(gpio->adapter.name, sizeof(gpio->adapter.name),
|
||||
"gma500 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;
|
||||
}
|
||||
|
||||
static int
|
||||
intel_i2c_quirk_xfer(struct drm_psb_private *dev_priv,
|
||||
struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct intel_gpio *gpio = container_of(adapter,
|
||||
struct intel_gpio,
|
||||
adapter);
|
||||
int ret;
|
||||
|
||||
gma_intel_i2c_reset(dev_priv->dev);
|
||||
|
||||
intel_i2c_quirk_set(dev_priv, true);
|
||||
set_data(gpio, 1);
|
||||
set_clock(gpio, 1);
|
||||
udelay(I2C_RISEFALL_TIME);
|
||||
|
||||
ret = adapter->algo->master_xfer(adapter, msgs, num);
|
||||
|
||||
set_data(gpio, 1);
|
||||
set_clock(gpio, 1);
|
||||
intel_i2c_quirk_set(dev_priv, false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
gmbus_xfer(struct i2c_adapter *adapter,
|
||||
struct i2c_msg *msgs,
|
||||
int num)
|
||||
{
|
||||
struct intel_gmbus *bus = container_of(adapter,
|
||||
struct intel_gmbus,
|
||||
adapter);
|
||||
struct drm_psb_private *dev_priv = adapter->algo_data;
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
int i, reg_offset;
|
||||
|
||||
if (bus->force_bit)
|
||||
return intel_i2c_quirk_xfer(dev_priv,
|
||||
bus->force_bit, msgs, num);
|
||||
|
||||
reg_offset = 0;
|
||||
|
||||
REG_WRITE(GMBUS0 + reg_offset, bus->reg0);
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
u16 len = msgs[i].len;
|
||||
u8 *buf = msgs[i].buf;
|
||||
|
||||
if (msgs[i].flags & I2C_M_RD) {
|
||||
REG_WRITE(GMBUS1 + reg_offset,
|
||||
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);
|
||||
REG_READ(GMBUS2+reg_offset);
|
||||
do {
|
||||
u32 val, loop = 0;
|
||||
|
||||
if (wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50))
|
||||
goto timeout;
|
||||
if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
|
||||
goto clear_err;
|
||||
|
||||
val = REG_READ(GMBUS3 + reg_offset);
|
||||
do {
|
||||
*buf++ = val & 0xff;
|
||||
val >>= 8;
|
||||
} while (--len && ++loop < 4);
|
||||
} while (len);
|
||||
} else {
|
||||
u32 val, loop;
|
||||
|
||||
val = loop = 0;
|
||||
do {
|
||||
val |= *buf++ << (8 * loop);
|
||||
} while (--len && ++loop < 4);
|
||||
|
||||
REG_WRITE(GMBUS3 + reg_offset, val);
|
||||
REG_WRITE(GMBUS1 + reg_offset,
|
||||
(i + 1 == num ? GMBUS_CYCLE_STOP : GMBUS_CYCLE_WAIT) |
|
||||
(msgs[i].len << GMBUS_BYTE_COUNT_SHIFT) |
|
||||
(msgs[i].addr << GMBUS_SLAVE_ADDR_SHIFT) |
|
||||
GMBUS_SLAVE_WRITE | GMBUS_SW_RDY);
|
||||
REG_READ(GMBUS2+reg_offset);
|
||||
|
||||
while (len) {
|
||||
if (wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_RDY), 50))
|
||||
goto timeout;
|
||||
if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
|
||||
goto clear_err;
|
||||
|
||||
val = loop = 0;
|
||||
do {
|
||||
val |= *buf++ << (8 * loop);
|
||||
} while (--len && ++loop < 4);
|
||||
|
||||
REG_WRITE(GMBUS3 + reg_offset, val);
|
||||
REG_READ(GMBUS2+reg_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (i + 1 < num && wait_for(REG_READ(GMBUS2 + reg_offset) & (GMBUS_SATOER | GMBUS_HW_WAIT_PHASE), 50))
|
||||
goto timeout;
|
||||
if (REG_READ(GMBUS2 + reg_offset) & GMBUS_SATOER)
|
||||
goto clear_err;
|
||||
}
|
||||
|
||||
goto done;
|
||||
|
||||
clear_err:
|
||||
/* Toggle the Software Clear Interrupt bit. This has the effect
|
||||
* of resetting the GMBUS controller and so clearing the
|
||||
* BUS_ERROR raised by the slave's NAK.
|
||||
*/
|
||||
REG_WRITE(GMBUS1 + reg_offset, GMBUS_SW_CLR_INT);
|
||||
REG_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.
|
||||
*/
|
||||
REG_WRITE(GMBUS0 + reg_offset, 0);
|
||||
return i;
|
||||
|
||||
timeout:
|
||||
DRM_INFO("GMBUS timed out, falling back to bit banging on pin %d [%s]\n",
|
||||
bus->reg0 & 0xff, bus->adapter.name);
|
||||
REG_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)
|
||||
return -ENOMEM;
|
||||
|
||||
return intel_i2c_quirk_xfer(dev_priv, bus->force_bit, msgs, num);
|
||||
}
|
||||
|
||||
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 |
|
||||
/* I2C_FUNC_10BIT_ADDR | */
|
||||
I2C_FUNC_SMBUS_READ_BLOCK_DATA |
|
||||
I2C_FUNC_SMBUS_BLOCK_PROC_CALL);
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm gmbus_algorithm = {
|
||||
.master_xfer = gmbus_xfer,
|
||||
.functionality = gmbus_func
|
||||
};
|
||||
|
||||
/**
|
||||
* intel_gmbus_setup - instantiate all Intel i2c GMBuses
|
||||
* @dev: DRM device
|
||||
*/
|
||||
int gma_intel_setup_gmbus(struct drm_device *dev)
|
||||
{
|
||||
static const char *names[GMBUS_NUM_PORTS] = {
|
||||
"disabled",
|
||||
"ssc",
|
||||
"vga",
|
||||
"panel",
|
||||
"dpc",
|
||||
"dpb",
|
||||
"reserved",
|
||||
"dpd",
|
||||
};
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret, i;
|
||||
|
||||
dev_priv->gmbus = kcalloc(sizeof(struct intel_gmbus), GMBUS_NUM_PORTS,
|
||||
GFP_KERNEL);
|
||||
if (dev_priv->gmbus == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < GMBUS_NUM_PORTS; i++) {
|
||||
struct intel_gmbus *bus = &dev_priv->gmbus[i];
|
||||
|
||||
bus->adapter.owner = THIS_MODULE;
|
||||
bus->adapter.class = I2C_CLASS_DDC;
|
||||
snprintf(bus->adapter.name,
|
||||
sizeof(bus->adapter.name),
|
||||
"gma500 gmbus %s",
|
||||
names[i]);
|
||||
|
||||
bus->adapter.dev.parent = &dev->pdev->dev;
|
||||
bus->adapter.algo_data = dev_priv;
|
||||
|
||||
bus->adapter.algo = &gmbus_algorithm;
|
||||
ret = i2c_add_adapter(&bus->adapter);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
/* By default use a conservative clock rate */
|
||||
bus->reg0 = i | GMBUS_RATE_100KHZ;
|
||||
|
||||
/* XXX force bit banging until GMBUS is fully debugged */
|
||||
bus->force_bit = intel_gpio_create(dev_priv, i);
|
||||
}
|
||||
|
||||
gma_intel_i2c_reset(dev_priv->dev);
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
while (--i) {
|
||||
struct intel_gmbus *bus = &dev_priv->gmbus[i];
|
||||
i2c_del_adapter(&bus->adapter);
|
||||
}
|
||||
kfree(dev_priv->gmbus);
|
||||
dev_priv->gmbus = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
|
||||
{
|
||||
struct intel_gmbus *bus = to_intel_gmbus(adapter);
|
||||
|
||||
/* speed:
|
||||
* 0x0 = 100 KHz
|
||||
* 0x1 = 50 KHz
|
||||
* 0x2 = 400 KHz
|
||||
* 0x3 = 1000 Khz
|
||||
*/
|
||||
bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8);
|
||||
}
|
||||
|
||||
void gma_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_psb_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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gma_intel_teardown_gmbus(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
if (dev_priv->gmbus == NULL)
|
||||
return;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
kfree(dev_priv->gmbus);
|
||||
dev_priv->gmbus = NULL;
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* Copyright © 2006-2007 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
#include <linux/export.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
|
||||
/*
|
||||
* Intel GPIO access functions
|
||||
*/
|
||||
|
||||
#define I2C_RISEFALL_TIME 20
|
||||
|
||||
static int get_clock(void *data)
|
||||
{
|
||||
struct psb_intel_i2c_chan *chan = data;
|
||||
struct drm_device *dev = chan->drm_dev;
|
||||
u32 val;
|
||||
|
||||
val = REG_READ(chan->reg);
|
||||
return (val & GPIO_CLOCK_VAL_IN) != 0;
|
||||
}
|
||||
|
||||
static int get_data(void *data)
|
||||
{
|
||||
struct psb_intel_i2c_chan *chan = data;
|
||||
struct drm_device *dev = chan->drm_dev;
|
||||
u32 val;
|
||||
|
||||
val = REG_READ(chan->reg);
|
||||
return (val & GPIO_DATA_VAL_IN) != 0;
|
||||
}
|
||||
|
||||
static void set_clock(void *data, int state_high)
|
||||
{
|
||||
struct psb_intel_i2c_chan *chan = data;
|
||||
struct drm_device *dev = chan->drm_dev;
|
||||
u32 reserved = 0, clock_bits;
|
||||
|
||||
/* On most chips, these bits must be preserved in software. */
|
||||
reserved =
|
||||
REG_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
|
||||
GPIO_CLOCK_PULLUP_DISABLE);
|
||||
|
||||
if (state_high)
|
||||
clock_bits = GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK;
|
||||
else
|
||||
clock_bits = GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK |
|
||||
GPIO_CLOCK_VAL_MASK;
|
||||
REG_WRITE(chan->reg, reserved | clock_bits);
|
||||
udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
|
||||
}
|
||||
|
||||
static void set_data(void *data, int state_high)
|
||||
{
|
||||
struct psb_intel_i2c_chan *chan = data;
|
||||
struct drm_device *dev = chan->drm_dev;
|
||||
u32 reserved = 0, data_bits;
|
||||
|
||||
/* On most chips, these bits must be preserved in software. */
|
||||
reserved =
|
||||
REG_READ(chan->reg) & (GPIO_DATA_PULLUP_DISABLE |
|
||||
GPIO_CLOCK_PULLUP_DISABLE);
|
||||
|
||||
if (state_high)
|
||||
data_bits = GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK;
|
||||
else
|
||||
data_bits =
|
||||
GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK |
|
||||
GPIO_DATA_VAL_MASK;
|
||||
|
||||
REG_WRITE(chan->reg, reserved | data_bits);
|
||||
udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
|
||||
* @dev: DRM device
|
||||
* @output: driver specific output device
|
||||
* @reg: GPIO reg to use
|
||||
* @name: name for this bus
|
||||
*
|
||||
* Creates and registers a new i2c bus with the Linux i2c layer, for use
|
||||
* in output probing and control (e.g. DDC or SDVO control functions).
|
||||
*
|
||||
* Possible values for @reg include:
|
||||
* %GPIOA
|
||||
* %GPIOB
|
||||
* %GPIOC
|
||||
* %GPIOD
|
||||
* %GPIOE
|
||||
* %GPIOF
|
||||
* %GPIOG
|
||||
* %GPIOH
|
||||
* see PRM for details on how these different busses are used.
|
||||
*/
|
||||
struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev,
|
||||
const u32 reg, const char *name)
|
||||
{
|
||||
struct psb_intel_i2c_chan *chan;
|
||||
|
||||
chan = kzalloc(sizeof(struct psb_intel_i2c_chan), GFP_KERNEL);
|
||||
if (!chan)
|
||||
goto out_free;
|
||||
|
||||
chan->drm_dev = dev;
|
||||
chan->reg = reg;
|
||||
snprintf(chan->adapter.name, I2C_NAME_SIZE, "intel drm %s", name);
|
||||
chan->adapter.owner = THIS_MODULE;
|
||||
chan->adapter.algo_data = &chan->algo;
|
||||
chan->adapter.dev.parent = &dev->pdev->dev;
|
||||
chan->algo.setsda = set_data;
|
||||
chan->algo.setscl = set_clock;
|
||||
chan->algo.getsda = get_data;
|
||||
chan->algo.getscl = get_clock;
|
||||
chan->algo.udelay = 20;
|
||||
chan->algo.timeout = usecs_to_jiffies(2200);
|
||||
chan->algo.data = chan;
|
||||
|
||||
i2c_set_adapdata(&chan->adapter, chan);
|
||||
|
||||
if (i2c_bit_add_bus(&chan->adapter))
|
||||
goto out_free;
|
||||
|
||||
/* JJJ: raise SCL and SDA? */
|
||||
set_data(chan, 1);
|
||||
set_clock(chan, 1);
|
||||
udelay(20);
|
||||
|
||||
return chan;
|
||||
|
||||
out_free:
|
||||
kfree(chan);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_intel_i2c_destroy - unregister and free i2c bus resources
|
||||
* @output: channel to free
|
||||
*
|
||||
* Unregister the adapter from the i2c layer, then free the structure.
|
||||
*/
|
||||
void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan)
|
||||
{
|
||||
if (!chan)
|
||||
return;
|
||||
|
||||
i2c_del_adapter(&chan->adapter);
|
||||
kfree(chan);
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/*
|
||||
* Copyright 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* FIXME: resolve with the i915 version
|
||||
*/
|
||||
|
||||
#include "psb_drv.h"
|
||||
|
||||
struct opregion_header {
|
||||
u8 signature[16];
|
||||
u32 size;
|
||||
u32 opregion_ver;
|
||||
u8 bios_ver[32];
|
||||
u8 vbios_ver[16];
|
||||
u8 driver_ver[16];
|
||||
u32 mboxes;
|
||||
u8 reserved[164];
|
||||
} __packed;
|
||||
|
||||
struct opregion_apci {
|
||||
/*FIXME: add it later*/
|
||||
} __packed;
|
||||
|
||||
struct opregion_swsci {
|
||||
/*FIXME: add it later*/
|
||||
} __packed;
|
||||
|
||||
struct opregion_acpi {
|
||||
/*FIXME: add it later*/
|
||||
} __packed;
|
||||
|
||||
int gma_intel_opregion_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 opregion_phy;
|
||||
void *base;
|
||||
u32 *lid_state;
|
||||
|
||||
dev_priv->lid_state = NULL;
|
||||
|
||||
pci_read_config_dword(dev->pdev, 0xfc, &opregion_phy);
|
||||
if (opregion_phy == 0)
|
||||
return -ENOTSUPP;
|
||||
|
||||
base = ioremap(opregion_phy, 8*1024);
|
||||
if (!base)
|
||||
return -ENOMEM;
|
||||
|
||||
lid_state = base + 0x01ac;
|
||||
|
||||
dev_priv->lid_state = lid_state;
|
||||
dev_priv->lid_last_state = readl(lid_state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gma_intel_opregion_exit(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
if (dev_priv->lid_state)
|
||||
iounmap(dev_priv->lid_state);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/* TODO
|
||||
* - Split functions by vbt type
|
||||
* - Make them all take drm_device
|
||||
* - Check ioremap failures
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "mid_bios.h"
|
||||
|
||||
static void mid_get_fuse_settings(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
uint32_t fuse_value = 0;
|
||||
uint32_t fuse_value_tmp = 0;
|
||||
|
||||
#define FB_REG06 0xD0810600
|
||||
#define FB_MIPI_DISABLE (1 << 11)
|
||||
#define FB_REG09 0xD0810900
|
||||
#define FB_REG09 0xD0810900
|
||||
#define FB_SKU_MASK 0x7000
|
||||
#define FB_SKU_SHIFT 12
|
||||
#define FB_SKU_100 0
|
||||
#define FB_SKU_100L 1
|
||||
#define FB_SKU_83 2
|
||||
if (pci_root == NULL) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
pci_write_config_dword(pci_root, 0xD0, FB_REG06);
|
||||
pci_read_config_dword(pci_root, 0xD4, &fuse_value);
|
||||
|
||||
/* FB_MIPI_DISABLE doesn't mean LVDS on with Medfield */
|
||||
if (IS_MRST(dev))
|
||||
dev_priv->iLVDS_enable = fuse_value & FB_MIPI_DISABLE;
|
||||
|
||||
DRM_INFO("internal display is %s\n",
|
||||
dev_priv->iLVDS_enable ? "LVDS display" : "MIPI display");
|
||||
|
||||
/* Prevent runtime suspend at start*/
|
||||
if (dev_priv->iLVDS_enable) {
|
||||
dev_priv->is_lvds_on = true;
|
||||
dev_priv->is_mipi_on = false;
|
||||
} else {
|
||||
dev_priv->is_mipi_on = true;
|
||||
dev_priv->is_lvds_on = false;
|
||||
}
|
||||
|
||||
dev_priv->video_device_fuse = fuse_value;
|
||||
|
||||
pci_write_config_dword(pci_root, 0xD0, FB_REG09);
|
||||
pci_read_config_dword(pci_root, 0xD4, &fuse_value);
|
||||
|
||||
dev_dbg(dev->dev, "SKU values is 0x%x.\n", fuse_value);
|
||||
fuse_value_tmp = (fuse_value & FB_SKU_MASK) >> FB_SKU_SHIFT;
|
||||
|
||||
dev_priv->fuse_reg_value = fuse_value;
|
||||
|
||||
switch (fuse_value_tmp) {
|
||||
case FB_SKU_100:
|
||||
dev_priv->core_freq = 200;
|
||||
break;
|
||||
case FB_SKU_100L:
|
||||
dev_priv->core_freq = 100;
|
||||
break;
|
||||
case FB_SKU_83:
|
||||
dev_priv->core_freq = 166;
|
||||
break;
|
||||
default:
|
||||
dev_warn(dev->dev, "Invalid SKU values, SKU value = 0x%08x\n",
|
||||
fuse_value_tmp);
|
||||
dev_priv->core_freq = 0;
|
||||
}
|
||||
dev_dbg(dev->dev, "LNC core clk is %dMHz.\n", dev_priv->core_freq);
|
||||
pci_dev_put(pci_root);
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the revison ID, B0:D2:F0;0x08
|
||||
*/
|
||||
static void mid_get_pci_revID(struct drm_psb_private *dev_priv)
|
||||
{
|
||||
uint32_t platform_rev_id = 0;
|
||||
struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0));
|
||||
|
||||
if (pci_gfx_root == NULL) {
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
pci_read_config_dword(pci_gfx_root, 0x08, &platform_rev_id);
|
||||
dev_priv->platform_rev_id = (uint8_t) platform_rev_id;
|
||||
pci_dev_put(pci_gfx_root);
|
||||
dev_dbg(dev_priv->dev->dev, "platform_rev_id is %x\n",
|
||||
dev_priv->platform_rev_id);
|
||||
}
|
||||
|
||||
static void mid_get_vbt_data(struct drm_psb_private *dev_priv)
|
||||
{
|
||||
struct drm_device *dev = dev_priv->dev;
|
||||
struct oaktrail_vbt *vbt = &dev_priv->vbt_data;
|
||||
u32 addr;
|
||||
u16 new_size;
|
||||
u8 *vbt_virtual;
|
||||
u8 bpi;
|
||||
u8 number_desc = 0;
|
||||
struct oaktrail_timing_info *dp_ti = &dev_priv->gct_data.DTD;
|
||||
struct gct_r10_timing_info ti;
|
||||
void *pGCT;
|
||||
struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0));
|
||||
|
||||
/* Get the address of the platform config vbt, B0:D2:F0;0xFC */
|
||||
pci_read_config_dword(pci_gfx_root, 0xFC, &addr);
|
||||
pci_dev_put(pci_gfx_root);
|
||||
|
||||
dev_dbg(dev->dev, "drm platform config address is %x\n", addr);
|
||||
|
||||
/* check for platform config address == 0. */
|
||||
/* this means fw doesn't support vbt */
|
||||
|
||||
if (addr == 0) {
|
||||
vbt->size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the virtual address of the vbt */
|
||||
vbt_virtual = ioremap(addr, sizeof(*vbt));
|
||||
if (vbt_virtual == NULL) {
|
||||
vbt->size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(vbt, vbt_virtual, sizeof(*vbt));
|
||||
iounmap(vbt_virtual); /* Free virtual address space */
|
||||
|
||||
/* No matching signature don't process the data */
|
||||
if (memcmp(vbt->signature, "$GCT", 4)) {
|
||||
vbt->size = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
dev_dbg(dev->dev, "GCT revision is %x\n", vbt->revision);
|
||||
|
||||
switch (vbt->revision) {
|
||||
case 0:
|
||||
vbt->oaktrail_gct = ioremap(addr + sizeof(*vbt) - 4,
|
||||
vbt->size - sizeof(*vbt) + 4);
|
||||
pGCT = vbt->oaktrail_gct;
|
||||
bpi = ((struct oaktrail_gct_v1 *)pGCT)->PD.BootPanelIndex;
|
||||
dev_priv->gct_data.bpi = bpi;
|
||||
dev_priv->gct_data.pt =
|
||||
((struct oaktrail_gct_v1 *)pGCT)->PD.PanelType;
|
||||
memcpy(&dev_priv->gct_data.DTD,
|
||||
&((struct oaktrail_gct_v1 *)pGCT)->panel[bpi].DTD,
|
||||
sizeof(struct oaktrail_timing_info));
|
||||
dev_priv->gct_data.Panel_Port_Control =
|
||||
((struct oaktrail_gct_v1 *)pGCT)->panel[bpi].Panel_Port_Control;
|
||||
dev_priv->gct_data.Panel_MIPI_Display_Descriptor =
|
||||
((struct oaktrail_gct_v1 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor;
|
||||
break;
|
||||
case 1:
|
||||
vbt->oaktrail_gct = ioremap(addr + sizeof(*vbt) - 4,
|
||||
vbt->size - sizeof(*vbt) + 4);
|
||||
pGCT = vbt->oaktrail_gct;
|
||||
bpi = ((struct oaktrail_gct_v2 *)pGCT)->PD.BootPanelIndex;
|
||||
dev_priv->gct_data.bpi = bpi;
|
||||
dev_priv->gct_data.pt =
|
||||
((struct oaktrail_gct_v2 *)pGCT)->PD.PanelType;
|
||||
memcpy(&dev_priv->gct_data.DTD,
|
||||
&((struct oaktrail_gct_v2 *)pGCT)->panel[bpi].DTD,
|
||||
sizeof(struct oaktrail_timing_info));
|
||||
dev_priv->gct_data.Panel_Port_Control =
|
||||
((struct oaktrail_gct_v2 *)pGCT)->panel[bpi].Panel_Port_Control;
|
||||
dev_priv->gct_data.Panel_MIPI_Display_Descriptor =
|
||||
((struct oaktrail_gct_v2 *)pGCT)->panel[bpi].Panel_MIPI_Display_Descriptor;
|
||||
break;
|
||||
case 0x10:
|
||||
/*header definition changed from rev 01 (v2) to rev 10h. */
|
||||
/*so, some values have changed location*/
|
||||
new_size = vbt->checksum; /*checksum contains lo size byte*/
|
||||
/*LSB of oaktrail_gct contains hi size byte*/
|
||||
new_size |= ((0xff & (unsigned int)(long)vbt->oaktrail_gct)) << 8;
|
||||
|
||||
vbt->checksum = vbt->size; /*size contains the checksum*/
|
||||
if (new_size > 0xff)
|
||||
vbt->size = 0xff; /*restrict size to 255*/
|
||||
else
|
||||
vbt->size = new_size;
|
||||
|
||||
/* number of descriptors defined in the GCT */
|
||||
number_desc = ((0xff00 & (unsigned int)(long)vbt->oaktrail_gct)) >> 8;
|
||||
bpi = ((0xff0000 & (unsigned int)(long)vbt->oaktrail_gct)) >> 16;
|
||||
vbt->oaktrail_gct = ioremap(addr + GCT_R10_HEADER_SIZE,
|
||||
GCT_R10_DISPLAY_DESC_SIZE * number_desc);
|
||||
pGCT = vbt->oaktrail_gct;
|
||||
pGCT = (u8 *)pGCT + (bpi*GCT_R10_DISPLAY_DESC_SIZE);
|
||||
dev_priv->gct_data.bpi = bpi; /*save boot panel id*/
|
||||
|
||||
/*copy the GCT display timings into a temp structure*/
|
||||
memcpy(&ti, pGCT, sizeof(struct gct_r10_timing_info));
|
||||
|
||||
/*now copy the temp struct into the dev_priv->gct_data*/
|
||||
dp_ti->pixel_clock = ti.pixel_clock;
|
||||
dp_ti->hactive_hi = ti.hactive_hi;
|
||||
dp_ti->hactive_lo = ti.hactive_lo;
|
||||
dp_ti->hblank_hi = ti.hblank_hi;
|
||||
dp_ti->hblank_lo = ti.hblank_lo;
|
||||
dp_ti->hsync_offset_hi = ti.hsync_offset_hi;
|
||||
dp_ti->hsync_offset_lo = ti.hsync_offset_lo;
|
||||
dp_ti->hsync_pulse_width_hi = ti.hsync_pulse_width_hi;
|
||||
dp_ti->hsync_pulse_width_lo = ti.hsync_pulse_width_lo;
|
||||
dp_ti->vactive_hi = ti.vactive_hi;
|
||||
dp_ti->vactive_lo = ti.vactive_lo;
|
||||
dp_ti->vblank_hi = ti.vblank_hi;
|
||||
dp_ti->vblank_lo = ti.vblank_lo;
|
||||
dp_ti->vsync_offset_hi = ti.vsync_offset_hi;
|
||||
dp_ti->vsync_offset_lo = ti.vsync_offset_lo;
|
||||
dp_ti->vsync_pulse_width_hi = ti.vsync_pulse_width_hi;
|
||||
dp_ti->vsync_pulse_width_lo = ti.vsync_pulse_width_lo;
|
||||
|
||||
/* Move the MIPI_Display_Descriptor data from GCT to dev priv */
|
||||
dev_priv->gct_data.Panel_MIPI_Display_Descriptor =
|
||||
*((u8 *)pGCT + 0x0d);
|
||||
dev_priv->gct_data.Panel_MIPI_Display_Descriptor |=
|
||||
(*((u8 *)pGCT + 0x0e)) << 8;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "Unknown revision of GCT!\n");
|
||||
vbt->size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int mid_chip_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
mid_get_fuse_settings(dev);
|
||||
mid_get_vbt_data(dev_priv);
|
||||
mid_get_pci_revID(dev_priv);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
extern int mid_chip_setup(struct drm_device *dev);
|
||||
|
|
@ -0,0 +1,858 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 <drm/drmP.h>
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
|
||||
/*
|
||||
* Code for the SGX MMU:
|
||||
*/
|
||||
|
||||
/*
|
||||
* clflush on one processor only:
|
||||
* clflush should apparently flush the cache line on all processors in an
|
||||
* SMP system.
|
||||
*/
|
||||
|
||||
/*
|
||||
* kmap atomic:
|
||||
* The usage of the slots must be completely encapsulated within a spinlock, and
|
||||
* no other functions that may be using the locks for other purposed may be
|
||||
* called from within the locked region.
|
||||
* Since the slots are per processor, this will guarantee that we are the only
|
||||
* user.
|
||||
*/
|
||||
|
||||
/*
|
||||
* TODO: Inserting ptes from an interrupt handler:
|
||||
* This may be desirable for some SGX functionality where the GPU can fault in
|
||||
* needed pages. For that, we need to make an atomic insert_pages function, that
|
||||
* may fail.
|
||||
* If it fails, the caller need to insert the page using a workqueue function,
|
||||
* but on average it should be fast.
|
||||
*/
|
||||
|
||||
struct psb_mmu_driver {
|
||||
/* protects driver- and pd structures. Always take in read mode
|
||||
* before taking the page table spinlock.
|
||||
*/
|
||||
struct rw_semaphore sem;
|
||||
|
||||
/* protects page tables, directory tables and pt tables.
|
||||
* and pt structures.
|
||||
*/
|
||||
spinlock_t lock;
|
||||
|
||||
atomic_t needs_tlbflush;
|
||||
|
||||
uint8_t __iomem *register_map;
|
||||
struct psb_mmu_pd *default_pd;
|
||||
/*uint32_t bif_ctrl;*/
|
||||
int has_clflush;
|
||||
int clflush_add;
|
||||
unsigned long clflush_mask;
|
||||
|
||||
struct drm_psb_private *dev_priv;
|
||||
};
|
||||
|
||||
struct psb_mmu_pd;
|
||||
|
||||
struct psb_mmu_pt {
|
||||
struct psb_mmu_pd *pd;
|
||||
uint32_t index;
|
||||
uint32_t count;
|
||||
struct page *p;
|
||||
uint32_t *v;
|
||||
};
|
||||
|
||||
struct psb_mmu_pd {
|
||||
struct psb_mmu_driver *driver;
|
||||
int hw_context;
|
||||
struct psb_mmu_pt **tables;
|
||||
struct page *p;
|
||||
struct page *dummy_pt;
|
||||
struct page *dummy_page;
|
||||
uint32_t pd_mask;
|
||||
uint32_t invalid_pde;
|
||||
uint32_t invalid_pte;
|
||||
};
|
||||
|
||||
static inline uint32_t psb_mmu_pt_index(uint32_t offset)
|
||||
{
|
||||
return (offset >> PSB_PTE_SHIFT) & 0x3FF;
|
||||
}
|
||||
|
||||
static inline uint32_t psb_mmu_pd_index(uint32_t offset)
|
||||
{
|
||||
return offset >> PSB_PDE_SHIFT;
|
||||
}
|
||||
|
||||
static inline void psb_clflush(void *addr)
|
||||
{
|
||||
__asm__ __volatile__("clflush (%0)\n" : : "r"(addr) : "memory");
|
||||
}
|
||||
|
||||
static inline void psb_mmu_clflush(struct psb_mmu_driver *driver,
|
||||
void *addr)
|
||||
{
|
||||
if (!driver->has_clflush)
|
||||
return;
|
||||
|
||||
mb();
|
||||
psb_clflush(addr);
|
||||
mb();
|
||||
}
|
||||
|
||||
static void psb_page_clflush(struct psb_mmu_driver *driver, struct page* page)
|
||||
{
|
||||
uint32_t clflush_add = driver->clflush_add >> PAGE_SHIFT;
|
||||
uint32_t clflush_count = PAGE_SIZE / clflush_add;
|
||||
int i;
|
||||
uint8_t *clf;
|
||||
|
||||
clf = kmap_atomic(page, KM_USER0);
|
||||
mb();
|
||||
for (i = 0; i < clflush_count; ++i) {
|
||||
psb_clflush(clf);
|
||||
clf += clflush_add;
|
||||
}
|
||||
mb();
|
||||
kunmap_atomic(clf, KM_USER0);
|
||||
}
|
||||
|
||||
static void psb_pages_clflush(struct psb_mmu_driver *driver,
|
||||
struct page *page[], unsigned long num_pages)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!driver->has_clflush)
|
||||
return ;
|
||||
|
||||
for (i = 0; i < num_pages; i++)
|
||||
psb_page_clflush(driver, *page++);
|
||||
}
|
||||
|
||||
static void psb_mmu_flush_pd_locked(struct psb_mmu_driver *driver,
|
||||
int force)
|
||||
{
|
||||
atomic_set(&driver->needs_tlbflush, 0);
|
||||
}
|
||||
|
||||
static void psb_mmu_flush_pd(struct psb_mmu_driver *driver, int force)
|
||||
{
|
||||
down_write(&driver->sem);
|
||||
psb_mmu_flush_pd_locked(driver, force);
|
||||
up_write(&driver->sem);
|
||||
}
|
||||
|
||||
void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot)
|
||||
{
|
||||
if (rc_prot)
|
||||
down_write(&driver->sem);
|
||||
if (rc_prot)
|
||||
up_write(&driver->sem);
|
||||
}
|
||||
|
||||
void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context)
|
||||
{
|
||||
/*ttm_tt_cache_flush(&pd->p, 1);*/
|
||||
psb_pages_clflush(pd->driver, &pd->p, 1);
|
||||
down_write(&pd->driver->sem);
|
||||
wmb();
|
||||
psb_mmu_flush_pd_locked(pd->driver, 1);
|
||||
pd->hw_context = hw_context;
|
||||
up_write(&pd->driver->sem);
|
||||
|
||||
}
|
||||
|
||||
static inline unsigned long psb_pd_addr_end(unsigned long addr,
|
||||
unsigned long end)
|
||||
{
|
||||
|
||||
addr = (addr + PSB_PDE_MASK + 1) & ~PSB_PDE_MASK;
|
||||
return (addr < end) ? addr : end;
|
||||
}
|
||||
|
||||
static inline uint32_t psb_mmu_mask_pte(uint32_t pfn, int type)
|
||||
{
|
||||
uint32_t mask = PSB_PTE_VALID;
|
||||
|
||||
if (type & PSB_MMU_CACHED_MEMORY)
|
||||
mask |= PSB_PTE_CACHED;
|
||||
if (type & PSB_MMU_RO_MEMORY)
|
||||
mask |= PSB_PTE_RO;
|
||||
if (type & PSB_MMU_WO_MEMORY)
|
||||
mask |= PSB_PTE_WO;
|
||||
|
||||
return (pfn << PAGE_SHIFT) | mask;
|
||||
}
|
||||
|
||||
struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
|
||||
int trap_pagefaults, int invalid_type)
|
||||
{
|
||||
struct psb_mmu_pd *pd = kmalloc(sizeof(*pd), GFP_KERNEL);
|
||||
uint32_t *v;
|
||||
int i;
|
||||
|
||||
if (!pd)
|
||||
return NULL;
|
||||
|
||||
pd->p = alloc_page(GFP_DMA32);
|
||||
if (!pd->p)
|
||||
goto out_err1;
|
||||
pd->dummy_pt = alloc_page(GFP_DMA32);
|
||||
if (!pd->dummy_pt)
|
||||
goto out_err2;
|
||||
pd->dummy_page = alloc_page(GFP_DMA32);
|
||||
if (!pd->dummy_page)
|
||||
goto out_err3;
|
||||
|
||||
if (!trap_pagefaults) {
|
||||
pd->invalid_pde =
|
||||
psb_mmu_mask_pte(page_to_pfn(pd->dummy_pt),
|
||||
invalid_type);
|
||||
pd->invalid_pte =
|
||||
psb_mmu_mask_pte(page_to_pfn(pd->dummy_page),
|
||||
invalid_type);
|
||||
} else {
|
||||
pd->invalid_pde = 0;
|
||||
pd->invalid_pte = 0;
|
||||
}
|
||||
|
||||
v = kmap(pd->dummy_pt);
|
||||
for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
|
||||
v[i] = pd->invalid_pte;
|
||||
|
||||
kunmap(pd->dummy_pt);
|
||||
|
||||
v = kmap(pd->p);
|
||||
for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
|
||||
v[i] = pd->invalid_pde;
|
||||
|
||||
kunmap(pd->p);
|
||||
|
||||
clear_page(kmap(pd->dummy_page));
|
||||
kunmap(pd->dummy_page);
|
||||
|
||||
pd->tables = vmalloc_user(sizeof(struct psb_mmu_pt *) * 1024);
|
||||
if (!pd->tables)
|
||||
goto out_err4;
|
||||
|
||||
pd->hw_context = -1;
|
||||
pd->pd_mask = PSB_PTE_VALID;
|
||||
pd->driver = driver;
|
||||
|
||||
return pd;
|
||||
|
||||
out_err4:
|
||||
__free_page(pd->dummy_page);
|
||||
out_err3:
|
||||
__free_page(pd->dummy_pt);
|
||||
out_err2:
|
||||
__free_page(pd->p);
|
||||
out_err1:
|
||||
kfree(pd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void psb_mmu_free_pt(struct psb_mmu_pt *pt)
|
||||
{
|
||||
__free_page(pt->p);
|
||||
kfree(pt);
|
||||
}
|
||||
|
||||
void psb_mmu_free_pagedir(struct psb_mmu_pd *pd)
|
||||
{
|
||||
struct psb_mmu_driver *driver = pd->driver;
|
||||
struct psb_mmu_pt *pt;
|
||||
int i;
|
||||
|
||||
down_write(&driver->sem);
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush_pd_locked(driver, 1);
|
||||
|
||||
/* Should take the spinlock here, but we don't need to do that
|
||||
since we have the semaphore in write mode. */
|
||||
|
||||
for (i = 0; i < 1024; ++i) {
|
||||
pt = pd->tables[i];
|
||||
if (pt)
|
||||
psb_mmu_free_pt(pt);
|
||||
}
|
||||
|
||||
vfree(pd->tables);
|
||||
__free_page(pd->dummy_page);
|
||||
__free_page(pd->dummy_pt);
|
||||
__free_page(pd->p);
|
||||
kfree(pd);
|
||||
up_write(&driver->sem);
|
||||
}
|
||||
|
||||
static struct psb_mmu_pt *psb_mmu_alloc_pt(struct psb_mmu_pd *pd)
|
||||
{
|
||||
struct psb_mmu_pt *pt = kmalloc(sizeof(*pt), GFP_KERNEL);
|
||||
void *v;
|
||||
uint32_t clflush_add = pd->driver->clflush_add >> PAGE_SHIFT;
|
||||
uint32_t clflush_count = PAGE_SIZE / clflush_add;
|
||||
spinlock_t *lock = &pd->driver->lock;
|
||||
uint8_t *clf;
|
||||
uint32_t *ptes;
|
||||
int i;
|
||||
|
||||
if (!pt)
|
||||
return NULL;
|
||||
|
||||
pt->p = alloc_page(GFP_DMA32);
|
||||
if (!pt->p) {
|
||||
kfree(pt);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
spin_lock(lock);
|
||||
|
||||
v = kmap_atomic(pt->p, KM_USER0);
|
||||
clf = (uint8_t *) v;
|
||||
ptes = (uint32_t *) v;
|
||||
for (i = 0; i < (PAGE_SIZE / sizeof(uint32_t)); ++i)
|
||||
*ptes++ = pd->invalid_pte;
|
||||
|
||||
|
||||
if (pd->driver->has_clflush && pd->hw_context != -1) {
|
||||
mb();
|
||||
for (i = 0; i < clflush_count; ++i) {
|
||||
psb_clflush(clf);
|
||||
clf += clflush_add;
|
||||
}
|
||||
mb();
|
||||
}
|
||||
|
||||
kunmap_atomic(v, KM_USER0);
|
||||
spin_unlock(lock);
|
||||
|
||||
pt->count = 0;
|
||||
pt->pd = pd;
|
||||
pt->index = 0;
|
||||
|
||||
return pt;
|
||||
}
|
||||
|
||||
struct psb_mmu_pt *psb_mmu_pt_alloc_map_lock(struct psb_mmu_pd *pd,
|
||||
unsigned long addr)
|
||||
{
|
||||
uint32_t index = psb_mmu_pd_index(addr);
|
||||
struct psb_mmu_pt *pt;
|
||||
uint32_t *v;
|
||||
spinlock_t *lock = &pd->driver->lock;
|
||||
|
||||
spin_lock(lock);
|
||||
pt = pd->tables[index];
|
||||
while (!pt) {
|
||||
spin_unlock(lock);
|
||||
pt = psb_mmu_alloc_pt(pd);
|
||||
if (!pt)
|
||||
return NULL;
|
||||
spin_lock(lock);
|
||||
|
||||
if (pd->tables[index]) {
|
||||
spin_unlock(lock);
|
||||
psb_mmu_free_pt(pt);
|
||||
spin_lock(lock);
|
||||
pt = pd->tables[index];
|
||||
continue;
|
||||
}
|
||||
|
||||
v = kmap_atomic(pd->p, KM_USER0);
|
||||
pd->tables[index] = pt;
|
||||
v[index] = (page_to_pfn(pt->p) << 12) | pd->pd_mask;
|
||||
pt->index = index;
|
||||
kunmap_atomic((void *) v, KM_USER0);
|
||||
|
||||
if (pd->hw_context != -1) {
|
||||
psb_mmu_clflush(pd->driver, (void *) &v[index]);
|
||||
atomic_set(&pd->driver->needs_tlbflush, 1);
|
||||
}
|
||||
}
|
||||
pt->v = kmap_atomic(pt->p, KM_USER0);
|
||||
return pt;
|
||||
}
|
||||
|
||||
static struct psb_mmu_pt *psb_mmu_pt_map_lock(struct psb_mmu_pd *pd,
|
||||
unsigned long addr)
|
||||
{
|
||||
uint32_t index = psb_mmu_pd_index(addr);
|
||||
struct psb_mmu_pt *pt;
|
||||
spinlock_t *lock = &pd->driver->lock;
|
||||
|
||||
spin_lock(lock);
|
||||
pt = pd->tables[index];
|
||||
if (!pt) {
|
||||
spin_unlock(lock);
|
||||
return NULL;
|
||||
}
|
||||
pt->v = kmap_atomic(pt->p, KM_USER0);
|
||||
return pt;
|
||||
}
|
||||
|
||||
static void psb_mmu_pt_unmap_unlock(struct psb_mmu_pt *pt)
|
||||
{
|
||||
struct psb_mmu_pd *pd = pt->pd;
|
||||
uint32_t *v;
|
||||
|
||||
kunmap_atomic(pt->v, KM_USER0);
|
||||
if (pt->count == 0) {
|
||||
v = kmap_atomic(pd->p, KM_USER0);
|
||||
v[pt->index] = pd->invalid_pde;
|
||||
pd->tables[pt->index] = NULL;
|
||||
|
||||
if (pd->hw_context != -1) {
|
||||
psb_mmu_clflush(pd->driver,
|
||||
(void *) &v[pt->index]);
|
||||
atomic_set(&pd->driver->needs_tlbflush, 1);
|
||||
}
|
||||
kunmap_atomic(pt->v, KM_USER0);
|
||||
spin_unlock(&pd->driver->lock);
|
||||
psb_mmu_free_pt(pt);
|
||||
return;
|
||||
}
|
||||
spin_unlock(&pd->driver->lock);
|
||||
}
|
||||
|
||||
static inline void psb_mmu_set_pte(struct psb_mmu_pt *pt,
|
||||
unsigned long addr, uint32_t pte)
|
||||
{
|
||||
pt->v[psb_mmu_pt_index(addr)] = pte;
|
||||
}
|
||||
|
||||
static inline void psb_mmu_invalidate_pte(struct psb_mmu_pt *pt,
|
||||
unsigned long addr)
|
||||
{
|
||||
pt->v[psb_mmu_pt_index(addr)] = pt->pd->invalid_pte;
|
||||
}
|
||||
|
||||
|
||||
void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd,
|
||||
uint32_t mmu_offset, uint32_t gtt_start,
|
||||
uint32_t gtt_pages)
|
||||
{
|
||||
uint32_t *v;
|
||||
uint32_t start = psb_mmu_pd_index(mmu_offset);
|
||||
struct psb_mmu_driver *driver = pd->driver;
|
||||
int num_pages = gtt_pages;
|
||||
|
||||
down_read(&driver->sem);
|
||||
spin_lock(&driver->lock);
|
||||
|
||||
v = kmap_atomic(pd->p, KM_USER0);
|
||||
v += start;
|
||||
|
||||
while (gtt_pages--) {
|
||||
*v++ = gtt_start | pd->pd_mask;
|
||||
gtt_start += PAGE_SIZE;
|
||||
}
|
||||
|
||||
/*ttm_tt_cache_flush(&pd->p, num_pages);*/
|
||||
psb_pages_clflush(pd->driver, &pd->p, num_pages);
|
||||
kunmap_atomic(v, KM_USER0);
|
||||
spin_unlock(&driver->lock);
|
||||
|
||||
if (pd->hw_context != -1)
|
||||
atomic_set(&pd->driver->needs_tlbflush, 1);
|
||||
|
||||
up_read(&pd->driver->sem);
|
||||
psb_mmu_flush_pd(pd->driver, 0);
|
||||
}
|
||||
|
||||
struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver *driver)
|
||||
{
|
||||
struct psb_mmu_pd *pd;
|
||||
|
||||
/* down_read(&driver->sem); */
|
||||
pd = driver->default_pd;
|
||||
/* up_read(&driver->sem); */
|
||||
|
||||
return pd;
|
||||
}
|
||||
|
||||
/* Returns the physical address of the PD shared by sgx/msvdx */
|
||||
uint32_t psb_get_default_pd_addr(struct psb_mmu_driver *driver)
|
||||
{
|
||||
struct psb_mmu_pd *pd;
|
||||
|
||||
pd = psb_mmu_get_default_pd(driver);
|
||||
return page_to_pfn(pd->p) << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
void psb_mmu_driver_takedown(struct psb_mmu_driver *driver)
|
||||
{
|
||||
psb_mmu_free_pagedir(driver->default_pd);
|
||||
kfree(driver);
|
||||
}
|
||||
|
||||
struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
|
||||
int trap_pagefaults,
|
||||
int invalid_type,
|
||||
struct drm_psb_private *dev_priv)
|
||||
{
|
||||
struct psb_mmu_driver *driver;
|
||||
|
||||
driver = kmalloc(sizeof(*driver), GFP_KERNEL);
|
||||
|
||||
if (!driver)
|
||||
return NULL;
|
||||
driver->dev_priv = dev_priv;
|
||||
|
||||
driver->default_pd = psb_mmu_alloc_pd(driver, trap_pagefaults,
|
||||
invalid_type);
|
||||
if (!driver->default_pd)
|
||||
goto out_err1;
|
||||
|
||||
spin_lock_init(&driver->lock);
|
||||
init_rwsem(&driver->sem);
|
||||
down_write(&driver->sem);
|
||||
driver->register_map = registers;
|
||||
atomic_set(&driver->needs_tlbflush, 1);
|
||||
|
||||
driver->has_clflush = 0;
|
||||
|
||||
if (boot_cpu_has(X86_FEATURE_CLFLSH)) {
|
||||
uint32_t tfms, misc, cap0, cap4, clflush_size;
|
||||
|
||||
/*
|
||||
* clflush size is determined at kernel setup for x86_64
|
||||
* but not for i386. We have to do it here.
|
||||
*/
|
||||
|
||||
cpuid(0x00000001, &tfms, &misc, &cap0, &cap4);
|
||||
clflush_size = ((misc >> 8) & 0xff) * 8;
|
||||
driver->has_clflush = 1;
|
||||
driver->clflush_add =
|
||||
PAGE_SIZE * clflush_size / sizeof(uint32_t);
|
||||
driver->clflush_mask = driver->clflush_add - 1;
|
||||
driver->clflush_mask = ~driver->clflush_mask;
|
||||
}
|
||||
|
||||
up_write(&driver->sem);
|
||||
return driver;
|
||||
|
||||
out_err1:
|
||||
kfree(driver);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void psb_mmu_flush_ptes(struct psb_mmu_pd *pd,
|
||||
unsigned long address, uint32_t num_pages,
|
||||
uint32_t desired_tile_stride,
|
||||
uint32_t hw_tile_stride)
|
||||
{
|
||||
struct psb_mmu_pt *pt;
|
||||
uint32_t rows = 1;
|
||||
uint32_t i;
|
||||
unsigned long addr;
|
||||
unsigned long end;
|
||||
unsigned long next;
|
||||
unsigned long add;
|
||||
unsigned long row_add;
|
||||
unsigned long clflush_add = pd->driver->clflush_add;
|
||||
unsigned long clflush_mask = pd->driver->clflush_mask;
|
||||
|
||||
if (!pd->driver->has_clflush) {
|
||||
/*ttm_tt_cache_flush(&pd->p, num_pages);*/
|
||||
psb_pages_clflush(pd->driver, &pd->p, num_pages);
|
||||
return;
|
||||
}
|
||||
|
||||
if (hw_tile_stride)
|
||||
rows = num_pages / desired_tile_stride;
|
||||
else
|
||||
desired_tile_stride = num_pages;
|
||||
|
||||
add = desired_tile_stride << PAGE_SHIFT;
|
||||
row_add = hw_tile_stride << PAGE_SHIFT;
|
||||
mb();
|
||||
for (i = 0; i < rows; ++i) {
|
||||
|
||||
addr = address;
|
||||
end = addr + add;
|
||||
|
||||
do {
|
||||
next = psb_pd_addr_end(addr, end);
|
||||
pt = psb_mmu_pt_map_lock(pd, addr);
|
||||
if (!pt)
|
||||
continue;
|
||||
do {
|
||||
psb_clflush(&pt->v
|
||||
[psb_mmu_pt_index(addr)]);
|
||||
} while (addr +=
|
||||
clflush_add,
|
||||
(addr & clflush_mask) < next);
|
||||
|
||||
psb_mmu_pt_unmap_unlock(pt);
|
||||
} while (addr = next, next != end);
|
||||
address += row_add;
|
||||
}
|
||||
mb();
|
||||
}
|
||||
|
||||
void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
|
||||
unsigned long address, uint32_t num_pages)
|
||||
{
|
||||
struct psb_mmu_pt *pt;
|
||||
unsigned long addr;
|
||||
unsigned long end;
|
||||
unsigned long next;
|
||||
unsigned long f_address = address;
|
||||
|
||||
down_read(&pd->driver->sem);
|
||||
|
||||
addr = address;
|
||||
end = addr + (num_pages << PAGE_SHIFT);
|
||||
|
||||
do {
|
||||
next = psb_pd_addr_end(addr, end);
|
||||
pt = psb_mmu_pt_alloc_map_lock(pd, addr);
|
||||
if (!pt)
|
||||
goto out;
|
||||
do {
|
||||
psb_mmu_invalidate_pte(pt, addr);
|
||||
--pt->count;
|
||||
} while (addr += PAGE_SIZE, addr < next);
|
||||
psb_mmu_pt_unmap_unlock(pt);
|
||||
|
||||
} while (addr = next, next != end);
|
||||
|
||||
out:
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1);
|
||||
|
||||
up_read(&pd->driver->sem);
|
||||
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush(pd->driver, 0);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void psb_mmu_remove_pages(struct psb_mmu_pd *pd, unsigned long address,
|
||||
uint32_t num_pages, uint32_t desired_tile_stride,
|
||||
uint32_t hw_tile_stride)
|
||||
{
|
||||
struct psb_mmu_pt *pt;
|
||||
uint32_t rows = 1;
|
||||
uint32_t i;
|
||||
unsigned long addr;
|
||||
unsigned long end;
|
||||
unsigned long next;
|
||||
unsigned long add;
|
||||
unsigned long row_add;
|
||||
unsigned long f_address = address;
|
||||
|
||||
if (hw_tile_stride)
|
||||
rows = num_pages / desired_tile_stride;
|
||||
else
|
||||
desired_tile_stride = num_pages;
|
||||
|
||||
add = desired_tile_stride << PAGE_SHIFT;
|
||||
row_add = hw_tile_stride << PAGE_SHIFT;
|
||||
|
||||
/* down_read(&pd->driver->sem); */
|
||||
|
||||
/* Make sure we only need to flush this processor's cache */
|
||||
|
||||
for (i = 0; i < rows; ++i) {
|
||||
|
||||
addr = address;
|
||||
end = addr + add;
|
||||
|
||||
do {
|
||||
next = psb_pd_addr_end(addr, end);
|
||||
pt = psb_mmu_pt_map_lock(pd, addr);
|
||||
if (!pt)
|
||||
continue;
|
||||
do {
|
||||
psb_mmu_invalidate_pte(pt, addr);
|
||||
--pt->count;
|
||||
|
||||
} while (addr += PAGE_SIZE, addr < next);
|
||||
psb_mmu_pt_unmap_unlock(pt);
|
||||
|
||||
} while (addr = next, next != end);
|
||||
address += row_add;
|
||||
}
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush_ptes(pd, f_address, num_pages,
|
||||
desired_tile_stride, hw_tile_stride);
|
||||
|
||||
/* up_read(&pd->driver->sem); */
|
||||
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush(pd->driver, 0);
|
||||
}
|
||||
|
||||
int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd, uint32_t start_pfn,
|
||||
unsigned long address, uint32_t num_pages,
|
||||
int type)
|
||||
{
|
||||
struct psb_mmu_pt *pt;
|
||||
uint32_t pte;
|
||||
unsigned long addr;
|
||||
unsigned long end;
|
||||
unsigned long next;
|
||||
unsigned long f_address = address;
|
||||
int ret = 0;
|
||||
|
||||
down_read(&pd->driver->sem);
|
||||
|
||||
addr = address;
|
||||
end = addr + (num_pages << PAGE_SHIFT);
|
||||
|
||||
do {
|
||||
next = psb_pd_addr_end(addr, end);
|
||||
pt = psb_mmu_pt_alloc_map_lock(pd, addr);
|
||||
if (!pt) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
do {
|
||||
pte = psb_mmu_mask_pte(start_pfn++, type);
|
||||
psb_mmu_set_pte(pt, addr, pte);
|
||||
pt->count++;
|
||||
} while (addr += PAGE_SIZE, addr < next);
|
||||
psb_mmu_pt_unmap_unlock(pt);
|
||||
|
||||
} while (addr = next, next != end);
|
||||
|
||||
out:
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush_ptes(pd, f_address, num_pages, 1, 1);
|
||||
|
||||
up_read(&pd->driver->sem);
|
||||
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush(pd->driver, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
|
||||
unsigned long address, uint32_t num_pages,
|
||||
uint32_t desired_tile_stride,
|
||||
uint32_t hw_tile_stride, int type)
|
||||
{
|
||||
struct psb_mmu_pt *pt;
|
||||
uint32_t rows = 1;
|
||||
uint32_t i;
|
||||
uint32_t pte;
|
||||
unsigned long addr;
|
||||
unsigned long end;
|
||||
unsigned long next;
|
||||
unsigned long add;
|
||||
unsigned long row_add;
|
||||
unsigned long f_address = address;
|
||||
int ret = 0;
|
||||
|
||||
if (hw_tile_stride) {
|
||||
if (num_pages % desired_tile_stride != 0)
|
||||
return -EINVAL;
|
||||
rows = num_pages / desired_tile_stride;
|
||||
} else {
|
||||
desired_tile_stride = num_pages;
|
||||
}
|
||||
|
||||
add = desired_tile_stride << PAGE_SHIFT;
|
||||
row_add = hw_tile_stride << PAGE_SHIFT;
|
||||
|
||||
down_read(&pd->driver->sem);
|
||||
|
||||
for (i = 0; i < rows; ++i) {
|
||||
|
||||
addr = address;
|
||||
end = addr + add;
|
||||
|
||||
do {
|
||||
next = psb_pd_addr_end(addr, end);
|
||||
pt = psb_mmu_pt_alloc_map_lock(pd, addr);
|
||||
if (!pt) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
do {
|
||||
pte =
|
||||
psb_mmu_mask_pte(page_to_pfn(*pages++),
|
||||
type);
|
||||
psb_mmu_set_pte(pt, addr, pte);
|
||||
pt->count++;
|
||||
} while (addr += PAGE_SIZE, addr < next);
|
||||
psb_mmu_pt_unmap_unlock(pt);
|
||||
|
||||
} while (addr = next, next != end);
|
||||
|
||||
address += row_add;
|
||||
}
|
||||
out:
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush_ptes(pd, f_address, num_pages,
|
||||
desired_tile_stride, hw_tile_stride);
|
||||
|
||||
up_read(&pd->driver->sem);
|
||||
|
||||
if (pd->hw_context != -1)
|
||||
psb_mmu_flush(pd->driver, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual,
|
||||
unsigned long *pfn)
|
||||
{
|
||||
int ret;
|
||||
struct psb_mmu_pt *pt;
|
||||
uint32_t tmp;
|
||||
spinlock_t *lock = &pd->driver->lock;
|
||||
|
||||
down_read(&pd->driver->sem);
|
||||
pt = psb_mmu_pt_map_lock(pd, virtual);
|
||||
if (!pt) {
|
||||
uint32_t *v;
|
||||
|
||||
spin_lock(lock);
|
||||
v = kmap_atomic(pd->p, KM_USER0);
|
||||
tmp = v[psb_mmu_pd_index(virtual)];
|
||||
kunmap_atomic(v, KM_USER0);
|
||||
spin_unlock(lock);
|
||||
|
||||
if (tmp != pd->invalid_pde || !(tmp & PSB_PTE_VALID) ||
|
||||
!(pd->invalid_pte & PSB_PTE_VALID)) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
ret = 0;
|
||||
*pfn = pd->invalid_pte >> PAGE_SHIFT;
|
||||
goto out;
|
||||
}
|
||||
tmp = pt->v[psb_mmu_pt_index(virtual)];
|
||||
if (!(tmp & PSB_PTE_VALID)) {
|
||||
ret = -EINVAL;
|
||||
} else {
|
||||
ret = 0;
|
||||
*pfn = tmp >> PAGE_SHIFT;
|
||||
}
|
||||
psb_mmu_pt_unmap_unlock(pt);
|
||||
out:
|
||||
up_read(&pd->driver->sem);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
/* MID device specific descriptors */
|
||||
|
||||
struct oaktrail_vbt {
|
||||
s8 signature[4]; /*4 bytes,"$GCT" */
|
||||
u8 revision;
|
||||
u8 size;
|
||||
u8 checksum;
|
||||
void *oaktrail_gct;
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_timing_info {
|
||||
u16 pixel_clock;
|
||||
u8 hactive_lo;
|
||||
u8 hblank_lo;
|
||||
u8 hblank_hi:4;
|
||||
u8 hactive_hi:4;
|
||||
u8 vactive_lo;
|
||||
u8 vblank_lo;
|
||||
u8 vblank_hi:4;
|
||||
u8 vactive_hi:4;
|
||||
u8 hsync_offset_lo;
|
||||
u8 hsync_pulse_width_lo;
|
||||
u8 vsync_pulse_width_lo:4;
|
||||
u8 vsync_offset_lo:4;
|
||||
u8 vsync_pulse_width_hi:2;
|
||||
u8 vsync_offset_hi:2;
|
||||
u8 hsync_pulse_width_hi:2;
|
||||
u8 hsync_offset_hi:2;
|
||||
u8 width_mm_lo;
|
||||
u8 height_mm_lo;
|
||||
u8 height_mm_hi:4;
|
||||
u8 width_mm_hi:4;
|
||||
u8 hborder;
|
||||
u8 vborder;
|
||||
u8 unknown0:1;
|
||||
u8 hsync_positive:1;
|
||||
u8 vsync_positive:1;
|
||||
u8 separate_sync:2;
|
||||
u8 stereo:1;
|
||||
u8 unknown6:1;
|
||||
u8 interlaced:1;
|
||||
} __packed;
|
||||
|
||||
struct gct_r10_timing_info {
|
||||
u16 pixel_clock;
|
||||
u32 hactive_lo:8;
|
||||
u32 hactive_hi:4;
|
||||
u32 hblank_lo:8;
|
||||
u32 hblank_hi:4;
|
||||
u32 hsync_offset_lo:8;
|
||||
u16 hsync_offset_hi:2;
|
||||
u16 hsync_pulse_width_lo:8;
|
||||
u16 hsync_pulse_width_hi:2;
|
||||
u16 hsync_positive:1;
|
||||
u16 rsvd_1:3;
|
||||
u8 vactive_lo:8;
|
||||
u16 vactive_hi:4;
|
||||
u16 vblank_lo:8;
|
||||
u16 vblank_hi:4;
|
||||
u16 vsync_offset_lo:4;
|
||||
u16 vsync_offset_hi:2;
|
||||
u16 vsync_pulse_width_lo:4;
|
||||
u16 vsync_pulse_width_hi:2;
|
||||
u16 vsync_positive:1;
|
||||
u16 rsvd_2:3;
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_panel_descriptor_v1 {
|
||||
u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */
|
||||
/* 0x61190 if MIPI */
|
||||
u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/
|
||||
u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/
|
||||
u32 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 dword */
|
||||
/* Register 0x61210 */
|
||||
struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */
|
||||
u16 Panel_Backlight_Inverter_Descriptor;/* 16 bits, as follows */
|
||||
/* Bit 0, Frequency, 15 bits,0 - 32767Hz */
|
||||
/* Bit 15, Polarity, 1 bit, 0: Normal, 1: Inverted */
|
||||
u16 Panel_MIPI_Display_Descriptor;
|
||||
/*16 bits, Defined as follows: */
|
||||
/* if MIPI, 0x0000 if LVDS */
|
||||
/* Bit 0, Type, 2 bits, */
|
||||
/* 0: Type-1, */
|
||||
/* 1: Type-2, */
|
||||
/* 2: Type-3, */
|
||||
/* 3: Type-4 */
|
||||
/* Bit 2, Pixel Format, 4 bits */
|
||||
/* Bit0: 16bpp (not supported in LNC), */
|
||||
/* Bit1: 18bpp loosely packed, */
|
||||
/* Bit2: 18bpp packed, */
|
||||
/* Bit3: 24bpp */
|
||||
/* Bit 6, Reserved, 2 bits, 00b */
|
||||
/* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */
|
||||
/* Bit 14, Reserved, 2 bits, 00b */
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_panel_descriptor_v2 {
|
||||
u32 Panel_Port_Control; /* 1 dword, Register 0x61180 if LVDS */
|
||||
/* 0x61190 if MIPI */
|
||||
u32 Panel_Power_On_Sequencing;/*1 dword,Register 0x61208,*/
|
||||
u32 Panel_Power_Off_Sequencing;/*1 dword,Register 0x6120C,*/
|
||||
u8 Panel_Power_Cycle_Delay_and_Reference_Divisor;/* 1 byte */
|
||||
/* Register 0x61210 */
|
||||
struct oaktrail_timing_info DTD;/*18 bytes, Standard definition */
|
||||
u16 Panel_Backlight_Inverter_Descriptor;/*16 bits, as follows*/
|
||||
/*Bit 0, Frequency, 16 bits, 0 - 32767Hz*/
|
||||
u8 Panel_Initial_Brightness;/* [7:0] 0 - 100% */
|
||||
/*Bit 7, Polarity, 1 bit,0: Normal, 1: Inverted*/
|
||||
u16 Panel_MIPI_Display_Descriptor;
|
||||
/*16 bits, Defined as follows: */
|
||||
/* if MIPI, 0x0000 if LVDS */
|
||||
/* Bit 0, Type, 2 bits, */
|
||||
/* 0: Type-1, */
|
||||
/* 1: Type-2, */
|
||||
/* 2: Type-3, */
|
||||
/* 3: Type-4 */
|
||||
/* Bit 2, Pixel Format, 4 bits */
|
||||
/* Bit0: 16bpp (not supported in LNC), */
|
||||
/* Bit1: 18bpp loosely packed, */
|
||||
/* Bit2: 18bpp packed, */
|
||||
/* Bit3: 24bpp */
|
||||
/* Bit 6, Reserved, 2 bits, 00b */
|
||||
/* Bit 8, Minimum Supported Frame Rate, 6 bits, 0 - 63Hz */
|
||||
/* Bit 14, Reserved, 2 bits, 00b */
|
||||
} __packed;
|
||||
|
||||
union oaktrail_panel_rx {
|
||||
struct {
|
||||
u16 NumberOfLanes:2; /*Num of Lanes, 2 bits,0 = 1 lane,*/
|
||||
/* 1 = 2 lanes, 2 = 3 lanes, 3 = 4 lanes. */
|
||||
u16 MaxLaneFreq:3; /* 0: 100MHz, 1: 200MHz, 2: 300MHz, */
|
||||
/*3: 400MHz, 4: 500MHz, 5: 600MHz, 6: 700MHz, 7: 800MHz.*/
|
||||
u16 SupportedVideoTransferMode:2; /*0: Non-burst only */
|
||||
/* 1: Burst and non-burst */
|
||||
/* 2/3: Reserved */
|
||||
u16 HSClkBehavior:1; /*0: Continuous, 1: Non-continuous*/
|
||||
u16 DuoDisplaySupport:1; /*1 bit,0: No, 1: Yes*/
|
||||
u16 ECC_ChecksumCapabilities:1;/*1 bit,0: No, 1: Yes*/
|
||||
u16 BidirectionalCommunication:1;/*1 bit,0: No, 1: Yes */
|
||||
u16 Rsvd:5;/*5 bits,00000b */
|
||||
} panelrx;
|
||||
u16 panel_receiver;
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_gct_v1 {
|
||||
union { /*8 bits,Defined as follows: */
|
||||
struct {
|
||||
u8 PanelType:4; /*4 bits, Bit field for panels*/
|
||||
/* 0 - 3: 0 = LVDS, 1 = MIPI*/
|
||||
/*2 bits,Specifies which of the*/
|
||||
u8 BootPanelIndex:2;
|
||||
/* 4 panels to use by default*/
|
||||
u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/
|
||||
/* the 4 MIPI DSI receivers to use*/
|
||||
} PD;
|
||||
u8 PanelDescriptor;
|
||||
};
|
||||
struct oaktrail_panel_descriptor_v1 panel[4];/*panel descrs,38 bytes each*/
|
||||
union oaktrail_panel_rx panelrx[4]; /* panel receivers*/
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_gct_v2 {
|
||||
union { /*8 bits,Defined as follows: */
|
||||
struct {
|
||||
u8 PanelType:4; /*4 bits, Bit field for panels*/
|
||||
/* 0 - 3: 0 = LVDS, 1 = MIPI*/
|
||||
/*2 bits,Specifies which of the*/
|
||||
u8 BootPanelIndex:2;
|
||||
/* 4 panels to use by default*/
|
||||
u8 BootMIPI_DSI_RxIndex:2;/*Specifies which of*/
|
||||
/* the 4 MIPI DSI receivers to use*/
|
||||
} PD;
|
||||
u8 PanelDescriptor;
|
||||
};
|
||||
struct oaktrail_panel_descriptor_v2 panel[4];/*panel descrs,38 bytes each*/
|
||||
union oaktrail_panel_rx panelrx[4]; /* panel receivers*/
|
||||
} __packed;
|
||||
|
||||
struct oaktrail_gct_data {
|
||||
u8 bpi; /* boot panel index, number of panel used during boot */
|
||||
u8 pt; /* panel type, 4 bit field, 0=lvds, 1=mipi */
|
||||
struct oaktrail_timing_info DTD; /* timing info for the selected panel */
|
||||
u32 Panel_Port_Control;
|
||||
u32 PP_On_Sequencing;/*1 dword,Register 0x61208,*/
|
||||
u32 PP_Off_Sequencing;/*1 dword,Register 0x6120C,*/
|
||||
u32 PP_Cycle_Delay;
|
||||
u16 Panel_Backlight_Inverter_Descriptor;
|
||||
u16 Panel_MIPI_Display_Descriptor;
|
||||
} __packed;
|
||||
|
||||
#define MODE_SETTING_IN_CRTC 0x1
|
||||
#define MODE_SETTING_IN_ENCODER 0x2
|
||||
#define MODE_SETTING_ON_GOING 0x3
|
||||
#define MODE_SETTING_IN_DSR 0x4
|
||||
#define MODE_SETTING_ENCODER_DONE 0x8
|
||||
|
||||
#define GCT_R10_HEADER_SIZE 16
|
||||
#define GCT_R10_DISPLAY_DESC_SIZE 28
|
||||
|
||||
/*
|
||||
* Moorestown HDMI interfaces
|
||||
*/
|
||||
|
||||
struct oaktrail_hdmi_dev {
|
||||
struct pci_dev *dev;
|
||||
void __iomem *regs;
|
||||
unsigned int mmio, mmio_len;
|
||||
int dpms_mode;
|
||||
struct hdmi_i2c_dev *i2c_dev;
|
||||
|
||||
/* register state */
|
||||
u32 saveDPLL_CTRL;
|
||||
u32 saveDPLL_DIV_CTRL;
|
||||
u32 saveDPLL_ADJUST;
|
||||
u32 saveDPLL_UPDATE;
|
||||
u32 saveDPLL_CLK_ENABLE;
|
||||
u32 savePCH_HTOTAL_B;
|
||||
u32 savePCH_HBLANK_B;
|
||||
u32 savePCH_HSYNC_B;
|
||||
u32 savePCH_VTOTAL_B;
|
||||
u32 savePCH_VBLANK_B;
|
||||
u32 savePCH_VSYNC_B;
|
||||
u32 savePCH_PIPEBCONF;
|
||||
u32 savePCH_PIPEBSRC;
|
||||
};
|
||||
|
||||
extern void oaktrail_hdmi_setup(struct drm_device *dev);
|
||||
extern void oaktrail_hdmi_teardown(struct drm_device *dev);
|
||||
extern int oaktrail_hdmi_i2c_init(struct pci_dev *dev);
|
||||
extern void oaktrail_hdmi_i2c_exit(struct pci_dev *dev);
|
||||
extern void oaktrail_hdmi_save(struct drm_device *dev);
|
||||
extern void oaktrail_hdmi_restore(struct drm_device *dev);
|
||||
extern void oaktrail_hdmi_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev);
|
|
@ -0,0 +1,604 @@
|
|||
/*
|
||||
* Copyright © 2009 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/i2c.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "framebuffer.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "psb_intel_display.h"
|
||||
#include "power.h"
|
||||
|
||||
struct psb_intel_range_t {
|
||||
int min, max;
|
||||
};
|
||||
|
||||
struct oaktrail_limit_t {
|
||||
struct psb_intel_range_t dot, m, p1;
|
||||
};
|
||||
|
||||
struct oaktrail_clock_t {
|
||||
/* derived values */
|
||||
int dot;
|
||||
int m;
|
||||
int p1;
|
||||
};
|
||||
|
||||
#define MRST_LIMIT_LVDS_100L 0
|
||||
#define MRST_LIMIT_LVDS_83 1
|
||||
#define MRST_LIMIT_LVDS_100 2
|
||||
|
||||
#define MRST_DOT_MIN 19750
|
||||
#define MRST_DOT_MAX 120000
|
||||
#define MRST_M_MIN_100L 20
|
||||
#define MRST_M_MIN_100 10
|
||||
#define MRST_M_MIN_83 12
|
||||
#define MRST_M_MAX_100L 34
|
||||
#define MRST_M_MAX_100 17
|
||||
#define MRST_M_MAX_83 20
|
||||
#define MRST_P1_MIN 2
|
||||
#define MRST_P1_MAX_0 7
|
||||
#define MRST_P1_MAX_1 8
|
||||
|
||||
static const struct oaktrail_limit_t oaktrail_limits[] = {
|
||||
{ /* MRST_LIMIT_LVDS_100L */
|
||||
.dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
|
||||
.m = {.min = MRST_M_MIN_100L, .max = MRST_M_MAX_100L},
|
||||
.p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1},
|
||||
},
|
||||
{ /* MRST_LIMIT_LVDS_83L */
|
||||
.dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
|
||||
.m = {.min = MRST_M_MIN_83, .max = MRST_M_MAX_83},
|
||||
.p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_0},
|
||||
},
|
||||
{ /* MRST_LIMIT_LVDS_100 */
|
||||
.dot = {.min = MRST_DOT_MIN, .max = MRST_DOT_MAX},
|
||||
.m = {.min = MRST_M_MIN_100, .max = MRST_M_MAX_100},
|
||||
.p1 = {.min = MRST_P1_MIN, .max = MRST_P1_MAX_1},
|
||||
},
|
||||
};
|
||||
|
||||
#define MRST_M_MIN 10
|
||||
static const u32 oaktrail_m_converts[] = {
|
||||
0x2B, 0x15, 0x2A, 0x35, 0x1A, 0x0D, 0x26, 0x33, 0x19, 0x2C,
|
||||
0x36, 0x3B, 0x1D, 0x2E, 0x37, 0x1B, 0x2D, 0x16, 0x0B, 0x25,
|
||||
0x12, 0x09, 0x24, 0x32, 0x39, 0x1c,
|
||||
};
|
||||
|
||||
static const struct oaktrail_limit_t *oaktrail_limit(struct drm_crtc *crtc)
|
||||
{
|
||||
const struct oaktrail_limit_t *limit = NULL;
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)
|
||||
|| psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_MIPI)) {
|
||||
switch (dev_priv->core_freq) {
|
||||
case 100:
|
||||
limit = &oaktrail_limits[MRST_LIMIT_LVDS_100L];
|
||||
break;
|
||||
case 166:
|
||||
limit = &oaktrail_limits[MRST_LIMIT_LVDS_83];
|
||||
break;
|
||||
case 200:
|
||||
limit = &oaktrail_limits[MRST_LIMIT_LVDS_100];
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
limit = NULL;
|
||||
dev_err(dev->dev, "oaktrail_limit Wrong display type.\n");
|
||||
}
|
||||
|
||||
return limit;
|
||||
}
|
||||
|
||||
/** Derive the pixel clock for the given refclk and divisors for 8xx chips. */
|
||||
static void oaktrail_clock(int refclk, struct oaktrail_clock_t *clock)
|
||||
{
|
||||
clock->dot = (refclk * clock->m) / (14 * clock->p1);
|
||||
}
|
||||
|
||||
void mrstPrintPll(char *prefix, struct oaktrail_clock_t *clock)
|
||||
{
|
||||
pr_debug("%s: dotclock = %d, m = %d, p1 = %d.\n",
|
||||
prefix, clock->dot, clock->m, clock->p1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of divisors for the desired target clock with the given refclk,
|
||||
* or FALSE. Divisor values are the actual divisors for
|
||||
*/
|
||||
static bool
|
||||
mrstFindBestPLL(struct drm_crtc *crtc, int target, int refclk,
|
||||
struct oaktrail_clock_t *best_clock)
|
||||
{
|
||||
struct oaktrail_clock_t clock;
|
||||
const struct oaktrail_limit_t *limit = oaktrail_limit(crtc);
|
||||
int err = target;
|
||||
|
||||
memset(best_clock, 0, sizeof(*best_clock));
|
||||
|
||||
for (clock.m = limit->m.min; clock.m <= limit->m.max; clock.m++) {
|
||||
for (clock.p1 = limit->p1.min; clock.p1 <= limit->p1.max;
|
||||
clock.p1++) {
|
||||
int this_err;
|
||||
|
||||
oaktrail_clock(refclk, &clock);
|
||||
|
||||
this_err = abs(clock.dot - target);
|
||||
if (this_err < err) {
|
||||
*best_clock = clock;
|
||||
err = this_err;
|
||||
}
|
||||
}
|
||||
}
|
||||
dev_dbg(crtc->dev->dev, "mrstFindBestPLL err = %d.\n", err);
|
||||
return err != target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the power management mode of the pipe and plane.
|
||||
*
|
||||
* This code should probably grow support for turning the cursor off and back
|
||||
* on appropriately at the same time as we're turning the pipe off/on.
|
||||
*/
|
||||
static void oaktrail_crtc_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
int pipe = psb_intel_crtc->pipe;
|
||||
int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B;
|
||||
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
|
||||
int dspbase_reg = (pipe == 0) ? MRST_DSPABASE : DSPBBASE;
|
||||
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
|
||||
u32 temp;
|
||||
bool enabled;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
/* XXX: When our outputs are all unaware of DPMS modes other than off
|
||||
* and on, we should map those modes to DRM_MODE_DPMS_OFF in the CRTC.
|
||||
*/
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
/* Enable the DPLL */
|
||||
temp = REG_READ(dpll_reg);
|
||||
if ((temp & DPLL_VCO_ENABLE) == 0) {
|
||||
REG_WRITE(dpll_reg, temp);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
REG_WRITE(dpll_reg, temp | DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
}
|
||||
/* Enable the pipe */
|
||||
temp = REG_READ(pipeconf_reg);
|
||||
if ((temp & PIPEACONF_ENABLE) == 0)
|
||||
REG_WRITE(pipeconf_reg, temp | PIPEACONF_ENABLE);
|
||||
/* Enable the plane */
|
||||
temp = REG_READ(dspcntr_reg);
|
||||
if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
|
||||
REG_WRITE(dspcntr_reg,
|
||||
temp | DISPLAY_PLANE_ENABLE);
|
||||
/* Flush the plane changes */
|
||||
REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
|
||||
}
|
||||
|
||||
psb_intel_crtc_load_lut(crtc);
|
||||
|
||||
/* Give the overlay scaler a chance to enable
|
||||
if it's on this pipe */
|
||||
/* psb_intel_crtc_dpms_video(crtc, true); TODO */
|
||||
break;
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
/* Give the overlay scaler a chance to disable
|
||||
* if it's on this pipe */
|
||||
/* psb_intel_crtc_dpms_video(crtc, FALSE); TODO */
|
||||
|
||||
/* Disable the VGA plane that we never use */
|
||||
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
|
||||
/* Disable display plane */
|
||||
temp = REG_READ(dspcntr_reg);
|
||||
if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
|
||||
REG_WRITE(dspcntr_reg,
|
||||
temp & ~DISPLAY_PLANE_ENABLE);
|
||||
/* Flush the plane changes */
|
||||
REG_WRITE(dspbase_reg, REG_READ(dspbase_reg));
|
||||
REG_READ(dspbase_reg);
|
||||
}
|
||||
|
||||
/* Next, disable display pipes */
|
||||
temp = REG_READ(pipeconf_reg);
|
||||
if ((temp & PIPEACONF_ENABLE) != 0) {
|
||||
REG_WRITE(pipeconf_reg, temp & ~PIPEACONF_ENABLE);
|
||||
REG_READ(pipeconf_reg);
|
||||
}
|
||||
/* Wait for for the pipe disable to take effect. */
|
||||
psb_intel_wait_for_vblank(dev);
|
||||
|
||||
temp = REG_READ(dpll_reg);
|
||||
if ((temp & DPLL_VCO_ENABLE) != 0) {
|
||||
REG_WRITE(dpll_reg, temp & ~DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
}
|
||||
|
||||
/* Wait for the clocks to turn off. */
|
||||
udelay(150);
|
||||
break;
|
||||
}
|
||||
|
||||
enabled = crtc->enabled && mode != DRM_MODE_DPMS_OFF;
|
||||
|
||||
/*Set FIFO Watermarks*/
|
||||
REG_WRITE(DSPARB, 0x3FFF);
|
||||
REG_WRITE(DSPFW1, 0x3F88080A);
|
||||
REG_WRITE(DSPFW2, 0x0b060808);
|
||||
REG_WRITE(DSPFW3, 0x0);
|
||||
REG_WRITE(DSPFW4, 0x08030404);
|
||||
REG_WRITE(DSPFW5, 0x04040404);
|
||||
REG_WRITE(DSPFW6, 0x78);
|
||||
REG_WRITE(0x70400, REG_READ(0x70400) | 0x4000);
|
||||
/* Must write Bit 14 of the Chicken Bit Register */
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the pipe currently connected to the panel fitter,
|
||||
* or -1 if the panel fitter is not present or not in use
|
||||
*/
|
||||
static int oaktrail_panel_fitter_pipe(struct drm_device *dev)
|
||||
{
|
||||
u32 pfit_control;
|
||||
|
||||
pfit_control = REG_READ(PFIT_CONTROL);
|
||||
|
||||
/* See if the panel fitter is in use */
|
||||
if ((pfit_control & PFIT_ENABLE) == 0)
|
||||
return -1;
|
||||
return (pfit_control >> 29) & 3;
|
||||
}
|
||||
|
||||
static int oaktrail_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 psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int pipe = psb_intel_crtc->pipe;
|
||||
int fp_reg = (pipe == 0) ? MRST_FPA0 : FPB0;
|
||||
int dpll_reg = (pipe == 0) ? MRST_DPLL_A : DPLL_B;
|
||||
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
|
||||
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
|
||||
int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
|
||||
int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
|
||||
int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
|
||||
int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
|
||||
int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
|
||||
int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
|
||||
int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
|
||||
int refclk = 0;
|
||||
struct oaktrail_clock_t clock;
|
||||
u32 dpll = 0, fp = 0, dspcntr, pipeconf;
|
||||
bool ok, is_sdvo = false;
|
||||
bool is_crt = false, is_lvds = false, is_tv = false;
|
||||
bool is_mipi = false;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
struct psb_intel_encoder *psb_intel_encoder = NULL;
|
||||
uint64_t scalingType = DRM_MODE_SCALE_FULLSCREEN;
|
||||
struct drm_connector *connector;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return 0;
|
||||
|
||||
memcpy(&psb_intel_crtc->saved_mode,
|
||||
mode,
|
||||
sizeof(struct drm_display_mode));
|
||||
memcpy(&psb_intel_crtc->saved_adjusted_mode,
|
||||
adjusted_mode,
|
||||
sizeof(struct drm_display_mode));
|
||||
|
||||
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
||||
if (!connector->encoder || connector->encoder->crtc != crtc)
|
||||
continue;
|
||||
|
||||
psb_intel_encoder = psb_intel_attached_encoder(connector);
|
||||
|
||||
switch (psb_intel_encoder->type) {
|
||||
case INTEL_OUTPUT_LVDS:
|
||||
is_lvds = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_SDVO:
|
||||
is_sdvo = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_TVOUT:
|
||||
is_tv = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_ANALOG:
|
||||
is_crt = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_MIPI:
|
||||
is_mipi = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable the VGA plane that we never use */
|
||||
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
|
||||
|
||||
/* Disable the panel fitter if it was on our pipe */
|
||||
if (oaktrail_panel_fitter_pipe(dev) == pipe)
|
||||
REG_WRITE(PFIT_CONTROL, 0);
|
||||
|
||||
REG_WRITE(pipesrc_reg,
|
||||
((mode->crtc_hdisplay - 1) << 16) |
|
||||
(mode->crtc_vdisplay - 1));
|
||||
|
||||
if (psb_intel_encoder)
|
||||
drm_connector_property_get_value(connector,
|
||||
dev->mode_config.scaling_mode_property, &scalingType);
|
||||
|
||||
if (scalingType == DRM_MODE_SCALE_NO_SCALE) {
|
||||
/* Moorestown doesn't have register support for centering so
|
||||
* we need to mess with the h/vblank and h/vsync start and
|
||||
* ends to get centering */
|
||||
int offsetX = 0, offsetY = 0;
|
||||
|
||||
offsetX = (adjusted_mode->crtc_hdisplay -
|
||||
mode->crtc_hdisplay) / 2;
|
||||
offsetY = (adjusted_mode->crtc_vdisplay -
|
||||
mode->crtc_vdisplay) / 2;
|
||||
|
||||
REG_WRITE(htot_reg, (mode->crtc_hdisplay - 1) |
|
||||
((adjusted_mode->crtc_htotal - 1) << 16));
|
||||
REG_WRITE(vtot_reg, (mode->crtc_vdisplay - 1) |
|
||||
((adjusted_mode->crtc_vtotal - 1) << 16));
|
||||
REG_WRITE(hblank_reg,
|
||||
(adjusted_mode->crtc_hblank_start - offsetX - 1) |
|
||||
((adjusted_mode->crtc_hblank_end - offsetX - 1) << 16));
|
||||
REG_WRITE(hsync_reg,
|
||||
(adjusted_mode->crtc_hsync_start - offsetX - 1) |
|
||||
((adjusted_mode->crtc_hsync_end - offsetX - 1) << 16));
|
||||
REG_WRITE(vblank_reg,
|
||||
(adjusted_mode->crtc_vblank_start - offsetY - 1) |
|
||||
((adjusted_mode->crtc_vblank_end - offsetY - 1) << 16));
|
||||
REG_WRITE(vsync_reg,
|
||||
(adjusted_mode->crtc_vsync_start - offsetY - 1) |
|
||||
((adjusted_mode->crtc_vsync_end - offsetY - 1) << 16));
|
||||
} else {
|
||||
REG_WRITE(htot_reg, (adjusted_mode->crtc_hdisplay - 1) |
|
||||
((adjusted_mode->crtc_htotal - 1) << 16));
|
||||
REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) |
|
||||
((adjusted_mode->crtc_vtotal - 1) << 16));
|
||||
REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) |
|
||||
((adjusted_mode->crtc_hblank_end - 1) << 16));
|
||||
REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) |
|
||||
((adjusted_mode->crtc_hsync_end - 1) << 16));
|
||||
REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) |
|
||||
((adjusted_mode->crtc_vblank_end - 1) << 16));
|
||||
REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) |
|
||||
((adjusted_mode->crtc_vsync_end - 1) << 16));
|
||||
}
|
||||
|
||||
/* Flush the plane changes */
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs =
|
||||
crtc->helper_private;
|
||||
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
|
||||
}
|
||||
|
||||
/* setup pipeconf */
|
||||
pipeconf = REG_READ(pipeconf_reg);
|
||||
|
||||
/* Set up the display plane register */
|
||||
dspcntr = REG_READ(dspcntr_reg);
|
||||
dspcntr |= DISPPLANE_GAMMA_ENABLE;
|
||||
|
||||
if (pipe == 0)
|
||||
dspcntr |= DISPPLANE_SEL_PIPE_A;
|
||||
else
|
||||
dspcntr |= DISPPLANE_SEL_PIPE_B;
|
||||
|
||||
dev_priv->dspcntr = dspcntr |= DISPLAY_PLANE_ENABLE;
|
||||
dev_priv->pipeconf = pipeconf |= PIPEACONF_ENABLE;
|
||||
|
||||
if (is_mipi)
|
||||
goto oaktrail_crtc_mode_set_exit;
|
||||
|
||||
refclk = dev_priv->core_freq * 1000;
|
||||
|
||||
dpll = 0; /*BIT16 = 0 for 100MHz reference */
|
||||
|
||||
ok = mrstFindBestPLL(crtc, adjusted_mode->clock, refclk, &clock);
|
||||
|
||||
if (!ok) {
|
||||
dev_dbg(dev->dev, "mrstFindBestPLL fail in oaktrail_crtc_mode_set.\n");
|
||||
} else {
|
||||
dev_dbg(dev->dev, "oaktrail_crtc_mode_set pixel clock = %d,"
|
||||
"m = %x, p1 = %x.\n", clock.dot, clock.m,
|
||||
clock.p1);
|
||||
}
|
||||
|
||||
fp = oaktrail_m_converts[(clock.m - MRST_M_MIN)] << 8;
|
||||
|
||||
dpll |= DPLL_VGA_MODE_DIS;
|
||||
|
||||
|
||||
dpll |= DPLL_VCO_ENABLE;
|
||||
|
||||
if (is_lvds)
|
||||
dpll |= DPLLA_MODE_LVDS;
|
||||
else
|
||||
dpll |= DPLLB_MODE_DAC_SERIAL;
|
||||
|
||||
if (is_sdvo) {
|
||||
int sdvo_pixel_multiply =
|
||||
adjusted_mode->clock / mode->clock;
|
||||
|
||||
dpll |= DPLL_DVO_HIGH_SPEED;
|
||||
dpll |=
|
||||
(sdvo_pixel_multiply -
|
||||
1) << SDVO_MULTIPLIER_SHIFT_HIRES;
|
||||
}
|
||||
|
||||
|
||||
/* compute bitmask from p1 value */
|
||||
dpll |= (1 << (clock.p1 - 2)) << 17;
|
||||
|
||||
dpll |= DPLL_VCO_ENABLE;
|
||||
|
||||
mrstPrintPll("chosen", &clock);
|
||||
|
||||
if (dpll & DPLL_VCO_ENABLE) {
|
||||
REG_WRITE(fp_reg, fp);
|
||||
REG_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
|
||||
REG_READ(dpll_reg);
|
||||
/* Check the DPLLA lock bit PIPEACONF[29] */
|
||||
udelay(150);
|
||||
}
|
||||
|
||||
REG_WRITE(fp_reg, fp);
|
||||
REG_WRITE(dpll_reg, dpll);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
|
||||
/* write it again -- the BIOS does, after all */
|
||||
REG_WRITE(dpll_reg, dpll);
|
||||
REG_READ(dpll_reg);
|
||||
/* Wait for the clocks to stabilize. */
|
||||
udelay(150);
|
||||
|
||||
REG_WRITE(pipeconf_reg, pipeconf);
|
||||
REG_READ(pipeconf_reg);
|
||||
psb_intel_wait_for_vblank(dev);
|
||||
|
||||
REG_WRITE(dspcntr_reg, dspcntr);
|
||||
psb_intel_wait_for_vblank(dev);
|
||||
|
||||
oaktrail_crtc_mode_set_exit:
|
||||
gma_power_end(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool oaktrail_crtc_mode_fixup(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int oaktrail_pipe_set_base(struct drm_crtc *crtc,
|
||||
int x, int y, struct drm_framebuffer *old_fb)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct psb_intel_crtc *psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
struct psb_framebuffer *psbfb = to_psb_fb(crtc->fb);
|
||||
int pipe = psb_intel_crtc->pipe;
|
||||
unsigned long start, offset;
|
||||
|
||||
int dspbase = (pipe == 0 ? DSPALINOFF : DSPBBASE);
|
||||
int dspsurf = (pipe == 0 ? DSPASURF : DSPBSURF);
|
||||
int dspstride = (pipe == 0) ? DSPASTRIDE : DSPBSTRIDE;
|
||||
int dspcntr_reg = (pipe == 0) ? DSPACNTR : DSPBCNTR;
|
||||
u32 dspcntr;
|
||||
int ret = 0;
|
||||
|
||||
/* no fb bound */
|
||||
if (!crtc->fb) {
|
||||
dev_dbg(dev->dev, "No FB bound\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return 0;
|
||||
|
||||
start = psbfb->gtt->offset;
|
||||
offset = y * crtc->fb->pitches[0] + x * (crtc->fb->bits_per_pixel / 8);
|
||||
|
||||
REG_WRITE(dspstride, crtc->fb->pitches[0]);
|
||||
|
||||
dspcntr = REG_READ(dspcntr_reg);
|
||||
dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
|
||||
|
||||
switch (crtc->fb->bits_per_pixel) {
|
||||
case 8:
|
||||
dspcntr |= DISPPLANE_8BPP;
|
||||
break;
|
||||
case 16:
|
||||
if (crtc->fb->depth == 15)
|
||||
dspcntr |= DISPPLANE_15_16BPP;
|
||||
else
|
||||
dspcntr |= DISPPLANE_16BPP;
|
||||
break;
|
||||
case 24:
|
||||
case 32:
|
||||
dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "Unknown color depth\n");
|
||||
ret = -EINVAL;
|
||||
goto pipe_set_base_exit;
|
||||
}
|
||||
REG_WRITE(dspcntr_reg, dspcntr);
|
||||
|
||||
REG_WRITE(dspbase, offset);
|
||||
REG_READ(dspbase);
|
||||
REG_WRITE(dspsurf, start);
|
||||
REG_READ(dspsurf);
|
||||
|
||||
pipe_set_base_exit:
|
||||
gma_power_end(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void oaktrail_crtc_prepare(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
static void oaktrail_crtc_commit(struct drm_crtc *crtc)
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
|
||||
}
|
||||
|
||||
const struct drm_crtc_helper_funcs oaktrail_helper_funcs = {
|
||||
.dpms = oaktrail_crtc_dpms,
|
||||
.mode_fixup = oaktrail_crtc_mode_fixup,
|
||||
.mode_set = oaktrail_crtc_mode_set,
|
||||
.mode_set_base = oaktrail_pipe_set_base,
|
||||
.prepare = oaktrail_crtc_prepare,
|
||||
.commit = oaktrail_crtc_commit,
|
||||
};
|
||||
|
|
@ -0,0 +1,512 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/backlight.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include <asm/mrst.h>
|
||||
#include <asm/intel_scu_ipc.h>
|
||||
#include "mid_bios.h"
|
||||
#include "intel_bios.h"
|
||||
|
||||
static int oaktrail_output_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
if (dev_priv->iLVDS_enable)
|
||||
oaktrail_lvds_init(dev, &dev_priv->mode_dev);
|
||||
else
|
||||
dev_err(dev->dev, "DSI is not supported\n");
|
||||
if (dev_priv->hdmi_priv)
|
||||
oaktrail_hdmi_init(dev, &dev_priv->mode_dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Provide the low level interfaces for the Moorestown backlight
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
|
||||
#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
|
||||
#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */
|
||||
#define BLC_PWM_FREQ_CALC_CONSTANT 32
|
||||
#define MHz 1000000
|
||||
#define BLC_ADJUSTMENT_MAX 100
|
||||
|
||||
static struct backlight_device *oaktrail_backlight_device;
|
||||
static int oaktrail_brightness;
|
||||
|
||||
static int oaktrail_set_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(oaktrail_backlight_device);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int level = bd->props.brightness;
|
||||
u32 blc_pwm_ctl;
|
||||
u32 max_pwm_blc;
|
||||
|
||||
/* Percentage 1-100% being valid */
|
||||
if (level < 1)
|
||||
level = 1;
|
||||
|
||||
if (gma_power_begin(dev, 0)) {
|
||||
/* Calculate and set the brightness value */
|
||||
max_pwm_blc = REG_READ(BLC_PWM_CTL) >> 16;
|
||||
blc_pwm_ctl = level * max_pwm_blc / 100;
|
||||
|
||||
/* Adjust the backlight level with the percent in
|
||||
* dev_priv->blc_adj1;
|
||||
*/
|
||||
blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj1;
|
||||
blc_pwm_ctl = blc_pwm_ctl / 100;
|
||||
|
||||
/* Adjust the backlight level with the percent in
|
||||
* dev_priv->blc_adj2;
|
||||
*/
|
||||
blc_pwm_ctl = blc_pwm_ctl * dev_priv->blc_adj2;
|
||||
blc_pwm_ctl = blc_pwm_ctl / 100;
|
||||
|
||||
/* force PWM bit on */
|
||||
REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2)));
|
||||
REG_WRITE(BLC_PWM_CTL, (max_pwm_blc << 16) | blc_pwm_ctl);
|
||||
gma_power_end(dev);
|
||||
}
|
||||
oaktrail_brightness = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oaktrail_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
/* return locally cached var instead of HW read (due to DPST etc.) */
|
||||
/* FIXME: ideally return actual value in case firmware fiddled with
|
||||
it */
|
||||
return oaktrail_brightness;
|
||||
}
|
||||
|
||||
static int device_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long core_clock;
|
||||
u16 bl_max_freq;
|
||||
uint32_t value;
|
||||
uint32_t blc_pwm_precision_factor;
|
||||
|
||||
dev_priv->blc_adj1 = BLC_ADJUSTMENT_MAX;
|
||||
dev_priv->blc_adj2 = BLC_ADJUSTMENT_MAX;
|
||||
bl_max_freq = 256;
|
||||
/* this needs to be set elsewhere */
|
||||
blc_pwm_precision_factor = BLC_PWM_PRECISION_FACTOR;
|
||||
|
||||
core_clock = dev_priv->core_freq;
|
||||
|
||||
value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT;
|
||||
value *= blc_pwm_precision_factor;
|
||||
value /= bl_max_freq;
|
||||
value /= blc_pwm_precision_factor;
|
||||
|
||||
if (value > (unsigned long long)MRST_BLC_MAX_PWM_REG_FREQ)
|
||||
return -ERANGE;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
REG_WRITE(BLC_PWM_CTL2, (0x80000000 | REG_READ(BLC_PWM_CTL2)));
|
||||
REG_WRITE(BLC_PWM_CTL, value | (value << 16));
|
||||
gma_power_end(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops oaktrail_ops = {
|
||||
.get_brightness = oaktrail_get_brightness,
|
||||
.update_status = oaktrail_set_brightness,
|
||||
};
|
||||
|
||||
int oaktrail_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
struct backlight_properties props;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 100;
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
|
||||
oaktrail_backlight_device = backlight_device_register("oaktrail-bl",
|
||||
NULL, (void *)dev, &oaktrail_ops, &props);
|
||||
|
||||
if (IS_ERR(oaktrail_backlight_device))
|
||||
return PTR_ERR(oaktrail_backlight_device);
|
||||
|
||||
ret = device_backlight_init(dev);
|
||||
if (ret < 0) {
|
||||
backlight_device_unregister(oaktrail_backlight_device);
|
||||
return ret;
|
||||
}
|
||||
oaktrail_backlight_device->props.brightness = 100;
|
||||
oaktrail_backlight_device->props.max_brightness = 100;
|
||||
backlight_update_status(oaktrail_backlight_device);
|
||||
dev_priv->backlight_device = oaktrail_backlight_device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provide the Moorestown specific chip logic and low level methods
|
||||
* for power management
|
||||
*/
|
||||
|
||||
static void oaktrail_init_pm(struct drm_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* oaktrail_save_display_registers - save registers lost on suspend
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Save the state we need in order to be able to restore the interface
|
||||
* upon resume from suspend
|
||||
*/
|
||||
static int oaktrail_save_display_registers(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
u32 pp_stat;
|
||||
|
||||
/* Display arbitration control + watermarks */
|
||||
dev_priv->saveDSPARB = PSB_RVDC32(DSPARB);
|
||||
dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1);
|
||||
dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2);
|
||||
dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3);
|
||||
dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4);
|
||||
dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5);
|
||||
dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6);
|
||||
dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
|
||||
|
||||
/* Pipe & plane A info */
|
||||
dev_priv->savePIPEACONF = PSB_RVDC32(PIPEACONF);
|
||||
dev_priv->savePIPEASRC = PSB_RVDC32(PIPEASRC);
|
||||
dev_priv->saveFPA0 = PSB_RVDC32(MRST_FPA0);
|
||||
dev_priv->saveFPA1 = PSB_RVDC32(MRST_FPA1);
|
||||
dev_priv->saveDPLL_A = PSB_RVDC32(MRST_DPLL_A);
|
||||
dev_priv->saveHTOTAL_A = PSB_RVDC32(HTOTAL_A);
|
||||
dev_priv->saveHBLANK_A = PSB_RVDC32(HBLANK_A);
|
||||
dev_priv->saveHSYNC_A = PSB_RVDC32(HSYNC_A);
|
||||
dev_priv->saveVTOTAL_A = PSB_RVDC32(VTOTAL_A);
|
||||
dev_priv->saveVBLANK_A = PSB_RVDC32(VBLANK_A);
|
||||
dev_priv->saveVSYNC_A = PSB_RVDC32(VSYNC_A);
|
||||
dev_priv->saveBCLRPAT_A = PSB_RVDC32(BCLRPAT_A);
|
||||
dev_priv->saveDSPACNTR = PSB_RVDC32(DSPACNTR);
|
||||
dev_priv->saveDSPASTRIDE = PSB_RVDC32(DSPASTRIDE);
|
||||
dev_priv->saveDSPAADDR = PSB_RVDC32(DSPABASE);
|
||||
dev_priv->saveDSPASURF = PSB_RVDC32(DSPASURF);
|
||||
dev_priv->saveDSPALINOFF = PSB_RVDC32(DSPALINOFF);
|
||||
dev_priv->saveDSPATILEOFF = PSB_RVDC32(DSPATILEOFF);
|
||||
|
||||
/* Save cursor regs */
|
||||
dev_priv->saveDSPACURSOR_CTRL = PSB_RVDC32(CURACNTR);
|
||||
dev_priv->saveDSPACURSOR_BASE = PSB_RVDC32(CURABASE);
|
||||
dev_priv->saveDSPACURSOR_POS = PSB_RVDC32(CURAPOS);
|
||||
|
||||
/* Save palette (gamma) */
|
||||
for (i = 0; i < 256; i++)
|
||||
dev_priv->save_palette_a[i] = PSB_RVDC32(PALETTE_A + (i << 2));
|
||||
|
||||
if (dev_priv->hdmi_priv)
|
||||
oaktrail_hdmi_save(dev);
|
||||
|
||||
/* Save performance state */
|
||||
dev_priv->savePERF_MODE = PSB_RVDC32(MRST_PERF_MODE);
|
||||
|
||||
/* LVDS state */
|
||||
dev_priv->savePP_CONTROL = PSB_RVDC32(PP_CONTROL);
|
||||
dev_priv->savePFIT_PGM_RATIOS = PSB_RVDC32(PFIT_PGM_RATIOS);
|
||||
dev_priv->savePFIT_AUTO_RATIOS = PSB_RVDC32(PFIT_AUTO_RATIOS);
|
||||
dev_priv->saveBLC_PWM_CTL = PSB_RVDC32(BLC_PWM_CTL);
|
||||
dev_priv->saveBLC_PWM_CTL2 = PSB_RVDC32(BLC_PWM_CTL2);
|
||||
dev_priv->saveLVDS = PSB_RVDC32(LVDS);
|
||||
dev_priv->savePFIT_CONTROL = PSB_RVDC32(PFIT_CONTROL);
|
||||
dev_priv->savePP_ON_DELAYS = PSB_RVDC32(LVDSPP_ON);
|
||||
dev_priv->savePP_OFF_DELAYS = PSB_RVDC32(LVDSPP_OFF);
|
||||
dev_priv->savePP_DIVISOR = PSB_RVDC32(PP_CYCLE);
|
||||
|
||||
/* HW overlay */
|
||||
dev_priv->saveOV_OVADD = PSB_RVDC32(OV_OVADD);
|
||||
dev_priv->saveOV_OGAMC0 = PSB_RVDC32(OV_OGAMC0);
|
||||
dev_priv->saveOV_OGAMC1 = PSB_RVDC32(OV_OGAMC1);
|
||||
dev_priv->saveOV_OGAMC2 = PSB_RVDC32(OV_OGAMC2);
|
||||
dev_priv->saveOV_OGAMC3 = PSB_RVDC32(OV_OGAMC3);
|
||||
dev_priv->saveOV_OGAMC4 = PSB_RVDC32(OV_OGAMC4);
|
||||
dev_priv->saveOV_OGAMC5 = PSB_RVDC32(OV_OGAMC5);
|
||||
|
||||
/* DPST registers */
|
||||
dev_priv->saveHISTOGRAM_INT_CONTROL_REG =
|
||||
PSB_RVDC32(HISTOGRAM_INT_CONTROL);
|
||||
dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG =
|
||||
PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
|
||||
dev_priv->savePWM_CONTROL_LOGIC = PSB_RVDC32(PWM_CONTROL_LOGIC);
|
||||
|
||||
if (dev_priv->iLVDS_enable) {
|
||||
/* Shut down the panel */
|
||||
PSB_WVDC32(0, PP_CONTROL);
|
||||
|
||||
do {
|
||||
pp_stat = PSB_RVDC32(PP_STATUS);
|
||||
} while (pp_stat & 0x80000000);
|
||||
|
||||
/* Turn off the plane */
|
||||
PSB_WVDC32(0x58000000, DSPACNTR);
|
||||
/* Trigger the plane disable */
|
||||
PSB_WVDC32(0, DSPASURF);
|
||||
|
||||
/* Wait ~4 ticks */
|
||||
msleep(4);
|
||||
|
||||
/* Turn off pipe */
|
||||
PSB_WVDC32(0x0, PIPEACONF);
|
||||
/* Wait ~8 ticks */
|
||||
msleep(8);
|
||||
|
||||
/* Turn off PLLs */
|
||||
PSB_WVDC32(0, MRST_DPLL_A);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* oaktrail_restore_display_registers - restore lost register state
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Restore register state that was lost during suspend and resume.
|
||||
*/
|
||||
static int oaktrail_restore_display_registers(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pp_stat;
|
||||
int i;
|
||||
|
||||
/* Display arbitration + watermarks */
|
||||
PSB_WVDC32(dev_priv->saveDSPARB, DSPARB);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6);
|
||||
PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT);
|
||||
|
||||
/* Make sure VGA plane is off. it initializes to on after reset!*/
|
||||
PSB_WVDC32(0x80000000, VGACNTRL);
|
||||
|
||||
/* set the plls */
|
||||
PSB_WVDC32(dev_priv->saveFPA0, MRST_FPA0);
|
||||
PSB_WVDC32(dev_priv->saveFPA1, MRST_FPA1);
|
||||
|
||||
/* Actually enable it */
|
||||
PSB_WVDC32(dev_priv->saveDPLL_A, MRST_DPLL_A);
|
||||
DRM_UDELAY(150);
|
||||
|
||||
/* Restore mode */
|
||||
PSB_WVDC32(dev_priv->saveHTOTAL_A, HTOTAL_A);
|
||||
PSB_WVDC32(dev_priv->saveHBLANK_A, HBLANK_A);
|
||||
PSB_WVDC32(dev_priv->saveHSYNC_A, HSYNC_A);
|
||||
PSB_WVDC32(dev_priv->saveVTOTAL_A, VTOTAL_A);
|
||||
PSB_WVDC32(dev_priv->saveVBLANK_A, VBLANK_A);
|
||||
PSB_WVDC32(dev_priv->saveVSYNC_A, VSYNC_A);
|
||||
PSB_WVDC32(dev_priv->savePIPEASRC, PIPEASRC);
|
||||
PSB_WVDC32(dev_priv->saveBCLRPAT_A, BCLRPAT_A);
|
||||
|
||||
/* Restore performance mode*/
|
||||
PSB_WVDC32(dev_priv->savePERF_MODE, MRST_PERF_MODE);
|
||||
|
||||
/* Enable the pipe*/
|
||||
if (dev_priv->iLVDS_enable)
|
||||
PSB_WVDC32(dev_priv->savePIPEACONF, PIPEACONF);
|
||||
|
||||
/* Set up the plane*/
|
||||
PSB_WVDC32(dev_priv->saveDSPALINOFF, DSPALINOFF);
|
||||
PSB_WVDC32(dev_priv->saveDSPASTRIDE, DSPASTRIDE);
|
||||
PSB_WVDC32(dev_priv->saveDSPATILEOFF, DSPATILEOFF);
|
||||
|
||||
/* Enable the plane */
|
||||
PSB_WVDC32(dev_priv->saveDSPACNTR, DSPACNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPASURF, DSPASURF);
|
||||
|
||||
/* Enable Cursor A */
|
||||
PSB_WVDC32(dev_priv->saveDSPACURSOR_CTRL, CURACNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPACURSOR_POS, CURAPOS);
|
||||
PSB_WVDC32(dev_priv->saveDSPACURSOR_BASE, CURABASE);
|
||||
|
||||
/* Restore palette (gamma) */
|
||||
for (i = 0; i < 256; i++)
|
||||
PSB_WVDC32(dev_priv->save_palette_a[i], PALETTE_A + (i << 2));
|
||||
|
||||
if (dev_priv->hdmi_priv)
|
||||
oaktrail_hdmi_restore(dev);
|
||||
|
||||
if (dev_priv->iLVDS_enable) {
|
||||
PSB_WVDC32(dev_priv->saveBLC_PWM_CTL2, BLC_PWM_CTL2);
|
||||
PSB_WVDC32(dev_priv->saveLVDS, LVDS); /*port 61180h*/
|
||||
PSB_WVDC32(dev_priv->savePFIT_CONTROL, PFIT_CONTROL);
|
||||
PSB_WVDC32(dev_priv->savePFIT_PGM_RATIOS, PFIT_PGM_RATIOS);
|
||||
PSB_WVDC32(dev_priv->savePFIT_AUTO_RATIOS, PFIT_AUTO_RATIOS);
|
||||
PSB_WVDC32(dev_priv->saveBLC_PWM_CTL, BLC_PWM_CTL);
|
||||
PSB_WVDC32(dev_priv->savePP_ON_DELAYS, LVDSPP_ON);
|
||||
PSB_WVDC32(dev_priv->savePP_OFF_DELAYS, LVDSPP_OFF);
|
||||
PSB_WVDC32(dev_priv->savePP_DIVISOR, PP_CYCLE);
|
||||
PSB_WVDC32(dev_priv->savePP_CONTROL, PP_CONTROL);
|
||||
}
|
||||
|
||||
/* Wait for cycle delay */
|
||||
do {
|
||||
pp_stat = PSB_RVDC32(PP_STATUS);
|
||||
} while (pp_stat & 0x08000000);
|
||||
|
||||
/* Wait for panel power up */
|
||||
do {
|
||||
pp_stat = PSB_RVDC32(PP_STATUS);
|
||||
} while (pp_stat & 0x10000000);
|
||||
|
||||
/* Restore HW overlay */
|
||||
PSB_WVDC32(dev_priv->saveOV_OVADD, OV_OVADD);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC0, OV_OGAMC0);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC1, OV_OGAMC1);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC2, OV_OGAMC2);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC3, OV_OGAMC3);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC4, OV_OGAMC4);
|
||||
PSB_WVDC32(dev_priv->saveOV_OGAMC5, OV_OGAMC5);
|
||||
|
||||
/* DPST registers */
|
||||
PSB_WVDC32(dev_priv->saveHISTOGRAM_INT_CONTROL_REG,
|
||||
HISTOGRAM_INT_CONTROL);
|
||||
PSB_WVDC32(dev_priv->saveHISTOGRAM_LOGIC_CONTROL_REG,
|
||||
HISTOGRAM_LOGIC_CONTROL);
|
||||
PSB_WVDC32(dev_priv->savePWM_CONTROL_LOGIC, PWM_CONTROL_LOGIC);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* oaktrail_power_down - power down the display island
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Power down the display interface of our device
|
||||
*/
|
||||
static int oaktrail_power_down(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pwr_mask ;
|
||||
u32 pwr_sts;
|
||||
|
||||
pwr_mask = PSB_PWRGT_DISPLAY_MASK;
|
||||
outl(pwr_mask, dev_priv->ospm_base + PSB_PM_SSC);
|
||||
|
||||
while (true) {
|
||||
pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS);
|
||||
if ((pwr_sts & pwr_mask) == pwr_mask)
|
||||
break;
|
||||
else
|
||||
udelay(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* oaktrail_power_up
|
||||
*
|
||||
* Restore power to the specified island(s) (powergating)
|
||||
*/
|
||||
static int oaktrail_power_up(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pwr_mask = PSB_PWRGT_DISPLAY_MASK;
|
||||
u32 pwr_sts, pwr_cnt;
|
||||
|
||||
pwr_cnt = inl(dev_priv->ospm_base + PSB_PM_SSC);
|
||||
pwr_cnt &= ~pwr_mask;
|
||||
outl(pwr_cnt, (dev_priv->ospm_base + PSB_PM_SSC));
|
||||
|
||||
while (true) {
|
||||
pwr_sts = inl(dev_priv->ospm_base + PSB_PM_SSS);
|
||||
if ((pwr_sts & pwr_mask) == 0)
|
||||
break;
|
||||
else
|
||||
udelay(10);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int oaktrail_chip_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_vbt *vbt = &dev_priv->vbt_data;
|
||||
int ret;
|
||||
|
||||
ret = mid_chip_setup(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (vbt->size == 0) {
|
||||
/* Now pull the BIOS data */
|
||||
gma_intel_opregion_init(dev);
|
||||
psb_intel_init_bios(dev);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void oaktrail_teardown(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_vbt *vbt = &dev_priv->vbt_data;
|
||||
|
||||
oaktrail_hdmi_teardown(dev);
|
||||
if (vbt->size == 0)
|
||||
psb_intel_destroy_bios(dev);
|
||||
}
|
||||
|
||||
const struct psb_ops oaktrail_chip_ops = {
|
||||
.name = "Oaktrail",
|
||||
.accel_2d = 1,
|
||||
.pipes = 2,
|
||||
.crtcs = 2,
|
||||
.sgx_offset = MRST_SGX_OFFSET,
|
||||
|
||||
.chip_setup = oaktrail_chip_setup,
|
||||
.chip_teardown = oaktrail_teardown,
|
||||
.crtc_helper = &oaktrail_helper_funcs,
|
||||
.crtc_funcs = &psb_intel_crtc_funcs,
|
||||
|
||||
.output_init = oaktrail_output_init,
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
.backlight_init = oaktrail_backlight_init,
|
||||
#endif
|
||||
|
||||
.init_pm = oaktrail_init_pm,
|
||||
.save_regs = oaktrail_save_display_registers,
|
||||
.restore_regs = oaktrail_restore_display_registers,
|
||||
.power_down = oaktrail_power_down,
|
||||
.power_up = oaktrail_power_up,
|
||||
|
||||
.i2c_bus = 1,
|
||||
};
|
|
@ -0,0 +1,859 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Li Peng <peng.li@intel.com>
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "psb_drv.h"
|
||||
|
||||
#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg))
|
||||
#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg))
|
||||
|
||||
#define HDMI_HCR 0x1000
|
||||
#define HCR_ENABLE_HDCP (1 << 5)
|
||||
#define HCR_ENABLE_AUDIO (1 << 2)
|
||||
#define HCR_ENABLE_PIXEL (1 << 1)
|
||||
#define HCR_ENABLE_TMDS (1 << 0)
|
||||
|
||||
#define HDMI_HICR 0x1004
|
||||
#define HDMI_HSR 0x1008
|
||||
#define HDMI_HISR 0x100C
|
||||
#define HDMI_DETECT_HDP (1 << 0)
|
||||
|
||||
#define HDMI_VIDEO_REG 0x3000
|
||||
#define HDMI_UNIT_EN (1 << 7)
|
||||
#define HDMI_MODE_OUTPUT (1 << 0)
|
||||
#define HDMI_HBLANK_A 0x3100
|
||||
|
||||
#define HDMI_AUDIO_CTRL 0x4000
|
||||
#define HDMI_ENABLE_AUDIO (1 << 0)
|
||||
|
||||
#define PCH_HTOTAL_B 0x3100
|
||||
#define PCH_HBLANK_B 0x3104
|
||||
#define PCH_HSYNC_B 0x3108
|
||||
#define PCH_VTOTAL_B 0x310C
|
||||
#define PCH_VBLANK_B 0x3110
|
||||
#define PCH_VSYNC_B 0x3114
|
||||
#define PCH_PIPEBSRC 0x311C
|
||||
|
||||
#define PCH_PIPEB_DSL 0x3800
|
||||
#define PCH_PIPEB_SLC 0x3804
|
||||
#define PCH_PIPEBCONF 0x3808
|
||||
#define PCH_PIPEBSTAT 0x3824
|
||||
|
||||
#define CDVO_DFT 0x5000
|
||||
#define CDVO_SLEWRATE 0x5004
|
||||
#define CDVO_STRENGTH 0x5008
|
||||
#define CDVO_RCOMP 0x500C
|
||||
|
||||
#define DPLL_CTRL 0x6000
|
||||
#define DPLL_PDIV_SHIFT 16
|
||||
#define DPLL_PDIV_MASK (0xf << 16)
|
||||
#define DPLL_PWRDN (1 << 4)
|
||||
#define DPLL_RESET (1 << 3)
|
||||
#define DPLL_FASTEN (1 << 2)
|
||||
#define DPLL_ENSTAT (1 << 1)
|
||||
#define DPLL_DITHEN (1 << 0)
|
||||
|
||||
#define DPLL_DIV_CTRL 0x6004
|
||||
#define DPLL_CLKF_MASK 0xffffffc0
|
||||
#define DPLL_CLKR_MASK (0x3f)
|
||||
|
||||
#define DPLL_CLK_ENABLE 0x6008
|
||||
#define DPLL_EN_DISP (1 << 31)
|
||||
#define DPLL_SEL_HDMI (1 << 8)
|
||||
#define DPLL_EN_HDMI (1 << 1)
|
||||
#define DPLL_EN_VGA (1 << 0)
|
||||
|
||||
#define DPLL_ADJUST 0x600C
|
||||
#define DPLL_STATUS 0x6010
|
||||
#define DPLL_UPDATE 0x6014
|
||||
#define DPLL_DFT 0x6020
|
||||
|
||||
struct intel_range {
|
||||
int min, max;
|
||||
};
|
||||
|
||||
struct oaktrail_hdmi_limit {
|
||||
struct intel_range vco, np, nr, nf;
|
||||
};
|
||||
|
||||
struct oaktrail_hdmi_clock {
|
||||
int np;
|
||||
int nr;
|
||||
int nf;
|
||||
int dot;
|
||||
};
|
||||
|
||||
#define VCO_MIN 320000
|
||||
#define VCO_MAX 1650000
|
||||
#define NP_MIN 1
|
||||
#define NP_MAX 15
|
||||
#define NR_MIN 1
|
||||
#define NR_MAX 64
|
||||
#define NF_MIN 2
|
||||
#define NF_MAX 4095
|
||||
|
||||
static const struct oaktrail_hdmi_limit oaktrail_hdmi_limit = {
|
||||
.vco = { .min = VCO_MIN, .max = VCO_MAX },
|
||||
.np = { .min = NP_MIN, .max = NP_MAX },
|
||||
.nr = { .min = NR_MIN, .max = NR_MAX },
|
||||
.nf = { .min = NF_MIN, .max = NF_MAX },
|
||||
};
|
||||
|
||||
static void wait_for_vblank(struct drm_device *dev)
|
||||
{
|
||||
/* FIXME: Can we do this as a sleep ? */
|
||||
/* Wait for 20ms, i.e. one cycle at 50hz. */
|
||||
mdelay(20);
|
||||
}
|
||||
|
||||
static void scu_busy_loop(void *scu_base)
|
||||
{
|
||||
u32 status = 0;
|
||||
u32 loop_count = 0;
|
||||
|
||||
status = readl(scu_base + 0x04);
|
||||
while (status & 1) {
|
||||
udelay(1); /* scu processing time is in few u secods */
|
||||
status = readl(scu_base + 0x04);
|
||||
loop_count++;
|
||||
/* break if scu doesn't reset busy bit after huge retry */
|
||||
if (loop_count > 1000) {
|
||||
DRM_DEBUG_KMS("SCU IPC timed out");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_reset(struct drm_device *dev)
|
||||
{
|
||||
void *base;
|
||||
/* FIXME: at least make these defines */
|
||||
unsigned int scu_ipc_mmio = 0xff11c000;
|
||||
int scu_len = 1024;
|
||||
|
||||
base = ioremap((resource_size_t)scu_ipc_mmio, scu_len);
|
||||
if (base == NULL) {
|
||||
DRM_ERROR("failed to map SCU mmio\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* scu ipc: assert hdmi controller reset */
|
||||
writel(0xff11d118, base + 0x0c);
|
||||
writel(0x7fffffdf, base + 0x80);
|
||||
writel(0x42005, base + 0x0);
|
||||
scu_busy_loop(base);
|
||||
|
||||
/* scu ipc: de-assert hdmi controller reset */
|
||||
writel(0xff11d118, base + 0x0c);
|
||||
writel(0x7fffffff, base + 0x80);
|
||||
writel(0x42005, base + 0x0);
|
||||
scu_busy_loop(base);
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_audio_enable(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
|
||||
HDMI_WRITE(HDMI_HCR, 0x67);
|
||||
HDMI_READ(HDMI_HCR);
|
||||
|
||||
HDMI_WRITE(0x51a8, 0x10);
|
||||
HDMI_READ(0x51a8);
|
||||
|
||||
HDMI_WRITE(HDMI_AUDIO_CTRL, 0x1);
|
||||
HDMI_READ(HDMI_AUDIO_CTRL);
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_audio_disable(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
|
||||
HDMI_WRITE(0x51a8, 0x0);
|
||||
HDMI_READ(0x51a8);
|
||||
|
||||
HDMI_WRITE(HDMI_AUDIO_CTRL, 0x0);
|
||||
HDMI_READ(HDMI_AUDIO_CTRL);
|
||||
|
||||
HDMI_WRITE(HDMI_HCR, 0x47);
|
||||
HDMI_READ(HDMI_HCR);
|
||||
}
|
||||
|
||||
void oaktrail_crtc_hdmi_dpms(struct drm_crtc *crtc, int mode)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
u32 temp;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
/* Disable VGACNTRL */
|
||||
REG_WRITE(VGACNTRL, 0x80000000);
|
||||
|
||||
/* Disable plane */
|
||||
temp = REG_READ(DSPBCNTR);
|
||||
if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
|
||||
REG_WRITE(DSPBCNTR, temp & ~DISPLAY_PLANE_ENABLE);
|
||||
REG_READ(DSPBCNTR);
|
||||
/* Flush the plane changes */
|
||||
REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
|
||||
REG_READ(DSPBSURF);
|
||||
}
|
||||
|
||||
/* Disable pipe B */
|
||||
temp = REG_READ(PIPEBCONF);
|
||||
if ((temp & PIPEACONF_ENABLE) != 0) {
|
||||
REG_WRITE(PIPEBCONF, temp & ~PIPEACONF_ENABLE);
|
||||
REG_READ(PIPEBCONF);
|
||||
}
|
||||
|
||||
/* Disable LNW Pipes, etc */
|
||||
temp = REG_READ(PCH_PIPEBCONF);
|
||||
if ((temp & PIPEACONF_ENABLE) != 0) {
|
||||
REG_WRITE(PCH_PIPEBCONF, temp & ~PIPEACONF_ENABLE);
|
||||
REG_READ(PCH_PIPEBCONF);
|
||||
}
|
||||
/* wait for pipe off */
|
||||
udelay(150);
|
||||
/* Disable dpll */
|
||||
temp = REG_READ(DPLL_CTRL);
|
||||
if ((temp & DPLL_PWRDN) == 0) {
|
||||
REG_WRITE(DPLL_CTRL, temp | (DPLL_PWRDN | DPLL_RESET));
|
||||
REG_WRITE(DPLL_STATUS, 0x1);
|
||||
}
|
||||
/* wait for dpll off */
|
||||
udelay(150);
|
||||
break;
|
||||
case DRM_MODE_DPMS_ON:
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
/* Enable dpll */
|
||||
temp = REG_READ(DPLL_CTRL);
|
||||
if ((temp & DPLL_PWRDN) != 0) {
|
||||
REG_WRITE(DPLL_CTRL, temp & ~(DPLL_PWRDN | DPLL_RESET));
|
||||
temp = REG_READ(DPLL_CLK_ENABLE);
|
||||
REG_WRITE(DPLL_CLK_ENABLE, temp | DPLL_EN_DISP | DPLL_SEL_HDMI | DPLL_EN_HDMI);
|
||||
REG_READ(DPLL_CLK_ENABLE);
|
||||
}
|
||||
/* wait for dpll warm up */
|
||||
udelay(150);
|
||||
|
||||
/* Enable pipe B */
|
||||
temp = REG_READ(PIPEBCONF);
|
||||
if ((temp & PIPEACONF_ENABLE) == 0) {
|
||||
REG_WRITE(PIPEBCONF, temp | PIPEACONF_ENABLE);
|
||||
REG_READ(PIPEBCONF);
|
||||
}
|
||||
|
||||
/* Enable LNW Pipe B */
|
||||
temp = REG_READ(PCH_PIPEBCONF);
|
||||
if ((temp & PIPEACONF_ENABLE) == 0) {
|
||||
REG_WRITE(PCH_PIPEBCONF, temp | PIPEACONF_ENABLE);
|
||||
REG_READ(PCH_PIPEBCONF);
|
||||
}
|
||||
wait_for_vblank(dev);
|
||||
|
||||
/* Enable plane */
|
||||
temp = REG_READ(DSPBCNTR);
|
||||
if ((temp & DISPLAY_PLANE_ENABLE) == 0) {
|
||||
REG_WRITE(DSPBCNTR, temp | DISPLAY_PLANE_ENABLE);
|
||||
/* Flush the plane changes */
|
||||
REG_WRITE(DSPBSURF, REG_READ(DSPBSURF));
|
||||
REG_READ(DSPBSURF);
|
||||
}
|
||||
psb_intel_crtc_load_lut(crtc);
|
||||
}
|
||||
/* DSPARB */
|
||||
REG_WRITE(DSPARB, 0x00003fbf);
|
||||
/* FW1 */
|
||||
REG_WRITE(0x70034, 0x3f880a0a);
|
||||
/* FW2 */
|
||||
REG_WRITE(0x70038, 0x0b060808);
|
||||
/* FW4 */
|
||||
REG_WRITE(0x70050, 0x08030404);
|
||||
/* FW5 */
|
||||
REG_WRITE(0x70054, 0x04040404);
|
||||
/* LNC Chicken Bits */
|
||||
REG_WRITE(0x70400, 0x4000);
|
||||
}
|
||||
|
||||
|
||||
static void oaktrail_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
static int dpms_mode = -1;
|
||||
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
u32 temp;
|
||||
|
||||
if (dpms_mode == mode)
|
||||
return;
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
temp = 0x0;
|
||||
else
|
||||
temp = 0x99;
|
||||
|
||||
dpms_mode = mode;
|
||||
HDMI_WRITE(HDMI_VIDEO_REG, temp);
|
||||
}
|
||||
|
||||
static unsigned int htotal_calculate(struct drm_display_mode *mode)
|
||||
{
|
||||
u32 htotal, new_crtc_htotal;
|
||||
|
||||
htotal = (mode->crtc_hdisplay - 1) | ((mode->crtc_htotal - 1) << 16);
|
||||
|
||||
/*
|
||||
* 1024 x 768 new_crtc_htotal = 0x1024;
|
||||
* 1280 x 1024 new_crtc_htotal = 0x0c34;
|
||||
*/
|
||||
new_crtc_htotal = (mode->crtc_htotal - 1) * 200 * 1000 / mode->clock;
|
||||
|
||||
return (mode->crtc_hdisplay - 1) | (new_crtc_htotal << 16);
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_find_dpll(struct drm_crtc *crtc, int target,
|
||||
int refclk, struct oaktrail_hdmi_clock *best_clock)
|
||||
{
|
||||
int np_min, np_max, nr_min, nr_max;
|
||||
int np, nr, nf;
|
||||
|
||||
np_min = DIV_ROUND_UP(oaktrail_hdmi_limit.vco.min, target * 10);
|
||||
np_max = oaktrail_hdmi_limit.vco.max / (target * 10);
|
||||
if (np_min < oaktrail_hdmi_limit.np.min)
|
||||
np_min = oaktrail_hdmi_limit.np.min;
|
||||
if (np_max > oaktrail_hdmi_limit.np.max)
|
||||
np_max = oaktrail_hdmi_limit.np.max;
|
||||
|
||||
nr_min = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_max));
|
||||
nr_max = DIV_ROUND_UP((refclk * 1000), (target * 10 * np_min));
|
||||
if (nr_min < oaktrail_hdmi_limit.nr.min)
|
||||
nr_min = oaktrail_hdmi_limit.nr.min;
|
||||
if (nr_max > oaktrail_hdmi_limit.nr.max)
|
||||
nr_max = oaktrail_hdmi_limit.nr.max;
|
||||
|
||||
np = DIV_ROUND_UP((refclk * 1000), (target * 10 * nr_max));
|
||||
nr = DIV_ROUND_UP((refclk * 1000), (target * 10 * np));
|
||||
nf = DIV_ROUND_CLOSEST((target * 10 * np * nr), refclk);
|
||||
DRM_DEBUG_KMS("np, nr, nf %d %d %d\n", np, nr, nf);
|
||||
|
||||
/*
|
||||
* 1024 x 768 np = 1; nr = 0x26; nf = 0x0fd8000;
|
||||
* 1280 x 1024 np = 1; nr = 0x17; nf = 0x1034000;
|
||||
*/
|
||||
best_clock->np = np;
|
||||
best_clock->nr = nr - 1;
|
||||
best_clock->nf = (nf << 14);
|
||||
}
|
||||
|
||||
int oaktrail_crtc_hdmi_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 drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
int pipe = 1;
|
||||
int htot_reg = (pipe == 0) ? HTOTAL_A : HTOTAL_B;
|
||||
int hblank_reg = (pipe == 0) ? HBLANK_A : HBLANK_B;
|
||||
int hsync_reg = (pipe == 0) ? HSYNC_A : HSYNC_B;
|
||||
int vtot_reg = (pipe == 0) ? VTOTAL_A : VTOTAL_B;
|
||||
int vblank_reg = (pipe == 0) ? VBLANK_A : VBLANK_B;
|
||||
int vsync_reg = (pipe == 0) ? VSYNC_A : VSYNC_B;
|
||||
int dspsize_reg = (pipe == 0) ? DSPASIZE : DSPBSIZE;
|
||||
int dsppos_reg = (pipe == 0) ? DSPAPOS : DSPBPOS;
|
||||
int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
|
||||
int pipeconf_reg = (pipe == 0) ? PIPEACONF : PIPEBCONF;
|
||||
int refclk;
|
||||
struct oaktrail_hdmi_clock clock;
|
||||
u32 dspcntr, pipeconf, dpll, temp;
|
||||
int dspcntr_reg = DSPBCNTR;
|
||||
|
||||
/* Disable the VGA plane that we never use */
|
||||
REG_WRITE(VGACNTRL, VGA_DISP_DISABLE);
|
||||
|
||||
/* XXX: Disable the panel fitter if it was on our pipe */
|
||||
|
||||
/* Disable dpll if necessary */
|
||||
dpll = REG_READ(DPLL_CTRL);
|
||||
if ((dpll & DPLL_PWRDN) == 0) {
|
||||
REG_WRITE(DPLL_CTRL, dpll | (DPLL_PWRDN | DPLL_RESET));
|
||||
REG_WRITE(DPLL_DIV_CTRL, 0x00000000);
|
||||
REG_WRITE(DPLL_STATUS, 0x1);
|
||||
}
|
||||
udelay(150);
|
||||
|
||||
/* reset controller: FIXME - can we sort out the ioremap mess ? */
|
||||
iounmap(hdmi_dev->regs);
|
||||
oaktrail_hdmi_reset(dev);
|
||||
|
||||
/* program and enable dpll */
|
||||
refclk = 25000;
|
||||
oaktrail_hdmi_find_dpll(crtc, adjusted_mode->clock, refclk, &clock);
|
||||
|
||||
/* Setting DPLL */
|
||||
dpll = REG_READ(DPLL_CTRL);
|
||||
dpll &= ~DPLL_PDIV_MASK;
|
||||
dpll &= ~(DPLL_PWRDN | DPLL_RESET);
|
||||
REG_WRITE(DPLL_CTRL, 0x00000008);
|
||||
REG_WRITE(DPLL_DIV_CTRL, ((clock.nf << 6) | clock.nr));
|
||||
REG_WRITE(DPLL_ADJUST, ((clock.nf >> 14) - 1));
|
||||
REG_WRITE(DPLL_CTRL, (dpll | (clock.np << DPLL_PDIV_SHIFT) | DPLL_ENSTAT | DPLL_DITHEN));
|
||||
REG_WRITE(DPLL_UPDATE, 0x80000000);
|
||||
REG_WRITE(DPLL_CLK_ENABLE, 0x80050102);
|
||||
udelay(150);
|
||||
|
||||
hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
|
||||
if (hdmi_dev->regs == NULL) {
|
||||
DRM_ERROR("failed to do hdmi mmio mapping\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* configure HDMI */
|
||||
HDMI_WRITE(0x1004, 0x1fd);
|
||||
HDMI_WRITE(0x2000, 0x1);
|
||||
HDMI_WRITE(0x2008, 0x0);
|
||||
HDMI_WRITE(0x3130, 0x8);
|
||||
HDMI_WRITE(0x101c, 0x1800810);
|
||||
|
||||
temp = htotal_calculate(adjusted_mode);
|
||||
REG_WRITE(htot_reg, temp);
|
||||
REG_WRITE(hblank_reg, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
|
||||
REG_WRITE(hsync_reg, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
|
||||
REG_WRITE(vtot_reg, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
|
||||
REG_WRITE(vblank_reg, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
|
||||
REG_WRITE(vsync_reg, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
|
||||
REG_WRITE(pipesrc_reg,
|
||||
((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1));
|
||||
|
||||
REG_WRITE(PCH_HTOTAL_B, (adjusted_mode->crtc_hdisplay - 1) | ((adjusted_mode->crtc_htotal - 1) << 16));
|
||||
REG_WRITE(PCH_HBLANK_B, (adjusted_mode->crtc_hblank_start - 1) | ((adjusted_mode->crtc_hblank_end - 1) << 16));
|
||||
REG_WRITE(PCH_HSYNC_B, (adjusted_mode->crtc_hsync_start - 1) | ((adjusted_mode->crtc_hsync_end - 1) << 16));
|
||||
REG_WRITE(PCH_VTOTAL_B, (adjusted_mode->crtc_vdisplay - 1) | ((adjusted_mode->crtc_vtotal - 1) << 16));
|
||||
REG_WRITE(PCH_VBLANK_B, (adjusted_mode->crtc_vblank_start - 1) | ((adjusted_mode->crtc_vblank_end - 1) << 16));
|
||||
REG_WRITE(PCH_VSYNC_B, (adjusted_mode->crtc_vsync_start - 1) | ((adjusted_mode->crtc_vsync_end - 1) << 16));
|
||||
REG_WRITE(PCH_PIPEBSRC,
|
||||
((mode->crtc_hdisplay - 1) << 16) | (mode->crtc_vdisplay - 1));
|
||||
|
||||
temp = adjusted_mode->crtc_hblank_end - adjusted_mode->crtc_hblank_start;
|
||||
HDMI_WRITE(HDMI_HBLANK_A, ((adjusted_mode->crtc_hdisplay - 1) << 16) | temp);
|
||||
|
||||
REG_WRITE(dspsize_reg,
|
||||
((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1));
|
||||
REG_WRITE(dsppos_reg, 0);
|
||||
|
||||
/* Flush the plane changes */
|
||||
{
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
crtc_funcs->mode_set_base(crtc, x, y, old_fb);
|
||||
}
|
||||
|
||||
/* Set up the display plane register */
|
||||
dspcntr = REG_READ(dspcntr_reg);
|
||||
dspcntr |= DISPPLANE_GAMMA_ENABLE;
|
||||
dspcntr |= DISPPLANE_SEL_PIPE_B;
|
||||
dspcntr |= DISPLAY_PLANE_ENABLE;
|
||||
|
||||
/* setup pipeconf */
|
||||
pipeconf = REG_READ(pipeconf_reg);
|
||||
pipeconf |= PIPEACONF_ENABLE;
|
||||
|
||||
REG_WRITE(pipeconf_reg, pipeconf);
|
||||
REG_READ(pipeconf_reg);
|
||||
|
||||
REG_WRITE(PCH_PIPEBCONF, pipeconf);
|
||||
REG_READ(PCH_PIPEBCONF);
|
||||
wait_for_vblank(dev);
|
||||
|
||||
REG_WRITE(dspcntr_reg, dspcntr);
|
||||
wait_for_vblank(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oaktrail_hdmi_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
if (mode->clock > 165000)
|
||||
return MODE_CLOCK_HIGH;
|
||||
if (mode->clock < 20000)
|
||||
return MODE_CLOCK_LOW;
|
||||
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
static bool oaktrail_hdmi_mode_fixup(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
oaktrail_hdmi_detect(struct drm_connector *connector, bool force)
|
||||
{
|
||||
enum drm_connector_status status;
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
u32 temp;
|
||||
|
||||
temp = HDMI_READ(HDMI_HSR);
|
||||
DRM_DEBUG_KMS("HDMI_HSR %x\n", temp);
|
||||
|
||||
if ((temp & HDMI_DETECT_HDP) != 0)
|
||||
status = connector_status_connected;
|
||||
else
|
||||
status = connector_status_disconnected;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static const unsigned char raw_edid[] = {
|
||||
0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x10, 0xac, 0x2f, 0xa0,
|
||||
0x53, 0x55, 0x33, 0x30, 0x16, 0x13, 0x01, 0x03, 0x0e, 0x3a, 0x24, 0x78,
|
||||
0xea, 0xe9, 0xf5, 0xac, 0x51, 0x30, 0xb4, 0x25, 0x11, 0x50, 0x54, 0xa5,
|
||||
0x4b, 0x00, 0x81, 0x80, 0xa9, 0x40, 0x71, 0x4f, 0xb3, 0x00, 0x01, 0x01,
|
||||
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x28, 0x3c, 0x80, 0xa0, 0x70, 0xb0,
|
||||
0x23, 0x40, 0x30, 0x20, 0x36, 0x00, 0x46, 0x6c, 0x21, 0x00, 0x00, 0x1a,
|
||||
0x00, 0x00, 0x00, 0xff, 0x00, 0x47, 0x4e, 0x37, 0x32, 0x31, 0x39, 0x35,
|
||||
0x52, 0x30, 0x33, 0x55, 0x53, 0x0a, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x44,
|
||||
0x45, 0x4c, 0x4c, 0x20, 0x32, 0x37, 0x30, 0x39, 0x57, 0x0a, 0x20, 0x20,
|
||||
0x00, 0x00, 0x00, 0xfd, 0x00, 0x38, 0x4c, 0x1e, 0x53, 0x11, 0x00, 0x0a,
|
||||
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x00, 0x8d
|
||||
};
|
||||
|
||||
static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct i2c_adapter *i2c_adap;
|
||||
struct edid *edid;
|
||||
struct drm_display_mode *mode, *t;
|
||||
int i = 0, ret = 0;
|
||||
|
||||
i2c_adap = i2c_get_adapter(3);
|
||||
if (i2c_adap == NULL) {
|
||||
DRM_ERROR("No ddc adapter available!\n");
|
||||
edid = (struct edid *)raw_edid;
|
||||
} else {
|
||||
edid = (struct edid *)raw_edid;
|
||||
/* FIXME ? edid = drm_get_edid(connector, i2c_adap); */
|
||||
}
|
||||
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
connector->display_info.raw_edid = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* prune modes that require frame buffer bigger than stolen mem
|
||||
*/
|
||||
list_for_each_entry_safe(mode, t, &connector->probed_modes, head) {
|
||||
if ((mode->hdisplay * mode->vdisplay * 4) >= dev_priv->vram_stolen_size) {
|
||||
i++;
|
||||
drm_mode_remove(connector, mode);
|
||||
}
|
||||
}
|
||||
return ret - i;
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
|
||||
oaktrail_hdmi_audio_enable(dev);
|
||||
return;
|
||||
}
|
||||
|
||||
static void oaktrail_hdmi_destroy(struct drm_connector *connector)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs oaktrail_hdmi_helper_funcs = {
|
||||
.dpms = oaktrail_hdmi_dpms,
|
||||
.mode_fixup = oaktrail_hdmi_mode_fixup,
|
||||
.prepare = psb_intel_encoder_prepare,
|
||||
.mode_set = oaktrail_hdmi_mode_set,
|
||||
.commit = psb_intel_encoder_commit,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs
|
||||
oaktrail_hdmi_connector_helper_funcs = {
|
||||
.get_modes = oaktrail_hdmi_get_modes,
|
||||
.mode_valid = oaktrail_hdmi_mode_valid,
|
||||
.best_encoder = psb_intel_best_encoder,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs oaktrail_hdmi_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.detect = oaktrail_hdmi_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = oaktrail_hdmi_destroy,
|
||||
};
|
||||
|
||||
static void oaktrail_hdmi_enc_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_funcs oaktrail_hdmi_enc_funcs = {
|
||||
.destroy = oaktrail_hdmi_enc_destroy,
|
||||
};
|
||||
|
||||
void oaktrail_hdmi_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
struct psb_intel_connector *psb_intel_connector;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
|
||||
psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL);
|
||||
if (!psb_intel_encoder)
|
||||
return;
|
||||
|
||||
psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL);
|
||||
if (!psb_intel_connector)
|
||||
goto failed_connector;
|
||||
|
||||
connector = &psb_intel_connector->base;
|
||||
encoder = &psb_intel_encoder->base;
|
||||
drm_connector_init(dev, connector,
|
||||
&oaktrail_hdmi_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DVID);
|
||||
|
||||
drm_encoder_init(dev, encoder,
|
||||
&oaktrail_hdmi_enc_funcs,
|
||||
DRM_MODE_ENCODER_TMDS);
|
||||
|
||||
psb_intel_connector_attach_encoder(psb_intel_connector,
|
||||
psb_intel_encoder);
|
||||
|
||||
psb_intel_encoder->type = INTEL_OUTPUT_HDMI;
|
||||
drm_encoder_helper_add(encoder, &oaktrail_hdmi_helper_funcs);
|
||||
drm_connector_helper_add(connector, &oaktrail_hdmi_connector_helper_funcs);
|
||||
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
||||
return;
|
||||
|
||||
failed_connector:
|
||||
kfree(psb_intel_encoder);
|
||||
}
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(hdmi_ids) = {
|
||||
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x080d) },
|
||||
{}
|
||||
};
|
||||
|
||||
void oaktrail_hdmi_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct pci_dev *pdev;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev;
|
||||
int ret;
|
||||
|
||||
pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x080d, NULL);
|
||||
if (!pdev)
|
||||
return;
|
||||
|
||||
hdmi_dev = kzalloc(sizeof(struct oaktrail_hdmi_dev), GFP_KERNEL);
|
||||
if (!hdmi_dev) {
|
||||
dev_err(dev->dev, "failed to allocate memory\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
ret = pci_enable_device(pdev);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to enable hdmi controller\n");
|
||||
goto free;
|
||||
}
|
||||
|
||||
hdmi_dev->mmio = pci_resource_start(pdev, 0);
|
||||
hdmi_dev->mmio_len = pci_resource_len(pdev, 0);
|
||||
hdmi_dev->regs = ioremap(hdmi_dev->mmio, hdmi_dev->mmio_len);
|
||||
if (!hdmi_dev->regs) {
|
||||
dev_err(dev->dev, "failed to map hdmi mmio\n");
|
||||
goto free;
|
||||
}
|
||||
|
||||
hdmi_dev->dev = pdev;
|
||||
pci_set_drvdata(pdev, hdmi_dev);
|
||||
|
||||
/* Initialize i2c controller */
|
||||
ret = oaktrail_hdmi_i2c_init(hdmi_dev->dev);
|
||||
if (ret)
|
||||
dev_err(dev->dev, "HDMI I2C initialization failed\n");
|
||||
|
||||
dev_priv->hdmi_priv = hdmi_dev;
|
||||
oaktrail_hdmi_audio_disable(dev);
|
||||
return;
|
||||
|
||||
free:
|
||||
kfree(hdmi_dev);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
void oaktrail_hdmi_teardown(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
struct pci_dev *pdev;
|
||||
|
||||
if (hdmi_dev) {
|
||||
pdev = hdmi_dev->dev;
|
||||
pci_set_drvdata(pdev, NULL);
|
||||
oaktrail_hdmi_i2c_exit(pdev);
|
||||
iounmap(hdmi_dev->regs);
|
||||
kfree(hdmi_dev);
|
||||
pci_dev_put(pdev);
|
||||
}
|
||||
}
|
||||
|
||||
/* save HDMI register state */
|
||||
void oaktrail_hdmi_save(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
int i;
|
||||
|
||||
/* dpll */
|
||||
hdmi_dev->saveDPLL_CTRL = PSB_RVDC32(DPLL_CTRL);
|
||||
hdmi_dev->saveDPLL_DIV_CTRL = PSB_RVDC32(DPLL_DIV_CTRL);
|
||||
hdmi_dev->saveDPLL_ADJUST = PSB_RVDC32(DPLL_ADJUST);
|
||||
hdmi_dev->saveDPLL_UPDATE = PSB_RVDC32(DPLL_UPDATE);
|
||||
hdmi_dev->saveDPLL_CLK_ENABLE = PSB_RVDC32(DPLL_CLK_ENABLE);
|
||||
|
||||
/* pipe B */
|
||||
dev_priv->savePIPEBCONF = PSB_RVDC32(PIPEBCONF);
|
||||
dev_priv->savePIPEBSRC = PSB_RVDC32(PIPEBSRC);
|
||||
dev_priv->saveHTOTAL_B = PSB_RVDC32(HTOTAL_B);
|
||||
dev_priv->saveHBLANK_B = PSB_RVDC32(HBLANK_B);
|
||||
dev_priv->saveHSYNC_B = PSB_RVDC32(HSYNC_B);
|
||||
dev_priv->saveVTOTAL_B = PSB_RVDC32(VTOTAL_B);
|
||||
dev_priv->saveVBLANK_B = PSB_RVDC32(VBLANK_B);
|
||||
dev_priv->saveVSYNC_B = PSB_RVDC32(VSYNC_B);
|
||||
|
||||
hdmi_dev->savePCH_PIPEBCONF = PSB_RVDC32(PCH_PIPEBCONF);
|
||||
hdmi_dev->savePCH_PIPEBSRC = PSB_RVDC32(PCH_PIPEBSRC);
|
||||
hdmi_dev->savePCH_HTOTAL_B = PSB_RVDC32(PCH_HTOTAL_B);
|
||||
hdmi_dev->savePCH_HBLANK_B = PSB_RVDC32(PCH_HBLANK_B);
|
||||
hdmi_dev->savePCH_HSYNC_B = PSB_RVDC32(PCH_HSYNC_B);
|
||||
hdmi_dev->savePCH_VTOTAL_B = PSB_RVDC32(PCH_VTOTAL_B);
|
||||
hdmi_dev->savePCH_VBLANK_B = PSB_RVDC32(PCH_VBLANK_B);
|
||||
hdmi_dev->savePCH_VSYNC_B = PSB_RVDC32(PCH_VSYNC_B);
|
||||
|
||||
/* plane */
|
||||
dev_priv->saveDSPBCNTR = PSB_RVDC32(DSPBCNTR);
|
||||
dev_priv->saveDSPBSTRIDE = PSB_RVDC32(DSPBSTRIDE);
|
||||
dev_priv->saveDSPBADDR = PSB_RVDC32(DSPBBASE);
|
||||
dev_priv->saveDSPBSURF = PSB_RVDC32(DSPBSURF);
|
||||
dev_priv->saveDSPBLINOFF = PSB_RVDC32(DSPBLINOFF);
|
||||
dev_priv->saveDSPBTILEOFF = PSB_RVDC32(DSPBTILEOFF);
|
||||
|
||||
/* cursor B */
|
||||
dev_priv->saveDSPBCURSOR_CTRL = PSB_RVDC32(CURBCNTR);
|
||||
dev_priv->saveDSPBCURSOR_BASE = PSB_RVDC32(CURBBASE);
|
||||
dev_priv->saveDSPBCURSOR_POS = PSB_RVDC32(CURBPOS);
|
||||
|
||||
/* save palette */
|
||||
for (i = 0; i < 256; i++)
|
||||
dev_priv->save_palette_b[i] = PSB_RVDC32(PALETTE_B + (i << 2));
|
||||
}
|
||||
|
||||
/* restore HDMI register state */
|
||||
void oaktrail_hdmi_restore(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev_priv->hdmi_priv;
|
||||
int i;
|
||||
|
||||
/* dpll */
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_CTRL, DPLL_CTRL);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_DIV_CTRL, DPLL_DIV_CTRL);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_ADJUST, DPLL_ADJUST);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_UPDATE, DPLL_UPDATE);
|
||||
PSB_WVDC32(hdmi_dev->saveDPLL_CLK_ENABLE, DPLL_CLK_ENABLE);
|
||||
DRM_UDELAY(150);
|
||||
|
||||
/* pipe */
|
||||
PSB_WVDC32(dev_priv->savePIPEBSRC, PIPEBSRC);
|
||||
PSB_WVDC32(dev_priv->saveHTOTAL_B, HTOTAL_B);
|
||||
PSB_WVDC32(dev_priv->saveHBLANK_B, HBLANK_B);
|
||||
PSB_WVDC32(dev_priv->saveHSYNC_B, HSYNC_B);
|
||||
PSB_WVDC32(dev_priv->saveVTOTAL_B, VTOTAL_B);
|
||||
PSB_WVDC32(dev_priv->saveVBLANK_B, VBLANK_B);
|
||||
PSB_WVDC32(dev_priv->saveVSYNC_B, VSYNC_B);
|
||||
|
||||
PSB_WVDC32(hdmi_dev->savePCH_PIPEBSRC, PCH_PIPEBSRC);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_HTOTAL_B, PCH_HTOTAL_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_HBLANK_B, PCH_HBLANK_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_HSYNC_B, PCH_HSYNC_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_VTOTAL_B, PCH_VTOTAL_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_VBLANK_B, PCH_VBLANK_B);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_VSYNC_B, PCH_VSYNC_B);
|
||||
|
||||
PSB_WVDC32(dev_priv->savePIPEBCONF, PIPEBCONF);
|
||||
PSB_WVDC32(hdmi_dev->savePCH_PIPEBCONF, PCH_PIPEBCONF);
|
||||
|
||||
/* plane */
|
||||
PSB_WVDC32(dev_priv->saveDSPBLINOFF, DSPBLINOFF);
|
||||
PSB_WVDC32(dev_priv->saveDSPBSTRIDE, DSPBSTRIDE);
|
||||
PSB_WVDC32(dev_priv->saveDSPBTILEOFF, DSPBTILEOFF);
|
||||
PSB_WVDC32(dev_priv->saveDSPBCNTR, DSPBCNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPBSURF, DSPBSURF);
|
||||
|
||||
/* cursor B */
|
||||
PSB_WVDC32(dev_priv->saveDSPBCURSOR_CTRL, CURBCNTR);
|
||||
PSB_WVDC32(dev_priv->saveDSPBCURSOR_POS, CURBPOS);
|
||||
PSB_WVDC32(dev_priv->saveDSPBCURSOR_BASE, CURBBASE);
|
||||
|
||||
/* restore palette */
|
||||
for (i = 0; i < 256; i++)
|
||||
PSB_WVDC32(dev_priv->save_palette_b[i], PALETTE_B + (i << 2));
|
||||
}
|
|
@ -0,0 +1,328 @@
|
|||
/*
|
||||
* Copyright © 2010 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Li Peng <peng.li@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/delay.h>
|
||||
#include "psb_drv.h"
|
||||
|
||||
#define HDMI_READ(reg) readl(hdmi_dev->regs + (reg))
|
||||
#define HDMI_WRITE(reg, val) writel(val, hdmi_dev->regs + (reg))
|
||||
|
||||
#define HDMI_HCR 0x1000
|
||||
#define HCR_DETECT_HDP (1 << 6)
|
||||
#define HCR_ENABLE_HDCP (1 << 5)
|
||||
#define HCR_ENABLE_AUDIO (1 << 2)
|
||||
#define HCR_ENABLE_PIXEL (1 << 1)
|
||||
#define HCR_ENABLE_TMDS (1 << 0)
|
||||
#define HDMI_HICR 0x1004
|
||||
#define HDMI_INTR_I2C_ERROR (1 << 4)
|
||||
#define HDMI_INTR_I2C_FULL (1 << 3)
|
||||
#define HDMI_INTR_I2C_DONE (1 << 2)
|
||||
#define HDMI_INTR_HPD (1 << 0)
|
||||
#define HDMI_HSR 0x1008
|
||||
#define HDMI_HISR 0x100C
|
||||
#define HDMI_HI2CRDB0 0x1200
|
||||
#define HDMI_HI2CHCR 0x1240
|
||||
#define HI2C_HDCP_WRITE (0 << 2)
|
||||
#define HI2C_HDCP_RI_READ (1 << 2)
|
||||
#define HI2C_HDCP_READ (2 << 2)
|
||||
#define HI2C_EDID_READ (3 << 2)
|
||||
#define HI2C_READ_CONTINUE (1 << 1)
|
||||
#define HI2C_ENABLE_TRANSACTION (1 << 0)
|
||||
|
||||
#define HDMI_ICRH 0x1100
|
||||
#define HDMI_HI2CTDR0 0x1244
|
||||
#define HDMI_HI2CTDR1 0x1248
|
||||
|
||||
#define I2C_STAT_INIT 0
|
||||
#define I2C_READ_DONE 1
|
||||
#define I2C_TRANSACTION_DONE 2
|
||||
|
||||
struct hdmi_i2c_dev {
|
||||
struct i2c_adapter *adap;
|
||||
struct mutex i2c_lock;
|
||||
struct completion complete;
|
||||
int status;
|
||||
struct i2c_msg *msg;
|
||||
int buf_offset;
|
||||
};
|
||||
|
||||
static void hdmi_i2c_irq_enable(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
u32 temp;
|
||||
|
||||
temp = HDMI_READ(HDMI_HICR);
|
||||
temp |= (HDMI_INTR_I2C_ERROR | HDMI_INTR_I2C_FULL | HDMI_INTR_I2C_DONE);
|
||||
HDMI_WRITE(HDMI_HICR, temp);
|
||||
HDMI_READ(HDMI_HICR);
|
||||
}
|
||||
|
||||
static void hdmi_i2c_irq_disable(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
HDMI_WRITE(HDMI_HICR, 0x0);
|
||||
HDMI_READ(HDMI_HICR);
|
||||
}
|
||||
|
||||
static int xfer_read(struct i2c_adapter *adap, struct i2c_msg *pmsg)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
u32 temp;
|
||||
|
||||
i2c_dev->status = I2C_STAT_INIT;
|
||||
i2c_dev->msg = pmsg;
|
||||
i2c_dev->buf_offset = 0;
|
||||
INIT_COMPLETION(i2c_dev->complete);
|
||||
|
||||
/* Enable I2C transaction */
|
||||
temp = ((pmsg->len) << 20) | HI2C_EDID_READ | HI2C_ENABLE_TRANSACTION;
|
||||
HDMI_WRITE(HDMI_HI2CHCR, temp);
|
||||
HDMI_READ(HDMI_HI2CHCR);
|
||||
|
||||
while (i2c_dev->status != I2C_TRANSACTION_DONE)
|
||||
wait_for_completion_interruptible_timeout(&i2c_dev->complete,
|
||||
10 * HZ);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xfer_write(struct i2c_adapter *adap, struct i2c_msg *pmsg)
|
||||
{
|
||||
/*
|
||||
* XXX: i2c write seems isn't useful for EDID probe, don't do anything
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int oaktrail_hdmi_i2c_access(struct i2c_adapter *adap,
|
||||
struct i2c_msg *pmsg,
|
||||
int num)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = i2c_get_adapdata(adap);
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
int i, err = 0;
|
||||
|
||||
mutex_lock(&i2c_dev->i2c_lock);
|
||||
|
||||
/* Enable i2c unit */
|
||||
HDMI_WRITE(HDMI_ICRH, 0x00008760);
|
||||
|
||||
/* Enable irq */
|
||||
hdmi_i2c_irq_enable(hdmi_dev);
|
||||
for (i = 0; i < num; i++) {
|
||||
if (pmsg->len && pmsg->buf) {
|
||||
if (pmsg->flags & I2C_M_RD)
|
||||
err = xfer_read(adap, pmsg);
|
||||
else
|
||||
err = xfer_write(adap, pmsg);
|
||||
}
|
||||
pmsg++; /* next message */
|
||||
}
|
||||
|
||||
/* Disable irq */
|
||||
hdmi_i2c_irq_disable(hdmi_dev);
|
||||
|
||||
mutex_unlock(&i2c_dev->i2c_lock);
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static u32 oaktrail_hdmi_i2c_func(struct i2c_adapter *adapter)
|
||||
{
|
||||
return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
|
||||
}
|
||||
|
||||
static const struct i2c_algorithm oaktrail_hdmi_i2c_algorithm = {
|
||||
.master_xfer = oaktrail_hdmi_i2c_access,
|
||||
.functionality = oaktrail_hdmi_i2c_func,
|
||||
};
|
||||
|
||||
static struct i2c_adapter oaktrail_hdmi_i2c_adapter = {
|
||||
.name = "oaktrail_hdmi_i2c",
|
||||
.nr = 3,
|
||||
.owner = THIS_MODULE,
|
||||
.class = I2C_CLASS_DDC,
|
||||
.algo = &oaktrail_hdmi_i2c_algorithm,
|
||||
};
|
||||
|
||||
static void hdmi_i2c_read(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
struct i2c_msg *msg = i2c_dev->msg;
|
||||
u8 *buf = msg->buf;
|
||||
u32 temp;
|
||||
int i, offset;
|
||||
|
||||
offset = i2c_dev->buf_offset;
|
||||
for (i = 0; i < 0x10; i++) {
|
||||
temp = HDMI_READ(HDMI_HI2CRDB0 + (i * 4));
|
||||
memcpy(buf + (offset + i * 4), &temp, 4);
|
||||
}
|
||||
i2c_dev->buf_offset += (0x10 * 4);
|
||||
|
||||
/* clearing read buffer full intr */
|
||||
temp = HDMI_READ(HDMI_HISR);
|
||||
HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_FULL);
|
||||
HDMI_READ(HDMI_HISR);
|
||||
|
||||
/* continue read transaction */
|
||||
temp = HDMI_READ(HDMI_HI2CHCR);
|
||||
HDMI_WRITE(HDMI_HI2CHCR, temp | HI2C_READ_CONTINUE);
|
||||
HDMI_READ(HDMI_HI2CHCR);
|
||||
|
||||
i2c_dev->status = I2C_READ_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
static void hdmi_i2c_transaction_done(struct oaktrail_hdmi_dev *hdmi_dev)
|
||||
{
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
u32 temp;
|
||||
|
||||
/* clear transaction done intr */
|
||||
temp = HDMI_READ(HDMI_HISR);
|
||||
HDMI_WRITE(HDMI_HISR, temp | HDMI_INTR_I2C_DONE);
|
||||
HDMI_READ(HDMI_HISR);
|
||||
|
||||
|
||||
temp = HDMI_READ(HDMI_HI2CHCR);
|
||||
HDMI_WRITE(HDMI_HI2CHCR, temp & ~HI2C_ENABLE_TRANSACTION);
|
||||
HDMI_READ(HDMI_HI2CHCR);
|
||||
|
||||
i2c_dev->status = I2C_TRANSACTION_DONE;
|
||||
return;
|
||||
}
|
||||
|
||||
static irqreturn_t oaktrail_hdmi_i2c_handler(int this_irq, void *dev)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev = dev;
|
||||
struct hdmi_i2c_dev *i2c_dev = hdmi_dev->i2c_dev;
|
||||
u32 stat;
|
||||
|
||||
stat = HDMI_READ(HDMI_HISR);
|
||||
|
||||
if (stat & HDMI_INTR_HPD) {
|
||||
HDMI_WRITE(HDMI_HISR, stat | HDMI_INTR_HPD);
|
||||
HDMI_READ(HDMI_HISR);
|
||||
}
|
||||
|
||||
if (stat & HDMI_INTR_I2C_FULL)
|
||||
hdmi_i2c_read(hdmi_dev);
|
||||
|
||||
if (stat & HDMI_INTR_I2C_DONE)
|
||||
hdmi_i2c_transaction_done(hdmi_dev);
|
||||
|
||||
complete(&i2c_dev->complete);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* choose alternate function 2 of GPIO pin 52, 53,
|
||||
* which is used by HDMI I2C logic
|
||||
*/
|
||||
static void oaktrail_hdmi_i2c_gpio_fix(void)
|
||||
{
|
||||
void *base;
|
||||
unsigned int gpio_base = 0xff12c000;
|
||||
int gpio_len = 0x1000;
|
||||
u32 temp;
|
||||
|
||||
base = ioremap((resource_size_t)gpio_base, gpio_len);
|
||||
if (base == NULL) {
|
||||
DRM_ERROR("gpio ioremap fail\n");
|
||||
return;
|
||||
}
|
||||
|
||||
temp = readl(base + 0x44);
|
||||
DRM_DEBUG_DRIVER("old gpio val %x\n", temp);
|
||||
writel((temp | 0x00000a00), (base + 0x44));
|
||||
temp = readl(base + 0x44);
|
||||
DRM_DEBUG_DRIVER("new gpio val %x\n", temp);
|
||||
|
||||
iounmap(base);
|
||||
}
|
||||
|
||||
int oaktrail_hdmi_i2c_init(struct pci_dev *dev)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev;
|
||||
struct hdmi_i2c_dev *i2c_dev;
|
||||
int ret;
|
||||
|
||||
hdmi_dev = pci_get_drvdata(dev);
|
||||
|
||||
i2c_dev = kzalloc(sizeof(struct hdmi_i2c_dev), GFP_KERNEL);
|
||||
if (i2c_dev == NULL) {
|
||||
DRM_ERROR("Can't allocate interface\n");
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
i2c_dev->adap = &oaktrail_hdmi_i2c_adapter;
|
||||
i2c_dev->status = I2C_STAT_INIT;
|
||||
init_completion(&i2c_dev->complete);
|
||||
mutex_init(&i2c_dev->i2c_lock);
|
||||
i2c_set_adapdata(&oaktrail_hdmi_i2c_adapter, hdmi_dev);
|
||||
hdmi_dev->i2c_dev = i2c_dev;
|
||||
|
||||
/* Enable HDMI I2C function on gpio */
|
||||
oaktrail_hdmi_i2c_gpio_fix();
|
||||
|
||||
/* request irq */
|
||||
ret = request_irq(dev->irq, oaktrail_hdmi_i2c_handler, IRQF_SHARED,
|
||||
oaktrail_hdmi_i2c_adapter.name, hdmi_dev);
|
||||
if (ret) {
|
||||
DRM_ERROR("Failed to request IRQ for I2C controller\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Adapter registration */
|
||||
ret = i2c_add_numbered_adapter(&oaktrail_hdmi_i2c_adapter);
|
||||
return ret;
|
||||
|
||||
err:
|
||||
kfree(i2c_dev);
|
||||
exit:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void oaktrail_hdmi_i2c_exit(struct pci_dev *dev)
|
||||
{
|
||||
struct oaktrail_hdmi_dev *hdmi_dev;
|
||||
struct hdmi_i2c_dev *i2c_dev;
|
||||
|
||||
hdmi_dev = pci_get_drvdata(dev);
|
||||
if (i2c_del_adapter(&oaktrail_hdmi_i2c_adapter))
|
||||
DRM_DEBUG_DRIVER("Failed to delete hdmi-i2c adapter\n");
|
||||
|
||||
i2c_dev = hdmi_dev->i2c_dev;
|
||||
kfree(i2c_dev);
|
||||
free_irq(dev->irq, hdmi_dev);
|
||||
}
|
|
@ -0,0 +1,449 @@
|
|||
/*
|
||||
* Copyright © 2006-2009 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Dave Airlie <airlied@linux.ie>
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <asm/mrst.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "power.h"
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/* The max/min PWM frequency in BPCR[31:17] - */
|
||||
/* The smallest number is 1 (not 0) that can fit in the
|
||||
* 15-bit field of the and then*/
|
||||
/* shifts to the left by one bit to get the actual 16-bit
|
||||
* value that the 15-bits correspond to.*/
|
||||
#define MRST_BLC_MAX_PWM_REG_FREQ 0xFFFF
|
||||
#define BRIGHTNESS_MAX_LEVEL 100
|
||||
|
||||
/**
|
||||
* Sets the power state for the panel.
|
||||
*/
|
||||
static void oaktrail_lvds_set_power(struct drm_device *dev,
|
||||
struct psb_intel_encoder *psb_intel_encoder,
|
||||
bool on)
|
||||
{
|
||||
u32 pp_status;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
if (on) {
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
|
||||
POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & (PP_ON | PP_READY)) == PP_READY);
|
||||
dev_priv->is_lvds_on = true;
|
||||
if (dev_priv->ops->lvds_bl_power)
|
||||
dev_priv->ops->lvds_bl_power(dev, true);
|
||||
} else {
|
||||
if (dev_priv->ops->lvds_bl_power)
|
||||
dev_priv->ops->lvds_bl_power(dev, false);
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
|
||||
~POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while (pp_status & PP_ON);
|
||||
dev_priv->is_lvds_on = false;
|
||||
pm_request_idle(&dev->pdev->dev);
|
||||
}
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
to_psb_intel_encoder(encoder);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
oaktrail_lvds_set_power(dev, psb_intel_encoder, true);
|
||||
else
|
||||
oaktrail_lvds_set_power(dev, psb_intel_encoder, false);
|
||||
|
||||
/* XXX: We never power down the LVDS pairs. */
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
struct drm_connector *connector = NULL;
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
u32 lvds_port;
|
||||
uint64_t v = DRM_MODE_SCALE_FULLSCREEN;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
/*
|
||||
* The LVDS pin pair will already have been turned on in the
|
||||
* psb_intel_crtc_mode_set since it has a large impact on the DPLL
|
||||
* settings.
|
||||
*/
|
||||
lvds_port = (REG_READ(LVDS) &
|
||||
(~LVDS_PIPEB_SELECT)) |
|
||||
LVDS_PORT_EN |
|
||||
LVDS_BORDER_EN;
|
||||
|
||||
/* If the firmware says dither on Moorestown, or the BIOS does
|
||||
on Oaktrail then enable dithering */
|
||||
if (mode_dev->panel_wants_dither || dev_priv->lvds_dither)
|
||||
lvds_port |= MRST_PANEL_8TO6_DITHER_ENABLE;
|
||||
|
||||
REG_WRITE(LVDS, lvds_port);
|
||||
|
||||
/* Find the connector we're trying to set up */
|
||||
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
||||
if (!connector->encoder || connector->encoder->crtc != crtc)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!connector) {
|
||||
DRM_ERROR("Couldn't find connector when setting mode");
|
||||
return;
|
||||
}
|
||||
|
||||
drm_connector_property_get_value(
|
||||
connector,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
&v);
|
||||
|
||||
if (v == DRM_MODE_SCALE_NO_SCALE)
|
||||
REG_WRITE(PFIT_CONTROL, 0);
|
||||
else if (v == DRM_MODE_SCALE_ASPECT) {
|
||||
if ((mode->vdisplay != adjusted_mode->crtc_vdisplay) ||
|
||||
(mode->hdisplay != adjusted_mode->crtc_hdisplay)) {
|
||||
if ((adjusted_mode->crtc_hdisplay * mode->vdisplay) ==
|
||||
(mode->hdisplay * adjusted_mode->crtc_vdisplay))
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
|
||||
else if ((adjusted_mode->crtc_hdisplay *
|
||||
mode->vdisplay) > (mode->hdisplay *
|
||||
adjusted_mode->crtc_vdisplay))
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
|
||||
PFIT_SCALING_MODE_PILLARBOX);
|
||||
else
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE |
|
||||
PFIT_SCALING_MODE_LETTERBOX);
|
||||
} else
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
|
||||
} else /*(v == DRM_MODE_SCALE_FULLSCREEN)*/
|
||||
REG_WRITE(PFIT_CONTROL, PFIT_ENABLE);
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
to_psb_intel_encoder(encoder);
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
|
||||
mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_DUTY_CYCLE_MASK);
|
||||
oaktrail_lvds_set_power(dev, psb_intel_encoder, false);
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static u32 oaktrail_lvds_get_max_backlight(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 ret;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
ret = ((REG_READ(BLC_PWM_CTL) &
|
||||
BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
|
||||
|
||||
gma_power_end(dev);
|
||||
} else
|
||||
ret = ((dev_priv->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT) * 2;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void oaktrail_lvds_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
to_psb_intel_encoder(encoder);
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
|
||||
if (mode_dev->backlight_duty_cycle == 0)
|
||||
mode_dev->backlight_duty_cycle =
|
||||
oaktrail_lvds_get_max_backlight(dev);
|
||||
oaktrail_lvds_set_power(dev, psb_intel_encoder, true);
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs oaktrail_lvds_helper_funcs = {
|
||||
.dpms = oaktrail_lvds_dpms,
|
||||
.mode_fixup = psb_intel_lvds_mode_fixup,
|
||||
.prepare = oaktrail_lvds_prepare,
|
||||
.mode_set = oaktrail_lvds_mode_set,
|
||||
.commit = oaktrail_lvds_commit,
|
||||
};
|
||||
|
||||
static struct drm_display_mode lvds_configuration_modes[] = {
|
||||
/* hard coded fixed mode for TPO LTPS LPJ040K001A */
|
||||
{ DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 33264, 800, 836,
|
||||
846, 1056, 0, 480, 489, 491, 525, 0, 0) },
|
||||
/* hard coded fixed mode for LVDS 800x480 */
|
||||
{ DRM_MODE("800x480", DRM_MODE_TYPE_DRIVER, 30994, 800, 801,
|
||||
802, 1024, 0, 480, 481, 482, 525, 0, 0) },
|
||||
/* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
|
||||
{ DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1072,
|
||||
1104, 1184, 0, 600, 603, 604, 608, 0, 0) },
|
||||
/* hard coded fixed mode for Samsung 480wsvga LVDS 1024x600@75 */
|
||||
{ DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 53990, 1024, 1104,
|
||||
1136, 1184, 0, 600, 603, 604, 608, 0, 0) },
|
||||
/* hard coded fixed mode for Sharp wsvga LVDS 1024x600 */
|
||||
{ DRM_MODE("1024x600", DRM_MODE_TYPE_DRIVER, 48885, 1024, 1124,
|
||||
1204, 1312, 0, 600, 607, 610, 621, 0, 0) },
|
||||
/* hard coded fixed mode for LVDS 1024x768 */
|
||||
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
|
||||
1184, 1344, 0, 768, 771, 777, 806, 0, 0) },
|
||||
/* hard coded fixed mode for LVDS 1366x768 */
|
||||
{ DRM_MODE("1366x768", DRM_MODE_TYPE_DRIVER, 77500, 1366, 1430,
|
||||
1558, 1664, 0, 768, 769, 770, 776, 0, 0) },
|
||||
};
|
||||
|
||||
/* Returns the panel fixed mode from configuration. */
|
||||
|
||||
static void oaktrail_lvds_get_configuration_mode(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct drm_display_mode *mode = NULL;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct oaktrail_timing_info *ti = &dev_priv->gct_data.DTD;
|
||||
|
||||
mode_dev->panel_fixed_mode = NULL;
|
||||
|
||||
/* Use the firmware provided data on Moorestown */
|
||||
if (dev_priv->vbt_data.size != 0x00) { /*if non-zero, then use vbt*/
|
||||
mode = kzalloc(sizeof(*mode), GFP_KERNEL);
|
||||
if (!mode)
|
||||
return;
|
||||
|
||||
mode->hdisplay = (ti->hactive_hi << 8) | ti->hactive_lo;
|
||||
mode->vdisplay = (ti->vactive_hi << 8) | ti->vactive_lo;
|
||||
mode->hsync_start = mode->hdisplay + \
|
||||
((ti->hsync_offset_hi << 8) | \
|
||||
ti->hsync_offset_lo);
|
||||
mode->hsync_end = mode->hsync_start + \
|
||||
((ti->hsync_pulse_width_hi << 8) | \
|
||||
ti->hsync_pulse_width_lo);
|
||||
mode->htotal = mode->hdisplay + ((ti->hblank_hi << 8) | \
|
||||
ti->hblank_lo);
|
||||
mode->vsync_start = \
|
||||
mode->vdisplay + ((ti->vsync_offset_hi << 4) | \
|
||||
ti->vsync_offset_lo);
|
||||
mode->vsync_end = \
|
||||
mode->vsync_start + ((ti->vsync_pulse_width_hi << 4) | \
|
||||
ti->vsync_pulse_width_lo);
|
||||
mode->vtotal = mode->vdisplay + \
|
||||
((ti->vblank_hi << 8) | ti->vblank_lo);
|
||||
mode->clock = ti->pixel_clock * 10;
|
||||
#if 0
|
||||
printk(KERN_INFO "hdisplay is %d\n", mode->hdisplay);
|
||||
printk(KERN_INFO "vdisplay is %d\n", mode->vdisplay);
|
||||
printk(KERN_INFO "HSS is %d\n", mode->hsync_start);
|
||||
printk(KERN_INFO "HSE is %d\n", mode->hsync_end);
|
||||
printk(KERN_INFO "htotal is %d\n", mode->htotal);
|
||||
printk(KERN_INFO "VSS is %d\n", mode->vsync_start);
|
||||
printk(KERN_INFO "VSE is %d\n", mode->vsync_end);
|
||||
printk(KERN_INFO "vtotal is %d\n", mode->vtotal);
|
||||
printk(KERN_INFO "clock is %d\n", mode->clock);
|
||||
#endif
|
||||
mode_dev->panel_fixed_mode = mode;
|
||||
}
|
||||
|
||||
/* Use the BIOS VBT mode if available */
|
||||
if (mode_dev->panel_fixed_mode == NULL && mode_dev->vbt_mode)
|
||||
mode_dev->panel_fixed_mode = drm_mode_duplicate(dev,
|
||||
mode_dev->vbt_mode);
|
||||
|
||||
/* Then try the LVDS VBT mode */
|
||||
if (mode_dev->panel_fixed_mode == NULL)
|
||||
if (dev_priv->lfp_lvds_vbt_mode)
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev,
|
||||
dev_priv->lfp_lvds_vbt_mode);
|
||||
/* Then guess */
|
||||
if (mode_dev->panel_fixed_mode == NULL)
|
||||
mode_dev->panel_fixed_mode
|
||||
= drm_mode_duplicate(dev, &lvds_configuration_modes[2]);
|
||||
|
||||
drm_mode_set_name(mode_dev->panel_fixed_mode);
|
||||
drm_mode_set_crtcinfo(mode_dev->panel_fixed_mode, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* oaktrail_lvds_init - setup LVDS connectors on this device
|
||||
* @dev: drm device
|
||||
*
|
||||
* Create the connector, register the LVDS DDC bus, and try to figure out what
|
||||
* modes we can display on the LVDS panel (if present).
|
||||
*/
|
||||
void oaktrail_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
struct psb_intel_connector *psb_intel_connector;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
struct i2c_adapter *i2c_adap;
|
||||
struct drm_display_mode *scan; /* *modes, *bios_mode; */
|
||||
|
||||
psb_intel_encoder = kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL);
|
||||
if (!psb_intel_encoder)
|
||||
return;
|
||||
|
||||
psb_intel_connector = kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL);
|
||||
if (!psb_intel_connector)
|
||||
goto failed_connector;
|
||||
|
||||
connector = &psb_intel_connector->base;
|
||||
encoder = &psb_intel_encoder->base;
|
||||
dev_priv->is_lvds_on = true;
|
||||
drm_connector_init(dev, connector,
|
||||
&psb_intel_lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
|
||||
drm_encoder_init(dev, encoder, &psb_intel_lvds_enc_funcs,
|
||||
DRM_MODE_ENCODER_LVDS);
|
||||
|
||||
psb_intel_connector_attach_encoder(psb_intel_connector,
|
||||
psb_intel_encoder);
|
||||
psb_intel_encoder->type = INTEL_OUTPUT_LVDS;
|
||||
|
||||
drm_encoder_helper_add(encoder, &oaktrail_lvds_helper_funcs);
|
||||
drm_connector_helper_add(connector,
|
||||
&psb_intel_lvds_connector_helper_funcs);
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
||||
drm_connector_attach_property(connector,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
DRM_MODE_SCALE_FULLSCREEN);
|
||||
drm_connector_attach_property(connector,
|
||||
dev_priv->backlight_property,
|
||||
BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
mode_dev->panel_wants_dither = false;
|
||||
if (dev_priv->vbt_data.size != 0x00)
|
||||
mode_dev->panel_wants_dither = (dev_priv->gct_data.
|
||||
Panel_Port_Control & MRST_PANEL_8TO6_DITHER_ENABLE);
|
||||
if (dev_priv->lvds_dither)
|
||||
mode_dev->panel_wants_dither = 1;
|
||||
|
||||
/*
|
||||
* LVDS discovery:
|
||||
* 1) check for EDID on DDC
|
||||
* 2) check for VBT data
|
||||
* 3) check to see if LVDS is already on
|
||||
* if none of the above, no panel
|
||||
* 4) make sure lid is open
|
||||
* if closed, act like it's not there for now
|
||||
*/
|
||||
|
||||
i2c_adap = i2c_get_adapter(dev_priv->ops->i2c_bus);
|
||||
if (i2c_adap == NULL)
|
||||
dev_err(dev->dev, "No ddc adapter available!\n");
|
||||
/*
|
||||
* Attempt to get the fixed panel mode from DDC. Assume that the
|
||||
* preferred mode is the right one.
|
||||
*/
|
||||
if (i2c_adap) {
|
||||
edid = drm_get_edid(connector, i2c_adap);
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector,
|
||||
edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
list_for_each_entry(scan, &connector->probed_modes, head) {
|
||||
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, scan);
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* If we didn't get EDID, try geting panel timing
|
||||
* from configuration data
|
||||
*/
|
||||
oaktrail_lvds_get_configuration_mode(dev, mode_dev);
|
||||
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED;
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
|
||||
/* If we still don't have a mode after all that, give up. */
|
||||
if (!mode_dev->panel_fixed_mode) {
|
||||
dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
|
||||
goto failed_find;
|
||||
}
|
||||
|
||||
out:
|
||||
drm_sysfs_connector_add(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
dev_dbg(dev->dev, "No LVDS modes found, disabling.\n");
|
||||
if (psb_intel_encoder->ddc_bus)
|
||||
psb_intel_i2c_destroy(psb_intel_encoder->ddc_bus);
|
||||
|
||||
/* failed_ddc: */
|
||||
|
||||
drm_encoder_cleanup(encoder);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(psb_intel_connector);
|
||||
failed_connector:
|
||||
kfree(psb_intel_encoder);
|
||||
}
|
||||
|
|
@ -0,0 +1,316 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2009-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Benjamin Defnet <benjamin.r.defnet@intel.com>
|
||||
* Rajesh Poornachandran <rajesh.poornachandran@intel.com>
|
||||
* Massively reworked
|
||||
* Alan Cox <alan@linux.intel.com>
|
||||
*/
|
||||
|
||||
#include "power.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
static struct mutex power_mutex; /* Serialize power ops */
|
||||
static spinlock_t power_ctrl_lock; /* Serialize power claim */
|
||||
|
||||
/**
|
||||
* gma_power_init - initialise power manager
|
||||
* @dev: our device
|
||||
*
|
||||
* Set up for power management tracking of our hardware.
|
||||
*/
|
||||
void gma_power_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* FIXME: Move APM/OSPM base into relevant device code */
|
||||
dev_priv->apm_base = dev_priv->apm_reg & 0xffff;
|
||||
dev_priv->ospm_base &= 0xffff;
|
||||
|
||||
dev_priv->display_power = true; /* We start active */
|
||||
dev_priv->display_count = 0; /* Currently no users */
|
||||
dev_priv->suspended = false; /* And not suspended */
|
||||
spin_lock_init(&power_ctrl_lock);
|
||||
mutex_init(&power_mutex);
|
||||
|
||||
dev_priv->ops->init_pm(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_power_uninit - end power manager
|
||||
* @dev: device to end for
|
||||
*
|
||||
* Undo the effects of gma_power_init
|
||||
*/
|
||||
void gma_power_uninit(struct drm_device *dev)
|
||||
{
|
||||
pm_runtime_disable(&dev->pdev->dev);
|
||||
pm_runtime_set_suspended(&dev->pdev->dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_suspend_display - suspend the display logic
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Suspend the display logic of the graphics interface
|
||||
*/
|
||||
static void gma_suspend_display(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (dev_priv->suspended)
|
||||
return;
|
||||
dev_priv->ops->save_regs(dev);
|
||||
dev_priv->ops->power_down(dev);
|
||||
dev_priv->display_power = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_resume_display - resume display side logic
|
||||
*
|
||||
* Resume the display hardware restoring state and enabling
|
||||
* as necessary.
|
||||
*/
|
||||
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;
|
||||
dev_priv->display_power = true;
|
||||
|
||||
PSB_WVDC32(dev_priv->pge_ctl | _PSB_PGETBL_ENABLED, PSB_PGETBL_CTL);
|
||||
pci_write_config_word(pdev, PSB_GMCH_CTRL,
|
||||
dev_priv->gmch_ctrl | _PSB_GMCH_ENABLED);
|
||||
dev_priv->ops->restore_regs(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_suspend_pci - suspend PCI side
|
||||
* @pdev: PCI device
|
||||
*
|
||||
* Perform the suspend processing on our PCI device state
|
||||
*/
|
||||
static void gma_suspend_pci(struct pci_dev *pdev)
|
||||
{
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int bsm, vbt;
|
||||
|
||||
if (dev_priv->suspended)
|
||||
return;
|
||||
|
||||
pci_save_state(pdev);
|
||||
pci_read_config_dword(pdev, 0x5C, &bsm);
|
||||
dev_priv->saveBSM = bsm;
|
||||
pci_read_config_dword(pdev, 0xFC, &vbt);
|
||||
dev_priv->saveVBT = vbt;
|
||||
pci_read_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, &dev_priv->msi_addr);
|
||||
pci_read_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, &dev_priv->msi_data);
|
||||
|
||||
pci_disable_device(pdev);
|
||||
pci_set_power_state(pdev, PCI_D3hot);
|
||||
|
||||
dev_priv->suspended = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_resume_pci - resume helper
|
||||
* @dev: our PCI device
|
||||
*
|
||||
* Perform the resume processing on our PCI device state - rewrite
|
||||
* register state and re-enable the PCI device
|
||||
*/
|
||||
static bool gma_resume_pci(struct pci_dev *pdev)
|
||||
{
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
if (!dev_priv->suspended)
|
||||
return true;
|
||||
|
||||
pci_set_power_state(pdev, PCI_D0);
|
||||
pci_restore_state(pdev);
|
||||
pci_write_config_dword(pdev, 0x5c, dev_priv->saveBSM);
|
||||
pci_write_config_dword(pdev, 0xFC, dev_priv->saveVBT);
|
||||
/* restoring MSI address and data in PCIx space */
|
||||
pci_write_config_dword(pdev, PSB_PCIx_MSI_ADDR_LOC, dev_priv->msi_addr);
|
||||
pci_write_config_dword(pdev, PSB_PCIx_MSI_DATA_LOC, dev_priv->msi_data);
|
||||
ret = pci_enable_device(pdev);
|
||||
|
||||
if (ret != 0)
|
||||
dev_err(&pdev->dev, "pci_enable failed: %d\n", ret);
|
||||
else
|
||||
dev_priv->suspended = false;
|
||||
return !dev_priv->suspended;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_power_suspend - bus callback for suspend
|
||||
* @pdev: our PCI device
|
||||
* @state: suspend type
|
||||
*
|
||||
* Called back by the PCI layer during a suspend of the system. We
|
||||
* perform the necessary shut down steps and save enough state that
|
||||
* we can undo this when resume is called.
|
||||
*/
|
||||
int gma_power_suspend(struct device *_dev)
|
||||
{
|
||||
struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev);
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
mutex_lock(&power_mutex);
|
||||
if (!dev_priv->suspended) {
|
||||
if (dev_priv->display_count) {
|
||||
mutex_unlock(&power_mutex);
|
||||
return -EBUSY;
|
||||
}
|
||||
psb_irq_uninstall(dev);
|
||||
gma_suspend_display(dev);
|
||||
gma_suspend_pci(pdev);
|
||||
}
|
||||
mutex_unlock(&power_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_power_resume - resume power
|
||||
* @pdev: PCI device
|
||||
*
|
||||
* Resume the PCI side of the graphics and then the displays
|
||||
*/
|
||||
int gma_power_resume(struct device *_dev)
|
||||
{
|
||||
struct pci_dev *pdev = container_of(_dev, struct pci_dev, dev);
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
|
||||
mutex_lock(&power_mutex);
|
||||
gma_resume_pci(pdev);
|
||||
gma_resume_display(pdev);
|
||||
psb_irq_preinstall(dev);
|
||||
psb_irq_postinstall(dev);
|
||||
mutex_unlock(&power_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_power_is_on - returne true if power is on
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Returns true if the display island power is on at this moment
|
||||
*/
|
||||
bool gma_power_is_on(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
return dev_priv->display_power;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_power_begin - begin requiring power
|
||||
* @dev: our DRM device
|
||||
* @force_on: true to force power on
|
||||
*
|
||||
* Begin an action that requires the display power island is enabled.
|
||||
* We refcount the islands.
|
||||
*/
|
||||
bool gma_power_begin(struct drm_device *dev, bool force_on)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&power_ctrl_lock, flags);
|
||||
/* Power already on ? */
|
||||
if (dev_priv->display_power) {
|
||||
dev_priv->display_count++;
|
||||
pm_runtime_get(&dev->pdev->dev);
|
||||
spin_unlock_irqrestore(&power_ctrl_lock, flags);
|
||||
return true;
|
||||
}
|
||||
if (force_on == false)
|
||||
goto out_false;
|
||||
|
||||
/* Ok power up needed */
|
||||
ret = gma_resume_pci(dev->pdev);
|
||||
if (ret == 0) {
|
||||
psb_irq_preinstall(dev);
|
||||
psb_irq_postinstall(dev);
|
||||
pm_runtime_get(&dev->pdev->dev);
|
||||
dev_priv->display_count++;
|
||||
spin_unlock_irqrestore(&power_ctrl_lock, flags);
|
||||
return true;
|
||||
}
|
||||
out_false:
|
||||
spin_unlock_irqrestore(&power_ctrl_lock, flags);
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* gma_power_end - end use of power
|
||||
* @dev: Our DRM device
|
||||
*
|
||||
* Indicate that one of our gma_power_begin() requested periods when
|
||||
* the diplay island power is needed has completed.
|
||||
*/
|
||||
void gma_power_end(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long flags;
|
||||
spin_lock_irqsave(&power_ctrl_lock, flags);
|
||||
dev_priv->display_count--;
|
||||
WARN_ON(dev_priv->display_count < 0);
|
||||
spin_unlock_irqrestore(&power_ctrl_lock, flags);
|
||||
pm_runtime_put(&dev->pdev->dev);
|
||||
}
|
||||
|
||||
int psb_runtime_suspend(struct device *dev)
|
||||
{
|
||||
return gma_power_suspend(dev);
|
||||
}
|
||||
|
||||
int psb_runtime_resume(struct device *dev)
|
||||
{
|
||||
return gma_power_resume(dev);;
|
||||
}
|
||||
|
||||
int psb_runtime_idle(struct device *dev)
|
||||
{
|
||||
struct drm_device *drmdev = pci_get_drvdata(to_pci_dev(dev));
|
||||
struct drm_psb_private *dev_priv = drmdev->dev_private;
|
||||
if (dev_priv->display_count)
|
||||
return 0;
|
||||
else
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2009-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Benjamin Defnet <benjamin.r.defnet@intel.com>
|
||||
* Rajesh Poornachandran <rajesh.poornachandran@intel.com>
|
||||
* Massively reworked
|
||||
* Alan Cox <alan@linux.intel.com>
|
||||
*/
|
||||
#ifndef _PSB_POWERMGMT_H_
|
||||
#define _PSB_POWERMGMT_H_
|
||||
|
||||
#include <linux/pci.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
void gma_power_init(struct drm_device *dev);
|
||||
void gma_power_uninit(struct drm_device *dev);
|
||||
|
||||
/*
|
||||
* The kernel bus power management will call these functions
|
||||
*/
|
||||
int gma_power_suspend(struct device *dev);
|
||||
int gma_power_resume(struct device *dev);
|
||||
|
||||
/*
|
||||
* These are the functions the driver should use to wrap all hw access
|
||||
* (i.e. register reads and writes)
|
||||
*/
|
||||
bool gma_power_begin(struct drm_device *dev, bool force);
|
||||
void gma_power_end(struct drm_device *dev);
|
||||
|
||||
/*
|
||||
* Use this function to do an instantaneous check for if the hw is on.
|
||||
* Only use this in cases where you know the mutex is already held such
|
||||
* as in irq install/uninstall and you need to
|
||||
* prevent a deadlock situation. Otherwise use gma_power_begin().
|
||||
*/
|
||||
bool gma_power_is_on(struct drm_device *dev);
|
||||
|
||||
/*
|
||||
* GFX-Runtime PM callbacks
|
||||
*/
|
||||
int psb_runtime_suspend(struct device *dev);
|
||||
int psb_runtime_resume(struct device *dev);
|
||||
int psb_runtime_idle(struct device *dev);
|
||||
|
||||
#endif /*_PSB_POWERMGMT_H_*/
|
|
@ -0,0 +1,328 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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/backlight.h>
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "intel_bios.h"
|
||||
|
||||
|
||||
static int psb_output_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
psb_intel_lvds_init(dev, &dev_priv->mode_dev);
|
||||
psb_intel_sdvo_init(dev, SDVOB);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
|
||||
/*
|
||||
* Poulsbo Backlight Interfaces
|
||||
*/
|
||||
|
||||
#define BLC_PWM_PRECISION_FACTOR 100 /* 10000000 */
|
||||
#define BLC_PWM_FREQ_CALC_CONSTANT 32
|
||||
#define MHz 1000000
|
||||
|
||||
#define PSB_BLC_PWM_PRECISION_FACTOR 10
|
||||
#define PSB_BLC_MAX_PWM_REG_FREQ 0xFFFE
|
||||
#define PSB_BLC_MIN_PWM_REG_FREQ 0x2
|
||||
|
||||
#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
|
||||
#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16)
|
||||
|
||||
static int psb_brightness;
|
||||
static struct backlight_device *psb_backlight_device;
|
||||
|
||||
static int psb_get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
/* return locally cached var instead of HW read (due to DPST etc.) */
|
||||
/* FIXME: ideally return actual value in case firmware fiddled with
|
||||
it */
|
||||
return psb_brightness;
|
||||
}
|
||||
|
||||
|
||||
static int psb_backlight_setup(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long core_clock;
|
||||
/* u32 bl_max_freq; */
|
||||
/* unsigned long value; */
|
||||
u16 bl_max_freq;
|
||||
uint32_t value;
|
||||
uint32_t blc_pwm_precision_factor;
|
||||
|
||||
/* get bl_max_freq and pol from dev_priv*/
|
||||
if (!dev_priv->lvds_bl) {
|
||||
dev_err(dev->dev, "Has no valid LVDS backlight info\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
bl_max_freq = dev_priv->lvds_bl->freq;
|
||||
blc_pwm_precision_factor = PSB_BLC_PWM_PRECISION_FACTOR;
|
||||
|
||||
core_clock = dev_priv->core_freq;
|
||||
|
||||
value = (core_clock * MHz) / BLC_PWM_FREQ_CALC_CONSTANT;
|
||||
value *= blc_pwm_precision_factor;
|
||||
value /= bl_max_freq;
|
||||
value /= blc_pwm_precision_factor;
|
||||
|
||||
if (value > (unsigned long long)PSB_BLC_MAX_PWM_REG_FREQ ||
|
||||
value < (unsigned long long)PSB_BLC_MIN_PWM_REG_FREQ)
|
||||
return -ERANGE;
|
||||
else {
|
||||
value &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR;
|
||||
REG_WRITE(BLC_PWM_CTL,
|
||||
(value << PSB_BACKLIGHT_PWM_CTL_SHIFT) | (value));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_set_brightness(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(psb_backlight_device);
|
||||
int level = bd->props.brightness;
|
||||
|
||||
/* Percentage 1-100% being valid */
|
||||
if (level < 1)
|
||||
level = 1;
|
||||
|
||||
psb_intel_lvds_set_brightness(dev, level);
|
||||
psb_brightness = level;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops psb_ops = {
|
||||
.get_brightness = psb_get_brightness,
|
||||
.update_status = psb_set_brightness,
|
||||
};
|
||||
|
||||
static int psb_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
struct backlight_properties props;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.max_brightness = 100;
|
||||
props.type = BACKLIGHT_PLATFORM;
|
||||
|
||||
psb_backlight_device = backlight_device_register("psb-bl",
|
||||
NULL, (void *)dev, &psb_ops, &props);
|
||||
if (IS_ERR(psb_backlight_device))
|
||||
return PTR_ERR(psb_backlight_device);
|
||||
|
||||
ret = psb_backlight_setup(dev);
|
||||
if (ret < 0) {
|
||||
backlight_device_unregister(psb_backlight_device);
|
||||
psb_backlight_device = NULL;
|
||||
return ret;
|
||||
}
|
||||
psb_backlight_device->props.brightness = 100;
|
||||
psb_backlight_device->props.max_brightness = 100;
|
||||
backlight_update_status(psb_backlight_device);
|
||||
dev_priv->backlight_device = psb_backlight_device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Provide the Poulsbo specific chip logic and low level methods
|
||||
* for power management
|
||||
*/
|
||||
|
||||
static void psb_init_pm(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
u32 gating = PSB_RSGX32(PSB_CR_CLKGATECTL);
|
||||
gating &= ~3; /* Disable 2D clock gating */
|
||||
gating |= 1;
|
||||
PSB_WSGX32(gating, PSB_CR_CLKGATECTL);
|
||||
PSB_RSGX32(PSB_CR_CLKGATECTL);
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_save_display_registers - save registers lost on suspend
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Save the state we need in order to be able to restore the interface
|
||||
* upon resume from suspend
|
||||
*/
|
||||
static int psb_save_display_registers(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_connector *connector;
|
||||
|
||||
/* Display arbitration control + watermarks */
|
||||
dev_priv->saveDSPARB = PSB_RVDC32(DSPARB);
|
||||
dev_priv->saveDSPFW1 = PSB_RVDC32(DSPFW1);
|
||||
dev_priv->saveDSPFW2 = PSB_RVDC32(DSPFW2);
|
||||
dev_priv->saveDSPFW3 = PSB_RVDC32(DSPFW3);
|
||||
dev_priv->saveDSPFW4 = PSB_RVDC32(DSPFW4);
|
||||
dev_priv->saveDSPFW5 = PSB_RVDC32(DSPFW5);
|
||||
dev_priv->saveDSPFW6 = PSB_RVDC32(DSPFW6);
|
||||
dev_priv->saveCHICKENBIT = PSB_RVDC32(DSPCHICKENBIT);
|
||||
|
||||
/* Save crtc and output state */
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
|
||||
if (drm_helper_crtc_in_use(crtc))
|
||||
crtc->funcs->save(crtc);
|
||||
}
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
|
||||
connector->funcs->save(connector);
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_restore_display_registers - restore lost register state
|
||||
* @dev: our DRM device
|
||||
*
|
||||
* Restore register state that was lost during suspend and resume.
|
||||
*/
|
||||
static int psb_restore_display_registers(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_connector *connector;
|
||||
|
||||
/* Display arbitration + watermarks */
|
||||
PSB_WVDC32(dev_priv->saveDSPARB, DSPARB);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW1, DSPFW1);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW2, DSPFW2);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW3, DSPFW3);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW4, DSPFW4);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW5, DSPFW5);
|
||||
PSB_WVDC32(dev_priv->saveDSPFW6, DSPFW6);
|
||||
PSB_WVDC32(dev_priv->saveCHICKENBIT, DSPCHICKENBIT);
|
||||
|
||||
/*make sure VGA plane is off. it initializes to on after reset!*/
|
||||
PSB_WVDC32(0x80000000, VGACNTRL);
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
||||
if (drm_helper_crtc_in_use(crtc))
|
||||
crtc->funcs->restore(crtc);
|
||||
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
|
||||
connector->funcs->restore(connector);
|
||||
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_power_down(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_power_up(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void psb_get_core_freq(struct drm_device *dev)
|
||||
{
|
||||
uint32_t clock;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
/*pci_write_config_dword(pci_root, 0xD4, 0x00C32004);*/
|
||||
/*pci_write_config_dword(pci_root, 0xD0, 0xE0033000);*/
|
||||
|
||||
pci_write_config_dword(pci_root, 0xD0, 0xD0050300);
|
||||
pci_read_config_dword(pci_root, 0xD4, &clock);
|
||||
pci_dev_put(pci_root);
|
||||
|
||||
switch (clock & 0x07) {
|
||||
case 0:
|
||||
dev_priv->core_freq = 100;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->core_freq = 133;
|
||||
break;
|
||||
case 2:
|
||||
dev_priv->core_freq = 150;
|
||||
break;
|
||||
case 3:
|
||||
dev_priv->core_freq = 178;
|
||||
break;
|
||||
case 4:
|
||||
dev_priv->core_freq = 200;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
case 7:
|
||||
dev_priv->core_freq = 266;
|
||||
default:
|
||||
dev_priv->core_freq = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int psb_chip_setup(struct drm_device *dev)
|
||||
{
|
||||
psb_get_core_freq(dev);
|
||||
gma_intel_setup_gmbus(dev);
|
||||
gma_intel_opregion_init(dev);
|
||||
psb_intel_init_bios(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void psb_chip_teardown(struct drm_device *dev)
|
||||
{
|
||||
gma_intel_teardown_gmbus(dev);
|
||||
}
|
||||
|
||||
const struct psb_ops psb_chip_ops = {
|
||||
.name = "Poulsbo",
|
||||
.accel_2d = 1,
|
||||
.pipes = 2,
|
||||
.crtcs = 2,
|
||||
.sgx_offset = PSB_SGX_OFFSET,
|
||||
.chip_setup = psb_chip_setup,
|
||||
.chip_teardown = psb_chip_teardown,
|
||||
|
||||
.crtc_helper = &psb_intel_helper_funcs,
|
||||
.crtc_funcs = &psb_intel_crtc_funcs,
|
||||
|
||||
.output_init = psb_output_init,
|
||||
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
.backlight_init = psb_backlight_init,
|
||||
#endif
|
||||
|
||||
.init_pm = psb_init_pm,
|
||||
.save_regs = psb_save_display_registers,
|
||||
.restore_regs = psb_restore_display_registers,
|
||||
.power_down = psb_power_down,
|
||||
.power_up = psb_power_up,
|
||||
};
|
||||
|
|
@ -0,0 +1,703 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
* Copyright (c) 2008, Tungsten Graphics, Inc. Cedar Park, TX., USA.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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 <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gma_drm.h"
|
||||
#include "psb_drv.h"
|
||||
#include "framebuffer.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "intel_bios.h"
|
||||
#include "mid_bios.h"
|
||||
#include <drm/drm_pciids.h>
|
||||
#include "power.h"
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <acpi/video.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
static int drm_psb_trap_pagefaults;
|
||||
|
||||
static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
|
||||
|
||||
MODULE_PARM_DESC(trap_pagefaults, "Error and reset on MMU pagefaults");
|
||||
module_param_named(trap_pagefaults, drm_psb_trap_pagefaults, int, 0600);
|
||||
|
||||
|
||||
static DEFINE_PCI_DEVICE_TABLE(pciidlist) = {
|
||||
{ 0x8086, 0x8108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
|
||||
{ 0x8086, 0x8109, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &psb_chip_ops },
|
||||
#if defined(CONFIG_DRM_GMA600)
|
||||
{ 0x8086, 0x4100, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4101, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4102, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4103, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4104, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
{ 0x8086, 0x4107, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
/* Atom E620 */
|
||||
{ 0x8086, 0x4108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &oaktrail_chip_ops},
|
||||
#endif
|
||||
#if defined(CONFIG_DRM_GMA3600)
|
||||
{ 0x8086, 0x0be0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be3, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be4, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be5, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be6, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
{ 0x8086, 0x0be7, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (long) &cdv_chip_ops},
|
||||
#endif
|
||||
{ 0, 0, 0}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pci, pciidlist);
|
||||
|
||||
/*
|
||||
* Standard IOCTLs.
|
||||
*/
|
||||
|
||||
#define DRM_IOCTL_PSB_ADB \
|
||||
DRM_IOWR(DRM_GMA_ADB + DRM_COMMAND_BASE, uint32_t)
|
||||
#define DRM_IOCTL_PSB_MODE_OPERATION \
|
||||
DRM_IOWR(DRM_GMA_MODE_OPERATION + DRM_COMMAND_BASE, \
|
||||
struct drm_psb_mode_operation_arg)
|
||||
#define DRM_IOCTL_PSB_STOLEN_MEMORY \
|
||||
DRM_IOWR(DRM_GMA_STOLEN_MEMORY + DRM_COMMAND_BASE, \
|
||||
struct drm_psb_stolen_memory_arg)
|
||||
#define DRM_IOCTL_PSB_GAMMA \
|
||||
DRM_IOWR(DRM_GMA_GAMMA + DRM_COMMAND_BASE, \
|
||||
struct drm_psb_dpst_lut_arg)
|
||||
#define DRM_IOCTL_PSB_DPST_BL \
|
||||
DRM_IOWR(DRM_GMA_DPST_BL + DRM_COMMAND_BASE, \
|
||||
uint32_t)
|
||||
#define DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID \
|
||||
DRM_IOWR(DRM_GMA_GET_PIPE_FROM_CRTC_ID + DRM_COMMAND_BASE, \
|
||||
struct drm_psb_get_pipe_from_crtc_id_arg)
|
||||
#define DRM_IOCTL_PSB_GEM_CREATE \
|
||||
DRM_IOWR(DRM_GMA_GEM_CREATE + DRM_COMMAND_BASE, \
|
||||
struct drm_psb_gem_create)
|
||||
#define DRM_IOCTL_PSB_GEM_MMAP \
|
||||
DRM_IOWR(DRM_GMA_GEM_MMAP + DRM_COMMAND_BASE, \
|
||||
struct drm_psb_gem_mmap)
|
||||
|
||||
static int psb_adb_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
static int psb_gamma_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
|
||||
#define PSB_IOCTL_DEF(ioctl, func, flags) \
|
||||
[DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func}
|
||||
|
||||
static struct drm_ioctl_desc psb_ioctls[] = {
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_ADB, psb_adb_ioctl, DRM_AUTH),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_MODE_OPERATION, psb_mode_operation_ioctl,
|
||||
DRM_AUTH),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_STOLEN_MEMORY, psb_stolen_memory_ioctl,
|
||||
DRM_AUTH),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_GAMMA, psb_gamma_ioctl, DRM_AUTH),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_DPST_BL, psb_dpst_bl_ioctl, DRM_AUTH),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_GET_PIPE_FROM_CRTC_ID,
|
||||
psb_intel_get_pipe_from_crtc_id, 0),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_CREATE, psb_gem_create_ioctl,
|
||||
DRM_UNLOCKED | DRM_AUTH),
|
||||
PSB_IOCTL_DEF(DRM_IOCTL_PSB_GEM_MMAP, psb_gem_mmap_ioctl,
|
||||
DRM_UNLOCKED | DRM_AUTH),
|
||||
};
|
||||
|
||||
static void psb_lastclose(struct drm_device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static void psb_do_takedown(struct drm_device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int psb_do_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_gtt *pg = &dev_priv->gtt;
|
||||
|
||||
uint32_t stolen_gtt;
|
||||
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (pg->mmu_gatt_start & 0x0FFFFFFF) {
|
||||
dev_err(dev->dev, "Gatt must be 256M aligned. This is a bug.\n");
|
||||
ret = -EINVAL;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
|
||||
stolen_gtt = (pg->stolen_size >> PAGE_SHIFT) * 4;
|
||||
stolen_gtt = (stolen_gtt + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
||||
stolen_gtt =
|
||||
(stolen_gtt < pg->gtt_pages) ? stolen_gtt : pg->gtt_pages;
|
||||
|
||||
dev_priv->gatt_free_offset = pg->mmu_gatt_start +
|
||||
(stolen_gtt << PAGE_SHIFT) * 1024;
|
||||
|
||||
if (1 || drm_debug) {
|
||||
uint32_t core_id = PSB_RSGX32(PSB_CR_CORE_ID);
|
||||
uint32_t core_rev = PSB_RSGX32(PSB_CR_CORE_REVISION);
|
||||
DRM_INFO("SGX core id = 0x%08x\n", core_id);
|
||||
DRM_INFO("SGX core rev major = 0x%02x, minor = 0x%02x\n",
|
||||
(core_rev & _PSB_CC_REVISION_MAJOR_MASK) >>
|
||||
_PSB_CC_REVISION_MAJOR_SHIFT,
|
||||
(core_rev & _PSB_CC_REVISION_MINOR_MASK) >>
|
||||
_PSB_CC_REVISION_MINOR_SHIFT);
|
||||
DRM_INFO
|
||||
("SGX core rev maintenance = 0x%02x, designer = 0x%02x\n",
|
||||
(core_rev & _PSB_CC_REVISION_MAINTENANCE_MASK) >>
|
||||
_PSB_CC_REVISION_MAINTENANCE_SHIFT,
|
||||
(core_rev & _PSB_CC_REVISION_DESIGNER_MASK) >>
|
||||
_PSB_CC_REVISION_DESIGNER_SHIFT);
|
||||
}
|
||||
|
||||
|
||||
spin_lock_init(&dev_priv->irqmask_lock);
|
||||
spin_lock_init(&dev_priv->lock_2d);
|
||||
|
||||
PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK0);
|
||||
PSB_WSGX32(0x00000000, PSB_CR_BIF_BANK1);
|
||||
PSB_RSGX32(PSB_CR_BIF_BANK1);
|
||||
PSB_WSGX32(PSB_RSGX32(PSB_CR_BIF_CTRL) | _PSB_MMU_ER_MASK,
|
||||
PSB_CR_BIF_CTRL);
|
||||
psb_spank(dev_priv);
|
||||
|
||||
/* mmu_gatt ?? */
|
||||
PSB_WSGX32(pg->gatt_start, PSB_CR_BIF_TWOD_REQ_BASE);
|
||||
return 0;
|
||||
out_err:
|
||||
psb_do_takedown(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int psb_driver_unload(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* Kill vblank etc here */
|
||||
|
||||
gma_backlight_exit(dev);
|
||||
|
||||
psb_modeset_cleanup(dev);
|
||||
|
||||
if (dev_priv) {
|
||||
psb_lid_timer_takedown(dev_priv);
|
||||
gma_intel_opregion_exit(dev);
|
||||
|
||||
if (dev_priv->ops->chip_teardown)
|
||||
dev_priv->ops->chip_teardown(dev);
|
||||
psb_do_takedown(dev);
|
||||
|
||||
|
||||
if (dev_priv->pf_pd) {
|
||||
psb_mmu_free_pagedir(dev_priv->pf_pd);
|
||||
dev_priv->pf_pd = NULL;
|
||||
}
|
||||
if (dev_priv->mmu) {
|
||||
struct psb_gtt *pg = &dev_priv->gtt;
|
||||
|
||||
down_read(&pg->sem);
|
||||
psb_mmu_remove_pfn_sequence(
|
||||
psb_mmu_get_default_pd
|
||||
(dev_priv->mmu),
|
||||
pg->mmu_gatt_start,
|
||||
dev_priv->vram_stolen_size >> PAGE_SHIFT);
|
||||
up_read(&pg->sem);
|
||||
psb_mmu_driver_takedown(dev_priv->mmu);
|
||||
dev_priv->mmu = NULL;
|
||||
}
|
||||
psb_gtt_takedown(dev);
|
||||
if (dev_priv->scratch_page) {
|
||||
__free_page(dev_priv->scratch_page);
|
||||
dev_priv->scratch_page = NULL;
|
||||
}
|
||||
if (dev_priv->vdc_reg) {
|
||||
iounmap(dev_priv->vdc_reg);
|
||||
dev_priv->vdc_reg = NULL;
|
||||
}
|
||||
if (dev_priv->sgx_reg) {
|
||||
iounmap(dev_priv->sgx_reg);
|
||||
dev_priv->sgx_reg = NULL;
|
||||
}
|
||||
|
||||
kfree(dev_priv);
|
||||
dev->dev_private = NULL;
|
||||
|
||||
/*destroy VBT data*/
|
||||
psb_intel_destroy_bios(dev);
|
||||
}
|
||||
|
||||
gma_power_uninit(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int psb_driver_load(struct drm_device *dev, unsigned long chipset)
|
||||
{
|
||||
struct drm_psb_private *dev_priv;
|
||||
unsigned long resource_start;
|
||||
struct psb_gtt *pg;
|
||||
unsigned long irqflags;
|
||||
int ret = -ENOMEM;
|
||||
uint32_t tt_pages;
|
||||
struct drm_connector *connector;
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
|
||||
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
|
||||
if (dev_priv == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_priv->ops = (struct psb_ops *)chipset;
|
||||
dev_priv->dev = dev;
|
||||
dev->dev_private = (void *) dev_priv;
|
||||
|
||||
if (!IS_PSB(dev)) {
|
||||
if (pci_enable_msi(dev->pdev))
|
||||
dev_warn(dev->dev, "Enabling MSI failed!\n");
|
||||
}
|
||||
|
||||
dev_priv->num_pipe = dev_priv->ops->pipes;
|
||||
|
||||
resource_start = pci_resource_start(dev->pdev, PSB_MMIO_RESOURCE);
|
||||
|
||||
dev_priv->vdc_reg =
|
||||
ioremap(resource_start + PSB_VDC_OFFSET, PSB_VDC_SIZE);
|
||||
if (!dev_priv->vdc_reg)
|
||||
goto out_err;
|
||||
|
||||
dev_priv->sgx_reg = ioremap(resource_start + dev_priv->ops->sgx_offset,
|
||||
PSB_SGX_SIZE);
|
||||
if (!dev_priv->sgx_reg)
|
||||
goto out_err;
|
||||
|
||||
ret = dev_priv->ops->chip_setup(dev);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/* Init OSPM support */
|
||||
gma_power_init(dev);
|
||||
|
||||
ret = -ENOMEM;
|
||||
|
||||
dev_priv->scratch_page = alloc_page(GFP_DMA32 | __GFP_ZERO);
|
||||
if (!dev_priv->scratch_page)
|
||||
goto out_err;
|
||||
|
||||
set_pages_uc(dev_priv->scratch_page, 1);
|
||||
|
||||
ret = psb_gtt_init(dev, 0);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
dev_priv->mmu = psb_mmu_driver_init((void *)0,
|
||||
drm_psb_trap_pagefaults, 0,
|
||||
dev_priv);
|
||||
if (!dev_priv->mmu)
|
||||
goto out_err;
|
||||
|
||||
pg = &dev_priv->gtt;
|
||||
|
||||
tt_pages = (pg->gatt_pages < PSB_TT_PRIV0_PLIMIT) ?
|
||||
(pg->gatt_pages) : PSB_TT_PRIV0_PLIMIT;
|
||||
|
||||
|
||||
dev_priv->pf_pd = psb_mmu_alloc_pd(dev_priv->mmu, 1, 0);
|
||||
if (!dev_priv->pf_pd)
|
||||
goto out_err;
|
||||
|
||||
psb_mmu_set_pd_context(psb_mmu_get_default_pd(dev_priv->mmu), 0);
|
||||
psb_mmu_set_pd_context(dev_priv->pf_pd, 1);
|
||||
|
||||
ret = psb_do_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
PSB_WSGX32(0x20000000, PSB_CR_PDS_EXEC_BASE);
|
||||
PSB_WSGX32(0x30000000, PSB_CR_BIF_3D_REQ_BASE);
|
||||
|
||||
/* igd_opregion_init(&dev_priv->opregion_dev); */
|
||||
acpi_video_register();
|
||||
if (dev_priv->lid_state)
|
||||
psb_lid_timer_init(dev_priv);
|
||||
|
||||
ret = drm_vblank_init(dev, dev_priv->num_pipe);
|
||||
if (ret)
|
||||
goto out_err;
|
||||
|
||||
/*
|
||||
* Install interrupt handlers prior to powering off SGX or else we will
|
||||
* crash.
|
||||
*/
|
||||
dev_priv->vdc_irq_mask = 0;
|
||||
dev_priv->pipestat[0] = 0;
|
||||
dev_priv->pipestat[1] = 0;
|
||||
dev_priv->pipestat[2] = 0;
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
||||
PSB_WVDC32(0x00000000, PSB_INT_ENABLE_R);
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_INT_MASK_R);
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
if (IS_PSB(dev) && drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
drm_irq_install(dev);
|
||||
|
||||
dev->vblank_disable_allowed = 1;
|
||||
|
||||
dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
|
||||
|
||||
dev->driver->get_vblank_counter = psb_get_vblank_counter;
|
||||
|
||||
psb_modeset_init(dev);
|
||||
psb_fbdev_init(dev);
|
||||
drm_kms_helper_poll_init(dev);
|
||||
|
||||
/* Only add backlight support if we have LVDS output */
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list,
|
||||
head) {
|
||||
psb_intel_encoder = psb_intel_attached_encoder(connector);
|
||||
|
||||
switch (psb_intel_encoder->type) {
|
||||
case INTEL_OUTPUT_LVDS:
|
||||
case INTEL_OUTPUT_MIPI:
|
||||
ret = gma_backlight_init(dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
#if 0
|
||||
/*enable runtime pm at last*/
|
||||
pm_runtime_enable(&dev->pdev->dev);
|
||||
pm_runtime_set_active(&dev->pdev->dev);
|
||||
#endif
|
||||
/*Intel drm driver load is done, continue doing pvr load*/
|
||||
return 0;
|
||||
out_err:
|
||||
psb_driver_unload(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int psb_driver_device_is_agp(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void get_brightness(struct backlight_device *bd)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
if (bd) {
|
||||
bd->props.brightness = bd->ops->get_brightness(bd);
|
||||
backlight_update_status(bd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static int psb_dpst_bl_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = psb_priv(dev);
|
||||
uint32_t *arg = data;
|
||||
|
||||
dev_priv->blc_adj2 = *arg;
|
||||
get_brightness(dev_priv->backlight_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_adb_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = psb_priv(dev);
|
||||
uint32_t *arg = data;
|
||||
|
||||
dev_priv->blc_adj1 = *arg;
|
||||
get_brightness(dev_priv->backlight_device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_gamma_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_psb_dpst_lut_arg *lut_arg = data;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_connector *connector;
|
||||
struct psb_intel_crtc *psb_intel_crtc;
|
||||
int i = 0;
|
||||
int32_t obj_id;
|
||||
|
||||
obj_id = lut_arg->output_id;
|
||||
obj = drm_mode_object_find(dev, obj_id, DRM_MODE_OBJECT_CONNECTOR);
|
||||
if (!obj) {
|
||||
dev_dbg(dev->dev, "Invalid Connector object.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
connector = obj_to_connector(obj);
|
||||
crtc = connector->encoder->crtc;
|
||||
psb_intel_crtc = to_psb_intel_crtc(crtc);
|
||||
|
||||
for (i = 0; i < 256; i++)
|
||||
psb_intel_crtc->lut_adj[i] = lut_arg->lut[i];
|
||||
|
||||
psb_intel_crtc_load_lut(crtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_mode_operation_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
uint32_t obj_id;
|
||||
uint16_t op;
|
||||
struct drm_mode_modeinfo *umode;
|
||||
struct drm_display_mode *mode = NULL;
|
||||
struct drm_psb_mode_operation_arg *arg;
|
||||
struct drm_mode_object *obj;
|
||||
struct drm_connector *connector;
|
||||
struct drm_connector_helper_funcs *connector_funcs;
|
||||
int ret = 0;
|
||||
int resp = MODE_OK;
|
||||
|
||||
arg = (struct drm_psb_mode_operation_arg *)data;
|
||||
obj_id = arg->obj_id;
|
||||
op = arg->operation;
|
||||
|
||||
switch (op) {
|
||||
case PSB_MODE_OPERATION_MODE_VALID:
|
||||
umode = &arg->mode;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
obj = drm_mode_object_find(dev, obj_id,
|
||||
DRM_MODE_OBJECT_CONNECTOR);
|
||||
if (!obj) {
|
||||
ret = -EINVAL;
|
||||
goto mode_op_out;
|
||||
}
|
||||
|
||||
connector = obj_to_connector(obj);
|
||||
|
||||
mode = drm_mode_create(dev);
|
||||
if (!mode) {
|
||||
ret = -ENOMEM;
|
||||
goto mode_op_out;
|
||||
}
|
||||
|
||||
/* drm_crtc_convert_umode(mode, umode); */
|
||||
{
|
||||
mode->clock = umode->clock;
|
||||
mode->hdisplay = umode->hdisplay;
|
||||
mode->hsync_start = umode->hsync_start;
|
||||
mode->hsync_end = umode->hsync_end;
|
||||
mode->htotal = umode->htotal;
|
||||
mode->hskew = umode->hskew;
|
||||
mode->vdisplay = umode->vdisplay;
|
||||
mode->vsync_start = umode->vsync_start;
|
||||
mode->vsync_end = umode->vsync_end;
|
||||
mode->vtotal = umode->vtotal;
|
||||
mode->vscan = umode->vscan;
|
||||
mode->vrefresh = umode->vrefresh;
|
||||
mode->flags = umode->flags;
|
||||
mode->type = umode->type;
|
||||
strncpy(mode->name, umode->name, DRM_DISPLAY_MODE_LEN);
|
||||
mode->name[DRM_DISPLAY_MODE_LEN-1] = 0;
|
||||
}
|
||||
|
||||
connector_funcs = (struct drm_connector_helper_funcs *)
|
||||
connector->helper_private;
|
||||
|
||||
if (connector_funcs->mode_valid) {
|
||||
resp = connector_funcs->mode_valid(connector, mode);
|
||||
arg->data = resp;
|
||||
}
|
||||
|
||||
/*do some clean up work*/
|
||||
if (mode)
|
||||
drm_mode_destroy(dev, mode);
|
||||
mode_op_out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
return ret;
|
||||
|
||||
default:
|
||||
dev_dbg(dev->dev, "Unsupported psb mode operation\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_stolen_memory_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = psb_priv(dev);
|
||||
struct drm_psb_stolen_memory_arg *arg = data;
|
||||
|
||||
arg->base = dev_priv->stolen_base;
|
||||
arg->size = dev_priv->vram_stolen_size;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int psb_driver_open(struct drm_device *dev, struct drm_file *priv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void psb_driver_close(struct drm_device *dev, struct drm_file *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static long psb_unlocked_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct drm_file *file_priv = filp->private_data;
|
||||
struct drm_device *dev = file_priv->minor->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
static unsigned int runtime_allowed;
|
||||
|
||||
if (runtime_allowed == 1 && dev_priv->is_lvds_on) {
|
||||
runtime_allowed++;
|
||||
pm_runtime_allow(&dev->pdev->dev);
|
||||
dev_priv->rpm_enabled = 1;
|
||||
}
|
||||
return drm_ioctl(filp, cmd, arg);
|
||||
/* FIXME: do we need to wrap the other side of this */
|
||||
}
|
||||
|
||||
|
||||
/* When a client dies:
|
||||
* - Check for and clean up flipped page state
|
||||
*/
|
||||
void psb_driver_preclose(struct drm_device *dev, struct drm_file *priv)
|
||||
{
|
||||
}
|
||||
|
||||
static void psb_remove(struct pci_dev *pdev)
|
||||
{
|
||||
struct drm_device *dev = pci_get_drvdata(pdev);
|
||||
drm_put_dev(dev);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops psb_pm_ops = {
|
||||
.resume = gma_power_resume,
|
||||
.suspend = gma_power_suspend,
|
||||
.runtime_suspend = psb_runtime_suspend,
|
||||
.runtime_resume = psb_runtime_resume,
|
||||
.runtime_idle = psb_runtime_idle,
|
||||
};
|
||||
|
||||
static struct vm_operations_struct psb_gem_vm_ops = {
|
||||
.fault = psb_gem_fault,
|
||||
.open = drm_gem_vm_open,
|
||||
.close = drm_gem_vm_close,
|
||||
};
|
||||
|
||||
static const struct file_operations psb_gem_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = psb_unlocked_ioctl,
|
||||
.mmap = drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.read = drm_read,
|
||||
};
|
||||
|
||||
static struct drm_driver driver = {
|
||||
.driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | \
|
||||
DRIVER_IRQ_VBL | DRIVER_MODESET | DRIVER_GEM ,
|
||||
.load = psb_driver_load,
|
||||
.unload = psb_driver_unload,
|
||||
|
||||
.ioctls = psb_ioctls,
|
||||
.num_ioctls = DRM_ARRAY_SIZE(psb_ioctls),
|
||||
.device_is_agp = psb_driver_device_is_agp,
|
||||
.irq_preinstall = psb_irq_preinstall,
|
||||
.irq_postinstall = psb_irq_postinstall,
|
||||
.irq_uninstall = psb_irq_uninstall,
|
||||
.irq_handler = psb_irq_handler,
|
||||
.enable_vblank = psb_enable_vblank,
|
||||
.disable_vblank = psb_disable_vblank,
|
||||
.get_vblank_counter = psb_get_vblank_counter,
|
||||
.lastclose = psb_lastclose,
|
||||
.open = psb_driver_open,
|
||||
.preclose = psb_driver_preclose,
|
||||
.postclose = psb_driver_close,
|
||||
.reclaim_buffers = drm_core_reclaim_buffers,
|
||||
|
||||
.gem_init_object = psb_gem_init_object,
|
||||
.gem_free_object = psb_gem_free_object,
|
||||
.gem_vm_ops = &psb_gem_vm_ops,
|
||||
.dumb_create = psb_gem_dumb_create,
|
||||
.dumb_map_offset = psb_gem_dumb_map_gtt,
|
||||
.dumb_destroy = psb_gem_dumb_destroy,
|
||||
.fops = &psb_gem_fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = PSB_DRM_DRIVER_DATE,
|
||||
.major = PSB_DRM_DRIVER_MAJOR,
|
||||
.minor = PSB_DRM_DRIVER_MINOR,
|
||||
.patchlevel = PSB_DRM_DRIVER_PATCHLEVEL
|
||||
};
|
||||
|
||||
static struct pci_driver psb_pci_driver = {
|
||||
.name = DRIVER_NAME,
|
||||
.id_table = pciidlist,
|
||||
.probe = psb_probe,
|
||||
.remove = psb_remove,
|
||||
.driver.pm = &psb_pm_ops,
|
||||
};
|
||||
|
||||
static int psb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
|
||||
{
|
||||
return drm_get_pci_dev(pdev, ent, &driver);
|
||||
}
|
||||
|
||||
static int __init psb_init(void)
|
||||
{
|
||||
return drm_pci_init(&driver, &psb_pci_driver);
|
||||
}
|
||||
|
||||
static void __exit psb_exit(void)
|
||||
{
|
||||
drm_pci_exit(&driver, &psb_pci_driver);
|
||||
}
|
||||
|
||||
late_initcall(psb_init);
|
||||
module_exit(psb_exit);
|
||||
|
||||
MODULE_AUTHOR("Alan Cox <alan@linux.intel.com> and others");
|
||||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,956 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _PSB_DRV_H_
|
||||
#define _PSB_DRV_H_
|
||||
|
||||
#include <linux/kref.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "drm_global.h"
|
||||
#include "gem_glue.h"
|
||||
#include "gma_drm.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "gtt.h"
|
||||
#include "power.h"
|
||||
#include "oaktrail.h"
|
||||
|
||||
/* Append new drm mode definition here, align with libdrm definition */
|
||||
#define DRM_MODE_SCALE_NO_SCALE 2
|
||||
|
||||
enum {
|
||||
CHIP_PSB_8108 = 0, /* Poulsbo */
|
||||
CHIP_PSB_8109 = 1, /* Poulsbo */
|
||||
CHIP_MRST_4100 = 2, /* Moorestown/Oaktrail */
|
||||
CHIP_MFLD_0130 = 3, /* Medfield */
|
||||
};
|
||||
|
||||
#define IS_PSB(dev) (((dev)->pci_device & 0xfffe) == 0x8108)
|
||||
#define IS_MRST(dev) (((dev)->pci_device & 0xfffc) == 0x4100)
|
||||
#define IS_MFLD(dev) (((dev)->pci_device & 0xfff8) == 0x0130)
|
||||
|
||||
/*
|
||||
* Driver definitions
|
||||
*/
|
||||
|
||||
#define DRIVER_NAME "gma500"
|
||||
#define DRIVER_DESC "DRM driver for the Intel GMA500"
|
||||
|
||||
#define PSB_DRM_DRIVER_DATE "2011-06-06"
|
||||
#define PSB_DRM_DRIVER_MAJOR 1
|
||||
#define PSB_DRM_DRIVER_MINOR 0
|
||||
#define PSB_DRM_DRIVER_PATCHLEVEL 0
|
||||
|
||||
/*
|
||||
* Hardware offsets
|
||||
*/
|
||||
#define PSB_VDC_OFFSET 0x00000000
|
||||
#define PSB_VDC_SIZE 0x000080000
|
||||
#define MRST_MMIO_SIZE 0x0000C0000
|
||||
#define MDFLD_MMIO_SIZE 0x000100000
|
||||
#define PSB_SGX_SIZE 0x8000
|
||||
#define PSB_SGX_OFFSET 0x00040000
|
||||
#define MRST_SGX_OFFSET 0x00080000
|
||||
/*
|
||||
* PCI resource identifiers
|
||||
*/
|
||||
#define PSB_MMIO_RESOURCE 0
|
||||
#define PSB_GATT_RESOURCE 2
|
||||
#define PSB_GTT_RESOURCE 3
|
||||
/*
|
||||
* PCI configuration
|
||||
*/
|
||||
#define PSB_GMCH_CTRL 0x52
|
||||
#define PSB_BSM 0x5C
|
||||
#define _PSB_GMCH_ENABLED 0x4
|
||||
#define PSB_PGETBL_CTL 0x2020
|
||||
#define _PSB_PGETBL_ENABLED 0x00000001
|
||||
#define PSB_SGX_2D_SLAVE_PORT 0x4000
|
||||
|
||||
/* To get rid of */
|
||||
#define PSB_TT_PRIV0_LIMIT (256*1024*1024)
|
||||
#define PSB_TT_PRIV0_PLIMIT (PSB_TT_PRIV0_LIMIT >> PAGE_SHIFT)
|
||||
|
||||
/*
|
||||
* SGX side MMU definitions (these can probably go)
|
||||
*/
|
||||
|
||||
/*
|
||||
* Flags for external memory type field.
|
||||
*/
|
||||
#define PSB_MMU_CACHED_MEMORY 0x0001 /* Bind to MMU only */
|
||||
#define PSB_MMU_RO_MEMORY 0x0002 /* MMU RO memory */
|
||||
#define PSB_MMU_WO_MEMORY 0x0004 /* MMU WO memory */
|
||||
/*
|
||||
* PTE's and PDE's
|
||||
*/
|
||||
#define PSB_PDE_MASK 0x003FFFFF
|
||||
#define PSB_PDE_SHIFT 22
|
||||
#define PSB_PTE_SHIFT 12
|
||||
/*
|
||||
* Cache control
|
||||
*/
|
||||
#define PSB_PTE_VALID 0x0001 /* PTE / PDE valid */
|
||||
#define PSB_PTE_WO 0x0002 /* Write only */
|
||||
#define PSB_PTE_RO 0x0004 /* Read only */
|
||||
#define PSB_PTE_CACHED 0x0008 /* CPU cache coherent */
|
||||
|
||||
/*
|
||||
* VDC registers and bits
|
||||
*/
|
||||
#define PSB_MSVDX_CLOCKGATING 0x2064
|
||||
#define PSB_TOPAZ_CLOCKGATING 0x2068
|
||||
#define PSB_HWSTAM 0x2098
|
||||
#define PSB_INSTPM 0x20C0
|
||||
#define PSB_INT_IDENTITY_R 0x20A4
|
||||
#define _MDFLD_PIPEC_EVENT_FLAG (1<<2)
|
||||
#define _MDFLD_PIPEC_VBLANK_FLAG (1<<3)
|
||||
#define _PSB_DPST_PIPEB_FLAG (1<<4)
|
||||
#define _MDFLD_PIPEB_EVENT_FLAG (1<<4)
|
||||
#define _PSB_VSYNC_PIPEB_FLAG (1<<5)
|
||||
#define _PSB_DPST_PIPEA_FLAG (1<<6)
|
||||
#define _PSB_PIPEA_EVENT_FLAG (1<<6)
|
||||
#define _PSB_VSYNC_PIPEA_FLAG (1<<7)
|
||||
#define _MDFLD_MIPIA_FLAG (1<<16)
|
||||
#define _MDFLD_MIPIC_FLAG (1<<17)
|
||||
#define _PSB_IRQ_SGX_FLAG (1<<18)
|
||||
#define _PSB_IRQ_MSVDX_FLAG (1<<19)
|
||||
#define _LNC_IRQ_TOPAZ_FLAG (1<<20)
|
||||
|
||||
#define _PSB_PIPE_EVENT_FLAG (_PSB_VSYNC_PIPEA_FLAG | \
|
||||
_PSB_VSYNC_PIPEB_FLAG)
|
||||
|
||||
/* This flag includes all the display IRQ bits excepts the vblank irqs. */
|
||||
#define _MDFLD_DISP_ALL_IRQ_FLAG (_MDFLD_PIPEC_EVENT_FLAG | \
|
||||
_MDFLD_PIPEB_EVENT_FLAG | \
|
||||
_PSB_PIPEA_EVENT_FLAG | \
|
||||
_PSB_VSYNC_PIPEA_FLAG | \
|
||||
_MDFLD_MIPIA_FLAG | \
|
||||
_MDFLD_MIPIC_FLAG)
|
||||
#define PSB_INT_IDENTITY_R 0x20A4
|
||||
#define PSB_INT_MASK_R 0x20A8
|
||||
#define PSB_INT_ENABLE_R 0x20A0
|
||||
|
||||
#define _PSB_MMU_ER_MASK 0x0001FF00
|
||||
#define _PSB_MMU_ER_HOST (1 << 16)
|
||||
#define GPIOA 0x5010
|
||||
#define GPIOB 0x5014
|
||||
#define GPIOC 0x5018
|
||||
#define GPIOD 0x501c
|
||||
#define GPIOE 0x5020
|
||||
#define GPIOF 0x5024
|
||||
#define GPIOG 0x5028
|
||||
#define GPIOH 0x502c
|
||||
#define GPIO_CLOCK_DIR_MASK (1 << 0)
|
||||
#define GPIO_CLOCK_DIR_IN (0 << 1)
|
||||
#define GPIO_CLOCK_DIR_OUT (1 << 1)
|
||||
#define GPIO_CLOCK_VAL_MASK (1 << 2)
|
||||
#define GPIO_CLOCK_VAL_OUT (1 << 3)
|
||||
#define GPIO_CLOCK_VAL_IN (1 << 4)
|
||||
#define GPIO_CLOCK_PULLUP_DISABLE (1 << 5)
|
||||
#define GPIO_DATA_DIR_MASK (1 << 8)
|
||||
#define GPIO_DATA_DIR_IN (0 << 9)
|
||||
#define GPIO_DATA_DIR_OUT (1 << 9)
|
||||
#define GPIO_DATA_VAL_MASK (1 << 10)
|
||||
#define GPIO_DATA_VAL_OUT (1 << 11)
|
||||
#define GPIO_DATA_VAL_IN (1 << 12)
|
||||
#define GPIO_DATA_PULLUP_DISABLE (1 << 13)
|
||||
|
||||
#define VCLK_DIVISOR_VGA0 0x6000
|
||||
#define VCLK_DIVISOR_VGA1 0x6004
|
||||
#define VCLK_POST_DIV 0x6010
|
||||
|
||||
#define PSB_COMM_2D (PSB_ENGINE_2D << 4)
|
||||
#define PSB_COMM_3D (PSB_ENGINE_3D << 4)
|
||||
#define PSB_COMM_TA (PSB_ENGINE_TA << 4)
|
||||
#define PSB_COMM_HP (PSB_ENGINE_HP << 4)
|
||||
#define PSB_COMM_USER_IRQ (1024 >> 2)
|
||||
#define PSB_COMM_USER_IRQ_LOST (PSB_COMM_USER_IRQ + 1)
|
||||
#define PSB_COMM_FW (2048 >> 2)
|
||||
|
||||
#define PSB_UIRQ_VISTEST 1
|
||||
#define PSB_UIRQ_OOM_REPLY 2
|
||||
#define PSB_UIRQ_FIRE_TA_REPLY 3
|
||||
#define PSB_UIRQ_FIRE_RASTER_REPLY 4
|
||||
|
||||
#define PSB_2D_SIZE (256*1024*1024)
|
||||
#define PSB_MAX_RELOC_PAGES 1024
|
||||
|
||||
#define PSB_LOW_REG_OFFS 0x0204
|
||||
#define PSB_HIGH_REG_OFFS 0x0600
|
||||
|
||||
#define PSB_NUM_VBLANKS 2
|
||||
|
||||
|
||||
#define PSB_2D_SIZE (256*1024*1024)
|
||||
#define PSB_MAX_RELOC_PAGES 1024
|
||||
|
||||
#define PSB_LOW_REG_OFFS 0x0204
|
||||
#define PSB_HIGH_REG_OFFS 0x0600
|
||||
|
||||
#define PSB_NUM_VBLANKS 2
|
||||
#define PSB_WATCHDOG_DELAY (DRM_HZ * 2)
|
||||
#define PSB_LID_DELAY (DRM_HZ / 10)
|
||||
|
||||
#define MDFLD_PNW_B0 0x04
|
||||
#define MDFLD_PNW_C0 0x08
|
||||
|
||||
#define MDFLD_DSR_2D_3D_0 (1 << 0)
|
||||
#define MDFLD_DSR_2D_3D_2 (1 << 1)
|
||||
#define MDFLD_DSR_CURSOR_0 (1 << 2)
|
||||
#define MDFLD_DSR_CURSOR_2 (1 << 3)
|
||||
#define MDFLD_DSR_OVERLAY_0 (1 << 4)
|
||||
#define MDFLD_DSR_OVERLAY_2 (1 << 5)
|
||||
#define MDFLD_DSR_MIPI_CONTROL (1 << 6)
|
||||
#define MDFLD_DSR_DAMAGE_MASK_0 ((1 << 0) | (1 << 2) | (1 << 4))
|
||||
#define MDFLD_DSR_DAMAGE_MASK_2 ((1 << 1) | (1 << 3) | (1 << 5))
|
||||
#define MDFLD_DSR_2D_3D (MDFLD_DSR_2D_3D_0 | MDFLD_DSR_2D_3D_2)
|
||||
|
||||
#define MDFLD_DSR_RR 45
|
||||
#define MDFLD_DPU_ENABLE (1 << 31)
|
||||
#define MDFLD_DSR_FULLSCREEN (1 << 30)
|
||||
#define MDFLD_DSR_DELAY (DRM_HZ / MDFLD_DSR_RR)
|
||||
|
||||
#define PSB_PWR_STATE_ON 1
|
||||
#define PSB_PWR_STATE_OFF 2
|
||||
|
||||
#define PSB_PMPOLICY_NOPM 0
|
||||
#define PSB_PMPOLICY_CLOCKGATING 1
|
||||
#define PSB_PMPOLICY_POWERDOWN 2
|
||||
|
||||
#define PSB_PMSTATE_POWERUP 0
|
||||
#define PSB_PMSTATE_CLOCKGATED 1
|
||||
#define PSB_PMSTATE_POWERDOWN 2
|
||||
#define PSB_PCIx_MSI_ADDR_LOC 0x94
|
||||
#define PSB_PCIx_MSI_DATA_LOC 0x98
|
||||
|
||||
/* Medfield crystal settings */
|
||||
#define KSEL_CRYSTAL_19 1
|
||||
#define KSEL_BYPASS_19 5
|
||||
#define KSEL_BYPASS_25 6
|
||||
#define KSEL_BYPASS_83_100 7
|
||||
|
||||
struct opregion_header;
|
||||
struct opregion_acpi;
|
||||
struct opregion_swsci;
|
||||
struct opregion_asle;
|
||||
|
||||
struct psb_intel_opregion {
|
||||
struct opregion_header *header;
|
||||
struct opregion_acpi *acpi;
|
||||
struct opregion_swsci *swsci;
|
||||
struct opregion_asle *asle;
|
||||
int enabled;
|
||||
};
|
||||
|
||||
struct sdvo_device_mapping {
|
||||
u8 initialized;
|
||||
u8 dvo_port;
|
||||
u8 slave_addr;
|
||||
u8 dvo_wiring;
|
||||
u8 i2c_pin;
|
||||
u8 i2c_speed;
|
||||
u8 ddc_pin;
|
||||
};
|
||||
|
||||
struct intel_gmbus {
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_adapter *force_bit;
|
||||
u32 reg0;
|
||||
};
|
||||
|
||||
struct psb_ops;
|
||||
|
||||
#define PSB_NUM_PIPE 3
|
||||
|
||||
struct drm_psb_private {
|
||||
struct drm_device *dev;
|
||||
const struct psb_ops *ops;
|
||||
|
||||
struct psb_gtt gtt;
|
||||
|
||||
/* GTT Memory manager */
|
||||
struct psb_gtt_mm *gtt_mm;
|
||||
struct page *scratch_page;
|
||||
u32 *gtt_map;
|
||||
uint32_t stolen_base;
|
||||
void *vram_addr;
|
||||
unsigned long vram_stolen_size;
|
||||
int gtt_initialized;
|
||||
u16 gmch_ctrl; /* Saved GTT setup */
|
||||
u32 pge_ctl;
|
||||
|
||||
struct mutex gtt_mutex;
|
||||
struct resource *gtt_mem; /* Our PCI resource */
|
||||
|
||||
struct psb_mmu_driver *mmu;
|
||||
struct psb_mmu_pd *pf_pd;
|
||||
|
||||
/*
|
||||
* Register base
|
||||
*/
|
||||
|
||||
uint8_t *sgx_reg;
|
||||
uint8_t *vdc_reg;
|
||||
uint32_t gatt_free_offset;
|
||||
|
||||
/*
|
||||
* Fencing / irq.
|
||||
*/
|
||||
|
||||
uint32_t vdc_irq_mask;
|
||||
uint32_t pipestat[PSB_NUM_PIPE];
|
||||
|
||||
spinlock_t irqmask_lock;
|
||||
|
||||
/*
|
||||
* Power
|
||||
*/
|
||||
|
||||
bool suspended;
|
||||
bool display_power;
|
||||
int display_count;
|
||||
|
||||
/*
|
||||
* Modesetting
|
||||
*/
|
||||
struct psb_intel_mode_device mode_dev;
|
||||
|
||||
struct drm_crtc *plane_to_crtc_mapping[PSB_NUM_PIPE];
|
||||
struct drm_crtc *pipe_to_crtc_mapping[PSB_NUM_PIPE];
|
||||
uint32_t num_pipe;
|
||||
|
||||
/*
|
||||
* OSPM info (Power management base) (can go ?)
|
||||
*/
|
||||
uint32_t ospm_base;
|
||||
|
||||
/*
|
||||
* Sizes info
|
||||
*/
|
||||
|
||||
u32 fuse_reg_value;
|
||||
u32 video_device_fuse;
|
||||
|
||||
/* PCI revision ID for B0:D2:F0 */
|
||||
uint8_t platform_rev_id;
|
||||
|
||||
/* gmbus */
|
||||
struct intel_gmbus *gmbus;
|
||||
|
||||
/* Used by SDVO */
|
||||
int crt_ddc_pin;
|
||||
/* FIXME: The mappings should be parsed from bios but for now we can
|
||||
pretend there are no mappings available */
|
||||
struct sdvo_device_mapping sdvo_mappings[2];
|
||||
u32 hotplug_supported_mask;
|
||||
struct drm_property *broadcast_rgb_property;
|
||||
struct drm_property *force_audio_property;
|
||||
|
||||
/*
|
||||
* LVDS info
|
||||
*/
|
||||
int backlight_duty_cycle; /* restore backlight to this value */
|
||||
bool panel_wants_dither;
|
||||
struct drm_display_mode *panel_fixed_mode;
|
||||
struct drm_display_mode *lfp_lvds_vbt_mode;
|
||||
struct drm_display_mode *sdvo_lvds_vbt_mode;
|
||||
|
||||
struct bdb_lvds_backlight *lvds_bl; /* LVDS backlight info from VBT */
|
||||
struct psb_intel_i2c_chan *lvds_i2c_bus; /* FIXME: Remove this? */
|
||||
|
||||
/* Feature bits from the VBIOS */
|
||||
unsigned int int_tv_support:1;
|
||||
unsigned int lvds_dither:1;
|
||||
unsigned int lvds_vbt:1;
|
||||
unsigned int int_crt_support:1;
|
||||
unsigned int lvds_use_ssc:1;
|
||||
int lvds_ssc_freq;
|
||||
bool is_lvds_on;
|
||||
bool is_mipi_on;
|
||||
u32 mipi_ctrl_display;
|
||||
|
||||
unsigned int core_freq;
|
||||
uint32_t iLVDS_enable;
|
||||
|
||||
/* Runtime PM state */
|
||||
int rpm_enabled;
|
||||
|
||||
/* MID specific */
|
||||
struct oaktrail_vbt vbt_data;
|
||||
struct oaktrail_gct_data gct_data;
|
||||
|
||||
/* MIPI Panel type etc */
|
||||
int panel_id;
|
||||
bool dual_mipi; /* dual display - DPI & DBI */
|
||||
bool dpi_panel_on; /* The DPI panel power is on */
|
||||
bool dpi_panel_on2; /* The DPI panel power is on */
|
||||
bool dbi_panel_on; /* The DBI panel power is on */
|
||||
bool dbi_panel_on2; /* The DBI panel power is on */
|
||||
u32 dsr_fb_update; /* DSR FB update counter */
|
||||
|
||||
/* Moorestown HDMI state */
|
||||
struct oaktrail_hdmi_dev *hdmi_priv;
|
||||
|
||||
/* Moorestown pipe config register value cache */
|
||||
uint32_t pipeconf;
|
||||
uint32_t pipeconf1;
|
||||
uint32_t pipeconf2;
|
||||
|
||||
/* Moorestown plane control register value cache */
|
||||
uint32_t dspcntr;
|
||||
uint32_t dspcntr1;
|
||||
uint32_t dspcntr2;
|
||||
|
||||
/* Moorestown MM backlight cache */
|
||||
uint8_t saveBKLTCNT;
|
||||
uint8_t saveBKLTREQ;
|
||||
uint8_t saveBKLTBRTL;
|
||||
|
||||
/*
|
||||
* Register state
|
||||
*/
|
||||
uint32_t saveDSPACNTR;
|
||||
uint32_t saveDSPBCNTR;
|
||||
uint32_t savePIPEACONF;
|
||||
uint32_t savePIPEBCONF;
|
||||
uint32_t savePIPEASRC;
|
||||
uint32_t savePIPEBSRC;
|
||||
uint32_t saveFPA0;
|
||||
uint32_t saveFPA1;
|
||||
uint32_t saveDPLL_A;
|
||||
uint32_t saveDPLL_A_MD;
|
||||
uint32_t saveHTOTAL_A;
|
||||
uint32_t saveHBLANK_A;
|
||||
uint32_t saveHSYNC_A;
|
||||
uint32_t saveVTOTAL_A;
|
||||
uint32_t saveVBLANK_A;
|
||||
uint32_t saveVSYNC_A;
|
||||
uint32_t saveDSPASTRIDE;
|
||||
uint32_t saveDSPASIZE;
|
||||
uint32_t saveDSPAPOS;
|
||||
uint32_t saveDSPABASE;
|
||||
uint32_t saveDSPASURF;
|
||||
uint32_t saveDSPASTATUS;
|
||||
uint32_t saveFPB0;
|
||||
uint32_t saveFPB1;
|
||||
uint32_t saveDPLL_B;
|
||||
uint32_t saveDPLL_B_MD;
|
||||
uint32_t saveHTOTAL_B;
|
||||
uint32_t saveHBLANK_B;
|
||||
uint32_t saveHSYNC_B;
|
||||
uint32_t saveVTOTAL_B;
|
||||
uint32_t saveVBLANK_B;
|
||||
uint32_t saveVSYNC_B;
|
||||
uint32_t saveDSPBSTRIDE;
|
||||
uint32_t saveDSPBSIZE;
|
||||
uint32_t saveDSPBPOS;
|
||||
uint32_t saveDSPBBASE;
|
||||
uint32_t saveDSPBSURF;
|
||||
uint32_t saveDSPBSTATUS;
|
||||
uint32_t saveVCLK_DIVISOR_VGA0;
|
||||
uint32_t saveVCLK_DIVISOR_VGA1;
|
||||
uint32_t saveVCLK_POST_DIV;
|
||||
uint32_t saveVGACNTRL;
|
||||
uint32_t saveADPA;
|
||||
uint32_t saveLVDS;
|
||||
uint32_t saveDVOA;
|
||||
uint32_t saveDVOB;
|
||||
uint32_t saveDVOC;
|
||||
uint32_t savePP_ON;
|
||||
uint32_t savePP_OFF;
|
||||
uint32_t savePP_CONTROL;
|
||||
uint32_t savePP_CYCLE;
|
||||
uint32_t savePFIT_CONTROL;
|
||||
uint32_t savePaletteA[256];
|
||||
uint32_t savePaletteB[256];
|
||||
uint32_t saveBLC_PWM_CTL2;
|
||||
uint32_t saveBLC_PWM_CTL;
|
||||
uint32_t saveCLOCKGATING;
|
||||
uint32_t saveDSPARB;
|
||||
uint32_t saveDSPATILEOFF;
|
||||
uint32_t saveDSPBTILEOFF;
|
||||
uint32_t saveDSPAADDR;
|
||||
uint32_t saveDSPBADDR;
|
||||
uint32_t savePFIT_AUTO_RATIOS;
|
||||
uint32_t savePFIT_PGM_RATIOS;
|
||||
uint32_t savePP_ON_DELAYS;
|
||||
uint32_t savePP_OFF_DELAYS;
|
||||
uint32_t savePP_DIVISOR;
|
||||
uint32_t saveBSM;
|
||||
uint32_t saveVBT;
|
||||
uint32_t saveBCLRPAT_A;
|
||||
uint32_t saveBCLRPAT_B;
|
||||
uint32_t saveDSPALINOFF;
|
||||
uint32_t saveDSPBLINOFF;
|
||||
uint32_t savePERF_MODE;
|
||||
uint32_t saveDSPFW1;
|
||||
uint32_t saveDSPFW2;
|
||||
uint32_t saveDSPFW3;
|
||||
uint32_t saveDSPFW4;
|
||||
uint32_t saveDSPFW5;
|
||||
uint32_t saveDSPFW6;
|
||||
uint32_t saveCHICKENBIT;
|
||||
uint32_t saveDSPACURSOR_CTRL;
|
||||
uint32_t saveDSPBCURSOR_CTRL;
|
||||
uint32_t saveDSPACURSOR_BASE;
|
||||
uint32_t saveDSPBCURSOR_BASE;
|
||||
uint32_t saveDSPACURSOR_POS;
|
||||
uint32_t saveDSPBCURSOR_POS;
|
||||
uint32_t save_palette_a[256];
|
||||
uint32_t save_palette_b[256];
|
||||
uint32_t saveOV_OVADD;
|
||||
uint32_t saveOV_OGAMC0;
|
||||
uint32_t saveOV_OGAMC1;
|
||||
uint32_t saveOV_OGAMC2;
|
||||
uint32_t saveOV_OGAMC3;
|
||||
uint32_t saveOV_OGAMC4;
|
||||
uint32_t saveOV_OGAMC5;
|
||||
uint32_t saveOVC_OVADD;
|
||||
uint32_t saveOVC_OGAMC0;
|
||||
uint32_t saveOVC_OGAMC1;
|
||||
uint32_t saveOVC_OGAMC2;
|
||||
uint32_t saveOVC_OGAMC3;
|
||||
uint32_t saveOVC_OGAMC4;
|
||||
uint32_t saveOVC_OGAMC5;
|
||||
|
||||
/* MSI reg save */
|
||||
uint32_t msi_addr;
|
||||
uint32_t msi_data;
|
||||
|
||||
/* Medfield specific register save state */
|
||||
uint32_t saveHDMIPHYMISCCTL;
|
||||
uint32_t saveHDMIB_CONTROL;
|
||||
uint32_t saveDSPCCNTR;
|
||||
uint32_t savePIPECCONF;
|
||||
uint32_t savePIPECSRC;
|
||||
uint32_t saveHTOTAL_C;
|
||||
uint32_t saveHBLANK_C;
|
||||
uint32_t saveHSYNC_C;
|
||||
uint32_t saveVTOTAL_C;
|
||||
uint32_t saveVBLANK_C;
|
||||
uint32_t saveVSYNC_C;
|
||||
uint32_t saveDSPCSTRIDE;
|
||||
uint32_t saveDSPCSIZE;
|
||||
uint32_t saveDSPCPOS;
|
||||
uint32_t saveDSPCSURF;
|
||||
uint32_t saveDSPCSTATUS;
|
||||
uint32_t saveDSPCLINOFF;
|
||||
uint32_t saveDSPCTILEOFF;
|
||||
uint32_t saveDSPCCURSOR_CTRL;
|
||||
uint32_t saveDSPCCURSOR_BASE;
|
||||
uint32_t saveDSPCCURSOR_POS;
|
||||
uint32_t save_palette_c[256];
|
||||
uint32_t saveOV_OVADD_C;
|
||||
uint32_t saveOV_OGAMC0_C;
|
||||
uint32_t saveOV_OGAMC1_C;
|
||||
uint32_t saveOV_OGAMC2_C;
|
||||
uint32_t saveOV_OGAMC3_C;
|
||||
uint32_t saveOV_OGAMC4_C;
|
||||
uint32_t saveOV_OGAMC5_C;
|
||||
|
||||
/* DSI register save */
|
||||
uint32_t saveDEVICE_READY_REG;
|
||||
uint32_t saveINTR_EN_REG;
|
||||
uint32_t saveDSI_FUNC_PRG_REG;
|
||||
uint32_t saveHS_TX_TIMEOUT_REG;
|
||||
uint32_t saveLP_RX_TIMEOUT_REG;
|
||||
uint32_t saveTURN_AROUND_TIMEOUT_REG;
|
||||
uint32_t saveDEVICE_RESET_REG;
|
||||
uint32_t saveDPI_RESOLUTION_REG;
|
||||
uint32_t saveHORIZ_SYNC_PAD_COUNT_REG;
|
||||
uint32_t saveHORIZ_BACK_PORCH_COUNT_REG;
|
||||
uint32_t saveHORIZ_FRONT_PORCH_COUNT_REG;
|
||||
uint32_t saveHORIZ_ACTIVE_AREA_COUNT_REG;
|
||||
uint32_t saveVERT_SYNC_PAD_COUNT_REG;
|
||||
uint32_t saveVERT_BACK_PORCH_COUNT_REG;
|
||||
uint32_t saveVERT_FRONT_PORCH_COUNT_REG;
|
||||
uint32_t saveHIGH_LOW_SWITCH_COUNT_REG;
|
||||
uint32_t saveINIT_COUNT_REG;
|
||||
uint32_t saveMAX_RET_PAK_REG;
|
||||
uint32_t saveVIDEO_FMT_REG;
|
||||
uint32_t saveEOT_DISABLE_REG;
|
||||
uint32_t saveLP_BYTECLK_REG;
|
||||
uint32_t saveHS_LS_DBI_ENABLE_REG;
|
||||
uint32_t saveTXCLKESC_REG;
|
||||
uint32_t saveDPHY_PARAM_REG;
|
||||
uint32_t saveMIPI_CONTROL_REG;
|
||||
uint32_t saveMIPI;
|
||||
uint32_t saveMIPI_C;
|
||||
|
||||
/* DPST register save */
|
||||
uint32_t saveHISTOGRAM_INT_CONTROL_REG;
|
||||
uint32_t saveHISTOGRAM_LOGIC_CONTROL_REG;
|
||||
uint32_t savePWM_CONTROL_LOGIC;
|
||||
|
||||
/*
|
||||
* DSI info.
|
||||
*/
|
||||
void * dbi_dsr_info;
|
||||
void * dbi_dpu_info;
|
||||
void * dsi_configs[2];
|
||||
/*
|
||||
* LID-Switch
|
||||
*/
|
||||
spinlock_t lid_lock;
|
||||
struct timer_list lid_timer;
|
||||
struct psb_intel_opregion opregion;
|
||||
u32 *lid_state;
|
||||
u32 lid_last_state;
|
||||
|
||||
/*
|
||||
* Watchdog
|
||||
*/
|
||||
|
||||
uint32_t apm_reg;
|
||||
uint16_t apm_base;
|
||||
|
||||
/*
|
||||
* Used for modifying backlight from
|
||||
* xrandr -- consider removing and using HAL instead
|
||||
*/
|
||||
struct backlight_device *backlight_device;
|
||||
struct drm_property *backlight_property;
|
||||
uint32_t blc_adj1;
|
||||
uint32_t blc_adj2;
|
||||
|
||||
void *fbdev;
|
||||
|
||||
/* 2D acceleration */
|
||||
spinlock_t lock_2d;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Operations for each board type
|
||||
*/
|
||||
|
||||
struct psb_ops {
|
||||
const char *name;
|
||||
unsigned int accel_2d:1;
|
||||
int pipes; /* Number of output pipes */
|
||||
int crtcs; /* Number of CRTCs */
|
||||
int sgx_offset; /* Base offset of SGX device */
|
||||
|
||||
/* Sub functions */
|
||||
struct drm_crtc_helper_funcs const *crtc_helper;
|
||||
struct drm_crtc_funcs const *crtc_funcs;
|
||||
|
||||
/* Setup hooks */
|
||||
int (*chip_setup)(struct drm_device *dev);
|
||||
void (*chip_teardown)(struct drm_device *dev);
|
||||
|
||||
/* Display management hooks */
|
||||
int (*output_init)(struct drm_device *dev);
|
||||
/* Power management hooks */
|
||||
void (*init_pm)(struct drm_device *dev);
|
||||
int (*save_regs)(struct drm_device *dev);
|
||||
int (*restore_regs)(struct drm_device *dev);
|
||||
int (*power_up)(struct drm_device *dev);
|
||||
int (*power_down)(struct drm_device *dev);
|
||||
|
||||
void (*lvds_bl_power)(struct drm_device *dev, bool on);
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
/* Backlight */
|
||||
int (*backlight_init)(struct drm_device *dev);
|
||||
#endif
|
||||
int i2c_bus; /* I2C bus identifier for Moorestown */
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct psb_mmu_driver;
|
||||
|
||||
extern int drm_crtc_probe_output_modes(struct drm_device *dev, int, int);
|
||||
extern int drm_pick_crtcs(struct drm_device *dev);
|
||||
|
||||
static inline struct drm_psb_private *psb_priv(struct drm_device *dev)
|
||||
{
|
||||
return (struct drm_psb_private *) dev->dev_private;
|
||||
}
|
||||
|
||||
/*
|
||||
* MMU stuff.
|
||||
*/
|
||||
|
||||
extern struct psb_mmu_driver *psb_mmu_driver_init(uint8_t __iomem * registers,
|
||||
int trap_pagefaults,
|
||||
int invalid_type,
|
||||
struct drm_psb_private *dev_priv);
|
||||
extern void psb_mmu_driver_takedown(struct psb_mmu_driver *driver);
|
||||
extern struct psb_mmu_pd *psb_mmu_get_default_pd(struct psb_mmu_driver
|
||||
*driver);
|
||||
extern void psb_mmu_mirror_gtt(struct psb_mmu_pd *pd, uint32_t mmu_offset,
|
||||
uint32_t gtt_start, uint32_t gtt_pages);
|
||||
extern struct psb_mmu_pd *psb_mmu_alloc_pd(struct psb_mmu_driver *driver,
|
||||
int trap_pagefaults,
|
||||
int invalid_type);
|
||||
extern void psb_mmu_free_pagedir(struct psb_mmu_pd *pd);
|
||||
extern void psb_mmu_flush(struct psb_mmu_driver *driver, int rc_prot);
|
||||
extern void psb_mmu_remove_pfn_sequence(struct psb_mmu_pd *pd,
|
||||
unsigned long address,
|
||||
uint32_t num_pages);
|
||||
extern int psb_mmu_insert_pfn_sequence(struct psb_mmu_pd *pd,
|
||||
uint32_t start_pfn,
|
||||
unsigned long address,
|
||||
uint32_t num_pages, int type);
|
||||
extern int psb_mmu_virtual_to_pfn(struct psb_mmu_pd *pd, uint32_t virtual,
|
||||
unsigned long *pfn);
|
||||
|
||||
/*
|
||||
* Enable / disable MMU for different requestors.
|
||||
*/
|
||||
|
||||
|
||||
extern void psb_mmu_set_pd_context(struct psb_mmu_pd *pd, int hw_context);
|
||||
extern int psb_mmu_insert_pages(struct psb_mmu_pd *pd, struct page **pages,
|
||||
unsigned long address, uint32_t num_pages,
|
||||
uint32_t desired_tile_stride,
|
||||
uint32_t hw_tile_stride, int type);
|
||||
extern void psb_mmu_remove_pages(struct psb_mmu_pd *pd,
|
||||
unsigned long address, uint32_t num_pages,
|
||||
uint32_t desired_tile_stride,
|
||||
uint32_t hw_tile_stride);
|
||||
/*
|
||||
*psb_irq.c
|
||||
*/
|
||||
|
||||
extern irqreturn_t psb_irq_handler(DRM_IRQ_ARGS);
|
||||
extern int psb_irq_enable_dpst(struct drm_device *dev);
|
||||
extern int psb_irq_disable_dpst(struct drm_device *dev);
|
||||
extern void psb_irq_preinstall(struct drm_device *dev);
|
||||
extern int psb_irq_postinstall(struct drm_device *dev);
|
||||
extern void psb_irq_uninstall(struct drm_device *dev);
|
||||
extern void psb_irq_turn_on_dpst(struct drm_device *dev);
|
||||
extern void psb_irq_turn_off_dpst(struct drm_device *dev);
|
||||
|
||||
extern void psb_irq_uninstall_islands(struct drm_device *dev, int hw_islands);
|
||||
extern int psb_vblank_wait2(struct drm_device *dev, unsigned int *sequence);
|
||||
extern int psb_vblank_wait(struct drm_device *dev, unsigned int *sequence);
|
||||
extern int psb_enable_vblank(struct drm_device *dev, int crtc);
|
||||
extern void psb_disable_vblank(struct drm_device *dev, int crtc);
|
||||
void
|
||||
psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask);
|
||||
|
||||
void
|
||||
psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask);
|
||||
|
||||
extern u32 psb_get_vblank_counter(struct drm_device *dev, int crtc);
|
||||
|
||||
/*
|
||||
* intel_opregion.c
|
||||
*/
|
||||
extern int gma_intel_opregion_init(struct drm_device *dev);
|
||||
extern int gma_intel_opregion_exit(struct drm_device *dev);
|
||||
|
||||
/*
|
||||
* framebuffer.c
|
||||
*/
|
||||
extern int psbfb_probed(struct drm_device *dev);
|
||||
extern int psbfb_remove(struct drm_device *dev,
|
||||
struct drm_framebuffer *fb);
|
||||
/*
|
||||
* accel_2d.c
|
||||
*/
|
||||
extern void psbfb_copyarea(struct fb_info *info,
|
||||
const struct fb_copyarea *region);
|
||||
extern int psbfb_sync(struct fb_info *info);
|
||||
extern void psb_spank(struct drm_psb_private *dev_priv);
|
||||
|
||||
/*
|
||||
* psb_reset.c
|
||||
*/
|
||||
|
||||
extern void psb_lid_timer_init(struct drm_psb_private *dev_priv);
|
||||
extern void psb_lid_timer_takedown(struct drm_psb_private *dev_priv);
|
||||
extern void psb_print_pagefault(struct drm_psb_private *dev_priv);
|
||||
|
||||
/* modesetting */
|
||||
extern void psb_modeset_init(struct drm_device *dev);
|
||||
extern void psb_modeset_cleanup(struct drm_device *dev);
|
||||
extern int psb_fbdev_init(struct drm_device *dev);
|
||||
|
||||
/* backlight.c */
|
||||
int gma_backlight_init(struct drm_device *dev);
|
||||
void gma_backlight_exit(struct drm_device *dev);
|
||||
|
||||
/* oaktrail_crtc.c */
|
||||
extern const struct drm_crtc_helper_funcs oaktrail_helper_funcs;
|
||||
|
||||
/* oaktrail_lvds.c */
|
||||
extern void oaktrail_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
|
||||
/* psb_intel_display.c */
|
||||
extern const struct drm_crtc_helper_funcs psb_intel_helper_funcs;
|
||||
extern const struct drm_crtc_funcs psb_intel_crtc_funcs;
|
||||
|
||||
/* psb_intel_lvds.c */
|
||||
extern const struct drm_connector_helper_funcs
|
||||
psb_intel_lvds_connector_helper_funcs;
|
||||
extern const struct drm_connector_funcs psb_intel_lvds_connector_funcs;
|
||||
|
||||
/* gem.c */
|
||||
extern int psb_gem_init_object(struct drm_gem_object *obj);
|
||||
extern void psb_gem_free_object(struct drm_gem_object *obj);
|
||||
extern int psb_gem_get_aperture(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
extern int psb_gem_dumb_create(struct drm_file *file, struct drm_device *dev,
|
||||
struct drm_mode_create_dumb *args);
|
||||
extern int psb_gem_dumb_destroy(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle);
|
||||
extern int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
|
||||
uint32_t handle, uint64_t *offset);
|
||||
extern int psb_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||
extern int psb_gem_create_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
extern int psb_gem_mmap_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
/* psb_device.c */
|
||||
extern const struct psb_ops psb_chip_ops;
|
||||
|
||||
/* oaktrail_device.c */
|
||||
extern const struct psb_ops oaktrail_chip_ops;
|
||||
|
||||
/* cdv_device.c */
|
||||
extern const struct psb_ops cdv_chip_ops;
|
||||
|
||||
/*
|
||||
* Debug print bits setting
|
||||
*/
|
||||
#define PSB_D_GENERAL (1 << 0)
|
||||
#define PSB_D_INIT (1 << 1)
|
||||
#define PSB_D_IRQ (1 << 2)
|
||||
#define PSB_D_ENTRY (1 << 3)
|
||||
/* debug the get H/V BP/FP count */
|
||||
#define PSB_D_HV (1 << 4)
|
||||
#define PSB_D_DBI_BF (1 << 5)
|
||||
#define PSB_D_PM (1 << 6)
|
||||
#define PSB_D_RENDER (1 << 7)
|
||||
#define PSB_D_REG (1 << 8)
|
||||
#define PSB_D_MSVDX (1 << 9)
|
||||
#define PSB_D_TOPAZ (1 << 10)
|
||||
|
||||
extern int drm_psb_no_fb;
|
||||
extern int drm_idle_check_interval;
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
|
||||
static inline u32 MRST_MSG_READ32(uint port, uint offset)
|
||||
{
|
||||
int mcr = (0xD0<<24) | (port << 16) | (offset << 8);
|
||||
uint32_t ret_val = 0;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
pci_write_config_dword(pci_root, 0xD0, mcr);
|
||||
pci_read_config_dword(pci_root, 0xD4, &ret_val);
|
||||
pci_dev_put(pci_root);
|
||||
return ret_val;
|
||||
}
|
||||
static inline void MRST_MSG_WRITE32(uint port, uint offset, u32 value)
|
||||
{
|
||||
int mcr = (0xE0<<24) | (port << 16) | (offset << 8) | 0xF0;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
pci_write_config_dword(pci_root, 0xD4, value);
|
||||
pci_write_config_dword(pci_root, 0xD0, mcr);
|
||||
pci_dev_put(pci_root);
|
||||
}
|
||||
static inline u32 MDFLD_MSG_READ32(uint port, uint offset)
|
||||
{
|
||||
int mcr = (0x10<<24) | (port << 16) | (offset << 8);
|
||||
uint32_t ret_val = 0;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
pci_write_config_dword(pci_root, 0xD0, mcr);
|
||||
pci_read_config_dword(pci_root, 0xD4, &ret_val);
|
||||
pci_dev_put(pci_root);
|
||||
return ret_val;
|
||||
}
|
||||
static inline void MDFLD_MSG_WRITE32(uint port, uint offset, u32 value)
|
||||
{
|
||||
int mcr = (0x11<<24) | (port << 16) | (offset << 8) | 0xF0;
|
||||
struct pci_dev *pci_root = pci_get_bus_and_slot(0, 0);
|
||||
pci_write_config_dword(pci_root, 0xD4, value);
|
||||
pci_write_config_dword(pci_root, 0xD0, mcr);
|
||||
pci_dev_put(pci_root);
|
||||
}
|
||||
|
||||
static inline uint32_t REGISTER_READ(struct drm_device *dev, uint32_t reg)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
return ioread32(dev_priv->vdc_reg + reg);
|
||||
}
|
||||
|
||||
#define REG_READ(reg) REGISTER_READ(dev, (reg))
|
||||
|
||||
static inline void REGISTER_WRITE(struct drm_device *dev, uint32_t reg,
|
||||
uint32_t val)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
iowrite32((val), dev_priv->vdc_reg + (reg));
|
||||
}
|
||||
|
||||
#define REG_WRITE(reg, val) REGISTER_WRITE(dev, (reg), (val))
|
||||
|
||||
static inline void REGISTER_WRITE16(struct drm_device *dev,
|
||||
uint32_t reg, uint32_t val)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
iowrite16((val), dev_priv->vdc_reg + (reg));
|
||||
}
|
||||
|
||||
#define REG_WRITE16(reg, val) REGISTER_WRITE16(dev, (reg), (val))
|
||||
|
||||
static inline void REGISTER_WRITE8(struct drm_device *dev,
|
||||
uint32_t reg, uint32_t val)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
iowrite8((val), dev_priv->vdc_reg + (reg));
|
||||
}
|
||||
|
||||
#define REG_WRITE8(reg, val) REGISTER_WRITE8(dev, (reg), (val))
|
||||
|
||||
#define PSB_WVDC32(_val, _offs) iowrite32(_val, dev_priv->vdc_reg + (_offs))
|
||||
#define PSB_RVDC32(_offs) ioread32(dev_priv->vdc_reg + (_offs))
|
||||
|
||||
/* #define TRAP_SGX_PM_FAULT 1 */
|
||||
#ifdef TRAP_SGX_PM_FAULT
|
||||
#define PSB_RSGX32(_offs) \
|
||||
({ \
|
||||
if (inl(dev_priv->apm_base + PSB_APM_STS) & 0x3) { \
|
||||
printk(KERN_ERR \
|
||||
"access sgx when it's off!! (READ) %s, %d\n", \
|
||||
__FILE__, __LINE__); \
|
||||
melay(1000); \
|
||||
} \
|
||||
ioread32(dev_priv->sgx_reg + (_offs)); \
|
||||
})
|
||||
#else
|
||||
#define PSB_RSGX32(_offs) ioread32(dev_priv->sgx_reg + (_offs))
|
||||
#endif
|
||||
#define PSB_WSGX32(_val, _offs) iowrite32(_val, dev_priv->sgx_reg + (_offs))
|
||||
|
||||
#define MSVDX_REG_DUMP 0
|
||||
|
||||
#define PSB_WMSVDX32(_val, _offs) iowrite32(_val, dev_priv->msvdx_reg + (_offs))
|
||||
#define PSB_RMSVDX32(_offs) ioread32(dev_priv->msvdx_reg + (_offs))
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,28 @@
|
|||
/* copyright (c) 2008, Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
|
||||
#ifndef _INTEL_DISPLAY_H_
|
||||
#define _INTEL_DISPLAY_H_
|
||||
|
||||
bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type);
|
||||
void psb_intel_crtc_gamma_set(struct drm_crtc *crtc, u16 *red,
|
||||
u16 *green, u16 *blue, uint32_t type, uint32_t size);
|
||||
void psb_intel_crtc_destroy(struct drm_crtc *crtc);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,289 @@
|
|||
/*
|
||||
* Copyright (c) 2009-2011, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __INTEL_DRV_H__
|
||||
#define __INTEL_DRV_H__
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <linux/gpio.h>
|
||||
|
||||
/*
|
||||
* Display related stuff
|
||||
*/
|
||||
|
||||
/* store information about an Ixxx DVO */
|
||||
/* The i830->i865 use multiple DVOs with multiple i2cs */
|
||||
/* the i915, i945 have a single sDVO i2c bus - which is different */
|
||||
#define MAX_OUTPUTS 6
|
||||
/* maximum connectors per crtcs in the mode set */
|
||||
#define INTELFB_CONN_LIMIT 4
|
||||
|
||||
#define INTEL_I2C_BUS_DVO 1
|
||||
#define INTEL_I2C_BUS_SDVO 2
|
||||
|
||||
/* Intel Pipe Clone Bit */
|
||||
#define INTEL_HDMIB_CLONE_BIT 1
|
||||
#define INTEL_HDMIC_CLONE_BIT 2
|
||||
#define INTEL_HDMID_CLONE_BIT 3
|
||||
#define INTEL_HDMIE_CLONE_BIT 4
|
||||
#define INTEL_HDMIF_CLONE_BIT 5
|
||||
#define INTEL_SDVO_NON_TV_CLONE_BIT 6
|
||||
#define INTEL_SDVO_TV_CLONE_BIT 7
|
||||
#define INTEL_SDVO_LVDS_CLONE_BIT 8
|
||||
#define INTEL_ANALOG_CLONE_BIT 9
|
||||
#define INTEL_TV_CLONE_BIT 10
|
||||
#define INTEL_DP_B_CLONE_BIT 11
|
||||
#define INTEL_DP_C_CLONE_BIT 12
|
||||
#define INTEL_DP_D_CLONE_BIT 13
|
||||
#define INTEL_LVDS_CLONE_BIT 14
|
||||
#define INTEL_DVO_TMDS_CLONE_BIT 15
|
||||
#define INTEL_DVO_LVDS_CLONE_BIT 16
|
||||
#define INTEL_EDP_CLONE_BIT 17
|
||||
|
||||
/* these are outputs from the chip - integrated only
|
||||
* external chips are via DVO or SDVO output */
|
||||
#define INTEL_OUTPUT_UNUSED 0
|
||||
#define INTEL_OUTPUT_ANALOG 1
|
||||
#define INTEL_OUTPUT_DVO 2
|
||||
#define INTEL_OUTPUT_SDVO 3
|
||||
#define INTEL_OUTPUT_LVDS 4
|
||||
#define INTEL_OUTPUT_TVOUT 5
|
||||
#define INTEL_OUTPUT_HDMI 6
|
||||
#define INTEL_OUTPUT_MIPI 7
|
||||
#define INTEL_OUTPUT_MIPI2 8
|
||||
|
||||
#define INTEL_DVO_CHIP_NONE 0
|
||||
#define INTEL_DVO_CHIP_LVDS 1
|
||||
#define INTEL_DVO_CHIP_TMDS 2
|
||||
#define INTEL_DVO_CHIP_TVOUT 4
|
||||
|
||||
#define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0)
|
||||
#define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT)
|
||||
|
||||
static inline void
|
||||
psb_intel_mode_set_pixel_multiplier(struct drm_display_mode *mode,
|
||||
int multiplier)
|
||||
{
|
||||
mode->clock *= multiplier;
|
||||
mode->private_flags |= multiplier;
|
||||
}
|
||||
|
||||
static inline int
|
||||
psb_intel_mode_get_pixel_multiplier(const struct drm_display_mode *mode)
|
||||
{
|
||||
return (mode->private_flags & INTEL_MODE_PIXEL_MULTIPLIER_MASK)
|
||||
>> INTEL_MODE_PIXEL_MULTIPLIER_SHIFT;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Hold information useally put on the device driver privates here,
|
||||
* since it needs to be shared across multiple of devices drivers privates.
|
||||
*/
|
||||
struct psb_intel_mode_device {
|
||||
|
||||
/*
|
||||
* Abstracted memory manager operations
|
||||
*/
|
||||
size_t(*bo_offset) (struct drm_device *dev, void *bo);
|
||||
|
||||
/*
|
||||
* Cursor (Can go ?)
|
||||
*/
|
||||
int cursor_needs_physical;
|
||||
|
||||
/*
|
||||
* LVDS info
|
||||
*/
|
||||
int backlight_duty_cycle; /* restore backlight to this value */
|
||||
bool panel_wants_dither;
|
||||
struct drm_display_mode *panel_fixed_mode;
|
||||
struct drm_display_mode *panel_fixed_mode2;
|
||||
struct drm_display_mode *vbt_mode; /* if any */
|
||||
|
||||
uint32_t saveBLC_PWM_CTL;
|
||||
};
|
||||
|
||||
struct psb_intel_i2c_chan {
|
||||
/* for getting at dev. private (mmio etc.) */
|
||||
struct drm_device *drm_dev;
|
||||
u32 reg; /* GPIO reg */
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_algo_bit_data algo;
|
||||
u8 slave_addr;
|
||||
};
|
||||
|
||||
struct psb_intel_encoder {
|
||||
struct drm_encoder base;
|
||||
int type;
|
||||
bool needs_tv_clock;
|
||||
void (*hot_plug)(struct psb_intel_encoder *);
|
||||
int crtc_mask;
|
||||
int clone_mask;
|
||||
void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */
|
||||
|
||||
/* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's
|
||||
own set of output privates */
|
||||
struct psb_intel_i2c_chan *i2c_bus;
|
||||
struct psb_intel_i2c_chan *ddc_bus;
|
||||
};
|
||||
|
||||
struct psb_intel_connector {
|
||||
struct drm_connector base;
|
||||
struct psb_intel_encoder *encoder;
|
||||
};
|
||||
|
||||
struct psb_intel_crtc_state {
|
||||
uint32_t saveDSPCNTR;
|
||||
uint32_t savePIPECONF;
|
||||
uint32_t savePIPESRC;
|
||||
uint32_t saveDPLL;
|
||||
uint32_t saveFP0;
|
||||
uint32_t saveFP1;
|
||||
uint32_t saveHTOTAL;
|
||||
uint32_t saveHBLANK;
|
||||
uint32_t saveHSYNC;
|
||||
uint32_t saveVTOTAL;
|
||||
uint32_t saveVBLANK;
|
||||
uint32_t saveVSYNC;
|
||||
uint32_t saveDSPSTRIDE;
|
||||
uint32_t saveDSPSIZE;
|
||||
uint32_t saveDSPPOS;
|
||||
uint32_t saveDSPBASE;
|
||||
uint32_t savePalette[256];
|
||||
};
|
||||
|
||||
struct psb_intel_crtc {
|
||||
struct drm_crtc base;
|
||||
int pipe;
|
||||
int plane;
|
||||
uint32_t cursor_addr;
|
||||
u8 lut_r[256], lut_g[256], lut_b[256];
|
||||
u8 lut_adj[256];
|
||||
struct psb_intel_framebuffer *fbdev_fb;
|
||||
/* a mode_set for fbdev users on this crtc */
|
||||
struct drm_mode_set mode_set;
|
||||
|
||||
/* GEM object that holds our cursor */
|
||||
struct drm_gem_object *cursor_obj;
|
||||
|
||||
struct drm_display_mode saved_mode;
|
||||
struct drm_display_mode saved_adjusted_mode;
|
||||
|
||||
struct psb_intel_mode_device *mode_dev;
|
||||
|
||||
/*crtc mode setting flags*/
|
||||
u32 mode_flags;
|
||||
|
||||
/* Saved Crtc HW states */
|
||||
struct psb_intel_crtc_state *crtc_state;
|
||||
};
|
||||
|
||||
#define to_psb_intel_crtc(x) \
|
||||
container_of(x, struct psb_intel_crtc, base)
|
||||
#define to_psb_intel_connector(x) \
|
||||
container_of(x, struct psb_intel_connector, base)
|
||||
#define to_psb_intel_encoder(x) \
|
||||
container_of(x, struct psb_intel_encoder, base)
|
||||
#define to_psb_intel_framebuffer(x) \
|
||||
container_of(x, struct psb_intel_framebuffer, base)
|
||||
|
||||
struct psb_intel_i2c_chan *psb_intel_i2c_create(struct drm_device *dev,
|
||||
const u32 reg, const char *name);
|
||||
void psb_intel_i2c_destroy(struct psb_intel_i2c_chan *chan);
|
||||
int psb_intel_ddc_get_modes(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter);
|
||||
extern bool psb_intel_ddc_probe(struct i2c_adapter *adapter);
|
||||
|
||||
extern void psb_intel_crtc_init(struct drm_device *dev, int pipe,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
extern void psb_intel_crt_init(struct drm_device *dev);
|
||||
extern bool psb_intel_sdvo_init(struct drm_device *dev, int output_device);
|
||||
extern void psb_intel_dvo_init(struct drm_device *dev);
|
||||
extern void psb_intel_tv_init(struct drm_device *dev);
|
||||
extern void psb_intel_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
extern void psb_intel_lvds_set_brightness(struct drm_device *dev, int level);
|
||||
extern void oaktrail_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
extern void oaktrail_wait_for_INTR_PKT_SENT(struct drm_device *dev);
|
||||
extern void oaktrail_dsi_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev);
|
||||
extern void mid_dsi_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev, int dsi_num);
|
||||
|
||||
extern void psb_intel_crtc_load_lut(struct drm_crtc *crtc);
|
||||
extern void psb_intel_encoder_prepare(struct drm_encoder *encoder);
|
||||
extern void psb_intel_encoder_commit(struct drm_encoder *encoder);
|
||||
extern void psb_intel_encoder_destroy(struct drm_encoder *encoder);
|
||||
|
||||
static inline struct psb_intel_encoder *psb_intel_attached_encoder(
|
||||
struct drm_connector *connector)
|
||||
{
|
||||
return to_psb_intel_connector(connector)->encoder;
|
||||
}
|
||||
|
||||
extern void psb_intel_connector_attach_encoder(
|
||||
struct psb_intel_connector *connector,
|
||||
struct psb_intel_encoder *encoder);
|
||||
|
||||
extern struct drm_encoder *psb_intel_best_encoder(struct drm_connector
|
||||
*connector);
|
||||
|
||||
extern struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev,
|
||||
struct drm_crtc *crtc);
|
||||
extern void psb_intel_wait_for_vblank(struct drm_device *dev);
|
||||
extern int psb_intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
extern struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev,
|
||||
int pipe);
|
||||
extern struct drm_connector *psb_intel_sdvo_find(struct drm_device *dev,
|
||||
int sdvoB);
|
||||
extern int psb_intel_sdvo_supports_hotplug(struct drm_connector *connector);
|
||||
extern void psb_intel_sdvo_set_hotplug(struct drm_connector *connector,
|
||||
int enable);
|
||||
extern int intelfb_probe(struct drm_device *dev);
|
||||
extern int intelfb_remove(struct drm_device *dev,
|
||||
struct drm_framebuffer *fb);
|
||||
extern struct drm_framebuffer *psb_intel_framebuffer_create(struct drm_device
|
||||
*dev, struct
|
||||
drm_mode_fb_cmd
|
||||
*mode_cmd,
|
||||
void *mm_private);
|
||||
extern bool psb_intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
extern int psb_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode);
|
||||
extern int psb_intel_lvds_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
uint64_t value);
|
||||
extern void psb_intel_lvds_destroy(struct drm_connector *connector);
|
||||
extern const struct drm_encoder_funcs psb_intel_lvds_enc_funcs;
|
||||
|
||||
/* intel_gmbus.c */
|
||||
extern void gma_intel_i2c_reset(struct drm_device *dev);
|
||||
extern int gma_intel_setup_gmbus(struct drm_device *dev);
|
||||
extern void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed);
|
||||
extern void gma_intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit);
|
||||
extern void gma_intel_teardown_gmbus(struct drm_device *dev);
|
||||
|
||||
#endif /* __INTEL_DRV_H__ */
|
|
@ -0,0 +1,868 @@
|
|||
/*
|
||||
* Copyright © 2006-2007 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
* Dave Airlie <airlied@linux.ie>
|
||||
* Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <drm/drmP.h>
|
||||
|
||||
#include "intel_bios.h"
|
||||
#include "psb_drv.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "power.h"
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
/*
|
||||
* LVDS I2C backlight control macros
|
||||
*/
|
||||
#define BRIGHTNESS_MAX_LEVEL 100
|
||||
#define BRIGHTNESS_MASK 0xFF
|
||||
#define BLC_I2C_TYPE 0x01
|
||||
#define BLC_PWM_TYPT 0x02
|
||||
|
||||
#define BLC_POLARITY_NORMAL 0
|
||||
#define BLC_POLARITY_INVERSE 1
|
||||
|
||||
#define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE)
|
||||
#define PSB_BLC_MIN_PWM_REG_FREQ (0x2)
|
||||
#define PSB_BLC_PWM_PRECISION_FACTOR (10)
|
||||
#define PSB_BACKLIGHT_PWM_CTL_SHIFT (16)
|
||||
#define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE)
|
||||
|
||||
struct psb_intel_lvds_priv {
|
||||
/*
|
||||
* Saved LVDO output states
|
||||
*/
|
||||
uint32_t savePP_ON;
|
||||
uint32_t savePP_OFF;
|
||||
uint32_t saveLVDS;
|
||||
uint32_t savePP_CONTROL;
|
||||
uint32_t savePP_CYCLE;
|
||||
uint32_t savePFIT_CONTROL;
|
||||
uint32_t savePFIT_PGM_RATIOS;
|
||||
uint32_t saveBLC_PWM_CTL;
|
||||
|
||||
struct psb_intel_i2c_chan *i2c_bus;
|
||||
struct psb_intel_i2c_chan *ddc_bus;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Returns the maximum level of the backlight duty cycle field.
|
||||
*/
|
||||
static u32 psb_intel_lvds_get_max_backlight(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 ret;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
ret = REG_READ(BLC_PWM_CTL);
|
||||
gma_power_end(dev);
|
||||
} else /* Powered off, use the saved value */
|
||||
ret = dev_priv->saveBLC_PWM_CTL;
|
||||
|
||||
/* Top 15bits hold the frequency mask */
|
||||
ret = (ret & BACKLIGHT_MODULATION_FREQ_MASK) >>
|
||||
BACKLIGHT_MODULATION_FREQ_SHIFT;
|
||||
|
||||
ret *= 2; /* Return a 16bit range as needed for setting */
|
||||
if (ret == 0)
|
||||
dev_err(dev->dev, "BL bug: Reg %08x save %08X\n",
|
||||
REG_READ(BLC_PWM_CTL), dev_priv->saveBLC_PWM_CTL);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set LVDS backlight level by I2C command
|
||||
*
|
||||
* FIXME: at some point we need to both track this for PM and also
|
||||
* disable runtime pm on MRST if the brightness is nil (ie blanked)
|
||||
*/
|
||||
static int psb_lvds_i2c_set_brightness(struct drm_device *dev,
|
||||
unsigned int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *)dev->dev_private;
|
||||
|
||||
struct psb_intel_i2c_chan *lvds_i2c_bus = dev_priv->lvds_i2c_bus;
|
||||
u8 out_buf[2];
|
||||
unsigned int blc_i2c_brightness;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = lvds_i2c_bus->slave_addr,
|
||||
.flags = 0,
|
||||
.len = 2,
|
||||
.buf = out_buf,
|
||||
}
|
||||
};
|
||||
|
||||
blc_i2c_brightness = BRIGHTNESS_MASK & ((unsigned int)level *
|
||||
BRIGHTNESS_MASK /
|
||||
BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE)
|
||||
blc_i2c_brightness = BRIGHTNESS_MASK - blc_i2c_brightness;
|
||||
|
||||
out_buf[0] = dev_priv->lvds_bl->brightnesscmd;
|
||||
out_buf[1] = (u8)blc_i2c_brightness;
|
||||
|
||||
if (i2c_transfer(&lvds_i2c_bus->adapter, msgs, 1) == 1) {
|
||||
dev_dbg(dev->dev, "I2C set brightness.(command, value) (%d, %d)\n",
|
||||
dev_priv->lvds_bl->brightnesscmd,
|
||||
blc_i2c_brightness);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dev_err(dev->dev, "I2C transfer error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int psb_lvds_pwm_set_brightness(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *)dev->dev_private;
|
||||
|
||||
u32 max_pwm_blc;
|
||||
u32 blc_pwm_duty_cycle;
|
||||
|
||||
max_pwm_blc = psb_intel_lvds_get_max_backlight(dev);
|
||||
|
||||
/*BLC_PWM_CTL Should be initiated while backlight device init*/
|
||||
BUG_ON(max_pwm_blc == 0);
|
||||
|
||||
blc_pwm_duty_cycle = level * max_pwm_blc / BRIGHTNESS_MAX_LEVEL;
|
||||
|
||||
if (dev_priv->lvds_bl->pol == BLC_POLARITY_INVERSE)
|
||||
blc_pwm_duty_cycle = max_pwm_blc - blc_pwm_duty_cycle;
|
||||
|
||||
blc_pwm_duty_cycle &= PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR;
|
||||
REG_WRITE(BLC_PWM_CTL,
|
||||
(max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) |
|
||||
(blc_pwm_duty_cycle));
|
||||
|
||||
dev_info(dev->dev, "Backlight lvds set brightness %08x\n",
|
||||
(max_pwm_blc << PSB_BACKLIGHT_PWM_CTL_SHIFT) |
|
||||
(blc_pwm_duty_cycle));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set LVDS backlight level either by I2C or PWM
|
||||
*/
|
||||
void psb_intel_lvds_set_brightness(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
|
||||
dev_dbg(dev->dev, "backlight level is %d\n", level);
|
||||
|
||||
if (!dev_priv->lvds_bl) {
|
||||
dev_err(dev->dev, "NO LVDS backlight info\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev_priv->lvds_bl->type == BLC_I2C_TYPE)
|
||||
psb_lvds_i2c_set_brightness(dev, level);
|
||||
else
|
||||
psb_lvds_pwm_set_brightness(dev, level);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the backlight level.
|
||||
*
|
||||
* level: backlight level, from 0 to psb_intel_lvds_get_max_backlight().
|
||||
*/
|
||||
static void psb_intel_lvds_set_backlight(struct drm_device *dev, int level)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 blc_pwm_ctl;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
blc_pwm_ctl = REG_READ(BLC_PWM_CTL);
|
||||
blc_pwm_ctl &= ~BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
REG_WRITE(BLC_PWM_CTL,
|
||||
(blc_pwm_ctl |
|
||||
(level << BACKLIGHT_DUTY_CYCLE_SHIFT)));
|
||||
dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
|
||||
(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
|
||||
gma_power_end(dev);
|
||||
} else {
|
||||
blc_pwm_ctl = dev_priv->saveBLC_PWM_CTL &
|
||||
~BACKLIGHT_DUTY_CYCLE_MASK;
|
||||
dev_priv->saveBLC_PWM_CTL = (blc_pwm_ctl |
|
||||
(level << BACKLIGHT_DUTY_CYCLE_SHIFT));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Sets the power state for the panel.
|
||||
*/
|
||||
static void psb_intel_lvds_set_power(struct drm_device *dev, bool on)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
u32 pp_status;
|
||||
|
||||
if (!gma_power_begin(dev, true)) {
|
||||
dev_err(dev->dev, "set power, chip off!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (on) {
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
|
||||
POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & PP_ON) == 0);
|
||||
|
||||
psb_intel_lvds_set_backlight(dev,
|
||||
mode_dev->backlight_duty_cycle);
|
||||
} else {
|
||||
psb_intel_lvds_set_backlight(dev, 0);
|
||||
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
|
||||
~POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while (pp_status & PP_ON);
|
||||
}
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void psb_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
psb_intel_lvds_set_power(dev, true);
|
||||
else
|
||||
psb_intel_lvds_set_power(dev, false);
|
||||
|
||||
/* XXX: We never power down the LVDS pairs. */
|
||||
}
|
||||
|
||||
static void psb_intel_lvds_save(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *)dev->dev_private;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_lvds_priv *lvds_priv =
|
||||
(struct psb_intel_lvds_priv *)psb_intel_encoder->dev_priv;
|
||||
|
||||
lvds_priv->savePP_ON = REG_READ(LVDSPP_ON);
|
||||
lvds_priv->savePP_OFF = REG_READ(LVDSPP_OFF);
|
||||
lvds_priv->saveLVDS = REG_READ(LVDS);
|
||||
lvds_priv->savePP_CONTROL = REG_READ(PP_CONTROL);
|
||||
lvds_priv->savePP_CYCLE = REG_READ(PP_CYCLE);
|
||||
/*lvds_priv->savePP_DIVISOR = REG_READ(PP_DIVISOR);*/
|
||||
lvds_priv->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
|
||||
lvds_priv->savePFIT_CONTROL = REG_READ(PFIT_CONTROL);
|
||||
lvds_priv->savePFIT_PGM_RATIOS = REG_READ(PFIT_PGM_RATIOS);
|
||||
|
||||
/*TODO: move backlight_duty_cycle to psb_intel_lvds_priv*/
|
||||
dev_priv->backlight_duty_cycle = (dev_priv->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_DUTY_CYCLE_MASK);
|
||||
|
||||
/*
|
||||
* If the light is off at server startup,
|
||||
* just make it full brightness
|
||||
*/
|
||||
if (dev_priv->backlight_duty_cycle == 0)
|
||||
dev_priv->backlight_duty_cycle =
|
||||
psb_intel_lvds_get_max_backlight(dev);
|
||||
|
||||
dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
|
||||
lvds_priv->savePP_ON,
|
||||
lvds_priv->savePP_OFF,
|
||||
lvds_priv->saveLVDS,
|
||||
lvds_priv->savePP_CONTROL,
|
||||
lvds_priv->savePP_CYCLE,
|
||||
lvds_priv->saveBLC_PWM_CTL);
|
||||
}
|
||||
|
||||
static void psb_intel_lvds_restore(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
u32 pp_status;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_lvds_priv *lvds_priv =
|
||||
(struct psb_intel_lvds_priv *)psb_intel_encoder->dev_priv;
|
||||
|
||||
dev_dbg(dev->dev, "(0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x)\n",
|
||||
lvds_priv->savePP_ON,
|
||||
lvds_priv->savePP_OFF,
|
||||
lvds_priv->saveLVDS,
|
||||
lvds_priv->savePP_CONTROL,
|
||||
lvds_priv->savePP_CYCLE,
|
||||
lvds_priv->saveBLC_PWM_CTL);
|
||||
|
||||
REG_WRITE(BLC_PWM_CTL, lvds_priv->saveBLC_PWM_CTL);
|
||||
REG_WRITE(PFIT_CONTROL, lvds_priv->savePFIT_CONTROL);
|
||||
REG_WRITE(PFIT_PGM_RATIOS, lvds_priv->savePFIT_PGM_RATIOS);
|
||||
REG_WRITE(LVDSPP_ON, lvds_priv->savePP_ON);
|
||||
REG_WRITE(LVDSPP_OFF, lvds_priv->savePP_OFF);
|
||||
/*REG_WRITE(PP_DIVISOR, lvds_priv->savePP_DIVISOR);*/
|
||||
REG_WRITE(PP_CYCLE, lvds_priv->savePP_CYCLE);
|
||||
REG_WRITE(PP_CONTROL, lvds_priv->savePP_CONTROL);
|
||||
REG_WRITE(LVDS, lvds_priv->saveLVDS);
|
||||
|
||||
if (lvds_priv->savePP_CONTROL & POWER_TARGET_ON) {
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) |
|
||||
POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & PP_ON) == 0);
|
||||
} else {
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) &
|
||||
~POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while (pp_status & PP_ON);
|
||||
}
|
||||
}
|
||||
|
||||
int psb_intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = connector->dev->dev_private;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct drm_display_mode *fixed_mode =
|
||||
dev_priv->mode_dev.panel_fixed_mode;
|
||||
|
||||
if (psb_intel_encoder->type == INTEL_OUTPUT_MIPI2)
|
||||
fixed_mode = dev_priv->mode_dev.panel_fixed_mode2;
|
||||
|
||||
/* just in case */
|
||||
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
|
||||
return MODE_NO_DBLESCAN;
|
||||
|
||||
/* just in case */
|
||||
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
|
||||
return MODE_NO_INTERLACE;
|
||||
|
||||
if (fixed_mode) {
|
||||
if (mode->hdisplay > fixed_mode->hdisplay)
|
||||
return MODE_PANEL;
|
||||
if (mode->vdisplay > fixed_mode->vdisplay)
|
||||
return MODE_PANEL;
|
||||
}
|
||||
return MODE_OK;
|
||||
}
|
||||
|
||||
bool psb_intel_lvds_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_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
struct psb_intel_crtc *psb_intel_crtc =
|
||||
to_psb_intel_crtc(encoder->crtc);
|
||||
struct drm_encoder *tmp_encoder;
|
||||
struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
to_psb_intel_encoder(encoder);
|
||||
|
||||
if (psb_intel_encoder->type == INTEL_OUTPUT_MIPI2)
|
||||
panel_fixed_mode = mode_dev->panel_fixed_mode2;
|
||||
|
||||
/* PSB requires the LVDS is on pipe B, MRST has only one pipe anyway */
|
||||
if (!IS_MRST(dev) && psb_intel_crtc->pipe == 0) {
|
||||
printk(KERN_ERR "Can't support LVDS on pipe A\n");
|
||||
return false;
|
||||
}
|
||||
if (IS_MRST(dev) && psb_intel_crtc->pipe != 0) {
|
||||
printk(KERN_ERR "Must use PIPE A\n");
|
||||
return false;
|
||||
}
|
||||
/* Should never happen!! */
|
||||
list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list,
|
||||
head) {
|
||||
if (tmp_encoder != encoder
|
||||
&& tmp_encoder->crtc == encoder->crtc) {
|
||||
printk(KERN_ERR "Can't enable LVDS and another "
|
||||
"encoder on the same pipe\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If we have timings from the BIOS for the panel, put them in
|
||||
* to the adjusted mode. The CRTC will be set up for this mode,
|
||||
* with the panel scaling set up to source from the H/VDisplay
|
||||
* of the original mode.
|
||||
*/
|
||||
if (panel_fixed_mode != NULL) {
|
||||
adjusted_mode->hdisplay = panel_fixed_mode->hdisplay;
|
||||
adjusted_mode->hsync_start = panel_fixed_mode->hsync_start;
|
||||
adjusted_mode->hsync_end = panel_fixed_mode->hsync_end;
|
||||
adjusted_mode->htotal = panel_fixed_mode->htotal;
|
||||
adjusted_mode->vdisplay = panel_fixed_mode->vdisplay;
|
||||
adjusted_mode->vsync_start = panel_fixed_mode->vsync_start;
|
||||
adjusted_mode->vsync_end = panel_fixed_mode->vsync_end;
|
||||
adjusted_mode->vtotal = panel_fixed_mode->vtotal;
|
||||
adjusted_mode->clock = panel_fixed_mode->clock;
|
||||
drm_mode_set_crtcinfo(adjusted_mode,
|
||||
CRTC_INTERLACE_HALVE_V);
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX: It would be nice to support lower refresh rates on the
|
||||
* panels to reduce power consumption, and perhaps match the
|
||||
* user's requested refresh rate.
|
||||
*/
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void psb_intel_lvds_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
|
||||
if (!gma_power_begin(dev, true))
|
||||
return;
|
||||
|
||||
mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL);
|
||||
mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL &
|
||||
BACKLIGHT_DUTY_CYCLE_MASK);
|
||||
|
||||
psb_intel_lvds_set_power(dev, false);
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
static void psb_intel_lvds_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
|
||||
if (mode_dev->backlight_duty_cycle == 0)
|
||||
mode_dev->backlight_duty_cycle =
|
||||
psb_intel_lvds_get_max_backlight(dev);
|
||||
|
||||
psb_intel_lvds_set_power(dev, true);
|
||||
}
|
||||
|
||||
static void psb_intel_lvds_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 pfit_control;
|
||||
|
||||
/*
|
||||
* The LVDS pin pair will already have been turned on in the
|
||||
* psb_intel_crtc_mode_set since it has a large impact on the DPLL
|
||||
* settings.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Enable automatic panel scaling so that non-native modes fill the
|
||||
* screen. Should be enabled before the pipe is enabled, according to
|
||||
* register description and PRM.
|
||||
*/
|
||||
if (mode->hdisplay != adjusted_mode->hdisplay ||
|
||||
mode->vdisplay != adjusted_mode->vdisplay)
|
||||
pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE |
|
||||
HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR |
|
||||
HORIZ_INTERP_BILINEAR);
|
||||
else
|
||||
pfit_control = 0;
|
||||
|
||||
if (dev_priv->lvds_dither)
|
||||
pfit_control |= PANEL_8TO6_DITHER_ENABLE;
|
||||
|
||||
REG_WRITE(PFIT_CONTROL, pfit_control);
|
||||
}
|
||||
|
||||
/*
|
||||
* Detect the LVDS connection.
|
||||
*
|
||||
* This always returns CONNECTOR_STATUS_CONNECTED.
|
||||
* This connector should only have
|
||||
* been set up if the LVDS was actually connected anyway.
|
||||
*/
|
||||
static enum drm_connector_status psb_intel_lvds_detect(struct drm_connector
|
||||
*connector, bool force)
|
||||
{
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the list of DDC modes if available, or the BIOS fixed mode otherwise.
|
||||
*/
|
||||
static int psb_intel_lvds_get_modes(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev;
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_lvds_priv *lvds_priv = psb_intel_encoder->dev_priv;
|
||||
int ret = 0;
|
||||
|
||||
if (!IS_MRST(dev))
|
||||
ret = psb_intel_ddc_get_modes(connector, &lvds_priv->i2c_bus->adapter);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Didn't get an EDID, so
|
||||
* Set wide sync ranges so we get all modes
|
||||
* handed to valid_mode for checking
|
||||
*/
|
||||
connector->display_info.min_vfreq = 0;
|
||||
connector->display_info.max_vfreq = 200;
|
||||
connector->display_info.min_hfreq = 0;
|
||||
connector->display_info.max_hfreq = 200;
|
||||
|
||||
if (mode_dev->panel_fixed_mode != NULL) {
|
||||
struct drm_display_mode *mode =
|
||||
drm_mode_duplicate(dev, mode_dev->panel_fixed_mode);
|
||||
drm_mode_probed_add(connector, mode);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_intel_lvds_destroy - unregister and free LVDS structures
|
||||
* @connector: connector to free
|
||||
*
|
||||
* Unregister the DDC bus for this connector then free the driver private
|
||||
* structure.
|
||||
*/
|
||||
void psb_intel_lvds_destroy(struct drm_connector *connector)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_lvds_priv *lvds_priv = psb_intel_encoder->dev_priv;
|
||||
|
||||
if (lvds_priv->ddc_bus)
|
||||
psb_intel_i2c_destroy(lvds_priv->ddc_bus);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
kfree(connector);
|
||||
}
|
||||
|
||||
int psb_intel_lvds_set_property(struct drm_connector *connector,
|
||||
struct drm_property *property,
|
||||
uint64_t value)
|
||||
{
|
||||
struct drm_encoder *encoder = connector->encoder;
|
||||
|
||||
if (!encoder)
|
||||
return -1;
|
||||
|
||||
if (!strcmp(property->name, "scaling mode")) {
|
||||
struct psb_intel_crtc *crtc =
|
||||
to_psb_intel_crtc(encoder->crtc);
|
||||
uint64_t curval;
|
||||
|
||||
if (!crtc)
|
||||
goto set_prop_error;
|
||||
|
||||
switch (value) {
|
||||
case DRM_MODE_SCALE_FULLSCREEN:
|
||||
break;
|
||||
case DRM_MODE_SCALE_NO_SCALE:
|
||||
break;
|
||||
case DRM_MODE_SCALE_ASPECT:
|
||||
break;
|
||||
default:
|
||||
goto set_prop_error;
|
||||
}
|
||||
|
||||
if (drm_connector_property_get_value(connector,
|
||||
property,
|
||||
&curval))
|
||||
goto set_prop_error;
|
||||
|
||||
if (curval == value)
|
||||
goto set_prop_done;
|
||||
|
||||
if (drm_connector_property_set_value(connector,
|
||||
property,
|
||||
value))
|
||||
goto set_prop_error;
|
||||
|
||||
if (crtc->saved_mode.hdisplay != 0 &&
|
||||
crtc->saved_mode.vdisplay != 0) {
|
||||
if (!drm_crtc_helper_set_mode(encoder->crtc,
|
||||
&crtc->saved_mode,
|
||||
encoder->crtc->x,
|
||||
encoder->crtc->y,
|
||||
encoder->crtc->fb))
|
||||
goto set_prop_error;
|
||||
}
|
||||
} else if (!strcmp(property->name, "backlight")) {
|
||||
if (drm_connector_property_set_value(connector,
|
||||
property,
|
||||
value))
|
||||
goto set_prop_error;
|
||||
else {
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *devp =
|
||||
encoder->dev->dev_private;
|
||||
struct backlight_device *bd = devp->backlight_device;
|
||||
if (bd) {
|
||||
bd->props.brightness = value;
|
||||
backlight_update_status(bd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else if (!strcmp(property->name, "DPMS")) {
|
||||
struct drm_encoder_helper_funcs *hfuncs
|
||||
= encoder->helper_private;
|
||||
hfuncs->dpms(encoder, value);
|
||||
}
|
||||
|
||||
set_prop_done:
|
||||
return 0;
|
||||
set_prop_error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs psb_intel_lvds_helper_funcs = {
|
||||
.dpms = psb_intel_lvds_encoder_dpms,
|
||||
.mode_fixup = psb_intel_lvds_mode_fixup,
|
||||
.prepare = psb_intel_lvds_prepare,
|
||||
.mode_set = psb_intel_lvds_mode_set,
|
||||
.commit = psb_intel_lvds_commit,
|
||||
};
|
||||
|
||||
const struct drm_connector_helper_funcs
|
||||
psb_intel_lvds_connector_helper_funcs = {
|
||||
.get_modes = psb_intel_lvds_get_modes,
|
||||
.mode_valid = psb_intel_lvds_mode_valid,
|
||||
.best_encoder = psb_intel_best_encoder,
|
||||
};
|
||||
|
||||
const struct drm_connector_funcs psb_intel_lvds_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.save = psb_intel_lvds_save,
|
||||
.restore = psb_intel_lvds_restore,
|
||||
.detect = psb_intel_lvds_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = psb_intel_lvds_set_property,
|
||||
.destroy = psb_intel_lvds_destroy,
|
||||
};
|
||||
|
||||
|
||||
static void psb_intel_lvds_enc_destroy(struct drm_encoder *encoder)
|
||||
{
|
||||
drm_encoder_cleanup(encoder);
|
||||
}
|
||||
|
||||
const struct drm_encoder_funcs psb_intel_lvds_enc_funcs = {
|
||||
.destroy = psb_intel_lvds_enc_destroy,
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* psb_intel_lvds_init - setup LVDS connectors on this device
|
||||
* @dev: drm device
|
||||
*
|
||||
* Create the connector, register the LVDS DDC bus, and try to figure out what
|
||||
* modes we can display on the LVDS panel (if present).
|
||||
*/
|
||||
void psb_intel_lvds_init(struct drm_device *dev,
|
||||
struct psb_intel_mode_device *mode_dev)
|
||||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder;
|
||||
struct psb_intel_connector *psb_intel_connector;
|
||||
struct psb_intel_lvds_priv *lvds_priv;
|
||||
struct drm_connector *connector;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_display_mode *scan; /* *modes, *bios_mode; */
|
||||
struct drm_crtc *crtc;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
u32 lvds;
|
||||
int pipe;
|
||||
|
||||
psb_intel_encoder =
|
||||
kzalloc(sizeof(struct psb_intel_encoder), GFP_KERNEL);
|
||||
|
||||
if (!psb_intel_encoder) {
|
||||
dev_err(dev->dev, "psb_intel_encoder allocation error\n");
|
||||
return;
|
||||
}
|
||||
|
||||
psb_intel_connector =
|
||||
kzalloc(sizeof(struct psb_intel_connector), GFP_KERNEL);
|
||||
|
||||
if (!psb_intel_connector) {
|
||||
kfree(psb_intel_encoder);
|
||||
dev_err(dev->dev, "psb_intel_connector allocation error\n");
|
||||
}
|
||||
|
||||
lvds_priv = kzalloc(sizeof(struct psb_intel_lvds_priv), GFP_KERNEL);
|
||||
if (!lvds_priv) {
|
||||
dev_err(dev->dev, "LVDS private allocation error\n");
|
||||
goto failed_connector;
|
||||
}
|
||||
|
||||
psb_intel_encoder->dev_priv = lvds_priv;
|
||||
|
||||
connector = &psb_intel_connector->base;
|
||||
encoder = &psb_intel_encoder->base;
|
||||
drm_connector_init(dev, connector,
|
||||
&psb_intel_lvds_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
|
||||
drm_encoder_init(dev, encoder,
|
||||
&psb_intel_lvds_enc_funcs,
|
||||
DRM_MODE_ENCODER_LVDS);
|
||||
|
||||
psb_intel_connector_attach_encoder(psb_intel_connector,
|
||||
psb_intel_encoder);
|
||||
psb_intel_encoder->type = INTEL_OUTPUT_LVDS;
|
||||
|
||||
drm_encoder_helper_add(encoder, &psb_intel_lvds_helper_funcs);
|
||||
drm_connector_helper_add(connector,
|
||||
&psb_intel_lvds_connector_helper_funcs);
|
||||
connector->display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->interlace_allowed = false;
|
||||
connector->doublescan_allowed = false;
|
||||
|
||||
/*Attach connector properties*/
|
||||
drm_connector_attach_property(connector,
|
||||
dev->mode_config.scaling_mode_property,
|
||||
DRM_MODE_SCALE_FULLSCREEN);
|
||||
drm_connector_attach_property(connector,
|
||||
dev_priv->backlight_property,
|
||||
BRIGHTNESS_MAX_LEVEL);
|
||||
|
||||
/*
|
||||
* Set up I2C bus
|
||||
* FIXME: distroy i2c_bus when exit
|
||||
*/
|
||||
lvds_priv->i2c_bus = psb_intel_i2c_create(dev, GPIOB, "LVDSBLC_B");
|
||||
if (!lvds_priv->i2c_bus) {
|
||||
dev_printk(KERN_ERR,
|
||||
&dev->pdev->dev, "I2C bus registration failed.\n");
|
||||
goto failed_blc_i2c;
|
||||
}
|
||||
lvds_priv->i2c_bus->slave_addr = 0x2C;
|
||||
dev_priv->lvds_i2c_bus = lvds_priv->i2c_bus;
|
||||
|
||||
/*
|
||||
* LVDS discovery:
|
||||
* 1) check for EDID on DDC
|
||||
* 2) check for VBT data
|
||||
* 3) check to see if LVDS is already on
|
||||
* if none of the above, no panel
|
||||
* 4) make sure lid is open
|
||||
* if closed, act like it's not there for now
|
||||
*/
|
||||
|
||||
/* Set up the DDC bus. */
|
||||
lvds_priv->ddc_bus = psb_intel_i2c_create(dev, GPIOC, "LVDSDDC_C");
|
||||
if (!lvds_priv->ddc_bus) {
|
||||
dev_printk(KERN_ERR, &dev->pdev->dev,
|
||||
"DDC bus registration " "failed.\n");
|
||||
goto failed_ddc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to get the fixed panel mode from DDC. Assume that the
|
||||
* preferred mode is the right one.
|
||||
*/
|
||||
psb_intel_ddc_get_modes(connector, &lvds_priv->ddc_bus->adapter);
|
||||
list_for_each_entry(scan, &connector->probed_modes, head) {
|
||||
if (scan->type & DRM_MODE_TYPE_PREFERRED) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, scan);
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
|
||||
/* Failed to get EDID, what about VBT? do we need this? */
|
||||
if (mode_dev->vbt_mode)
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev, mode_dev->vbt_mode);
|
||||
|
||||
if (!mode_dev->panel_fixed_mode)
|
||||
if (dev_priv->lfp_lvds_vbt_mode)
|
||||
mode_dev->panel_fixed_mode =
|
||||
drm_mode_duplicate(dev,
|
||||
dev_priv->lfp_lvds_vbt_mode);
|
||||
|
||||
/*
|
||||
* If we didn't get EDID, try checking if the panel is already turned
|
||||
* on. If so, assume that whatever is currently programmed is the
|
||||
* correct mode.
|
||||
*/
|
||||
lvds = REG_READ(LVDS);
|
||||
pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0;
|
||||
crtc = psb_intel_get_crtc_from_pipe(dev, pipe);
|
||||
|
||||
if (crtc && (lvds & LVDS_PORT_EN)) {
|
||||
mode_dev->panel_fixed_mode =
|
||||
psb_intel_crtc_mode_get(dev, crtc);
|
||||
if (mode_dev->panel_fixed_mode) {
|
||||
mode_dev->panel_fixed_mode->type |=
|
||||
DRM_MODE_TYPE_PREFERRED;
|
||||
goto out; /* FIXME: check for quirks */
|
||||
}
|
||||
}
|
||||
|
||||
/* If we still don't have a mode after all that, give up. */
|
||||
if (!mode_dev->panel_fixed_mode) {
|
||||
dev_err(dev->dev, "Found no modes on the lvds, ignoring the LVDS\n");
|
||||
goto failed_find;
|
||||
}
|
||||
|
||||
/*
|
||||
* Blacklist machines with BIOSes that list an LVDS panel without
|
||||
* actually having one.
|
||||
*/
|
||||
out:
|
||||
drm_sysfs_connector_add(connector);
|
||||
return;
|
||||
|
||||
failed_find:
|
||||
if (lvds_priv->ddc_bus)
|
||||
psb_intel_i2c_destroy(lvds_priv->ddc_bus);
|
||||
failed_ddc:
|
||||
if (lvds_priv->i2c_bus)
|
||||
psb_intel_i2c_destroy(lvds_priv->i2c_bus);
|
||||
failed_blc_i2c:
|
||||
drm_encoder_cleanup(encoder);
|
||||
drm_connector_cleanup(connector);
|
||||
failed_connector:
|
||||
if (psb_intel_connector)
|
||||
kfree(psb_intel_connector);
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2007 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authers: Jesse Barnes <jesse.barnes@intel.com>
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/fb.h>
|
||||
#include <drm/drmP.h>
|
||||
#include "psb_intel_drv.h"
|
||||
|
||||
/**
|
||||
* psb_intel_ddc_probe
|
||||
*
|
||||
*/
|
||||
bool psb_intel_ddc_probe(struct i2c_adapter *adapter)
|
||||
{
|
||||
u8 out_buf[] = { 0x0, 0x0 };
|
||||
u8 buf[2];
|
||||
int ret;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = 0x50,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = out_buf,
|
||||
},
|
||||
{
|
||||
.addr = 0x50,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = buf,
|
||||
}
|
||||
};
|
||||
|
||||
ret = i2c_transfer(adapter, msgs, 2);
|
||||
if (ret == 2)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* psb_intel_ddc_get_modes - get modelist from monitor
|
||||
* @connector: DRM connector device to use
|
||||
*
|
||||
* Fetch the EDID information from @connector using the DDC bus.
|
||||
*/
|
||||
int psb_intel_ddc_get_modes(struct drm_connector *connector,
|
||||
struct i2c_adapter *adapter)
|
||||
{
|
||||
struct edid *edid;
|
||||
int ret = 0;
|
||||
|
||||
edid = drm_get_edid(connector, adapter);
|
||||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
}
|
||||
return ret;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,723 @@
|
|||
/*
|
||||
* Copyright ? 2006-2007 Intel Corporation
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice (including the next
|
||||
* paragraph) shall be included in all copies or substantial portions of the
|
||||
* Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
* DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors:
|
||||
* Eric Anholt <eric@anholt.net>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file SDVO command definitions and structures.
|
||||
*/
|
||||
|
||||
#define SDVO_OUTPUT_FIRST (0)
|
||||
#define SDVO_OUTPUT_TMDS0 (1 << 0)
|
||||
#define SDVO_OUTPUT_RGB0 (1 << 1)
|
||||
#define SDVO_OUTPUT_CVBS0 (1 << 2)
|
||||
#define SDVO_OUTPUT_SVID0 (1 << 3)
|
||||
#define SDVO_OUTPUT_YPRPB0 (1 << 4)
|
||||
#define SDVO_OUTPUT_SCART0 (1 << 5)
|
||||
#define SDVO_OUTPUT_LVDS0 (1 << 6)
|
||||
#define SDVO_OUTPUT_TMDS1 (1 << 8)
|
||||
#define SDVO_OUTPUT_RGB1 (1 << 9)
|
||||
#define SDVO_OUTPUT_CVBS1 (1 << 10)
|
||||
#define SDVO_OUTPUT_SVID1 (1 << 11)
|
||||
#define SDVO_OUTPUT_YPRPB1 (1 << 12)
|
||||
#define SDVO_OUTPUT_SCART1 (1 << 13)
|
||||
#define SDVO_OUTPUT_LVDS1 (1 << 14)
|
||||
#define SDVO_OUTPUT_LAST (14)
|
||||
|
||||
struct psb_intel_sdvo_caps {
|
||||
u8 vendor_id;
|
||||
u8 device_id;
|
||||
u8 device_rev_id;
|
||||
u8 sdvo_version_major;
|
||||
u8 sdvo_version_minor;
|
||||
unsigned int sdvo_inputs_mask:2;
|
||||
unsigned int smooth_scaling:1;
|
||||
unsigned int sharp_scaling:1;
|
||||
unsigned int up_scaling:1;
|
||||
unsigned int down_scaling:1;
|
||||
unsigned int stall_support:1;
|
||||
unsigned int pad:1;
|
||||
u16 output_flags;
|
||||
} __attribute__((packed));
|
||||
|
||||
/** This matches the EDID DTD structure, more or less */
|
||||
struct psb_intel_sdvo_dtd {
|
||||
struct {
|
||||
u16 clock; /**< pixel clock, in 10kHz units */
|
||||
u8 h_active; /**< lower 8 bits (pixels) */
|
||||
u8 h_blank; /**< lower 8 bits (pixels) */
|
||||
u8 h_high; /**< upper 4 bits each h_active, h_blank */
|
||||
u8 v_active; /**< lower 8 bits (lines) */
|
||||
u8 v_blank; /**< lower 8 bits (lines) */
|
||||
u8 v_high; /**< upper 4 bits each v_active, v_blank */
|
||||
} part1;
|
||||
|
||||
struct {
|
||||
u8 h_sync_off; /**< lower 8 bits, from hblank start */
|
||||
u8 h_sync_width; /**< lower 8 bits (pixels) */
|
||||
/** lower 4 bits each vsync offset, vsync width */
|
||||
u8 v_sync_off_width;
|
||||
/**
|
||||
* 2 high bits of hsync offset, 2 high bits of hsync width,
|
||||
* bits 4-5 of vsync offset, and 2 high bits of vsync width.
|
||||
*/
|
||||
u8 sync_off_width_high;
|
||||
u8 dtd_flags;
|
||||
u8 sdvo_flags;
|
||||
/** bits 6-7 of vsync offset at bits 6-7 */
|
||||
u8 v_sync_off_high;
|
||||
u8 reserved;
|
||||
} part2;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct psb_intel_sdvo_pixel_clock_range {
|
||||
u16 min; /**< pixel clock, in 10kHz units */
|
||||
u16 max; /**< pixel clock, in 10kHz units */
|
||||
} __attribute__((packed));
|
||||
|
||||
struct psb_intel_sdvo_preferred_input_timing_args {
|
||||
u16 clock;
|
||||
u16 width;
|
||||
u16 height;
|
||||
u8 interlace:1;
|
||||
u8 scaled:1;
|
||||
u8 pad:6;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* I2C registers for SDVO */
|
||||
#define SDVO_I2C_ARG_0 0x07
|
||||
#define SDVO_I2C_ARG_1 0x06
|
||||
#define SDVO_I2C_ARG_2 0x05
|
||||
#define SDVO_I2C_ARG_3 0x04
|
||||
#define SDVO_I2C_ARG_4 0x03
|
||||
#define SDVO_I2C_ARG_5 0x02
|
||||
#define SDVO_I2C_ARG_6 0x01
|
||||
#define SDVO_I2C_ARG_7 0x00
|
||||
#define SDVO_I2C_OPCODE 0x08
|
||||
#define SDVO_I2C_CMD_STATUS 0x09
|
||||
#define SDVO_I2C_RETURN_0 0x0a
|
||||
#define SDVO_I2C_RETURN_1 0x0b
|
||||
#define SDVO_I2C_RETURN_2 0x0c
|
||||
#define SDVO_I2C_RETURN_3 0x0d
|
||||
#define SDVO_I2C_RETURN_4 0x0e
|
||||
#define SDVO_I2C_RETURN_5 0x0f
|
||||
#define SDVO_I2C_RETURN_6 0x10
|
||||
#define SDVO_I2C_RETURN_7 0x11
|
||||
#define SDVO_I2C_VENDOR_BEGIN 0x20
|
||||
|
||||
/* Status results */
|
||||
#define SDVO_CMD_STATUS_POWER_ON 0x0
|
||||
#define SDVO_CMD_STATUS_SUCCESS 0x1
|
||||
#define SDVO_CMD_STATUS_NOTSUPP 0x2
|
||||
#define SDVO_CMD_STATUS_INVALID_ARG 0x3
|
||||
#define SDVO_CMD_STATUS_PENDING 0x4
|
||||
#define SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED 0x5
|
||||
#define SDVO_CMD_STATUS_SCALING_NOT_SUPP 0x6
|
||||
|
||||
/* SDVO commands, argument/result registers */
|
||||
|
||||
#define SDVO_CMD_RESET 0x01
|
||||
|
||||
/** Returns a struct intel_sdvo_caps */
|
||||
#define SDVO_CMD_GET_DEVICE_CAPS 0x02
|
||||
|
||||
#define SDVO_CMD_GET_FIRMWARE_REV 0x86
|
||||
# define SDVO_DEVICE_FIRMWARE_MINOR SDVO_I2C_RETURN_0
|
||||
# define SDVO_DEVICE_FIRMWARE_MAJOR SDVO_I2C_RETURN_1
|
||||
# define SDVO_DEVICE_FIRMWARE_PATCH SDVO_I2C_RETURN_2
|
||||
|
||||
/**
|
||||
* Reports which inputs are trained (managed to sync).
|
||||
*
|
||||
* Devices must have trained within 2 vsyncs of a mode change.
|
||||
*/
|
||||
#define SDVO_CMD_GET_TRAINED_INPUTS 0x03
|
||||
struct psb_intel_sdvo_get_trained_inputs_response {
|
||||
unsigned int input0_trained:1;
|
||||
unsigned int input1_trained:1;
|
||||
unsigned int pad:6;
|
||||
} __attribute__((packed));
|
||||
|
||||
/** Returns a struct intel_sdvo_output_flags of active outputs. */
|
||||
#define SDVO_CMD_GET_ACTIVE_OUTPUTS 0x04
|
||||
|
||||
/**
|
||||
* Sets the current set of active outputs.
|
||||
*
|
||||
* Takes a struct intel_sdvo_output_flags. Must be preceded by a SET_IN_OUT_MAP
|
||||
* on multi-output devices.
|
||||
*/
|
||||
#define SDVO_CMD_SET_ACTIVE_OUTPUTS 0x05
|
||||
|
||||
/**
|
||||
* Returns the current mapping of SDVO inputs to outputs on the device.
|
||||
*
|
||||
* Returns two struct intel_sdvo_output_flags structures.
|
||||
*/
|
||||
#define SDVO_CMD_GET_IN_OUT_MAP 0x06
|
||||
struct psb_intel_sdvo_in_out_map {
|
||||
u16 in0, in1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current mapping of SDVO inputs to outputs on the device.
|
||||
*
|
||||
* Takes two struct i380_sdvo_output_flags structures.
|
||||
*/
|
||||
#define SDVO_CMD_SET_IN_OUT_MAP 0x07
|
||||
|
||||
/**
|
||||
* Returns a struct intel_sdvo_output_flags of attached displays.
|
||||
*/
|
||||
#define SDVO_CMD_GET_ATTACHED_DISPLAYS 0x0b
|
||||
|
||||
/**
|
||||
* Returns a struct intel_sdvo_ouptut_flags of displays supporting hot plugging.
|
||||
*/
|
||||
#define SDVO_CMD_GET_HOT_PLUG_SUPPORT 0x0c
|
||||
|
||||
/**
|
||||
* Takes a struct intel_sdvo_output_flags.
|
||||
*/
|
||||
#define SDVO_CMD_SET_ACTIVE_HOT_PLUG 0x0d
|
||||
|
||||
/**
|
||||
* Returns a struct intel_sdvo_output_flags of displays with hot plug
|
||||
* interrupts enabled.
|
||||
*/
|
||||
#define SDVO_CMD_GET_ACTIVE_HOT_PLUG 0x0e
|
||||
|
||||
#define SDVO_CMD_GET_INTERRUPT_EVENT_SOURCE 0x0f
|
||||
struct intel_sdvo_get_interrupt_event_source_response {
|
||||
u16 interrupt_status;
|
||||
unsigned int ambient_light_interrupt:1;
|
||||
unsigned int hdmi_audio_encrypt_change:1;
|
||||
unsigned int pad:6;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* Selects which input is affected by future input commands.
|
||||
*
|
||||
* Commands affected include SET_INPUT_TIMINGS_PART[12],
|
||||
* GET_INPUT_TIMINGS_PART[12], GET_PREFERRED_INPUT_TIMINGS_PART[12],
|
||||
* GET_INPUT_PIXEL_CLOCK_RANGE, and CREATE_PREFERRED_INPUT_TIMINGS.
|
||||
*/
|
||||
#define SDVO_CMD_SET_TARGET_INPUT 0x10
|
||||
struct psb_intel_sdvo_set_target_input_args {
|
||||
unsigned int target_1:1;
|
||||
unsigned int pad:7;
|
||||
} __attribute__((packed));
|
||||
|
||||
/**
|
||||
* Takes a struct intel_sdvo_output_flags of which outputs are targeted by
|
||||
* future output commands.
|
||||
*
|
||||
* Affected commands inclue SET_OUTPUT_TIMINGS_PART[12],
|
||||
* GET_OUTPUT_TIMINGS_PART[12], and GET_OUTPUT_PIXEL_CLOCK_RANGE.
|
||||
*/
|
||||
#define SDVO_CMD_SET_TARGET_OUTPUT 0x11
|
||||
|
||||
#define SDVO_CMD_GET_INPUT_TIMINGS_PART1 0x12
|
||||
#define SDVO_CMD_GET_INPUT_TIMINGS_PART2 0x13
|
||||
#define SDVO_CMD_SET_INPUT_TIMINGS_PART1 0x14
|
||||
#define SDVO_CMD_SET_INPUT_TIMINGS_PART2 0x15
|
||||
#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART1 0x16
|
||||
#define SDVO_CMD_SET_OUTPUT_TIMINGS_PART2 0x17
|
||||
#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART1 0x18
|
||||
#define SDVO_CMD_GET_OUTPUT_TIMINGS_PART2 0x19
|
||||
/* Part 1 */
|
||||
# define SDVO_DTD_CLOCK_LOW SDVO_I2C_ARG_0
|
||||
# define SDVO_DTD_CLOCK_HIGH SDVO_I2C_ARG_1
|
||||
# define SDVO_DTD_H_ACTIVE SDVO_I2C_ARG_2
|
||||
# define SDVO_DTD_H_BLANK SDVO_I2C_ARG_3
|
||||
# define SDVO_DTD_H_HIGH SDVO_I2C_ARG_4
|
||||
# define SDVO_DTD_V_ACTIVE SDVO_I2C_ARG_5
|
||||
# define SDVO_DTD_V_BLANK SDVO_I2C_ARG_6
|
||||
# define SDVO_DTD_V_HIGH SDVO_I2C_ARG_7
|
||||
/* Part 2 */
|
||||
# define SDVO_DTD_HSYNC_OFF SDVO_I2C_ARG_0
|
||||
# define SDVO_DTD_HSYNC_WIDTH SDVO_I2C_ARG_1
|
||||
# define SDVO_DTD_VSYNC_OFF_WIDTH SDVO_I2C_ARG_2
|
||||
# define SDVO_DTD_SYNC_OFF_WIDTH_HIGH SDVO_I2C_ARG_3
|
||||
# define SDVO_DTD_DTD_FLAGS SDVO_I2C_ARG_4
|
||||
# define SDVO_DTD_DTD_FLAG_INTERLACED (1 << 7)
|
||||
# define SDVO_DTD_DTD_FLAG_STEREO_MASK (3 << 5)
|
||||
# define SDVO_DTD_DTD_FLAG_INPUT_MASK (3 << 3)
|
||||
# define SDVO_DTD_DTD_FLAG_SYNC_MASK (3 << 1)
|
||||
# define SDVO_DTD_SDVO_FLAS SDVO_I2C_ARG_5
|
||||
# define SDVO_DTD_SDVO_FLAG_STALL (1 << 7)
|
||||
# define SDVO_DTD_SDVO_FLAG_CENTERED (0 << 6)
|
||||
# define SDVO_DTD_SDVO_FLAG_UPPER_LEFT (1 << 6)
|
||||
# define SDVO_DTD_SDVO_FLAG_SCALING_MASK (3 << 4)
|
||||
# define SDVO_DTD_SDVO_FLAG_SCALING_NONE (0 << 4)
|
||||
# define SDVO_DTD_SDVO_FLAG_SCALING_SHARP (1 << 4)
|
||||
# define SDVO_DTD_SDVO_FLAG_SCALING_SMOOTH (2 << 4)
|
||||
# define SDVO_DTD_VSYNC_OFF_HIGH SDVO_I2C_ARG_6
|
||||
|
||||
/**
|
||||
* Generates a DTD based on the given width, height, and flags.
|
||||
*
|
||||
* This will be supported by any device supporting scaling or interlaced
|
||||
* modes.
|
||||
*/
|
||||
#define SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING 0x1a
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_LOW SDVO_I2C_ARG_0
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_CLOCK_HIGH SDVO_I2C_ARG_1
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_LOW SDVO_I2C_ARG_2
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_WIDTH_HIGH SDVO_I2C_ARG_3
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_LOW SDVO_I2C_ARG_4
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_HEIGHT_HIGH SDVO_I2C_ARG_5
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_FLAGS SDVO_I2C_ARG_6
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_INTERLACED (1 << 0)
|
||||
# define SDVO_PREFERRED_INPUT_TIMING_FLAGS_SCALED (1 << 1)
|
||||
|
||||
#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART1 0x1b
|
||||
#define SDVO_CMD_GET_PREFERRED_INPUT_TIMING_PART2 0x1c
|
||||
|
||||
/** Returns a struct intel_sdvo_pixel_clock_range */
|
||||
#define SDVO_CMD_GET_INPUT_PIXEL_CLOCK_RANGE 0x1d
|
||||
/** Returns a struct intel_sdvo_pixel_clock_range */
|
||||
#define SDVO_CMD_GET_OUTPUT_PIXEL_CLOCK_RANGE 0x1e
|
||||
|
||||
/** Returns a byte bitfield containing SDVO_CLOCK_RATE_MULT_* flags */
|
||||
#define SDVO_CMD_GET_SUPPORTED_CLOCK_RATE_MULTS 0x1f
|
||||
|
||||
/** Returns a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
|
||||
#define SDVO_CMD_GET_CLOCK_RATE_MULT 0x20
|
||||
/** Takes a byte containing a SDVO_CLOCK_RATE_MULT_* flag */
|
||||
#define SDVO_CMD_SET_CLOCK_RATE_MULT 0x21
|
||||
# define SDVO_CLOCK_RATE_MULT_1X (1 << 0)
|
||||
# define SDVO_CLOCK_RATE_MULT_2X (1 << 1)
|
||||
# define SDVO_CLOCK_RATE_MULT_4X (1 << 3)
|
||||
|
||||
#define SDVO_CMD_GET_SUPPORTED_TV_FORMATS 0x27
|
||||
/** 6 bytes of bit flags for TV formats shared by all TV format functions */
|
||||
struct psb_intel_sdvo_tv_format {
|
||||
unsigned int ntsc_m:1;
|
||||
unsigned int ntsc_j:1;
|
||||
unsigned int ntsc_443:1;
|
||||
unsigned int pal_b:1;
|
||||
unsigned int pal_d:1;
|
||||
unsigned int pal_g:1;
|
||||
unsigned int pal_h:1;
|
||||
unsigned int pal_i:1;
|
||||
|
||||
unsigned int pal_m:1;
|
||||
unsigned int pal_n:1;
|
||||
unsigned int pal_nc:1;
|
||||
unsigned int pal_60:1;
|
||||
unsigned int secam_b:1;
|
||||
unsigned int secam_d:1;
|
||||
unsigned int secam_g:1;
|
||||
unsigned int secam_k:1;
|
||||
|
||||
unsigned int secam_k1:1;
|
||||
unsigned int secam_l:1;
|
||||
unsigned int secam_60:1;
|
||||
unsigned int hdtv_std_smpte_240m_1080i_59:1;
|
||||
unsigned int hdtv_std_smpte_240m_1080i_60:1;
|
||||
unsigned int hdtv_std_smpte_260m_1080i_59:1;
|
||||
unsigned int hdtv_std_smpte_260m_1080i_60:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080i_50:1;
|
||||
|
||||
unsigned int hdtv_std_smpte_274m_1080i_59:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080i_60:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_23:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_24:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_25:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_29:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_30:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_50:1;
|
||||
|
||||
unsigned int hdtv_std_smpte_274m_1080p_59:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_60:1;
|
||||
unsigned int hdtv_std_smpte_295m_1080i_50:1;
|
||||
unsigned int hdtv_std_smpte_295m_1080p_50:1;
|
||||
unsigned int hdtv_std_smpte_296m_720p_59:1;
|
||||
unsigned int hdtv_std_smpte_296m_720p_60:1;
|
||||
unsigned int hdtv_std_smpte_296m_720p_50:1;
|
||||
unsigned int hdtv_std_smpte_293m_480p_59:1;
|
||||
|
||||
unsigned int hdtv_std_smpte_170m_480i_59:1;
|
||||
unsigned int hdtv_std_iturbt601_576i_50:1;
|
||||
unsigned int hdtv_std_iturbt601_576p_50:1;
|
||||
unsigned int hdtv_std_eia_7702a_480i_60:1;
|
||||
unsigned int hdtv_std_eia_7702a_480p_60:1;
|
||||
unsigned int pad:3;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SDVO_CMD_GET_TV_FORMAT 0x28
|
||||
|
||||
#define SDVO_CMD_SET_TV_FORMAT 0x29
|
||||
|
||||
/** Returns the resolutiosn that can be used with the given TV format */
|
||||
#define SDVO_CMD_GET_SDTV_RESOLUTION_SUPPORT 0x83
|
||||
struct psb_intel_sdvo_sdtv_resolution_request {
|
||||
unsigned int ntsc_m:1;
|
||||
unsigned int ntsc_j:1;
|
||||
unsigned int ntsc_443:1;
|
||||
unsigned int pal_b:1;
|
||||
unsigned int pal_d:1;
|
||||
unsigned int pal_g:1;
|
||||
unsigned int pal_h:1;
|
||||
unsigned int pal_i:1;
|
||||
|
||||
unsigned int pal_m:1;
|
||||
unsigned int pal_n:1;
|
||||
unsigned int pal_nc:1;
|
||||
unsigned int pal_60:1;
|
||||
unsigned int secam_b:1;
|
||||
unsigned int secam_d:1;
|
||||
unsigned int secam_g:1;
|
||||
unsigned int secam_k:1;
|
||||
|
||||
unsigned int secam_k1:1;
|
||||
unsigned int secam_l:1;
|
||||
unsigned int secam_60:1;
|
||||
unsigned int pad:5;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct psb_intel_sdvo_sdtv_resolution_reply {
|
||||
unsigned int res_320x200:1;
|
||||
unsigned int res_320x240:1;
|
||||
unsigned int res_400x300:1;
|
||||
unsigned int res_640x350:1;
|
||||
unsigned int res_640x400:1;
|
||||
unsigned int res_640x480:1;
|
||||
unsigned int res_704x480:1;
|
||||
unsigned int res_704x576:1;
|
||||
|
||||
unsigned int res_720x350:1;
|
||||
unsigned int res_720x400:1;
|
||||
unsigned int res_720x480:1;
|
||||
unsigned int res_720x540:1;
|
||||
unsigned int res_720x576:1;
|
||||
unsigned int res_768x576:1;
|
||||
unsigned int res_800x600:1;
|
||||
unsigned int res_832x624:1;
|
||||
|
||||
unsigned int res_920x766:1;
|
||||
unsigned int res_1024x768:1;
|
||||
unsigned int res_1280x1024:1;
|
||||
unsigned int pad:5;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Get supported resolution with squire pixel aspect ratio that can be
|
||||
scaled for the requested HDTV format */
|
||||
#define SDVO_CMD_GET_SCALED_HDTV_RESOLUTION_SUPPORT 0x85
|
||||
|
||||
struct psb_intel_sdvo_hdtv_resolution_request {
|
||||
unsigned int hdtv_std_smpte_240m_1080i_59:1;
|
||||
unsigned int hdtv_std_smpte_240m_1080i_60:1;
|
||||
unsigned int hdtv_std_smpte_260m_1080i_59:1;
|
||||
unsigned int hdtv_std_smpte_260m_1080i_60:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080i_50:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080i_59:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080i_60:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_23:1;
|
||||
|
||||
unsigned int hdtv_std_smpte_274m_1080p_24:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_25:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_29:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_30:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_50:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_59:1;
|
||||
unsigned int hdtv_std_smpte_274m_1080p_60:1;
|
||||
unsigned int hdtv_std_smpte_295m_1080i_50:1;
|
||||
|
||||
unsigned int hdtv_std_smpte_295m_1080p_50:1;
|
||||
unsigned int hdtv_std_smpte_296m_720p_59:1;
|
||||
unsigned int hdtv_std_smpte_296m_720p_60:1;
|
||||
unsigned int hdtv_std_smpte_296m_720p_50:1;
|
||||
unsigned int hdtv_std_smpte_293m_480p_59:1;
|
||||
unsigned int hdtv_std_smpte_170m_480i_59:1;
|
||||
unsigned int hdtv_std_iturbt601_576i_50:1;
|
||||
unsigned int hdtv_std_iturbt601_576p_50:1;
|
||||
|
||||
unsigned int hdtv_std_eia_7702a_480i_60:1;
|
||||
unsigned int hdtv_std_eia_7702a_480p_60:1;
|
||||
unsigned int pad:6;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct psb_intel_sdvo_hdtv_resolution_reply {
|
||||
unsigned int res_640x480:1;
|
||||
unsigned int res_800x600:1;
|
||||
unsigned int res_1024x768:1;
|
||||
unsigned int res_1280x960:1;
|
||||
unsigned int res_1400x1050:1;
|
||||
unsigned int res_1600x1200:1;
|
||||
unsigned int res_1920x1440:1;
|
||||
unsigned int res_2048x1536:1;
|
||||
|
||||
unsigned int res_2560x1920:1;
|
||||
unsigned int res_3200x2400:1;
|
||||
unsigned int res_3840x2880:1;
|
||||
unsigned int pad1:5;
|
||||
|
||||
unsigned int res_848x480:1;
|
||||
unsigned int res_1064x600:1;
|
||||
unsigned int res_1280x720:1;
|
||||
unsigned int res_1360x768:1;
|
||||
unsigned int res_1704x960:1;
|
||||
unsigned int res_1864x1050:1;
|
||||
unsigned int res_1920x1080:1;
|
||||
unsigned int res_2128x1200:1;
|
||||
|
||||
unsigned int res_2560x1400:1;
|
||||
unsigned int res_2728x1536:1;
|
||||
unsigned int res_3408x1920:1;
|
||||
unsigned int res_4264x2400:1;
|
||||
unsigned int res_5120x2880:1;
|
||||
unsigned int pad2:3;
|
||||
|
||||
unsigned int res_768x480:1;
|
||||
unsigned int res_960x600:1;
|
||||
unsigned int res_1152x720:1;
|
||||
unsigned int res_1124x768:1;
|
||||
unsigned int res_1536x960:1;
|
||||
unsigned int res_1680x1050:1;
|
||||
unsigned int res_1728x1080:1;
|
||||
unsigned int res_1920x1200:1;
|
||||
|
||||
unsigned int res_2304x1440:1;
|
||||
unsigned int res_2456x1536:1;
|
||||
unsigned int res_3072x1920:1;
|
||||
unsigned int res_3840x2400:1;
|
||||
unsigned int res_4608x2880:1;
|
||||
unsigned int pad3:3;
|
||||
|
||||
unsigned int res_1280x1024:1;
|
||||
unsigned int pad4:7;
|
||||
|
||||
unsigned int res_1280x768:1;
|
||||
unsigned int pad5:7;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Get supported power state returns info for encoder and monitor, rely on
|
||||
last SetTargetInput and SetTargetOutput calls */
|
||||
#define SDVO_CMD_GET_SUPPORTED_POWER_STATES 0x2a
|
||||
/* Get power state returns info for encoder and monitor, rely on last
|
||||
SetTargetInput and SetTargetOutput calls */
|
||||
#define SDVO_CMD_GET_POWER_STATE 0x2b
|
||||
#define SDVO_CMD_GET_ENCODER_POWER_STATE 0x2b
|
||||
#define SDVO_CMD_SET_ENCODER_POWER_STATE 0x2c
|
||||
# define SDVO_ENCODER_STATE_ON (1 << 0)
|
||||
# define SDVO_ENCODER_STATE_STANDBY (1 << 1)
|
||||
# define SDVO_ENCODER_STATE_SUSPEND (1 << 2)
|
||||
# define SDVO_ENCODER_STATE_OFF (1 << 3)
|
||||
# define SDVO_MONITOR_STATE_ON (1 << 4)
|
||||
# define SDVO_MONITOR_STATE_STANDBY (1 << 5)
|
||||
# define SDVO_MONITOR_STATE_SUSPEND (1 << 6)
|
||||
# define SDVO_MONITOR_STATE_OFF (1 << 7)
|
||||
|
||||
#define SDVO_CMD_GET_MAX_PANEL_POWER_SEQUENCING 0x2d
|
||||
#define SDVO_CMD_GET_PANEL_POWER_SEQUENCING 0x2e
|
||||
#define SDVO_CMD_SET_PANEL_POWER_SEQUENCING 0x2f
|
||||
/**
|
||||
* The panel power sequencing parameters are in units of milliseconds.
|
||||
* The high fields are bits 8:9 of the 10-bit values.
|
||||
*/
|
||||
struct psb_sdvo_panel_power_sequencing {
|
||||
u8 t0;
|
||||
u8 t1;
|
||||
u8 t2;
|
||||
u8 t3;
|
||||
u8 t4;
|
||||
|
||||
unsigned int t0_high:2;
|
||||
unsigned int t1_high:2;
|
||||
unsigned int t2_high:2;
|
||||
unsigned int t3_high:2;
|
||||
|
||||
unsigned int t4_high:2;
|
||||
unsigned int pad:6;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SDVO_CMD_GET_MAX_BACKLIGHT_LEVEL 0x30
|
||||
struct sdvo_max_backlight_reply {
|
||||
u8 max_value;
|
||||
u8 default_value;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SDVO_CMD_GET_BACKLIGHT_LEVEL 0x31
|
||||
#define SDVO_CMD_SET_BACKLIGHT_LEVEL 0x32
|
||||
|
||||
#define SDVO_CMD_GET_AMBIENT_LIGHT 0x33
|
||||
struct sdvo_get_ambient_light_reply {
|
||||
u16 trip_low;
|
||||
u16 trip_high;
|
||||
u16 value;
|
||||
} __attribute__((packed));
|
||||
#define SDVO_CMD_SET_AMBIENT_LIGHT 0x34
|
||||
struct sdvo_set_ambient_light_reply {
|
||||
u16 trip_low;
|
||||
u16 trip_high;
|
||||
unsigned int enable:1;
|
||||
unsigned int pad:7;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Set display power state */
|
||||
#define SDVO_CMD_SET_DISPLAY_POWER_STATE 0x7d
|
||||
# define SDVO_DISPLAY_STATE_ON (1 << 0)
|
||||
# define SDVO_DISPLAY_STATE_STANDBY (1 << 1)
|
||||
# define SDVO_DISPLAY_STATE_SUSPEND (1 << 2)
|
||||
# define SDVO_DISPLAY_STATE_OFF (1 << 3)
|
||||
|
||||
#define SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS 0x84
|
||||
struct psb_intel_sdvo_enhancements_reply {
|
||||
unsigned int flicker_filter:1;
|
||||
unsigned int flicker_filter_adaptive:1;
|
||||
unsigned int flicker_filter_2d:1;
|
||||
unsigned int saturation:1;
|
||||
unsigned int hue:1;
|
||||
unsigned int brightness:1;
|
||||
unsigned int contrast:1;
|
||||
unsigned int overscan_h:1;
|
||||
|
||||
unsigned int overscan_v:1;
|
||||
unsigned int hpos:1;
|
||||
unsigned int vpos:1;
|
||||
unsigned int sharpness:1;
|
||||
unsigned int dot_crawl:1;
|
||||
unsigned int dither:1;
|
||||
unsigned int tv_chroma_filter:1;
|
||||
unsigned int tv_luma_filter:1;
|
||||
} __attribute__((packed));
|
||||
|
||||
/* Picture enhancement limits below are dependent on the current TV format,
|
||||
* and thus need to be queried and set after it.
|
||||
*/
|
||||
#define SDVO_CMD_GET_MAX_FLICKER_FILTER 0x4d
|
||||
#define SDVO_CMD_GET_MAX_FLICKER_FILTER_ADAPTIVE 0x7b
|
||||
#define SDVO_CMD_GET_MAX_FLICKER_FILTER_2D 0x52
|
||||
#define SDVO_CMD_GET_MAX_SATURATION 0x55
|
||||
#define SDVO_CMD_GET_MAX_HUE 0x58
|
||||
#define SDVO_CMD_GET_MAX_BRIGHTNESS 0x5b
|
||||
#define SDVO_CMD_GET_MAX_CONTRAST 0x5e
|
||||
#define SDVO_CMD_GET_MAX_OVERSCAN_H 0x61
|
||||
#define SDVO_CMD_GET_MAX_OVERSCAN_V 0x64
|
||||
#define SDVO_CMD_GET_MAX_HPOS 0x67
|
||||
#define SDVO_CMD_GET_MAX_VPOS 0x6a
|
||||
#define SDVO_CMD_GET_MAX_SHARPNESS 0x6d
|
||||
#define SDVO_CMD_GET_MAX_TV_CHROMA_FILTER 0x74
|
||||
#define SDVO_CMD_GET_MAX_TV_LUMA_FILTER 0x77
|
||||
struct psb_intel_sdvo_enhancement_limits_reply {
|
||||
u16 max_value;
|
||||
u16 default_value;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define SDVO_CMD_GET_LVDS_PANEL_INFORMATION 0x7f
|
||||
#define SDVO_CMD_SET_LVDS_PANEL_INFORMATION 0x80
|
||||
# define SDVO_LVDS_COLOR_DEPTH_18 (0 << 0)
|
||||
# define SDVO_LVDS_COLOR_DEPTH_24 (1 << 0)
|
||||
# define SDVO_LVDS_CONNECTOR_SPWG (0 << 2)
|
||||
# define SDVO_LVDS_CONNECTOR_OPENLDI (1 << 2)
|
||||
# define SDVO_LVDS_SINGLE_CHANNEL (0 << 4)
|
||||
# define SDVO_LVDS_DUAL_CHANNEL (1 << 4)
|
||||
|
||||
#define SDVO_CMD_GET_FLICKER_FILTER 0x4e
|
||||
#define SDVO_CMD_SET_FLICKER_FILTER 0x4f
|
||||
#define SDVO_CMD_GET_FLICKER_FILTER_ADAPTIVE 0x50
|
||||
#define SDVO_CMD_SET_FLICKER_FILTER_ADAPTIVE 0x51
|
||||
#define SDVO_CMD_GET_FLICKER_FILTER_2D 0x53
|
||||
#define SDVO_CMD_SET_FLICKER_FILTER_2D 0x54
|
||||
#define SDVO_CMD_GET_SATURATION 0x56
|
||||
#define SDVO_CMD_SET_SATURATION 0x57
|
||||
#define SDVO_CMD_GET_HUE 0x59
|
||||
#define SDVO_CMD_SET_HUE 0x5a
|
||||
#define SDVO_CMD_GET_BRIGHTNESS 0x5c
|
||||
#define SDVO_CMD_SET_BRIGHTNESS 0x5d
|
||||
#define SDVO_CMD_GET_CONTRAST 0x5f
|
||||
#define SDVO_CMD_SET_CONTRAST 0x60
|
||||
#define SDVO_CMD_GET_OVERSCAN_H 0x62
|
||||
#define SDVO_CMD_SET_OVERSCAN_H 0x63
|
||||
#define SDVO_CMD_GET_OVERSCAN_V 0x65
|
||||
#define SDVO_CMD_SET_OVERSCAN_V 0x66
|
||||
#define SDVO_CMD_GET_HPOS 0x68
|
||||
#define SDVO_CMD_SET_HPOS 0x69
|
||||
#define SDVO_CMD_GET_VPOS 0x6b
|
||||
#define SDVO_CMD_SET_VPOS 0x6c
|
||||
#define SDVO_CMD_GET_SHARPNESS 0x6e
|
||||
#define SDVO_CMD_SET_SHARPNESS 0x6f
|
||||
#define SDVO_CMD_GET_TV_CHROMA_FILTER 0x75
|
||||
#define SDVO_CMD_SET_TV_CHROMA_FILTER 0x76
|
||||
#define SDVO_CMD_GET_TV_LUMA_FILTER 0x78
|
||||
#define SDVO_CMD_SET_TV_LUMA_FILTER 0x79
|
||||
struct psb_intel_sdvo_enhancements_arg {
|
||||
u16 value;
|
||||
}__attribute__((packed));
|
||||
|
||||
#define SDVO_CMD_GET_DOT_CRAWL 0x70
|
||||
#define SDVO_CMD_SET_DOT_CRAWL 0x71
|
||||
# define SDVO_DOT_CRAWL_ON (1 << 0)
|
||||
# define SDVO_DOT_CRAWL_DEFAULT_ON (1 << 1)
|
||||
|
||||
#define SDVO_CMD_GET_DITHER 0x72
|
||||
#define SDVO_CMD_SET_DITHER 0x73
|
||||
# define SDVO_DITHER_ON (1 << 0)
|
||||
# define SDVO_DITHER_DEFAULT_ON (1 << 1)
|
||||
|
||||
#define SDVO_CMD_SET_CONTROL_BUS_SWITCH 0x7a
|
||||
# define SDVO_CONTROL_BUS_PROM (1 << 0)
|
||||
# define SDVO_CONTROL_BUS_DDC1 (1 << 1)
|
||||
# define SDVO_CONTROL_BUS_DDC2 (1 << 2)
|
||||
# define SDVO_CONTROL_BUS_DDC3 (1 << 3)
|
||||
|
||||
/* HDMI op codes */
|
||||
#define SDVO_CMD_GET_SUPP_ENCODE 0x9d
|
||||
#define SDVO_CMD_GET_ENCODE 0x9e
|
||||
#define SDVO_CMD_SET_ENCODE 0x9f
|
||||
#define SDVO_ENCODE_DVI 0x0
|
||||
#define SDVO_ENCODE_HDMI 0x1
|
||||
#define SDVO_CMD_SET_PIXEL_REPLI 0x8b
|
||||
#define SDVO_CMD_GET_PIXEL_REPLI 0x8c
|
||||
#define SDVO_CMD_GET_COLORIMETRY_CAP 0x8d
|
||||
#define SDVO_CMD_SET_COLORIMETRY 0x8e
|
||||
#define SDVO_COLORIMETRY_RGB256 0x0
|
||||
#define SDVO_COLORIMETRY_RGB220 0x1
|
||||
#define SDVO_COLORIMETRY_YCrCb422 0x3
|
||||
#define SDVO_COLORIMETRY_YCrCb444 0x4
|
||||
#define SDVO_CMD_GET_COLORIMETRY 0x8f
|
||||
#define SDVO_CMD_GET_AUDIO_ENCRYPT_PREFER 0x90
|
||||
#define SDVO_CMD_SET_AUDIO_STAT 0x91
|
||||
#define SDVO_CMD_GET_AUDIO_STAT 0x92
|
||||
#define SDVO_CMD_SET_HBUF_INDEX 0x93
|
||||
#define SDVO_CMD_GET_HBUF_INDEX 0x94
|
||||
#define SDVO_CMD_GET_HBUF_INFO 0x95
|
||||
#define SDVO_CMD_SET_HBUF_AV_SPLIT 0x96
|
||||
#define SDVO_CMD_GET_HBUF_AV_SPLIT 0x97
|
||||
#define SDVO_CMD_SET_HBUF_DATA 0x98
|
||||
#define SDVO_CMD_GET_HBUF_DATA 0x99
|
||||
#define SDVO_CMD_SET_HBUF_TXRATE 0x9a
|
||||
#define SDVO_CMD_GET_HBUF_TXRATE 0x9b
|
||||
#define SDVO_HBUF_TX_DISABLED (0 << 6)
|
||||
#define SDVO_HBUF_TX_ONCE (2 << 6)
|
||||
#define SDVO_HBUF_TX_VSYNC (3 << 6)
|
||||
#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c
|
||||
#define SDVO_NEED_TO_STALL (1 << 7)
|
||||
|
||||
struct psb_intel_sdvo_encode {
|
||||
u8 dvi_rev;
|
||||
u8 hdmi_rev;
|
||||
} __attribute__ ((packed));
|
|
@ -0,0 +1,564 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Intel funded Tungsten Graphics (http://www.tungstengraphics.com) to
|
||||
* develop this driver.
|
||||
*
|
||||
**************************************************************************/
|
||||
/*
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include "power.h"
|
||||
|
||||
/*
|
||||
* inline functions
|
||||
*/
|
||||
|
||||
static inline u32
|
||||
psb_pipestat(int pipe)
|
||||
{
|
||||
if (pipe == 0)
|
||||
return PIPEASTAT;
|
||||
if (pipe == 1)
|
||||
return PIPEBSTAT;
|
||||
if (pipe == 2)
|
||||
return PIPECSTAT;
|
||||
BUG();
|
||||
}
|
||||
|
||||
static inline u32
|
||||
mid_pipe_event(int pipe)
|
||||
{
|
||||
if (pipe == 0)
|
||||
return _PSB_PIPEA_EVENT_FLAG;
|
||||
if (pipe == 1)
|
||||
return _MDFLD_PIPEB_EVENT_FLAG;
|
||||
if (pipe == 2)
|
||||
return _MDFLD_PIPEC_EVENT_FLAG;
|
||||
BUG();
|
||||
}
|
||||
|
||||
static inline u32
|
||||
mid_pipe_vsync(int pipe)
|
||||
{
|
||||
if (pipe == 0)
|
||||
return _PSB_VSYNC_PIPEA_FLAG;
|
||||
if (pipe == 1)
|
||||
return _PSB_VSYNC_PIPEB_FLAG;
|
||||
if (pipe == 2)
|
||||
return _MDFLD_PIPEC_VBLANK_FLAG;
|
||||
BUG();
|
||||
}
|
||||
|
||||
static inline u32
|
||||
mid_pipeconf(int pipe)
|
||||
{
|
||||
if (pipe == 0)
|
||||
return PIPEACONF;
|
||||
if (pipe == 1)
|
||||
return PIPEBCONF;
|
||||
if (pipe == 2)
|
||||
return PIPECCONF;
|
||||
BUG();
|
||||
}
|
||||
|
||||
void
|
||||
psb_enable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
|
||||
{
|
||||
if ((dev_priv->pipestat[pipe] & mask) != mask) {
|
||||
u32 reg = psb_pipestat(pipe);
|
||||
dev_priv->pipestat[pipe] |= mask;
|
||||
/* Enable the interrupt, clear any pending status */
|
||||
if (gma_power_begin(dev_priv->dev, false)) {
|
||||
u32 writeVal = PSB_RVDC32(reg);
|
||||
writeVal |= (mask | (mask >> 16));
|
||||
PSB_WVDC32(writeVal, reg);
|
||||
(void) PSB_RVDC32(reg);
|
||||
gma_power_end(dev_priv->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
psb_disable_pipestat(struct drm_psb_private *dev_priv, int pipe, u32 mask)
|
||||
{
|
||||
if ((dev_priv->pipestat[pipe] & mask) != 0) {
|
||||
u32 reg = psb_pipestat(pipe);
|
||||
dev_priv->pipestat[pipe] &= ~mask;
|
||||
if (gma_power_begin(dev_priv->dev, false)) {
|
||||
u32 writeVal = PSB_RVDC32(reg);
|
||||
writeVal &= ~mask;
|
||||
PSB_WVDC32(writeVal, reg);
|
||||
(void) PSB_RVDC32(reg);
|
||||
gma_power_end(dev_priv->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mid_enable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
|
||||
{
|
||||
if (gma_power_begin(dev_priv->dev, false)) {
|
||||
u32 pipe_event = mid_pipe_event(pipe);
|
||||
dev_priv->vdc_irq_mask |= pipe_event;
|
||||
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
||||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
gma_power_end(dev_priv->dev);
|
||||
}
|
||||
}
|
||||
|
||||
void mid_disable_pipe_event(struct drm_psb_private *dev_priv, int pipe)
|
||||
{
|
||||
if (dev_priv->pipestat[pipe] == 0) {
|
||||
if (gma_power_begin(dev_priv->dev, false)) {
|
||||
u32 pipe_event = mid_pipe_event(pipe);
|
||||
dev_priv->vdc_irq_mask &= ~pipe_event;
|
||||
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
||||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
gma_power_end(dev_priv->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Display controller interrupt handler for pipe event.
|
||||
*
|
||||
*/
|
||||
static void mid_pipe_event_handler(struct drm_device *dev, int pipe)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
|
||||
uint32_t pipe_stat_val = 0;
|
||||
uint32_t pipe_stat_reg = psb_pipestat(pipe);
|
||||
uint32_t pipe_enable = dev_priv->pipestat[pipe];
|
||||
uint32_t pipe_status = dev_priv->pipestat[pipe] >> 16;
|
||||
uint32_t pipe_clear;
|
||||
uint32_t i = 0;
|
||||
|
||||
spin_lock(&dev_priv->irqmask_lock);
|
||||
|
||||
pipe_stat_val = PSB_RVDC32(pipe_stat_reg);
|
||||
pipe_stat_val &= pipe_enable | pipe_status;
|
||||
pipe_stat_val &= pipe_stat_val >> 16;
|
||||
|
||||
spin_unlock(&dev_priv->irqmask_lock);
|
||||
|
||||
/* Clear the 2nd level interrupt status bits
|
||||
* Sometimes the bits are very sticky so we repeat until they unstick */
|
||||
for (i = 0; i < 0xffff; i++) {
|
||||
PSB_WVDC32(PSB_RVDC32(pipe_stat_reg), pipe_stat_reg);
|
||||
pipe_clear = PSB_RVDC32(pipe_stat_reg) & pipe_status;
|
||||
|
||||
if (pipe_clear == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
if (pipe_clear)
|
||||
dev_err(dev->dev,
|
||||
"%s, can't clear status bits for pipe %d, its value = 0x%x.\n",
|
||||
__func__, pipe, PSB_RVDC32(pipe_stat_reg));
|
||||
|
||||
if (pipe_stat_val & PIPE_VBLANK_STATUS)
|
||||
drm_handle_vblank(dev, pipe);
|
||||
|
||||
if (pipe_stat_val & PIPE_TE_STATUS)
|
||||
drm_handle_vblank(dev, pipe);
|
||||
}
|
||||
|
||||
/*
|
||||
* Display controller interrupt handler.
|
||||
*/
|
||||
static void psb_vdc_interrupt(struct drm_device *dev, uint32_t vdc_stat)
|
||||
{
|
||||
if (vdc_stat & _PSB_VSYNC_PIPEA_FLAG)
|
||||
mid_pipe_event_handler(dev, 0);
|
||||
|
||||
if (vdc_stat & _PSB_VSYNC_PIPEB_FLAG)
|
||||
mid_pipe_event_handler(dev, 1);
|
||||
}
|
||||
|
||||
irqreturn_t psb_irq_handler(DRM_IRQ_ARGS)
|
||||
{
|
||||
struct drm_device *dev = (struct drm_device *) arg;
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
|
||||
uint32_t vdc_stat, dsp_int = 0, sgx_int = 0;
|
||||
int handled = 0;
|
||||
|
||||
spin_lock(&dev_priv->irqmask_lock);
|
||||
|
||||
vdc_stat = PSB_RVDC32(PSB_INT_IDENTITY_R);
|
||||
|
||||
if (vdc_stat & _PSB_PIPE_EVENT_FLAG)
|
||||
dsp_int = 1;
|
||||
|
||||
/* FIXME: Handle Medfield
|
||||
if (vdc_stat & _MDFLD_DISP_ALL_IRQ_FLAG)
|
||||
dsp_int = 1;
|
||||
*/
|
||||
|
||||
if (vdc_stat & _PSB_IRQ_SGX_FLAG)
|
||||
sgx_int = 1;
|
||||
|
||||
vdc_stat &= dev_priv->vdc_irq_mask;
|
||||
spin_unlock(&dev_priv->irqmask_lock);
|
||||
|
||||
if (dsp_int && gma_power_is_on(dev)) {
|
||||
psb_vdc_interrupt(dev, vdc_stat);
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
if (sgx_int) {
|
||||
/* Not expected - we have it masked, shut it up */
|
||||
u32 s, s2;
|
||||
s = PSB_RSGX32(PSB_CR_EVENT_STATUS);
|
||||
s2 = PSB_RSGX32(PSB_CR_EVENT_STATUS2);
|
||||
PSB_WSGX32(s, PSB_CR_EVENT_HOST_CLEAR);
|
||||
PSB_WSGX32(s2, PSB_CR_EVENT_HOST_CLEAR2);
|
||||
/* if s & _PSB_CE_TWOD_COMPLETE we have 2D done but
|
||||
we may as well poll even if we add that ! */
|
||||
handled = 1;
|
||||
}
|
||||
|
||||
PSB_WVDC32(vdc_stat, PSB_INT_IDENTITY_R);
|
||||
(void) PSB_RVDC32(PSB_INT_IDENTITY_R);
|
||||
DRM_READMEMORYBARRIER();
|
||||
|
||||
if (!handled)
|
||||
return IRQ_NONE;
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
void psb_irq_preinstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
if (gma_power_is_on(dev))
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
||||
if (dev->vblank_enabled[0])
|
||||
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
|
||||
if (dev->vblank_enabled[1])
|
||||
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
|
||||
|
||||
/* FIXME: Handle Medfield irq mask
|
||||
if (dev->vblank_enabled[1])
|
||||
dev_priv->vdc_irq_mask |= _MDFLD_PIPEB_EVENT_FLAG;
|
||||
if (dev->vblank_enabled[2])
|
||||
dev_priv->vdc_irq_mask |= _MDFLD_PIPEC_EVENT_FLAG;
|
||||
*/
|
||||
|
||||
/* This register is safe even if display island is off */
|
||||
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
}
|
||||
|
||||
int psb_irq_postinstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
/* This register is safe even if display island is off */
|
||||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
||||
|
||||
if (dev->vblank_enabled[0])
|
||||
psb_enable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
else
|
||||
psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
if (dev->vblank_enabled[1])
|
||||
psb_enable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
else
|
||||
psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
if (dev->vblank_enabled[2])
|
||||
psb_enable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
else
|
||||
psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psb_irq_uninstall(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
PSB_WVDC32(0xFFFFFFFF, PSB_HWSTAM);
|
||||
|
||||
if (dev->vblank_enabled[0])
|
||||
psb_disable_pipestat(dev_priv, 0, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
if (dev->vblank_enabled[1])
|
||||
psb_disable_pipestat(dev_priv, 1, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
if (dev->vblank_enabled[2])
|
||||
psb_disable_pipestat(dev_priv, 2, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
dev_priv->vdc_irq_mask &= _PSB_IRQ_SGX_FLAG |
|
||||
_PSB_IRQ_MSVDX_FLAG |
|
||||
_LNC_IRQ_TOPAZ_FLAG;
|
||||
|
||||
/* These two registers are safe even if display island is off */
|
||||
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
||||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
|
||||
wmb();
|
||||
|
||||
/* This register is safe even if display island is off */
|
||||
PSB_WVDC32(PSB_RVDC32(PSB_INT_IDENTITY_R), PSB_INT_IDENTITY_R);
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
}
|
||||
|
||||
void psb_irq_turn_on_dpst(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
u32 hist_reg;
|
||||
u32 pwm_reg;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
PSB_WVDC32(1 << 31, HISTOGRAM_LOGIC_CONTROL);
|
||||
hist_reg = PSB_RVDC32(HISTOGRAM_LOGIC_CONTROL);
|
||||
PSB_WVDC32(1 << 31, HISTOGRAM_INT_CONTROL);
|
||||
hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
|
||||
|
||||
PSB_WVDC32(0x80010100, PWM_CONTROL_LOGIC);
|
||||
pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
|
||||
PSB_WVDC32(pwm_reg | PWM_PHASEIN_ENABLE
|
||||
| PWM_PHASEIN_INT_ENABLE,
|
||||
PWM_CONTROL_LOGIC);
|
||||
pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
|
||||
|
||||
psb_enable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE);
|
||||
|
||||
hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
|
||||
PSB_WVDC32(hist_reg | HISTOGRAM_INT_CTRL_CLEAR,
|
||||
HISTOGRAM_INT_CONTROL);
|
||||
pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
|
||||
PSB_WVDC32(pwm_reg | 0x80010100 | PWM_PHASEIN_ENABLE,
|
||||
PWM_CONTROL_LOGIC);
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
}
|
||||
|
||||
int psb_irq_enable_dpst(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
/* enable DPST */
|
||||
mid_enable_pipe_event(dev_priv, 0);
|
||||
psb_irq_turn_on_dpst(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void psb_irq_turn_off_dpst(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
u32 hist_reg;
|
||||
u32 pwm_reg;
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
PSB_WVDC32(0x00000000, HISTOGRAM_INT_CONTROL);
|
||||
hist_reg = PSB_RVDC32(HISTOGRAM_INT_CONTROL);
|
||||
|
||||
psb_disable_pipestat(dev_priv, 0, PIPE_DPST_EVENT_ENABLE);
|
||||
|
||||
pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
|
||||
PSB_WVDC32(pwm_reg & !(PWM_PHASEIN_INT_ENABLE),
|
||||
PWM_CONTROL_LOGIC);
|
||||
pwm_reg = PSB_RVDC32(PWM_CONTROL_LOGIC);
|
||||
|
||||
gma_power_end(dev);
|
||||
}
|
||||
}
|
||||
|
||||
int psb_irq_disable_dpst(struct drm_device *dev)
|
||||
{
|
||||
struct drm_psb_private *dev_priv =
|
||||
(struct drm_psb_private *) dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
mid_disable_pipe_event(dev_priv, 0);
|
||||
psb_irq_turn_off_dpst(dev);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef PSB_FIXME
|
||||
static int psb_vblank_do_wait(struct drm_device *dev,
|
||||
unsigned int *sequence, atomic_t *counter)
|
||||
{
|
||||
unsigned int cur_vblank;
|
||||
int ret = 0;
|
||||
DRM_WAIT_ON(ret, dev->vbl_queue, 3 * DRM_HZ,
|
||||
(((cur_vblank = atomic_read(counter))
|
||||
- *sequence) <= (1 << 23)));
|
||||
*sequence = cur_vblank;
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* It is used to enable VBLANK interrupt
|
||||
*/
|
||||
int psb_enable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
uint32_t reg_val = 0;
|
||||
uint32_t pipeconf_reg = mid_pipeconf(pipe);
|
||||
|
||||
if (gma_power_begin(dev, false)) {
|
||||
reg_val = REG_READ(pipeconf_reg);
|
||||
gma_power_end(dev);
|
||||
}
|
||||
|
||||
if (!(reg_val & PIPEACONF_ENABLE))
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
if (pipe == 0)
|
||||
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEA_FLAG;
|
||||
else if (pipe == 1)
|
||||
dev_priv->vdc_irq_mask |= _PSB_VSYNC_PIPEB_FLAG;
|
||||
|
||||
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
||||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
psb_enable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* It is used to disable VBLANK interrupt
|
||||
*/
|
||||
void psb_disable_vblank(struct drm_device *dev, int pipe)
|
||||
{
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
unsigned long irqflags;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irqmask_lock, irqflags);
|
||||
|
||||
if (pipe == 0)
|
||||
dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEA_FLAG;
|
||||
else if (pipe == 1)
|
||||
dev_priv->vdc_irq_mask &= ~_PSB_VSYNC_PIPEB_FLAG;
|
||||
|
||||
PSB_WVDC32(~dev_priv->vdc_irq_mask, PSB_INT_MASK_R);
|
||||
PSB_WVDC32(dev_priv->vdc_irq_mask, PSB_INT_ENABLE_R);
|
||||
psb_disable_pipestat(dev_priv, pipe, PIPE_VBLANK_INTERRUPT_ENABLE);
|
||||
|
||||
spin_unlock_irqrestore(&dev_priv->irqmask_lock, irqflags);
|
||||
}
|
||||
|
||||
/* Called from drm generic code, passed a 'crtc', which
|
||||
* we use as a pipe index
|
||||
*/
|
||||
u32 psb_get_vblank_counter(struct drm_device *dev, int pipe)
|
||||
{
|
||||
uint32_t high_frame = PIPEAFRAMEHIGH;
|
||||
uint32_t low_frame = PIPEAFRAMEPIXEL;
|
||||
uint32_t pipeconf_reg = PIPEACONF;
|
||||
uint32_t reg_val = 0;
|
||||
uint32_t high1 = 0, high2 = 0, low = 0, count = 0;
|
||||
|
||||
switch (pipe) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
high_frame = PIPEBFRAMEHIGH;
|
||||
low_frame = PIPEBFRAMEPIXEL;
|
||||
pipeconf_reg = PIPEBCONF;
|
||||
break;
|
||||
case 2:
|
||||
high_frame = PIPECFRAMEHIGH;
|
||||
low_frame = PIPECFRAMEPIXEL;
|
||||
pipeconf_reg = PIPECCONF;
|
||||
break;
|
||||
default:
|
||||
dev_err(dev->dev, "%s, invalid pipe.\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!gma_power_begin(dev, false))
|
||||
return 0;
|
||||
|
||||
reg_val = REG_READ(pipeconf_reg);
|
||||
|
||||
if (!(reg_val & PIPEACONF_ENABLE)) {
|
||||
dev_err(dev->dev, "trying to get vblank count for disabled pipe %d\n",
|
||||
pipe);
|
||||
goto psb_get_vblank_counter_exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* High & low register fields aren't synchronized, so make sure
|
||||
* we get a low value that's stable across two reads of the high
|
||||
* register.
|
||||
*/
|
||||
do {
|
||||
high1 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
|
||||
PIPE_FRAME_HIGH_SHIFT);
|
||||
low = ((REG_READ(low_frame) & PIPE_FRAME_LOW_MASK) >>
|
||||
PIPE_FRAME_LOW_SHIFT);
|
||||
high2 = ((REG_READ(high_frame) & PIPE_FRAME_HIGH_MASK) >>
|
||||
PIPE_FRAME_HIGH_SHIFT);
|
||||
} while (high1 != high2);
|
||||
|
||||
count = (high1 << 8) | low;
|
||||
|
||||
psb_get_vblank_counter_exit:
|
||||
|
||||
gma_power_end(dev);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2009-2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors:
|
||||
* Benjamin Defnet <benjamin.r.defnet@intel.com>
|
||||
* Rajesh Poornachandran <rajesh.poornachandran@intel.com>
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _SYSIRQ_H_
|
||||
#define _SYSIRQ_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
|
||||
bool sysirq_init(struct drm_device *dev);
|
||||
void sysirq_uninit(struct drm_device *dev);
|
||||
|
||||
void psb_irq_preinstall(struct drm_device *dev);
|
||||
int psb_irq_postinstall(struct drm_device *dev);
|
||||
void psb_irq_uninstall(struct drm_device *dev);
|
||||
irqreturn_t psb_irq_handler(DRM_IRQ_ARGS);
|
||||
|
||||
int psb_irq_enable_dpst(struct drm_device *dev);
|
||||
int psb_irq_disable_dpst(struct drm_device *dev);
|
||||
void psb_irq_turn_on_dpst(struct drm_device *dev);
|
||||
void psb_irq_turn_off_dpst(struct drm_device *dev);
|
||||
int psb_enable_vblank(struct drm_device *dev, int pipe);
|
||||
void psb_disable_vblank(struct drm_device *dev, int pipe);
|
||||
u32 psb_get_vblank_counter(struct drm_device *dev, int pipe);
|
||||
|
||||
#endif /* _SYSIRQ_H_ */
|
|
@ -0,0 +1,88 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2007, Intel Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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.
|
||||
*
|
||||
* Authors: Thomas Hellstrom <thomas-at-tungstengraphics-dot-com>
|
||||
**************************************************************************/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "psb_drv.h"
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_reg.h"
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
static void psb_lid_timer_func(unsigned long data)
|
||||
{
|
||||
struct drm_psb_private * dev_priv = (struct drm_psb_private *)data;
|
||||
struct drm_device *dev = (struct drm_device *)dev_priv->dev;
|
||||
struct timer_list *lid_timer = &dev_priv->lid_timer;
|
||||
unsigned long irq_flags;
|
||||
u32 *lid_state = dev_priv->lid_state;
|
||||
u32 pp_status;
|
||||
|
||||
if (readl(lid_state) == dev_priv->lid_last_state)
|
||||
goto lid_timer_schedule;
|
||||
|
||||
if ((readl(lid_state)) & 0x01) {
|
||||
/*lid state is open*/
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & PP_ON) == 0);
|
||||
|
||||
/*FIXME: should be backlight level before*/
|
||||
psb_intel_lvds_set_brightness(dev, 100);
|
||||
} else {
|
||||
psb_intel_lvds_set_brightness(dev, 0);
|
||||
|
||||
REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & ~POWER_TARGET_ON);
|
||||
do {
|
||||
pp_status = REG_READ(PP_STATUS);
|
||||
} while ((pp_status & PP_ON) == 0);
|
||||
}
|
||||
dev_priv->lid_last_state = readl(lid_state);
|
||||
|
||||
lid_timer_schedule:
|
||||
spin_lock_irqsave(&dev_priv->lid_lock, irq_flags);
|
||||
if (!timer_pending(lid_timer)) {
|
||||
lid_timer->expires = jiffies + PSB_LID_DELAY;
|
||||
add_timer(lid_timer);
|
||||
}
|
||||
spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags);
|
||||
}
|
||||
|
||||
void psb_lid_timer_init(struct drm_psb_private *dev_priv)
|
||||
{
|
||||
struct timer_list *lid_timer = &dev_priv->lid_timer;
|
||||
unsigned long irq_flags;
|
||||
|
||||
spin_lock_init(&dev_priv->lid_lock);
|
||||
spin_lock_irqsave(&dev_priv->lid_lock, irq_flags);
|
||||
|
||||
init_timer(lid_timer);
|
||||
|
||||
lid_timer->data = (unsigned long)dev_priv;
|
||||
lid_timer->function = psb_lid_timer_func;
|
||||
lid_timer->expires = jiffies + PSB_LID_DELAY;
|
||||
|
||||
add_timer(lid_timer);
|
||||
spin_unlock_irqrestore(&dev_priv->lid_lock, irq_flags);
|
||||
}
|
||||
|
||||
void psb_lid_timer_takedown(struct drm_psb_private *dev_priv)
|
||||
{
|
||||
del_timer_sync(&dev_priv->lid_timer);
|
||||
}
|
||||
|
|
@ -0,0 +1,582 @@
|
|||
/**************************************************************************
|
||||
*
|
||||
* Copyright (c) (2005-2007) Imagination Technologies Limited.
|
||||
* Copyright (c) 2007, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope 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..
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#ifndef _PSB_REG_H_
|
||||
#define _PSB_REG_H_
|
||||
|
||||
#define PSB_CR_CLKGATECTL 0x0000
|
||||
#define _PSB_C_CLKGATECTL_AUTO_MAN_REG (1 << 24)
|
||||
#define _PSB_C_CLKGATECTL_USE_CLKG_SHIFT (20)
|
||||
#define _PSB_C_CLKGATECTL_USE_CLKG_MASK (0x3 << 20)
|
||||
#define _PSB_C_CLKGATECTL_DPM_CLKG_SHIFT (16)
|
||||
#define _PSB_C_CLKGATECTL_DPM_CLKG_MASK (0x3 << 16)
|
||||
#define _PSB_C_CLKGATECTL_TA_CLKG_SHIFT (12)
|
||||
#define _PSB_C_CLKGATECTL_TA_CLKG_MASK (0x3 << 12)
|
||||
#define _PSB_C_CLKGATECTL_TSP_CLKG_SHIFT (8)
|
||||
#define _PSB_C_CLKGATECTL_TSP_CLKG_MASK (0x3 << 8)
|
||||
#define _PSB_C_CLKGATECTL_ISP_CLKG_SHIFT (4)
|
||||
#define _PSB_C_CLKGATECTL_ISP_CLKG_MASK (0x3 << 4)
|
||||
#define _PSB_C_CLKGATECTL_2D_CLKG_SHIFT (0)
|
||||
#define _PSB_C_CLKGATECTL_2D_CLKG_MASK (0x3 << 0)
|
||||
#define _PSB_C_CLKGATECTL_CLKG_ENABLED (0)
|
||||
#define _PSB_C_CLKGATECTL_CLKG_DISABLED (1)
|
||||
#define _PSB_C_CLKGATECTL_CLKG_AUTO (2)
|
||||
|
||||
#define PSB_CR_CORE_ID 0x0010
|
||||
#define _PSB_CC_ID_ID_SHIFT (16)
|
||||
#define _PSB_CC_ID_ID_MASK (0xFFFF << 16)
|
||||
#define _PSB_CC_ID_CONFIG_SHIFT (0)
|
||||
#define _PSB_CC_ID_CONFIG_MASK (0xFFFF << 0)
|
||||
|
||||
#define PSB_CR_CORE_REVISION 0x0014
|
||||
#define _PSB_CC_REVISION_DESIGNER_SHIFT (24)
|
||||
#define _PSB_CC_REVISION_DESIGNER_MASK (0xFF << 24)
|
||||
#define _PSB_CC_REVISION_MAJOR_SHIFT (16)
|
||||
#define _PSB_CC_REVISION_MAJOR_MASK (0xFF << 16)
|
||||
#define _PSB_CC_REVISION_MINOR_SHIFT (8)
|
||||
#define _PSB_CC_REVISION_MINOR_MASK (0xFF << 8)
|
||||
#define _PSB_CC_REVISION_MAINTENANCE_SHIFT (0)
|
||||
#define _PSB_CC_REVISION_MAINTENANCE_MASK (0xFF << 0)
|
||||
|
||||
#define PSB_CR_DESIGNER_REV_FIELD1 0x0018
|
||||
|
||||
#define PSB_CR_SOFT_RESET 0x0080
|
||||
#define _PSB_CS_RESET_TSP_RESET (1 << 6)
|
||||
#define _PSB_CS_RESET_ISP_RESET (1 << 5)
|
||||
#define _PSB_CS_RESET_USE_RESET (1 << 4)
|
||||
#define _PSB_CS_RESET_TA_RESET (1 << 3)
|
||||
#define _PSB_CS_RESET_DPM_RESET (1 << 2)
|
||||
#define _PSB_CS_RESET_TWOD_RESET (1 << 1)
|
||||
#define _PSB_CS_RESET_BIF_RESET (1 << 0)
|
||||
|
||||
#define PSB_CR_DESIGNER_REV_FIELD2 0x001C
|
||||
|
||||
#define PSB_CR_EVENT_HOST_ENABLE2 0x0110
|
||||
|
||||
#define PSB_CR_EVENT_STATUS2 0x0118
|
||||
|
||||
#define PSB_CR_EVENT_HOST_CLEAR2 0x0114
|
||||
#define _PSB_CE2_BIF_REQUESTER_FAULT (1 << 4)
|
||||
|
||||
#define PSB_CR_EVENT_STATUS 0x012C
|
||||
|
||||
#define PSB_CR_EVENT_HOST_ENABLE 0x0130
|
||||
|
||||
#define PSB_CR_EVENT_HOST_CLEAR 0x0134
|
||||
#define _PSB_CE_MASTER_INTERRUPT (1 << 31)
|
||||
#define _PSB_CE_TA_DPM_FAULT (1 << 28)
|
||||
#define _PSB_CE_TWOD_COMPLETE (1 << 27)
|
||||
#define _PSB_CE_DPM_OUT_OF_MEMORY_ZLS (1 << 25)
|
||||
#define _PSB_CE_DPM_TA_MEM_FREE (1 << 24)
|
||||
#define _PSB_CE_PIXELBE_END_RENDER (1 << 18)
|
||||
#define _PSB_CE_SW_EVENT (1 << 14)
|
||||
#define _PSB_CE_TA_FINISHED (1 << 13)
|
||||
#define _PSB_CE_TA_TERMINATE (1 << 12)
|
||||
#define _PSB_CE_DPM_REACHED_MEM_THRESH (1 << 3)
|
||||
#define _PSB_CE_DPM_OUT_OF_MEMORY_GBL (1 << 2)
|
||||
#define _PSB_CE_DPM_OUT_OF_MEMORY_MT (1 << 1)
|
||||
#define _PSB_CE_DPM_3D_MEM_FREE (1 << 0)
|
||||
|
||||
|
||||
#define PSB_USE_OFFSET_MASK 0x0007FFFF
|
||||
#define PSB_USE_OFFSET_SIZE (PSB_USE_OFFSET_MASK + 1)
|
||||
#define PSB_CR_USE_CODE_BASE0 0x0A0C
|
||||
#define PSB_CR_USE_CODE_BASE1 0x0A10
|
||||
#define PSB_CR_USE_CODE_BASE2 0x0A14
|
||||
#define PSB_CR_USE_CODE_BASE3 0x0A18
|
||||
#define PSB_CR_USE_CODE_BASE4 0x0A1C
|
||||
#define PSB_CR_USE_CODE_BASE5 0x0A20
|
||||
#define PSB_CR_USE_CODE_BASE6 0x0A24
|
||||
#define PSB_CR_USE_CODE_BASE7 0x0A28
|
||||
#define PSB_CR_USE_CODE_BASE8 0x0A2C
|
||||
#define PSB_CR_USE_CODE_BASE9 0x0A30
|
||||
#define PSB_CR_USE_CODE_BASE10 0x0A34
|
||||
#define PSB_CR_USE_CODE_BASE11 0x0A38
|
||||
#define PSB_CR_USE_CODE_BASE12 0x0A3C
|
||||
#define PSB_CR_USE_CODE_BASE13 0x0A40
|
||||
#define PSB_CR_USE_CODE_BASE14 0x0A44
|
||||
#define PSB_CR_USE_CODE_BASE15 0x0A48
|
||||
#define PSB_CR_USE_CODE_BASE(_i) (0x0A0C + ((_i) << 2))
|
||||
#define _PSB_CUC_BASE_DM_SHIFT (25)
|
||||
#define _PSB_CUC_BASE_DM_MASK (0x3 << 25)
|
||||
#define _PSB_CUC_BASE_ADDR_SHIFT (0) /* 1024-bit aligned address? */
|
||||
#define _PSB_CUC_BASE_ADDR_ALIGNSHIFT (7)
|
||||
#define _PSB_CUC_BASE_ADDR_MASK (0x1FFFFFF << 0)
|
||||
#define _PSB_CUC_DM_VERTEX (0)
|
||||
#define _PSB_CUC_DM_PIXEL (1)
|
||||
#define _PSB_CUC_DM_RESERVED (2)
|
||||
#define _PSB_CUC_DM_EDM (3)
|
||||
|
||||
#define PSB_CR_PDS_EXEC_BASE 0x0AB8
|
||||
#define _PSB_CR_PDS_EXEC_BASE_ADDR_SHIFT (20) /* 1MB aligned address */
|
||||
#define _PSB_CR_PDS_EXEC_BASE_ADDR_ALIGNSHIFT (20)
|
||||
|
||||
#define PSB_CR_EVENT_KICKER 0x0AC4
|
||||
#define _PSB_CE_KICKER_ADDRESS_SHIFT (4) /* 128-bit aligned address */
|
||||
|
||||
#define PSB_CR_EVENT_KICK 0x0AC8
|
||||
#define _PSB_CE_KICK_NOW (1 << 0)
|
||||
|
||||
#define PSB_CR_BIF_DIR_LIST_BASE1 0x0C38
|
||||
|
||||
#define PSB_CR_BIF_CTRL 0x0C00
|
||||
#define _PSB_CB_CTRL_CLEAR_FAULT (1 << 4)
|
||||
#define _PSB_CB_CTRL_INVALDC (1 << 3)
|
||||
#define _PSB_CB_CTRL_FLUSH (1 << 2)
|
||||
|
||||
#define PSB_CR_BIF_INT_STAT 0x0C04
|
||||
|
||||
#define PSB_CR_BIF_FAULT 0x0C08
|
||||
#define _PSB_CBI_STAT_PF_N_RW (1 << 14)
|
||||
#define _PSB_CBI_STAT_FAULT_SHIFT (0)
|
||||
#define _PSB_CBI_STAT_FAULT_MASK (0x3FFF << 0)
|
||||
#define _PSB_CBI_STAT_FAULT_CACHE (1 << 1)
|
||||
#define _PSB_CBI_STAT_FAULT_TA (1 << 2)
|
||||
#define _PSB_CBI_STAT_FAULT_VDM (1 << 3)
|
||||
#define _PSB_CBI_STAT_FAULT_2D (1 << 4)
|
||||
#define _PSB_CBI_STAT_FAULT_PBE (1 << 5)
|
||||
#define _PSB_CBI_STAT_FAULT_TSP (1 << 6)
|
||||
#define _PSB_CBI_STAT_FAULT_ISP (1 << 7)
|
||||
#define _PSB_CBI_STAT_FAULT_USSEPDS (1 << 8)
|
||||
#define _PSB_CBI_STAT_FAULT_HOST (1 << 9)
|
||||
|
||||
#define PSB_CR_BIF_BANK0 0x0C78
|
||||
#define PSB_CR_BIF_BANK1 0x0C7C
|
||||
#define PSB_CR_BIF_DIR_LIST_BASE0 0x0C84
|
||||
#define PSB_CR_BIF_TWOD_REQ_BASE 0x0C88
|
||||
#define PSB_CR_BIF_3D_REQ_BASE 0x0CAC
|
||||
|
||||
#define PSB_CR_2D_SOCIF 0x0E18
|
||||
#define _PSB_C2_SOCIF_FREESPACE_SHIFT (0)
|
||||
#define _PSB_C2_SOCIF_FREESPACE_MASK (0xFF << 0)
|
||||
#define _PSB_C2_SOCIF_EMPTY (0x80 << 0)
|
||||
|
||||
#define PSB_CR_2D_BLIT_STATUS 0x0E04
|
||||
#define _PSB_C2B_STATUS_BUSY (1 << 24)
|
||||
#define _PSB_C2B_STATUS_COMPLETE_SHIFT (0)
|
||||
#define _PSB_C2B_STATUS_COMPLETE_MASK (0xFFFFFF << 0)
|
||||
|
||||
/*
|
||||
* 2D defs.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 2D Slave Port Data : Block Header's Object Type
|
||||
*/
|
||||
|
||||
#define PSB_2D_CLIP_BH (0x00000000)
|
||||
#define PSB_2D_PAT_BH (0x10000000)
|
||||
#define PSB_2D_CTRL_BH (0x20000000)
|
||||
#define PSB_2D_SRC_OFF_BH (0x30000000)
|
||||
#define PSB_2D_MASK_OFF_BH (0x40000000)
|
||||
#define PSB_2D_RESERVED1_BH (0x50000000)
|
||||
#define PSB_2D_RESERVED2_BH (0x60000000)
|
||||
#define PSB_2D_FENCE_BH (0x70000000)
|
||||
#define PSB_2D_BLIT_BH (0x80000000)
|
||||
#define PSB_2D_SRC_SURF_BH (0x90000000)
|
||||
#define PSB_2D_DST_SURF_BH (0xA0000000)
|
||||
#define PSB_2D_PAT_SURF_BH (0xB0000000)
|
||||
#define PSB_2D_SRC_PAL_BH (0xC0000000)
|
||||
#define PSB_2D_PAT_PAL_BH (0xD0000000)
|
||||
#define PSB_2D_MASK_SURF_BH (0xE0000000)
|
||||
#define PSB_2D_FLUSH_BH (0xF0000000)
|
||||
|
||||
/*
|
||||
* Clip Definition block (PSB_2D_CLIP_BH)
|
||||
*/
|
||||
#define PSB_2D_CLIPCOUNT_MAX (1)
|
||||
#define PSB_2D_CLIPCOUNT_MASK (0x00000000)
|
||||
#define PSB_2D_CLIPCOUNT_CLRMASK (0xFFFFFFFF)
|
||||
#define PSB_2D_CLIPCOUNT_SHIFT (0)
|
||||
/* clip rectangle min & max */
|
||||
#define PSB_2D_CLIP_XMAX_MASK (0x00FFF000)
|
||||
#define PSB_2D_CLIP_XMAX_CLRMASK (0xFF000FFF)
|
||||
#define PSB_2D_CLIP_XMAX_SHIFT (12)
|
||||
#define PSB_2D_CLIP_XMIN_MASK (0x00000FFF)
|
||||
#define PSB_2D_CLIP_XMIN_CLRMASK (0x00FFF000)
|
||||
#define PSB_2D_CLIP_XMIN_SHIFT (0)
|
||||
/* clip rectangle offset */
|
||||
#define PSB_2D_CLIP_YMAX_MASK (0x00FFF000)
|
||||
#define PSB_2D_CLIP_YMAX_CLRMASK (0xFF000FFF)
|
||||
#define PSB_2D_CLIP_YMAX_SHIFT (12)
|
||||
#define PSB_2D_CLIP_YMIN_MASK (0x00000FFF)
|
||||
#define PSB_2D_CLIP_YMIN_CLRMASK (0x00FFF000)
|
||||
#define PSB_2D_CLIP_YMIN_SHIFT (0)
|
||||
|
||||
/*
|
||||
* Pattern Control (PSB_2D_PAT_BH)
|
||||
*/
|
||||
#define PSB_2D_PAT_HEIGHT_MASK (0x0000001F)
|
||||
#define PSB_2D_PAT_HEIGHT_SHIFT (0)
|
||||
#define PSB_2D_PAT_WIDTH_MASK (0x000003E0)
|
||||
#define PSB_2D_PAT_WIDTH_SHIFT (5)
|
||||
#define PSB_2D_PAT_YSTART_MASK (0x00007C00)
|
||||
#define PSB_2D_PAT_YSTART_SHIFT (10)
|
||||
#define PSB_2D_PAT_XSTART_MASK (0x000F8000)
|
||||
#define PSB_2D_PAT_XSTART_SHIFT (15)
|
||||
|
||||
/*
|
||||
* 2D Control block (PSB_2D_CTRL_BH)
|
||||
*/
|
||||
/* Present Flags */
|
||||
#define PSB_2D_SRCCK_CTRL (0x00000001)
|
||||
#define PSB_2D_DSTCK_CTRL (0x00000002)
|
||||
#define PSB_2D_ALPHA_CTRL (0x00000004)
|
||||
/* Colour Key Colour (SRC/DST)*/
|
||||
#define PSB_2D_CK_COL_MASK (0xFFFFFFFF)
|
||||
#define PSB_2D_CK_COL_CLRMASK (0x00000000)
|
||||
#define PSB_2D_CK_COL_SHIFT (0)
|
||||
/* Colour Key Mask (SRC/DST)*/
|
||||
#define PSB_2D_CK_MASK_MASK (0xFFFFFFFF)
|
||||
#define PSB_2D_CK_MASK_CLRMASK (0x00000000)
|
||||
#define PSB_2D_CK_MASK_SHIFT (0)
|
||||
/* Alpha Control (Alpha/RGB)*/
|
||||
#define PSB_2D_GBLALPHA_MASK (0x000FF000)
|
||||
#define PSB_2D_GBLALPHA_CLRMASK (0xFFF00FFF)
|
||||
#define PSB_2D_GBLALPHA_SHIFT (12)
|
||||
#define PSB_2D_SRCALPHA_OP_MASK (0x00700000)
|
||||
#define PSB_2D_SRCALPHA_OP_CLRMASK (0xFF8FFFFF)
|
||||
#define PSB_2D_SRCALPHA_OP_SHIFT (20)
|
||||
#define PSB_2D_SRCALPHA_OP_ONE (0x00000000)
|
||||
#define PSB_2D_SRCALPHA_OP_SRC (0x00100000)
|
||||
#define PSB_2D_SRCALPHA_OP_DST (0x00200000)
|
||||
#define PSB_2D_SRCALPHA_OP_SG (0x00300000)
|
||||
#define PSB_2D_SRCALPHA_OP_DG (0x00400000)
|
||||
#define PSB_2D_SRCALPHA_OP_GBL (0x00500000)
|
||||
#define PSB_2D_SRCALPHA_OP_ZERO (0x00600000)
|
||||
#define PSB_2D_SRCALPHA_INVERT (0x00800000)
|
||||
#define PSB_2D_SRCALPHA_INVERT_CLR (0xFF7FFFFF)
|
||||
#define PSB_2D_DSTALPHA_OP_MASK (0x07000000)
|
||||
#define PSB_2D_DSTALPHA_OP_CLRMASK (0xF8FFFFFF)
|
||||
#define PSB_2D_DSTALPHA_OP_SHIFT (24)
|
||||
#define PSB_2D_DSTALPHA_OP_ONE (0x00000000)
|
||||
#define PSB_2D_DSTALPHA_OP_SRC (0x01000000)
|
||||
#define PSB_2D_DSTALPHA_OP_DST (0x02000000)
|
||||
#define PSB_2D_DSTALPHA_OP_SG (0x03000000)
|
||||
#define PSB_2D_DSTALPHA_OP_DG (0x04000000)
|
||||
#define PSB_2D_DSTALPHA_OP_GBL (0x05000000)
|
||||
#define PSB_2D_DSTALPHA_OP_ZERO (0x06000000)
|
||||
#define PSB_2D_DSTALPHA_INVERT (0x08000000)
|
||||
#define PSB_2D_DSTALPHA_INVERT_CLR (0xF7FFFFFF)
|
||||
|
||||
#define PSB_2D_PRE_MULTIPLICATION_ENABLE (0x10000000)
|
||||
#define PSB_2D_PRE_MULTIPLICATION_CLRMASK (0xEFFFFFFF)
|
||||
#define PSB_2D_ZERO_SOURCE_ALPHA_ENABLE (0x20000000)
|
||||
#define PSB_2D_ZERO_SOURCE_ALPHA_CLRMASK (0xDFFFFFFF)
|
||||
|
||||
/*
|
||||
*Source Offset (PSB_2D_SRC_OFF_BH)
|
||||
*/
|
||||
#define PSB_2D_SRCOFF_XSTART_MASK ((0x00000FFF) << 12)
|
||||
#define PSB_2D_SRCOFF_XSTART_SHIFT (12)
|
||||
#define PSB_2D_SRCOFF_YSTART_MASK (0x00000FFF)
|
||||
#define PSB_2D_SRCOFF_YSTART_SHIFT (0)
|
||||
|
||||
/*
|
||||
* Mask Offset (PSB_2D_MASK_OFF_BH)
|
||||
*/
|
||||
#define PSB_2D_MASKOFF_XSTART_MASK ((0x00000FFF) << 12)
|
||||
#define PSB_2D_MASKOFF_XSTART_SHIFT (12)
|
||||
#define PSB_2D_MASKOFF_YSTART_MASK (0x00000FFF)
|
||||
#define PSB_2D_MASKOFF_YSTART_SHIFT (0)
|
||||
|
||||
/*
|
||||
* 2D Fence (see PSB_2D_FENCE_BH): bits 0:27 are ignored
|
||||
*/
|
||||
|
||||
/*
|
||||
*Blit Rectangle (PSB_2D_BLIT_BH)
|
||||
*/
|
||||
|
||||
#define PSB_2D_ROT_MASK (3 << 25)
|
||||
#define PSB_2D_ROT_CLRMASK (~PSB_2D_ROT_MASK)
|
||||
#define PSB_2D_ROT_NONE (0 << 25)
|
||||
#define PSB_2D_ROT_90DEGS (1 << 25)
|
||||
#define PSB_2D_ROT_180DEGS (2 << 25)
|
||||
#define PSB_2D_ROT_270DEGS (3 << 25)
|
||||
|
||||
#define PSB_2D_COPYORDER_MASK (3 << 23)
|
||||
#define PSB_2D_COPYORDER_CLRMASK (~PSB_2D_COPYORDER_MASK)
|
||||
#define PSB_2D_COPYORDER_TL2BR (0 << 23)
|
||||
#define PSB_2D_COPYORDER_BR2TL (1 << 23)
|
||||
#define PSB_2D_COPYORDER_TR2BL (2 << 23)
|
||||
#define PSB_2D_COPYORDER_BL2TR (3 << 23)
|
||||
|
||||
#define PSB_2D_DSTCK_CLRMASK (0xFF9FFFFF)
|
||||
#define PSB_2D_DSTCK_DISABLE (0x00000000)
|
||||
#define PSB_2D_DSTCK_PASS (0x00200000)
|
||||
#define PSB_2D_DSTCK_REJECT (0x00400000)
|
||||
|
||||
#define PSB_2D_SRCCK_CLRMASK (0xFFE7FFFF)
|
||||
#define PSB_2D_SRCCK_DISABLE (0x00000000)
|
||||
#define PSB_2D_SRCCK_PASS (0x00080000)
|
||||
#define PSB_2D_SRCCK_REJECT (0x00100000)
|
||||
|
||||
#define PSB_2D_CLIP_ENABLE (0x00040000)
|
||||
|
||||
#define PSB_2D_ALPHA_ENABLE (0x00020000)
|
||||
|
||||
#define PSB_2D_PAT_CLRMASK (0xFFFEFFFF)
|
||||
#define PSB_2D_PAT_MASK (0x00010000)
|
||||
#define PSB_2D_USE_PAT (0x00010000)
|
||||
#define PSB_2D_USE_FILL (0x00000000)
|
||||
/*
|
||||
* Tungsten Graphics note on rop codes: If rop A and rop B are
|
||||
* identical, the mask surface will not be read and need not be
|
||||
* set up.
|
||||
*/
|
||||
|
||||
#define PSB_2D_ROP3B_MASK (0x0000FF00)
|
||||
#define PSB_2D_ROP3B_CLRMASK (0xFFFF00FF)
|
||||
#define PSB_2D_ROP3B_SHIFT (8)
|
||||
/* rop code A */
|
||||
#define PSB_2D_ROP3A_MASK (0x000000FF)
|
||||
#define PSB_2D_ROP3A_CLRMASK (0xFFFFFF00)
|
||||
#define PSB_2D_ROP3A_SHIFT (0)
|
||||
|
||||
#define PSB_2D_ROP4_MASK (0x0000FFFF)
|
||||
/*
|
||||
* DWORD0: (Only pass if Pattern control == Use Fill Colour)
|
||||
* Fill Colour RGBA8888
|
||||
*/
|
||||
#define PSB_2D_FILLCOLOUR_MASK (0xFFFFFFFF)
|
||||
#define PSB_2D_FILLCOLOUR_SHIFT (0)
|
||||
/*
|
||||
* DWORD1: (Always Present)
|
||||
* X Start (Dest)
|
||||
* Y Start (Dest)
|
||||
*/
|
||||
#define PSB_2D_DST_XSTART_MASK (0x00FFF000)
|
||||
#define PSB_2D_DST_XSTART_CLRMASK (0xFF000FFF)
|
||||
#define PSB_2D_DST_XSTART_SHIFT (12)
|
||||
#define PSB_2D_DST_YSTART_MASK (0x00000FFF)
|
||||
#define PSB_2D_DST_YSTART_CLRMASK (0xFFFFF000)
|
||||
#define PSB_2D_DST_YSTART_SHIFT (0)
|
||||
/*
|
||||
* DWORD2: (Always Present)
|
||||
* X Size (Dest)
|
||||
* Y Size (Dest)
|
||||
*/
|
||||
#define PSB_2D_DST_XSIZE_MASK (0x00FFF000)
|
||||
#define PSB_2D_DST_XSIZE_CLRMASK (0xFF000FFF)
|
||||
#define PSB_2D_DST_XSIZE_SHIFT (12)
|
||||
#define PSB_2D_DST_YSIZE_MASK (0x00000FFF)
|
||||
#define PSB_2D_DST_YSIZE_CLRMASK (0xFFFFF000)
|
||||
#define PSB_2D_DST_YSIZE_SHIFT (0)
|
||||
|
||||
/*
|
||||
* Source Surface (PSB_2D_SRC_SURF_BH)
|
||||
*/
|
||||
/*
|
||||
* WORD 0
|
||||
*/
|
||||
|
||||
#define PSB_2D_SRC_FORMAT_MASK (0x00078000)
|
||||
#define PSB_2D_SRC_1_PAL (0x00000000)
|
||||
#define PSB_2D_SRC_2_PAL (0x00008000)
|
||||
#define PSB_2D_SRC_4_PAL (0x00010000)
|
||||
#define PSB_2D_SRC_8_PAL (0x00018000)
|
||||
#define PSB_2D_SRC_8_ALPHA (0x00020000)
|
||||
#define PSB_2D_SRC_4_ALPHA (0x00028000)
|
||||
#define PSB_2D_SRC_332RGB (0x00030000)
|
||||
#define PSB_2D_SRC_4444ARGB (0x00038000)
|
||||
#define PSB_2D_SRC_555RGB (0x00040000)
|
||||
#define PSB_2D_SRC_1555ARGB (0x00048000)
|
||||
#define PSB_2D_SRC_565RGB (0x00050000)
|
||||
#define PSB_2D_SRC_0888ARGB (0x00058000)
|
||||
#define PSB_2D_SRC_8888ARGB (0x00060000)
|
||||
#define PSB_2D_SRC_8888UYVY (0x00068000)
|
||||
#define PSB_2D_SRC_RESERVED (0x00070000)
|
||||
#define PSB_2D_SRC_1555ARGB_LOOKUP (0x00078000)
|
||||
|
||||
|
||||
#define PSB_2D_SRC_STRIDE_MASK (0x00007FFF)
|
||||
#define PSB_2D_SRC_STRIDE_CLRMASK (0xFFFF8000)
|
||||
#define PSB_2D_SRC_STRIDE_SHIFT (0)
|
||||
/*
|
||||
* WORD 1 - Base Address
|
||||
*/
|
||||
#define PSB_2D_SRC_ADDR_MASK (0x0FFFFFFC)
|
||||
#define PSB_2D_SRC_ADDR_CLRMASK (0x00000003)
|
||||
#define PSB_2D_SRC_ADDR_SHIFT (2)
|
||||
#define PSB_2D_SRC_ADDR_ALIGNSHIFT (2)
|
||||
|
||||
/*
|
||||
* Pattern Surface (PSB_2D_PAT_SURF_BH)
|
||||
*/
|
||||
/*
|
||||
* WORD 0
|
||||
*/
|
||||
|
||||
#define PSB_2D_PAT_FORMAT_MASK (0x00078000)
|
||||
#define PSB_2D_PAT_1_PAL (0x00000000)
|
||||
#define PSB_2D_PAT_2_PAL (0x00008000)
|
||||
#define PSB_2D_PAT_4_PAL (0x00010000)
|
||||
#define PSB_2D_PAT_8_PAL (0x00018000)
|
||||
#define PSB_2D_PAT_8_ALPHA (0x00020000)
|
||||
#define PSB_2D_PAT_4_ALPHA (0x00028000)
|
||||
#define PSB_2D_PAT_332RGB (0x00030000)
|
||||
#define PSB_2D_PAT_4444ARGB (0x00038000)
|
||||
#define PSB_2D_PAT_555RGB (0x00040000)
|
||||
#define PSB_2D_PAT_1555ARGB (0x00048000)
|
||||
#define PSB_2D_PAT_565RGB (0x00050000)
|
||||
#define PSB_2D_PAT_0888ARGB (0x00058000)
|
||||
#define PSB_2D_PAT_8888ARGB (0x00060000)
|
||||
|
||||
#define PSB_2D_PAT_STRIDE_MASK (0x00007FFF)
|
||||
#define PSB_2D_PAT_STRIDE_CLRMASK (0xFFFF8000)
|
||||
#define PSB_2D_PAT_STRIDE_SHIFT (0)
|
||||
/*
|
||||
* WORD 1 - Base Address
|
||||
*/
|
||||
#define PSB_2D_PAT_ADDR_MASK (0x0FFFFFFC)
|
||||
#define PSB_2D_PAT_ADDR_CLRMASK (0x00000003)
|
||||
#define PSB_2D_PAT_ADDR_SHIFT (2)
|
||||
#define PSB_2D_PAT_ADDR_ALIGNSHIFT (2)
|
||||
|
||||
/*
|
||||
* Destination Surface (PSB_2D_DST_SURF_BH)
|
||||
*/
|
||||
/*
|
||||
* WORD 0
|
||||
*/
|
||||
|
||||
#define PSB_2D_DST_FORMAT_MASK (0x00078000)
|
||||
#define PSB_2D_DST_332RGB (0x00030000)
|
||||
#define PSB_2D_DST_4444ARGB (0x00038000)
|
||||
#define PSB_2D_DST_555RGB (0x00040000)
|
||||
#define PSB_2D_DST_1555ARGB (0x00048000)
|
||||
#define PSB_2D_DST_565RGB (0x00050000)
|
||||
#define PSB_2D_DST_0888ARGB (0x00058000)
|
||||
#define PSB_2D_DST_8888ARGB (0x00060000)
|
||||
#define PSB_2D_DST_8888AYUV (0x00070000)
|
||||
|
||||
#define PSB_2D_DST_STRIDE_MASK (0x00007FFF)
|
||||
#define PSB_2D_DST_STRIDE_CLRMASK (0xFFFF8000)
|
||||
#define PSB_2D_DST_STRIDE_SHIFT (0)
|
||||
/*
|
||||
* WORD 1 - Base Address
|
||||
*/
|
||||
#define PSB_2D_DST_ADDR_MASK (0x0FFFFFFC)
|
||||
#define PSB_2D_DST_ADDR_CLRMASK (0x00000003)
|
||||
#define PSB_2D_DST_ADDR_SHIFT (2)
|
||||
#define PSB_2D_DST_ADDR_ALIGNSHIFT (2)
|
||||
|
||||
/*
|
||||
* Mask Surface (PSB_2D_MASK_SURF_BH)
|
||||
*/
|
||||
/*
|
||||
* WORD 0
|
||||
*/
|
||||
#define PSB_2D_MASK_STRIDE_MASK (0x00007FFF)
|
||||
#define PSB_2D_MASK_STRIDE_CLRMASK (0xFFFF8000)
|
||||
#define PSB_2D_MASK_STRIDE_SHIFT (0)
|
||||
/*
|
||||
* WORD 1 - Base Address
|
||||
*/
|
||||
#define PSB_2D_MASK_ADDR_MASK (0x0FFFFFFC)
|
||||
#define PSB_2D_MASK_ADDR_CLRMASK (0x00000003)
|
||||
#define PSB_2D_MASK_ADDR_SHIFT (2)
|
||||
#define PSB_2D_MASK_ADDR_ALIGNSHIFT (2)
|
||||
|
||||
/*
|
||||
* Source Palette (PSB_2D_SRC_PAL_BH)
|
||||
*/
|
||||
|
||||
#define PSB_2D_SRCPAL_ADDR_SHIFT (0)
|
||||
#define PSB_2D_SRCPAL_ADDR_CLRMASK (0xF0000007)
|
||||
#define PSB_2D_SRCPAL_ADDR_MASK (0x0FFFFFF8)
|
||||
#define PSB_2D_SRCPAL_BYTEALIGN (1024)
|
||||
|
||||
/*
|
||||
* Pattern Palette (PSB_2D_PAT_PAL_BH)
|
||||
*/
|
||||
|
||||
#define PSB_2D_PATPAL_ADDR_SHIFT (0)
|
||||
#define PSB_2D_PATPAL_ADDR_CLRMASK (0xF0000007)
|
||||
#define PSB_2D_PATPAL_ADDR_MASK (0x0FFFFFF8)
|
||||
#define PSB_2D_PATPAL_BYTEALIGN (1024)
|
||||
|
||||
/*
|
||||
* Rop3 Codes (2 LS bytes)
|
||||
*/
|
||||
|
||||
#define PSB_2D_ROP3_SRCCOPY (0xCCCC)
|
||||
#define PSB_2D_ROP3_PATCOPY (0xF0F0)
|
||||
#define PSB_2D_ROP3_WHITENESS (0xFFFF)
|
||||
#define PSB_2D_ROP3_BLACKNESS (0x0000)
|
||||
#define PSB_2D_ROP3_SRC (0xCC)
|
||||
#define PSB_2D_ROP3_PAT (0xF0)
|
||||
#define PSB_2D_ROP3_DST (0xAA)
|
||||
|
||||
/*
|
||||
* Sizes.
|
||||
*/
|
||||
|
||||
#define PSB_SCENE_HW_COOKIE_SIZE 16
|
||||
#define PSB_TA_MEM_HW_COOKIE_SIZE 16
|
||||
|
||||
/*
|
||||
* Scene stuff.
|
||||
*/
|
||||
|
||||
#define PSB_NUM_HW_SCENES 2
|
||||
|
||||
/*
|
||||
* Scheduler completion actions.
|
||||
*/
|
||||
|
||||
#define PSB_RASTER_BLOCK 0
|
||||
#define PSB_RASTER 1
|
||||
#define PSB_RETURN 2
|
||||
#define PSB_TA 3
|
||||
|
||||
/* Power management */
|
||||
#define PSB_PUNIT_PORT 0x04
|
||||
#define PSB_OSPMBA 0x78
|
||||
#define PSB_APMBA 0x7a
|
||||
#define PSB_APM_CMD 0x0
|
||||
#define PSB_APM_STS 0x04
|
||||
#define PSB_PWRGT_VID_ENC_MASK 0x30
|
||||
#define PSB_PWRGT_VID_DEC_MASK 0xc
|
||||
#define PSB_PWRGT_GL3_MASK 0xc0
|
||||
|
||||
#define PSB_PM_SSC 0x20
|
||||
#define PSB_PM_SSS 0x30
|
||||
#define PSB_PWRGT_DISPLAY_MASK 0xc /*on a different BA than video/gfx*/
|
||||
#define MDFLD_PWRGT_DISPLAY_A_CNTR 0x0000000c
|
||||
#define MDFLD_PWRGT_DISPLAY_B_CNTR 0x0000c000
|
||||
#define MDFLD_PWRGT_DISPLAY_C_CNTR 0x00030000
|
||||
#define MDFLD_PWRGT_DISP_MIPI_CNTR 0x000c0000
|
||||
#define MDFLD_PWRGT_DISPLAY_CNTR (MDFLD_PWRGT_DISPLAY_A_CNTR | MDFLD_PWRGT_DISPLAY_B_CNTR | MDFLD_PWRGT_DISPLAY_C_CNTR | MDFLD_PWRGT_DISP_MIPI_CNTR) /* 0x000fc00c */
|
||||
/* Display SSS register bits are different in A0 vs. B0 */
|
||||
#define PSB_PWRGT_GFX_MASK 0x3
|
||||
#define MDFLD_PWRGT_DISPLAY_A_STS 0x000000c0
|
||||
#define MDFLD_PWRGT_DISPLAY_B_STS 0x00000300
|
||||
#define MDFLD_PWRGT_DISPLAY_C_STS 0x00000c00
|
||||
#define PSB_PWRGT_GFX_MASK_B0 0xc3
|
||||
#define MDFLD_PWRGT_DISPLAY_A_STS_B0 0x0000000c
|
||||
#define MDFLD_PWRGT_DISPLAY_B_STS_B0 0x0000c000
|
||||
#define MDFLD_PWRGT_DISPLAY_C_STS_B0 0x00030000
|
||||
#define MDFLD_PWRGT_DISP_MIPI_STS 0x000c0000
|
||||
#define MDFLD_PWRGT_DISPLAY_STS_A0 (MDFLD_PWRGT_DISPLAY_A_STS | MDFLD_PWRGT_DISPLAY_B_STS | MDFLD_PWRGT_DISPLAY_C_STS | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */
|
||||
#define MDFLD_PWRGT_DISPLAY_STS_B0 (MDFLD_PWRGT_DISPLAY_A_STS_B0 | MDFLD_PWRGT_DISPLAY_B_STS_B0 | MDFLD_PWRGT_DISPLAY_C_STS_B0 | MDFLD_PWRGT_DISP_MIPI_STS) /* 0x000fc00c */
|
||||
#endif
|
|
@ -222,8 +222,6 @@ static int i810_dma_cleanup(struct drm_device *dev)
|
|||
pci_free_consistent(dev->pdev, PAGE_SIZE,
|
||||
dev_priv->hw_status_page,
|
||||
dev_priv->dma_status_page);
|
||||
/* Need to rewrite hardware status page */
|
||||
I810_WRITE(0x02080, 0x1ffff000);
|
||||
}
|
||||
kfree(dev->dev_private);
|
||||
dev->dev_private = NULL;
|
||||
|
@ -888,7 +886,7 @@ static int i810_flush_queue(struct drm_device *dev)
|
|||
}
|
||||
|
||||
/* Must be called with the lock held */
|
||||
static void i810_reclaim_buffers(struct drm_device *dev,
|
||||
void i810_driver_reclaim_buffers(struct drm_device *dev,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
struct drm_device_dma *dma = dev->dma;
|
||||
|
@ -1225,12 +1223,17 @@ void i810_driver_preclose(struct drm_device *dev, struct drm_file *file_priv)
|
|||
if (dev_priv->page_flipping)
|
||||
i810_do_cleanup_pageflip(dev);
|
||||
}
|
||||
}
|
||||
|
||||
void i810_driver_reclaim_buffers_locked(struct drm_device *dev,
|
||||
struct drm_file *file_priv)
|
||||
{
|
||||
i810_reclaim_buffers(dev, file_priv);
|
||||
if (file_priv->master && file_priv->master->lock.hw_lock) {
|
||||
drm_idlelock_take(&file_priv->master->lock);
|
||||
i810_driver_reclaim_buffers(dev, file_priv);
|
||||
drm_idlelock_release(&file_priv->master->lock);
|
||||
} else {
|
||||
/* master disappeared, clean up stuff anyway and hope nothing
|
||||
* goes wrong */
|
||||
i810_driver_reclaim_buffers(dev, file_priv);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int i810_driver_dma_quiescent(struct drm_device *dev)
|
||||
|
|
|
@ -43,6 +43,17 @@ static struct pci_device_id pciidlist[] = {
|
|||
i810_PCI_IDS
|
||||
};
|
||||
|
||||
static const struct file_operations i810_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static struct drm_driver driver = {
|
||||
.driver_features =
|
||||
DRIVER_USE_AGP | DRIVER_REQUIRE_AGP | DRIVER_USE_MTRR |
|
||||
|
@ -52,20 +63,9 @@ static struct drm_driver driver = {
|
|||
.lastclose = i810_driver_lastclose,
|
||||
.preclose = i810_driver_preclose,
|
||||
.device_is_agp = i810_driver_device_is_agp,
|
||||
.reclaim_buffers_locked = i810_driver_reclaim_buffers_locked,
|
||||
.dma_quiescent = i810_driver_dma_quiescent,
|
||||
.ioctls = i810_ioctls,
|
||||
.fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.llseek = noop_llseek,
|
||||
},
|
||||
|
||||
.fops = &i810_driver_fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
|
|
|
@ -116,14 +116,12 @@ typedef struct drm_i810_private {
|
|||
|
||||
/* i810_dma.c */
|
||||
extern int i810_driver_dma_quiescent(struct drm_device *dev);
|
||||
extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev,
|
||||
struct drm_file *file_priv);
|
||||
void i810_driver_reclaim_buffers(struct drm_device *dev,
|
||||
struct drm_file *file_priv);
|
||||
extern int i810_driver_load(struct drm_device *, unsigned long flags);
|
||||
extern void i810_driver_lastclose(struct drm_device *dev);
|
||||
extern void i810_driver_preclose(struct drm_device *dev,
|
||||
struct drm_file *file_priv);
|
||||
extern void i810_driver_reclaim_buffers_locked(struct drm_device *dev,
|
||||
struct drm_file *file_priv);
|
||||
extern int i810_driver_device_is_agp(struct drm_device *dev);
|
||||
|
||||
extern long i810_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
|
|
@ -28,6 +28,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
|
|||
intel_dvo.o \
|
||||
intel_ringbuffer.o \
|
||||
intel_overlay.o \
|
||||
intel_sprite.o \
|
||||
intel_opregion.o \
|
||||
dvo_ch7xxx.o \
|
||||
dvo_ch7017.o \
|
||||
|
|
|
@ -1001,7 +1001,7 @@ static int i915_inttoext_table(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int i915_drpc_info(struct seq_file *m, void *unused)
|
||||
static int ironlake_drpc_info(struct seq_file *m)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
|
@ -1068,6 +1068,90 @@ static int i915_drpc_info(struct seq_file *m, void *unused)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int gen6_drpc_info(struct seq_file *m)
|
||||
{
|
||||
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 rpmodectl1, gt_core_status, rcctl1;
|
||||
int count=0, ret;
|
||||
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (atomic_read(&dev_priv->forcewake_count)) {
|
||||
seq_printf(m, "RC information inaccurate because userspace "
|
||||
"holds a reference \n");
|
||||
} else {
|
||||
/* NB: we cannot use forcewake, else we read the wrong values */
|
||||
while (count++ < 50 && (I915_READ_NOTRACE(FORCEWAKE_ACK) & 1))
|
||||
udelay(10);
|
||||
seq_printf(m, "RC information accurate: %s\n", yesno(count < 51));
|
||||
}
|
||||
|
||||
gt_core_status = readl(dev_priv->regs + GEN6_GT_CORE_STATUS);
|
||||
trace_i915_reg_rw(false, GEN6_GT_CORE_STATUS, gt_core_status, 4);
|
||||
|
||||
rpmodectl1 = I915_READ(GEN6_RP_CONTROL);
|
||||
rcctl1 = I915_READ(GEN6_RC_CONTROL);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
seq_printf(m, "Video Turbo Mode: %s\n",
|
||||
yesno(rpmodectl1 & GEN6_RP_MEDIA_TURBO));
|
||||
seq_printf(m, "HW control enabled: %s\n",
|
||||
yesno(rpmodectl1 & GEN6_RP_ENABLE));
|
||||
seq_printf(m, "SW control enabled: %s\n",
|
||||
yesno((rpmodectl1 & GEN6_RP_MEDIA_MODE_MASK) ==
|
||||
GEN6_RP_MEDIA_SW_MODE));
|
||||
seq_printf(m, "RC6 Enabled: %s\n",
|
||||
yesno(rcctl1 & GEN6_RC_CTL_RC1e_ENABLE));
|
||||
seq_printf(m, "RC6 Enabled: %s\n",
|
||||
yesno(rcctl1 & GEN6_RC_CTL_RC6_ENABLE));
|
||||
seq_printf(m, "Deep RC6 Enabled: %s\n",
|
||||
yesno(rcctl1 & GEN6_RC_CTL_RC6p_ENABLE));
|
||||
seq_printf(m, "Deepest RC6 Enabled: %s\n",
|
||||
yesno(rcctl1 & GEN6_RC_CTL_RC6pp_ENABLE));
|
||||
seq_printf(m, "Current RC state: ");
|
||||
switch (gt_core_status & GEN6_RCn_MASK) {
|
||||
case GEN6_RC0:
|
||||
if (gt_core_status & GEN6_CORE_CPD_STATE_MASK)
|
||||
seq_printf(m, "Core Power Down\n");
|
||||
else
|
||||
seq_printf(m, "on\n");
|
||||
break;
|
||||
case GEN6_RC3:
|
||||
seq_printf(m, "RC3\n");
|
||||
break;
|
||||
case GEN6_RC6:
|
||||
seq_printf(m, "RC6\n");
|
||||
break;
|
||||
case GEN6_RC7:
|
||||
seq_printf(m, "RC7\n");
|
||||
break;
|
||||
default:
|
||||
seq_printf(m, "Unknown\n");
|
||||
break;
|
||||
}
|
||||
|
||||
seq_printf(m, "Core Power Down: %s\n",
|
||||
yesno(gt_core_status & GEN6_CORE_CPD_STATE_MASK));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int i915_drpc_info(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
|
||||
if (IS_GEN6(dev) || IS_GEN7(dev))
|
||||
return gen6_drpc_info(m);
|
||||
else
|
||||
return ironlake_drpc_info(m);
|
||||
}
|
||||
|
||||
static int i915_fbc_status(struct seq_file *m, void *unused)
|
||||
{
|
||||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
|
|
|
@ -781,6 +781,9 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
|||
case I915_PARAM_HAS_RELAXED_DELTA:
|
||||
value = 1;
|
||||
break;
|
||||
case I915_PARAM_HAS_GEN7_SOL_RESET:
|
||||
value = 1;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DRIVER("Unknown parameter %d\n",
|
||||
param->param);
|
||||
|
@ -2305,6 +2308,8 @@ struct drm_ioctl_desc i915_ioctls[] = {
|
|||
DRM_IOCTL_DEF_DRV(I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_SET_SPRITE_COLORKEY, intel_sprite_set_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GET_SPRITE_COLORKEY, intel_sprite_get_colorkey, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
};
|
||||
|
||||
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
|
||||
|
|
|
@ -810,6 +810,21 @@ static struct vm_operations_struct i915_gem_vm_ops = {
|
|||
.close = drm_gem_vm_close,
|
||||
};
|
||||
|
||||
static const struct file_operations i915_driver_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.read = drm_read,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = i915_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
static struct drm_driver driver = {
|
||||
/* Don't use MTRRs here; the Xserver or userspace app should
|
||||
* deal with them for Intel hardware.
|
||||
|
@ -843,21 +858,7 @@ static struct drm_driver driver = {
|
|||
.dumb_map_offset = i915_gem_mmap_gtt,
|
||||
.dumb_destroy = i915_gem_dumb_destroy,
|
||||
.ioctls = i915_ioctls,
|
||||
.fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = drm_open,
|
||||
.release = drm_release,
|
||||
.unlocked_ioctl = drm_ioctl,
|
||||
.mmap = drm_gem_mmap,
|
||||
.poll = drm_poll,
|
||||
.fasync = drm_fasync,
|
||||
.read = drm_read,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = i915_compat_ioctl,
|
||||
#endif
|
||||
.llseek = noop_llseek,
|
||||
},
|
||||
|
||||
.fops = &i915_driver_fops,
|
||||
.name = DRIVER_NAME,
|
||||
.desc = DRIVER_DESC,
|
||||
.date = DRIVER_DATE,
|
||||
|
@ -922,13 +923,6 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
|
|||
MODULE_DESCRIPTION(DRIVER_DESC);
|
||||
MODULE_LICENSE("GPL and additional rights");
|
||||
|
||||
/* We give fast paths for the really cool registers */
|
||||
#define NEEDS_FORCE_WAKE(dev_priv, reg) \
|
||||
(((dev_priv)->info->gen >= 6) && \
|
||||
((reg) < 0x40000) && \
|
||||
((reg) != FORCEWAKE) && \
|
||||
((reg) != ECOBUS))
|
||||
|
||||
#define __i915_read(x, y) \
|
||||
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
|
||||
u##x val = 0; \
|
||||
|
|
|
@ -207,6 +207,8 @@ struct drm_i915_display_funcs {
|
|||
int (*get_display_clock_speed)(struct drm_device *dev);
|
||||
int (*get_fifo_size)(struct drm_device *dev, int plane);
|
||||
void (*update_wm)(struct drm_device *dev);
|
||||
void (*update_sprite_wm)(struct drm_device *dev, int pipe,
|
||||
uint32_t sprite_width, int pixel_size);
|
||||
int (*crtc_mode_set)(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode,
|
||||
|
@ -337,6 +339,8 @@ typedef struct drm_i915_private {
|
|||
struct timer_list hangcheck_timer;
|
||||
int hangcheck_count;
|
||||
uint32_t last_acthd;
|
||||
uint32_t last_acthd_bsd;
|
||||
uint32_t last_acthd_blt;
|
||||
uint32_t last_instdone;
|
||||
uint32_t last_instdone1;
|
||||
|
||||
|
@ -350,6 +354,7 @@ typedef struct drm_i915_private {
|
|||
|
||||
/* overlay */
|
||||
struct intel_overlay *overlay;
|
||||
bool sprite_scaling_enabled;
|
||||
|
||||
/* LVDS info */
|
||||
int backlight_level; /* restore backlight to this value */
|
||||
|
@ -1362,8 +1367,7 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
|
|||
#define NEEDS_FORCE_WAKE(dev_priv, reg) \
|
||||
(((dev_priv)->info->gen >= 6) && \
|
||||
((reg) < 0x40000) && \
|
||||
((reg) != FORCEWAKE) && \
|
||||
((reg) != ECOBUS))
|
||||
((reg) != FORCEWAKE))
|
||||
|
||||
#define __i915_read(x, y) \
|
||||
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg);
|
||||
|
|
|
@ -2006,9 +2006,9 @@ i915_wait_request(struct intel_ring_buffer *ring,
|
|||
|| atomic_read(&dev_priv->mm.wedged));
|
||||
|
||||
ring->irq_put(ring);
|
||||
} else if (wait_for(i915_seqno_passed(ring->get_seqno(ring),
|
||||
seqno) ||
|
||||
atomic_read(&dev_priv->mm.wedged), 3000))
|
||||
} else if (wait_for_atomic(i915_seqno_passed(ring->get_seqno(ring),
|
||||
seqno) ||
|
||||
atomic_read(&dev_priv->mm.wedged), 3000))
|
||||
ret = -EBUSY;
|
||||
ring->waiting_seqno = 0;
|
||||
|
||||
|
@ -3309,6 +3309,10 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file)
|
|||
|
||||
if (ret == 0 && atomic_read(&dev_priv->mm.wedged))
|
||||
ret = -EIO;
|
||||
} else if (wait_for_atomic(i915_seqno_passed(ring->get_seqno(ring),
|
||||
seqno) ||
|
||||
atomic_read(&dev_priv->mm.wedged), 3000)) {
|
||||
ret = -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -970,6 +970,31 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev,
|
|||
}
|
||||
}
|
||||
|
||||
static int
|
||||
i915_reset_gen7_sol_offsets(struct drm_device *dev,
|
||||
struct intel_ring_buffer *ring)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
int ret, i;
|
||||
|
||||
if (!IS_GEN7(dev) || ring != &dev_priv->ring[RCS])
|
||||
return 0;
|
||||
|
||||
ret = intel_ring_begin(ring, 4 * 3);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
|
||||
intel_ring_emit(ring, GEN7_SO_WRITE_OFFSET(i));
|
||||
intel_ring_emit(ring, 0);
|
||||
}
|
||||
|
||||
intel_ring_advance(ring);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
||||
struct drm_file *file,
|
||||
|
@ -984,6 +1009,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
|||
struct intel_ring_buffer *ring;
|
||||
u32 exec_start, exec_len;
|
||||
u32 seqno;
|
||||
u32 mask;
|
||||
int ret, mode, i;
|
||||
|
||||
if (!i915_gem_check_execbuffer(args)) {
|
||||
|
@ -1021,6 +1047,7 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
|||
}
|
||||
|
||||
mode = args->flags & I915_EXEC_CONSTANTS_MASK;
|
||||
mask = I915_EXEC_CONSTANTS_MASK;
|
||||
switch (mode) {
|
||||
case I915_EXEC_CONSTANTS_REL_GENERAL:
|
||||
case I915_EXEC_CONSTANTS_ABSOLUTE:
|
||||
|
@ -1034,18 +1061,9 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
|||
mode == I915_EXEC_CONSTANTS_REL_SURFACE)
|
||||
return -EINVAL;
|
||||
|
||||
ret = intel_ring_begin(ring, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
intel_ring_emit(ring, MI_NOOP);
|
||||
intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
|
||||
intel_ring_emit(ring, INSTPM);
|
||||
intel_ring_emit(ring,
|
||||
I915_EXEC_CONSTANTS_MASK << 16 | mode);
|
||||
intel_ring_advance(ring);
|
||||
|
||||
dev_priv->relative_constants_mode = mode;
|
||||
/* The HW changed the meaning on this bit on gen6 */
|
||||
if (INTEL_INFO(dev)->gen >= 6)
|
||||
mask &= ~I915_EXEC_CONSTANTS_REL_SURFACE;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -1176,6 +1194,27 @@ i915_gem_do_execbuffer(struct drm_device *dev, void *data,
|
|||
}
|
||||
}
|
||||
|
||||
if (ring == &dev_priv->ring[RCS] &&
|
||||
mode != dev_priv->relative_constants_mode) {
|
||||
ret = intel_ring_begin(ring, 4);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
intel_ring_emit(ring, MI_NOOP);
|
||||
intel_ring_emit(ring, MI_LOAD_REGISTER_IMM(1));
|
||||
intel_ring_emit(ring, INSTPM);
|
||||
intel_ring_emit(ring, mask << 16 | mode);
|
||||
intel_ring_advance(ring);
|
||||
|
||||
dev_priv->relative_constants_mode = mode;
|
||||
}
|
||||
|
||||
if (args->flags & I915_EXEC_GEN7_SOL_RESET) {
|
||||
ret = i915_reset_gen7_sol_offsets(dev, ring);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
trace_i915_gem_ring_dispatch(ring, seqno);
|
||||
|
||||
exec_start = batch_obj->gtt_offset + args->batch_start_offset;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue