Commit 37be944a authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'drm-core-next' of git://people.freedesktop.org/~airlied/linux

* 'drm-core-next' of git://people.freedesktop.org/~airlied/linux: (290 commits)
  Revert "drm/ttm: add a way to bo_wait for either the last read or last write"
  Revert "drm/radeon/kms: add a new gem_wait ioctl with read/write flags"
  vmwgfx: Don't pass unused arguments to do_dirty functions
  vmwgfx: Emulate depth 32 framebuffers
  drm/radeon: Lower the severity of the radeon lockup messages.
  drm/i915/dp: Fix eDP on PCH DP on CPT/PPT
  drm/i915/dp: Introduce is_cpu_edp()
  drm/i915: use correct SPD type value
  drm/i915: fix ILK+ infoframe support
  drm/i915: add DP test request handling
  drm/i915: read full receiver capability field during DP hot plug
  drm/i915/dp: Remove eDP special cases from bandwidth checks
  drm/i915/dp: Fix the math in intel_dp_link_required
  drm/i915/panel: Always record the backlight level again (but cleverly)
  i915: Move i915_read/write out of line
  drm/i915: remove transcoder PLL mashing from mode_set per specs
  drm/i915: if transcoder disable fails, say which
  drm/i915: set watermarks for third pipe on IVB
  drm/i915: export a CPT mode set verification function
  drm/i915: fix transcoder PLL select masking
  ...
parents ca836a25 1717c0e2
...@@ -923,6 +923,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem, ...@@ -923,6 +923,9 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
{ {
int ret = -EINVAL; int ret = -EINVAL;
if (intel_private.base.do_idle_maps)
return -ENODEV;
if (intel_private.clear_fake_agp) { if (intel_private.clear_fake_agp) {
int start = intel_private.base.stolen_size / PAGE_SIZE; int start = intel_private.base.stolen_size / PAGE_SIZE;
int end = intel_private.base.gtt_mappable_entries; int end = intel_private.base.gtt_mappable_entries;
...@@ -985,6 +988,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem, ...@@ -985,6 +988,9 @@ static int intel_fake_agp_remove_entries(struct agp_memory *mem,
if (mem->page_count == 0) if (mem->page_count == 0)
return 0; return 0;
if (intel_private.base.do_idle_maps)
return -ENODEV;
intel_gtt_clear_range(pg_start, mem->page_count); intel_gtt_clear_range(pg_start, mem->page_count);
if (intel_private.base.needs_dmar) { if (intel_private.base.needs_dmar) {
...@@ -1177,6 +1183,25 @@ static void gen6_cleanup(void) ...@@ -1177,6 +1183,25 @@ static void gen6_cleanup(void)
{ {
} }
/* Certain Gen5 chipsets require require idling the GPU before
* unmapping anything from the GTT when VT-d is enabled.
*/
extern int intel_iommu_gfx_mapped;
static inline int needs_idle_maps(void)
{
const unsigned short gpu_devid = intel_private.pcidev->device;
/* Query intel_iommu to see if we need the workaround. Presumably that
* was loaded first.
*/
if ((gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_HB ||
gpu_devid == PCI_DEVICE_ID_INTEL_IRONLAKE_M_IG) &&
intel_iommu_gfx_mapped)
return 1;
return 0;
}
static int i9xx_setup(void) static int i9xx_setup(void)
{ {
u32 reg_addr; u32 reg_addr;
...@@ -1211,6 +1236,9 @@ static int i9xx_setup(void) ...@@ -1211,6 +1236,9 @@ static int i9xx_setup(void)
intel_private.gtt_bus_addr = reg_addr + gtt_offset; intel_private.gtt_bus_addr = reg_addr + gtt_offset;
} }
if (needs_idle_maps());
intel_private.base.do_idle_maps = 1;
intel_i9xx_setup_flush(); intel_i9xx_setup_flush();
return 0; return 0;
......
...@@ -158,3 +158,7 @@ config DRM_SAVAGE ...@@ -158,3 +158,7 @@ config DRM_SAVAGE
help help
Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister Choose this option if you have a Savage3D/4/SuperSavage/Pro/Twister
chipset. If M is selected the module will be called savage. chipset. If M is selected the module will be called savage.
source "drivers/gpu/drm/exynos/Kconfig"
source "drivers/gpu/drm/vmwgfx/Kconfig"
...@@ -35,4 +35,5 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/ ...@@ -35,4 +35,5 @@ obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/ obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
obj-$(CONFIG_DRM_VIA) +=via/ obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/ obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-$(CONFIG_DRM_EXYNOS) +=exynos/
obj-y += i2c/ obj-y += i2c/
...@@ -372,11 +372,13 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc, ...@@ -372,11 +372,13 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
encoder_funcs = encoder->helper_private; encoder_funcs = encoder->helper_private;
if (!(ret = encoder_funcs->mode_fixup(encoder, mode, if (!(ret = encoder_funcs->mode_fixup(encoder, mode,
adjusted_mode))) { adjusted_mode))) {
DRM_DEBUG_KMS("Encoder fixup failed\n");
goto done; goto done;
} }
} }
if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) { if (!(ret = crtc_funcs->mode_fixup(crtc, mode, adjusted_mode))) {
DRM_DEBUG_KMS("CRTC fixup failed\n");
goto done; goto done;
} }
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
......
...@@ -107,11 +107,8 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count, ...@@ -107,11 +107,8 @@ int drm_debugfs_create_files(struct drm_info_list *files, int count,
ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO, ent = debugfs_create_file(files[i].name, S_IFREG | S_IRUGO,
root, tmp, &drm_debugfs_fops); root, tmp, &drm_debugfs_fops);
if (!ent) { if (!ent) {
char name[64];
strncpy(name, root->d_name.name,
min(root->d_name.len, 64U));
DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n", DRM_ERROR("Cannot create /sys/kernel/debug/dri/%s/%s\n",
name, files[i].name); root->d_name.name, files[i].name);
kfree(tmp); kfree(tmp);
ret = -1; ret = -1;
goto fail; goto fail;
......
...@@ -438,6 +438,8 @@ long drm_ioctl(struct file *filp, ...@@ -438,6 +438,8 @@ long drm_ioctl(struct file *filp,
goto err_i1; goto err_i1;
} }
} }
if (asize > usize)
memset(kdata + usize, 0, asize - usize);
} }
if (cmd & IOC_IN) { if (cmd & IOC_IN) {
......
...@@ -1319,6 +1319,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, ...@@ -1319,6 +1319,7 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid,
#define HDMI_IDENTIFIER 0x000C03 #define HDMI_IDENTIFIER 0x000C03
#define AUDIO_BLOCK 0x01 #define AUDIO_BLOCK 0x01
#define VENDOR_BLOCK 0x03 #define VENDOR_BLOCK 0x03
#define SPEAKER_BLOCK 0x04
#define EDID_BASIC_AUDIO (1 << 6) #define EDID_BASIC_AUDIO (1 << 6)
/** /**
...@@ -1347,6 +1348,176 @@ u8 *drm_find_cea_extension(struct edid *edid) ...@@ -1347,6 +1348,176 @@ u8 *drm_find_cea_extension(struct edid *edid)
} }
EXPORT_SYMBOL(drm_find_cea_extension); EXPORT_SYMBOL(drm_find_cea_extension);
static void
parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
{
connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */
connector->dvi_dual = db[6] & 1;
connector->max_tmds_clock = db[7] * 5;
connector->latency_present[0] = db[8] >> 7;
connector->latency_present[1] = (db[8] >> 6) & 1;
connector->video_latency[0] = db[9];
connector->audio_latency[0] = db[10];
connector->video_latency[1] = db[11];
connector->audio_latency[1] = db[12];
DRM_LOG_KMS("HDMI: DVI dual %d, "
"max TMDS clock %d, "
"latency present %d %d, "
"video latency %d %d, "
"audio latency %d %d\n",
connector->dvi_dual,
connector->max_tmds_clock,
(int) connector->latency_present[0],
(int) connector->latency_present[1],
connector->video_latency[0],
connector->video_latency[1],
connector->audio_latency[0],
connector->audio_latency[1]);
}
static void
monitor_name(struct detailed_timing *t, void *data)
{
if (t->data.other_data.type == EDID_DETAIL_MONITOR_NAME)
*(u8 **)data = t->data.other_data.data.str.str;
}
/**
* drm_edid_to_eld - build ELD from EDID
* @connector: connector corresponding to the HDMI/DP sink
* @edid: EDID to parse
*
* Fill the ELD (EDID-Like Data) buffer for passing to the audio driver.
* Some ELD fields are left to the graphics driver caller:
* - Conn_Type
* - HDCP
* - Port_ID
*/
void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
{
uint8_t *eld = connector->eld;
u8 *cea;
u8 *name;
u8 *db;
int sad_count = 0;
int mnl;
int dbl;
memset(eld, 0, sizeof(connector->eld));
cea = drm_find_cea_extension(edid);
if (!cea) {
DRM_DEBUG_KMS("ELD: no CEA Extension found\n");
return;
}
name = NULL;
drm_for_each_detailed_block((u8 *)edid, monitor_name, &name);
for (mnl = 0; name && mnl < 13; mnl++) {
if (name[mnl] == 0x0a)
break;
eld[20 + mnl] = name[mnl];
}
eld[4] = (cea[1] << 5) | mnl;
DRM_DEBUG_KMS("ELD monitor %s\n", eld + 20);
eld[0] = 2 << 3; /* ELD version: 2 */
eld[16] = edid->mfg_id[0];
eld[17] = edid->mfg_id[1];
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;
}
}
eld[5] |= sad_count << 4;
eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
DRM_DEBUG_KMS("ELD size %d, SAD count %d\n", (int)eld[2], sad_count);
}
EXPORT_SYMBOL(drm_edid_to_eld);
/**
* drm_av_sync_delay - HDMI/DP sink audio-video sync delay in millisecond
* @connector: connector associated with the HDMI/DP sink
* @mode: the display mode
*/
int drm_av_sync_delay(struct drm_connector *connector,
struct drm_display_mode *mode)
{
int i = !!(mode->flags & DRM_MODE_FLAG_INTERLACE);
int a, v;
if (!connector->latency_present[0])
return 0;
if (!connector->latency_present[1])
i = 0;
a = connector->audio_latency[i];
v = connector->video_latency[i];
/*
* HDMI/DP sink doesn't support audio or video?
*/
if (a == 255 || v == 255)
return 0;
/*
* Convert raw EDID values to millisecond.
* Treat unknown latency as 0ms.
*/
if (a)
a = min(2 * (a - 1), 500);
if (v)
v = min(2 * (v - 1), 500);
return max(v - a, 0);
}
EXPORT_SYMBOL(drm_av_sync_delay);
/**
* drm_select_eld - select one ELD from multiple HDMI/DP sinks
* @encoder: the encoder just changed display mode
* @mode: the adjusted display mode
*
* It's possible for one encoder to be associated with multiple HDMI/DP sinks.
* The policy is now hard coded to simply use the first HDMI/DP sink's ELD.
*/
struct drm_connector *drm_select_eld(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
list_for_each_entry(connector, &dev->mode_config.connector_list, head)
if (connector->encoder == encoder && connector->eld[0])
return connector;
return NULL;
}
EXPORT_SYMBOL(drm_select_eld);
/** /**
* drm_detect_hdmi_monitor - detect whether monitor is hdmi. * drm_detect_hdmi_monitor - detect whether monitor is hdmi.
* @edid: monitor EDID information * @edid: monitor EDID information
......
...@@ -285,6 +285,94 @@ drm_gem_handle_create(struct drm_file *file_priv, ...@@ -285,6 +285,94 @@ drm_gem_handle_create(struct drm_file *file_priv,
} }
EXPORT_SYMBOL(drm_gem_handle_create); EXPORT_SYMBOL(drm_gem_handle_create);
/**
* drm_gem_free_mmap_offset - release a fake mmap offset for an object
* @obj: obj in question
*
* This routine frees fake offsets allocated by drm_gem_create_mmap_offset().
*/
void
drm_gem_free_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 = &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;
}
EXPORT_SYMBOL(drm_gem_free_mmap_offset);
/**
* drm_gem_create_mmap_offset - create a fake mmap offset for an object
* @obj: obj in question
*
* GEM memory mapping works by handing back to userspace a fake mmap offset
* it can use in a subsequent mmap(2) call. The DRM core code then looks
* up the object based on the offset and sets up the various memory mapping
* structures.
*
* This routine allocates and attaches a fake offset for @obj.
*/
int
drm_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 = 0;
/* Set the object up for mmap'ing */
list = &obj->map_list;
list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
if (!list->map)
return -ENOMEM;
map = list->map;
map->type = _DRM_GEM;
map->size = obj->size;
map->handle = obj;
/* Get a DRM GEM mmap offset allocated... */
list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
obj->size / PAGE_SIZE, 0, 0);
if (!list->file_offset_node) {
DRM_ERROR("failed to allocate offset for bo %d\n", obj->name);
ret = -ENOSPC;
goto out_free_list;
}
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 out_free_list;
}
list->hash.key = list->file_offset_node->start;
ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
if (ret) {
DRM_ERROR("failed to add to map hash\n");
goto out_free_mm;
}
return 0;
out_free_mm:
drm_mm_put_block(list->file_offset_node);
out_free_list:
kfree(list->map);
list->map = NULL;
return ret;
}
EXPORT_SYMBOL(drm_gem_create_mmap_offset);
/** Returns a reference to the object named by the handle. */ /** Returns a reference to the object named by the handle. */
struct drm_gem_object * struct drm_gem_object *
drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp, drm_gem_object_lookup(struct drm_device *dev, struct drm_file *filp,
......
...@@ -95,7 +95,6 @@ int drm_proc_create_files(struct drm_info_list *files, int count, ...@@ -95,7 +95,6 @@ int drm_proc_create_files(struct drm_info_list *files, int count,
struct drm_device *dev = minor->dev; struct drm_device *dev = minor->dev;
struct proc_dir_entry *ent; struct proc_dir_entry *ent;
struct drm_info_node *tmp; struct drm_info_node *tmp;
char name[64];
int i, ret; int i, ret;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
...@@ -118,7 +117,7 @@ int drm_proc_create_files(struct drm_info_list *files, int count, ...@@ -118,7 +117,7 @@ int drm_proc_create_files(struct drm_info_list *files, int count,
&drm_proc_fops, tmp); &drm_proc_fops, tmp);
if (!ent) { if (!ent) {
DRM_ERROR("Cannot create /proc/dri/%s/%s\n", DRM_ERROR("Cannot create /proc/dri/%s/%s\n",
name, files[i].name); root->name, files[i].name);
list_del(&tmp->list); list_del(&tmp->list);
kfree(tmp); kfree(tmp);
ret = -1; ret = -1;
......
config DRM_EXYNOS
tristate "DRM Support for Samsung SoC EXYNOS Series"
depends on DRM && PLAT_SAMSUNG
default n
select DRM_KMS_HELPER
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select VT_HW_CONSOLE_BINDING if FRAMEBUFFER_CONSOLE
help
Choose this option if you have a Samsung SoC EXYNOS chipset.
If M is selected the module will be called exynosdrm.
config DRM_EXYNOS_FIMD
tristate "Exynos DRM FIMD"
depends on DRM_EXYNOS
default n
help
Choose this option if you want to use Exynos FIMD for DRM.
If M is selected, the module will be called exynos_drm_fimd
#
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
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
obj-$(CONFIG_DRM_EXYNOS) += exynosdrm.o
obj-$(CONFIG_DRM_EXYNOS_FIMD) += exynos_drm_fimd.o
/* exynos_drm_buf.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_buf.h"
static DEFINE_MUTEX(exynos_drm_buf_lock);
static int lowlevel_buffer_allocate(struct drm_device *dev,
struct exynos_drm_buf_entry *entry)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
entry->vaddr = dma_alloc_writecombine(dev->dev, entry->size,
(dma_addr_t *)&entry->paddr, GFP_KERNEL);
if (!entry->paddr) {
DRM_ERROR("failed to allocate buffer.\n");
return -ENOMEM;
}
DRM_DEBUG_KMS("allocated : vaddr(0x%x), paddr(0x%x), size(0x%x)\n",
(unsigned int)entry->vaddr, entry->paddr, entry->size);
return 0;
}
static void lowlevel_buffer_deallocate(struct drm_device *dev,
struct exynos_drm_buf_entry *entry)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
if (entry->paddr && entry->vaddr && entry->size)
dma_free_writecombine(dev->dev, entry->size, entry->vaddr,
entry->paddr);
else
DRM_DEBUG_KMS("entry data is null.\n");
}
struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev,
unsigned int size)
{
struct exynos_drm_buf_entry *entry;
DRM_DEBUG_KMS("%s.\n", __FILE__);
entry = kzalloc(sizeof(*entry), GFP_KERNEL);
if (!entry) {
DRM_ERROR("failed to allocate exynos_drm_buf_entry.\n");
return ERR_PTR(-ENOMEM);
}
entry->size = size;
/*
* allocate memory region with size and set the memory information
* to vaddr and paddr of a entry object.
*/
if (lowlevel_buffer_allocate(dev, entry) < 0) {
kfree(entry);
entry = NULL;
return ERR_PTR(-ENOMEM);
}
return entry;
}
void exynos_drm_buf_destroy(struct drm_device *dev,
struct exynos_drm_buf_entry *entry)
{
DRM_DEBUG_KMS("%s.\n", __FILE__);
if (!entry) {
DRM_DEBUG_KMS("entry is null.\n");
return;
}
lowlevel_buffer_deallocate(dev, entry);
kfree(entry);
entry = NULL;
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Buffer Management Module");
MODULE_LICENSE("GPL");
/* exynos_drm_buf.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author: Inki Dae <inki.dae@samsung.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef _EXYNOS_DRM_BUF_H_
#define _EXYNOS_DRM_BUF_H_
/*
* exynos drm buffer entry structure.
*
* @paddr: physical address of allocated memory.
* @vaddr: kernel virtual address of allocated memory.
* @size: size of allocated memory.
*/
struct exynos_drm_buf_entry {
dma_addr_t paddr;
void __iomem *vaddr;
unsigned int size;
};
/* allocate physical memory. */
struct exynos_drm_buf_entry *exynos_drm_buf_create(struct drm_device *dev,
unsigned int size);
/* get physical memory information of a drm framebuffer. */
struct exynos_drm_buf_entry *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_buf_entry *entry);
#endif
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#define MAX_EDID 256
#define to_exynos_connector(x) container_of(x, struct exynos_drm_connector,\
drm_connector)
struct exynos_drm_connector {
struct drm_connector drm_connector;
};
/* convert exynos_video_timings to drm_display_mode */
static inline void
convert_to_display_mode(struct drm_display_mode *mode,
struct fb_videomode *timing)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
mode->clock = timing->pixclock / 1000;
mode->hdisplay = timing->xres;
mode->hsync_start = mode->hdisplay + timing->left_margin;
mode->hsync_end = mode->hsync_start + timing->hsync_len;
mode->htotal = mode->hsync_end + timing->right_margin;
mode->vdisplay = timing->yres;
mode->vsync_start = mode->vdisplay + timing->upper_margin;
mode->vsync_end = mode->vsync_start + timing->vsync_len;
mode->vtotal = mode->vsync_end + timing->lower_margin;
}
/* convert drm_display_mode to exynos_video_timings */
static inline void
convert_to_video_timing(struct fb_videomode *timing,
struct drm_display_mode *mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
memset(timing, 0, sizeof(*timing));
timing->pixclock = mode->clock * 1000;
timing->refresh = mode->vrefresh;
timing->xres = mode->hdisplay;
timing->left_margin = mode->hsync_start - mode->hdisplay;
timing->hsync_len = mode->hsync_end - mode->hsync_start;
timing->right_margin = mode->htotal - mode->hsync_end;
timing->yres = mode->vdisplay;
timing->upper_margin = mode->vsync_start - mode->vdisplay;
timing->vsync_len = mode->vsync_end - mode->vsync_start;
timing->lower_margin = mode->vtotal - mode->vsync_end;
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
timing->vmode = FB_VMODE_INTERLACED;
else
timing->vmode = FB_VMODE_NONINTERLACED;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
timing->vmode |= FB_VMODE_DOUBLE;
}
static int exynos_drm_connector_get_modes(struct drm_connector *connector)
{
struct exynos_drm_manager *manager =
exynos_drm_get_manager(connector->encoder);
struct exynos_drm_display *display = manager->display;
unsigned int count;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!display) {
DRM_DEBUG_KMS("display is null.\n");
return 0;
}
/*
* if get_edid() exists then get_edid() callback of hdmi side
* is called to get edid data through i2c interface else
* get timing from the FIMD driver(display controller).
*
* P.S. in case of lcd panel, count is always 1 if success
* because lcd panel has only one mode.
*/
if (display->get_edid) {
int ret;
void *edid;
edid = kzalloc(MAX_EDID, GFP_KERNEL);
if (!edid) {
DRM_ERROR("failed to allocate edid\n");
return 0;
}
ret = display->get_edid(manager->dev, connector,
edid, MAX_EDID);
if (ret < 0) {
DRM_ERROR("failed to get edid data.\n");
kfree(edid);
edid = NULL;
return 0;
}
drm_mode_connector_update_edid_property(connector, edid);
count = drm_add_edid_modes(connector, edid);
kfree(connector->display_info.raw_edid);
connector->display_info.raw_edid = edid;
} else {
struct drm_display_mode *mode = drm_mode_create(connector->dev);
struct fb_videomode *timing;
if (display->get_timing)
timing = display->get_timing(manager->dev);
else {
drm_mode_destroy(connector->dev, mode);
return 0;
}
convert_to_display_mode(mode, timing);
mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
drm_mode_set_name(mode);
drm_mode_probed_add(connector, mode);
count = 1;
}
return count;
}
static int exynos_drm_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct exynos_drm_manager *manager =
exynos_drm_get_manager(connector->encoder);
struct exynos_drm_display *display = manager->display;
struct fb_videomode timing;
int ret = MODE_BAD;
DRM_DEBUG_KMS("%s\n", __FILE__);
convert_to_video_timing(&timing, mode);
if (display && display->check_timing)
if (!display->check_timing(manager->dev, (void *)&timing))
ret = MODE_OK;
return ret;
}
struct drm_encoder *exynos_drm_best_encoder(struct drm_connector *connector)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return connector->encoder;
}
static struct drm_connector_helper_funcs exynos_connector_helper_funcs = {
.get_modes = exynos_drm_connector_get_modes,
.mode_valid = exynos_drm_connector_mode_valid,
.best_encoder = exynos_drm_best_encoder,
};
/* get detection status of display device. */
static enum drm_connector_status
exynos_drm_connector_detect(struct drm_connector *connector, bool force)
{
struct exynos_drm_manager *manager =
exynos_drm_get_manager(connector->encoder);
struct exynos_drm_display *display = manager->display;
enum drm_connector_status status = connector_status_disconnected;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (display && display->is_connected) {
if (display->is_connected(manager->dev))
status = connector_status_connected;
else
status = connector_status_disconnected;
}
return status;
}
static void exynos_drm_connector_destroy(struct drm_connector *connector)
{
struct exynos_drm_connector *exynos_connector =
to_exynos_connector(connector);
DRM_DEBUG_KMS("%s\n", __FILE__);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(exynos_connector);
}
static struct drm_connector_funcs exynos_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.fill_modes = drm_helper_probe_single_connector_modes,
.detect = exynos_drm_connector_detect,
.destroy = exynos_drm_connector_destroy,
};
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder)
{
struct exynos_drm_connector *exynos_connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct drm_connector *connector;
int type;
int err;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_connector = kzalloc(sizeof(*exynos_connector), GFP_KERNEL);
if (!exynos_connector) {
DRM_ERROR("failed to allocate connector\n");
return NULL;
}
connector = &exynos_connector->drm_connector;
switch (manager->display->type) {
case EXYNOS_DISPLAY_TYPE_HDMI:
type = DRM_MODE_CONNECTOR_HDMIA;
break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
break;
}
drm_connector_init(dev, connector, &exynos_connector_funcs, type);
drm_connector_helper_add(connector, &exynos_connector_helper_funcs);
err = drm_sysfs_connector_add(connector);
if (err)
goto err_connector;
connector->encoder = encoder;
err = drm_mode_connector_attach_encoder(connector, encoder);
if (err) {
DRM_ERROR("failed to attach a connector to a encoder\n");
goto err_sysfs;
}
DRM_DEBUG_KMS("connector has been created\n");
return connector;
err_sysfs:
drm_sysfs_connector_remove(connector);
err_connector:
drm_connector_cleanup(connector);
kfree(exynos_connector);
return NULL;
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Connector Driver");
MODULE_LICENSE("GPL");
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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_DRM_CONNECTOR_H_
#define _EXYNOS_DRM_CONNECTOR_H_
struct drm_connector *exynos_drm_connector_create(struct drm_device *dev,
struct drm_encoder *encoder);
#endif
/* exynos_drm_core.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Author:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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.
*/
#include "drmP.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_connector.h"
#include "exynos_drm_fbdev.h"
static DEFINE_MUTEX(exynos_drm_mutex);
static LIST_HEAD(exynos_drm_subdrv_list);
static struct drm_device *drm_dev;
static int exynos_drm_subdrv_probe(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
struct drm_encoder *encoder;
struct drm_connector *connector;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->probe) {
int ret;
/*
* this probe callback would be called by sub driver
* after setting of all resources to this sub driver,
* such as clock, irq and register map are done or by load()
* of exynos drm driver.
*
* P.S. note that this driver is considered for modularization.
*/
ret = subdrv->probe(dev, subdrv->manager.dev);
if (ret)
return ret;
}
/* create and initialize a encoder for this sub driver. */
encoder = exynos_drm_encoder_create(dev, &subdrv->manager,
(1 << MAX_CRTC) - 1);
if (!encoder) {
DRM_ERROR("failed to create encoder\n");
return -EFAULT;
}
/*
* create and initialize a connector for this sub driver and
* attach the encoder created above to the connector.
*/
connector = exynos_drm_connector_create(dev, encoder);
if (!connector) {
DRM_ERROR("failed to create connector\n");
encoder->funcs->destroy(encoder);
return -EFAULT;
}
subdrv->encoder = encoder;
subdrv->connector = connector;
return 0;
}
static void exynos_drm_subdrv_remove(struct drm_device *dev,
struct exynos_drm_subdrv *subdrv)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (subdrv->remove)
subdrv->remove(dev);
if (subdrv->encoder) {
struct drm_encoder *encoder = subdrv->encoder;
encoder->funcs->destroy(encoder);
subdrv->encoder = NULL;
}
if (subdrv->connector) {
struct drm_connector *connector = subdrv->connector;
connector->funcs->destroy(connector);
subdrv->connector = NULL;
}
}
int exynos_drm_device_register(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv, *n;
int err;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!dev)
return -EINVAL;
if (drm_dev) {
DRM_ERROR("Already drm device were registered\n");
return -EBUSY;
}
mutex_lock(&exynos_drm_mutex);
list_for_each_entry_safe(subdrv, n, &exynos_drm_subdrv_list, list) {
err = exynos_drm_subdrv_probe(dev, subdrv);
if (err) {
DRM_DEBUG("exynos drm subdrv probe failed.\n");
list_del(&subdrv->list);
}
}
drm_dev = dev;
mutex_unlock(&exynos_drm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_register);
int exynos_drm_device_unregister(struct drm_device *dev)
{
struct exynos_drm_subdrv *subdrv;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!dev || dev != drm_dev) {
WARN(1, "Unexpected drm device unregister!\n");
return -EINVAL;
}
mutex_lock(&exynos_drm_mutex);
list_for_each_entry(subdrv, &exynos_drm_subdrv_list, list)
exynos_drm_subdrv_remove(dev, subdrv);
drm_dev = NULL;
mutex_unlock(&exynos_drm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_device_unregister);
static int exynos_drm_mode_group_reinit(struct drm_device *dev)
{
struct drm_mode_group *group = &dev->primary->mode_group;
uint32_t *id_list = group->id_list;
int ret;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
ret = drm_mode_group_init_legacy_group(dev, group);
if (ret < 0)
return ret;
kfree(id_list);
return 0;
}
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *subdrv)
{
int err;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv)
return -EINVAL;
mutex_lock(&exynos_drm_mutex);
if (drm_dev) {
err = exynos_drm_subdrv_probe(drm_dev, subdrv);
if (err) {
DRM_ERROR("failed to probe exynos drm subdrv\n");
mutex_unlock(&exynos_drm_mutex);
return err;
}
/*
* if any specific driver such as fimd or hdmi driver called
* exynos_drm_subdrv_register() later than drm_load(),
* the fb helper should be re-initialized and re-configured.
*/
err = exynos_drm_fbdev_reinit(drm_dev);
if (err) {
DRM_ERROR("failed to reinitialize exynos drm fbdev\n");
exynos_drm_subdrv_remove(drm_dev, subdrv);
mutex_unlock(&exynos_drm_mutex);
return err;
}
err = exynos_drm_mode_group_reinit(drm_dev);
if (err) {
DRM_ERROR("failed to reinitialize mode group\n");
exynos_drm_fbdev_fini(drm_dev);
exynos_drm_subdrv_remove(drm_dev, subdrv);
mutex_unlock(&exynos_drm_mutex);
return err;
}
}
subdrv->drm_dev = drm_dev;
list_add_tail(&subdrv->list, &exynos_drm_subdrv_list);
mutex_unlock(&exynos_drm_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_register);
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *subdrv)
{
int ret = -EFAULT;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
if (!subdrv) {
DRM_DEBUG("Unexpected exynos drm subdrv unregister!\n");
return ret;
}
mutex_lock(&exynos_drm_mutex);
if (drm_dev) {
exynos_drm_subdrv_remove(drm_dev, subdrv);
list_del(&subdrv->list);
/*
* fb helper should be updated once a sub driver is released
* to re-configure crtc and connector and also to re-setup
* drm framebuffer.
*/
ret = exynos_drm_fbdev_reinit(drm_dev);
if (ret < 0) {
DRM_ERROR("failed fb helper reinit.\n");
goto fail;
}
ret = exynos_drm_mode_group_reinit(drm_dev);
if (ret < 0) {
DRM_ERROR("failed drm mode group reinit.\n");
goto fail;
}
}
fail:
mutex_unlock(&exynos_drm_mutex);
return ret;
}
EXPORT_SYMBOL_GPL(exynos_drm_subdrv_unregister);
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Core Driver");
MODULE_LICENSE("GPL");
This diff is collapsed.
/* exynos_drm_crtc.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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_DRM_CRTC_H_
#define _EXYNOS_DRM_CRTC_H_
struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
struct drm_crtc *crtc);
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr);
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc);
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc);
#endif
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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.
*/
#include "drmP.h"
#include "drm.h"
#include <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_gem.h"
#define DRIVER_NAME "exynos-drm"
#define DRIVER_DESC "Samsung SoC DRM"
#define DRIVER_DATE "20110530"
#define DRIVER_MAJOR 1
#define DRIVER_MINOR 0
static int exynos_drm_load(struct drm_device *dev, unsigned long flags)
{
struct exynos_drm_private *private;
int ret;
int nr;
DRM_DEBUG_DRIVER("%s\n", __FILE__);
private = kzalloc(sizeof(struct exynos_drm_private), GFP_KERNEL);
if (!private) {
DRM_ERROR("failed to allocate private\n");
return -ENOMEM;
}
INIT_LIST_HEAD(&private->pageflip_event_list);
dev->dev_private = (void *)private;
drm_mode_config_init(dev);
exynos_drm_mode_config_init(dev);
/*
* EXYNOS4 is enough to have two CRTCs and each crtc would be used
* without dependency of hardware.
*/
for (nr = 0; nr < MAX_CRTC; nr++) {
ret = exynos_drm_crtc_create(dev, nr);
if (ret)
goto err_crtc;
}
ret = drm_vblank_init(dev, MAX_CRTC);
if (ret)
goto err_crtc;
/*
* probe sub drivers such as display controller and hdmi driver,
* that were registered at probe() of platform driver
* to the sub driver and create encoder and connector for them.
*/
ret = exynos_drm_device_register(dev);
if (ret)
goto err_vblank;
/*
* create and configure fb helper and also exynos specific
* fbdev object.
*/
ret = exynos_drm_fbdev_init(dev);
if (ret) {
DRM_ERROR("failed to initialize drm fbdev\n");
goto err_drm_device;
}
return 0;
err_drm_device:
exynos_drm_device_unregister(dev);
err_vblank:
drm_vblank_cleanup(dev);
err_crtc:
drm_mode_config_cleanup(dev);
kfree(private);
return ret;
}
static int exynos_drm_unload(struct drm_device *dev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
exynos_drm_fbdev_fini(dev);
exynos_drm_device_unregister(dev);
drm_vblank_cleanup(dev);
drm_mode_config_cleanup(dev);
kfree(dev->dev_private);
dev->dev_private = NULL;
return 0;
}
static void exynos_drm_preclose(struct drm_device *dev,
struct drm_file *file_priv)
{
struct exynos_drm_private *dev_priv = dev->dev_private;
/*
* drm framework frees all events at release time,
* so private event list should be cleared.
*/
if (!list_empty(&dev_priv->pageflip_event_list))
INIT_LIST_HEAD(&dev_priv->pageflip_event_list);
}
static void exynos_drm_lastclose(struct drm_device *dev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
exynos_drm_fbdev_restore_mode(dev);
}
static struct vm_operations_struct exynos_drm_gem_vm_ops = {
.fault = exynos_drm_gem_fault,
.open = drm_gem_vm_open,
.close = drm_gem_vm_close,
};
static struct drm_ioctl_desc exynos_ioctls[] = {
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_CREATE, exynos_drm_gem_create_ioctl,
DRM_UNLOCKED | DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MAP_OFFSET,
exynos_drm_gem_map_offset_ioctl, DRM_UNLOCKED |
DRM_AUTH),
DRM_IOCTL_DEF_DRV(EXYNOS_GEM_MMAP,
exynos_drm_gem_mmap_ioctl, DRM_UNLOCKED | DRM_AUTH),
};
static struct drm_driver exynos_drm_driver = {
.driver_features = DRIVER_HAVE_IRQ | DRIVER_BUS_PLATFORM |
DRIVER_MODESET | DRIVER_GEM,
.load = exynos_drm_load,
.unload = exynos_drm_unload,
.preclose = exynos_drm_preclose,
.lastclose = exynos_drm_lastclose,
.get_vblank_counter = drm_vblank_count,
.enable_vblank = exynos_drm_crtc_enable_vblank,
.disable_vblank = exynos_drm_crtc_disable_vblank,
.gem_init_object = exynos_drm_gem_init_object,
.gem_free_object = exynos_drm_gem_free_object,
.gem_vm_ops = &exynos_drm_gem_vm_ops,
.dumb_create = exynos_drm_gem_dumb_create,
.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,
},
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
.date = DRIVER_DATE,
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
};
static int exynos_drm_platform_probe(struct platform_device *pdev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
exynos_drm_driver.num_ioctls = DRM_ARRAY_SIZE(exynos_ioctls);
return drm_platform_init(&exynos_drm_driver, pdev);
}
static int exynos_drm_platform_remove(struct platform_device *pdev)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
drm_platform_exit(&exynos_drm_driver, pdev);
return 0;
}
static struct platform_driver exynos_drm_platform_driver = {
.probe = exynos_drm_platform_probe,
.remove = __devexit_p(exynos_drm_platform_remove),
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
},
};
static int __init exynos_drm_init(void)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
return platform_driver_register(&exynos_drm_platform_driver);
}
static void __exit exynos_drm_exit(void)
{
DRM_DEBUG_DRIVER("%s\n", __FILE__);
platform_driver_unregister(&exynos_drm_platform_driver);
}
module_init(exynos_drm_init);
module_exit(exynos_drm_exit);
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Driver");
MODULE_LICENSE("GPL");
/* exynos_drm_drv.h
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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_DRM_DRV_H_
#define _EXYNOS_DRM_DRV_H_
#include "drm.h"
#define MAX_CRTC 2
struct drm_device;
struct exynos_drm_overlay;
struct drm_connector;
/* this enumerates display type. */
enum exynos_drm_output_type {
EXYNOS_DISPLAY_TYPE_NONE,
/* RGB or CPU Interface. */
EXYNOS_DISPLAY_TYPE_LCD,
/* HDMI Interface. */
EXYNOS_DISPLAY_TYPE_HDMI,
};
/*
* Exynos drm overlay ops structure.
*
* @mode_set: copy drm overlay info to hw specific overlay info.
* @commit: apply hardware specific overlay data to registers.
* @disable: disable hardware specific overlay.
*/
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);
};
/*
* Exynos drm common overlay structure.
*
* @fb_x: offset x on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed.
* - the unit is screen coordinates.
* @fb_width: width of a framebuffer.
* @fb_height: height of a framebuffer.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_width: window width to be displayed (hardware screen).
* @crtc_height: window height to be displayed (hardware screen).
* @mode_width: width of screen mode.
* @mode_height: height of screen mode.
* @refresh: refresh rate.
* @scan_flag: interlace or progressive way.
* (it could be DRM_MODE_FLAG_*)
* @bpp: pixel size.(in bit)
* @paddr: bus(accessed by dma) physical memory address to this overlay
* and this is physically continuous.
* @vaddr: virtual memory addresss to this overlay.
* @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
* as index color.
* @local_path: in case of lcd type, local path mode on or off.
* @transparency: transparency on or off.
* @activated: activated or not.
*
* this structure is common to exynos SoC and its contents would be copied
* to hardware specific overlay info.
*/
struct exynos_drm_overlay {
unsigned int fb_x;
unsigned int fb_y;
unsigned int fb_width;
unsigned int fb_height;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_width;
unsigned int crtc_height;
unsigned int mode_width;
unsigned int mode_height;
unsigned int refresh;
unsigned int scan_flag;
unsigned int bpp;
unsigned int pitch;
dma_addr_t paddr;
void __iomem *vaddr;
bool default_win;
bool color_key;
unsigned int index_color;
bool local_path;
bool transparency;
bool activated;
};
/*
* Exynos DRM Display Structure.
* - this structure is common to analog tv, digital tv and lcd panel.
*
* @type: one of EXYNOS_DISPLAY_TYPE_LCD and HDMI.
* @is_connected: check for that display is connected or not.
* @get_edid: get edid modes from display driver.
* @get_timing: get timing object from display driver.
* @check_timing: check if timing is valid or not.
* @power_on: display device on or off.
*/
struct exynos_drm_display {
enum exynos_drm_output_type type;
bool (*is_connected)(struct device *dev);
int (*get_edid)(struct device *dev, struct drm_connector *connector,
u8 *edid, int len);
void *(*get_timing)(struct device *dev);
int (*check_timing)(struct device *dev, void *timing);
int (*power_on)(struct device *dev, int mode);
};
/*
* Exynos drm manager ops
*
* @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.
* @enable_vblank: specific driver callback for enabling vblank interrupt.
* @disable_vblank: specific driver callback for disabling vblank interrupt.
*/
struct exynos_drm_manager_ops {
void (*mode_set)(struct device *subdrv_dev, void *mode);
void (*commit)(struct device *subdrv_dev);
int (*enable_vblank)(struct device *subdrv_dev);
void (*disable_vblank)(struct device *subdrv_dev);
};
/*
* Exynos drm common manager structure.
*
* @dev: pointer to device object for subdrv device driver.
* sub drivers such as display controller or hdmi driver,
* have their own device object.
* @ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware global registers.
* @overlay_ops: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control hardware overlay reigsters.
* @display: pointer to callbacks for exynos drm specific framebuffer.
* these callbacks should be set by specific drivers such fimd
* or hdmi driver and are used to control display devices such as
* analog tv, digital tv and lcd panel and also get timing data for them.
*/
struct exynos_drm_manager {
struct device *dev;
int pipe;
struct exynos_drm_manager_ops *ops;
struct exynos_drm_overlay_ops *overlay_ops;
struct exynos_drm_display *display;
};
/*
* Exynos drm private structure.
*/
struct exynos_drm_private {
struct drm_fb_helper *fb_helper;
/* list head for new event to be added. */
struct list_head pageflip_event_list;
/*
* created crtc object would be contained at this array and
* this array is used to be aware of which crtc did it request vblank.
*/
struct drm_crtc *crtc[MAX_CRTC];
};
/*
* Exynos drm sub driver structure.
*
* @list: sub driver has its own list object to register to exynos drm driver.
* @drm_dev: pointer to drm_device and this pointer would be set
* when sub driver calls exynos_drm_subdrv_register().
* @probe: this callback would be called by exynos drm driver after
* subdrv is registered to it.
* @remove: this callback is used to release resources created
* by probe callback.
* @manager: subdrv has its own manager to control a hardware appropriately
* and we can access a hardware drawing on this manager.
* @encoder: encoder object owned by this sub driver.
* @connector: connector object owned by this sub driver.
*/
struct exynos_drm_subdrv {
struct list_head list;
struct drm_device *drm_dev;
int (*probe)(struct drm_device *drm_dev, struct device *dev);
void (*remove)(struct drm_device *dev);
struct exynos_drm_manager manager;
struct drm_encoder *encoder;
struct drm_connector *connector;
};
/*
* this function calls a probe callback registered to sub driver list and
* create its own encoder and connector and then set drm_device object
* to global one.
*/
int exynos_drm_device_register(struct drm_device *dev);
/*
* this function calls a remove callback registered to sub driver list and
* destroy its own encoder and connetor.
*/
int exynos_drm_device_unregister(struct drm_device *dev);
/*
* this function would be called by sub drivers such as display controller
* or hdmi driver to register this sub driver object to exynos drm driver
* and when a sub driver is registered to exynos drm driver a probe callback
* of the sub driver is called and creates its own encoder and connector
* and then fb helper and drm mode group would be re-initialized.
*/
int exynos_drm_subdrv_register(struct exynos_drm_subdrv *drm_subdrv);
/*
* this function removes subdrv list from exynos drm driver and fb helper
* and drm mode group would be re-initialized.
*/
int exynos_drm_subdrv_unregister(struct exynos_drm_subdrv *drm_subdrv);
#endif
/* exynos_drm_encoder.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_crtc.h"
#include "exynos_drm_encoder.h"
#define to_exynos_encoder(x) container_of(x, struct exynos_drm_encoder,\
drm_encoder)
/*
* exynos specific encoder structure.
*
* @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.
*/
struct exynos_drm_encoder {
struct drm_encoder drm_encoder;
struct exynos_drm_manager *manager;
};
static void exynos_drm_encoder_dpms(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);
DRM_DEBUG_KMS("%s, encoder dpms: %d\n", __FILE__, mode);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
struct exynos_drm_display *display = manager->display;
if (display && display->power_on)
display->power_on(manager->dev, mode);
}
}
}
static bool
exynos_drm_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
return true;
}
static void exynos_drm_encoder_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_connector *connector;
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
struct exynos_drm_overlay *overlay = get_exynos_drm_overlay(dev,
encoder->crtc);
DRM_DEBUG_KMS("%s\n", __FILE__);
mode = adjusted_mode;
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->encoder == encoder) {
if (manager_ops && manager_ops->mode_set)
manager_ops->mode_set(manager->dev, mode);
if (overlay_ops && overlay_ops->mode_set)
overlay_ops->mode_set(manager->dev, overlay);
}
}
}
static void exynos_drm_encoder_prepare(struct drm_encoder *encoder)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static void exynos_drm_encoder_commit(struct drm_encoder *encoder)
{
struct exynos_drm_manager *manager = exynos_drm_get_manager(encoder);
struct exynos_drm_manager_ops *manager_ops = manager->ops;
struct exynos_drm_overlay_ops *overlay_ops = manager->overlay_ops;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (manager_ops && manager_ops->commit)
manager_ops->commit(manager->dev);
if (overlay_ops && overlay_ops->commit)
overlay_ops->commit(manager->dev);
}
static struct drm_crtc *
exynos_drm_encoder_get_crtc(struct drm_encoder *encoder)
{
return encoder->crtc;
}
static struct drm_encoder_helper_funcs exynos_encoder_helper_funcs = {
.dpms = exynos_drm_encoder_dpms,
.mode_fixup = exynos_drm_encoder_mode_fixup,
.mode_set = exynos_drm_encoder_mode_set,
.prepare = exynos_drm_encoder_prepare,
.commit = exynos_drm_encoder_commit,
.get_crtc = exynos_drm_encoder_get_crtc,
};
static void exynos_drm_encoder_destroy(struct drm_encoder *encoder)
{
struct exynos_drm_encoder *exynos_encoder =
to_exynos_encoder(encoder);
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_encoder->manager->pipe = -1;
drm_encoder_cleanup(encoder);
encoder->dev->mode_config.num_encoder--;
kfree(exynos_encoder);
}
static struct drm_encoder_funcs exynos_encoder_funcs = {
.destroy = exynos_drm_encoder_destroy,
};
struct drm_encoder *
exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *manager,
unsigned int possible_crtcs)
{
struct drm_encoder *encoder;
struct exynos_drm_encoder *exynos_encoder;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!manager || !possible_crtcs)
return NULL;
if (!manager->dev)
return NULL;
exynos_encoder = kzalloc(sizeof(*exynos_encoder), GFP_KERNEL);
if (!exynos_encoder) {
DRM_ERROR("failed to allocate encoder\n");
return NULL;
}
exynos_encoder->manager = manager;
encoder = &exynos_encoder->drm_encoder;
encoder->possible_crtcs = possible_crtcs;
DRM_DEBUG_KMS("possible_crtcs = 0x%x\n", encoder->possible_crtcs);
drm_encoder_init(dev, encoder, &exynos_encoder_funcs,
DRM_MODE_ENCODER_TMDS);
drm_encoder_helper_add(encoder, &exynos_encoder_helper_funcs);
DRM_DEBUG_KMS("encoder has been created\n");
return encoder;
}
struct exynos_drm_manager *exynos_drm_get_manager(struct drm_encoder *encoder)
{
return to_exynos_encoder(encoder)->manager;
}
void exynos_drm_fn_encoder(struct drm_crtc *crtc, void *data,
void (*fn)(struct drm_encoder *, void *))
{
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
if (encoder->crtc != crtc)
continue;
fn(encoder, data);
}
}
void exynos_drm_enable_vblank(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe == -1)
manager->pipe = crtc;
if (manager_ops->enable_vblank)
manager_ops->enable_vblank(manager->dev);
}
void exynos_drm_disable_vblank(struct drm_encoder *encoder, void *data)
{
struct exynos_drm_manager *manager =
to_exynos_encoder(encoder)->manager;
struct exynos_drm_manager_ops *manager_ops = manager->ops;
int crtc = *(int *)data;
if (manager->pipe == -1)
manager->pipe = crtc;
if (manager_ops->disable_vblank)
manager_ops->disable_vblank(manager->dev);
}
void exynos_drm_encoder_crtc_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;
overlay_ops->commit(manager->dev);
}
void exynos_drm_encoder_crtc_mode_set(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;
struct exynos_drm_overlay *overlay = data;
overlay_ops->mode_set(manager->dev, overlay);
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM Encoder Driver");
MODULE_LICENSE("GPL");
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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_DRM_ENCODER_H_
#define _EXYNOS_DRM_ENCODER_H_
struct exynos_drm_manager;
struct drm_encoder *exynos_drm_encoder_create(struct drm_device *dev,
struct exynos_drm_manager *mgr,
unsigned int possible_crtcs);
struct exynos_drm_manager *
exynos_drm_get_manager(struct drm_encoder *encoder);
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_commit(struct drm_encoder *encoder, void *data);
void exynos_drm_encoder_crtc_mode_set(struct drm_encoder *encoder, void *data);
#endif
/* exynos_drm_fb.c
*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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.
*/
#include "drmP.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.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)
/*
* exynos specific framebuffer structure.
*
* @fb: drm framebuffer obejct.
* @exynos_gem_obj: exynos specific gem object containing a gem object.
* @entry: pointer to exynos drm buffer entry object.
* - containing only the information to physically continuous memory
* region allocated at default framebuffer creation.
*/
struct exynos_drm_fb {
struct drm_framebuffer fb;
struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_buf_entry *entry;
};
static void exynos_drm_fb_destroy(struct drm_framebuffer *fb)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
DRM_DEBUG_KMS("%s\n", __FILE__);
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->entry)
exynos_drm_buf_destroy(fb->dev, exynos_fb->entry);
kfree(exynos_fb);
exynos_fb = NULL;
}
static int exynos_drm_fb_create_handle(struct drm_framebuffer *fb,
struct drm_file *file_priv,
unsigned int *handle)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
DRM_DEBUG_KMS("%s\n", __FILE__);
return drm_gem_handle_create(file_priv,
&exynos_fb->exynos_gem_obj->base, handle);
}
static int exynos_drm_fb_dirty(struct drm_framebuffer *fb,
struct drm_file *file_priv, unsigned flags,
unsigned color, struct drm_clip_rect *clips,
unsigned num_clips)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO */
return 0;
}
static struct drm_framebuffer_funcs exynos_drm_fb_funcs = {
.destroy = exynos_drm_fb_destroy,
.create_handle = exynos_drm_fb_create_handle,
.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 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");
return ERR_PTR(-ENOMEM);
}
fb = &exynos_fb->fb;
ret = drm_framebuffer_init(dev, fb, &exynos_drm_fb_funcs);
if (ret) {
DRM_ERROR("failed to initialize framebuffer.\n");
goto err_init;
}
DRM_LOG_KMS("create: fb id: %d\n", fb->base.id);
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_buf_entry *entry;
/*
* in case that file_priv is NULL, it allocates
* only buffer and this buffer would be used
* for default framebuffer.
*/
entry = exynos_drm_buf_create(dev, size);
if (IS_ERR(entry)) {
ret = PTR_ERR(entry);
goto err_buffer;
}
exynos_fb->entry = entry;
DRM_LOG_KMS("default fb: paddr = 0x%lx, size = 0x%x\n",
(unsigned long)entry->paddr, size);
goto out;
} else {
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev,
size,
&mode_cmd->handle);
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->entry = exynos_gem_obj->entry;
DRM_LOG_KMS("paddr = 0x%lx, size = 0x%x, gem object = 0x%x\n",
(unsigned long)exynos_fb->entry->paddr, 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);
}
struct drm_framebuffer *exynos_drm_fb_create(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_mode_fb_cmd *mode_cmd)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return exynos_drm_fb_init(file_priv, dev, mode_cmd);
}
struct exynos_drm_buf_entry *exynos_drm_fb_get_buf(struct drm_framebuffer *fb)
{
struct exynos_drm_fb *exynos_fb = to_exynos_fb(fb);
struct exynos_drm_buf_entry *entry;
DRM_DEBUG_KMS("%s\n", __FILE__);
entry = exynos_fb->entry;
if (!entry)
return NULL;
DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n",
(unsigned long)entry->vaddr,
(unsigned long)entry->paddr);
return entry;
}
static struct drm_mode_config_funcs exynos_drm_mode_config_funcs = {
.fb_create = exynos_drm_fb_create,
};
void exynos_drm_mode_config_init(struct drm_device *dev)
{
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
/*
* set max width and height as default value(4096x4096).
* this value would be used to check framebuffer size limitation
* at drm_mode_addfb().
*/
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
dev->mode_config.funcs = &exynos_drm_mode_config_funcs;
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM FB Driver");
MODULE_LICENSE("GPL");
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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_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);
void exynos_drm_mode_config_init(struct drm_device *dev);
#endif
This diff is collapsed.
/*
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
*
* Authors:
* Inki Dae <inki.dae@samsung.com>
* Joonyoung Shim <jy0922.shim@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_DRM_FBDEV_H_
#define _EXYNOS_DRM_FBDEV_H_
int exynos_drm_fbdev_init(struct drm_device *dev);
int exynos_drm_fbdev_reinit(struct drm_device *dev);
void exynos_drm_fbdev_fini(struct drm_device *dev);
void exynos_drm_fbdev_restore_mode(struct drm_device *dev);
#endif
This diff is collapsed.
This diff is collapsed.
/* exynos_drm_gem.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_GEM_H_
#define _EXYNOS_DRM_GEM_H_
#define to_exynos_gem_obj(x) container_of(x,\
struct exynos_drm_gem_obj, base)
/*
* exynos drm buffer structure.
*
* @base: a gem object.
* - a new handle to this gem object would be created
* by drm_gem_handle_create().
* @entry: pointer to exynos drm buffer entry object.
* - containing the information to physically
* continuous memory region allocated by user request
* or at framebuffer creation.
*
* P.S. this object would be transfered to user as kms_bo.handle so
* user can access the buffer through kms_bo.handle.
*/
struct exynos_drm_gem_obj {
struct drm_gem_object base;
struct exynos_drm_buf_entry *entry;
};
/* create a new buffer and get a new gem handle. */
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
struct drm_device *dev, unsigned int size,
unsigned int *handle);
/*
* request gem object creation and buffer allocation as the size
* that it is calculated with framebuffer information such as width,
* height and bpp.
*/
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
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);
/* unmap a buffer from user space. */
int exynos_drm_gem_munmap_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);
/* free gem object. */
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);
/* 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);
/*
* destroy memory region allocated.
* - a gem handle and physical memory region pointed by a gem object
* 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);
#endif
...@@ -227,7 +227,7 @@ static bool ch7017_init(struct intel_dvo_device *dvo, ...@@ -227,7 +227,7 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
default: default:
DRM_DEBUG_KMS("ch701x not detected, got %d: from %s " DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
"slave %d.\n", "slave %d.\n",
val, adapter->name,dvo->slave_addr); val, adapter->name, dvo->slave_addr);
goto fail; goto fail;
} }
......
...@@ -111,7 +111,7 @@ static char *ch7xxx_get_id(uint8_t vid) ...@@ -111,7 +111,7 @@ static char *ch7xxx_get_id(uint8_t vid)
/** Reads an 8 bit register */ /** Reads an 8 bit register */
static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
{ {
struct ch7xxx_priv *ch7xxx= dvo->dev_priv; struct ch7xxx_priv *ch7xxx = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus; struct i2c_adapter *adapter = dvo->i2c_bus;
u8 out_buf[2]; u8 out_buf[2];
u8 in_buf[2]; u8 in_buf[2];
...@@ -303,7 +303,7 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo) ...@@ -303,7 +303,7 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
for (i = 0; i < CH7xxx_NUM_REGS; i++) { for (i = 0; i < CH7xxx_NUM_REGS; i++) {
uint8_t val; uint8_t val;
if ((i % 8) == 0 ) if ((i % 8) == 0)
DRM_LOG_KMS("\n %02X: ", i); DRM_LOG_KMS("\n %02X: ", i);
ch7xxx_readb(dvo, i, &val); ch7xxx_readb(dvo, i, &val);
DRM_LOG_KMS("%02X ", val); DRM_LOG_KMS("%02X ", val);
......
...@@ -344,8 +344,8 @@ static void ivch_mode_set(struct intel_dvo_device *dvo, ...@@ -344,8 +344,8 @@ static void ivch_mode_set(struct intel_dvo_device *dvo,
(adjusted_mode->hdisplay - 1)) >> 2; (adjusted_mode->hdisplay - 1)) >> 2;
y_ratio = (((mode->vdisplay - 1) << 16) / y_ratio = (((mode->vdisplay - 1) << 16) /
(adjusted_mode->vdisplay - 1)) >> 2; (adjusted_mode->vdisplay - 1)) >> 2;
ivch_write (dvo, VR42, x_ratio); ivch_write(dvo, VR42, x_ratio);
ivch_write (dvo, VR41, y_ratio); ivch_write(dvo, VR41, y_ratio);
} else { } else {
vr01 &= ~VR01_PANEL_FIT_ENABLE; vr01 &= ~VR01_PANEL_FIT_ENABLE;
vr40 &= ~VR40_CLOCK_GATING_ENABLE; vr40 &= ~VR40_CLOCK_GATING_ENABLE;
...@@ -410,7 +410,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo) ...@@ -410,7 +410,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo)
} }
} }
struct intel_dvo_dev_ops ivch_ops= { struct intel_dvo_dev_ops ivch_ops = {
.init = ivch_init, .init = ivch_init,
.dpms = ivch_dpms, .dpms = ivch_dpms,
.mode_valid = ivch_mode_valid, .mode_valid = ivch_mode_valid,
......
...@@ -104,7 +104,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch) ...@@ -104,7 +104,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch) static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
{ {
struct sil164_priv *sil= dvo->dev_priv; struct sil164_priv *sil = dvo->dev_priv;
struct i2c_adapter *adapter = dvo->i2c_bus; struct i2c_adapter *adapter = dvo->i2c_bus;
uint8_t out_buf[2]; uint8_t out_buf[2];
struct i2c_msg msg = { struct i2c_msg msg = {
......
...@@ -217,7 +217,7 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data) ...@@ -217,7 +217,7 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
++mappable_count; \ ++mappable_count; \
} \ } \
} \ } \
} while(0) } while (0)
static int i915_gem_object_info(struct seq_file *m, void* data) static int i915_gem_object_info(struct seq_file *m, void* data)
{ {
...@@ -1293,12 +1293,12 @@ i915_wedged_read(struct file *filp, ...@@ -1293,12 +1293,12 @@ i915_wedged_read(struct file *filp,
char buf[80]; char buf[80];
int len; int len;
len = snprintf(buf, sizeof (buf), len = snprintf(buf, sizeof(buf),
"wedged : %d\n", "wedged : %d\n",
atomic_read(&dev_priv->mm.wedged)); atomic_read(&dev_priv->mm.wedged));
if (len > sizeof (buf)) if (len > sizeof(buf))
len = sizeof (buf); len = sizeof(buf);
return simple_read_from_buffer(ubuf, max, ppos, buf, len); return simple_read_from_buffer(ubuf, max, ppos, buf, len);
} }
...@@ -1314,7 +1314,7 @@ i915_wedged_write(struct file *filp, ...@@ -1314,7 +1314,7 @@ i915_wedged_write(struct file *filp,
int val = 1; int val = 1;
if (cnt > 0) { if (cnt > 0) {
if (cnt > sizeof (buf) - 1) if (cnt > sizeof(buf) - 1)
return -EINVAL; return -EINVAL;
if (copy_from_user(buf, ubuf, cnt)) if (copy_from_user(buf, ubuf, cnt))
...@@ -1357,11 +1357,11 @@ i915_max_freq_read(struct file *filp, ...@@ -1357,11 +1357,11 @@ i915_max_freq_read(struct file *filp,
char buf[80]; char buf[80];
int len; int len;
len = snprintf(buf, sizeof (buf), len = snprintf(buf, sizeof(buf),
"max freq: %d\n", dev_priv->max_delay * 50); "max freq: %d\n", dev_priv->max_delay * 50);
if (len > sizeof (buf)) if (len > sizeof(buf))
len = sizeof (buf); len = sizeof(buf);
return simple_read_from_buffer(ubuf, max, ppos, buf, len); return simple_read_from_buffer(ubuf, max, ppos, buf, len);
} }
...@@ -1378,7 +1378,7 @@ i915_max_freq_write(struct file *filp, ...@@ -1378,7 +1378,7 @@ i915_max_freq_write(struct file *filp,
int val = 1; int val = 1;
if (cnt > 0) { if (cnt > 0) {
if (cnt > sizeof (buf) - 1) if (cnt > sizeof(buf) - 1)
return -EINVAL; return -EINVAL;
if (copy_from_user(buf, ubuf, cnt)) if (copy_from_user(buf, ubuf, cnt))
...@@ -1432,12 +1432,12 @@ i915_cache_sharing_read(struct file *filp, ...@@ -1432,12 +1432,12 @@ i915_cache_sharing_read(struct file *filp,
snpcr = I915_READ(GEN6_MBCUNIT_SNPCR); snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
mutex_unlock(&dev_priv->dev->struct_mutex); mutex_unlock(&dev_priv->dev->struct_mutex);
len = snprintf(buf, sizeof (buf), len = snprintf(buf, sizeof(buf),
"%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >> "%d\n", (snpcr & GEN6_MBC_SNPCR_MASK) >>
GEN6_MBC_SNPCR_SHIFT); GEN6_MBC_SNPCR_SHIFT);
if (len > sizeof (buf)) if (len > sizeof(buf))
len = sizeof (buf); len = sizeof(buf);
return simple_read_from_buffer(ubuf, max, ppos, buf, len); return simple_read_from_buffer(ubuf, max, ppos, buf, len);
} }
...@@ -1455,7 +1455,7 @@ i915_cache_sharing_write(struct file *filp, ...@@ -1455,7 +1455,7 @@ i915_cache_sharing_write(struct file *filp,
int val = 1; int val = 1;
if (cnt > 0) { if (cnt > 0) {
if (cnt > sizeof (buf) - 1) if (cnt > sizeof(buf) - 1)
return -EINVAL; return -EINVAL;
if (copy_from_user(buf, ubuf, cnt)) if (copy_from_user(buf, ubuf, cnt))
......
...@@ -884,7 +884,7 @@ static int i915_get_bridge_dev(struct drm_device *dev) ...@@ -884,7 +884,7 @@ static int i915_get_bridge_dev(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0,0)); dev_priv->bridge_dev = pci_get_bus_and_slot(0, PCI_DEVFN(0, 0));
if (!dev_priv->bridge_dev) { if (!dev_priv->bridge_dev) {
DRM_ERROR("bridge device not found\n"); DRM_ERROR("bridge device not found\n");
return -1; return -1;
...@@ -2035,7 +2035,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags) ...@@ -2035,7 +2035,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
spin_lock_init(&dev_priv->error_lock); spin_lock_init(&dev_priv->error_lock);
spin_lock_init(&dev_priv->rps_lock); spin_lock_init(&dev_priv->rps_lock);
if (IS_MOBILE(dev) || !IS_GEN2(dev)) if (IS_IVYBRIDGE(dev))
dev_priv->num_pipe = 3;
else if (IS_MOBILE(dev) || !IS_GEN2(dev))
dev_priv->num_pipe = 2; dev_priv->num_pipe = 2;
else else
dev_priv->num_pipe = 1; dev_priv->num_pipe = 1;
......
...@@ -79,11 +79,11 @@ MODULE_PARM_DESC(lvds_downclock, ...@@ -79,11 +79,11 @@ MODULE_PARM_DESC(lvds_downclock,
"Use panel (LVDS/eDP) downclocking for power savings " "Use panel (LVDS/eDP) downclocking for power savings "
"(default: false)"); "(default: false)");
unsigned int i915_panel_use_ssc __read_mostly = 1; unsigned int i915_panel_use_ssc __read_mostly = -1;
module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600); module_param_named(lvds_use_ssc, i915_panel_use_ssc, int, 0600);
MODULE_PARM_DESC(lvds_use_ssc, MODULE_PARM_DESC(lvds_use_ssc,
"Use Spread Spectrum Clock with panels [LVDS/eDP] " "Use Spread Spectrum Clock with panels [LVDS/eDP] "
"(default: true)"); "(default: auto from VBT)");
int i915_vbt_sdvo_panel_type __read_mostly = -1; int i915_vbt_sdvo_panel_type __read_mostly = -1;
module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600); module_param_named(vbt_sdvo_panel_type, i915_vbt_sdvo_panel_type, int, 0600);
...@@ -294,7 +294,7 @@ MODULE_DEVICE_TABLE(pci, pciidlist); ...@@ -294,7 +294,7 @@ MODULE_DEVICE_TABLE(pci, pciidlist);
#define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00 #define INTEL_PCH_CPT_DEVICE_ID_TYPE 0x1c00
#define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00 #define INTEL_PCH_PPT_DEVICE_ID_TYPE 0x1e00
void intel_detect_pch (struct drm_device *dev) void intel_detect_pch(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct pci_dev *pch; struct pci_dev *pch;
...@@ -377,7 +377,7 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv) ...@@ -377,7 +377,7 @@ void gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv) void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
{ {
if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES ) { if (dev_priv->gt_fifo_count < GT_FIFO_NUM_RESERVED_ENTRIES) {
int loop = 500; int loop = 500;
u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES); u32 fifo = I915_READ_NOTRACE(GT_FIFO_FREE_ENTRIES);
while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) { while (fifo <= GT_FIFO_NUM_RESERVED_ENTRIES && loop--) {
...@@ -471,6 +471,9 @@ static int i915_drm_thaw(struct drm_device *dev) ...@@ -471,6 +471,9 @@ static int i915_drm_thaw(struct drm_device *dev)
error = i915_gem_init_ringbuffer(dev); error = i915_gem_init_ringbuffer(dev);
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
if (HAS_PCH_SPLIT(dev))
ironlake_init_pch_refclk(dev);
drm_mode_config_reset(dev); drm_mode_config_reset(dev);
drm_irq_install(dev); drm_irq_install(dev);
...@@ -895,3 +898,43 @@ module_exit(i915_exit); ...@@ -895,3 +898,43 @@ module_exit(i915_exit);
MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC); MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights"); 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))
#define __i915_read(x, y) \
u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \
u##x val = 0; \
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
gen6_gt_force_wake_get(dev_priv); \
val = read##y(dev_priv->regs + reg); \
gen6_gt_force_wake_put(dev_priv); \
} else { \
val = read##y(dev_priv->regs + reg); \
} \
trace_i915_reg_rw(false, reg, val, sizeof(val)); \
return val; \
}
__i915_read(8, b)
__i915_read(16, w)
__i915_read(32, l)
__i915_read(64, q)
#undef __i915_read
#define __i915_write(x, y) \
void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
trace_i915_reg_rw(true, reg, val, sizeof(val)); \
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
__gen6_gt_wait_for_fifo(dev_priv); \
} \
write##y(val, dev_priv->regs + reg); \
}
__i915_write(8, b)
__i915_write(16, w)
__i915_write(32, l)
__i915_write(64, q)
#undef __i915_write
...@@ -139,7 +139,6 @@ struct sdvo_device_mapping { ...@@ -139,7 +139,6 @@ struct sdvo_device_mapping {
u8 slave_addr; u8 slave_addr;
u8 dvo_wiring; u8 dvo_wiring;
u8 i2c_pin; u8 i2c_pin;
u8 i2c_speed;
u8 ddc_pin; u8 ddc_pin;
}; };
...@@ -209,6 +208,8 @@ struct drm_i915_display_funcs { ...@@ -209,6 +208,8 @@ struct drm_i915_display_funcs {
struct drm_display_mode *adjusted_mode, struct drm_display_mode *adjusted_mode,
int x, int y, int x, int y,
struct drm_framebuffer *old_fb); struct drm_framebuffer *old_fb);
void (*write_eld)(struct drm_connector *connector,
struct drm_crtc *crtc);
void (*fdi_link_train)(struct drm_crtc *crtc); void (*fdi_link_train)(struct drm_crtc *crtc);
void (*init_clock_gating)(struct drm_device *dev); void (*init_clock_gating)(struct drm_device *dev);
void (*init_pch_clock_gating)(struct drm_device *dev); void (*init_pch_clock_gating)(struct drm_device *dev);
...@@ -226,26 +227,26 @@ struct drm_i915_display_funcs { ...@@ -226,26 +227,26 @@ struct drm_i915_display_funcs {
struct intel_device_info { struct intel_device_info {
u8 gen; u8 gen;
u8 is_mobile : 1; u8 is_mobile:1;
u8 is_i85x : 1; u8 is_i85x:1;
u8 is_i915g : 1; u8 is_i915g:1;
u8 is_i945gm : 1; u8 is_i945gm:1;
u8 is_g33 : 1; u8 is_g33:1;
u8 need_gfx_hws : 1; u8 need_gfx_hws:1;
u8 is_g4x : 1; u8 is_g4x:1;
u8 is_pineview : 1; u8 is_pineview:1;
u8 is_broadwater : 1; u8 is_broadwater:1;
u8 is_crestline : 1; u8 is_crestline:1;
u8 is_ivybridge : 1; u8 is_ivybridge:1;
u8 has_fbc : 1; u8 has_fbc:1;
u8 has_pipe_cxsr : 1; u8 has_pipe_cxsr:1;
u8 has_hotplug : 1; u8 has_hotplug:1;
u8 cursor_needs_physical : 1; u8 cursor_needs_physical:1;
u8 has_overlay : 1; u8 has_overlay:1;
u8 overlay_needs_physical : 1; u8 overlay_needs_physical:1;
u8 supports_tv : 1; u8 supports_tv:1;
u8 has_bsd_ring : 1; u8 has_bsd_ring:1;
u8 has_blt_ring : 1; u8 has_blt_ring:1;
}; };
enum no_fbc_reason { enum no_fbc_reason {
...@@ -347,7 +348,6 @@ typedef struct drm_i915_private { ...@@ -347,7 +348,6 @@ typedef struct drm_i915_private {
/* LVDS info */ /* LVDS info */
int backlight_level; /* restore backlight to this value */ int backlight_level; /* restore backlight to this value */
bool backlight_enabled; bool backlight_enabled;
struct drm_display_mode *panel_fixed_mode;
struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */ struct drm_display_mode *lfp_lvds_vbt_mode; /* if any */
struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */ struct drm_display_mode *sdvo_lvds_vbt_mode; /* if any */
...@@ -357,6 +357,7 @@ typedef struct drm_i915_private { ...@@ -357,6 +357,7 @@ typedef struct drm_i915_private {
unsigned int lvds_vbt:1; unsigned int lvds_vbt:1;
unsigned int int_crt_support:1; unsigned int int_crt_support:1;
unsigned int lvds_use_ssc:1; unsigned int lvds_use_ssc:1;
unsigned int display_clock_mode:1;
int lvds_ssc_freq; int lvds_ssc_freq;
struct { struct {
int rate; int rate;
...@@ -672,10 +673,9 @@ typedef struct drm_i915_private { ...@@ -672,10 +673,9 @@ typedef struct drm_i915_private {
unsigned int lvds_border_bits; unsigned int lvds_border_bits;
/* Panel fitter placement and size for Ironlake+ */ /* Panel fitter placement and size for Ironlake+ */
u32 pch_pf_pos, pch_pf_size; u32 pch_pf_pos, pch_pf_size;
int panel_t3, panel_t12;
struct drm_crtc *plane_to_crtc_mapping[2]; struct drm_crtc *plane_to_crtc_mapping[3];
struct drm_crtc *pipe_to_crtc_mapping[2]; struct drm_crtc *pipe_to_crtc_mapping[3];
wait_queue_head_t pending_flip_queue; wait_queue_head_t pending_flip_queue;
bool flip_pending_is_done; bool flip_pending_is_done;
...@@ -759,19 +759,19 @@ struct drm_i915_gem_object { ...@@ -759,19 +759,19 @@ struct drm_i915_gem_object {
* (has pending rendering), and is not set if it's on inactive (ready * (has pending rendering), and is not set if it's on inactive (ready
* to be unbound). * to be unbound).
*/ */
unsigned int active : 1; unsigned int active:1;
/** /**
* This is set if the object has been written to since last bound * This is set if the object has been written to since last bound
* to the GTT * to the GTT
*/ */
unsigned int dirty : 1; unsigned int dirty:1;
/** /**
* This is set if the object has been written to since the last * This is set if the object has been written to since the last
* GPU flush. * GPU flush.
*/ */
unsigned int pending_gpu_write : 1; unsigned int pending_gpu_write:1;
/** /**
* Fence register bits (if any) for this object. Will be set * Fence register bits (if any) for this object. Will be set
...@@ -780,18 +780,18 @@ struct drm_i915_gem_object { ...@@ -780,18 +780,18 @@ struct drm_i915_gem_object {
* *
* Size: 4 bits for 16 fences + sign (for FENCE_REG_NONE) * Size: 4 bits for 16 fences + sign (for FENCE_REG_NONE)
*/ */
signed int fence_reg : 5; signed int fence_reg:5;
/** /**
* Advice: are the backing pages purgeable? * Advice: are the backing pages purgeable?
*/ */
unsigned int madv : 2; unsigned int madv:2;
/** /**
* Current tiling mode for the object. * Current tiling mode for the object.
*/ */
unsigned int tiling_mode : 2; unsigned int tiling_mode:2;
unsigned int tiling_changed : 1; unsigned int tiling_changed:1;
/** How many users have pinned this object in GTT space. The following /** How many users have pinned this object in GTT space. The following
* users can each hold at most one reference: pwrite/pread, pin_ioctl * users can each hold at most one reference: pwrite/pread, pin_ioctl
...@@ -802,22 +802,22 @@ struct drm_i915_gem_object { ...@@ -802,22 +802,22 @@ struct drm_i915_gem_object {
* *
* In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3 * In the worst case this is 1 + 1 + 1 + 2*2 = 7. That would fit into 3
* bits with absolutely no headroom. So use 4 bits. */ * bits with absolutely no headroom. So use 4 bits. */
unsigned int pin_count : 4; unsigned int pin_count:4;
#define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf #define DRM_I915_GEM_OBJECT_MAX_PIN_COUNT 0xf
/** /**
* Is the object at the current location in the gtt mappable and * Is the object at the current location in the gtt mappable and
* fenceable? Used to avoid costly recalculations. * fenceable? Used to avoid costly recalculations.
*/ */
unsigned int map_and_fenceable : 1; unsigned int map_and_fenceable:1;
/** /**
* Whether the current gtt mapping needs to be mappable (and isn't just * Whether the current gtt mapping needs to be mappable (and isn't just
* mappable by accident). Track pin and fault separate for a more * mappable by accident). Track pin and fault separate for a more
* accurate mappable working set. * accurate mappable working set.
*/ */
unsigned int fault_mappable : 1; unsigned int fault_mappable:1;
unsigned int pin_mappable : 1; unsigned int pin_mappable:1;
/* /*
* Is the GPU currently using a fence to access this buffer, * Is the GPU currently using a fence to access this buffer,
...@@ -1056,7 +1056,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); ...@@ -1056,7 +1056,7 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
void void
i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask); i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
void intel_enable_asle (struct drm_device *dev); void intel_enable_asle(struct drm_device *dev);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
extern void i915_destroy_error_state(struct drm_device *dev); extern void i915_destroy_error_state(struct drm_device *dev);
...@@ -1301,10 +1301,11 @@ extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state); ...@@ -1301,10 +1301,11 @@ extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
extern bool intel_fbc_enabled(struct drm_device *dev); extern bool intel_fbc_enabled(struct drm_device *dev);
extern void intel_disable_fbc(struct drm_device *dev); extern void intel_disable_fbc(struct drm_device *dev);
extern bool ironlake_set_drps(struct drm_device *dev, u8 val); extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
extern void ironlake_init_pch_refclk(struct drm_device *dev);
extern void ironlake_enable_rc6(struct drm_device *dev); extern void ironlake_enable_rc6(struct drm_device *dev);
extern void gen6_set_rps(struct drm_device *dev, u8 val); extern void gen6_set_rps(struct drm_device *dev, u8 val);
extern void intel_detect_pch (struct drm_device *dev); extern void intel_detect_pch(struct drm_device *dev);
extern int intel_trans_dp_port_sel (struct drm_crtc *crtc); extern int intel_trans_dp_port_sel(struct drm_crtc *crtc);
/* overlay */ /* overlay */
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
...@@ -1354,18 +1355,7 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv); ...@@ -1354,18 +1355,7 @@ void __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv);
((reg) != FORCEWAKE)) ((reg) != FORCEWAKE))
#define __i915_read(x, y) \ #define __i915_read(x, y) \
static inline u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg) { \ u##x i915_read##x(struct drm_i915_private *dev_priv, u32 reg);
u##x val = 0; \
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
gen6_gt_force_wake_get(dev_priv); \
val = read##y(dev_priv->regs + reg); \
gen6_gt_force_wake_put(dev_priv); \
} else { \
val = read##y(dev_priv->regs + reg); \
} \
trace_i915_reg_rw(false, reg, val, sizeof(val)); \
return val; \
}
__i915_read(8, b) __i915_read(8, b)
__i915_read(16, w) __i915_read(16, w)
...@@ -1374,13 +1364,8 @@ __i915_read(64, q) ...@@ -1374,13 +1364,8 @@ __i915_read(64, q)
#undef __i915_read #undef __i915_read
#define __i915_write(x, y) \ #define __i915_write(x, y) \
static inline void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val);
trace_i915_reg_rw(true, reg, val, sizeof(val)); \
if (NEEDS_FORCE_WAKE((dev_priv), (reg))) { \
__gen6_gt_wait_for_fifo(dev_priv); \
} \
write##y(val, dev_priv->regs + reg); \
}
__i915_write(8, b) __i915_write(8, b)
__i915_write(16, w) __i915_write(16, w)
__i915_write(32, l) __i915_write(32, l)
......
This diff is collapsed.
This diff is collapsed.
...@@ -122,7 +122,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size, ...@@ -122,7 +122,7 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
goto found; goto found;
} }
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) { list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
if (! obj->base.write_domain || obj->pin_count) if (!obj->base.write_domain || obj->pin_count)
continue; continue;
if (mark_free(obj, &unwind_list)) if (mark_free(obj, &unwind_list))
......
...@@ -784,7 +784,8 @@ i915_gem_execbuffer_sync_rings(struct drm_i915_gem_object *obj, ...@@ -784,7 +784,8 @@ i915_gem_execbuffer_sync_rings(struct drm_i915_gem_object *obj,
} }
from->sync_seqno[idx] = seqno; from->sync_seqno[idx] = seqno;
return intel_ring_sync(to, from, seqno - 1);
return to->sync_to(to, from, seqno - 1);
} }
static int static int
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment