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
  ...
This commit is contained in:
Linus Torvalds 2011-10-28 05:54:23 -07:00
commit 37be944a02
195 changed files with 18683 additions and 5605 deletions

View file

@ -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,
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)
{ {
} }
/* 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)
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;

View file

@ -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"

View file

@ -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/

View file

@ -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);

View file

@ -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;

View file

@ -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) {

View file

@ -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)
} }
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

View file

@ -285,6 +285,94 @@ again:
} }
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,

View file

@ -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,
&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;

View file

@ -0,0 +1,20 @@
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

View file

@ -0,0 +1,11 @@
#
# 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

View file

@ -0,0 +1,110 @@
/* 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");

View file

@ -0,0 +1,53 @@
/* 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

View file

@ -0,0 +1,293 @@
/*
* 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");

View file

@ -0,0 +1,34 @@
/*
* 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

View file

@ -0,0 +1,272 @@
/* 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");

View file

@ -0,0 +1,381 @@
/* exynos_drm_crtc.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_fb.h"
#include "exynos_drm_encoder.h"
#include "exynos_drm_buf.h"
#define to_exynos_crtc(x) container_of(x, struct exynos_drm_crtc,\
drm_crtc)
/*
* Exynos specific crtc postion structure.
*
* @fb_x: offset x on a framebuffer to be displyed
* - the unit is screen coordinates.
* @fb_y: offset y on a framebuffer to be displayed
* - the unit is screen coordinates.
* @crtc_x: offset x on hardware screen.
* @crtc_y: offset y on hardware screen.
* @crtc_w: width of hardware screen.
* @crtc_h: height of hardware screen.
*/
struct exynos_drm_crtc_pos {
unsigned int fb_x;
unsigned int fb_y;
unsigned int crtc_x;
unsigned int crtc_y;
unsigned int crtc_w;
unsigned int crtc_h;
};
/*
* Exynos specific crtc structure.
*
* @drm_crtc: crtc object.
* @overlay: contain information common to display controller and hdmi and
* contents of this overlay object would be copied to sub driver size.
* @pipe: a crtc index created at load() with a new crtc object creation
* and the crtc object would be set to private->crtc array
* to get a crtc object corresponding to this pipe from private->crtc
* array when irq interrupt occured. the reason of using this pipe is that
* drm framework doesn't support multiple irq yet.
* we can refer to the crtc to current hardware interrupt occured through
* this pipe value.
*/
struct exynos_drm_crtc {
struct drm_crtc drm_crtc;
struct exynos_drm_overlay overlay;
unsigned int pipe;
};
static void exynos_drm_crtc_apply(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_overlay *overlay = &exynos_crtc->overlay;
exynos_drm_fn_encoder(crtc, overlay,
exynos_drm_encoder_crtc_mode_set);
exynos_drm_fn_encoder(crtc, NULL, exynos_drm_encoder_crtc_commit);
}
static int exynos_drm_overlay_update(struct exynos_drm_overlay *overlay,
struct drm_framebuffer *fb,
struct drm_display_mode *mode,
struct exynos_drm_crtc_pos *pos)
{
struct exynos_drm_buf_entry *entry;
unsigned int actual_w;
unsigned int actual_h;
entry = exynos_drm_fb_get_buf(fb);
if (!entry) {
DRM_LOG_KMS("entry is null.\n");
return -EFAULT;
}
overlay->paddr = entry->paddr;
overlay->vaddr = entry->vaddr;
DRM_DEBUG_KMS("vaddr = 0x%lx, paddr = 0x%lx\n",
(unsigned long)overlay->vaddr,
(unsigned long)overlay->paddr);
actual_w = min((mode->hdisplay - pos->crtc_x), pos->crtc_w);
actual_h = min((mode->vdisplay - pos->crtc_y), pos->crtc_h);
/* set drm framebuffer data. */
overlay->fb_x = pos->fb_x;
overlay->fb_y = pos->fb_y;
overlay->fb_width = fb->width;
overlay->fb_height = fb->height;
overlay->bpp = fb->bits_per_pixel;
overlay->pitch = fb->pitch;
/* set overlay range to be displayed. */
overlay->crtc_x = pos->crtc_x;
overlay->crtc_y = pos->crtc_y;
overlay->crtc_width = actual_w;
overlay->crtc_height = actual_h;
/* set drm mode data. */
overlay->mode_width = mode->hdisplay;
overlay->mode_height = mode->vdisplay;
overlay->refresh = mode->vrefresh;
overlay->scan_flag = mode->flags;
DRM_DEBUG_KMS("overlay : offset_x/y(%d,%d), width/height(%d,%d)",
overlay->crtc_x, overlay->crtc_y,
overlay->crtc_width, overlay->crtc_height);
return 0;
}
static int exynos_drm_crtc_update(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_overlay *overlay;
struct exynos_drm_crtc_pos pos;
struct drm_display_mode *mode = &crtc->mode;
struct drm_framebuffer *fb = crtc->fb;
if (!mode || !fb)
return -EINVAL;
exynos_crtc = to_exynos_crtc(crtc);
overlay = &exynos_crtc->overlay;
memset(&pos, 0, sizeof(struct exynos_drm_crtc_pos));
/* it means the offset of framebuffer to be displayed. */
pos.fb_x = crtc->x;
pos.fb_y = crtc->y;
/* OSD position to be displayed. */
pos.crtc_x = 0;
pos.crtc_y = 0;
pos.crtc_w = fb->width - crtc->x;
pos.crtc_h = fb->height - crtc->y;
return exynos_drm_overlay_update(overlay, crtc->fb, mode, &pos);
}
static void exynos_drm_crtc_dpms(struct drm_crtc *crtc, int mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO */
}
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static void exynos_drm_crtc_commit(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL. */
}
static bool
exynos_drm_crtc_mode_fixup(struct drm_crtc *crtc,
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 int
exynos_drm_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
mode = adjusted_mode;
return exynos_drm_crtc_update(crtc);
}
static int exynos_drm_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
ret = exynos_drm_crtc_update(crtc);
if (ret)
return ret;
exynos_drm_crtc_apply(crtc);
return ret;
}
static void exynos_drm_crtc_load_lut(struct drm_crtc *crtc)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* drm framework doesn't check NULL */
}
static struct drm_crtc_helper_funcs exynos_crtc_helper_funcs = {
.dpms = exynos_drm_crtc_dpms,
.prepare = exynos_drm_crtc_prepare,
.commit = exynos_drm_crtc_commit,
.mode_fixup = exynos_drm_crtc_mode_fixup,
.mode_set = exynos_drm_crtc_mode_set,
.mode_set_base = exynos_drm_crtc_mode_set_base,
.load_lut = exynos_drm_crtc_load_lut,
};
static int exynos_drm_crtc_page_flip(struct drm_crtc *crtc,
struct drm_framebuffer *fb,
struct drm_pending_vblank_event *event)
{
struct drm_device *dev = crtc->dev;
struct exynos_drm_private *dev_priv = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct drm_framebuffer *old_fb = crtc->fb;
int ret = -EINVAL;
DRM_DEBUG_KMS("%s\n", __FILE__);
mutex_lock(&dev->struct_mutex);
if (event) {
/*
* the pipe from user always is 0 so we can set pipe number
* of current owner to event.
*/
event->pipe = exynos_crtc->pipe;
list_add_tail(&event->base.link,
&dev_priv->pageflip_event_list);
ret = drm_vblank_get(dev, exynos_crtc->pipe);
if (ret) {
DRM_DEBUG("failed to acquire vblank counter\n");
list_del(&event->base.link);
goto out;
}
crtc->fb = fb;
ret = exynos_drm_crtc_update(crtc);
if (ret) {
crtc->fb = old_fb;
drm_vblank_put(dev, exynos_crtc->pipe);
list_del(&event->base.link);
goto out;
}
/*
* the values related to a buffer of the drm framebuffer
* to be applied should be set at here. because these values
* first, are set to shadow registers and then to
* real registers at vsync front porch period.
*/
exynos_drm_crtc_apply(crtc);
}
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
static void exynos_drm_crtc_destroy(struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
struct exynos_drm_private *private = crtc->dev->dev_private;
DRM_DEBUG_KMS("%s\n", __FILE__);
private->crtc[exynos_crtc->pipe] = NULL;
drm_crtc_cleanup(crtc);
kfree(exynos_crtc);
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
.page_flip = exynos_drm_crtc_page_flip,
.destroy = exynos_drm_crtc_destroy,
};
struct exynos_drm_overlay *get_exynos_drm_overlay(struct drm_device *dev,
struct drm_crtc *crtc)
{
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
return &exynos_crtc->overlay;
}
int exynos_drm_crtc_create(struct drm_device *dev, unsigned int nr)
{
struct exynos_drm_crtc *exynos_crtc;
struct exynos_drm_private *private = dev->dev_private;
struct drm_crtc *crtc;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_crtc = kzalloc(sizeof(*exynos_crtc), GFP_KERNEL);
if (!exynos_crtc) {
DRM_ERROR("failed to allocate exynos crtc\n");
return -ENOMEM;
}
exynos_crtc->pipe = nr;
crtc = &exynos_crtc->drm_crtc;
private->crtc[nr] = crtc;
drm_crtc_init(dev, crtc, &exynos_crtc_funcs);
drm_crtc_helper_add(crtc, &exynos_crtc_helper_funcs);
return 0;
}
int exynos_drm_crtc_enable_vblank(struct drm_device *dev, int crtc)
{
struct exynos_drm_private *private = dev->dev_private;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_enable_vblank);
return 0;
}
void exynos_drm_crtc_disable_vblank(struct drm_device *dev, int crtc)
{
struct exynos_drm_private *private = dev->dev_private;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_fn_encoder(private->crtc[crtc], &crtc,
exynos_drm_disable_vblank);
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM CRTC Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,38 @@
/* 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

View file

@ -0,0 +1,244 @@
/*
* 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");

View file

@ -0,0 +1,254 @@
/* 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

View file

@ -0,0 +1,271 @@
/* 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");

View file

@ -0,0 +1,45 @@
/*
* 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

View file

@ -0,0 +1,265 @@
/* 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");

View file

@ -0,0 +1,37 @@
/*
* 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

View file

@ -0,0 +1,456 @@
/* exynos_drm_fbdev.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_fb_helper.h"
#include "drm_crtc_helper.h"
#include "exynos_drm_drv.h"
#include "exynos_drm_fb.h"
#include "exynos_drm_buf.h"
#define MAX_CONNECTOR 4
#define PREFERRED_BPP 32
#define to_exynos_fbdev(x) container_of(x, struct exynos_drm_fbdev,\
drm_fb_helper)
struct exynos_drm_fbdev {
struct drm_fb_helper drm_fb_helper;
struct drm_framebuffer *fb;
};
static int exynos_drm_fbdev_set_par(struct fb_info *info)
{
struct fb_var_screeninfo *var = &info->var;
switch (var->bits_per_pixel) {
case 32:
case 24:
case 18:
case 16:
case 12:
info->fix.visual = FB_VISUAL_TRUECOLOR;
break;
case 1:
info->fix.visual = FB_VISUAL_MONO01;
break;
default:
info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
break;
}
info->fix.line_length = (var->xres_virtual * var->bits_per_pixel) / 8;
return drm_fb_helper_set_par(info);
}
static struct fb_ops exynos_drm_fb_ops = {
.owner = THIS_MODULE,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = exynos_drm_fbdev_set_par,
.fb_blank = drm_fb_helper_blank,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_setcmap = drm_fb_helper_setcmap,
};
static int exynos_drm_fbdev_update(struct drm_fb_helper *helper,
struct drm_framebuffer *fb,
unsigned int fb_width,
unsigned int fb_height)
{
struct fb_info *fbi = helper->fbdev;
struct drm_device *dev = helper->dev;
struct exynos_drm_fbdev *exynos_fb = to_exynos_fbdev(helper);
struct exynos_drm_buf_entry *entry;
unsigned int size = fb_width * fb_height * (fb->bits_per_pixel >> 3);
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_fb->fb = fb;
drm_fb_helper_fill_fix(fbi, fb->pitch, fb->depth);
drm_fb_helper_fill_var(fbi, helper, fb_width, fb_height);
entry = exynos_drm_fb_get_buf(fb);
if (!entry) {
DRM_LOG_KMS("entry is null.\n");
return -EFAULT;
}
offset = fbi->var.xoffset * (fb->bits_per_pixel >> 3);
offset += fbi->var.yoffset * fb->pitch;
dev->mode_config.fb_base = entry->paddr;
fbi->screen_base = entry->vaddr + offset;
fbi->fix.smem_start = entry->paddr + offset;
fbi->screen_size = size;
fbi->fix.smem_len = size;
return 0;
}
static int exynos_drm_fbdev_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
struct drm_device *dev = helper->dev;
struct fb_info *fbi;
struct drm_mode_fb_cmd mode_cmd = { 0 };
struct platform_device *pdev = dev->platformdev;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
sizes->surface_width, sizes->surface_height,
sizes->surface_bpp);
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.bpp = sizes->surface_bpp;
mode_cmd.depth = sizes->surface_depth;
mutex_lock(&dev->struct_mutex);
fbi = framebuffer_alloc(0, &pdev->dev);
if (!fbi) {
DRM_ERROR("failed to allocate fb info.\n");
ret = -ENOMEM;
goto out;
}
exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
if (IS_ERR_OR_NULL(exynos_fbdev->fb)) {
DRM_ERROR("failed to create drm framebuffer.\n");
ret = PTR_ERR(exynos_fbdev->fb);
goto out;
}
helper->fb = exynos_fbdev->fb;
helper->fbdev = fbi;
fbi->par = helper;
fbi->flags = FBINFO_FLAG_DEFAULT;
fbi->fbops = &exynos_drm_fb_ops;
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
if (ret) {
DRM_ERROR("failed to allocate cmap.\n");
goto out;
}
ret = exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
sizes->fb_height);
if (ret < 0)
fb_dealloc_cmap(&fbi->cmap);
/*
* if failed, all resources allocated above would be released by
* drm_mode_config_cleanup() when drm_load() had been called prior
* to any specific driver such as fimd or hdmi driver.
*/
out:
mutex_unlock(&dev->struct_mutex);
return ret;
}
static bool
exynos_drm_fbdev_is_samefb(struct drm_framebuffer *fb,
struct drm_fb_helper_surface_size *sizes)
{
if (fb->width != sizes->surface_width)
return false;
if (fb->height != sizes->surface_height)
return false;
if (fb->bits_per_pixel != sizes->surface_bpp)
return false;
if (fb->depth != sizes->surface_depth)
return false;
return true;
}
static int exynos_drm_fbdev_recreate(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
struct drm_device *dev = helper->dev;
struct exynos_drm_fbdev *exynos_fbdev = to_exynos_fbdev(helper);
struct drm_framebuffer *fb = exynos_fbdev->fb;
struct drm_mode_fb_cmd mode_cmd = { 0 };
DRM_DEBUG_KMS("%s\n", __FILE__);
if (helper->fb != fb) {
DRM_ERROR("drm framebuffer is different\n");
return -EINVAL;
}
if (exynos_drm_fbdev_is_samefb(fb, sizes))
return 0;
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
mode_cmd.bpp = sizes->surface_bpp;
mode_cmd.depth = sizes->surface_depth;
if (fb->funcs->destroy)
fb->funcs->destroy(fb);
exynos_fbdev->fb = exynos_drm_fb_create(dev, NULL, &mode_cmd);
if (IS_ERR(exynos_fbdev->fb)) {
DRM_ERROR("failed to allocate fb.\n");
return PTR_ERR(exynos_fbdev->fb);
}
helper->fb = exynos_fbdev->fb;
return exynos_drm_fbdev_update(helper, helper->fb, sizes->fb_width,
sizes->fb_height);
}
static int exynos_drm_fbdev_probe(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
int ret = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!helper->fb) {
ret = exynos_drm_fbdev_create(helper, sizes);
if (ret < 0) {
DRM_ERROR("failed to create fbdev.\n");
return ret;
}
/*
* fb_helper expects a value more than 1 if succeed
* because register_framebuffer() should be called.
*/
ret = 1;
} else {
ret = exynos_drm_fbdev_recreate(helper, sizes);
if (ret < 0) {
DRM_ERROR("failed to reconfigure fbdev\n");
return ret;
}
}
return ret;
}
static struct drm_fb_helper_funcs exynos_drm_fb_helper_funcs = {
.fb_probe = exynos_drm_fbdev_probe,
};
int exynos_drm_fbdev_init(struct drm_device *dev)
{
struct exynos_drm_fbdev *fbdev;
struct exynos_drm_private *private = dev->dev_private;
struct drm_fb_helper *helper;
unsigned int num_crtc;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!dev->mode_config.num_crtc || !dev->mode_config.num_connector)
return 0;
fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL);
if (!fbdev) {
DRM_ERROR("failed to allocate drm fbdev.\n");
return -ENOMEM;
}
private->fb_helper = helper = &fbdev->drm_fb_helper;
helper->funcs = &exynos_drm_fb_helper_funcs;
num_crtc = dev->mode_config.num_crtc;
ret = drm_fb_helper_init(dev, helper, num_crtc, MAX_CONNECTOR);
if (ret < 0) {
DRM_ERROR("failed to initialize drm fb helper.\n");
goto err_init;
}
ret = drm_fb_helper_single_add_all_connectors(helper);
if (ret < 0) {
DRM_ERROR("failed to register drm_fb_helper_connector.\n");
goto err_setup;
}
ret = drm_fb_helper_initial_config(helper, PREFERRED_BPP);
if (ret < 0) {
DRM_ERROR("failed to set up hw configuration.\n");
goto err_setup;
}
return 0;
err_setup:
drm_fb_helper_fini(helper);
err_init:
private->fb_helper = NULL;
kfree(fbdev);
return ret;
}
static void exynos_drm_fbdev_destroy(struct drm_device *dev,
struct drm_fb_helper *fb_helper)
{
struct drm_framebuffer *fb;
/* release drm framebuffer and real buffer */
if (fb_helper->fb && fb_helper->fb->funcs) {
fb = fb_helper->fb;
if (fb && fb->funcs->destroy)
fb->funcs->destroy(fb);
}
/* release linux framebuffer */
if (fb_helper->fbdev) {
struct fb_info *info;
int ret;
info = fb_helper->fbdev;
ret = unregister_framebuffer(info);
if (ret < 0)
DRM_DEBUG_KMS("failed unregister_framebuffer()\n");
if (info->cmap.len)
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
}
drm_fb_helper_fini(fb_helper);
}
void exynos_drm_fbdev_fini(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
struct exynos_drm_fbdev *fbdev;
if (!private || !private->fb_helper)
return;
fbdev = to_exynos_fbdev(private->fb_helper);
exynos_drm_fbdev_destroy(dev, private->fb_helper);
kfree(fbdev);
private->fb_helper = NULL;
}
void exynos_drm_fbdev_restore_mode(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
if (!private || !private->fb_helper)
return;
drm_fb_helper_restore_fbdev_mode(private->fb_helper);
}
int exynos_drm_fbdev_reinit(struct drm_device *dev)
{
struct exynos_drm_private *private = dev->dev_private;
struct drm_fb_helper *fb_helper;
int ret;
if (!private)
return -EINVAL;
/*
* if all sub drivers were unloaded then num_connector is 0
* so at this time, the framebuffers also should be destroyed.
*/
if (!dev->mode_config.num_connector) {
exynos_drm_fbdev_fini(dev);
return 0;
}
fb_helper = private->fb_helper;
if (fb_helper) {
drm_fb_helper_fini(fb_helper);
ret = drm_fb_helper_init(dev, fb_helper,
dev->mode_config.num_crtc, MAX_CONNECTOR);
if (ret < 0) {
DRM_ERROR("failed to initialize drm fb helper\n");
return ret;
}
ret = drm_fb_helper_single_add_all_connectors(fb_helper);
if (ret < 0) {
DRM_ERROR("failed to add fb helper to connectors\n");
goto err;
}
ret = drm_fb_helper_initial_config(fb_helper, PREFERRED_BPP);
if (ret < 0) {
DRM_ERROR("failed to set up hw configuration.\n");
goto err;
}
} else {
/*
* if drm_load() failed whem drm load() was called prior
* to specific drivers, fb_helper must be NULL and so
* this fuction should be called again to re-initialize and
* re-configure the fb helper. it means that this function
* has been called by the specific drivers.
*/
ret = exynos_drm_fbdev_init(dev);
}
return ret;
err:
/*
* if drm_load() failed when drm load() was called prior
* to specific drivers, the fb_helper must be NULL and so check it.
*/
if (fb_helper)
drm_fb_helper_fini(fb_helper);
return ret;
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Seung-Woo Kim <sw0312.kim@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM FBDEV Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,37 @@
/*
* 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

View file

@ -0,0 +1,811 @@
/* exynos_drm_fimd.c
*
* Copyright (C) 2011 Samsung Electronics Co.Ltd
* Authors:
* Joonyoung Shim <jy0922.shim@samsung.com>
* Inki Dae <inki.dae@samsung.com>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
*/
#include "drmP.h"
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <drm/exynos_drm.h>
#include <plat/regs-fb-v4.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_fbdev.h"
#include "exynos_drm_crtc.h"
/*
* FIMD is stand for Fully Interactive Mobile Display and
* as a display controller, it transfers contents drawn on memory
* to a LCD Panel through Display Interfaces such as RGB or
* CPU Interface.
*/
/* position control register for hardware window 0, 2 ~ 4.*/
#define VIDOSD_A(win) (VIDOSD_BASE + 0x00 + (win) * 16)
#define VIDOSD_B(win) (VIDOSD_BASE + 0x04 + (win) * 16)
/* size control register for hardware window 0. */
#define VIDOSD_C_SIZE_W0 (VIDOSD_BASE + 0x08)
/* alpha control register for hardware window 1 ~ 4. */
#define VIDOSD_C(win) (VIDOSD_BASE + 0x18 + (win) * 16)
/* size control register for hardware window 1 ~ 4. */
#define VIDOSD_D(win) (VIDOSD_BASE + 0x0C + (win) * 16)
#define VIDWx_BUF_START(win, buf) (VIDW_BUF_START(buf) + (win) * 8)
#define VIDWx_BUF_END(win, buf) (VIDW_BUF_END(buf) + (win) * 8)
#define VIDWx_BUF_SIZE(win, buf) (VIDW_BUF_SIZE(buf) + (win) * 4)
/* color key control register for hardware window 1 ~ 4. */
#define WKEYCON0_BASE(x) ((WKEYCON0 + 0x140) + (x * 8))
/* color key value register for hardware window 1 ~ 4. */
#define WKEYCON1_BASE(x) ((WKEYCON1 + 0x140) + (x * 8))
/* FIMD has totally five hardware windows. */
#define WINDOWS_NR 5
#define get_fimd_context(dev) platform_get_drvdata(to_platform_device(dev))
struct fimd_win_data {
unsigned int offset_x;
unsigned int offset_y;
unsigned int ovl_width;
unsigned int ovl_height;
unsigned int fb_width;
unsigned int fb_height;
unsigned int bpp;
dma_addr_t paddr;
void __iomem *vaddr;
unsigned int buf_offsize;
unsigned int line_size; /* bytes */
};
struct fimd_context {
struct exynos_drm_subdrv subdrv;
int irq;
struct drm_crtc *crtc;
struct clk *bus_clk;
struct clk *lcd_clk;
struct resource *regs_res;
void __iomem *regs;
struct fimd_win_data win_data[WINDOWS_NR];
unsigned int clkdiv;
unsigned int default_win;
unsigned long irq_flags;
u32 vidcon0;
u32 vidcon1;
struct fb_videomode *timing;
};
static bool fimd_display_is_connected(struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
return true;
}
static void *fimd_get_timing(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
DRM_DEBUG_KMS("%s\n", __FILE__);
return ctx->timing;
}
static int fimd_check_timing(struct device *dev, void *timing)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
return 0;
}
static int fimd_display_power_on(struct device *dev, int mode)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
return 0;
}
static struct exynos_drm_display fimd_display = {
.type = EXYNOS_DISPLAY_TYPE_LCD,
.is_connected = fimd_display_is_connected,
.get_timing = fimd_get_timing,
.check_timing = fimd_check_timing,
.power_on = fimd_display_power_on,
};
static void fimd_commit(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fb_videomode *timing = ctx->timing;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* setup polarity values from machine code. */
writel(ctx->vidcon1, ctx->regs + VIDCON1);
/* setup vertical timing values. */
val = VIDTCON0_VBPD(timing->upper_margin - 1) |
VIDTCON0_VFPD(timing->lower_margin - 1) |
VIDTCON0_VSPW(timing->vsync_len - 1);
writel(val, ctx->regs + VIDTCON0);
/* setup horizontal timing values. */
val = VIDTCON1_HBPD(timing->left_margin - 1) |
VIDTCON1_HFPD(timing->right_margin - 1) |
VIDTCON1_HSPW(timing->hsync_len - 1);
writel(val, ctx->regs + VIDTCON1);
/* setup horizontal and vertical display size. */
val = VIDTCON2_LINEVAL(timing->yres - 1) |
VIDTCON2_HOZVAL(timing->xres - 1);
writel(val, ctx->regs + VIDTCON2);
/* setup clock source, clock divider, enable dma. */
val = ctx->vidcon0;
val &= ~(VIDCON0_CLKVAL_F_MASK | VIDCON0_CLKDIR);
if (ctx->clkdiv > 1)
val |= VIDCON0_CLKVAL_F(ctx->clkdiv - 1) | VIDCON0_CLKDIR;
else
val &= ~VIDCON0_CLKDIR; /* 1:1 clock */
/*
* fields of register with prefix '_F' would be updated
* at vsync(same as dma start)
*/
val |= VIDCON0_ENVID | VIDCON0_ENVID_F;
writel(val, ctx->regs + VIDCON0);
}
static int fimd_enable_vblank(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!test_and_set_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
val |= VIDINTCON0_INT_ENABLE;
val |= VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_FRAMESEL0_MASK;
val |= VIDINTCON0_FRAMESEL0_VSYNC;
val &= ~VIDINTCON0_FRAMESEL1_MASK;
val |= VIDINTCON0_FRAMESEL1_NONE;
writel(val, ctx->regs + VIDINTCON0);
}
return 0;
}
static void fimd_disable_vblank(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (test_and_clear_bit(0, &ctx->irq_flags)) {
val = readl(ctx->regs + VIDINTCON0);
val &= ~VIDINTCON0_INT_FRAME;
val &= ~VIDINTCON0_INT_ENABLE;
writel(val, ctx->regs + VIDINTCON0);
}
}
static struct exynos_drm_manager_ops fimd_manager_ops = {
.commit = fimd_commit,
.enable_vblank = fimd_enable_vblank,
.disable_vblank = fimd_disable_vblank,
};
static void fimd_win_mode_set(struct device *dev,
struct exynos_drm_overlay *overlay)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data;
unsigned long offset;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!overlay) {
dev_err(dev, "overlay is NULL\n");
return;
}
offset = overlay->fb_x * (overlay->bpp >> 3);
offset += overlay->fb_y * overlay->pitch;
DRM_DEBUG_KMS("offset = 0x%lx, pitch = %x\n", offset, overlay->pitch);
win_data = &ctx->win_data[ctx->default_win];
win_data->offset_x = overlay->crtc_x;
win_data->offset_y = overlay->crtc_y;
win_data->ovl_width = overlay->crtc_width;
win_data->ovl_height = overlay->crtc_height;
win_data->fb_width = overlay->fb_width;
win_data->fb_height = overlay->fb_height;
win_data->paddr = overlay->paddr + offset;
win_data->vaddr = overlay->vaddr + offset;
win_data->bpp = overlay->bpp;
win_data->buf_offsize = (overlay->fb_width - overlay->crtc_width) *
(overlay->bpp >> 3);
win_data->line_size = overlay->crtc_width * (overlay->bpp >> 3);
DRM_DEBUG_KMS("offset_x = %d, offset_y = %d\n",
win_data->offset_x, win_data->offset_y);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
win_data->ovl_width, win_data->ovl_height);
DRM_DEBUG_KMS("paddr = 0x%lx, vaddr = 0x%lx\n",
(unsigned long)win_data->paddr,
(unsigned long)win_data->vaddr);
DRM_DEBUG_KMS("fb_width = %d, crtc_width = %d\n",
overlay->fb_width, overlay->crtc_width);
}
static void fimd_win_set_pixfmt(struct device *dev, unsigned int win)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data = &ctx->win_data[win];
unsigned long val;
DRM_DEBUG_KMS("%s\n", __FILE__);
val = WINCONx_ENWIN;
switch (win_data->bpp) {
case 1:
val |= WINCON0_BPPMODE_1BPP;
val |= WINCONx_BITSWP;
val |= WINCONx_BURSTLEN_4WORD;
break;
case 2:
val |= WINCON0_BPPMODE_2BPP;
val |= WINCONx_BITSWP;
val |= WINCONx_BURSTLEN_8WORD;
break;
case 4:
val |= WINCON0_BPPMODE_4BPP;
val |= WINCONx_BITSWP;
val |= WINCONx_BURSTLEN_8WORD;
break;
case 8:
val |= WINCON0_BPPMODE_8BPP_PALETTE;
val |= WINCONx_BURSTLEN_8WORD;
val |= WINCONx_BYTSWP;
break;
case 16:
val |= WINCON0_BPPMODE_16BPP_565;
val |= WINCONx_HAWSWP;
val |= WINCONx_BURSTLEN_16WORD;
break;
case 24:
val |= WINCON0_BPPMODE_24BPP_888;
val |= WINCONx_WSWP;
val |= WINCONx_BURSTLEN_16WORD;
break;
case 32:
val |= WINCON1_BPPMODE_28BPP_A4888
| WINCON1_BLD_PIX | WINCON1_ALPHA_SEL;
val |= WINCONx_WSWP;
val |= WINCONx_BURSTLEN_16WORD;
break;
default:
DRM_DEBUG_KMS("invalid pixel size so using unpacked 24bpp.\n");
val |= WINCON0_BPPMODE_24BPP_888;
val |= WINCONx_WSWP;
val |= WINCONx_BURSTLEN_16WORD;
break;
}
DRM_DEBUG_KMS("bpp = %d\n", win_data->bpp);
writel(val, ctx->regs + WINCON(win));
}
static void fimd_win_set_colkey(struct device *dev, unsigned int win)
{
struct fimd_context *ctx = get_fimd_context(dev);
unsigned int keycon0 = 0, keycon1 = 0;
DRM_DEBUG_KMS("%s\n", __FILE__);
keycon0 = ~(WxKEYCON0_KEYBL_EN | WxKEYCON0_KEYEN_F |
WxKEYCON0_DIRCON) | WxKEYCON0_COMPKEY(0);
keycon1 = WxKEYCON1_COLVAL(0xffffffff);
writel(keycon0, ctx->regs + WKEYCON0_BASE(win));
writel(keycon1, ctx->regs + WKEYCON1_BASE(win));
}
static void fimd_win_commit(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data;
int win = ctx->default_win;
unsigned long val, alpha, size;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (win < 0 || win > WINDOWS_NR)
return;
win_data = &ctx->win_data[win];
/*
* SHADOWCON register is used for enabling timing.
*
* for example, once only width value of a register is set,
* if the dma is started then fimd hardware could malfunction so
* with protect window setting, the register fields with prefix '_F'
* wouldn't be updated at vsync also but updated once unprotect window
* is set.
*/
/* protect windows */
val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON);
/* buffer start address */
val = win_data->paddr;
writel(val, ctx->regs + VIDWx_BUF_START(win, 0));
/* buffer end address */
size = win_data->fb_width * win_data->ovl_height * (win_data->bpp >> 3);
val = win_data->paddr + size;
writel(val, ctx->regs + VIDWx_BUF_END(win, 0));
DRM_DEBUG_KMS("start addr = 0x%lx, end addr = 0x%lx, size = 0x%lx\n",
(unsigned long)win_data->paddr, val, size);
DRM_DEBUG_KMS("ovl_width = %d, ovl_height = %d\n",
win_data->ovl_width, win_data->ovl_height);
/* buffer size */
val = VIDW_BUF_SIZE_OFFSET(win_data->buf_offsize) |
VIDW_BUF_SIZE_PAGEWIDTH(win_data->line_size);
writel(val, ctx->regs + VIDWx_BUF_SIZE(win, 0));
/* OSD position */
val = VIDOSDxA_TOPLEFT_X(win_data->offset_x) |
VIDOSDxA_TOPLEFT_Y(win_data->offset_y);
writel(val, ctx->regs + VIDOSD_A(win));
val = VIDOSDxB_BOTRIGHT_X(win_data->offset_x +
win_data->ovl_width - 1) |
VIDOSDxB_BOTRIGHT_Y(win_data->offset_y +
win_data->ovl_height - 1);
writel(val, ctx->regs + VIDOSD_B(win));
DRM_DEBUG_KMS("osd pos: tx = %d, ty = %d, bx = %d, by = %d\n",
win_data->offset_x, win_data->offset_y,
win_data->offset_x + win_data->ovl_width - 1,
win_data->offset_y + win_data->ovl_height - 1);
/* hardware window 0 doesn't support alpha channel. */
if (win != 0) {
/* OSD alpha */
alpha = VIDISD14C_ALPHA1_R(0xf) |
VIDISD14C_ALPHA1_G(0xf) |
VIDISD14C_ALPHA1_B(0xf);
writel(alpha, ctx->regs + VIDOSD_C(win));
}
/* OSD size */
if (win != 3 && win != 4) {
u32 offset = VIDOSD_D(win);
if (win == 0)
offset = VIDOSD_C_SIZE_W0;
val = win_data->ovl_width * win_data->ovl_height;
writel(val, ctx->regs + offset);
DRM_DEBUG_KMS("osd size = 0x%x\n", (unsigned int)val);
}
fimd_win_set_pixfmt(dev, win);
/* hardware window 0 doesn't support color key. */
if (win != 0)
fimd_win_set_colkey(dev, win);
/* Enable DMA channel and unprotect windows */
val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_CHx_ENABLE(win);
val &= ~SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON);
}
static void fimd_win_disable(struct device *dev)
{
struct fimd_context *ctx = get_fimd_context(dev);
struct fimd_win_data *win_data;
int win = ctx->default_win;
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (win < 0 || win > WINDOWS_NR)
return;
win_data = &ctx->win_data[win];
/* protect windows */
val = readl(ctx->regs + SHADOWCON);
val |= SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON);
/* wincon */
val = readl(ctx->regs + WINCON(win));
val &= ~WINCONx_ENWIN;
writel(val, ctx->regs + WINCON(win));
/* unprotect windows */
val = readl(ctx->regs + SHADOWCON);
val &= ~SHADOWCON_CHx_ENABLE(win);
val &= ~SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON);
}
static struct exynos_drm_overlay_ops fimd_overlay_ops = {
.mode_set = fimd_win_mode_set,
.commit = fimd_win_commit,
.disable = fimd_win_disable,
};
static void fimd_finish_pageflip(struct drm_device *drm_dev, int crtc)
{
struct exynos_drm_private *dev_priv = drm_dev->dev_private;
struct drm_pending_vblank_event *e, *t;
struct timeval now;
unsigned long flags;
bool is_checked = false;
spin_lock_irqsave(&drm_dev->event_lock, flags);
list_for_each_entry_safe(e, t, &dev_priv->pageflip_event_list,
base.link) {
/* if event's pipe isn't same as crtc then ignore it. */
if (crtc != e->pipe)
continue;
is_checked = true;
do_gettimeofday(&now);
e->event.sequence = 0;
e->event.tv_sec = now.tv_sec;
e->event.tv_usec = now.tv_usec;
list_move_tail(&e->base.link, &e->base.file_priv->event_list);
wake_up_interruptible(&e->base.file_priv->event_wait);
}
if (is_checked)
drm_vblank_put(drm_dev, crtc);
spin_unlock_irqrestore(&drm_dev->event_lock, flags);
}
static irqreturn_t fimd_irq_handler(int irq, void *dev_id)
{
struct fimd_context *ctx = (struct fimd_context *)dev_id;
struct exynos_drm_subdrv *subdrv = &ctx->subdrv;
struct drm_device *drm_dev = subdrv->drm_dev;
struct exynos_drm_manager *manager = &subdrv->manager;
u32 val;
val = readl(ctx->regs + VIDINTCON1);
if (val & VIDINTCON1_INT_FRAME)
/* VSYNC interrupt */
writel(VIDINTCON1_INT_FRAME, ctx->regs + VIDINTCON1);
drm_handle_vblank(drm_dev, manager->pipe);
fimd_finish_pageflip(drm_dev, manager->pipe);
return IRQ_HANDLED;
}
static int fimd_subdrv_probe(struct drm_device *drm_dev, struct device *dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* enable drm irq mode.
* - with irq_enabled = 1, we can use the vblank feature.
*
* P.S. note that we wouldn't use drm irq handler but
* just specific driver own one instead because
* drm framework supports only one irq handler.
*/
drm_dev->irq_enabled = 1;
/*
* with vblank_disable_allowed = 1, vblank interrupt will be disabled
* by drm timer once a current process gives up ownership of
* vblank event.(drm_vblank_put function was called)
*/
drm_dev->vblank_disable_allowed = 1;
return 0;
}
static void fimd_subdrv_remove(struct drm_device *drm_dev)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
/* TODO. */
}
static int fimd_calc_clkdiv(struct fimd_context *ctx,
struct fb_videomode *timing)
{
unsigned long clk = clk_get_rate(ctx->lcd_clk);
u32 retrace;
u32 clkdiv;
u32 best_framerate = 0;
u32 framerate;
DRM_DEBUG_KMS("%s\n", __FILE__);
retrace = timing->left_margin + timing->hsync_len +
timing->right_margin + timing->xres;
retrace *= timing->upper_margin + timing->vsync_len +
timing->lower_margin + timing->yres;
/* default framerate is 60Hz */
if (!timing->refresh)
timing->refresh = 60;
clk /= retrace;
for (clkdiv = 1; clkdiv < 0x100; clkdiv++) {
int tmp;
/* get best framerate */
framerate = clk / clkdiv;
tmp = timing->refresh - framerate;
if (tmp < 0) {
best_framerate = framerate;
continue;
} else {
if (!best_framerate)
best_framerate = framerate;
else if (tmp < (best_framerate - framerate))
best_framerate = framerate;
break;
}
}
return clkdiv;
}
static void fimd_clear_win(struct fimd_context *ctx, int win)
{
u32 val;
DRM_DEBUG_KMS("%s\n", __FILE__);
writel(0, ctx->regs + WINCON(win));
writel(0, ctx->regs + VIDOSD_A(win));
writel(0, ctx->regs + VIDOSD_B(win));
writel(0, ctx->regs + VIDOSD_C(win));
if (win == 1 || win == 2)
writel(0, ctx->regs + VIDOSD_D(win));
val = readl(ctx->regs + SHADOWCON);
val &= ~SHADOWCON_WINx_PROTECT(win);
writel(val, ctx->regs + SHADOWCON);
}
static int __devinit fimd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct fimd_context *ctx;
struct exynos_drm_subdrv *subdrv;
struct exynos_drm_fimd_pdata *pdata;
struct fb_videomode *timing;
struct resource *res;
int win;
int ret = -EINVAL;
DRM_DEBUG_KMS("%s\n", __FILE__);
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(dev, "no platform data specified\n");
return -EINVAL;
}
timing = &pdata->timing;
if (!timing) {
dev_err(dev, "timing is null.\n");
return -EINVAL;
}
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
ctx->bus_clk = clk_get(dev, "fimd");
if (IS_ERR(ctx->bus_clk)) {
dev_err(dev, "failed to get bus clock\n");
ret = PTR_ERR(ctx->bus_clk);
goto err_clk_get;
}
clk_enable(ctx->bus_clk);
ctx->lcd_clk = clk_get(dev, "sclk_fimd");
if (IS_ERR(ctx->lcd_clk)) {
dev_err(dev, "failed to get lcd clock\n");
ret = PTR_ERR(ctx->lcd_clk);
goto err_bus_clk;
}
clk_enable(ctx->lcd_clk);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "failed to find registers\n");
ret = -ENOENT;
goto err_clk;
}
ctx->regs_res = request_mem_region(res->start, resource_size(res),
dev_name(dev));
if (!ctx->regs_res) {
dev_err(dev, "failed to claim register region\n");
ret = -ENOENT;
goto err_clk;
}
ctx->regs = ioremap(res->start, resource_size(res));
if (!ctx->regs) {
dev_err(dev, "failed to map registers\n");
ret = -ENXIO;
goto err_req_region_io;
}
res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!res) {
dev_err(dev, "irq request failed.\n");
goto err_req_region_irq;
}
ctx->irq = res->start;
for (win = 0; win < WINDOWS_NR; win++)
fimd_clear_win(ctx, win);
ret = request_irq(ctx->irq, fimd_irq_handler, 0, "drm_fimd", ctx);
if (ret < 0) {
dev_err(dev, "irq request failed.\n");
goto err_req_irq;
}
ctx->clkdiv = fimd_calc_clkdiv(ctx, timing);
ctx->vidcon0 = pdata->vidcon0;
ctx->vidcon1 = pdata->vidcon1;
ctx->default_win = pdata->default_win;
ctx->timing = timing;
timing->pixclock = clk_get_rate(ctx->lcd_clk) / ctx->clkdiv;
DRM_DEBUG_KMS("pixel clock = %d, clkdiv = %d\n",
timing->pixclock, ctx->clkdiv);
subdrv = &ctx->subdrv;
subdrv->probe = fimd_subdrv_probe;
subdrv->remove = fimd_subdrv_remove;
subdrv->manager.pipe = -1;
subdrv->manager.ops = &fimd_manager_ops;
subdrv->manager.overlay_ops = &fimd_overlay_ops;
subdrv->manager.display = &fimd_display;
subdrv->manager.dev = dev;
platform_set_drvdata(pdev, ctx);
exynos_drm_subdrv_register(subdrv);
return 0;
err_req_irq:
err_req_region_irq:
iounmap(ctx->regs);
err_req_region_io:
release_resource(ctx->regs_res);
kfree(ctx->regs_res);
err_clk:
clk_disable(ctx->lcd_clk);
clk_put(ctx->lcd_clk);
err_bus_clk:
clk_disable(ctx->bus_clk);
clk_put(ctx->bus_clk);
err_clk_get:
kfree(ctx);
return ret;
}
static int __devexit fimd_remove(struct platform_device *pdev)
{
struct fimd_context *ctx = platform_get_drvdata(pdev);
DRM_DEBUG_KMS("%s\n", __FILE__);
exynos_drm_subdrv_unregister(&ctx->subdrv);
clk_disable(ctx->lcd_clk);
clk_disable(ctx->bus_clk);
clk_put(ctx->lcd_clk);
clk_put(ctx->bus_clk);
iounmap(ctx->regs);
release_resource(ctx->regs_res);
kfree(ctx->regs_res);
free_irq(ctx->irq, ctx);
kfree(ctx);
return 0;
}
static struct platform_driver fimd_driver = {
.probe = fimd_probe,
.remove = __devexit_p(fimd_remove),
.driver = {
.name = "exynos4-fb",
.owner = THIS_MODULE,
},
};
static int __init fimd_init(void)
{
return platform_driver_register(&fimd_driver);
}
static void __exit fimd_exit(void)
{
platform_driver_unregister(&fimd_driver);
}
module_init(fimd_init);
module_exit(fimd_exit);
MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_DESCRIPTION("Samsung DRM FIMD Driver");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,415 @@
/* exynos_drm_gem.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 <drm/exynos_drm.h>
#include "exynos_drm_drv.h"
#include "exynos_drm_gem.h"
#include "exynos_drm_buf.h"
static unsigned int convert_to_vm_err_msg(int msg)
{
unsigned int out_msg;
switch (msg) {
case 0:
case -ERESTARTSYS:
case -EINTR:
out_msg = VM_FAULT_NOPAGE;
break;
case -ENOMEM:
out_msg = VM_FAULT_OOM;
break;
default:
out_msg = VM_FAULT_SIGBUS;
break;
}
return out_msg;
}
static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
}
struct exynos_drm_gem_obj *exynos_drm_gem_create(struct drm_file *file_priv,
struct drm_device *dev, unsigned int size,
unsigned int *handle)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct exynos_drm_buf_entry *entry;
struct drm_gem_object *obj;
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
size = roundup(size, PAGE_SIZE);
exynos_gem_obj = kzalloc(sizeof(*exynos_gem_obj), GFP_KERNEL);
if (!exynos_gem_obj) {
DRM_ERROR("failed to allocate exynos gem object.\n");
return ERR_PTR(-ENOMEM);
}
/* allocate the new buffer object and memory region. */
entry = exynos_drm_buf_create(dev, size);
if (!entry) {
kfree(exynos_gem_obj);
return ERR_PTR(-ENOMEM);
}
exynos_gem_obj->entry = entry;
obj = &exynos_gem_obj->base;
ret = drm_gem_object_init(dev, obj, size);
if (ret < 0) {
DRM_ERROR("failed to initailize gem object.\n");
goto err_obj_init;
}
DRM_DEBUG_KMS("created file object = 0x%x\n", (unsigned int)obj->filp);
ret = drm_gem_create_mmap_offset(obj);
if (ret < 0) {
DRM_ERROR("failed to allocate mmap offset.\n");
goto err_create_mmap_offset;
}
/*
* allocate a id of idr table where the obj is registered
* and handle has the id what user can see.
*/
ret = drm_gem_handle_create(file_priv, obj, handle);
if (ret)
goto err_handle_create;
DRM_DEBUG_KMS("gem handle = 0x%x\n", *handle);
/* drop reference from allocate - handle holds it now. */
drm_gem_object_unreference_unlocked(obj);
return exynos_gem_obj;
err_handle_create:
drm_gem_free_mmap_offset(obj);
err_create_mmap_offset:
drm_gem_object_release(obj);
err_obj_init:
exynos_drm_buf_destroy(dev, exynos_gem_obj->entry);
kfree(exynos_gem_obj);
return ERR_PTR(ret);
}
int exynos_drm_gem_create_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_create *args = data;
struct exynos_drm_gem_obj *exynos_gem_obj;
DRM_DEBUG_KMS("%s : size = 0x%x\n", __FILE__, args->size);
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size,
&args->handle);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
return 0;
}
int exynos_drm_gem_map_offset_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_map_off *args = data;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("handle = 0x%x, offset = 0x%lx\n",
args->handle, (unsigned long)args->offset);
if (!(dev->driver->driver_features & DRIVER_GEM)) {
DRM_ERROR("does not support GEM.\n");
return -ENODEV;
}
return exynos_drm_gem_dumb_map_offset(file_priv, dev, args->handle,
&args->offset);
}
static int exynos_drm_gem_mmap_buffer(struct file *filp,
struct vm_area_struct *vma)
{
struct drm_gem_object *obj = filp->private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct exynos_drm_buf_entry *entry;
unsigned long pfn, vm_size;
DRM_DEBUG_KMS("%s\n", __FILE__);
vma->vm_flags |= (VM_IO | VM_RESERVED);
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
vma->vm_file = filp;
vm_size = vma->vm_end - vma->vm_start;
/*
* a entry contains information to physically continuous memory
* allocated by user request or at framebuffer creation.
*/
entry = exynos_gem_obj->entry;
/* check if user-requested size is valid. */
if (vm_size > entry->size)
return -EINVAL;
/*
* get page frame number to physical memory to be mapped
* to user space.
*/
pfn = exynos_gem_obj->entry->paddr >> PAGE_SHIFT;
DRM_DEBUG_KMS("pfn = 0x%lx\n", pfn);
if (remap_pfn_range(vma, vma->vm_start, pfn, vm_size,
vma->vm_page_prot)) {
DRM_ERROR("failed to remap pfn range.\n");
return -EAGAIN;
}
return 0;
}
static const struct file_operations exynos_drm_gem_fops = {
.mmap = exynos_drm_gem_mmap_buffer,
};
int exynos_drm_gem_mmap_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_exynos_gem_mmap *args = data;
struct drm_gem_object *obj;
unsigned int addr;
DRM_DEBUG_KMS("%s\n", __FILE__);
if (!(dev->driver->driver_features & DRIVER_GEM)) {
DRM_ERROR("does not support GEM.\n");
return -ENODEV;
}
obj = drm_gem_object_lookup(dev, file_priv, args->handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
return -EINVAL;
}
obj->filp->f_op = &exynos_drm_gem_fops;
obj->filp->private_data = obj;
down_write(&current->mm->mmap_sem);
addr = do_mmap(obj->filp, 0, args->size,
PROT_READ | PROT_WRITE, MAP_SHARED, 0);
up_write(&current->mm->mmap_sem);
drm_gem_object_unreference_unlocked(obj);
if (IS_ERR((void *)addr))
return PTR_ERR((void *)addr);
args->mapped = addr;
DRM_DEBUG_KMS("mapped = 0x%lx\n", (unsigned long)args->mapped);
return 0;
}
int exynos_drm_gem_init_object(struct drm_gem_object *obj)
{
DRM_DEBUG_KMS("%s\n", __FILE__);
return 0;
}
void exynos_drm_gem_free_object(struct drm_gem_object *gem_obj)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
DRM_DEBUG_KMS("%s\n", __FILE__);
DRM_DEBUG_KMS("handle count = %d\n",
atomic_read(&gem_obj->handle_count));
if (gem_obj->map_list.map)
drm_gem_free_mmap_offset(gem_obj);
/* release file pointer to gem object. */
drm_gem_object_release(gem_obj);
exynos_gem_obj = to_exynos_gem_obj(gem_obj);
exynos_drm_buf_destroy(gem_obj->dev, exynos_gem_obj->entry);
kfree(exynos_gem_obj);
}
int exynos_drm_gem_dumb_create(struct drm_file *file_priv,
struct drm_device *dev, struct drm_mode_create_dumb *args)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* alocate memory to be used for framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_CREATE_DUMB command.
*/
args->pitch = args->width * args->bpp >> 3;
args->size = args->pitch * args->height;
exynos_gem_obj = exynos_drm_gem_create(file_priv, dev, args->size,
&args->handle);
if (IS_ERR(exynos_gem_obj))
return PTR_ERR(exynos_gem_obj);
return 0;
}
int exynos_drm_gem_dumb_map_offset(struct drm_file *file_priv,
struct drm_device *dev, uint32_t handle, uint64_t *offset)
{
struct exynos_drm_gem_obj *exynos_gem_obj;
struct drm_gem_object *obj;
DRM_DEBUG_KMS("%s\n", __FILE__);
mutex_lock(&dev->struct_mutex);
/*
* get offset of memory allocated for drm framebuffer.
* - this callback would be called by user application
* with DRM_IOCTL_MODE_MAP_DUMB command.
*/
obj = drm_gem_object_lookup(dev, file_priv, handle);
if (!obj) {
DRM_ERROR("failed to lookup gem object.\n");
mutex_unlock(&dev->struct_mutex);
return -EINVAL;
}
exynos_gem_obj = to_exynos_gem_obj(obj);
*offset = get_gem_mmap_offset(&exynos_gem_obj->base);
drm_gem_object_unreference(obj);
DRM_DEBUG_KMS("offset = 0x%lx\n", (unsigned long)*offset);
mutex_unlock(&dev->struct_mutex);
return 0;
}
int exynos_drm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct drm_gem_object *obj = vma->vm_private_data;
struct exynos_drm_gem_obj *exynos_gem_obj = to_exynos_gem_obj(obj);
struct drm_device *dev = obj->dev;
unsigned long pfn;
pgoff_t page_offset;
int ret;
page_offset = ((unsigned long)vmf->virtual_address -
vma->vm_start) >> PAGE_SHIFT;
mutex_lock(&dev->struct_mutex);
pfn = (exynos_gem_obj->entry->paddr >> PAGE_SHIFT) + page_offset;
ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn);
mutex_unlock(&dev->struct_mutex);
return convert_to_vm_err_msg(ret);
}
int exynos_drm_gem_mmap(struct file *filp, struct vm_area_struct *vma)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/* set vm_area_struct. */
ret = drm_gem_mmap(filp, vma);
if (ret < 0) {
DRM_ERROR("failed to mmap.\n");
return ret;
}
vma->vm_flags &= ~VM_PFNMAP;
vma->vm_flags |= VM_MIXEDMAP;
return ret;
}
int exynos_drm_gem_dumb_destroy(struct drm_file *file_priv,
struct drm_device *dev, unsigned int handle)
{
int ret;
DRM_DEBUG_KMS("%s\n", __FILE__);
/*
* obj->refcount and obj->handle_count are decreased and
* if both them are 0 then exynos_drm_gem_free_object()
* would be called by callback to release resources.
*/
ret = drm_gem_handle_delete(file_priv, handle);
if (ret < 0) {
DRM_ERROR("failed to delete drm_gem_handle.\n");
return ret;
}
return 0;
}
MODULE_AUTHOR("Inki Dae <inki.dae@samsung.com>");
MODULE_DESCRIPTION("Samsung SoC DRM GEM Module");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,107 @@
/* 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

View file

@ -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;
} }

View file

@ -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)
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);

View file

@ -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)
} }
} }
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,

View file

@ -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 = {

View file

@ -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,
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,
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,
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,
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,
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,
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))

View file

@ -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)
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;

View file

@ -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);
#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)
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)
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);
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

View file

@ -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 {
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 {
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 {
/* 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 {
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 {
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 {
* (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 {
* *
* 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 {
* *
* 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);
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);
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);
((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)
#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)

View file

@ -179,7 +179,7 @@ i915_gem_get_aperture_ioctl(struct drm_device *dev, void *data,
mutex_unlock(&dev->struct_mutex); mutex_unlock(&dev->struct_mutex);
args->aper_size = dev_priv->mm.gtt_total; args->aper_size = dev_priv->mm.gtt_total;
args->aper_available_size = args->aper_size -pinned; args->aper_available_size = args->aper_size - pinned;
return 0; return 0;
} }
@ -195,6 +195,8 @@ i915_gem_create(struct drm_file *file,
u32 handle; u32 handle;
size = roundup(size, PAGE_SIZE); size = roundup(size, PAGE_SIZE);
if (size == 0)
return -EINVAL;
/* Allocate the new object */ /* Allocate the new object */
obj = i915_gem_alloc_object(dev, size); obj = i915_gem_alloc_object(dev, size);
@ -800,11 +802,11 @@ i915_gem_shmem_pwrite_fast(struct drm_device *dev,
if (IS_ERR(page)) if (IS_ERR(page))
return PTR_ERR(page); return PTR_ERR(page);
vaddr = kmap_atomic(page, KM_USER0); vaddr = kmap_atomic(page);
ret = __copy_from_user_inatomic(vaddr + page_offset, ret = __copy_from_user_inatomic(vaddr + page_offset,
user_data, user_data,
page_length); page_length);
kunmap_atomic(vaddr, KM_USER0); kunmap_atomic(vaddr);
set_page_dirty(page); set_page_dirty(page);
mark_page_accessed(page); mark_page_accessed(page);
@ -1264,74 +1266,6 @@ out:
} }
} }
/**
* i915_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.
*/
static int
i915_gem_create_mmap_offset(struct drm_i915_gem_object *obj)
{
struct drm_device *dev = obj->base.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->base.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->base.size;
map->handle = obj;
/* Get a DRM GEM mmap offset allocated... */
list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
obj->base.size / PAGE_SIZE,
0, 0);
if (!list->file_offset_node) {
DRM_ERROR("failed to allocate offset for bo %d\n",
obj->base.name);
ret = -ENOSPC;
goto out_free_list;
}
list->file_offset_node = drm_mm_get_block(list->file_offset_node,
obj->base.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;
}
/** /**
* i915_gem_release_mmap - remove physical page mappings * i915_gem_release_mmap - remove physical page mappings
* @obj: obj in question * @obj: obj in question
@ -1360,19 +1294,6 @@ i915_gem_release_mmap(struct drm_i915_gem_object *obj)
obj->fault_mappable = false; obj->fault_mappable = false;
} }
static void
i915_gem_free_mmap_offset(struct drm_i915_gem_object *obj)
{
struct drm_device *dev = obj->base.dev;
struct drm_gem_mm *mm = dev->mm_private;
struct drm_map_list *list = &obj->base.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;
}
static uint32_t static uint32_t
i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode) i915_gem_get_gtt_size(struct drm_device *dev, uint32_t size, int tiling_mode)
{ {
@ -1485,7 +1406,7 @@ i915_gem_mmap_gtt(struct drm_file *file,
} }
if (!obj->base.map_list.map) { if (!obj->base.map_list.map) {
ret = i915_gem_create_mmap_offset(obj); ret = drm_gem_create_mmap_offset(&obj->base);
if (ret) if (ret)
goto out; goto out;
} }
@ -1557,7 +1478,7 @@ i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
obj->pages[i] = page; obj->pages[i] = page;
} }
if (obj->tiling_mode != I915_TILING_NONE) if (i915_gem_object_needs_bit17_swizzle(obj))
i915_gem_object_do_bit_17_swizzle(obj); i915_gem_object_do_bit_17_swizzle(obj);
return 0; return 0;
@ -1579,7 +1500,7 @@ i915_gem_object_put_pages_gtt(struct drm_i915_gem_object *obj)
BUG_ON(obj->madv == __I915_MADV_PURGED); BUG_ON(obj->madv == __I915_MADV_PURGED);
if (obj->tiling_mode != I915_TILING_NONE) if (i915_gem_object_needs_bit17_swizzle(obj))
i915_gem_object_save_bit_17_swizzle(obj); i915_gem_object_save_bit_17_swizzle(obj);
if (obj->madv == I915_MADV_DONTNEED) if (obj->madv == I915_MADV_DONTNEED)
@ -1856,7 +1777,7 @@ void i915_gem_reset(struct drm_device *dev)
* lost bo to the inactive list. * lost bo to the inactive list.
*/ */
while (!list_empty(&dev_priv->mm.flushing_list)) { while (!list_empty(&dev_priv->mm.flushing_list)) {
obj= list_first_entry(&dev_priv->mm.flushing_list, obj = list_first_entry(&dev_priv->mm.flushing_list,
struct drm_i915_gem_object, struct drm_i915_gem_object,
mm_list); mm_list);
@ -1922,7 +1843,7 @@ i915_gem_retire_requests_ring(struct intel_ring_buffer *ring)
while (!list_empty(&ring->active_list)) { while (!list_empty(&ring->active_list)) {
struct drm_i915_gem_object *obj; struct drm_i915_gem_object *obj;
obj= list_first_entry(&ring->active_list, obj = list_first_entry(&ring->active_list,
struct drm_i915_gem_object, struct drm_i915_gem_object,
ring_list); ring_list);
@ -2272,14 +2193,8 @@ int
i915_gpu_idle(struct drm_device *dev) i915_gpu_idle(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
bool lists_empty;
int ret, i; int ret, i;
lists_empty = (list_empty(&dev_priv->mm.flushing_list) &&
list_empty(&dev_priv->mm.active_list));
if (lists_empty)
return 0;
/* Flush everything onto the inactive list. */ /* Flush everything onto the inactive list. */
for (i = 0; i < I915_NUM_RINGS; i++) { for (i = 0; i < I915_NUM_RINGS; i++) {
ret = i915_ring_idle(&dev_priv->ring[i]); ret = i915_ring_idle(&dev_priv->ring[i]);
@ -2882,7 +2797,7 @@ i915_gem_object_bind_to_gtt(struct drm_i915_gem_object *obj,
fenceable = fenceable =
obj->gtt_space->size == fence_size && obj->gtt_space->size == fence_size &&
(obj->gtt_space->start & (fence_alignment -1)) == 0; (obj->gtt_space->start & (fence_alignment - 1)) == 0;
mappable = mappable =
obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end; obj->gtt_offset + obj->base.size <= dev_priv->mm.gtt_mappable_end;
@ -3598,7 +3513,7 @@ i915_gem_busy_ioctl(struct drm_device *dev, void *data,
*/ */
request = kzalloc(sizeof(*request), GFP_KERNEL); request = kzalloc(sizeof(*request), GFP_KERNEL);
if (request) if (request)
ret = i915_add_request(obj->ring, NULL,request); ret = i915_add_request(obj->ring, NULL, request);
else else
ret = -ENOMEM; ret = -ENOMEM;
} }
@ -3752,7 +3667,7 @@ static void i915_gem_free_object_tail(struct drm_i915_gem_object *obj)
trace_i915_gem_object_destroy(obj); trace_i915_gem_object_destroy(obj);
if (obj->base.map_list.map) if (obj->base.map_list.map)
i915_gem_free_mmap_offset(obj); drm_gem_free_mmap_offset(&obj->base);
drm_gem_object_release(&obj->base); drm_gem_object_release(&obj->base);
i915_gem_info_remove_obj(dev_priv, obj->base.size); i915_gem_info_remove_obj(dev_priv, obj->base.size);

View file

@ -72,7 +72,7 @@ i915_verify_lists(struct drm_device *dev)
break; break;
} else if (!obj->active || } else if (!obj->active ||
(obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0 || (obj->base.write_domain & I915_GEM_GPU_DOMAINS) == 0 ||
list_empty(&obj->gpu_write_list)){ list_empty(&obj->gpu_write_list)) {
DRM_ERROR("invalid flushing %p (a %d w %x gwl %d)\n", DRM_ERROR("invalid flushing %p (a %d w %x gwl %d)\n",
obj, obj,
obj->active, obj->active,
@ -157,7 +157,7 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle)
for (page = 0; page < obj->size / PAGE_SIZE; page++) { for (page = 0; page < obj->size / PAGE_SIZE; page++) {
int i; int i;
backing_map = kmap_atomic(obj->pages[page], KM_USER0); backing_map = kmap_atomic(obj->pages[page]);
if (backing_map == NULL) { if (backing_map == NULL) {
DRM_ERROR("failed to map backing page\n"); DRM_ERROR("failed to map backing page\n");
@ -181,13 +181,13 @@ i915_gem_object_check_coherency(struct drm_i915_gem_object *obj, int handle)
} }
} }
} }
kunmap_atomic(backing_map, KM_USER0); kunmap_atomic(backing_map);
backing_map = NULL; backing_map = NULL;
} }
out: out:
if (backing_map != NULL) if (backing_map != NULL)
kunmap_atomic(backing_map, KM_USER0); kunmap_atomic(backing_map);
iounmap(gtt_mapping); iounmap(gtt_mapping);
/* give syslog time to catch up */ /* give syslog time to catch up */

View file

@ -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))

View file

@ -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

View file

@ -49,6 +49,28 @@ static unsigned int cache_level_to_agp_type(struct drm_device *dev,
} }
} }
static bool do_idling(struct drm_i915_private *dev_priv)
{
bool ret = dev_priv->mm.interruptible;
if (unlikely(dev_priv->mm.gtt->do_idle_maps)) {
dev_priv->mm.interruptible = false;
if (i915_gpu_idle(dev_priv->dev)) {
DRM_ERROR("Couldn't idle GPU\n");
/* Wait a bit, in hopes it avoids the hang */
udelay(10);
}
}
return ret;
}
static void undo_idling(struct drm_i915_private *dev_priv, bool interruptible)
{
if (unlikely(dev_priv->mm.gtt->do_idle_maps))
dev_priv->mm.interruptible = interruptible;
}
void i915_gem_restore_gtt_mappings(struct drm_device *dev) void i915_gem_restore_gtt_mappings(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
@ -117,6 +139,12 @@ void i915_gem_gtt_rebind_object(struct drm_i915_gem_object *obj,
void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj) void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
{ {
struct drm_device *dev = obj->base.dev;
struct drm_i915_private *dev_priv = dev->dev_private;
bool interruptible;
interruptible = do_idling(dev_priv);
intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT, intel_gtt_clear_range(obj->gtt_space->start >> PAGE_SHIFT,
obj->base.size >> PAGE_SHIFT); obj->base.size >> PAGE_SHIFT);
@ -124,4 +152,6 @@ void i915_gem_gtt_unbind_object(struct drm_i915_gem_object *obj)
intel_gtt_unmap_memory(obj->sg_list, obj->num_sg); intel_gtt_unmap_memory(obj->sg_list, obj->num_sg);
obj->sg_list = NULL; obj->sg_list = NULL;
} }
undo_idling(dev_priv, interruptible);
} }

View file

@ -92,7 +92,10 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_x = I915_BIT_6_SWIZZLE_UNKNOWN;
uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN; uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
if (INTEL_INFO(dev)->gen >= 5) { if (INTEL_INFO(dev)->gen >= 6) {
swizzle_x = I915_BIT_6_SWIZZLE_NONE;
swizzle_y = I915_BIT_6_SWIZZLE_NONE;
} else if (IS_GEN5(dev)) {
/* On Ironlake whatever DRAM config, GPU always do /* On Ironlake whatever DRAM config, GPU always do
* same swizzling setup. * same swizzling setup.
*/ */
@ -440,14 +443,9 @@ i915_gem_swizzle_page(struct page *page)
void void
i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj) i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
{ {
struct drm_device *dev = obj->base.dev;
drm_i915_private_t *dev_priv = dev->dev_private;
int page_count = obj->base.size >> PAGE_SHIFT; int page_count = obj->base.size >> PAGE_SHIFT;
int i; int i;
if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17)
return;
if (obj->bit_17 == NULL) if (obj->bit_17 == NULL)
return; return;
@ -464,14 +462,9 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
void void
i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj) i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
{ {
struct drm_device *dev = obj->base.dev;
drm_i915_private_t *dev_priv = dev->dev_private;
int page_count = obj->base.size >> PAGE_SHIFT; int page_count = obj->base.size >> PAGE_SHIFT;
int i; int i;
if (dev_priv->mm.bit_6_swizzle_x != I915_BIT_6_SWIZZLE_9_10_17)
return;
if (obj->bit_17 == NULL) { if (obj->bit_17 == NULL) {
obj->bit_17 = kmalloc(BITS_TO_LONGS(page_count) * obj->bit_17 = kmalloc(BITS_TO_LONGS(page_count) *
sizeof(long), GFP_KERNEL); sizeof(long), GFP_KERNEL);

View file

@ -383,6 +383,7 @@ static void gen6_pm_rps_work(struct work_struct *work)
pm_iir = dev_priv->pm_iir; pm_iir = dev_priv->pm_iir;
dev_priv->pm_iir = 0; dev_priv->pm_iir = 0;
pm_imr = I915_READ(GEN6_PMIMR); pm_imr = I915_READ(GEN6_PMIMR);
I915_WRITE(GEN6_PMIMR, 0);
spin_unlock_irq(&dev_priv->rps_lock); spin_unlock_irq(&dev_priv->rps_lock);
if (!pm_iir) if (!pm_iir)
@ -420,7 +421,6 @@ static void gen6_pm_rps_work(struct work_struct *work)
* an *extremely* unlikely race with gen6_rps_enable() that is prevented * an *extremely* unlikely race with gen6_rps_enable() that is prevented
* by holding struct_mutex for the duration of the write. * by holding struct_mutex for the duration of the write.
*/ */
I915_WRITE(GEN6_PMIMR, pm_imr & ~pm_iir);
mutex_unlock(&dev_priv->dev->struct_mutex); mutex_unlock(&dev_priv->dev->struct_mutex);
} }
@ -536,8 +536,9 @@ static irqreturn_t ivybridge_irq_handler(DRM_IRQ_ARGS)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&dev_priv->rps_lock, flags); spin_lock_irqsave(&dev_priv->rps_lock, flags);
WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
I915_WRITE(GEN6_PMIMR, pm_iir);
dev_priv->pm_iir |= pm_iir; dev_priv->pm_iir |= pm_iir;
I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir);
POSTING_READ(GEN6_PMIMR);
spin_unlock_irqrestore(&dev_priv->rps_lock, flags); spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
queue_work(dev_priv->wq, &dev_priv->rps_work); queue_work(dev_priv->wq, &dev_priv->rps_work);
} }
@ -649,8 +650,9 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&dev_priv->rps_lock, flags); spin_lock_irqsave(&dev_priv->rps_lock, flags);
WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n"); WARN(dev_priv->pm_iir & pm_iir, "Missed a PM interrupt\n");
I915_WRITE(GEN6_PMIMR, pm_iir);
dev_priv->pm_iir |= pm_iir; dev_priv->pm_iir |= pm_iir;
I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir);
POSTING_READ(GEN6_PMIMR);
spin_unlock_irqrestore(&dev_priv->rps_lock, flags); spin_unlock_irqrestore(&dev_priv->rps_lock, flags);
queue_work(dev_priv->wq, &dev_priv->rps_work); queue_work(dev_priv->wq, &dev_priv->rps_work);
} }
@ -711,7 +713,7 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
page_count = src->base.size / PAGE_SIZE; page_count = src->base.size / PAGE_SIZE;
dst = kmalloc(sizeof(*dst) + page_count * sizeof (u32 *), GFP_ATOMIC); dst = kmalloc(sizeof(*dst) + page_count * sizeof(u32 *), GFP_ATOMIC);
if (dst == NULL) if (dst == NULL)
return NULL; return NULL;
@ -1493,7 +1495,7 @@ static int ironlake_enable_vblank(struct drm_device *dev, int pipe)
spin_lock_irqsave(&dev_priv->irq_lock, irqflags); spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
ironlake_enable_display_irq(dev_priv, (pipe == 0) ? ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); DE_PIPEA_VBLANK : DE_PIPEB_VBLANK);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
return 0; return 0;
@ -1541,7 +1543,7 @@ static void ironlake_disable_vblank(struct drm_device *dev, int pipe)
spin_lock_irqsave(&dev_priv->irq_lock, irqflags); spin_lock_irqsave(&dev_priv->irq_lock, irqflags);
ironlake_disable_display_irq(dev_priv, (pipe == 0) ? ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
DE_PIPEA_VBLANK: DE_PIPEB_VBLANK); DE_PIPEA_VBLANK : DE_PIPEB_VBLANK);
spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags); spin_unlock_irqrestore(&dev_priv->irq_lock, irqflags);
} }
@ -1777,6 +1779,26 @@ static void ironlake_irq_preinstall(struct drm_device *dev)
POSTING_READ(SDEIER); POSTING_READ(SDEIER);
} }
/*
* Enable digital hotplug on the PCH, and configure the DP short pulse
* duration to 2ms (which is the minimum in the Display Port spec)
*
* This register is the same on all known PCH chips.
*/
static void ironlake_enable_pch_hotplug(struct drm_device *dev)
{
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
u32 hotplug;
hotplug = I915_READ(PCH_PORT_HOTPLUG);
hotplug &= ~(PORTD_PULSE_DURATION_MASK|PORTC_PULSE_DURATION_MASK|PORTB_PULSE_DURATION_MASK);
hotplug |= PORTD_HOTPLUG_ENABLE | PORTD_PULSE_DURATION_2ms;
hotplug |= PORTC_HOTPLUG_ENABLE | PORTC_PULSE_DURATION_2ms;
hotplug |= PORTB_HOTPLUG_ENABLE | PORTB_PULSE_DURATION_2ms;
I915_WRITE(PCH_PORT_HOTPLUG, hotplug);
}
static int ironlake_irq_postinstall(struct drm_device *dev) static int ironlake_irq_postinstall(struct drm_device *dev)
{ {
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private; drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
@ -1839,6 +1861,8 @@ static int ironlake_irq_postinstall(struct drm_device *dev)
I915_WRITE(SDEIER, hotplug_mask); I915_WRITE(SDEIER, hotplug_mask);
POSTING_READ(SDEIER); POSTING_READ(SDEIER);
ironlake_enable_pch_hotplug(dev);
if (IS_IRONLAKE_M(dev)) { if (IS_IRONLAKE_M(dev)) {
/* Clear & enable PCU event interrupts */ /* Clear & enable PCU event interrupts */
I915_WRITE(DEIIR, DE_PCU_EVENT); I915_WRITE(DEIIR, DE_PCU_EVENT);
@ -1896,6 +1920,8 @@ static int ivybridge_irq_postinstall(struct drm_device *dev)
I915_WRITE(SDEIER, hotplug_mask); I915_WRITE(SDEIER, hotplug_mask);
POSTING_READ(SDEIER); POSTING_READ(SDEIER);
ironlake_enable_pch_hotplug(dev);
return 0; return 0;
} }
@ -2020,6 +2046,10 @@ static void ironlake_irq_uninstall(struct drm_device *dev)
I915_WRITE(GTIMR, 0xffffffff); I915_WRITE(GTIMR, 0xffffffff);
I915_WRITE(GTIER, 0x0); I915_WRITE(GTIER, 0x0);
I915_WRITE(GTIIR, I915_READ(GTIIR)); I915_WRITE(GTIIR, I915_READ(GTIIR));
I915_WRITE(SDEIMR, 0xffffffff);
I915_WRITE(SDEIER, 0x0);
I915_WRITE(SDEIIR, I915_READ(SDEIIR));
} }
static void i915_driver_irq_uninstall(struct drm_device * dev) static void i915_driver_irq_uninstall(struct drm_device * dev)

View file

@ -202,7 +202,7 @@ static int init_heap(struct mem_block **heap, int start, int size)
blocks->next = blocks->prev = *heap; blocks->next = blocks->prev = *heap;
memset(*heap, 0, sizeof(**heap)); memset(*heap, 0, sizeof(**heap));
(*heap)->file_priv = (struct drm_file *) - 1; (*heap)->file_priv = (struct drm_file *) -1;
(*heap)->next = (*heap)->prev = blocks; (*heap)->next = (*heap)->prev = blocks;
return 0; return 0;
} }
@ -359,19 +359,19 @@ int i915_mem_init_heap(struct drm_device *dev, void *data,
return init_heap(heap, initheap->start, initheap->size); return init_heap(heap, initheap->start, initheap->size);
} }
int i915_mem_destroy_heap( struct drm_device *dev, void *data, int i915_mem_destroy_heap(struct drm_device *dev, void *data,
struct drm_file *file_priv ) struct drm_file *file_priv)
{ {
drm_i915_private_t *dev_priv = dev->dev_private; drm_i915_private_t *dev_priv = dev->dev_private;
drm_i915_mem_destroy_heap_t *destroyheap = data; drm_i915_mem_destroy_heap_t *destroyheap = data;
struct mem_block **heap; struct mem_block **heap;
if ( !dev_priv ) { if (!dev_priv) {
DRM_ERROR( "called with no initialization\n" ); DRM_ERROR("called with no initialization\n");
return -EINVAL; return -EINVAL;
} }
heap = get_heap( dev_priv, destroyheap->region ); heap = get_heap(dev_priv, destroyheap->region);
if (!heap) { if (!heap) {
DRM_ERROR("get_heap failed"); DRM_ERROR("get_heap failed");
return -EFAULT; return -EFAULT;
@ -382,6 +382,6 @@ int i915_mem_destroy_heap( struct drm_device *dev, void *data,
return -EFAULT; return -EFAULT;
} }
i915_mem_takedown( heap ); i915_mem_takedown(heap);
return 0; return 0;
} }

View file

@ -156,7 +156,7 @@
#define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0) #define MI_SUSPEND_FLUSH MI_INSTR(0x0b, 0)
#define MI_SUSPEND_FLUSH_EN (1<<0) #define MI_SUSPEND_FLUSH_EN (1<<0)
#define MI_REPORT_HEAD MI_INSTR(0x07, 0) #define MI_REPORT_HEAD MI_INSTR(0x07, 0)
#define MI_OVERLAY_FLIP MI_INSTR(0x11,0) #define MI_OVERLAY_FLIP MI_INSTR(0x11, 0)
#define MI_OVERLAY_CONTINUE (0x0<<21) #define MI_OVERLAY_CONTINUE (0x0<<21)
#define MI_OVERLAY_ON (0x1<<21) #define MI_OVERLAY_ON (0x1<<21)
#define MI_OVERLAY_OFF (0x2<<21) #define MI_OVERLAY_OFF (0x2<<21)
@ -194,6 +194,13 @@
#define MI_SEMAPHORE_UPDATE (1<<21) #define MI_SEMAPHORE_UPDATE (1<<21)
#define MI_SEMAPHORE_COMPARE (1<<20) #define MI_SEMAPHORE_COMPARE (1<<20)
#define MI_SEMAPHORE_REGISTER (1<<18) #define MI_SEMAPHORE_REGISTER (1<<18)
#define MI_SEMAPHORE_SYNC_RV (2<<16)
#define MI_SEMAPHORE_SYNC_RB (0<<16)
#define MI_SEMAPHORE_SYNC_VR (0<<16)
#define MI_SEMAPHORE_SYNC_VB (2<<16)
#define MI_SEMAPHORE_SYNC_BR (2<<16)
#define MI_SEMAPHORE_SYNC_BV (0<<16)
#define MI_SEMAPHORE_SYNC_INVALID (1<<0)
/* /*
* 3D instructions used by the kernel * 3D instructions used by the kernel
*/ */
@ -235,16 +242,22 @@
#define ASYNC_FLIP (1<<22) #define ASYNC_FLIP (1<<22)
#define DISPLAY_PLANE_A (0<<20) #define DISPLAY_PLANE_A (0<<20)
#define DISPLAY_PLANE_B (1<<20) #define DISPLAY_PLANE_B (1<<20)
#define GFX_OP_PIPE_CONTROL ((0x3<<29)|(0x3<<27)|(0x2<<24)|2) #define GFX_OP_PIPE_CONTROL(len) ((0x3<<29)|(0x3<<27)|(0x2<<24)|(len-2))
#define PIPE_CONTROL_CS_STALL (1<<20)
#define PIPE_CONTROL_QW_WRITE (1<<14) #define PIPE_CONTROL_QW_WRITE (1<<14)
#define PIPE_CONTROL_DEPTH_STALL (1<<13) #define PIPE_CONTROL_DEPTH_STALL (1<<13)
#define PIPE_CONTROL_WC_FLUSH (1<<12) #define PIPE_CONTROL_WRITE_FLUSH (1<<12)
#define PIPE_CONTROL_IS_FLUSH (1<<11) /* MBZ on Ironlake */ #define PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH (1<<12) /* gen6+ */
#define PIPE_CONTROL_TC_FLUSH (1<<10) /* GM45+ only */ #define PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE (1<<11) /* MBZ on Ironlake */
#define PIPE_CONTROL_ISP_DIS (1<<9) #define PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE (1<<10) /* GM45+ only */
#define PIPE_CONTROL_INDIRECT_STATE_DISABLE (1<<9)
#define PIPE_CONTROL_NOTIFY (1<<8) #define PIPE_CONTROL_NOTIFY (1<<8)
#define PIPE_CONTROL_VF_CACHE_INVALIDATE (1<<4)
#define PIPE_CONTROL_CONST_CACHE_INVALIDATE (1<<3)
#define PIPE_CONTROL_STATE_CACHE_INVALIDATE (1<<2)
#define PIPE_CONTROL_STALL_AT_SCOREBOARD (1<<1)
#define PIPE_CONTROL_DEPTH_CACHE_FLUSH (1<<0)
#define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */ #define PIPE_CONTROL_GLOBAL_GTT (1<<2) /* in addr dword */
#define PIPE_CONTROL_STALL_EN (1<<1) /* in addr word, Ironlake+ only */
/* /*
@ -296,6 +309,12 @@
#define RING_CTL(base) ((base)+0x3c) #define RING_CTL(base) ((base)+0x3c)
#define RING_SYNC_0(base) ((base)+0x40) #define RING_SYNC_0(base) ((base)+0x40)
#define RING_SYNC_1(base) ((base)+0x44) #define RING_SYNC_1(base) ((base)+0x44)
#define GEN6_RVSYNC (RING_SYNC_0(RENDER_RING_BASE))
#define GEN6_RBSYNC (RING_SYNC_1(RENDER_RING_BASE))
#define GEN6_VRSYNC (RING_SYNC_1(GEN6_BSD_RING_BASE))
#define GEN6_VBSYNC (RING_SYNC_0(GEN6_BSD_RING_BASE))
#define GEN6_BRSYNC (RING_SYNC_0(BLT_RING_BASE))
#define GEN6_BVSYNC (RING_SYNC_1(BLT_RING_BASE))
#define RING_MAX_IDLE(base) ((base)+0x54) #define RING_MAX_IDLE(base) ((base)+0x54)
#define RING_HWS_PGA(base) ((base)+0x80) #define RING_HWS_PGA(base) ((base)+0x80)
#define RING_HWS_PGA_GEN6(base) ((base)+0x2080) #define RING_HWS_PGA_GEN6(base) ((base)+0x2080)
@ -2416,6 +2435,7 @@
#define WM0_PIPE_CURSOR_MASK (0x1f) #define WM0_PIPE_CURSOR_MASK (0x1f)
#define WM0_PIPEB_ILK 0x45104 #define WM0_PIPEB_ILK 0x45104
#define WM0_PIPEC_IVB 0x45200
#define WM1_LP_ILK 0x45108 #define WM1_LP_ILK 0x45108
#define WM1_LP_SR_EN (1<<31) #define WM1_LP_SR_EN (1<<31)
#define WM1_LP_LATENCY_SHIFT 24 #define WM1_LP_LATENCY_SHIFT 24
@ -2554,10 +2574,18 @@
#define _CURBBASE 0x700c4 #define _CURBBASE 0x700c4
#define _CURBPOS 0x700c8 #define _CURBPOS 0x700c8
#define _CURBCNTR_IVB 0x71080
#define _CURBBASE_IVB 0x71084
#define _CURBPOS_IVB 0x71088
#define CURCNTR(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR) #define CURCNTR(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR)
#define CURBASE(pipe) _PIPE(pipe, _CURABASE, _CURBBASE) #define CURBASE(pipe) _PIPE(pipe, _CURABASE, _CURBBASE)
#define CURPOS(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS) #define CURPOS(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS)
#define CURCNTR_IVB(pipe) _PIPE(pipe, _CURACNTR, _CURBCNTR_IVB)
#define CURBASE_IVB(pipe) _PIPE(pipe, _CURABASE, _CURBBASE_IVB)
#define CURPOS_IVB(pipe) _PIPE(pipe, _CURAPOS, _CURBPOS_IVB)
/* Display A control */ /* Display A control */
#define _DSPACNTR 0x70180 #define _DSPACNTR 0x70180
#define DISPLAY_PLANE_ENABLE (1<<31) #define DISPLAY_PLANE_ENABLE (1<<31)
@ -2903,12 +2931,13 @@
#define SDEIER 0xc400c #define SDEIER 0xc400c
/* digital port hotplug */ /* digital port hotplug */
#define PCH_PORT_HOTPLUG 0xc4030 #define PCH_PORT_HOTPLUG 0xc4030 /* SHOTPLUG_CTL */
#define PORTD_HOTPLUG_ENABLE (1 << 20) #define PORTD_HOTPLUG_ENABLE (1 << 20)
#define PORTD_PULSE_DURATION_2ms (0) #define PORTD_PULSE_DURATION_2ms (0)
#define PORTD_PULSE_DURATION_4_5ms (1 << 18) #define PORTD_PULSE_DURATION_4_5ms (1 << 18)
#define PORTD_PULSE_DURATION_6ms (2 << 18) #define PORTD_PULSE_DURATION_6ms (2 << 18)
#define PORTD_PULSE_DURATION_100ms (3 << 18) #define PORTD_PULSE_DURATION_100ms (3 << 18)
#define PORTD_PULSE_DURATION_MASK (3 << 18)
#define PORTD_HOTPLUG_NO_DETECT (0) #define PORTD_HOTPLUG_NO_DETECT (0)
#define PORTD_HOTPLUG_SHORT_DETECT (1 << 16) #define PORTD_HOTPLUG_SHORT_DETECT (1 << 16)
#define PORTD_HOTPLUG_LONG_DETECT (1 << 17) #define PORTD_HOTPLUG_LONG_DETECT (1 << 17)
@ -2917,6 +2946,7 @@
#define PORTC_PULSE_DURATION_4_5ms (1 << 10) #define PORTC_PULSE_DURATION_4_5ms (1 << 10)
#define PORTC_PULSE_DURATION_6ms (2 << 10) #define PORTC_PULSE_DURATION_6ms (2 << 10)
#define PORTC_PULSE_DURATION_100ms (3 << 10) #define PORTC_PULSE_DURATION_100ms (3 << 10)
#define PORTC_PULSE_DURATION_MASK (3 << 10)
#define PORTC_HOTPLUG_NO_DETECT (0) #define PORTC_HOTPLUG_NO_DETECT (0)
#define PORTC_HOTPLUG_SHORT_DETECT (1 << 8) #define PORTC_HOTPLUG_SHORT_DETECT (1 << 8)
#define PORTC_HOTPLUG_LONG_DETECT (1 << 9) #define PORTC_HOTPLUG_LONG_DETECT (1 << 9)
@ -2925,6 +2955,7 @@
#define PORTB_PULSE_DURATION_4_5ms (1 << 2) #define PORTB_PULSE_DURATION_4_5ms (1 << 2)
#define PORTB_PULSE_DURATION_6ms (2 << 2) #define PORTB_PULSE_DURATION_6ms (2 << 2)
#define PORTB_PULSE_DURATION_100ms (3 << 2) #define PORTB_PULSE_DURATION_100ms (3 << 2)
#define PORTB_PULSE_DURATION_MASK (3 << 2)
#define PORTB_HOTPLUG_NO_DETECT (0) #define PORTB_HOTPLUG_NO_DETECT (0)
#define PORTB_HOTPLUG_SHORT_DETECT (1 << 0) #define PORTB_HOTPLUG_SHORT_DETECT (1 << 0)
#define PORTB_HOTPLUG_LONG_DETECT (1 << 1) #define PORTB_HOTPLUG_LONG_DETECT (1 << 1)
@ -2945,15 +2976,15 @@
#define _PCH_DPLL_A 0xc6014 #define _PCH_DPLL_A 0xc6014
#define _PCH_DPLL_B 0xc6018 #define _PCH_DPLL_B 0xc6018
#define PCH_DPLL(pipe) _PIPE(pipe, _PCH_DPLL_A, _PCH_DPLL_B) #define PCH_DPLL(pipe) (pipe == 0 ? _PCH_DPLL_A : _PCH_DPLL_B)
#define _PCH_FPA0 0xc6040 #define _PCH_FPA0 0xc6040
#define FP_CB_TUNE (0x3<<22) #define FP_CB_TUNE (0x3<<22)
#define _PCH_FPA1 0xc6044 #define _PCH_FPA1 0xc6044
#define _PCH_FPB0 0xc6048 #define _PCH_FPB0 0xc6048
#define _PCH_FPB1 0xc604c #define _PCH_FPB1 0xc604c
#define PCH_FP0(pipe) _PIPE(pipe, _PCH_FPA0, _PCH_FPB0) #define PCH_FP0(pipe) (pipe == 0 ? _PCH_FPA0 : _PCH_FPB0)
#define PCH_FP1(pipe) _PIPE(pipe, _PCH_FPA1, _PCH_FPB1) #define PCH_FP1(pipe) (pipe == 0 ? _PCH_FPA1 : _PCH_FPB1)
#define PCH_DPLL_TEST 0xc606c #define PCH_DPLL_TEST 0xc606c
@ -3167,6 +3198,7 @@
#define FDI_LINK_TRAIN_NONE_IVB (3<<8) #define FDI_LINK_TRAIN_NONE_IVB (3<<8)
/* both Tx and Rx */ /* both Tx and Rx */
#define FDI_COMPOSITE_SYNC (1<<11)
#define FDI_LINK_TRAIN_AUTO (1<<10) #define FDI_LINK_TRAIN_AUTO (1<<10)
#define FDI_SCRAMBLING_ENABLE (0<<7) #define FDI_SCRAMBLING_ENABLE (0<<7)
#define FDI_SCRAMBLING_DISABLE (1<<7) #define FDI_SCRAMBLING_DISABLE (1<<7)
@ -3308,15 +3340,35 @@
#define PCH_PP_STATUS 0xc7200 #define PCH_PP_STATUS 0xc7200
#define PCH_PP_CONTROL 0xc7204 #define PCH_PP_CONTROL 0xc7204
#define PANEL_UNLOCK_REGS (0xabcd << 16) #define PANEL_UNLOCK_REGS (0xabcd << 16)
#define PANEL_UNLOCK_MASK (0xffff << 16)
#define EDP_FORCE_VDD (1 << 3) #define EDP_FORCE_VDD (1 << 3)
#define EDP_BLC_ENABLE (1 << 2) #define EDP_BLC_ENABLE (1 << 2)
#define PANEL_POWER_RESET (1 << 1) #define PANEL_POWER_RESET (1 << 1)
#define PANEL_POWER_OFF (0 << 0) #define PANEL_POWER_OFF (0 << 0)
#define PANEL_POWER_ON (1 << 0) #define PANEL_POWER_ON (1 << 0)
#define PCH_PP_ON_DELAYS 0xc7208 #define PCH_PP_ON_DELAYS 0xc7208
#define PANEL_PORT_SELECT_MASK (3 << 30)
#define PANEL_PORT_SELECT_LVDS (0 << 30)
#define PANEL_PORT_SELECT_DPA (1 << 30)
#define EDP_PANEL (1 << 30) #define EDP_PANEL (1 << 30)
#define PANEL_PORT_SELECT_DPC (2 << 30)
#define PANEL_PORT_SELECT_DPD (3 << 30)
#define PANEL_POWER_UP_DELAY_MASK (0x1fff0000)
#define PANEL_POWER_UP_DELAY_SHIFT 16
#define PANEL_LIGHT_ON_DELAY_MASK (0x1fff)
#define PANEL_LIGHT_ON_DELAY_SHIFT 0
#define PCH_PP_OFF_DELAYS 0xc720c #define PCH_PP_OFF_DELAYS 0xc720c
#define PANEL_POWER_DOWN_DELAY_MASK (0x1fff0000)
#define PANEL_POWER_DOWN_DELAY_SHIFT 16
#define PANEL_LIGHT_OFF_DELAY_MASK (0x1fff)
#define PANEL_LIGHT_OFF_DELAY_SHIFT 0
#define PCH_PP_DIVISOR 0xc7210 #define PCH_PP_DIVISOR 0xc7210
#define PP_REFERENCE_DIVIDER_MASK (0xffffff00)
#define PP_REFERENCE_DIVIDER_SHIFT 8
#define PANEL_POWER_CYCLE_DELAY_MASK (0x1f)
#define PANEL_POWER_CYCLE_DELAY_SHIFT 0
#define PCH_DP_B 0xe4100 #define PCH_DP_B 0xe4100
#define PCH_DPB_AUX_CH_CTL 0xe4110 #define PCH_DPB_AUX_CH_CTL 0xe4110
@ -3470,4 +3522,29 @@
#define GEN6_PCODE_DATA 0x138128 #define GEN6_PCODE_DATA 0x138128
#define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8 #define GEN6_PCODE_FREQ_IA_RATIO_SHIFT 8
#define G4X_AUD_VID_DID 0x62020
#define INTEL_AUDIO_DEVCL 0x808629FB
#define INTEL_AUDIO_DEVBLC 0x80862801
#define INTEL_AUDIO_DEVCTG 0x80862802
#define G4X_AUD_CNTL_ST 0x620B4
#define G4X_ELDV_DEVCL_DEVBLC (1 << 13)
#define G4X_ELDV_DEVCTG (1 << 14)
#define G4X_ELD_ADDR (0xf << 5)
#define G4X_ELD_ACK (1 << 4)
#define G4X_HDMIW_HDMIEDID 0x6210C
#define GEN5_HDMIW_HDMIEDID_A 0xE2050
#define GEN5_AUD_CNTL_ST_A 0xE20B4
#define GEN5_ELD_BUFFER_SIZE (0x1f << 10)
#define GEN5_ELD_ADDRESS (0x1f << 5)
#define GEN5_ELD_ACK (1 << 4)
#define GEN5_AUD_CNTL_ST2 0xE20C0
#define GEN5_ELD_VALIDB (1 << 0)
#define GEN5_CP_READYB (1 << 1)
#define GEN7_HDMIW_HDMIEDID_A 0xE5050
#define GEN7_AUD_CNTRL_ST_A 0xE50B4
#define GEN7_AUD_CNTRL_ST2 0xE50C0
#endif /* _I915_REG_H_ */ #endif /* _I915_REG_H_ */

View file

@ -60,7 +60,7 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
else else
array = dev_priv->save_palette_b; array = dev_priv->save_palette_b;
for(i = 0; i < 256; i++) for (i = 0; i < 256; i++)
array[i] = I915_READ(reg + (i << 2)); array[i] = I915_READ(reg + (i << 2));
} }
@ -82,7 +82,7 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
else else
array = dev_priv->save_palette_b; array = dev_priv->save_palette_b;
for(i = 0; i < 256; i++) for (i = 0; i < 256; i++)
I915_WRITE(reg + (i << 2), array[i]); I915_WRITE(reg + (i << 2), array[i]);
} }
@ -887,10 +887,10 @@ int i915_restore_state(struct drm_device *dev)
mutex_lock(&dev->struct_mutex); mutex_lock(&dev->struct_mutex);
/* Cache mode state */ /* Cache mode state */
I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000); I915_WRITE(CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
/* Memory arbitration state */ /* Memory arbitration state */
I915_WRITE (MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000); I915_WRITE(MI_ARB_STATE, dev_priv->saveMI_ARB_STATE | 0xffff0000);
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]); I915_WRITE(SWF00 + (i << 2), dev_priv->saveSWF0[i]);

View file

@ -64,7 +64,7 @@ static int intel_dsm(acpi_handle handle, int func, int arg)
case ACPI_TYPE_BUFFER: case ACPI_TYPE_BUFFER:
if (obj->buffer.length == 4) { if (obj->buffer.length == 4) {
result =(obj->buffer.pointer[0] | result = (obj->buffer.pointer[0] |
(obj->buffer.pointer[1] << 8) | (obj->buffer.pointer[1] << 8) |
(obj->buffer.pointer[2] << 16) | (obj->buffer.pointer[2] << 16) |
(obj->buffer.pointer[3] << 24)); (obj->buffer.pointer[3] << 24));

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2006 Intel Corporation * Copyright © 2006 Intel Corporation
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -309,6 +309,13 @@ parse_general_features(struct drm_i915_private *dev_priv,
dev_priv->lvds_use_ssc = general->enable_ssc; dev_priv->lvds_use_ssc = general->enable_ssc;
dev_priv->lvds_ssc_freq = dev_priv->lvds_ssc_freq =
intel_bios_ssc_frequency(dev, general->ssc_freq); intel_bios_ssc_frequency(dev, general->ssc_freq);
dev_priv->display_clock_mode = general->display_clock_mode;
DRM_DEBUG_KMS("BDB_GENERAL_FEATURES int_tv_support %d int_crt_support %d lvds_use_ssc %d lvds_ssc_freq %d display_clock_mode %d\n",
dev_priv->int_tv_support,
dev_priv->int_crt_support,
dev_priv->lvds_use_ssc,
dev_priv->lvds_ssc_freq,
dev_priv->display_clock_mode);
} }
} }
@ -381,7 +388,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
if (p_child->dvo_port != DEVICE_PORT_DVOB && if (p_child->dvo_port != DEVICE_PORT_DVOB &&
p_child->dvo_port != DEVICE_PORT_DVOC) { p_child->dvo_port != DEVICE_PORT_DVOC) {
/* skip the incorrect SDVO port */ /* skip the incorrect SDVO port */
DRM_DEBUG_KMS("Incorrect SDVO port. Skip it \n"); DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n");
continue; continue;
} }
DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on" DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on"
@ -396,15 +403,13 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
p_mapping->dvo_wiring = p_child->dvo_wiring; p_mapping->dvo_wiring = p_child->dvo_wiring;
p_mapping->ddc_pin = p_child->ddc_pin; p_mapping->ddc_pin = p_child->ddc_pin;
p_mapping->i2c_pin = p_child->i2c_pin; p_mapping->i2c_pin = p_child->i2c_pin;
p_mapping->i2c_speed = p_child->i2c_speed;
p_mapping->initialized = 1; p_mapping->initialized = 1;
DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d, i2c_speed=%d\n", DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n",
p_mapping->dvo_port, p_mapping->dvo_port,
p_mapping->slave_addr, p_mapping->slave_addr,
p_mapping->dvo_wiring, p_mapping->dvo_wiring,
p_mapping->ddc_pin, p_mapping->ddc_pin,
p_mapping->i2c_pin, p_mapping->i2c_pin);
p_mapping->i2c_speed);
} else { } else {
DRM_DEBUG_KMS("Maybe one SDVO port is shared by " DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
"two SDVO device.\n"); "two SDVO device.\n");
@ -564,7 +569,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv,
count++; count++;
} }
if (!count) { if (!count) {
DRM_DEBUG_KMS("no child dev is parsed from VBT \n"); DRM_DEBUG_KMS("no child dev is parsed from VBT\n");
return; return;
} }
dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL); dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL);
@ -610,7 +615,7 @@ init_vbt_defaults(struct drm_i915_private *dev_priv)
/* Default to using SSC */ /* Default to using SSC */
dev_priv->lvds_use_ssc = 1; dev_priv->lvds_use_ssc = 1;
dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1); dev_priv->lvds_ssc_freq = intel_bios_ssc_frequency(dev, 1);
DRM_DEBUG("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq); DRM_DEBUG_KMS("Set default to SSC at %dMHz\n", dev_priv->lvds_ssc_freq);
/* eDP data */ /* eDP data */
dev_priv->edp.bpp = 18; dev_priv->edp.bpp = 18;
@ -639,7 +644,7 @@ intel_parse_bios(struct drm_device *dev)
if (dev_priv->opregion.vbt) { if (dev_priv->opregion.vbt) {
struct vbt_header *vbt = dev_priv->opregion.vbt; struct vbt_header *vbt = dev_priv->opregion.vbt;
if (memcmp(vbt->signature, "$VBT", 4) == 0) { if (memcmp(vbt->signature, "$VBT", 4) == 0) {
DRM_DEBUG_DRIVER("Using VBT from OpRegion: %20s\n", DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n",
vbt->signature); vbt->signature);
bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset); bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
} else } else

View file

@ -1,5 +1,5 @@
/* /*
* Copyright © 2006 Intel Corporation * Copyright © 2006 Intel Corporation
* *
* Permission is hereby granted, free of charge, to any person obtaining a * Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"), * copy of this software and associated documentation files (the "Software"),
@ -120,7 +120,9 @@ struct bdb_general_features {
u8 ssc_freq:1; u8 ssc_freq:1;
u8 enable_lfp_on_override:1; u8 enable_lfp_on_override:1;
u8 disable_ssc_ddt:1; u8 disable_ssc_ddt:1;
u8 rsvd8:3; /* finish byte */ u8 rsvd7:1;
u8 display_clock_mode:1;
u8 rsvd8:1; /* finish byte */
/* bits 3 */ /* bits 3 */
u8 disable_smooth_vision:1; u8 disable_smooth_vision:1;
@ -133,7 +135,10 @@ struct bdb_general_features {
/* bits 5 */ /* bits 5 */
u8 int_crt_support:1; u8 int_crt_support:1;
u8 int_tv_support:1; u8 int_tv_support:1;
u8 rsvd11:6; /* finish byte */ u8 int_efp_support:1;
u8 dp_ssc_enb:1; /* PCH attached eDP supports SSC */
u8 dp_ssc_freq:1; /* SSC freq for PCH attached eDP */
u8 rsvd11:3; /* finish byte */
} __attribute__((packed)); } __attribute__((packed));
/* pre-915 */ /* pre-915 */
@ -197,8 +202,7 @@ struct bdb_general_features {
struct child_device_config { struct child_device_config {
u16 handle; u16 handle;
u16 device_type; u16 device_type;
u8 i2c_speed; u8 device_id[10]; /* ascii string */
u8 rsvd[9];
u16 addin_offset; u16 addin_offset;
u8 dvo_port; /* See Device_PORT_* above */ u8 dvo_port; /* See Device_PORT_* above */
u8 i2c_pin; u8 i2c_pin;
@ -446,11 +450,11 @@ struct bdb_driver_features {
#define EDP_VSWING_1_2V 3 #define EDP_VSWING_1_2V 3
struct edp_power_seq { struct edp_power_seq {
u16 t3; u16 t1_t3;
u16 t7; u16 t8;
u16 t9; u16 t9;
u16 t10; u16 t10;
u16 t12; u16 t11_t12;
} __attribute__ ((packed)); } __attribute__ ((packed));
struct edp_link_params { struct edp_link_params {

View file

@ -69,7 +69,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE); temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
temp &= ~ADPA_DAC_ENABLE; temp &= ~ADPA_DAC_ENABLE;
switch(mode) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
temp |= ADPA_DAC_ENABLE; temp |= ADPA_DAC_ENABLE;
break; break;
@ -152,17 +152,13 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
adpa |= ADPA_VSYNC_ACTIVE_HIGH; adpa |= ADPA_VSYNC_ACTIVE_HIGH;
if (intel_crtc->pipe == 0) { /* For CPT allow 3 pipe config, for others just use A or B */
if (HAS_PCH_CPT(dev)) if (HAS_PCH_CPT(dev))
adpa |= PORT_TRANS_A_SEL_CPT; adpa |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
else else if (intel_crtc->pipe == 0)
adpa |= ADPA_PIPE_A_SELECT; adpa |= ADPA_PIPE_A_SELECT;
} else {
if (HAS_PCH_CPT(dev))
adpa |= PORT_TRANS_B_SEL_CPT;
else else
adpa |= ADPA_PIPE_B_SELECT; adpa |= ADPA_PIPE_B_SELECT;
}
if (!HAS_PCH_SPLIT(dev)) if (!HAS_PCH_SPLIT(dev))
I915_WRITE(BCLRPAT(intel_crtc->pipe), 0); I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);

View file

@ -31,6 +31,7 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/vgaarb.h> #include <linux/vgaarb.h>
#include <drm/drm_edid.h>
#include "drmP.h" #include "drmP.h"
#include "intel_drv.h" #include "intel_drv.h"
#include "i915_drm.h" #include "i915_drm.h"
@ -42,7 +43,7 @@
#define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) #define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP))
bool intel_pipe_has_type (struct drm_crtc *crtc, int type); bool intel_pipe_has_type(struct drm_crtc *crtc, int type);
static void intel_update_watermarks(struct drm_device *dev); static void intel_update_watermarks(struct drm_device *dev);
static void intel_increase_pllclock(struct drm_crtc *crtc); static void intel_increase_pllclock(struct drm_crtc *crtc);
static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on);
@ -321,7 +322,7 @@ static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
.m1 = { .min = 12, .max = 22 }, .m1 = { .min = 12, .max = 22 },
.m2 = { .min = 5, .max = 9 }, .m2 = { .min = 5, .max = 9 },
.p = { .min = 28, .max = 112 }, .p = { .min = 28, .max = 112 },
.p1 = { .min = 2,.max = 8 }, .p1 = { .min = 2, .max = 8 },
.p2 = { .dot_limit = 225000, .p2 = { .dot_limit = 225000,
.p2_slow = 14, .p2_fast = 14 }, .p2_slow = 14, .p2_fast = 14 },
.find_pll = intel_g4x_find_best_PLL, .find_pll = intel_g4x_find_best_PLL,
@ -335,7 +336,7 @@ static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
.m1 = { .min = 12, .max = 22 }, .m1 = { .min = 12, .max = 22 },
.m2 = { .min = 5, .max = 9 }, .m2 = { .min = 5, .max = 9 },
.p = { .min = 14, .max = 42 }, .p = { .min = 14, .max = 42 },
.p1 = { .min = 2,.max = 6 }, .p1 = { .min = 2, .max = 6 },
.p2 = { .dot_limit = 225000, .p2 = { .dot_limit = 225000,
.p2_slow = 7, .p2_fast = 7 }, .p2_slow = 7, .p2_fast = 7 },
.find_pll = intel_g4x_find_best_PLL, .find_pll = intel_g4x_find_best_PLL,
@ -404,7 +405,7 @@ static const intel_limit_t *intel_g4x_limit(struct drm_crtc *crtc)
limit = &intel_limits_g4x_hdmi; limit = &intel_limits_g4x_hdmi;
} else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) { } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_SDVO)) {
limit = &intel_limits_g4x_sdvo; limit = &intel_limits_g4x_sdvo;
} else if (intel_pipe_has_type (crtc, INTEL_OUTPUT_DISPLAYPORT)) { } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
limit = &intel_limits_g4x_display_port; limit = &intel_limits_g4x_display_port;
} else /* The option is for other outputs */ } else /* The option is for other outputs */
limit = &intel_limits_i9xx_sdvo; limit = &intel_limits_i9xx_sdvo;
@ -488,26 +489,26 @@ static bool intel_PLL_is_valid(struct drm_device *dev,
const intel_clock_t *clock) const intel_clock_t *clock)
{ {
if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1)
INTELPllInvalid ("p1 out of range\n"); INTELPllInvalid("p1 out of range\n");
if (clock->p < limit->p.min || limit->p.max < clock->p) if (clock->p < limit->p.min || limit->p.max < clock->p)
INTELPllInvalid ("p out of range\n"); INTELPllInvalid("p out of range\n");
if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2)
INTELPllInvalid ("m2 out of range\n"); INTELPllInvalid("m2 out of range\n");
if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
INTELPllInvalid ("m1 out of range\n"); INTELPllInvalid("m1 out of range\n");
if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev)) if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
INTELPllInvalid ("m1 <= m2\n"); INTELPllInvalid("m1 <= m2\n");
if (clock->m < limit->m.min || limit->m.max < clock->m) if (clock->m < limit->m.min || limit->m.max < clock->m)
INTELPllInvalid ("m out of range\n"); INTELPllInvalid("m out of range\n");
if (clock->n < limit->n.min || limit->n.max < clock->n) if (clock->n < limit->n.min || limit->n.max < clock->n)
INTELPllInvalid ("n out of range\n"); INTELPllInvalid("n out of range\n");
if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) if (clock->vco < limit->vco.min || limit->vco.max < clock->vco)
INTELPllInvalid ("vco out of range\n"); INTELPllInvalid("vco out of range\n");
/* XXX: We may need to be checking "Dot clock" depending on the multiplier, /* XXX: We may need to be checking "Dot clock" depending on the multiplier,
* connector, etc., rather than just a single range. * connector, etc., rather than just a single range.
*/ */
if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) if (clock->dot < limit->dot.min || limit->dot.max < clock->dot)
INTELPllInvalid ("dot out of range\n"); INTELPllInvalid("dot out of range\n");
return true; return true;
} }
@ -542,7 +543,7 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
clock.p2 = limit->p2.p2_fast; clock.p2 = limit->p2.p2_fast;
} }
memset (best_clock, 0, sizeof (*best_clock)); memset(best_clock, 0, sizeof(*best_clock));
for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
clock.m1++) { clock.m1++) {
@ -802,6 +803,19 @@ static void assert_pch_pll(struct drm_i915_private *dev_priv,
u32 val; u32 val;
bool cur_state; bool cur_state;
if (HAS_PCH_CPT(dev_priv->dev)) {
u32 pch_dpll;
pch_dpll = I915_READ(PCH_DPLL_SEL);
/* Make sure the selected PLL is enabled to the transcoder */
WARN(!((pch_dpll >> (4 * pipe)) & 8),
"transcoder %d PLL not enabled\n", pipe);
/* Convert the transcoder pipe number to a pll pipe number */
pipe = (pch_dpll >> (4 * pipe)) & 1;
}
reg = PCH_DPLL(pipe); reg = PCH_DPLL(pipe);
val = I915_READ(reg); val = I915_READ(reg);
cur_state = !!(val & DPLL_VCO_ENABLE); cur_state = !!(val & DPLL_VCO_ENABLE);
@ -1171,6 +1185,9 @@ static void intel_enable_pch_pll(struct drm_i915_private *dev_priv,
int reg; int reg;
u32 val; u32 val;
if (pipe > 1)
return;
/* PCH only available on ILK+ */ /* PCH only available on ILK+ */
BUG_ON(dev_priv->info->gen < 5); BUG_ON(dev_priv->info->gen < 5);
@ -1191,6 +1208,9 @@ static void intel_disable_pch_pll(struct drm_i915_private *dev_priv,
int reg; int reg;
u32 val; u32 val;
if (pipe > 1)
return;
/* PCH only available on ILK+ */ /* PCH only available on ILK+ */
BUG_ON(dev_priv->info->gen < 5); BUG_ON(dev_priv->info->gen < 5);
@ -1256,7 +1276,7 @@ static void intel_disable_transcoder(struct drm_i915_private *dev_priv,
I915_WRITE(reg, val); I915_WRITE(reg, val);
/* wait for PCH transcoder off, transcoder state */ /* wait for PCH transcoder off, transcoder state */
if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50)) if (wait_for((I915_READ(reg) & TRANS_STATE_ENABLE) == 0, 50))
DRM_ERROR("failed to disable transcoder\n"); DRM_ERROR("failed to disable transcoder %d\n", pipe);
} }
/** /**
@ -2085,6 +2105,7 @@ static int ironlake_update_plane(struct drm_crtc *crtc,
switch (plane) { switch (plane) {
case 0: case 0:
case 1: case 1:
case 2:
break; break;
default: default:
DRM_ERROR("Can't update plane %d in SAREA\n", plane); DRM_ERROR("Can't update plane %d in SAREA\n", plane);
@ -2184,6 +2205,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
case 0: case 0:
case 1: case 1:
break; break;
case 2:
if (IS_IVYBRIDGE(dev))
break;
/* fall through otherwise */
default: default:
DRM_ERROR("no plane for crtc\n"); DRM_ERROR("no plane for crtc\n");
return -EINVAL; return -EINVAL;
@ -2440,7 +2465,7 @@ static void ironlake_fdi_link_train(struct drm_crtc *crtc)
} }
static const int snb_b_fdi_train_param [] = { static const int snb_b_fdi_train_param[] = {
FDI_LINK_TRAIN_400MV_0DB_SNB_B, FDI_LINK_TRAIN_400MV_0DB_SNB_B,
FDI_LINK_TRAIN_400MV_6DB_SNB_B, FDI_LINK_TRAIN_400MV_6DB_SNB_B,
FDI_LINK_TRAIN_600MV_3_5DB_SNB_B, FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
@ -2496,7 +2521,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
if (HAS_PCH_CPT(dev)) if (HAS_PCH_CPT(dev))
cpt_phase_pointer_enable(dev, pipe); cpt_phase_pointer_enable(dev, pipe);
for (i = 0; i < 4; i++ ) { for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe); reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg); temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@ -2545,7 +2570,7 @@ static void gen6_fdi_link_train(struct drm_crtc *crtc)
POSTING_READ(reg); POSTING_READ(reg);
udelay(150); udelay(150);
for (i = 0; i < 4; i++ ) { for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe); reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg); temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@ -2600,6 +2625,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
temp |= FDI_LINK_TRAIN_PATTERN_1_IVB; temp |= FDI_LINK_TRAIN_PATTERN_1_IVB;
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B; temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
temp |= FDI_COMPOSITE_SYNC;
I915_WRITE(reg, temp | FDI_TX_ENABLE); I915_WRITE(reg, temp | FDI_TX_ENABLE);
reg = FDI_RX_CTL(pipe); reg = FDI_RX_CTL(pipe);
@ -2607,6 +2633,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
temp &= ~FDI_LINK_TRAIN_AUTO; temp &= ~FDI_LINK_TRAIN_AUTO;
temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT; temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
temp |= FDI_LINK_TRAIN_PATTERN_1_CPT; temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
temp |= FDI_COMPOSITE_SYNC;
I915_WRITE(reg, temp | FDI_RX_ENABLE); I915_WRITE(reg, temp | FDI_RX_ENABLE);
POSTING_READ(reg); POSTING_READ(reg);
@ -2615,7 +2642,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
if (HAS_PCH_CPT(dev)) if (HAS_PCH_CPT(dev))
cpt_phase_pointer_enable(dev, pipe); cpt_phase_pointer_enable(dev, pipe);
for (i = 0; i < 4; i++ ) { for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe); reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg); temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@ -2657,7 +2684,7 @@ static void ivb_manual_fdi_link_train(struct drm_crtc *crtc)
POSTING_READ(reg); POSTING_READ(reg);
udelay(150); udelay(150);
for (i = 0; i < 4; i++ ) { for (i = 0; i < 4; i++) {
reg = FDI_TX_CTL(pipe); reg = FDI_TX_CTL(pipe);
temp = I915_READ(reg); temp = I915_READ(reg);
temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK; temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
@ -2866,7 +2893,7 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe; int pipe = intel_crtc->pipe;
u32 reg, temp; u32 reg, temp, transc_sel;
/* For PCH output, training FDI link */ /* For PCH output, training FDI link */
dev_priv->display.fdi_link_train(crtc); dev_priv->display.fdi_link_train(crtc);
@ -2874,12 +2901,21 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
intel_enable_pch_pll(dev_priv, pipe); intel_enable_pch_pll(dev_priv, pipe);
if (HAS_PCH_CPT(dev)) { if (HAS_PCH_CPT(dev)) {
transc_sel = intel_crtc->use_pll_a ? TRANSC_DPLLA_SEL :
TRANSC_DPLLB_SEL;
/* Be sure PCH DPLL SEL is set */ /* Be sure PCH DPLL SEL is set */
temp = I915_READ(PCH_DPLL_SEL); temp = I915_READ(PCH_DPLL_SEL);
if (pipe == 0 && (temp & TRANSA_DPLL_ENABLE) == 0) if (pipe == 0) {
temp &= ~(TRANSA_DPLLB_SEL);
temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
else if (pipe == 1 && (temp & TRANSB_DPLL_ENABLE) == 0) } else if (pipe == 1) {
temp &= ~(TRANSB_DPLLB_SEL);
temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
} else if (pipe == 2) {
temp &= ~(TRANSC_DPLLB_SEL);
temp |= (TRANSC_DPLL_ENABLE | transc_sel);
}
I915_WRITE(PCH_DPLL_SEL, temp); I915_WRITE(PCH_DPLL_SEL, temp);
} }
@ -2935,6 +2971,24 @@ static void ironlake_pch_enable(struct drm_crtc *crtc)
intel_enable_transcoder(dev_priv, pipe); intel_enable_transcoder(dev_priv, pipe);
} }
void intel_cpt_verify_modeset(struct drm_device *dev, int pipe)
{
struct drm_i915_private *dev_priv = dev->dev_private;
int dslreg = PIPEDSL(pipe), tc2reg = TRANS_CHICKEN2(pipe);
u32 temp;
temp = I915_READ(dslreg);
udelay(500);
if (wait_for(I915_READ(dslreg) != temp, 5)) {
/* Without this, mode sets may fail silently on FDI */
I915_WRITE(tc2reg, TRANS_AUTOTRAIN_GEN_STALL_DIS);
udelay(250);
I915_WRITE(tc2reg, 0);
if (wait_for(I915_READ(dslreg) != temp, 5))
DRM_ERROR("mode set failed: pipe %d stuck\n", pipe);
}
}
static void ironlake_crtc_enable(struct drm_crtc *crtc) static void ironlake_crtc_enable(struct drm_crtc *crtc)
{ {
struct drm_device *dev = crtc->dev; struct drm_device *dev = crtc->dev;
@ -3045,13 +3099,13 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
temp = I915_READ(PCH_DPLL_SEL); temp = I915_READ(PCH_DPLL_SEL);
switch (pipe) { switch (pipe) {
case 0: case 0:
temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL); temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
break; break;
case 1: case 1:
temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL); temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
break; break;
case 2: case 2:
/* FIXME: manage transcoder PLLs? */ /* C shares PLL A or B */
temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL); temp &= ~(TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL);
break; break;
default: default:
@ -3061,6 +3115,7 @@ static void ironlake_crtc_disable(struct drm_crtc *crtc)
} }
/* disable PCH DPLL */ /* disable PCH DPLL */
if (!intel_crtc->no_pll)
intel_disable_pch_pll(dev_priv, pipe); intel_disable_pch_pll(dev_priv, pipe);
/* Switch from PCDclk to Rawclk */ /* Switch from PCDclk to Rawclk */
@ -3293,18 +3348,25 @@ static void ironlake_crtc_commit(struct drm_crtc *crtc)
ironlake_crtc_enable(crtc); ironlake_crtc_enable(crtc);
} }
void intel_encoder_prepare (struct drm_encoder *encoder) void intel_encoder_prepare(struct drm_encoder *encoder)
{ {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
/* lvds has its own version of prepare see intel_lvds_prepare */ /* lvds has its own version of prepare see intel_lvds_prepare */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF); encoder_funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
} }
void intel_encoder_commit (struct drm_encoder *encoder) void intel_encoder_commit(struct drm_encoder *encoder)
{ {
struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
struct drm_device *dev = encoder->dev;
struct intel_encoder *intel_encoder = to_intel_encoder(encoder);
struct intel_crtc *intel_crtc = to_intel_crtc(intel_encoder->base.crtc);
/* lvds has its own version of commit see intel_lvds_commit */ /* lvds has its own version of commit see intel_lvds_commit */
encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON); encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
if (HAS_PCH_CPT(dev))
intel_cpt_verify_modeset(dev, intel_crtc->pipe);
} }
void intel_encoder_destroy(struct drm_encoder *encoder) void intel_encoder_destroy(struct drm_encoder *encoder)
@ -4478,6 +4540,20 @@ static void sandybridge_update_wm(struct drm_device *dev)
enabled |= 2; enabled |= 2;
} }
/* IVB has 3 pipes */
if (IS_IVYBRIDGE(dev) &&
g4x_compute_wm0(dev, 2,
&sandybridge_display_wm_info, latency,
&sandybridge_cursor_wm_info, latency,
&plane_wm, &cursor_wm)) {
I915_WRITE(WM0_PIPEC_IVB,
(plane_wm << WM0_PIPE_PLANE_SHIFT) | cursor_wm);
DRM_DEBUG_KMS("FIFO watermarks For pipe C -"
" plane %d, cursor: %d\n",
plane_wm, cursor_wm);
enabled |= 3;
}
/* /*
* Calculate and update the self-refresh watermark only when one * Calculate and update the self-refresh watermark only when one
* display plane is used. * display plane is used.
@ -4584,7 +4660,9 @@ static void intel_update_watermarks(struct drm_device *dev)
static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv) static inline bool intel_panel_use_ssc(struct drm_i915_private *dev_priv)
{ {
return dev_priv->lvds_use_ssc && i915_panel_use_ssc if (i915_panel_use_ssc >= 0)
return i915_panel_use_ssc != 0;
return dev_priv->lvds_use_ssc
&& !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE); && !(dev_priv->quirks & QUIRK_LVDS_SSC_DISABLE);
} }
@ -5107,36 +5185,52 @@ static int i9xx_crtc_mode_set(struct drm_crtc *crtc,
return ret; return ret;
} }
static void ironlake_update_pch_refclk(struct drm_device *dev) /*
* Initialize reference clocks when the driver loads
*/
void ironlake_init_pch_refclk(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
struct drm_mode_config *mode_config = &dev->mode_config; struct drm_mode_config *mode_config = &dev->mode_config;
struct drm_crtc *crtc;
struct intel_encoder *encoder; struct intel_encoder *encoder;
struct intel_encoder *has_edp_encoder = NULL;
u32 temp; u32 temp;
bool has_lvds = false; bool has_lvds = false;
bool has_cpu_edp = false;
bool has_pch_edp = false;
bool has_panel = false;
bool has_ck505 = false;
bool can_ssc = false;
/* We need to take the global config into account */ /* We need to take the global config into account */
list_for_each_entry(crtc, &mode_config->crtc_list, head) {
if (!crtc->enabled)
continue;
list_for_each_entry(encoder, &mode_config->encoder_list, list_for_each_entry(encoder, &mode_config->encoder_list,
base.head) { base.head) {
if (encoder->base.crtc != crtc)
continue;
switch (encoder->type) { switch (encoder->type) {
case INTEL_OUTPUT_LVDS: case INTEL_OUTPUT_LVDS:
has_panel = true;
has_lvds = true; has_lvds = true;
break;
case INTEL_OUTPUT_EDP: case INTEL_OUTPUT_EDP:
has_edp_encoder = encoder; has_panel = true;
if (intel_encoder_is_pch_edp(&encoder->base))
has_pch_edp = true;
else
has_cpu_edp = true;
break; break;
} }
} }
if (HAS_PCH_IBX(dev)) {
has_ck505 = dev_priv->display_clock_mode;
can_ssc = has_ck505;
} else {
has_ck505 = false;
can_ssc = true;
} }
DRM_DEBUG_KMS("has_panel %d has_lvds %d has_pch_edp %d has_cpu_edp %d has_ck505 %d\n",
has_panel, has_lvds, has_pch_edp, has_cpu_edp,
has_ck505);
/* Ironlake: try to setup display ref clock before DPLL /* Ironlake: try to setup display ref clock before DPLL
* enabling. This is only under driver's control after * enabling. This is only under driver's control after
* PCH B stepping, previous chipset stepping should be * PCH B stepping, previous chipset stepping should be
@ -5145,43 +5239,102 @@ static void ironlake_update_pch_refclk(struct drm_device *dev)
temp = I915_READ(PCH_DREF_CONTROL); temp = I915_READ(PCH_DREF_CONTROL);
/* Always enable nonspread source */ /* Always enable nonspread source */
temp &= ~DREF_NONSPREAD_SOURCE_MASK; temp &= ~DREF_NONSPREAD_SOURCE_MASK;
if (has_ck505)
temp |= DREF_NONSPREAD_CK505_ENABLE;
else
temp |= DREF_NONSPREAD_SOURCE_ENABLE; temp |= DREF_NONSPREAD_SOURCE_ENABLE;
if (has_panel) {
temp &= ~DREF_SSC_SOURCE_MASK; temp &= ~DREF_SSC_SOURCE_MASK;
temp |= DREF_SSC_SOURCE_ENABLE; temp |= DREF_SSC_SOURCE_ENABLE;
I915_WRITE(PCH_DREF_CONTROL, temp);
POSTING_READ(PCH_DREF_CONTROL); /* SSC must be turned on before enabling the CPU output */
udelay(200); if (intel_panel_use_ssc(dev_priv) && can_ssc) {
DRM_DEBUG_KMS("Using SSC on panel\n");
if (has_edp_encoder) {
if (intel_panel_use_ssc(dev_priv)) {
temp |= DREF_SSC1_ENABLE; temp |= DREF_SSC1_ENABLE;
I915_WRITE(PCH_DREF_CONTROL, temp); }
/* Get SSC going before enabling the outputs */
I915_WRITE(PCH_DREF_CONTROL, temp);
POSTING_READ(PCH_DREF_CONTROL); POSTING_READ(PCH_DREF_CONTROL);
udelay(200); udelay(200);
}
temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK; temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
/* Enable CPU source on CPU attached eDP */ /* Enable CPU source on CPU attached eDP */
if (!intel_encoder_is_pch_edp(&has_edp_encoder->base)) { if (has_cpu_edp) {
if (intel_panel_use_ssc(dev_priv)) if (intel_panel_use_ssc(dev_priv) && can_ssc) {
DRM_DEBUG_KMS("Using SSC on eDP\n");
temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD; temp |= DREF_CPU_SOURCE_OUTPUT_DOWNSPREAD;
}
else else
temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD; temp |= DREF_CPU_SOURCE_OUTPUT_NONSPREAD;
} else
temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
I915_WRITE(PCH_DREF_CONTROL, temp);
POSTING_READ(PCH_DREF_CONTROL);
udelay(200);
} else { } else {
/* Enable SSC on PCH eDP if needed */ DRM_DEBUG_KMS("Disabling SSC entirely\n");
if (intel_panel_use_ssc(dev_priv)) {
DRM_ERROR("enabling SSC on PCH\n"); temp &= ~DREF_CPU_SOURCE_OUTPUT_MASK;
temp |= DREF_SUPERSPREAD_SOURCE_ENABLE;
} /* Turn off CPU output */
} temp |= DREF_CPU_SOURCE_OUTPUT_DISABLE;
I915_WRITE(PCH_DREF_CONTROL, temp);
POSTING_READ(PCH_DREF_CONTROL);
udelay(200);
/* Turn off the SSC source */
temp &= ~DREF_SSC_SOURCE_MASK;
temp |= DREF_SSC_SOURCE_DISABLE;
/* Turn off SSC1 */
temp &= ~ DREF_SSC1_ENABLE;
I915_WRITE(PCH_DREF_CONTROL, temp); I915_WRITE(PCH_DREF_CONTROL, temp);
POSTING_READ(PCH_DREF_CONTROL); POSTING_READ(PCH_DREF_CONTROL);
udelay(200); udelay(200);
} }
} }
static int ironlake_get_refclk(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_encoder *encoder;
struct drm_mode_config *mode_config = &dev->mode_config;
struct intel_encoder *edp_encoder = NULL;
int num_connectors = 0;
bool is_lvds = false;
list_for_each_entry(encoder, &mode_config->encoder_list, base.head) {
if (encoder->base.crtc != crtc)
continue;
switch (encoder->type) {
case INTEL_OUTPUT_LVDS:
is_lvds = true;
break;
case INTEL_OUTPUT_EDP:
edp_encoder = encoder;
break;
}
num_connectors++;
}
if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) {
DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
dev_priv->lvds_ssc_freq);
return dev_priv->lvds_ssc_freq * 1000;
}
return 120000;
}
static int ironlake_crtc_mode_set(struct drm_crtc *crtc, static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, struct drm_display_mode *adjusted_mode,
@ -5241,16 +5394,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
num_connectors++; num_connectors++;
} }
if (is_lvds && intel_panel_use_ssc(dev_priv) && num_connectors < 2) { refclk = ironlake_get_refclk(crtc);
refclk = dev_priv->lvds_ssc_freq * 1000;
DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
refclk / 1000);
} else {
refclk = 96000;
if (!has_edp_encoder ||
intel_encoder_is_pch_edp(&has_edp_encoder->base))
refclk = 120000; /* 120Mhz refclk */
}
/* /*
* Returns a set of divisors for the desired target clock with the given * Returns a set of divisors for the desired target clock with the given
@ -5377,8 +5521,6 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw, ironlake_compute_m_n(intel_crtc->bpp, lane, target_clock, link_bw,
&m_n); &m_n);
ironlake_update_pch_refclk(dev);
fp = clock.n << 16 | clock.m1 << 8 | clock.m2; fp = clock.n << 16 | clock.m1 << 8 | clock.m2;
if (has_reduced_clock) if (has_reduced_clock)
fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 | fp2 = reduced_clock.n << 16 | reduced_clock.m1 << 8 |
@ -5450,39 +5592,32 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
/* Set up the display plane register */ /* Set up the display plane register */
dspcntr = DISPPLANE_GAMMA_ENABLE; dspcntr = DISPPLANE_GAMMA_ENABLE;
DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B'); DRM_DEBUG_KMS("Mode for pipe %d:\n", pipe);
drm_mode_debug_printmodeline(mode); drm_mode_debug_printmodeline(mode);
/* PCH eDP needs FDI, but CPU eDP does not */ /* PCH eDP needs FDI, but CPU eDP does not */
if (!has_edp_encoder || intel_encoder_is_pch_edp(&has_edp_encoder->base)) { if (!intel_crtc->no_pll) {
if (!has_edp_encoder ||
intel_encoder_is_pch_edp(&has_edp_encoder->base)) {
I915_WRITE(PCH_FP0(pipe), fp); I915_WRITE(PCH_FP0(pipe), fp);
I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE); I915_WRITE(PCH_DPLL(pipe), dpll & ~DPLL_VCO_ENABLE);
POSTING_READ(PCH_DPLL(pipe)); POSTING_READ(PCH_DPLL(pipe));
udelay(150); udelay(150);
} }
} else {
/* enable transcoder DPLL */ if (dpll == (I915_READ(PCH_DPLL(0)) & 0x7fffffff) &&
if (HAS_PCH_CPT(dev)) { fp == I915_READ(PCH_FP0(0))) {
temp = I915_READ(PCH_DPLL_SEL); intel_crtc->use_pll_a = true;
switch (pipe) { DRM_DEBUG_KMS("using pipe a dpll\n");
case 0: } else if (dpll == (I915_READ(PCH_DPLL(1)) & 0x7fffffff) &&
temp |= TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL; fp == I915_READ(PCH_FP0(1))) {
break; intel_crtc->use_pll_a = false;
case 1: DRM_DEBUG_KMS("using pipe b dpll\n");
temp |= TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL; } else {
break; DRM_DEBUG_KMS("no matching PLL configuration for pipe 2\n");
case 2: return -EINVAL;
/* FIXME: manage transcoder PLLs? */
temp |= TRANSC_DPLL_ENABLE | TRANSC_DPLLB_SEL;
break;
default:
BUG();
} }
I915_WRITE(PCH_DPLL_SEL, temp);
POSTING_READ(PCH_DPLL_SEL);
udelay(150);
} }
/* The LVDS pin pair needs to be on before the DPLLs are enabled. /* The LVDS pin pair needs to be on before the DPLLs are enabled.
@ -5492,17 +5627,13 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
if (is_lvds) { if (is_lvds) {
temp = I915_READ(PCH_LVDS); temp = I915_READ(PCH_LVDS);
temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; temp |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
if (pipe == 1) {
if (HAS_PCH_CPT(dev)) if (HAS_PCH_CPT(dev))
temp |= PORT_TRANS_B_SEL_CPT; temp |= PORT_TRANS_SEL_CPT(pipe);
else else if (pipe == 1)
temp |= LVDS_PIPEB_SELECT; temp |= LVDS_PIPEB_SELECT;
} else {
if (HAS_PCH_CPT(dev))
temp &= ~PORT_TRANS_SEL_MASK;
else else
temp &= ~LVDS_PIPEB_SELECT; temp &= ~LVDS_PIPEB_SELECT;
}
/* set the corresponsding LVDS_BORDER bit */ /* set the corresponsding LVDS_BORDER bit */
temp |= dev_priv->lvds_border_bits; temp |= dev_priv->lvds_border_bits;
/* Set the B0-B3 data pairs corresponding to whether we're going to /* Set the B0-B3 data pairs corresponding to whether we're going to
@ -5552,8 +5683,9 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
I915_WRITE(TRANSDPLINK_N1(pipe), 0); I915_WRITE(TRANSDPLINK_N1(pipe), 0);
} }
if (!has_edp_encoder || if (!intel_crtc->no_pll &&
intel_encoder_is_pch_edp(&has_edp_encoder->base)) { (!has_edp_encoder ||
intel_encoder_is_pch_edp(&has_edp_encoder->base))) {
I915_WRITE(PCH_DPLL(pipe), dpll); I915_WRITE(PCH_DPLL(pipe), dpll);
/* Wait for the clocks to stabilize. */ /* Wait for the clocks to stabilize. */
@ -5569,6 +5701,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
} }
intel_crtc->lowfreq_avail = false; intel_crtc->lowfreq_avail = false;
if (!intel_crtc->no_pll) {
if (is_lvds && has_reduced_clock && i915_powersave) { if (is_lvds && has_reduced_clock && i915_powersave) {
I915_WRITE(PCH_FP1(pipe), fp2); I915_WRITE(PCH_FP1(pipe), fp2);
intel_crtc->lowfreq_avail = true; intel_crtc->lowfreq_avail = true;
@ -5583,6 +5716,7 @@ static int ironlake_crtc_mode_set(struct drm_crtc *crtc,
pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK; pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
} }
} }
}
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) { if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION; pipeconf |= PIPECONF_INTERLACE_W_FIELD_INDICATION;
@ -5677,6 +5811,131 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
return ret; return ret;
} }
static void g4x_write_eld(struct drm_connector *connector,
struct drm_crtc *crtc)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint8_t *eld = connector->eld;
uint32_t eldv;
uint32_t len;
uint32_t i;
i = I915_READ(G4X_AUD_VID_DID);
if (i == INTEL_AUDIO_DEVBLC || i == INTEL_AUDIO_DEVCL)
eldv = G4X_ELDV_DEVCL_DEVBLC;
else
eldv = G4X_ELDV_DEVCTG;
i = I915_READ(G4X_AUD_CNTL_ST);
i &= ~(eldv | G4X_ELD_ADDR);
len = (i >> 9) & 0x1f; /* ELD buffer size */
I915_WRITE(G4X_AUD_CNTL_ST, i);
if (!eld[0])
return;
len = min_t(uint8_t, eld[2], len);
DRM_DEBUG_DRIVER("ELD size %d\n", len);
for (i = 0; i < len; i++)
I915_WRITE(G4X_HDMIW_HDMIEDID, *((uint32_t *)eld + i));
i = I915_READ(G4X_AUD_CNTL_ST);
i |= eldv;
I915_WRITE(G4X_AUD_CNTL_ST, i);
}
static void ironlake_write_eld(struct drm_connector *connector,
struct drm_crtc *crtc)
{
struct drm_i915_private *dev_priv = connector->dev->dev_private;
uint8_t *eld = connector->eld;
uint32_t eldv;
uint32_t i;
int len;
int hdmiw_hdmiedid;
int aud_cntl_st;
int aud_cntrl_st2;
if (IS_IVYBRIDGE(connector->dev)) {
hdmiw_hdmiedid = GEN7_HDMIW_HDMIEDID_A;
aud_cntl_st = GEN7_AUD_CNTRL_ST_A;
aud_cntrl_st2 = GEN7_AUD_CNTRL_ST2;
} else {
hdmiw_hdmiedid = GEN5_HDMIW_HDMIEDID_A;
aud_cntl_st = GEN5_AUD_CNTL_ST_A;
aud_cntrl_st2 = GEN5_AUD_CNTL_ST2;
}
i = to_intel_crtc(crtc)->pipe;
hdmiw_hdmiedid += i * 0x100;
aud_cntl_st += i * 0x100;
DRM_DEBUG_DRIVER("ELD on pipe %c\n", pipe_name(i));
i = I915_READ(aud_cntl_st);
i = (i >> 29) & 0x3; /* DIP_Port_Select, 0x1 = PortB */
if (!i) {
DRM_DEBUG_DRIVER("Audio directed to unknown port\n");
/* operate blindly on all ports */
eldv = GEN5_ELD_VALIDB;
eldv |= GEN5_ELD_VALIDB << 4;
eldv |= GEN5_ELD_VALIDB << 8;
} else {
DRM_DEBUG_DRIVER("ELD on port %c\n", 'A' + i);
eldv = GEN5_ELD_VALIDB << ((i - 1) * 4);
}
i = I915_READ(aud_cntrl_st2);
i &= ~eldv;
I915_WRITE(aud_cntrl_st2, i);
if (!eld[0])
return;
if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
DRM_DEBUG_DRIVER("ELD: DisplayPort detected\n");
eld[5] |= (1 << 2); /* Conn_Type, 0x1 = DisplayPort */
}
i = I915_READ(aud_cntl_st);
i &= ~GEN5_ELD_ADDRESS;
I915_WRITE(aud_cntl_st, i);
len = min_t(uint8_t, eld[2], 21); /* 84 bytes of hw ELD buffer */
DRM_DEBUG_DRIVER("ELD size %d\n", len);
for (i = 0; i < len; i++)
I915_WRITE(hdmiw_hdmiedid, *((uint32_t *)eld + i));
i = I915_READ(aud_cntrl_st2);
i |= eldv;
I915_WRITE(aud_cntrl_st2, i);
}
void intel_write_eld(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
struct drm_crtc *crtc = encoder->crtc;
struct drm_connector *connector;
struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
connector = drm_select_eld(encoder, mode);
if (!connector)
return;
DRM_DEBUG_DRIVER("ELD on [CONNECTOR:%d:%s], [ENCODER:%d:%s]\n",
connector->base.id,
drm_get_connector_name(connector),
connector->encoder->base.id,
drm_get_encoder_name(connector->encoder));
connector->eld[6] = drm_av_sync_delay(connector, mode) / 2;
if (dev_priv->display.write_eld)
dev_priv->display.write_eld(connector, crtc);
}
/** Loads the palette/gamma unit for the CRTC with the prepared values */ /** Loads the palette/gamma unit for the CRTC with the prepared values */
void intel_crtc_load_lut(struct drm_crtc *crtc) void intel_crtc_load_lut(struct drm_crtc *crtc)
{ {
@ -5758,6 +6017,31 @@ static void i9xx_update_cursor(struct drm_crtc *crtc, u32 base)
I915_WRITE(CURBASE(pipe), base); I915_WRITE(CURBASE(pipe), base);
} }
static void ivb_update_cursor(struct drm_crtc *crtc, u32 base)
{
struct drm_device *dev = crtc->dev;
struct drm_i915_private *dev_priv = dev->dev_private;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
int pipe = intel_crtc->pipe;
bool visible = base != 0;
if (intel_crtc->cursor_visible != visible) {
uint32_t cntl = I915_READ(CURCNTR_IVB(pipe));
if (base) {
cntl &= ~CURSOR_MODE;
cntl |= CURSOR_MODE_64_ARGB_AX | MCURSOR_GAMMA_ENABLE;
} else {
cntl &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
cntl |= CURSOR_MODE_DISABLE;
}
I915_WRITE(CURCNTR_IVB(pipe), cntl);
intel_crtc->cursor_visible = visible;
}
/* and commit changes on next vblank */
I915_WRITE(CURBASE_IVB(pipe), base);
}
/* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */ /* If no-part of the cursor is visible on the framebuffer, then the GPU may hang... */
static void intel_crtc_update_cursor(struct drm_crtc *crtc, static void intel_crtc_update_cursor(struct drm_crtc *crtc,
bool on) bool on)
@ -5805,11 +6089,16 @@ static void intel_crtc_update_cursor(struct drm_crtc *crtc,
if (!visible && !intel_crtc->cursor_visible) if (!visible && !intel_crtc->cursor_visible)
return; return;
if (IS_IVYBRIDGE(dev)) {
I915_WRITE(CURPOS_IVB(pipe), pos);
ivb_update_cursor(crtc, base);
} else {
I915_WRITE(CURPOS(pipe), pos); I915_WRITE(CURPOS(pipe), pos);
if (IS_845G(dev) || IS_I865G(dev)) if (IS_845G(dev) || IS_I865G(dev))
i845_update_cursor(crtc, base); i845_update_cursor(crtc, base);
else else
i9xx_update_cursor(crtc, base); i9xx_update_cursor(crtc, base);
}
if (visible) if (visible)
intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj); intel_mark_busy(dev, to_intel_framebuffer(crtc->fb)->obj);
@ -7071,6 +7360,8 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
intel_crtc->bpp = 24; /* default for pre-Ironlake */ intel_crtc->bpp = 24; /* default for pre-Ironlake */
if (HAS_PCH_SPLIT(dev)) { if (HAS_PCH_SPLIT(dev)) {
if (pipe == 2 && IS_IVYBRIDGE(dev))
intel_crtc->no_pll = true;
intel_helper_funcs.prepare = ironlake_crtc_prepare; intel_helper_funcs.prepare = ironlake_crtc_prepare;
intel_helper_funcs.commit = ironlake_crtc_commit; intel_helper_funcs.commit = ironlake_crtc_commit;
} else { } else {
@ -7250,6 +7541,9 @@ static void intel_setup_outputs(struct drm_device *dev)
/* disable all the possible outputs/crtcs before entering KMS mode */ /* disable all the possible outputs/crtcs before entering KMS mode */
drm_helper_disable_unused_functions(dev); drm_helper_disable_unused_functions(dev);
if (HAS_PCH_SPLIT(dev))
ironlake_init_pch_refclk(dev);
} }
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb) static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
@ -7494,6 +7788,10 @@ void gen6_disable_rps(struct drm_device *dev)
I915_WRITE(GEN6_RPNSWREQ, 1 << 31); I915_WRITE(GEN6_RPNSWREQ, 1 << 31);
I915_WRITE(GEN6_PMINTRMSK, 0xffffffff); I915_WRITE(GEN6_PMINTRMSK, 0xffffffff);
I915_WRITE(GEN6_PMIER, 0); I915_WRITE(GEN6_PMIER, 0);
/* Complete PM interrupt masking here doesn't race with the rps work
* item again unmasking PM interrupts because that is using a different
* register (PMIMR) to mask PM interrupts. The only risk is in leaving
* stale bits in PMIIR and PMIMR which gen6_enable_rps will clean up. */
spin_lock_irq(&dev_priv->rps_lock); spin_lock_irq(&dev_priv->rps_lock);
dev_priv->pm_iir = 0; dev_priv->pm_iir = 0;
@ -8154,7 +8452,7 @@ static void intel_init_display(struct drm_device *dev)
} }
/* Returns the core display clock speed */ /* Returns the core display clock speed */
if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev))) if (IS_I945G(dev) || (IS_G33(dev) && !IS_PINEVIEW_M(dev)))
dev_priv->display.get_display_clock_speed = dev_priv->display.get_display_clock_speed =
i945_get_display_clock_speed; i945_get_display_clock_speed;
else if (IS_I915G(dev)) else if (IS_I915G(dev))
@ -8193,6 +8491,7 @@ static void intel_init_display(struct drm_device *dev)
} }
dev_priv->display.fdi_link_train = ironlake_fdi_link_train; dev_priv->display.fdi_link_train = ironlake_fdi_link_train;
dev_priv->display.init_clock_gating = ironlake_init_clock_gating; dev_priv->display.init_clock_gating = ironlake_init_clock_gating;
dev_priv->display.write_eld = ironlake_write_eld;
} else if (IS_GEN6(dev)) { } else if (IS_GEN6(dev)) {
if (SNB_READ_WM0_LATENCY()) { if (SNB_READ_WM0_LATENCY()) {
dev_priv->display.update_wm = sandybridge_update_wm; dev_priv->display.update_wm = sandybridge_update_wm;
@ -8203,6 +8502,7 @@ static void intel_init_display(struct drm_device *dev)
} }
dev_priv->display.fdi_link_train = gen6_fdi_link_train; dev_priv->display.fdi_link_train = gen6_fdi_link_train;
dev_priv->display.init_clock_gating = gen6_init_clock_gating; dev_priv->display.init_clock_gating = gen6_init_clock_gating;
dev_priv->display.write_eld = ironlake_write_eld;
} else if (IS_IVYBRIDGE(dev)) { } else if (IS_IVYBRIDGE(dev)) {
/* FIXME: detect B0+ stepping and use auto training */ /* FIXME: detect B0+ stepping and use auto training */
dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train; dev_priv->display.fdi_link_train = ivb_manual_fdi_link_train;
@ -8214,7 +8514,7 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.update_wm = NULL; dev_priv->display.update_wm = NULL;
} }
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating; dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
dev_priv->display.write_eld = ironlake_write_eld;
} else } else
dev_priv->display.update_wm = NULL; dev_priv->display.update_wm = NULL;
} else if (IS_PINEVIEW(dev)) { } else if (IS_PINEVIEW(dev)) {
@ -8225,7 +8525,7 @@ static void intel_init_display(struct drm_device *dev)
DRM_INFO("failed to find known CxSR latency " DRM_INFO("failed to find known CxSR latency "
"(found ddr%s fsb freq %d, mem freq %d), " "(found ddr%s fsb freq %d, mem freq %d), "
"disabling CxSR\n", "disabling CxSR\n",
(dev_priv->is_ddr3 == 1) ? "3": "2", (dev_priv->is_ddr3 == 1) ? "3" : "2",
dev_priv->fsb_freq, dev_priv->mem_freq); dev_priv->fsb_freq, dev_priv->mem_freq);
/* Disable CxSR and never update its watermark again */ /* Disable CxSR and never update its watermark again */
pineview_disable_cxsr(dev); pineview_disable_cxsr(dev);
@ -8234,6 +8534,7 @@ static void intel_init_display(struct drm_device *dev)
dev_priv->display.update_wm = pineview_update_wm; dev_priv->display.update_wm = pineview_update_wm;
dev_priv->display.init_clock_gating = gen3_init_clock_gating; dev_priv->display.init_clock_gating = gen3_init_clock_gating;
} else if (IS_G4X(dev)) { } else if (IS_G4X(dev)) {
dev_priv->display.write_eld = g4x_write_eld;
dev_priv->display.update_wm = g4x_update_wm; dev_priv->display.update_wm = g4x_update_wm;
dev_priv->display.init_clock_gating = g4x_init_clock_gating; dev_priv->display.init_clock_gating = g4x_init_clock_gating;
} else if (IS_GEN4(dev)) { } else if (IS_GEN4(dev)) {
@ -8294,7 +8595,7 @@ static void intel_init_display(struct drm_device *dev)
* resume, or other times. This quirk makes sure that's the case for * resume, or other times. This quirk makes sure that's the case for
* affected systems. * affected systems.
*/ */
static void quirk_pipea_force (struct drm_device *dev) static void quirk_pipea_force(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
@ -8322,7 +8623,7 @@ struct intel_quirk intel_quirks[] = {
/* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */ /* HP Compaq 2730p needs pipe A force quirk (LP: #291555) */
{ 0x2a42, 0x103c, 0x30eb, quirk_pipea_force }, { 0x2a42, 0x103c, 0x30eb, quirk_pipea_force },
/* HP Mini needs pipe A force quirk (LP: #322104) */ /* HP Mini needs pipe A force quirk (LP: #322104) */
{ 0x27ae,0x103c, 0x361a, quirk_pipea_force }, { 0x27ae, 0x103c, 0x361a, quirk_pipea_force },
/* Thinkpad R31 needs pipe A force quirk */ /* Thinkpad R31 needs pipe A force quirk */
{ 0x3577, 0x1014, 0x0505, quirk_pipea_force }, { 0x3577, 0x1014, 0x0505, quirk_pipea_force },
@ -8488,6 +8789,7 @@ void intel_modeset_cleanup(struct drm_device *dev)
* enqueue unpin/hotplug work. */ * enqueue unpin/hotplug work. */
drm_irq_uninstall(dev); drm_irq_uninstall(dev);
cancel_work_sync(&dev_priv->hotplug_work); cancel_work_sync(&dev_priv->hotplug_work);
cancel_work_sync(&dev_priv->rps_work);
/* flush any delayed tasks or pending work */ /* flush any delayed tasks or pending work */
flush_scheduled_work(); flush_scheduled_work();
@ -8589,7 +8891,7 @@ intel_display_capture_error_state(struct drm_device *dev)
error->plane[i].control = I915_READ(DSPCNTR(i)); error->plane[i].control = I915_READ(DSPCNTR(i));
error->plane[i].stride = I915_READ(DSPSTRIDE(i)); error->plane[i].stride = I915_READ(DSPSTRIDE(i));
error->plane[i].size = I915_READ(DSPSIZE(i)); error->plane[i].size = I915_READ(DSPSIZE(i));
error->plane[i].pos= I915_READ(DSPPOS(i)); error->plane[i].pos = I915_READ(DSPPOS(i));
error->plane[i].addr = I915_READ(DSPADDR(i)); error->plane[i].addr = I915_READ(DSPADDR(i));
if (INTEL_INFO(dev)->gen >= 4) { if (INTEL_INFO(dev)->gen >= 4) {
error->plane[i].surface = I915_READ(DSPSURF(i)); error->plane[i].surface = I915_READ(DSPSURF(i));

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@
#define _wait_for(COND, MS, W) ({ \ #define _wait_for(COND, MS, W) ({ \
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \ unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
int ret__ = 0; \ int ret__ = 0; \
while (! (COND)) { \ while (!(COND)) { \
if (time_after(jiffies, timeout__)) { \ if (time_after(jiffies, timeout__)) { \
ret__ = -ETIMEDOUT; \ ret__ = -ETIMEDOUT; \
break; \ break; \
@ -52,7 +52,7 @@
mdelay(x); \ mdelay(x); \
else \ else \
msleep(x); \ msleep(x); \
} while(0) } while (0)
#define KHz(x) (1000*x) #define KHz(x) (1000*x)
#define MHz(x) KHz(1000*x) #define MHz(x) KHz(1000*x)
@ -171,6 +171,9 @@ struct intel_crtc {
int16_t cursor_width, cursor_height; int16_t cursor_width, cursor_height;
bool cursor_visible; bool cursor_visible;
unsigned int bpp; unsigned int bpp;
bool no_pll; /* tertiary pipe for IVB */
bool use_pll_a;
}; };
#define to_intel_crtc(x) container_of(x, struct intel_crtc, base) #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
@ -184,7 +187,7 @@ struct intel_crtc {
#define DIP_VERSION_AVI 0x2 #define DIP_VERSION_AVI 0x2
#define DIP_LEN_AVI 13 #define DIP_LEN_AVI 13
#define DIP_TYPE_SPD 0x3 #define DIP_TYPE_SPD 0x83
#define DIP_VERSION_SPD 0x1 #define DIP_VERSION_SPD 0x1
#define DIP_LEN_SPD 25 #define DIP_LEN_SPD 25
#define DIP_SPD_UNKNOWN 0 #define DIP_SPD_UNKNOWN 0
@ -284,7 +287,7 @@ void
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode, intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode); struct drm_display_mode *adjusted_mode);
extern bool intel_dpd_is_edp(struct drm_device *dev); extern bool intel_dpd_is_edp(struct drm_device *dev);
extern void intel_edp_link_config (struct intel_encoder *, int *, int *); extern void intel_edp_link_config(struct intel_encoder *, int *, int *);
extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder); extern bool intel_encoder_is_pch_edp(struct drm_encoder *encoder);
/* intel_panel.c */ /* intel_panel.c */
@ -304,8 +307,8 @@ extern void intel_panel_destroy_backlight(struct drm_device *dev);
extern enum drm_connector_status intel_panel_detect(struct drm_device *dev); extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
extern void intel_crtc_load_lut(struct drm_crtc *crtc); extern void intel_crtc_load_lut(struct drm_crtc *crtc);
extern void intel_encoder_prepare (struct drm_encoder *encoder); extern void intel_encoder_prepare(struct drm_encoder *encoder);
extern void intel_encoder_commit (struct drm_encoder *encoder); extern void intel_encoder_commit(struct drm_encoder *encoder);
extern void intel_encoder_destroy(struct drm_encoder *encoder); extern void intel_encoder_destroy(struct drm_encoder *encoder);
static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector) static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
@ -377,4 +380,8 @@ extern void intel_fb_output_poll_changed(struct drm_device *dev);
extern void intel_fb_restore_mode(struct drm_device *dev); extern void intel_fb_restore_mode(struct drm_device *dev);
extern void intel_init_clock_gating(struct drm_device *dev); extern void intel_init_clock_gating(struct drm_device *dev);
extern void intel_write_eld(struct drm_encoder *encoder,
struct drm_display_mode *mode);
extern void intel_cpt_verify_modeset(struct drm_device *dev, int pipe);
#endif /* __INTEL_DRV_H__ */ #endif /* __INTEL_DRV_H__ */

View file

@ -69,8 +69,7 @@ void intel_dip_infoframe_csum(struct dip_infoframe *frame)
frame->checksum = 0; frame->checksum = 0;
frame->ecc = 0; frame->ecc = 0;
/* Header isn't part of the checksum */ for (i = 0; i < frame->len + DIP_HEADER_SIZE; i++)
for (i = 5; i < frame->len; i++)
sum += data[i]; sum += data[i];
frame->checksum = 0x100 - sum; frame->checksum = 0x100 - sum;
@ -104,7 +103,7 @@ static u32 intel_infoframe_flags(struct dip_infoframe *frame)
flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC; flags |= VIDEO_DIP_ENABLE_AVI | VIDEO_DIP_FREQ_VSYNC;
break; break;
case DIP_TYPE_SPD: case DIP_TYPE_SPD:
flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_2VSYNC; flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC;
break; break;
default: default:
DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type); DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
@ -165,9 +164,9 @@ static void ironlake_write_infoframe(struct drm_encoder *encoder,
flags = intel_infoframe_index(frame); flags = intel_infoframe_index(frame);
val &= ~VIDEO_DIP_SELECT_MASK; val &= ~(VIDEO_DIP_SELECT_MASK | 0xf); /* clear DIP data offset */
I915_WRITE(reg, val | flags); I915_WRITE(reg, VIDEO_DIP_ENABLE | val | flags);
for (i = 0; i < len; i += 4) { for (i = 0; i < len; i += 4) {
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data); I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
@ -245,16 +244,17 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
sdvox |= HDMI_MODE_SELECT; sdvox |= HDMI_MODE_SELECT;
if (intel_hdmi->has_audio) { if (intel_hdmi->has_audio) {
DRM_DEBUG_DRIVER("Enabling HDMI audio on pipe %c\n",
pipe_name(intel_crtc->pipe));
sdvox |= SDVO_AUDIO_ENABLE; sdvox |= SDVO_AUDIO_ENABLE;
sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC; sdvox |= SDVO_NULL_PACKETS_DURING_VSYNC;
intel_write_eld(encoder, adjusted_mode);
} }
if (intel_crtc->pipe == 1) {
if (HAS_PCH_CPT(dev)) if (HAS_PCH_CPT(dev))
sdvox |= PORT_TRANS_B_SEL_CPT; sdvox |= PORT_TRANS_SEL_CPT(intel_crtc->pipe);
else else if (intel_crtc->pipe == 1)
sdvox |= SDVO_PIPE_B_SELECT; sdvox |= SDVO_PIPE_B_SELECT;
}
I915_WRITE(intel_hdmi->sdvox_reg, sdvox); I915_WRITE(intel_hdmi->sdvox_reg, sdvox);
POSTING_READ(intel_hdmi->sdvox_reg); POSTING_READ(intel_hdmi->sdvox_reg);
@ -486,6 +486,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
struct intel_encoder *intel_encoder; struct intel_encoder *intel_encoder;
struct intel_connector *intel_connector; struct intel_connector *intel_connector;
struct intel_hdmi *intel_hdmi; struct intel_hdmi *intel_hdmi;
int i;
intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL); intel_hdmi = kzalloc(sizeof(struct intel_hdmi), GFP_KERNEL);
if (!intel_hdmi) if (!intel_hdmi)
@ -511,7 +512,7 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
connector->polled = DRM_CONNECTOR_POLL_HPD; connector->polled = DRM_CONNECTOR_POLL_HPD;
connector->interlace_allowed = 0; connector->interlace_allowed = 0;
connector->doublescan_allowed = 0; connector->doublescan_allowed = 0;
intel_encoder->crtc_mask = (1 << 0) | (1 << 1); intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
/* Set up the DDC bus. */ /* Set up the DDC bus. */
if (sdvox_reg == SDVOB) { if (sdvox_reg == SDVOB) {
@ -538,10 +539,14 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
intel_hdmi->sdvox_reg = sdvox_reg; intel_hdmi->sdvox_reg = sdvox_reg;
if (!HAS_PCH_SPLIT(dev)) if (!HAS_PCH_SPLIT(dev)) {
intel_hdmi->write_infoframe = i9xx_write_infoframe; intel_hdmi->write_infoframe = i9xx_write_infoframe;
else I915_WRITE(VIDEO_DIP_CTL, 0);
} else {
intel_hdmi->write_infoframe = ironlake_write_infoframe; intel_hdmi->write_infoframe = ironlake_write_infoframe;
for_each_pipe(i)
I915_WRITE(TVIDEO_DIP_CTL(i), 0);
}
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs); drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);

View file

@ -422,13 +422,7 @@ void intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed)
{ {
struct intel_gmbus *bus = to_intel_gmbus(adapter); struct intel_gmbus *bus = to_intel_gmbus(adapter);
/* speed: bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | speed;
* 0x0 = 100 KHz
* 0x1 = 50 KHz
* 0x2 = 400 KHz
* 0x3 = 1000 Khz
*/
bus->reg0 = (bus->reg0 & ~(0x3 << 8)) | (speed << 8);
} }
void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit) void intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit)

View file

@ -888,9 +888,11 @@ bool intel_lvds_init(struct drm_device *dev)
intel_encoder->type = INTEL_OUTPUT_LVDS; intel_encoder->type = INTEL_OUTPUT_LVDS;
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT); intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
if (HAS_PCH_SPLIT(dev))
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
else
intel_encoder->crtc_mask = (1 << 1); intel_encoder->crtc_mask = (1 << 1);
if (INTEL_INFO(dev)->gen >= 5)
intel_encoder->crtc_mask |= (1 << 0);
drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs); drm_encoder_helper_add(encoder, &intel_lvds_helper_funcs);
drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs); drm_connector_helper_add(connector, &intel_lvds_connector_helper_funcs);
connector->display_info.subpixel_order = SubPixelHorizontalRGB; connector->display_info.subpixel_order = SubPixelHorizontalRGB;

View file

@ -26,6 +26,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/fb.h> #include <linux/fb.h>
#include <drm/drm_edid.h>
#include "drmP.h" #include "drmP.h"
#include "intel_drv.h" #include "intel_drv.h"
#include "i915_drv.h" #include "i915_drv.h"
@ -74,6 +75,7 @@ int intel_ddc_get_modes(struct drm_connector *connector,
if (edid) { if (edid) {
drm_mode_connector_update_edid_property(connector, edid); drm_mode_connector_update_edid_property(connector, edid);
ret = drm_add_edid_modes(connector, edid); ret = drm_add_edid_modes(connector, edid);
drm_edid_to_eld(connector, edid);
connector->display_info.raw_edid = NULL; connector->display_info.raw_edid = NULL;
kfree(edid); kfree(edid);
} }

View file

@ -361,7 +361,7 @@ static void intel_didl_outputs(struct drm_device *dev)
list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) { list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
if (i >= 8) { if (i >= 8) {
dev_printk (KERN_ERR, &dev->pdev->dev, dev_printk(KERN_ERR, &dev->pdev->dev,
"More than 8 outputs detected\n"); "More than 8 outputs detected\n");
return; return;
} }
@ -387,7 +387,7 @@ blind_set:
list_for_each_entry(connector, &dev->mode_config.connector_list, head) { list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
int output_type = ACPI_OTHER_OUTPUT; int output_type = ACPI_OTHER_OUTPUT;
if (i >= 8) { if (i >= 8) {
dev_printk (KERN_ERR, &dev->pdev->dev, dev_printk(KERN_ERR, &dev->pdev->dev,
"More than 8 outputs detected\n"); "More than 8 outputs detected\n");
return; return;
} }

View file

@ -264,7 +264,7 @@ i830_activate_pipe_a(struct drm_device *dev)
mode = drm_mode_duplicate(dev, &vesa_640x480); mode = drm_mode_duplicate(dev, &vesa_640x480);
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
if(!drm_crtc_helper_set_mode(&crtc->base, mode, if (!drm_crtc_helper_set_mode(&crtc->base, mode,
crtc->base.x, crtc->base.y, crtc->base.x, crtc->base.y,
crtc->base.fb)) crtc->base.fb))
return 0; return 0;
@ -583,7 +583,7 @@ static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
ret = ((offset + width + mask) >> shift) - (offset >> shift); ret = ((offset + width + mask) >> shift) - (offset >> shift);
if (!IS_GEN2(dev)) if (!IS_GEN2(dev))
ret <<= 1; ret <<= 1;
ret -=1; ret -= 1;
return ret << 2; return ret << 2;
} }
@ -817,7 +817,7 @@ static int intel_overlay_do_put_image(struct intel_overlay *overlay,
regs->SWIDTHSW = calc_swidthsw(overlay->dev, regs->SWIDTHSW = calc_swidthsw(overlay->dev,
params->offset_Y, tmp_width); params->offset_Y, tmp_width);
regs->SHEIGHT = params->src_h; regs->SHEIGHT = params->src_h;
regs->OBUF_0Y = new_bo->gtt_offset + params-> offset_Y; regs->OBUF_0Y = new_bo->gtt_offset + params->offset_Y;
regs->OSTRIDE = params->stride_Y; regs->OSTRIDE = params->stride_Y;
if (params->format & I915_OVERLAY_YUV_PLANAR) { if (params->format & I915_OVERLAY_YUV_PLANAR) {

View file

@ -206,7 +206,7 @@ u32 intel_panel_get_backlight(struct drm_device *dev)
if (IS_PINEVIEW(dev)) if (IS_PINEVIEW(dev))
val >>= 1; val >>= 1;
if (is_backlight_combination_mode(dev)){ if (is_backlight_combination_mode(dev)) {
u8 lbpc; u8 lbpc;
val &= ~1; val &= ~1;
@ -226,7 +226,7 @@ static void intel_pch_panel_set_backlight(struct drm_device *dev, u32 level)
I915_WRITE(BLC_PWM_CPU_CTL, val | level); I915_WRITE(BLC_PWM_CPU_CTL, val | level);
} }
void intel_panel_set_backlight(struct drm_device *dev, u32 level) static void intel_panel_actually_set_backlight(struct drm_device *dev, u32 level)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
u32 tmp; u32 tmp;
@ -236,7 +236,7 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level)
if (HAS_PCH_SPLIT(dev)) if (HAS_PCH_SPLIT(dev))
return intel_pch_panel_set_backlight(dev, level); return intel_pch_panel_set_backlight(dev, level);
if (is_backlight_combination_mode(dev)){ if (is_backlight_combination_mode(dev)) {
u32 max = intel_panel_get_max_backlight(dev); u32 max = intel_panel_get_max_backlight(dev);
u8 lbpc; u8 lbpc;
@ -254,16 +254,21 @@ void intel_panel_set_backlight(struct drm_device *dev, u32 level)
I915_WRITE(BLC_PWM_CTL, tmp | level); I915_WRITE(BLC_PWM_CTL, tmp | level);
} }
void intel_panel_set_backlight(struct drm_device *dev, u32 level)
{
struct drm_i915_private *dev_priv = dev->dev_private;
dev_priv->backlight_level = level;
if (dev_priv->backlight_enabled)
intel_panel_actually_set_backlight(dev, level);
}
void intel_panel_disable_backlight(struct drm_device *dev) void intel_panel_disable_backlight(struct drm_device *dev)
{ {
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
if (dev_priv->backlight_enabled) {
dev_priv->backlight_level = intel_panel_get_backlight(dev);
dev_priv->backlight_enabled = false; dev_priv->backlight_enabled = false;
} intel_panel_actually_set_backlight(dev, 0);
intel_panel_set_backlight(dev, 0);
} }
void intel_panel_enable_backlight(struct drm_device *dev) void intel_panel_enable_backlight(struct drm_device *dev)
@ -273,8 +278,8 @@ void intel_panel_enable_backlight(struct drm_device *dev)
if (dev_priv->backlight_level == 0) if (dev_priv->backlight_level == 0)
dev_priv->backlight_level = intel_panel_get_max_backlight(dev); dev_priv->backlight_level = intel_panel_get_max_backlight(dev);
intel_panel_set_backlight(dev, dev_priv->backlight_level);
dev_priv->backlight_enabled = true; dev_priv->backlight_enabled = true;
intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);
} }
static void intel_panel_init_backlight(struct drm_device *dev) static void intel_panel_init_backlight(struct drm_device *dev)

View file

@ -34,6 +34,16 @@
#include "i915_trace.h" #include "i915_trace.h"
#include "intel_drv.h" #include "intel_drv.h"
/*
* 965+ support PIPE_CONTROL commands, which provide finer grained control
* over cache flushing.
*/
struct pipe_control {
struct drm_i915_gem_object *obj;
volatile u32 *cpu_page;
u32 gtt_offset;
};
static inline int ring_space(struct intel_ring_buffer *ring) static inline int ring_space(struct intel_ring_buffer *ring)
{ {
int space = (ring->head & HEAD_ADDR) - (ring->tail + 8); int space = (ring->head & HEAD_ADDR) - (ring->tail + 8);
@ -123,6 +133,118 @@ render_ring_flush(struct intel_ring_buffer *ring,
return 0; return 0;
} }
/**
* Emits a PIPE_CONTROL with a non-zero post-sync operation, for
* implementing two workarounds on gen6. From section 1.4.7.1
* "PIPE_CONTROL" of the Sandy Bridge PRM volume 2 part 1:
*
* [DevSNB-C+{W/A}] Before any depth stall flush (including those
* produced by non-pipelined state commands), software needs to first
* send a PIPE_CONTROL with no bits set except Post-Sync Operation !=
* 0.
*
* [Dev-SNB{W/A}]: Before a PIPE_CONTROL with Write Cache Flush Enable
* =1, a PIPE_CONTROL with any non-zero post-sync-op is required.
*
* And the workaround for these two requires this workaround first:
*
* [Dev-SNB{W/A}]: Pipe-control with CS-stall bit set must be sent
* BEFORE the pipe-control with a post-sync op and no write-cache
* flushes.
*
* And this last workaround is tricky because of the requirements on
* that bit. From section 1.4.7.2.3 "Stall" of the Sandy Bridge PRM
* volume 2 part 1:
*
* "1 of the following must also be set:
* - Render Target Cache Flush Enable ([12] of DW1)
* - Depth Cache Flush Enable ([0] of DW1)
* - Stall at Pixel Scoreboard ([1] of DW1)
* - Depth Stall ([13] of DW1)
* - Post-Sync Operation ([13] of DW1)
* - Notify Enable ([8] of DW1)"
*
* The cache flushes require the workaround flush that triggered this
* one, so we can't use it. Depth stall would trigger the same.
* Post-sync nonzero is what triggered this second workaround, so we
* can't use that one either. Notify enable is IRQs, which aren't
* really our business. That leaves only stall at scoreboard.
*/
static int
intel_emit_post_sync_nonzero_flush(struct intel_ring_buffer *ring)
{
struct pipe_control *pc = ring->private;
u32 scratch_addr = pc->gtt_offset + 128;
int ret;
ret = intel_ring_begin(ring, 6);
if (ret)
return ret;
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
intel_ring_emit(ring, PIPE_CONTROL_CS_STALL |
PIPE_CONTROL_STALL_AT_SCOREBOARD);
intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */
intel_ring_emit(ring, 0); /* low dword */
intel_ring_emit(ring, 0); /* high dword */
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
ret = intel_ring_begin(ring, 6);
if (ret)
return ret;
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
intel_ring_emit(ring, PIPE_CONTROL_QW_WRITE);
intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT); /* address */
intel_ring_emit(ring, 0);
intel_ring_emit(ring, 0);
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
return 0;
}
static int
gen6_render_ring_flush(struct intel_ring_buffer *ring,
u32 invalidate_domains, u32 flush_domains)
{
u32 flags = 0;
struct pipe_control *pc = ring->private;
u32 scratch_addr = pc->gtt_offset + 128;
int ret;
/* Force SNB workarounds for PIPE_CONTROL flushes */
intel_emit_post_sync_nonzero_flush(ring);
/* Just flush everything. Experiments have shown that reducing the
* number of bits based on the write domains has little performance
* impact.
*/
flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
ret = intel_ring_begin(ring, 6);
if (ret)
return ret;
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(5));
intel_ring_emit(ring, flags);
intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
intel_ring_emit(ring, 0); /* lower dword */
intel_ring_emit(ring, 0); /* uppwer dword */
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
return 0;
}
static void ring_write_tail(struct intel_ring_buffer *ring, static void ring_write_tail(struct intel_ring_buffer *ring,
u32 value) u32 value)
{ {
@ -206,16 +328,6 @@ static int init_ring_common(struct intel_ring_buffer *ring)
return 0; return 0;
} }
/*
* 965+ support PIPE_CONTROL commands, which provide finer grained control
* over cache flushing.
*/
struct pipe_control {
struct drm_i915_gem_object *obj;
volatile u32 *cpu_page;
u32 gtt_offset;
};
static int static int
init_pipe_control(struct intel_ring_buffer *ring) init_pipe_control(struct intel_ring_buffer *ring)
{ {
@ -296,8 +408,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
GFX_MODE_ENABLE(GFX_REPLAY_MODE)); GFX_MODE_ENABLE(GFX_REPLAY_MODE));
} }
if (INTEL_INFO(dev)->gen >= 6) { if (INTEL_INFO(dev)->gen >= 5) {
} else if (IS_GEN5(dev)) {
ret = init_pipe_control(ring); ret = init_pipe_control(ring);
if (ret) if (ret)
return ret; return ret;
@ -315,83 +426,131 @@ static void render_ring_cleanup(struct intel_ring_buffer *ring)
} }
static void static void
update_semaphore(struct intel_ring_buffer *ring, int i, u32 seqno) update_mboxes(struct intel_ring_buffer *ring,
u32 seqno,
u32 mmio_offset)
{ {
struct drm_device *dev = ring->dev; intel_ring_emit(ring, MI_SEMAPHORE_MBOX |
struct drm_i915_private *dev_priv = dev->dev_private; MI_SEMAPHORE_GLOBAL_GTT |
int id;
/*
* cs -> 1 = vcs, 0 = bcs
* vcs -> 1 = bcs, 0 = cs,
* bcs -> 1 = cs, 0 = vcs.
*/
id = ring - dev_priv->ring;
id += 2 - i;
id %= 3;
intel_ring_emit(ring,
MI_SEMAPHORE_MBOX |
MI_SEMAPHORE_REGISTER | MI_SEMAPHORE_REGISTER |
MI_SEMAPHORE_UPDATE); MI_SEMAPHORE_UPDATE);
intel_ring_emit(ring, seqno); intel_ring_emit(ring, seqno);
intel_ring_emit(ring, intel_ring_emit(ring, mmio_offset);
RING_SYNC_0(dev_priv->ring[id].mmio_base) + 4*i);
} }
/**
* gen6_add_request - Update the semaphore mailbox registers
*
* @ring - ring that is adding a request
* @seqno - return seqno stuck into the ring
*
* Update the mailbox registers in the *other* rings with the current seqno.
* This acts like a signal in the canonical semaphore.
*/
static int static int
gen6_add_request(struct intel_ring_buffer *ring, gen6_add_request(struct intel_ring_buffer *ring,
u32 *result) u32 *seqno)
{ {
u32 seqno; u32 mbox1_reg;
u32 mbox2_reg;
int ret; int ret;
ret = intel_ring_begin(ring, 10); ret = intel_ring_begin(ring, 10);
if (ret) if (ret)
return ret; return ret;
seqno = i915_gem_get_seqno(ring->dev); mbox1_reg = ring->signal_mbox[0];
update_semaphore(ring, 0, seqno); mbox2_reg = ring->signal_mbox[1];
update_semaphore(ring, 1, seqno);
*seqno = i915_gem_get_seqno(ring->dev);
update_mboxes(ring, *seqno, mbox1_reg);
update_mboxes(ring, *seqno, mbox2_reg);
intel_ring_emit(ring, MI_STORE_DWORD_INDEX); intel_ring_emit(ring, MI_STORE_DWORD_INDEX);
intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT); intel_ring_emit(ring, I915_GEM_HWS_INDEX << MI_STORE_DWORD_INDEX_SHIFT);
intel_ring_emit(ring, seqno); intel_ring_emit(ring, *seqno);
intel_ring_emit(ring, MI_USER_INTERRUPT); intel_ring_emit(ring, MI_USER_INTERRUPT);
intel_ring_advance(ring); intel_ring_advance(ring);
*result = seqno;
return 0; return 0;
} }
int /**
intel_ring_sync(struct intel_ring_buffer *ring, * intel_ring_sync - sync the waiter to the signaller on seqno
struct intel_ring_buffer *to, *
* @waiter - ring that is waiting
* @signaller - ring which has, or will signal
* @seqno - seqno which the waiter will block on
*/
static int
intel_ring_sync(struct intel_ring_buffer *waiter,
struct intel_ring_buffer *signaller,
int ring,
u32 seqno) u32 seqno)
{ {
int ret; int ret;
u32 dw1 = MI_SEMAPHORE_MBOX |
MI_SEMAPHORE_COMPARE |
MI_SEMAPHORE_REGISTER;
ret = intel_ring_begin(ring, 4); ret = intel_ring_begin(waiter, 4);
if (ret) if (ret)
return ret; return ret;
intel_ring_emit(ring, intel_ring_emit(waiter, dw1 | signaller->semaphore_register[ring]);
MI_SEMAPHORE_MBOX | intel_ring_emit(waiter, seqno);
MI_SEMAPHORE_REGISTER | intel_ring_emit(waiter, 0);
intel_ring_sync_index(ring, to) << 17 | intel_ring_emit(waiter, MI_NOOP);
MI_SEMAPHORE_COMPARE); intel_ring_advance(waiter);
intel_ring_emit(ring, seqno);
intel_ring_emit(ring, 0);
intel_ring_emit(ring, MI_NOOP);
intel_ring_advance(ring);
return 0; return 0;
} }
/* VCS->RCS (RVSYNC) or BCS->RCS (RBSYNC) */
int
render_ring_sync_to(struct intel_ring_buffer *waiter,
struct intel_ring_buffer *signaller,
u32 seqno)
{
WARN_ON(signaller->semaphore_register[RCS] == MI_SEMAPHORE_SYNC_INVALID);
return intel_ring_sync(waiter,
signaller,
RCS,
seqno);
}
/* RCS->VCS (VRSYNC) or BCS->VCS (VBSYNC) */
int
gen6_bsd_ring_sync_to(struct intel_ring_buffer *waiter,
struct intel_ring_buffer *signaller,
u32 seqno)
{
WARN_ON(signaller->semaphore_register[VCS] == MI_SEMAPHORE_SYNC_INVALID);
return intel_ring_sync(waiter,
signaller,
VCS,
seqno);
}
/* RCS->BCS (BRSYNC) or VCS->BCS (BVSYNC) */
int
gen6_blt_ring_sync_to(struct intel_ring_buffer *waiter,
struct intel_ring_buffer *signaller,
u32 seqno)
{
WARN_ON(signaller->semaphore_register[BCS] == MI_SEMAPHORE_SYNC_INVALID);
return intel_ring_sync(waiter,
signaller,
BCS,
seqno);
}
#define PIPE_CONTROL_FLUSH(ring__, addr__) \ #define PIPE_CONTROL_FLUSH(ring__, addr__) \
do { \ do { \
intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | \ intel_ring_emit(ring__, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE | \
PIPE_CONTROL_DEPTH_STALL | 2); \ PIPE_CONTROL_DEPTH_STALL); \
intel_ring_emit(ring__, (addr__) | PIPE_CONTROL_GLOBAL_GTT); \ intel_ring_emit(ring__, (addr__) | PIPE_CONTROL_GLOBAL_GTT); \
intel_ring_emit(ring__, 0); \ intel_ring_emit(ring__, 0); \
intel_ring_emit(ring__, 0); \ intel_ring_emit(ring__, 0); \
@ -419,8 +578,9 @@ pc_render_add_request(struct intel_ring_buffer *ring,
if (ret) if (ret)
return ret; return ret;
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH); PIPE_CONTROL_WRITE_FLUSH |
PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE);
intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT);
intel_ring_emit(ring, seqno); intel_ring_emit(ring, seqno);
intel_ring_emit(ring, 0); intel_ring_emit(ring, 0);
@ -435,8 +595,9 @@ pc_render_add_request(struct intel_ring_buffer *ring,
PIPE_CONTROL_FLUSH(ring, scratch_addr); PIPE_CONTROL_FLUSH(ring, scratch_addr);
scratch_addr += 128; scratch_addr += 128;
PIPE_CONTROL_FLUSH(ring, scratch_addr); PIPE_CONTROL_FLUSH(ring, scratch_addr);
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL | PIPE_CONTROL_QW_WRITE | intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4) | PIPE_CONTROL_QW_WRITE |
PIPE_CONTROL_WC_FLUSH | PIPE_CONTROL_TC_FLUSH | PIPE_CONTROL_WRITE_FLUSH |
PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE |
PIPE_CONTROL_NOTIFY); PIPE_CONTROL_NOTIFY);
intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT); intel_ring_emit(ring, pc->gtt_offset | PIPE_CONTROL_GLOBAL_GTT);
intel_ring_emit(ring, seqno); intel_ring_emit(ring, seqno);
@ -1027,6 +1188,11 @@ static const struct intel_ring_buffer render_ring = {
.irq_put = render_ring_put_irq, .irq_put = render_ring_put_irq,
.dispatch_execbuffer = render_ring_dispatch_execbuffer, .dispatch_execbuffer = render_ring_dispatch_execbuffer,
.cleanup = render_ring_cleanup, .cleanup = render_ring_cleanup,
.sync_to = render_ring_sync_to,
.semaphore_register = {MI_SEMAPHORE_SYNC_INVALID,
MI_SEMAPHORE_SYNC_RV,
MI_SEMAPHORE_SYNC_RB},
.signal_mbox = {GEN6_VRSYNC, GEN6_BRSYNC},
}; };
/* ring buffer for bit-stream decoder */ /* ring buffer for bit-stream decoder */
@ -1154,6 +1320,11 @@ static const struct intel_ring_buffer gen6_bsd_ring = {
.irq_get = gen6_bsd_ring_get_irq, .irq_get = gen6_bsd_ring_get_irq,
.irq_put = gen6_bsd_ring_put_irq, .irq_put = gen6_bsd_ring_put_irq,
.dispatch_execbuffer = gen6_ring_dispatch_execbuffer, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer,
.sync_to = gen6_bsd_ring_sync_to,
.semaphore_register = {MI_SEMAPHORE_SYNC_VR,
MI_SEMAPHORE_SYNC_INVALID,
MI_SEMAPHORE_SYNC_VB},
.signal_mbox = {GEN6_RVSYNC, GEN6_BVSYNC},
}; };
/* Blitter support (SandyBridge+) */ /* Blitter support (SandyBridge+) */
@ -1285,6 +1456,11 @@ static const struct intel_ring_buffer gen6_blt_ring = {
.irq_put = blt_ring_put_irq, .irq_put = blt_ring_put_irq,
.dispatch_execbuffer = gen6_ring_dispatch_execbuffer, .dispatch_execbuffer = gen6_ring_dispatch_execbuffer,
.cleanup = blt_ring_cleanup, .cleanup = blt_ring_cleanup,
.sync_to = gen6_blt_ring_sync_to,
.semaphore_register = {MI_SEMAPHORE_SYNC_BR,
MI_SEMAPHORE_SYNC_BV,
MI_SEMAPHORE_SYNC_INVALID},
.signal_mbox = {GEN6_RBSYNC, GEN6_VBSYNC},
}; };
int intel_init_render_ring_buffer(struct drm_device *dev) int intel_init_render_ring_buffer(struct drm_device *dev)
@ -1295,6 +1471,7 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
*ring = render_ring; *ring = render_ring;
if (INTEL_INFO(dev)->gen >= 6) { if (INTEL_INFO(dev)->gen >= 6) {
ring->add_request = gen6_add_request; ring->add_request = gen6_add_request;
ring->flush = gen6_render_ring_flush;
ring->irq_get = gen6_render_ring_get_irq; ring->irq_get = gen6_render_ring_get_irq;
ring->irq_put = gen6_render_ring_put_irq; ring->irq_put = gen6_render_ring_put_irq;
} else if (IS_GEN5(dev)) { } else if (IS_GEN5(dev)) {

View file

@ -75,7 +75,12 @@ struct intel_ring_buffer {
int (*dispatch_execbuffer)(struct intel_ring_buffer *ring, int (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
u32 offset, u32 length); u32 offset, u32 length);
void (*cleanup)(struct intel_ring_buffer *ring); void (*cleanup)(struct intel_ring_buffer *ring);
int (*sync_to)(struct intel_ring_buffer *ring,
struct intel_ring_buffer *to,
u32 seqno);
u32 semaphore_register[3]; /*our mbox written by others */
u32 signal_mbox[2]; /* mboxes this ring signals to */
/** /**
* List of objects currently involved in rendering from the * List of objects currently involved in rendering from the
* ringbuffer. * ringbuffer.
@ -180,9 +185,6 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring,
void intel_ring_advance(struct intel_ring_buffer *ring); void intel_ring_advance(struct intel_ring_buffer *ring);
u32 intel_ring_get_seqno(struct intel_ring_buffer *ring); u32 intel_ring_get_seqno(struct intel_ring_buffer *ring);
int intel_ring_sync(struct intel_ring_buffer *ring,
struct intel_ring_buffer *to,
u32 seqno);
int intel_init_render_ring_buffer(struct drm_device *dev); int intel_init_render_ring_buffer(struct drm_device *dev);
int intel_init_bsd_ring_buffer(struct drm_device *dev); int intel_init_bsd_ring_buffer(struct drm_device *dev);

View file

@ -1232,8 +1232,7 @@ static bool
intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo) intel_sdvo_multifunc_encoder(struct intel_sdvo *intel_sdvo)
{ {
/* Is there more than one type of output? */ /* Is there more than one type of output? */
int caps = intel_sdvo->caps.output_flags & 0xf; return hweight16(intel_sdvo->caps.output_flags) > 1;
return caps & -caps;
} }
static struct edid * static struct edid *
@ -1254,7 +1253,7 @@ intel_sdvo_get_analog_edid(struct drm_connector *connector)
} }
enum drm_connector_status enum drm_connector_status
intel_sdvo_hdmi_sink_detect(struct drm_connector *connector) intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
{ {
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector); struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
enum drm_connector_status status; enum drm_connector_status status;
@ -1349,7 +1348,7 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
if ((intel_sdvo_connector->output_flag & response) == 0) if ((intel_sdvo_connector->output_flag & response) == 0)
ret = connector_status_disconnected; ret = connector_status_disconnected;
else if (IS_TMDS(intel_sdvo_connector)) else if (IS_TMDS(intel_sdvo_connector))
ret = intel_sdvo_hdmi_sink_detect(connector); ret = intel_sdvo_tmds_sink_detect(connector);
else { else {
struct edid *edid; struct edid *edid;
@ -1896,7 +1895,7 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
struct intel_sdvo *sdvo, u32 reg) struct intel_sdvo *sdvo, u32 reg)
{ {
struct sdvo_device_mapping *mapping; struct sdvo_device_mapping *mapping;
u8 pin, speed; u8 pin;
if (IS_SDVOB(reg)) if (IS_SDVOB(reg))
mapping = &dev_priv->sdvo_mappings[0]; mapping = &dev_priv->sdvo_mappings[0];
@ -1904,18 +1903,16 @@ intel_sdvo_select_i2c_bus(struct drm_i915_private *dev_priv,
mapping = &dev_priv->sdvo_mappings[1]; mapping = &dev_priv->sdvo_mappings[1];
pin = GMBUS_PORT_DPB; pin = GMBUS_PORT_DPB;
speed = GMBUS_RATE_1MHZ >> 8; if (mapping->initialized)
if (mapping->initialized) {
pin = mapping->i2c_pin; pin = mapping->i2c_pin;
speed = mapping->i2c_speed;
}
if (pin < GMBUS_NUM_PORTS) { if (pin < GMBUS_NUM_PORTS) {
sdvo->i2c = &dev_priv->gmbus[pin].adapter; sdvo->i2c = &dev_priv->gmbus[pin].adapter;
intel_gmbus_set_speed(sdvo->i2c, speed); intel_gmbus_set_speed(sdvo->i2c, GMBUS_RATE_1MHZ);
intel_gmbus_force_bit(sdvo->i2c, true); intel_gmbus_force_bit(sdvo->i2c, true);
} else } else {
sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter; sdvo->i2c = &dev_priv->gmbus[GMBUS_PORT_DPB].adapter;
}
} }
static bool static bool
@ -2206,7 +2203,7 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo, uint16_t flags)
bytes[0], bytes[1]); bytes[0], bytes[1]);
return false; return false;
} }
intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1); intel_sdvo->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
return true; return true;
} }
@ -2275,7 +2272,7 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo,
DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \ DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \
data_value[0], data_value[1], response); \ data_value[0], data_value[1], response); \
} \ } \
} while(0) } while (0)
static bool static bool
intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo,
@ -2442,7 +2439,7 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
if (IS_TV(intel_sdvo_connector)) if (IS_TV(intel_sdvo_connector))
return intel_sdvo_create_enhance_property_tv(intel_sdvo, intel_sdvo_connector, enhancements.reply); return intel_sdvo_create_enhance_property_tv(intel_sdvo, intel_sdvo_connector, enhancements.reply);
else if(IS_LVDS(intel_sdvo_connector)) else if (IS_LVDS(intel_sdvo_connector))
return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply); return intel_sdvo_create_enhance_property_lvds(intel_sdvo, intel_sdvo_connector, enhancements.reply);
else else
return true; return true;

View file

@ -666,7 +666,7 @@ struct intel_sdvo_enhancement_limits_reply {
#define SDVO_CMD_SET_TV_LUMA_FILTER 0x79 #define SDVO_CMD_SET_TV_LUMA_FILTER 0x79
struct intel_sdvo_enhancements_arg { struct intel_sdvo_enhancements_arg {
u16 value; u16 value;
}__attribute__((packed)); } __attribute__((packed));
#define SDVO_CMD_GET_DOT_CRAWL 0x70 #define SDVO_CMD_GET_DOT_CRAWL 0x70
#define SDVO_CMD_SET_DOT_CRAWL 0x71 #define SDVO_CMD_SET_DOT_CRAWL 0x71
@ -717,7 +717,7 @@ struct intel_sdvo_enhancements_arg {
#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c #define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c
#define SDVO_NEED_TO_STALL (1 << 7) #define SDVO_NEED_TO_STALL (1 << 7)
struct intel_sdvo_encode{ struct intel_sdvo_encode {
u8 dvi_rev; u8 dvi_rev;
u8 hdmi_rev; u8 hdmi_rev;
} __attribute__ ((packed)); } __attribute__ ((packed));

View file

@ -683,7 +683,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 64, .hblank_end = 122, .hsync_end = 64, .hblank_end = 122,
.hblank_start = 842, .htotal = 857, .hblank_start = 842, .htotal = 857,
.progressive = true,.trilevel_sync = false, .progressive = true, .trilevel_sync = false,
.vsync_start_f1 = 12, .vsync_start_f2 = 12, .vsync_start_f1 = 12, .vsync_start_f2 = 12,
.vsync_len = 12, .vsync_len = 12,
@ -707,7 +707,7 @@ static const struct tv_mode tv_modes[] = {
.hsync_end = 64, .hblank_end = 122, .hsync_end = 64, .hblank_end = 122,
.hblank_start = 842, .htotal = 856, .hblank_start = 842, .htotal = 856,
.progressive = true,.trilevel_sync = false, .progressive = true, .trilevel_sync = false,
.vsync_start_f1 = 12, .vsync_start_f2 = 12, .vsync_start_f1 = 12, .vsync_start_f2 = 12,
.vsync_len = 12, .vsync_len = 12,
@ -916,7 +916,7 @@ intel_tv_dpms(struct drm_encoder *encoder, int mode)
struct drm_device *dev = encoder->dev; struct drm_device *dev = encoder->dev;
struct drm_i915_private *dev_priv = dev->dev_private; struct drm_i915_private *dev_priv = dev->dev_private;
switch(mode) { switch (mode) {
case DRM_MODE_DPMS_ON: case DRM_MODE_DPMS_ON:
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE); I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
break; break;
@ -933,7 +933,7 @@ intel_tv_mode_lookup(const char *tv_format)
{ {
int i; int i;
for (i = 0; i < sizeof(tv_modes) / sizeof (tv_modes[0]); i++) { for (i = 0; i < sizeof(tv_modes) / sizeof(tv_modes[0]); i++) {
const struct tv_mode *tv_mode = &tv_modes[i]; const struct tv_mode *tv_mode = &tv_modes[i];
if (!strcmp(tv_format, tv_mode->name)) if (!strcmp(tv_format, tv_mode->name))
@ -1128,7 +1128,7 @@ intel_tv_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
if (color_conversion) { if (color_conversion) {
I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) | I915_WRITE(TV_CSC_Y, (color_conversion->ry << 16) |
color_conversion->gy); color_conversion->gy);
I915_WRITE(TV_CSC_Y2,(color_conversion->by << 16) | I915_WRITE(TV_CSC_Y2, (color_conversion->by << 16) |
color_conversion->ay); color_conversion->ay);
I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) | I915_WRITE(TV_CSC_U, (color_conversion->ru << 16) |
color_conversion->gu); color_conversion->gu);
@ -1232,7 +1232,7 @@ static const struct drm_display_mode reported_modes[] = {
* \return false if TV is disconnected. * \return false if TV is disconnected.
*/ */
static int static int
intel_tv_detect_type (struct intel_tv *intel_tv, intel_tv_detect_type(struct intel_tv *intel_tv,
struct drm_connector *connector) struct drm_connector *connector)
{ {
struct drm_encoder *encoder = &intel_tv->base.base; struct drm_encoder *encoder = &intel_tv->base.base;
@ -1486,7 +1486,7 @@ intel_tv_get_modes(struct drm_connector *connector)
} }
static void static void
intel_tv_destroy (struct drm_connector *connector) intel_tv_destroy(struct drm_connector *connector)
{ {
drm_sysfs_connector_remove(connector); drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);

View file

@ -21,16 +21,17 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nv40_grctx.o nv50_grctx.o nvc0_grctx.o \ nv40_grctx.o nv50_grctx.o nvc0_grctx.o \
nv84_crypt.o \ nv84_crypt.o \
nva3_copy.o nvc0_copy.o \ nva3_copy.o nvc0_copy.o \
nv40_mpeg.o nv50_mpeg.o \ nv31_mpeg.o nv50_mpeg.o \
nv04_instmem.o nv50_instmem.o nvc0_instmem.o \ nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
nv04_crtc.o nv04_display.o nv04_cursor.o \ nv04_crtc.o nv04_display.o nv04_cursor.o \
nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o \
nvd0_display.o \
nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \ nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \
nv10_gpio.o nv50_gpio.o \ nv10_gpio.o nv50_gpio.o \
nv50_calc.o \ nv50_calc.o \
nv04_pm.o nv50_pm.o nva3_pm.o \ nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \
nv50_vram.o nvc0_vram.o \ nv50_vram.o nvc0_vram.o \
nv50_vm.o nvc0_vm.o nv50_vm.o nvc0_vm.o

View file

@ -37,8 +37,10 @@
#include "nouveau_drv.h" #include "nouveau_drv.h"
#include "nouveau_drm.h" #include "nouveau_drm.h"
#include "nouveau_reg.h" #include "nouveau_reg.h"
#include "nouveau_encoder.h"
static int nv40_get_intensity(struct backlight_device *bd) static int
nv40_get_intensity(struct backlight_device *bd)
{ {
struct drm_device *dev = bl_get_data(bd); struct drm_device *dev = bl_get_data(bd);
int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK) int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
@ -47,7 +49,8 @@ static int nv40_get_intensity(struct backlight_device *bd)
return val; return val;
} }
static int nv40_set_intensity(struct backlight_device *bd) static int
nv40_set_intensity(struct backlight_device *bd)
{ {
struct drm_device *dev = bl_get_data(bd); struct drm_device *dev = bl_get_data(bd);
int val = bd->props.brightness; int val = bd->props.brightness;
@ -65,30 +68,8 @@ static const struct backlight_ops nv40_bl_ops = {
.update_status = nv40_set_intensity, .update_status = nv40_set_intensity,
}; };
static int nv50_get_intensity(struct backlight_device *bd) static int
{ nv40_backlight_init(struct drm_connector *connector)
struct drm_device *dev = bl_get_data(bd);
return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
}
static int nv50_set_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
int val = bd->props.brightness;
nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
return 0;
}
static const struct backlight_ops nv50_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nv50_get_intensity,
.update_status = nv50_set_intensity,
};
static int nouveau_nv40_backlight_init(struct drm_connector *connector)
{ {
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
@ -113,34 +94,129 @@ static int nouveau_nv40_backlight_init(struct drm_connector *connector)
return 0; return 0;
} }
static int nouveau_nv50_backlight_init(struct drm_connector *connector) static int
nv50_get_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct drm_device *dev = nv_encoder->base.base.dev;
int or = nv_encoder->or;
u32 div = 1025;
u32 val;
val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or));
val &= NV50_PDISP_SOR_PWM_CTL_VAL;
return ((val * 100) + (div / 2)) / div;
}
static int
nv50_set_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct drm_device *dev = nv_encoder->base.base.dev;
int or = nv_encoder->or;
u32 div = 1025;
u32 val = (bd->props.brightness * div) / 100;
nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or),
NV50_PDISP_SOR_PWM_CTL_NEW | val);
return 0;
}
static const struct backlight_ops nv50_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nv50_get_intensity,
.update_status = nv50_set_intensity,
};
static int
nva3_get_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct drm_device *dev = nv_encoder->base.base.dev;
int or = nv_encoder->or;
u32 div, val;
div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or));
val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or));
val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
if (div && div >= val)
return ((val * 100) + (div / 2)) / div;
return 100;
}
static int
nva3_set_intensity(struct backlight_device *bd)
{
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
struct drm_device *dev = nv_encoder->base.base.dev;
int or = nv_encoder->or;
u32 div, val;
div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or));
val = (bd->props.brightness * div) / 100;
if (div) {
nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), val |
NV50_PDISP_SOR_PWM_CTL_NEW |
NVA3_PDISP_SOR_PWM_CTL_UNK);
return 0;
}
return -EINVAL;
}
static const struct backlight_ops nva3_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nva3_get_intensity,
.update_status = nva3_set_intensity,
};
static int
nv50_backlight_init(struct drm_connector *connector)
{ {
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_encoder *nv_encoder;
struct backlight_properties props; struct backlight_properties props;
struct backlight_device *bd; struct backlight_device *bd;
const struct backlight_ops *ops;
if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT)) nv_encoder = find_encoder(connector, OUTPUT_LVDS);
if (!nv_encoder) {
nv_encoder = find_encoder(connector, OUTPUT_DP);
if (!nv_encoder)
return -ENODEV;
}
if (!nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
return 0; return 0;
if (dev_priv->chipset <= 0xa0 ||
dev_priv->chipset == 0xaa ||
dev_priv->chipset == 0xac)
ops = &nv50_bl_ops;
else
ops = &nva3_bl_ops;
memset(&props, 0, sizeof(struct backlight_properties)); memset(&props, 0, sizeof(struct backlight_properties));
props.type = BACKLIGHT_RAW; props.type = BACKLIGHT_RAW;
props.max_brightness = 1025; props.max_brightness = 100;
bd = backlight_device_register("nv_backlight", &connector->kdev, dev, bd = backlight_device_register("nv_backlight", &connector->kdev,
&nv50_bl_ops, &props); nv_encoder, ops, &props);
if (IS_ERR(bd)) if (IS_ERR(bd))
return PTR_ERR(bd); return PTR_ERR(bd);
dev_priv->backlight = bd; dev_priv->backlight = bd;
bd->props.brightness = nv50_get_intensity(bd); bd->props.brightness = bd->ops->get_brightness(bd);
backlight_update_status(bd); backlight_update_status(bd);
return 0; return 0;
} }
int nouveau_backlight_init(struct drm_connector *connector) int
nouveau_backlight_init(struct drm_device *dev)
{ {
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_connector *connector;
#ifdef CONFIG_ACPI #ifdef CONFIG_ACPI
if (acpi_video_backlight_support()) { if (acpi_video_backlight_support()) {
@ -150,21 +226,28 @@ int nouveau_backlight_init(struct drm_connector *connector)
} }
#endif #endif
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
connector->connector_type != DRM_MODE_CONNECTOR_eDP)
continue;
switch (dev_priv->card_type) { switch (dev_priv->card_type) {
case NV_40: case NV_40:
return nouveau_nv40_backlight_init(connector); return nv40_backlight_init(connector);
case NV_50: case NV_50:
return nouveau_nv50_backlight_init(connector); return nv50_backlight_init(connector);
default: default:
break; break;
} }
}
return 0; return 0;
} }
void nouveau_backlight_exit(struct drm_connector *connector) void
nouveau_backlight_exit(struct drm_device *dev)
{ {
struct drm_device *dev = connector->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->backlight) { if (dev_priv->backlight) {

View file

@ -296,6 +296,11 @@ munge_reg(struct nvbios *bios, uint32_t reg)
if (dev_priv->card_type < NV_50) if (dev_priv->card_type < NV_50)
return reg; return reg;
if (reg & 0x80000000) {
BUG_ON(bios->display.crtc < 0);
reg += bios->display.crtc * 0x800;
}
if (reg & 0x40000000) { if (reg & 0x40000000) {
BUG_ON(!dcbent); BUG_ON(!dcbent);
@ -304,7 +309,7 @@ munge_reg(struct nvbios *bios, uint32_t reg)
reg += 0x00000080; reg += 0x00000080;
} }
reg &= ~0x60000000; reg &= ~0xe0000000;
return reg; return reg;
} }
@ -1174,22 +1179,19 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
* *
*/ */
struct bit_displayport_encoder_table *dpe = NULL;
struct dcb_entry *dcb = bios->display.output; struct dcb_entry *dcb = bios->display.output;
struct drm_device *dev = bios->dev; struct drm_device *dev = bios->dev;
uint8_t cond = bios->data[offset + 1]; uint8_t cond = bios->data[offset + 1];
int dummy; uint8_t *table, *entry;
BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond); BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond);
if (!iexec->execute) if (!iexec->execute)
return 3; return 3;
dpe = nouveau_bios_dp_table(dev, dcb, &dummy); table = nouveau_dp_bios_data(dev, dcb, &entry);
if (!dpe) { if (!table)
NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
return 3; return 3;
}
switch (cond) { switch (cond) {
case 0: case 0:
@ -1203,7 +1205,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
break; break;
case 1: case 1:
case 2: case 2:
if (!(dpe->unknown & cond)) if (!(entry[5] & cond))
iexec->execute = false; iexec->execute = false;
break; break;
case 5: case 5:
@ -3221,45 +3223,12 @@ init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
return 1; return 1;
} }
static int static void
init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec) init_gpio_unknv50(struct nvbios *bios, struct dcb_gpio_entry *gpio)
{ {
/*
* INIT_GPIO opcode: 0x8E ('')
*
* offset (8 bit): opcode
*
* Loop over all entries in the DCB GPIO table, and initialise
* each GPIO according to various values listed in each entry
*/
struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c }; const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
int i; u32 r, s, v;
if (dev_priv->card_type < NV_50) {
NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
return 1;
}
if (!iexec->execute)
return 1;
for (i = 0; i < bios->dcb.gpio.entries; i++) {
struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i];
uint32_t r, s, v;
BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry);
BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n",
offset, gpio->tag, gpio->state_default);
if (bios->execute)
pgpio->set(bios->dev, gpio->tag, gpio->state_default);
/* The NVIDIA binary driver doesn't appear to actually do
* any of this, my VBIOS does however.
*/
/* Not a clue, needs de-magicing */ /* Not a clue, needs de-magicing */
r = nv50_gpio_ctl[gpio->line >> 4]; r = nv50_gpio_ctl[gpio->line >> 4];
s = (gpio->line & 0x0f); s = (gpio->line & 0x0f);
@ -3274,7 +3243,69 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
default: default:
break; break;
} }
bios_wr32(bios, r, v); bios_wr32(bios, r, v);
}
static void
init_gpio_unknvd0(struct nvbios *bios, struct dcb_gpio_entry *gpio)
{
u32 v, i;
v = bios_rd32(bios, 0x00d610 + (gpio->line * 4));
v &= 0xffffff00;
v |= (gpio->entry & 0x00ff0000) >> 16;
bios_wr32(bios, 0x00d610 + (gpio->line * 4), v);
i = (gpio->entry & 0x1f000000) >> 24;
if (i) {
v = bios_rd32(bios, 0x00d640 + ((i - 1) * 4));
v &= 0xffffff00;
v |= gpio->line;
bios_wr32(bios, 0x00d640 + ((i - 1) * 4), v);
}
}
static int
init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
{
/*
* INIT_GPIO opcode: 0x8E ('')
*
* offset (8 bit): opcode
*
* Loop over all entries in the DCB GPIO table, and initialise
* each GPIO according to various values listed in each entry
*/
struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
int i;
if (dev_priv->card_type < NV_50) {
NV_ERROR(bios->dev, "INIT_GPIO on unsupported chipset\n");
return 1;
}
if (!iexec->execute)
return 1;
for (i = 0; i < bios->dcb.gpio.entries; i++) {
struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i];
BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry);
BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n",
offset, gpio->tag, gpio->state_default);
if (!bios->execute)
continue;
pgpio->set(bios->dev, gpio->tag, gpio->state_default);
if (dev_priv->card_type < NV_D0)
init_gpio_unknv50(bios, gpio);
else
init_gpio_unknvd0(bios, gpio);
} }
return 1; return 1;
@ -3737,6 +3768,10 @@ parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
int count = 0, i, ret; int count = 0, i, ret;
uint8_t id; uint8_t id;
/* catch NULL script pointers */
if (offset == 0)
return 0;
/* /*
* Loop until INIT_DONE causes us to break out of the loop * Loop until INIT_DONE causes us to break out of the loop
* (or until offset > bios length just in case... ) * (or until offset > bios length just in case... )
@ -4389,86 +4424,37 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
return 0; return 0;
} }
static uint8_t * /* BIT 'U'/'d' table encoder subtables have hashes matching them to
bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent, * a particular set of encoders.
uint16_t record, int record_len, int record_nr, *
bool match_link) * This function returns true if a particular DCB entry matches.
*/
bool
bios_encoder_match(struct dcb_entry *dcb, u32 hash)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; if ((hash & 0x000000f0) != (dcb->location << 4))
struct nvbios *bios = &dev_priv->vbios; return false;
uint32_t entry; if ((hash & 0x0000000f) != dcb->type)
uint16_t table; return false;
int i, v; if (!(hash & (dcb->or << 16)))
return false;
switch (dcbent->type) { switch (dcb->type) {
case OUTPUT_TMDS: case OUTPUT_TMDS:
case OUTPUT_LVDS: case OUTPUT_LVDS:
case OUTPUT_DP: case OUTPUT_DP:
break; if (hash & 0x00c00000) {
if (!(hash & (dcb->sorconf.link << 22)))
return false;
}
default: default:
match_link = false; return true;
break;
} }
for (i = 0; i < record_nr; i++, record += record_len) {
table = ROM16(bios->data[record]);
if (!table)
continue;
entry = ROM32(bios->data[table]);
if (match_link) {
v = (entry & 0x00c00000) >> 22;
if (!(v & dcbent->sorconf.link))
continue;
}
v = (entry & 0x000f0000) >> 16;
if (!(v & dcbent->or))
continue;
v = (entry & 0x000000f0) >> 4;
if (v != dcbent->location)
continue;
v = (entry & 0x0000000f);
if (v != dcbent->type)
continue;
return &bios->data[table];
}
return NULL;
}
void *
nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
int *length)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
uint8_t *table;
if (!bios->display.dp_table_ptr) {
NV_ERROR(dev, "No pointer to DisplayPort table\n");
return NULL;
}
table = &bios->data[bios->display.dp_table_ptr];
if (table[0] != 0x20 && table[0] != 0x21) {
NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
table[0]);
return NULL;
}
*length = table[4];
return bios_output_config_match(dev, dcbent,
bios->display.dp_table_ptr + table[1],
table[2], table[3], table[0] >= 0x21);
} }
int int
nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent, nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk,
uint32_t sub, int pxclk) struct dcb_entry *dcbent, int crtc)
{ {
/* /*
* The display script table is located by the BIT 'U' table. * The display script table is located by the BIT 'U' table.
@ -4498,7 +4484,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
uint8_t *table = &bios->data[bios->display.script_table_ptr]; uint8_t *table = &bios->data[bios->display.script_table_ptr];
uint8_t *otable = NULL; uint8_t *otable = NULL;
uint16_t script; uint16_t script;
int i = 0; int i;
if (!bios->display.script_table_ptr) { if (!bios->display.script_table_ptr) {
NV_ERROR(dev, "No pointer to output script table\n"); NV_ERROR(dev, "No pointer to output script table\n");
@ -4550,30 +4536,33 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n", NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n",
dcbent->type, dcbent->location, dcbent->or); dcbent->type, dcbent->location, dcbent->or);
otable = bios_output_config_match(dev, dcbent, table[1] + for (i = 0; i < table[3]; i++) {
bios->display.script_table_ptr, otable = ROMPTR(bios, table[table[1] + (i * table[2])]);
table[2], table[3], table[0] >= 0x21); if (otable && bios_encoder_match(dcbent, ROM32(otable[0])))
break;
}
if (!otable) { if (!otable) {
NV_DEBUG_KMS(dev, "failed to match any output table\n"); NV_DEBUG_KMS(dev, "failed to match any output table\n");
return 1; return 1;
} }
if (pxclk < -2 || pxclk > 0) { if (pclk < -2 || pclk > 0) {
/* Try to find matching script table entry */ /* Try to find matching script table entry */
for (i = 0; i < otable[5]; i++) { for (i = 0; i < otable[5]; i++) {
if (ROM16(otable[table[4] + i*6]) == sub) if (ROM16(otable[table[4] + i*6]) == type)
break; break;
} }
if (i == otable[5]) { if (i == otable[5]) {
NV_ERROR(dev, "Table 0x%04x not found for %d/%d, " NV_ERROR(dev, "Table 0x%04x not found for %d/%d, "
"using first\n", "using first\n",
sub, dcbent->type, dcbent->or); type, dcbent->type, dcbent->or);
i = 0; i = 0;
} }
} }
if (pxclk == 0) { if (pclk == 0) {
script = ROM16(otable[6]); script = ROM16(otable[6]);
if (!script) { if (!script) {
NV_DEBUG_KMS(dev, "output script 0 not found\n"); NV_DEBUG_KMS(dev, "output script 0 not found\n");
@ -4581,9 +4570,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
} }
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script); NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script);
nouveau_bios_run_init_table(dev, script, dcbent); nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else } else
if (pxclk == -1) { if (pclk == -1) {
script = ROM16(otable[8]); script = ROM16(otable[8]);
if (!script) { if (!script) {
NV_DEBUG_KMS(dev, "output script 1 not found\n"); NV_DEBUG_KMS(dev, "output script 1 not found\n");
@ -4591,9 +4580,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
} }
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script); NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script);
nouveau_bios_run_init_table(dev, script, dcbent); nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else } else
if (pxclk == -2) { if (pclk == -2) {
if (table[4] >= 12) if (table[4] >= 12)
script = ROM16(otable[10]); script = ROM16(otable[10]);
else else
@ -4604,31 +4593,31 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
} }
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script); NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script);
nouveau_bios_run_init_table(dev, script, dcbent); nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else } else
if (pxclk > 0) { if (pclk > 0) {
script = ROM16(otable[table[4] + i*6 + 2]); script = ROM16(otable[table[4] + i*6 + 2]);
if (script) if (script)
script = clkcmptable(bios, script, pxclk); script = clkcmptable(bios, script, pclk);
if (!script) { if (!script) {
NV_DEBUG_KMS(dev, "clock script 0 not found\n"); NV_DEBUG_KMS(dev, "clock script 0 not found\n");
return 1; return 1;
} }
NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script); NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script);
nouveau_bios_run_init_table(dev, script, dcbent); nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} else } else
if (pxclk < 0) { if (pclk < 0) {
script = ROM16(otable[table[4] + i*6 + 4]); script = ROM16(otable[table[4] + i*6 + 4]);
if (script) if (script)
script = clkcmptable(bios, script, -pxclk); script = clkcmptable(bios, script, -pclk);
if (!script) { if (!script) {
NV_DEBUG_KMS(dev, "clock script 1 not found\n"); NV_DEBUG_KMS(dev, "clock script 1 not found\n");
return 1; return 1;
} }
NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script); NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script);
nouveau_bios_run_init_table(dev, script, dcbent); nouveau_bios_run_init_table(dev, script, dcbent, crtc);
} }
return 0; return 0;
@ -5478,14 +5467,6 @@ parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios,
return 0; return 0;
} }
static int
parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios,
struct bit_entry *bitentry)
{
bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]);
return 0;
}
struct bit_table { struct bit_table {
const char id; const char id;
int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *); int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *);
@ -5559,7 +5540,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds));
parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds));
parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U)); parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U));
parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport));
return 0; return 0;
} }
@ -5884,9 +5864,15 @@ parse_dcb_gpio_table(struct nvbios *bios)
} }
e->line = (e->entry & 0x0000001f) >> 0; e->line = (e->entry & 0x0000001f) >> 0;
if (gpio[0] == 0x40) {
e->state_default = (e->entry & 0x01000000) >> 24; e->state_default = (e->entry & 0x01000000) >> 24;
e->state[0] = (e->entry & 0x18000000) >> 27; e->state[0] = (e->entry & 0x18000000) >> 27;
e->state[1] = (e->entry & 0x60000000) >> 29; e->state[1] = (e->entry & 0x60000000) >> 29;
} else {
e->state_default = (e->entry & 0x00000080) >> 7;
e->state[0] = (entry[4] >> 4) & 3;
e->state[1] = (entry[4] >> 6) & 3;
}
} }
} }
@ -6156,7 +6142,14 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
} }
case OUTPUT_DP: case OUTPUT_DP:
entry->dpconf.sor.link = (conf & 0x00000030) >> 4; entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
entry->dpconf.link_bw = (conf & 0x00e00000) >> 21; switch ((conf & 0x00e00000) >> 21) {
case 0:
entry->dpconf.link_bw = 162000;
break;
default:
entry->dpconf.link_bw = 270000;
break;
}
switch ((conf & 0x0f000000) >> 24) { switch ((conf & 0x0f000000) >> 24) {
case 0xf: case 0xf:
entry->dpconf.link_nr = 4; entry->dpconf.link_nr = 4;
@ -6769,7 +6762,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
void void
nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table, nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
struct dcb_entry *dcbent) struct dcb_entry *dcbent, int crtc)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios; struct nvbios *bios = &dev_priv->vbios;
@ -6777,11 +6770,22 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
spin_lock_bh(&bios->lock); spin_lock_bh(&bios->lock);
bios->display.output = dcbent; bios->display.output = dcbent;
bios->display.crtc = crtc;
parse_init_table(bios, table, &iexec); parse_init_table(bios, table, &iexec);
bios->display.output = NULL; bios->display.output = NULL;
spin_unlock_bh(&bios->lock); spin_unlock_bh(&bios->lock);
} }
void
nouveau_bios_init_exec(struct drm_device *dev, uint16_t table)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
struct init_exec iexec = { true, false };
parse_init_table(bios, table, &iexec);
}
static bool NVInitVBIOS(struct drm_device *dev) static bool NVInitVBIOS(struct drm_device *dev)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
@ -6863,9 +6867,8 @@ nouveau_run_vbios_init(struct drm_device *dev)
if (dev_priv->card_type >= NV_50) { if (dev_priv->card_type >= NV_50) {
for (i = 0; i < bios->dcb.entries; i++) { for (i = 0; i < bios->dcb.entries; i++) {
nouveau_bios_run_display_table(dev, nouveau_bios_run_display_table(dev, 0, 0,
&bios->dcb.entry[i], &bios->dcb.entry[i], -1);
0, 0);
} }
} }

View file

@ -289,8 +289,8 @@ struct nvbios {
struct { struct {
struct dcb_entry *output; struct dcb_entry *output;
int crtc;
uint16_t script_table_ptr; uint16_t script_table_ptr;
uint16_t dp_table_ptr;
} display; } display;
struct { struct {

View file

@ -956,7 +956,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
break; break;
} }
if (dev_priv->card_type == NV_C0) if (dev_priv->card_type >= NV_C0)
page_shift = node->page_shift; page_shift = node->page_shift;
else else
page_shift = 12; page_shift = 12;

View file

@ -411,6 +411,7 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
return ret; return ret;
init->channel = chan->id; init->channel = chan->id;
if (nouveau_vram_pushbuf == 0) {
if (chan->dma.ib_max) if (chan->dma.ib_max)
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM | init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
NOUVEAU_GEM_DOMAIN_GART; NOUVEAU_GEM_DOMAIN_GART;
@ -418,6 +419,9 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM; init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
else else
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART; init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
} else {
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
}
if (dev_priv->card_type < NV_C0) { if (dev_priv->card_type < NV_C0) {
init->subchan[0].handle = NvM2MF; init->subchan[0].handle = NvM2MF;

View file

@ -39,7 +39,7 @@
static void nouveau_connector_hotplug(void *, int); static void nouveau_connector_hotplug(void *, int);
static struct nouveau_encoder * struct nouveau_encoder *
find_encoder(struct drm_connector *connector, int type) find_encoder(struct drm_connector *connector, int type)
{ {
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
@ -116,10 +116,6 @@ nouveau_connector_destroy(struct drm_connector *connector)
nouveau_connector_hotplug, connector); nouveau_connector_hotplug, connector);
} }
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
nouveau_backlight_exit(connector);
kfree(nv_connector->edid); kfree(nv_connector->edid);
drm_sysfs_connector_remove(connector); drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector); drm_connector_cleanup(connector);
@ -712,11 +708,8 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
case OUTPUT_TV: case OUTPUT_TV:
return get_slave_funcs(encoder)->mode_valid(encoder, mode); return get_slave_funcs(encoder)->mode_valid(encoder, mode);
case OUTPUT_DP: case OUTPUT_DP:
if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7) max_clock = nv_encoder->dp.link_nr;
max_clock = nv_encoder->dp.link_nr * 270000; max_clock *= nv_encoder->dp.link_bw;
else
max_clock = nv_encoder->dp.link_nr * 162000;
clock = clock * nouveau_connector_bpp(connector) / 8; clock = clock * nouveau_connector_bpp(connector) / 8;
break; break;
default: default:
@ -871,7 +864,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
dev->mode_config.scaling_mode_property, dev->mode_config.scaling_mode_property,
nv_connector->scaling_mode); nv_connector->scaling_mode);
} }
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
/* fall-through */ /* fall-through */
case DCB_CONNECTOR_TV_0: case DCB_CONNECTOR_TV_0:
case DCB_CONNECTOR_TV_1: case DCB_CONNECTOR_TV_1:
@ -888,27 +880,20 @@ nouveau_connector_create(struct drm_device *dev, int index)
dev->mode_config.dithering_mode_property, dev->mode_config.dithering_mode_property,
nv_connector->use_dithering ? nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF); DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) {
if (dev_priv->card_type >= NV_50)
connector->polled = DRM_CONNECTOR_POLL_HPD;
else
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
}
break; break;
} }
if (pgpio->irq_register) { if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) {
pgpio->irq_register(dev, nv_connector->dcb->gpio_tag, pgpio->irq_register(dev, nv_connector->dcb->gpio_tag,
nouveau_connector_hotplug, connector); nouveau_connector_hotplug, connector);
connector->polled = DRM_CONNECTOR_POLL_HPD;
} else {
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
} }
drm_sysfs_connector_add(connector); drm_sysfs_connector_add(connector);
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
nouveau_backlight_init(connector);
dcb->drm = connector; dcb->drm = connector;
return dcb->drm; return dcb->drm;
@ -925,22 +910,13 @@ nouveau_connector_hotplug(void *data, int plugged)
struct drm_connector *connector = data; struct drm_connector *connector = data;
struct drm_device *dev = connector->dev; struct drm_device *dev = connector->dev;
NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un", NV_DEBUG(dev, "%splugged %s\n", plugged ? "" : "un",
drm_get_connector_name(connector)); drm_get_connector_name(connector));
if (connector->encoder && connector->encoder->crtc &&
connector->encoder->crtc->enabled) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder);
struct drm_encoder_helper_funcs *helper =
connector->encoder->helper_private;
if (nv_encoder->dcb->type == OUTPUT_DP) {
if (plugged) if (plugged)
helper->dpms(connector->encoder, DRM_MODE_DPMS_ON); drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
else else
helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF); drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
}
}
drm_helper_hpd_irq_event(dev); drm_helper_hpd_irq_event(dev);
} }

View file

@ -82,14 +82,13 @@ static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
} }
int nv50_crtc_create(struct drm_device *dev, int index); int nv50_crtc_create(struct drm_device *dev, int index);
int nv50_cursor_init(struct nouveau_crtc *);
void nv50_cursor_fini(struct nouveau_crtc *);
int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv, int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
uint32_t buffer_handle, uint32_t width, uint32_t buffer_handle, uint32_t width,
uint32_t height); uint32_t height);
int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y); int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
int nv04_cursor_init(struct nouveau_crtc *); int nv04_cursor_init(struct nouveau_crtc *);
int nv50_cursor_init(struct nouveau_crtc *);
struct nouveau_connector * struct nouveau_connector *
nouveau_crtc_connector_get(struct nouveau_crtc *crtc); nouveau_crtc_connector_get(struct nouveau_crtc *crtc);

View file

@ -105,9 +105,12 @@ nouveau_framebuffer_init(struct drm_device *dev,
if (dev_priv->chipset == 0x50) if (dev_priv->chipset == 0x50)
nv_fb->r_format |= (tile_flags << 8); nv_fb->r_format |= (tile_flags << 8);
if (!tile_flags) if (!tile_flags) {
if (dev_priv->card_type < NV_D0)
nv_fb->r_pitch = 0x00100000 | fb->pitch; nv_fb->r_pitch = 0x00100000 | fb->pitch;
else { else
nv_fb->r_pitch = 0x01000000 | fb->pitch;
} else {
u32 mode = nvbo->tile_mode; u32 mode = nvbo->tile_mode;
if (dev_priv->card_type >= NV_C0) if (dev_priv->card_type >= NV_C0)
mode >>= 4; mode >>= 4;

File diff suppressed because it is too large Load diff

View file

@ -41,7 +41,7 @@ int nouveau_agpmode = -1;
module_param_named(agpmode, nouveau_agpmode, int, 0400); module_param_named(agpmode, nouveau_agpmode, int, 0400);
MODULE_PARM_DESC(modeset, "Enable kernel modesetting"); MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
static int nouveau_modeset = -1; /* kms */ int nouveau_modeset = -1;
module_param_named(modeset, nouveau_modeset, int, 0400); module_param_named(modeset, nouveau_modeset, int, 0400);
MODULE_PARM_DESC(vbios, "Override default VBIOS location"); MODULE_PARM_DESC(vbios, "Override default VBIOS location");

View file

@ -414,12 +414,13 @@ struct nouveau_gpio_engine {
}; };
struct nouveau_pm_voltage_level { struct nouveau_pm_voltage_level {
u8 voltage; u32 voltage; /* microvolts */
u8 vid; u8 vid;
}; };
struct nouveau_pm_voltage { struct nouveau_pm_voltage {
bool supported; bool supported;
u8 version;
u8 vid_mask; u8 vid_mask;
struct nouveau_pm_voltage_level *level; struct nouveau_pm_voltage_level *level;
@ -428,17 +429,48 @@ struct nouveau_pm_voltage {
struct nouveau_pm_memtiming { struct nouveau_pm_memtiming {
int id; int id;
u32 reg_100220; u32 reg_0; /* 0x10f290 on Fermi, 0x100220 for older */
u32 reg_100224; u32 reg_1;
u32 reg_100228; u32 reg_2;
u32 reg_10022c; u32 reg_3;
u32 reg_100230; u32 reg_4;
u32 reg_100234; u32 reg_5;
u32 reg_100238; u32 reg_6;
u32 reg_10023c; u32 reg_7;
u32 reg_100240; u32 reg_8;
/* To be written to 0x1002c0 */
u8 CL;
u8 WR;
}; };
struct nouveau_pm_tbl_header{
u8 version;
u8 header_len;
u8 entry_cnt;
u8 entry_len;
};
struct nouveau_pm_tbl_entry{
u8 tWR;
u8 tUNK_1;
u8 tCL;
u8 tRP; /* Byte 3 */
u8 empty_4;
u8 tRAS; /* Byte 5 */
u8 empty_6;
u8 tRFC; /* Byte 7 */
u8 empty_8;
u8 tRC; /* Byte 9 */
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
u8 empty_15,empty_16,empty_17;
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
};
/* nouveau_mem.c */
void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
struct nouveau_pm_memtiming *timing);
#define NOUVEAU_PM_MAX_LEVEL 8 #define NOUVEAU_PM_MAX_LEVEL 8
struct nouveau_pm_level { struct nouveau_pm_level {
struct device_attribute dev_attr; struct device_attribute dev_attr;
@ -448,10 +480,18 @@ struct nouveau_pm_level {
u32 core; u32 core;
u32 memory; u32 memory;
u32 shader; u32 shader;
u32 unk05; u32 rop;
u32 unk0a; u32 copy;
u32 daemon;
u32 vdec;
u32 unk05; /* nv50:nva3, roughly.. */
u32 unka0; /* nva3:nvc0 */
u32 hub01; /* nvc0- */
u32 hub06; /* nvc0- */
u32 hub07; /* nvc0- */
u8 voltage; u32 volt_min; /* microvolts */
u32 volt_max;
u8 fanspeed; u8 fanspeed;
u16 memscript; u16 memscript;
@ -496,6 +536,11 @@ struct nouveau_pm_engine {
void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *, void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *,
u32 id, int khz); u32 id, int khz);
void (*clock_set)(struct drm_device *, void *); void (*clock_set)(struct drm_device *, void *);
int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *);
void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *);
void (*clocks_set)(struct drm_device *, void *);
int (*voltage_get)(struct drm_device *); int (*voltage_get)(struct drm_device *);
int (*voltage_set)(struct drm_device *, int voltage); int (*voltage_set)(struct drm_device *, int voltage);
int (*fanspeed_get)(struct drm_device *); int (*fanspeed_get)(struct drm_device *);
@ -504,7 +549,7 @@ struct nouveau_pm_engine {
}; };
struct nouveau_vram_engine { struct nouveau_vram_engine {
struct nouveau_mm *mm; struct nouveau_mm mm;
int (*init)(struct drm_device *); int (*init)(struct drm_device *);
void (*takedown)(struct drm_device *dev); void (*takedown)(struct drm_device *dev);
@ -623,6 +668,7 @@ enum nouveau_card_type {
NV_40 = 0x40, NV_40 = 0x40,
NV_50 = 0x50, NV_50 = 0x50,
NV_C0 = 0xc0, NV_C0 = 0xc0,
NV_D0 = 0xd0
}; };
struct drm_nouveau_private { struct drm_nouveau_private {
@ -633,8 +679,8 @@ struct drm_nouveau_private {
enum nouveau_card_type card_type; enum nouveau_card_type card_type;
/* exact chipset, derived from NV_PMC_BOOT_0 */ /* exact chipset, derived from NV_PMC_BOOT_0 */
int chipset; int chipset;
int stepping;
int flags; int flags;
u32 crystal;
void __iomem *mmio; void __iomem *mmio;
@ -721,7 +767,6 @@ struct drm_nouveau_private {
uint64_t vram_size; uint64_t vram_size;
uint64_t vram_sys_base; uint64_t vram_sys_base;
uint64_t fb_phys;
uint64_t fb_available_size; uint64_t fb_available_size;
uint64_t fb_mappable_pages; uint64_t fb_mappable_pages;
uint64_t fb_aper_free; uint64_t fb_aper_free;
@ -784,6 +829,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
} }
/* nouveau_drv.c */ /* nouveau_drv.c */
extern int nouveau_modeset;
extern int nouveau_agpmode; extern int nouveau_agpmode;
extern int nouveau_duallink; extern int nouveau_duallink;
extern int nouveau_uscript_lvds; extern int nouveau_uscript_lvds;
@ -824,6 +870,8 @@ extern bool nouveau_wait_eq(struct drm_device *, uint64_t timeout,
uint32_t reg, uint32_t mask, uint32_t val); uint32_t reg, uint32_t mask, uint32_t val);
extern bool nouveau_wait_ne(struct drm_device *, uint64_t timeout, extern bool nouveau_wait_ne(struct drm_device *, uint64_t timeout,
uint32_t reg, uint32_t mask, uint32_t val); uint32_t reg, uint32_t mask, uint32_t val);
extern bool nouveau_wait_cb(struct drm_device *, u64 timeout,
bool (*cond)(void *), void *);
extern bool nouveau_wait_for_idle(struct drm_device *); extern bool nouveau_wait_for_idle(struct drm_device *);
extern int nouveau_card_init(struct drm_device *); extern int nouveau_card_init(struct drm_device *);
@ -1006,15 +1054,15 @@ static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector
/* nouveau_backlight.c */ /* nouveau_backlight.c */
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
extern int nouveau_backlight_init(struct drm_connector *); extern int nouveau_backlight_init(struct drm_device *);
extern void nouveau_backlight_exit(struct drm_connector *); extern void nouveau_backlight_exit(struct drm_device *);
#else #else
static inline int nouveau_backlight_init(struct drm_connector *dev) static inline int nouveau_backlight_init(struct drm_device *dev)
{ {
return 0; return 0;
} }
static inline void nouveau_backlight_exit(struct drm_connector *dev) { } static inline void nouveau_backlight_exit(struct drm_device *dev) { }
#endif #endif
/* nouveau_bios.c */ /* nouveau_bios.c */
@ -1022,7 +1070,8 @@ extern int nouveau_bios_init(struct drm_device *);
extern void nouveau_bios_takedown(struct drm_device *dev); extern void nouveau_bios_takedown(struct drm_device *dev);
extern int nouveau_run_vbios_init(struct drm_device *); extern int nouveau_run_vbios_init(struct drm_device *);
extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table, extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
struct dcb_entry *); struct dcb_entry *, int crtc);
extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table);
extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *, extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
enum dcb_gpio_tag); enum dcb_gpio_tag);
extern struct dcb_connector_table_entry * extern struct dcb_connector_table_entry *
@ -1030,11 +1079,8 @@ nouveau_bios_connector_entry(struct drm_device *, int index);
extern u32 get_pll_register(struct drm_device *, enum pll_types); extern u32 get_pll_register(struct drm_device *, enum pll_types);
extern int get_pll_limits(struct drm_device *, uint32_t limit_match, extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
struct pll_lims *); struct pll_lims *);
extern int nouveau_bios_run_display_table(struct drm_device *, extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk,
struct dcb_entry *, struct dcb_entry *, int crtc);
uint32_t script, int pxclk);
extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *,
int *length);
extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *); extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *);
extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk,
@ -1043,6 +1089,7 @@ extern int run_tmds_table(struct drm_device *, struct dcb_entry *,
int head, int pxclk); int head, int pxclk);
extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head, extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head,
enum LVDS_script, int pxclk); enum LVDS_script, int pxclk);
bool bios_encoder_match(struct dcb_entry *, u32 hash);
/* nouveau_ttm.c */ /* nouveau_ttm.c */
int nouveau_ttm_global_init(struct drm_nouveau_private *); int nouveau_ttm_global_init(struct drm_nouveau_private *);
@ -1053,7 +1100,9 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr, int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr); uint8_t *data, int data_nr);
bool nouveau_dp_detect(struct drm_encoder *); bool nouveau_dp_detect(struct drm_encoder *);
bool nouveau_dp_link_train(struct drm_encoder *); bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate);
void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
/* nv04_fb.c */ /* nv04_fb.c */
extern int nv04_fb_init(struct drm_device *); extern int nv04_fb_init(struct drm_device *);
@ -1179,8 +1228,8 @@ extern int nva3_copy_create(struct drm_device *dev);
/* nvc0_copy.c */ /* nvc0_copy.c */
extern int nvc0_copy_create(struct drm_device *dev, int engine); extern int nvc0_copy_create(struct drm_device *dev, int engine);
/* nv40_mpeg.c */ /* nv31_mpeg.c */
extern int nv40_mpeg_create(struct drm_device *dev); extern int nv31_mpeg_create(struct drm_device *dev);
/* nv50_mpeg.c */ /* nv50_mpeg.c */
extern int nv50_mpeg_create(struct drm_device *dev); extern int nv50_mpeg_create(struct drm_device *dev);
@ -1265,6 +1314,11 @@ extern int nv04_display_create(struct drm_device *);
extern int nv04_display_init(struct drm_device *); extern int nv04_display_init(struct drm_device *);
extern void nv04_display_destroy(struct drm_device *); extern void nv04_display_destroy(struct drm_device *);
/* nvd0_display.c */
extern int nvd0_display_create(struct drm_device *);
extern int nvd0_display_init(struct drm_device *);
extern void nvd0_display_destroy(struct drm_device *);
/* nv04_crtc.c */ /* nv04_crtc.c */
extern int nv04_crtc_create(struct drm_device *, int index); extern int nv04_crtc_create(struct drm_device *, int index);
@ -1374,6 +1428,8 @@ int nv50_gpio_init(struct drm_device *dev);
void nv50_gpio_fini(struct drm_device *dev); void nv50_gpio_fini(struct drm_device *dev);
int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag); int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state); int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
int nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
int nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag, int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag,
void (*)(void *, int), void *); void (*)(void *, int), void *);
void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag, void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag,
@ -1448,6 +1504,8 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
nouveau_wait_eq(dev, 2000000000ULL, (reg), (mask), (val)) nouveau_wait_eq(dev, 2000000000ULL, (reg), (mask), (val))
#define nv_wait_ne(dev, reg, mask, val) \ #define nv_wait_ne(dev, reg, mask, val) \
nouveau_wait_ne(dev, 2000000000ULL, (reg), (mask), (val)) nouveau_wait_ne(dev, 2000000000ULL, (reg), (mask), (val))
#define nv_wait_cb(dev, func, data) \
nouveau_wait_cb(dev, 2000000000ULL, (func), (data))
/* PRAMIN access */ /* PRAMIN access */
static inline u32 nv_ri32(struct drm_device *dev, unsigned offset) static inline u32 nv_ri32(struct drm_device *dev, unsigned offset)
@ -1514,6 +1572,7 @@ enum {
NOUVEAU_REG_DEBUG_RMVIO = 0x80, NOUVEAU_REG_DEBUG_RMVIO = 0x80,
NOUVEAU_REG_DEBUG_VGAATTR = 0x100, NOUVEAU_REG_DEBUG_VGAATTR = 0x100,
NOUVEAU_REG_DEBUG_EVO = 0x200, NOUVEAU_REG_DEBUG_EVO = 0x200,
NOUVEAU_REG_DEBUG_AUXCH = 0x400
}; };
#define NV_REG_DEBUG(type, dev, fmt, arg...) do { \ #define NV_REG_DEBUG(type, dev, fmt, arg...) do { \

View file

@ -49,17 +49,17 @@ struct nouveau_encoder {
union { union {
struct { struct {
int mc_unknown; u8 dpcd[8];
uint32_t unk0;
uint32_t unk1;
int dpcd_version;
int link_nr; int link_nr;
int link_bw; int link_bw;
bool enhanced_frame; u32 datarate;
} dp; } dp;
}; };
}; };
struct nouveau_encoder *
find_encoder(struct drm_connector *connector, int type);
static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc) static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
{ {
struct drm_encoder_slave *slave = to_encoder_slave(enc); struct drm_encoder_slave *slave = to_encoder_slave(enc);
@ -83,21 +83,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int nv50_sor_create(struct drm_connector *, struct dcb_entry *); int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
int nv50_dac_create(struct drm_connector *, struct dcb_entry *); int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
struct bit_displayport_encoder_table {
uint32_t match;
uint8_t record_nr;
uint8_t unknown;
uint16_t script0;
uint16_t script1;
uint16_t unknown_table;
} __attribute__ ((packed));
struct bit_displayport_encoder_table_entry {
uint8_t vs_level;
uint8_t pre_level;
uint8_t reg0;
uint8_t reg1;
uint8_t reg2;
} __attribute__ ((packed));
#endif /* __NOUVEAU_ENCODER_H__ */ #endif /* __NOUVEAU_ENCODER_H__ */

View file

@ -519,7 +519,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
if (USE_SEMA(dev) && dev_priv->chipset < 0x84) { if (USE_SEMA(dev) && dev_priv->chipset < 0x84) {
struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem; struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem;
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY,
mem->start << PAGE_SHIFT, mem->start << PAGE_SHIFT,
mem->size, NV_MEM_ACCESS_RW, mem->size, NV_MEM_ACCESS_RW,
NV_MEM_TARGET_VRAM, &obj); NV_MEM_TARGET_VRAM, &obj);

View file

@ -107,6 +107,13 @@ nv4e_i2c_getsda(void *data)
return !!((nv_rd32(dev, i2c->rd) >> 16) & 8); return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
} }
static const uint32_t nv50_i2c_port[] = {
0x00e138, 0x00e150, 0x00e168, 0x00e180,
0x00e254, 0x00e274, 0x00e764, 0x00e780,
0x00e79c, 0x00e7b8
};
#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
static int static int
nv50_i2c_getscl(void *data) nv50_i2c_getscl(void *data)
{ {
@ -130,28 +137,32 @@ static void
nv50_i2c_setscl(void *data, int state) nv50_i2c_setscl(void *data, int state)
{ {
struct nouveau_i2c_chan *i2c = data; struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0)); nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
} }
static void static void
nv50_i2c_setsda(void *data, int state) nv50_i2c_setsda(void *data, int state)
{ {
struct nouveau_i2c_chan *i2c = data; struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
nv_wr32(dev, i2c->wr, nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0));
(nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0));
i2c->data = state; i2c->data = state;
} }
static const uint32_t nv50_i2c_port[] = { static int
0x00e138, 0x00e150, 0x00e168, 0x00e180, nvd0_i2c_getscl(void *data)
0x00e254, 0x00e274, 0x00e764, 0x00e780, {
0x00e79c, 0x00e7b8 struct nouveau_i2c_chan *i2c = data;
}; return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10);
#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port) }
static int
nvd0_i2c_getsda(void *data)
{
struct nouveau_i2c_chan *i2c = data;
return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20);
}
int int
nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index) nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
@ -163,7 +174,8 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
if (entry->chan) if (entry->chan)
return -EEXIST; return -EEXIST;
if (dev_priv->card_type >= NV_50 && entry->read >= NV50_I2C_PORTS) { if (dev_priv->card_type >= NV_50 &&
dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) {
NV_ERROR(dev, "unknown i2c port %d\n", entry->read); NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
return -EINVAL; return -EINVAL;
} }
@ -192,10 +204,17 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
case 5: case 5:
i2c->bit.setsda = nv50_i2c_setsda; i2c->bit.setsda = nv50_i2c_setsda;
i2c->bit.setscl = nv50_i2c_setscl; i2c->bit.setscl = nv50_i2c_setscl;
if (dev_priv->card_type < NV_D0) {
i2c->bit.getsda = nv50_i2c_getsda; i2c->bit.getsda = nv50_i2c_getsda;
i2c->bit.getscl = nv50_i2c_getscl; i2c->bit.getscl = nv50_i2c_getscl;
i2c->rd = nv50_i2c_port[entry->read]; i2c->rd = nv50_i2c_port[entry->read];
i2c->wr = i2c->rd; i2c->wr = i2c->rd;
} else {
i2c->bit.getsda = nvd0_i2c_getsda;
i2c->bit.getscl = nvd0_i2c_getscl;
i2c->rd = 0x00d014 + (entry->read * 0x20);
i2c->wr = i2c->rd;
}
break; break;
case 6: case 6:
i2c->rd = entry->read; i2c->rd = entry->read;
@ -267,7 +286,10 @@ nouveau_i2c_find(struct drm_device *dev, int index)
val = 0xe001; val = 0xe001;
} }
nv_wr32(dev, reg, (nv_rd32(dev, reg) & ~0xf003) | val); /* nfi, but neither auxch or i2c work if it's 1 */
nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000);
/* nfi, but switches auxch vs normal i2c */
nv_mask(dev, reg + 0x00, 0x0000f003, val);
} }
if (!i2c->chan && nouveau_i2c_init(dev, i2c, index)) if (!i2c->chan && nouveau_i2c_init(dev, i2c, index))

View file

@ -408,8 +408,6 @@ nouveau_mem_vram_init(struct drm_device *dev)
if (ret) if (ret)
return ret; return ret;
dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
ret = nouveau_ttm_global_init(dev_priv); ret = nouveau_ttm_global_init(dev_priv);
if (ret) if (ret)
return ret; return ret;
@ -504,35 +502,146 @@ nouveau_mem_gart_init(struct drm_device *dev)
return 0; return 0;
} }
/* XXX: For now a dummy. More samples required, possibly even a card
* Called from nouveau_perf.c */
void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
struct nouveau_pm_memtiming *timing) {
NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers");
}
void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
struct nouveau_pm_memtiming *timing) {
timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
timing->reg_1 = (e->tWR + 2 + magic_number) << 24 |
1 << 16 |
(e->tUNK_1 + 2 + magic_number) << 8 |
(e->tCL + 2 - magic_number);
timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
timing->reg_2 |= 0x20200000;
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id,
timing->reg_0, timing->reg_1,timing->reg_2);
}
void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr,
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,struct nouveau_pm_memtiming *timing) {
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint8_t unk18 = 1,
unk19 = 1,
unk20 = 0,
unk21 = 0;
switch (min(hdr->entry_len, (u8) 22)) {
case 22:
unk21 = e->tUNK_21;
case 21:
unk20 = e->tUNK_20;
case 20:
unk19 = e->tUNK_19;
case 19:
unk18 = e->tUNK_18;
break;
}
timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 |
max(unk18, (u8) 1) << 16 |
(e->tUNK_1 + unk19 + 1 + magic_number) << 8;
if (dev_priv->chipset == 0xa8) {
timing->reg_1 |= (e->tCL - 1);
} else {
timing->reg_1 |= (e->tCL + 2 - magic_number);
}
timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
timing->reg_5 = (e->tRAS << 24 | e->tRC);
timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16;
if (P->version == 1) {
timing->reg_2 |= magic_number << 24;
timing->reg_3 = (0x14 + e->tCL) << 24 |
0x16 << 16 |
(e->tCL - 1) << 8 |
(e->tCL - 1);
timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13;
timing->reg_5 |= (e->tCL + 2) << 8;
timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16;
} else {
timing->reg_2 |= (unk19 - 1) << 24;
/* XXX: reg_10022c for recentish cards pretty much unknown*/
timing->reg_3 = e->tCL - 1;
timing->reg_4 = (unk20 << 24 | unk21 << 16 |
e->tUNK_13 << 8 | e->tUNK_13);
/* XXX: +6? */
timing->reg_5 |= (unk19 + 6) << 8;
/* XXX: reg_10023c currently unknown
* 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
timing->reg_7 = 0x202;
}
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id,
timing->reg_0, timing->reg_1,
timing->reg_2, timing->reg_3);
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
timing->reg_4, timing->reg_5,
timing->reg_6, timing->reg_7);
NV_DEBUG(dev, " 240: %08x\n", timing->reg_8);
}
void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) {
timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP);
timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f);
timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8;
timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13;
timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15;
NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id,
timing->reg_0, timing->reg_1,
timing->reg_2, timing->reg_3);
NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n",
timing->reg_4, timing->reg_5,
timing->reg_6, timing->reg_7);
}
/**
* Processes the Memory Timing BIOS table, stores generated
* register values
* @pre init scripts were run, memtiming regs are initialized
*/
void void
nouveau_mem_timing_init(struct drm_device *dev) nouveau_mem_timing_init(struct drm_device *dev)
{ {
/* cards < NVC0 only */
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nouveau_pm_memtimings *memtimings = &pm->memtimings; struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
struct nvbios *bios = &dev_priv->vbios; struct nvbios *bios = &dev_priv->vbios;
struct bit_entry P; struct bit_entry P;
u8 tUNK_0, tUNK_1, tUNK_2; struct nouveau_pm_tbl_header *hdr = NULL;
u8 tRP; /* Byte 3 */ uint8_t magic_number;
u8 tRAS; /* Byte 5 */ u8 *entry;
u8 tRFC; /* Byte 7 */ int i;
u8 tRC; /* Byte 9 */
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
u8 magic_number = 0; /* Yeah... sorry*/
u8 *mem = NULL, *entry;
int i, recordlen, entries;
if (bios->type == NVBIOS_BIT) { if (bios->type == NVBIOS_BIT) {
if (bit_table(dev, 'P', &P)) if (bit_table(dev, 'P', &P))
return; return;
if (P.version == 1) if (P.version == 1)
mem = ROMPTR(bios, P.data[4]); hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]);
else else
if (P.version == 2) if (P.version == 2)
mem = ROMPTR(bios, P.data[8]); hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]);
else { else {
NV_WARN(dev, "unknown mem for BIT P %d\n", P.version); NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
} }
@ -541,150 +650,56 @@ nouveau_mem_timing_init(struct drm_device *dev)
return; return;
} }
if (!mem) { if (!hdr) {
NV_DEBUG(dev, "memory timing table pointer invalid\n"); NV_DEBUG(dev, "memory timing table pointer invalid\n");
return; return;
} }
if (mem[0] != 0x10) { if (hdr->version != 0x10) {
NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]); NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version);
return; return;
} }
/* validate record length */ /* validate record length */
entries = mem[2]; if (hdr->entry_len < 15) {
recordlen = mem[3]; NV_ERROR(dev, "mem timing table length unknown: %d\n", hdr->entry_len);
if (recordlen < 15) {
NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]);
return; return;
} }
/* parse vbios entries into common format */ /* parse vbios entries into common format */
memtimings->timing = memtimings->timing =
kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL); kcalloc(hdr->entry_cnt, sizeof(*memtimings->timing), GFP_KERNEL);
if (!memtimings->timing) if (!memtimings->timing)
return; return;
/* Get "some number" from the timing reg for NV_40 and NV_50 /* Get "some number" from the timing reg for NV_40 and NV_50
* Used in calculations later */ * Used in calculations later... source unknown */
if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) { magic_number = 0;
if (P.version == 1) {
magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24; magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24;
} }
entry = mem + mem[1]; entry = (u8*) hdr + hdr->header_len;
for (i = 0; i < entries; i++, entry += recordlen) { for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) {
struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i]; struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
if (entry[0] == 0) if (entry[0] == 0)
continue; continue;
tUNK_18 = 1;
tUNK_19 = 1;
tUNK_20 = 0;
tUNK_21 = 0;
switch (min(recordlen, 22)) {
case 22:
tUNK_21 = entry[21];
case 21:
tUNK_20 = entry[20];
case 20:
tUNK_19 = entry[19];
case 19:
tUNK_18 = entry[18];
default:
tUNK_0 = entry[0];
tUNK_1 = entry[1];
tUNK_2 = entry[2];
tRP = entry[3];
tRAS = entry[5];
tRFC = entry[7];
tRC = entry[9];
tUNK_10 = entry[10];
tUNK_11 = entry[11];
tUNK_12 = entry[12];
tUNK_13 = entry[13];
tUNK_14 = entry[14];
break;
}
timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP);
/* XXX: I don't trust the -1's and +1's... they must come
* from somewhere! */
timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
max(tUNK_18, (u8) 1) << 16 |
(tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
if (dev_priv->chipset == 0xa8) {
timing->reg_100224 |= (tUNK_2 - 1);
} else {
timing->reg_100224 |= (tUNK_2 + 2 - magic_number);
}
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa)
timing->reg_100228 |= (tUNK_19 - 1) << 24;
else
timing->reg_100228 |= magic_number << 24;
if (dev_priv->card_type == NV_40) {
/* NV40: don't know what the rest of the regs are..
* And don't need to know either */
timing->reg_100228 |= 0x20200000;
} else if (dev_priv->card_type >= NV_50) {
if (dev_priv->chipset < 0x98 ||
(dev_priv->chipset == 0x98 &&
dev_priv->stepping <= 0xa1)) {
timing->reg_10022c = (0x14 + tUNK_2) << 24 |
0x16 << 16 |
(tUNK_2 - 1) << 8 |
(tUNK_2 - 1);
} else {
/* XXX: reg_10022c for recentish cards */
timing->reg_10022c = tUNK_2 - 1;
}
timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
tUNK_13 << 8 | tUNK_13);
timing->reg_100234 = (tRAS << 24 | tRC);
timing->reg_100234 += max(tUNK_10, tUNK_11) << 16;
if (dev_priv->chipset < 0x98 ||
(dev_priv->chipset == 0x98 &&
dev_priv->stepping <= 0xa1)) {
timing->reg_100234 |= (tUNK_2 + 2) << 8;
} else {
/* XXX: +6? */
timing->reg_100234 |= (tUNK_19 + 6) << 8;
}
/* XXX; reg_100238
* reg_100238: 0x00?????? */
timing->reg_10023c = 0x202;
if (dev_priv->chipset < 0x98 ||
(dev_priv->chipset == 0x98 &&
dev_priv->stepping <= 0xa1)) {
timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
} else {
/* XXX: reg_10023c
* currently unknown
* 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
}
/* XXX: reg_100240? */
}
timing->id = i; timing->id = i;
timing->WR = entry[0];
timing->CL = entry[2];
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i, if(dev_priv->card_type <= NV_40) {
timing->reg_100220, timing->reg_100224, nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
timing->reg_100228, timing->reg_10022c); } else if(dev_priv->card_type == NV_50){
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n", nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
timing->reg_100230, timing->reg_100234, } else if(dev_priv->card_type == NV_C0) {
timing->reg_100238, timing->reg_10023c); nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]);
NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240); }
} }
memtimings->nr_timing = entries; memtimings->nr_timing = hdr->entry_cnt;
memtimings->supported = (dev_priv->chipset <= 0x98); memtimings->supported = P.version == 1;
} }
void void
@ -693,7 +708,10 @@ nouveau_mem_timing_fini(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings; struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings;
if(mem->timing) {
kfree(mem->timing); kfree(mem->timing);
mem->timing = NULL;
}
} }
static int static int

View file

@ -27,7 +27,7 @@
#include "nouveau_mm.h" #include "nouveau_mm.h"
static inline void static inline void
region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a) region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
{ {
list_del(&a->nl_entry); list_del(&a->nl_entry);
list_del(&a->fl_entry); list_del(&a->fl_entry);
@ -35,7 +35,7 @@ region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)
} }
static struct nouveau_mm_node * static struct nouveau_mm_node *
region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size) region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
{ {
struct nouveau_mm_node *b; struct nouveau_mm_node *b;
@ -57,33 +57,33 @@ region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)
return b; return b;
} }
#define node(root, dir) ((root)->nl_entry.dir == &rmm->nodes) ? NULL : \ #define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry) list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
void void
nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this) nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
{ {
struct nouveau_mm_node *prev = node(this, prev); struct nouveau_mm_node *prev = node(this, prev);
struct nouveau_mm_node *next = node(this, next); struct nouveau_mm_node *next = node(this, next);
list_add(&this->fl_entry, &rmm->free); list_add(&this->fl_entry, &mm->free);
this->type = 0; this->type = 0;
if (prev && prev->type == 0) { if (prev && prev->type == 0) {
prev->length += this->length; prev->length += this->length;
region_put(rmm, this); region_put(mm, this);
this = prev; this = prev;
} }
if (next && next->type == 0) { if (next && next->type == 0) {
next->offset = this->offset; next->offset = this->offset;
next->length += this->length; next->length += this->length;
region_put(rmm, this); region_put(mm, this);
} }
} }
int int
nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc, nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
u32 align, struct nouveau_mm_node **pnode) u32 align, struct nouveau_mm_node **pnode)
{ {
struct nouveau_mm_node *prev, *this, *next; struct nouveau_mm_node *prev, *this, *next;
@ -92,17 +92,17 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
u32 splitoff; u32 splitoff;
u32 s, e; u32 s, e;
list_for_each_entry(this, &rmm->free, fl_entry) { list_for_each_entry(this, &mm->free, fl_entry) {
e = this->offset + this->length; e = this->offset + this->length;
s = this->offset; s = this->offset;
prev = node(this, prev); prev = node(this, prev);
if (prev && prev->type != type) if (prev && prev->type != type)
s = roundup(s, rmm->block_size); s = roundup(s, mm->block_size);
next = node(this, next); next = node(this, next);
if (next && next->type != type) if (next && next->type != type)
e = rounddown(e, rmm->block_size); e = rounddown(e, mm->block_size);
s = (s + align_mask) & ~align_mask; s = (s + align_mask) & ~align_mask;
e &= ~align_mask; e &= ~align_mask;
@ -110,10 +110,10 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
continue; continue;
splitoff = s - this->offset; splitoff = s - this->offset;
if (splitoff && !region_split(rmm, this, splitoff)) if (splitoff && !region_split(mm, this, splitoff))
return -ENOMEM; return -ENOMEM;
this = region_split(rmm, this, min(size, e - s)); this = region_split(mm, this, min(size, e - s));
if (!this) if (!this)
return -ENOMEM; return -ENOMEM;
@ -127,52 +127,49 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
} }
int int
nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block) nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
{ {
struct nouveau_mm *rmm; struct nouveau_mm_node *node;
struct nouveau_mm_node *heap;
heap = kzalloc(sizeof(*heap), GFP_KERNEL); if (block) {
if (!heap) mutex_init(&mm->mutex);
return -ENOMEM; INIT_LIST_HEAD(&mm->nodes);
heap->offset = roundup(offset, block); INIT_LIST_HEAD(&mm->free);
heap->length = rounddown(offset + length, block) - heap->offset; mm->block_size = block;
mm->heap_nodes = 0;
rmm = kzalloc(sizeof(*rmm), GFP_KERNEL);
if (!rmm) {
kfree(heap);
return -ENOMEM;
} }
rmm->block_size = block;
mutex_init(&rmm->mutex);
INIT_LIST_HEAD(&rmm->nodes);
INIT_LIST_HEAD(&rmm->free);
list_add(&heap->nl_entry, &rmm->nodes);
list_add(&heap->fl_entry, &rmm->free);
*prmm = rmm; node = kzalloc(sizeof(*node), GFP_KERNEL);
if (!node)
return -ENOMEM;
node->offset = roundup(offset, mm->block_size);
node->length = rounddown(offset + length, mm->block_size) - node->offset;
list_add_tail(&node->nl_entry, &mm->nodes);
list_add_tail(&node->fl_entry, &mm->free);
mm->heap_nodes++;
return 0; return 0;
} }
int int
nouveau_mm_fini(struct nouveau_mm **prmm) nouveau_mm_fini(struct nouveau_mm *mm)
{ {
struct nouveau_mm *rmm = *prmm;
struct nouveau_mm_node *node, *heap = struct nouveau_mm_node *node, *heap =
list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry); list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry);
int nodes = 0;
if (!list_is_singular(&rmm->nodes)) { list_for_each_entry(node, &mm->nodes, nl_entry) {
printk(KERN_ERR "nouveau_mm not empty at destroy time!\n"); if (nodes++ == mm->heap_nodes) {
list_for_each_entry(node, &rmm->nodes, nl_entry) { printk(KERN_ERR "nouveau_mm in use at destroy time!\n");
list_for_each_entry(node, &mm->nodes, nl_entry) {
printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n", printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
node->type, node->offset, node->length); node->type, node->offset, node->length);
} }
WARN_ON(1); WARN_ON(1);
return -EBUSY; return -EBUSY;
} }
}
kfree(heap); kfree(heap);
kfree(rmm);
*prmm = NULL;
return 0; return 0;
} }

View file

@ -42,10 +42,11 @@ struct nouveau_mm {
struct mutex mutex; struct mutex mutex;
u32 block_size; u32 block_size;
int heap_nodes;
}; };
int nouveau_mm_init(struct nouveau_mm **, u32 offset, u32 length, u32 block); int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
int nouveau_mm_fini(struct nouveau_mm **); int nouveau_mm_fini(struct nouveau_mm *);
int nouveau_mm_pre(struct nouveau_mm *); int nouveau_mm_pre(struct nouveau_mm *);
int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc, int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc,
u32 align, struct nouveau_mm_node **); u32 align, struct nouveau_mm_node **);

View file

@ -693,6 +693,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
static int static int
nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm) nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm)
{ {
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_device *dev = chan->dev; struct drm_device *dev = chan->dev;
struct nouveau_gpuobj *pgd = NULL; struct nouveau_gpuobj *pgd = NULL;
struct nouveau_vm_pgd *vpgd; struct nouveau_vm_pgd *vpgd;
@ -722,6 +723,9 @@ nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm)
nv_wo32(chan->ramin, 0x020c, 0x000000ff); nv_wo32(chan->ramin, 0x020c, 0x000000ff);
/* map display semaphore buffers into channel's vm */ /* map display semaphore buffers into channel's vm */
if (dev_priv->card_type >= NV_D0)
return 0;
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i]; struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i];
@ -746,7 +750,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
int ret, i; int ret, i;
NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h); NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
if (dev_priv->card_type == NV_C0) if (dev_priv->card_type >= NV_C0)
return nvc0_gpuobj_channel_init(chan, vm); return nvc0_gpuobj_channel_init(chan, vm);
/* Allocate a chunk of memory for per-channel object storage */ /* Allocate a chunk of memory for per-channel object storage */
@ -793,7 +797,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
return ret; return ret;
/* dma objects for display sync channel semaphore blocks */ /* dma objects for display sync channel semaphore blocks */
for (i = 0; i < 2; i++) { for (i = 0; i < dev->mode_config.num_crtc; i++) {
struct nouveau_gpuobj *sem = NULL; struct nouveau_gpuobj *sem = NULL;
struct nv50_display_crtc *dispc = struct nv50_display_crtc *dispc =
&nv50_display(dev)->crtc[i]; &nv50_display(dev)->crtc[i];
@ -875,17 +879,17 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
NV_DEBUG(dev, "ch%d\n", chan->id); NV_DEBUG(dev, "ch%d\n", chan->id);
if (dev_priv->card_type >= NV_50) { if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) {
struct nv50_display *disp = nv50_display(dev); struct nv50_display *disp = nv50_display(dev);
for (i = 0; i < 2; i++) { for (i = 0; i < dev->mode_config.num_crtc; i++) {
struct nv50_display_crtc *dispc = &disp->crtc[i]; struct nv50_display_crtc *dispc = &disp->crtc[i];
nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]); nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]);
} }
}
nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd); nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd);
nouveau_gpuobj_ref(NULL, &chan->vm_pd); nouveau_gpuobj_ref(NULL, &chan->vm_pd);
}
if (drm_mm_initialized(&chan->ramin_heap)) if (drm_mm_initialized(&chan->ramin_heap))
drm_mm_takedown(&chan->ramin_heap); drm_mm_takedown(&chan->ramin_heap);

View file

@ -127,6 +127,7 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
entry += ramcfg * recordlen; entry += ramcfg * recordlen;
if (entry[1] >= pm->memtimings.nr_timing) { if (entry[1] >= pm->memtimings.nr_timing) {
if (entry[1] != 0xff)
NV_WARN(dev, "timingset %d does not exist\n", entry[1]); NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
return NULL; return NULL;
} }
@ -134,6 +135,49 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
return &pm->memtimings.timing[entry[1]]; return &pm->memtimings.timing[entry[1]];
} }
static void
nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
struct nouveau_pm_level *perflvl)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
u8 *vmap;
int id;
id = perflvl->volt_min;
perflvl->volt_min = 0;
/* boards using voltage table version <0x40 store the voltage
* level directly in the perflvl entry as a multiple of 10mV
*/
if (dev_priv->engine.pm.voltage.version < 0x40) {
perflvl->volt_min = id * 10000;
perflvl->volt_max = perflvl->volt_min;
return;
}
/* on newer ones, the perflvl stores an index into yet another
* vbios table containing a min/max voltage value for the perflvl
*/
if (P->version != 2 || P->length < 34) {
NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n",
P->version, P->length);
return;
}
vmap = ROMPTR(bios, P->data[32]);
if (!vmap) {
NV_DEBUG(dev, "volt map table pointer invalid\n");
return;
}
if (id < vmap[3]) {
vmap += vmap[1] + (vmap[2] * id);
perflvl->volt_min = ROM32(vmap[0]);
perflvl->volt_max = ROM32(vmap[4]);
}
}
void void
nouveau_perf_init(struct drm_device *dev) nouveau_perf_init(struct drm_device *dev)
{ {
@ -141,6 +185,8 @@ nouveau_perf_init(struct drm_device *dev)
struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct nvbios *bios = &dev_priv->vbios; struct nvbios *bios = &dev_priv->vbios;
struct bit_entry P; struct bit_entry P;
struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
struct nouveau_pm_tbl_header mt_hdr;
u8 version, headerlen, recordlen, entries; u8 version, headerlen, recordlen, entries;
u8 *perf, *entry; u8 *perf, *entry;
int vid, i; int vid, i;
@ -188,6 +234,22 @@ nouveau_perf_init(struct drm_device *dev)
} }
entry = perf + headerlen; entry = perf + headerlen;
/* For version 0x15, initialize memtiming table */
if(version == 0x15) {
memtimings->timing =
kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
if(!memtimings) {
NV_WARN(dev,"Could not allocate memtiming table\n");
return;
}
mt_hdr.entry_cnt = entries;
mt_hdr.entry_len = 14;
mt_hdr.version = version;
mt_hdr.header_len = 4;
}
for (i = 0; i < entries; i++) { for (i = 0; i < entries; i++) {
struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl]; struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
@ -203,7 +265,8 @@ nouveau_perf_init(struct drm_device *dev)
case 0x13: case 0x13:
case 0x15: case 0x15:
perflvl->fanspeed = entry[55]; perflvl->fanspeed = entry[55];
perflvl->voltage = (recordlen > 56) ? entry[56] : 0; if (recordlen > 56)
perflvl->volt_min = entry[56];
perflvl->core = ROM32(entry[1]) * 10; perflvl->core = ROM32(entry[1]) * 10;
perflvl->memory = ROM32(entry[5]) * 20; perflvl->memory = ROM32(entry[5]) * 20;
break; break;
@ -211,9 +274,10 @@ nouveau_perf_init(struct drm_device *dev)
case 0x23: case 0x23:
case 0x24: case 0x24:
perflvl->fanspeed = entry[4]; perflvl->fanspeed = entry[4];
perflvl->voltage = entry[5]; perflvl->volt_min = entry[5];
perflvl->core = ROM16(entry[6]) * 1000; perflvl->shader = ROM16(entry[6]) * 1000;
perflvl->core = perflvl->shader;
perflvl->core += (signed char)entry[8] * 1000;
if (dev_priv->chipset == 0x49 || if (dev_priv->chipset == 0x49 ||
dev_priv->chipset == 0x4b) dev_priv->chipset == 0x4b)
perflvl->memory = ROM16(entry[11]) * 1000; perflvl->memory = ROM16(entry[11]) * 1000;
@ -223,7 +287,7 @@ nouveau_perf_init(struct drm_device *dev)
break; break;
case 0x25: case 0x25:
perflvl->fanspeed = entry[4]; perflvl->fanspeed = entry[4];
perflvl->voltage = entry[5]; perflvl->volt_min = entry[5];
perflvl->core = ROM16(entry[6]) * 1000; perflvl->core = ROM16(entry[6]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000;
@ -232,7 +296,7 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->memscript = ROM16(entry[2]); perflvl->memscript = ROM16(entry[2]);
case 0x35: case 0x35:
perflvl->fanspeed = entry[6]; perflvl->fanspeed = entry[6];
perflvl->voltage = entry[7]; perflvl->volt_min = entry[7];
perflvl->core = ROM16(entry[8]) * 1000; perflvl->core = ROM16(entry[8]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000; perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000; perflvl->memory = ROM16(entry[12]) * 1000;
@ -240,30 +304,34 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->unk05 = ROM16(entry[16]) * 1000; perflvl->unk05 = ROM16(entry[16]) * 1000;
break; break;
case 0x40: case 0x40:
#define subent(n) entry[perf[2] + ((n) * perf[3])] #define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000
perflvl->fanspeed = 0; /*XXX*/ perflvl->fanspeed = 0; /*XXX*/
perflvl->voltage = entry[2]; perflvl->volt_min = entry[2];
if (dev_priv->card_type == NV_50) { if (dev_priv->card_type == NV_50) {
perflvl->core = ROM16(subent(0)) & 0xfff; perflvl->core = subent(0);
perflvl->shader = ROM16(subent(1)) & 0xfff; perflvl->shader = subent(1);
perflvl->memory = ROM16(subent(2)) & 0xfff; perflvl->memory = subent(2);
perflvl->vdec = subent(3);
perflvl->unka0 = subent(4);
} else { } else {
perflvl->shader = ROM16(subent(3)) & 0xfff; perflvl->hub06 = subent(0);
perflvl->hub01 = subent(1);
perflvl->copy = subent(2);
perflvl->shader = subent(3);
perflvl->rop = subent(4);
perflvl->memory = subent(5);
perflvl->vdec = subent(6);
perflvl->daemon = subent(10);
perflvl->hub07 = subent(11);
perflvl->core = perflvl->shader / 2; perflvl->core = perflvl->shader / 2;
perflvl->unk0a = ROM16(subent(4)) & 0xfff;
perflvl->memory = ROM16(subent(5)) & 0xfff;
} }
perflvl->core *= 1000;
perflvl->shader *= 1000;
perflvl->memory *= 1000;
perflvl->unk0a *= 1000;
break; break;
} }
/* make sure vid is valid */ /* make sure vid is valid */
if (pm->voltage.supported && perflvl->voltage) { nouveau_perf_voltage(dev, &P, perflvl);
vid = nouveau_volt_vid_lookup(dev, perflvl->voltage); if (pm->voltage.supported && perflvl->volt_min) {
vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
if (vid < 0) { if (vid < 0) {
NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i); NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
entry += recordlen; entry += recordlen;
@ -272,7 +340,11 @@ nouveau_perf_init(struct drm_device *dev)
} }
/* get the corresponding memory timings */ /* get the corresponding memory timings */
if (version > 0x15) { if (version == 0x15) {
memtimings->timing[i].id = i;
nv30_mem_timing_entry(dev,&mt_hdr,(struct nouveau_pm_tbl_entry*) &entry[41],0,&memtimings->timing[i]);
perflvl->timing = &memtimings->timing[i];
} else if (version > 0x15) {
/* last 3 args are for < 0x40, ignored for >= 0x40 */ /* last 3 args are for < 0x40, ignored for >= 0x40 */
perflvl->timing = perflvl->timing =
nouveau_perf_timing(dev, &P, nouveau_perf_timing(dev, &P,

View file

@ -64,18 +64,26 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
if (perflvl == pm->cur) if (perflvl == pm->cur)
return 0; return 0;
if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) { if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
ret = pm->voltage_set(dev, perflvl->voltage); ret = pm->voltage_set(dev, perflvl->volt_min);
if (ret) { if (ret) {
NV_ERROR(dev, "voltage_set %d failed: %d\n", NV_ERROR(dev, "voltage_set %d failed: %d\n",
perflvl->voltage, ret); perflvl->volt_min, ret);
} }
} }
if (pm->clocks_pre) {
void *state = pm->clocks_pre(dev, perflvl);
if (IS_ERR(state))
return PTR_ERR(state);
pm->clocks_set(dev, state);
} else
if (pm->clock_set) {
nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core); nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader); nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory); nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05); nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
}
pm->cur = perflvl; pm->cur = perflvl;
return 0; return 0;
@ -92,9 +100,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
if (nouveau_perflvl_wr != 7777) if (nouveau_perflvl_wr != 7777)
return -EPERM; return -EPERM;
if (!pm->clock_set)
return -EINVAL;
if (!strncmp(profile, "boot", 4)) if (!strncmp(profile, "boot", 4))
perflvl = &pm->boot; perflvl = &pm->boot;
else { else {
@ -123,11 +128,14 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
int ret; int ret;
if (!pm->clock_get)
return -EINVAL;
memset(perflvl, 0, sizeof(*perflvl)); memset(perflvl, 0, sizeof(*perflvl));
if (pm->clocks_get) {
ret = pm->clocks_get(dev, perflvl);
if (ret)
return ret;
} else
if (pm->clock_get) {
ret = pm->clock_get(dev, PLL_CORE); ret = pm->clock_get(dev, PLL_CORE);
if (ret > 0) if (ret > 0)
perflvl->core = ret; perflvl->core = ret;
@ -143,11 +151,14 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
ret = pm->clock_get(dev, PLL_UNK05); ret = pm->clock_get(dev, PLL_UNK05);
if (ret > 0) if (ret > 0)
perflvl->unk05 = ret; perflvl->unk05 = ret;
}
if (pm->voltage.supported && pm->voltage_get) { if (pm->voltage.supported && pm->voltage_get) {
ret = pm->voltage_get(dev); ret = pm->voltage_get(dev);
if (ret > 0) if (ret > 0) {
perflvl->voltage = ret; perflvl->volt_min = ret;
perflvl->volt_max = ret;
}
} }
return 0; return 0;
@ -156,7 +167,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
static void static void
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len) nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
{ {
char c[16], s[16], v[16], f[16], t[16]; char c[16], s[16], v[32], f[16], t[16], m[16];
c[0] = '\0'; c[0] = '\0';
if (perflvl->core) if (perflvl->core)
@ -166,9 +177,19 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->shader) if (perflvl->shader)
snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000); snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
m[0] = '\0';
if (perflvl->memory)
snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
v[0] = '\0'; v[0] = '\0';
if (perflvl->voltage) if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10); snprintf(v, sizeof(v), " voltage %dmV-%dmV",
perflvl->volt_min / 1000, perflvl->volt_max / 1000);
} else
if (perflvl->volt_min) {
snprintf(v, sizeof(v), " voltage %dmV",
perflvl->volt_min / 1000);
}
f[0] = '\0'; f[0] = '\0';
if (perflvl->fanspeed) if (perflvl->fanspeed)
@ -178,8 +199,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
if (perflvl->timing) if (perflvl->timing)
snprintf(t, sizeof(t), " timing %d", perflvl->timing->id); snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000, snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
c, s, v, f, t);
} }
static ssize_t static ssize_t
@ -190,7 +210,7 @@ nouveau_pm_get_perflvl_info(struct device *d,
char *ptr = buf; char *ptr = buf;
int len = PAGE_SIZE; int len = PAGE_SIZE;
snprintf(ptr, len, "%d: ", perflvl->id); snprintf(ptr, len, "%d:", perflvl->id);
ptr += strlen(buf); ptr += strlen(buf);
len -= strlen(buf); len -= strlen(buf);
@ -211,9 +231,9 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
if (!pm->cur) if (!pm->cur)
snprintf(ptr, len, "setting: boot\n"); snprintf(ptr, len, "setting: boot\n");
else if (pm->cur == &pm->boot) else if (pm->cur == &pm->boot)
snprintf(ptr, len, "setting: boot\nc: "); snprintf(ptr, len, "setting: boot\nc:");
else else
snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id); snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
ptr += strlen(buf); ptr += strlen(buf);
len -= strlen(buf); len -= strlen(buf);
@ -292,7 +312,7 @@ nouveau_sysfs_fini(struct drm_device *dev)
} }
} }
#ifdef CONFIG_HWMON #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
static ssize_t static ssize_t
nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf) nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
{ {
@ -409,7 +429,7 @@ static const struct attribute_group hwmon_attrgroup = {
static int static int
nouveau_hwmon_init(struct drm_device *dev) nouveau_hwmon_init(struct drm_device *dev)
{ {
#ifdef CONFIG_HWMON #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct device *hwmon_dev; struct device *hwmon_dev;
@ -442,7 +462,7 @@ nouveau_hwmon_init(struct drm_device *dev)
static void static void
nouveau_hwmon_fini(struct drm_device *dev) nouveau_hwmon_fini(struct drm_device *dev)
{ {
#ifdef CONFIG_HWMON #if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm; struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
@ -488,7 +508,7 @@ nouveau_pm_init(struct drm_device *dev)
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl); NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
for (i = 0; i < pm->nr_perflvl; i++) { for (i = 0; i < pm->nr_perflvl; i++) {
nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info)); nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info); NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
} }
/* determine current ("boot") performance level */ /* determine current ("boot") performance level */
@ -498,7 +518,7 @@ nouveau_pm_init(struct drm_device *dev)
pm->cur = &pm->boot; pm->cur = &pm->boot;
nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info)); nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
NV_INFO(dev, "c: %s", info); NV_INFO(dev, "c:%s", info);
} }
/* switch performance levels now if requested */ /* switch performance levels now if requested */

View file

@ -52,6 +52,11 @@ void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
u32 id, int khz); u32 id, int khz);
void nv04_pm_clock_set(struct drm_device *, void *); void nv04_pm_clock_set(struct drm_device *, void *);
/* nv40_pm.c */
int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
void nv40_pm_clocks_set(struct drm_device *, void *);
/* nv50_pm.c */ /* nv50_pm.c */
int nv50_pm_clock_get(struct drm_device *, u32 id); int nv50_pm_clock_get(struct drm_device *, u32 id);
void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
@ -59,10 +64,12 @@ void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
void nv50_pm_clock_set(struct drm_device *, void *); void nv50_pm_clock_set(struct drm_device *, void *);
/* nva3_pm.c */ /* nva3_pm.c */
int nva3_pm_clock_get(struct drm_device *, u32 id); int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
void *nva3_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *, void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
u32 id, int khz); void nva3_pm_clocks_set(struct drm_device *, void *);
void nva3_pm_clock_set(struct drm_device *, void *);
/* nvc0_pm.c */
int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
/* nouveau_temp.c */ /* nouveau_temp.c */
void nouveau_temp_init(struct drm_device *dev); void nouveau_temp_init(struct drm_device *dev);

View file

@ -826,9 +826,12 @@
#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000 #define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000
#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000 #define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000
#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000 #define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000
#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084 #define NV50_PDISP_SOR_PWM_DIV(i) (0x0061c080 + (i) * 0x800)
#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000 #define NV50_PDISP_SOR_PWM_CTL(i) (0x0061c084 + (i) * 0x800)
#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff #define NV50_PDISP_SOR_PWM_CTL_NEW 0x80000000
#define NVA3_PDISP_SOR_PWM_CTL_UNK 0x40000000
#define NV50_PDISP_SOR_PWM_CTL_VAL 0x000007ff
#define NVA3_PDISP_SOR_PWM_CTL_VAL 0x00ffffff
#define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_CTRL_ENABLED 0x00000001 #define NV50_SOR_DP_CTRL_ENABLED 0x00000001
#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000 #define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
@ -843,7 +846,7 @@
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000 #define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80) #define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000) #define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)

View file

@ -12,8 +12,8 @@ struct nouveau_sgdma_be {
struct drm_device *dev; struct drm_device *dev;
dma_addr_t *pages; dma_addr_t *pages;
bool *ttm_alloced;
unsigned nr_pages; unsigned nr_pages;
bool unmap_pages;
u64 offset; u64 offset;
bool bound; bool bound;
@ -26,43 +26,28 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
{ {
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev; struct drm_device *dev = nvbe->dev;
int i;
NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages); NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
if (nvbe->pages) nvbe->pages = dma_addrs;
return -EINVAL; nvbe->nr_pages = num_pages;
nvbe->unmap_pages = true;
nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL); /* this code path isn't called and is incorrect anyways */
if (!nvbe->pages) if (0) { /* dma_addrs[0] != DMA_ERROR_CODE) { */
return -ENOMEM; nvbe->unmap_pages = false;
return 0;
nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL);
if (!nvbe->ttm_alloced) {
kfree(nvbe->pages);
nvbe->pages = NULL;
return -ENOMEM;
} }
nvbe->nr_pages = 0; for (i = 0; i < num_pages; i++) {
while (num_pages--) { nvbe->pages[i] = pci_map_page(dev->pdev, pages[i], 0,
/* this code path isn't called and is incorrect anyways */
if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/
nvbe->pages[nvbe->nr_pages] =
dma_addrs[nvbe->nr_pages];
nvbe->ttm_alloced[nvbe->nr_pages] = true;
} else {
nvbe->pages[nvbe->nr_pages] =
pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(dev->pdev, if (pci_dma_mapping_error(dev->pdev, nvbe->pages[i])) {
nvbe->pages[nvbe->nr_pages])) { nvbe->nr_pages = --i;
be->func->clear(be); be->func->clear(be);
return -EFAULT; return -EFAULT;
} }
nvbe->ttm_alloced[nvbe->nr_pages] = false;
}
nvbe->nr_pages++;
} }
return 0; return 0;
@ -72,25 +57,16 @@ static void
nouveau_sgdma_clear(struct ttm_backend *be) nouveau_sgdma_clear(struct ttm_backend *be)
{ {
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be; struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev; struct drm_device *dev = nvbe->dev;
if (nvbe && nvbe->pages) {
dev = nvbe->dev;
NV_DEBUG(dev, "\n");
if (nvbe->bound) if (nvbe->bound)
be->func->unbind(be); be->func->unbind(be);
if (nvbe->unmap_pages) {
while (nvbe->nr_pages--) { while (nvbe->nr_pages--) {
if (!nvbe->ttm_alloced[nvbe->nr_pages])
pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages], pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL); PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
} }
kfree(nvbe->pages);
kfree(nvbe->ttm_alloced);
nvbe->pages = NULL;
nvbe->ttm_alloced = NULL;
nvbe->nr_pages = 0;
} }
} }

View file

@ -286,9 +286,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->gpio.get = nv10_gpio_get; engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set; engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL; engine->gpio.irq_enable = NULL;
engine->pm.clock_get = nv04_pm_clock_get; engine->pm.clocks_get = nv40_pm_clocks_get;
engine->pm.clock_pre = nv04_pm_clock_pre; engine->pm.clocks_pre = nv40_pm_clocks_pre;
engine->pm.clock_set = nv04_pm_clock_set; engine->pm.clocks_set = nv40_pm_clocks_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set; engine->pm.voltage_set = nouveau_voltage_gpio_set;
engine->pm.temp_get = nv40_temp_get; engine->pm.temp_get = nv40_temp_get;
@ -299,7 +299,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
case 0x50: case 0x50:
case 0x80: /* gotta love NVIDIA's consistency.. */ case 0x80: /* gotta love NVIDIA's consistency.. */
case 0x90: case 0x90:
case 0xA0: case 0xa0:
engine->instmem.init = nv50_instmem_init; engine->instmem.init = nv50_instmem_init;
engine->instmem.takedown = nv50_instmem_takedown; engine->instmem.takedown = nv50_instmem_takedown;
engine->instmem.suspend = nv50_instmem_suspend; engine->instmem.suspend = nv50_instmem_suspend;
@ -359,9 +359,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.clock_set = nv50_pm_clock_set; engine->pm.clock_set = nv50_pm_clock_set;
break; break;
default: default:
engine->pm.clock_get = nva3_pm_clock_get; engine->pm.clocks_get = nva3_pm_clocks_get;
engine->pm.clock_pre = nva3_pm_clock_pre; engine->pm.clocks_pre = nva3_pm_clocks_pre;
engine->pm.clock_set = nva3_pm_clock_set; engine->pm.clocks_set = nva3_pm_clocks_set;
break; break;
} }
engine->pm.voltage_get = nouveau_voltage_gpio_get; engine->pm.voltage_get = nouveau_voltage_gpio_get;
@ -376,7 +376,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->vram.put = nv50_vram_del; engine->vram.put = nv50_vram_del;
engine->vram.flags_valid = nv50_vram_flags_valid; engine->vram.flags_valid = nv50_vram_flags_valid;
break; break;
case 0xC0: case 0xc0:
engine->instmem.init = nvc0_instmem_init; engine->instmem.init = nvc0_instmem_init;
engine->instmem.takedown = nvc0_instmem_takedown; engine->instmem.takedown = nvc0_instmem_takedown;
engine->instmem.suspend = nvc0_instmem_suspend; engine->instmem.suspend = nvc0_instmem_suspend;
@ -422,12 +422,73 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->vram.put = nv50_vram_del; engine->vram.put = nv50_vram_del;
engine->vram.flags_valid = nvc0_vram_flags_valid; engine->vram.flags_valid = nvc0_vram_flags_valid;
engine->pm.temp_get = nv84_temp_get; engine->pm.temp_get = nv84_temp_get;
engine->pm.clocks_get = nvc0_pm_clocks_get;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
break;
case 0xd0:
engine->instmem.init = nvc0_instmem_init;
engine->instmem.takedown = nvc0_instmem_takedown;
engine->instmem.suspend = nvc0_instmem_suspend;
engine->instmem.resume = nvc0_instmem_resume;
engine->instmem.get = nv50_instmem_get;
engine->instmem.put = nv50_instmem_put;
engine->instmem.map = nv50_instmem_map;
engine->instmem.unmap = nv50_instmem_unmap;
engine->instmem.flush = nv84_instmem_flush;
engine->mc.init = nv50_mc_init;
engine->mc.takedown = nv50_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nvc0_fb_init;
engine->fb.takedown = nvc0_fb_takedown;
engine->fifo.channels = 128;
engine->fifo.init = nvc0_fifo_init;
engine->fifo.takedown = nvc0_fifo_takedown;
engine->fifo.disable = nvc0_fifo_disable;
engine->fifo.enable = nvc0_fifo_enable;
engine->fifo.reassign = nvc0_fifo_reassign;
engine->fifo.channel_id = nvc0_fifo_channel_id;
engine->fifo.create_context = nvc0_fifo_create_context;
engine->fifo.destroy_context = nvc0_fifo_destroy_context;
engine->fifo.load_context = nvc0_fifo_load_context;
engine->fifo.unload_context = nvc0_fifo_unload_context;
engine->display.early_init = nouveau_stub_init;
engine->display.late_takedown = nouveau_stub_takedown;
engine->display.create = nvd0_display_create;
engine->display.init = nvd0_display_init;
engine->display.destroy = nvd0_display_destroy;
engine->gpio.init = nv50_gpio_init;
engine->gpio.takedown = nouveau_stub_takedown;
engine->gpio.get = nvd0_gpio_get;
engine->gpio.set = nvd0_gpio_set;
engine->gpio.irq_register = nv50_gpio_irq_register;
engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
engine->vram.init = nvc0_vram_init;
engine->vram.takedown = nv50_vram_fini;
engine->vram.get = nvc0_vram_new;
engine->vram.put = nv50_vram_del;
engine->vram.flags_valid = nvc0_vram_flags_valid;
engine->pm.clocks_get = nvc0_pm_clocks_get;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
break; break;
default: default:
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset); NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
return 1; return 1;
} }
/* headless mode */
if (nouveau_modeset == 2) {
engine->display.early_init = nouveau_stub_init;
engine->display.late_takedown = nouveau_stub_takedown;
engine->display.create = nouveau_stub_init;
engine->display.init = nouveau_stub_init;
engine->display.destroy = nouveau_stub_takedown;
}
return 0; return 0;
} }
@ -449,21 +510,6 @@ nouveau_vga_set_decode(void *priv, bool state)
return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
} }
static int
nouveau_card_init_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int ret;
ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
NvDmaFB, NvDmaTT);
if (ret)
return ret;
mutex_unlock(&dev_priv->channel->mutex);
return 0;
}
static void nouveau_switcheroo_set_state(struct pci_dev *pdev, static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
enum vga_switcheroo_state state) enum vga_switcheroo_state state)
{ {
@ -630,8 +676,11 @@ nouveau_card_init(struct drm_device *dev)
break; break;
} }
if (dev_priv->card_type == NV_40) if (dev_priv->card_type == NV_40 ||
nv40_mpeg_create(dev); dev_priv->chipset == 0x31 ||
dev_priv->chipset == 0x34 ||
dev_priv->chipset == 0x36)
nv31_mpeg_create(dev);
else else
if (dev_priv->card_type == NV_50 && if (dev_priv->card_type == NV_50 &&
(dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0)) (dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0))
@ -651,41 +700,69 @@ nouveau_card_init(struct drm_device *dev)
goto out_engine; goto out_engine;
} }
ret = engine->display.create(dev); ret = nouveau_irq_init(dev);
if (ret) if (ret)
goto out_fifo; goto out_fifo;
ret = drm_vblank_init(dev, nv_two_heads(dev) ? 2 : 1); /* initialise general modesetting */
if (ret) drm_mode_config_init(dev);
goto out_vblank; drm_mode_create_scaling_mode_property(dev);
drm_mode_create_dithering_property(dev);
dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
dev->mode_config.fb_base = pci_resource_start(dev->pdev, 1);
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
if (dev_priv->card_type < NV_10) {
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
} else
if (dev_priv->card_type < NV_50) {
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
} else {
dev->mode_config.max_width = 8192;
dev->mode_config.max_height = 8192;
}
ret = nouveau_irq_init(dev); ret = engine->display.create(dev);
if (ret) if (ret)
goto out_vblank; goto out_irq;
/* what about PVIDEO/PCRTC/PRAMDAC etc? */ nouveau_backlight_init(dev);
if (dev_priv->eng[NVOBJ_ENGINE_GR]) { if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
ret = nouveau_fence_init(dev); ret = nouveau_fence_init(dev);
if (ret) if (ret)
goto out_irq; goto out_disp;
ret = nouveau_card_init_channel(dev); ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
NvDmaFB, NvDmaTT);
if (ret) if (ret)
goto out_fence; goto out_fence;
mutex_unlock(&dev_priv->channel->mutex);
} }
if (dev->mode_config.num_crtc) {
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret)
goto out_chan;
nouveau_fbcon_init(dev); nouveau_fbcon_init(dev);
drm_kms_helper_poll_init(dev); drm_kms_helper_poll_init(dev);
}
return 0; return 0;
out_chan:
nouveau_channel_put_unlocked(&dev_priv->channel);
out_fence: out_fence:
nouveau_fence_fini(dev); nouveau_fence_fini(dev);
out_disp:
nouveau_backlight_exit(dev);
engine->display.destroy(dev);
out_irq: out_irq:
nouveau_irq_fini(dev); nouveau_irq_fini(dev);
out_vblank:
drm_vblank_cleanup(dev);
engine->display.destroy(dev);
out_fifo: out_fifo:
if (!dev_priv->noaccel) if (!dev_priv->noaccel)
engine->fifo.takedown(dev); engine->fifo.takedown(dev);
@ -732,15 +809,20 @@ static void nouveau_card_takedown(struct drm_device *dev)
struct nouveau_engine *engine = &dev_priv->engine; struct nouveau_engine *engine = &dev_priv->engine;
int e; int e;
if (dev->mode_config.num_crtc) {
drm_kms_helper_poll_fini(dev); drm_kms_helper_poll_fini(dev);
nouveau_fbcon_fini(dev); nouveau_fbcon_fini(dev);
drm_vblank_cleanup(dev);
}
if (dev_priv->channel) { if (dev_priv->channel) {
nouveau_channel_put_unlocked(&dev_priv->channel); nouveau_channel_put_unlocked(&dev_priv->channel);
nouveau_fence_fini(dev); nouveau_fence_fini(dev);
} }
nouveau_backlight_exit(dev);
engine->display.destroy(dev); engine->display.destroy(dev);
drm_mode_config_cleanup(dev);
if (!dev_priv->noaccel) { if (!dev_priv->noaccel) {
engine->fifo.takedown(dev); engine->fifo.takedown(dev);
@ -774,7 +856,6 @@ static void nouveau_card_takedown(struct drm_device *dev)
engine->vram.takedown(dev); engine->vram.takedown(dev);
nouveau_irq_fini(dev); nouveau_irq_fini(dev);
drm_vblank_cleanup(dev);
nouveau_pm_fini(dev); nouveau_pm_fini(dev);
nouveau_bios_takedown(dev); nouveau_bios_takedown(dev);
@ -907,7 +988,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev)
int nouveau_load(struct drm_device *dev, unsigned long flags) int nouveau_load(struct drm_device *dev, unsigned long flags)
{ {
struct drm_nouveau_private *dev_priv; struct drm_nouveau_private *dev_priv;
uint32_t reg0; uint32_t reg0, strap;
resource_size_t mmio_start_offs; resource_size_t mmio_start_offs;
int ret; int ret;
@ -951,13 +1032,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
/* Time to determine the card architecture */ /* Time to determine the card architecture */
reg0 = nv_rd32(dev, NV03_PMC_BOOT_0); reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */
/* We're dealing with >=NV10 */ /* We're dealing with >=NV10 */
if ((reg0 & 0x0f000000) > 0) { if ((reg0 & 0x0f000000) > 0) {
/* Bit 27-20 contain the architecture in hex */ /* Bit 27-20 contain the architecture in hex */
dev_priv->chipset = (reg0 & 0xff00000) >> 20; dev_priv->chipset = (reg0 & 0xff00000) >> 20;
dev_priv->stepping = (reg0 & 0xff);
/* NV04 or NV05 */ /* NV04 or NV05 */
} else if ((reg0 & 0xff00fff0) == 0x20004000) { } else if ((reg0 & 0xff00fff0) == 0x20004000) {
if (reg0 & 0x00f00000) if (reg0 & 0x00f00000)
@ -987,6 +1066,9 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
case 0xc0: case 0xc0:
dev_priv->card_type = NV_C0; dev_priv->card_type = NV_C0;
break; break;
case 0xd0:
dev_priv->card_type = NV_D0;
break;
default: default:
NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0); NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
ret = -EINVAL; ret = -EINVAL;
@ -996,6 +1078,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n", NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
dev_priv->card_type, reg0); dev_priv->card_type, reg0);
/* determine frequency of timing crystal */
strap = nv_rd32(dev, 0x101000);
if ( dev_priv->chipset < 0x17 ||
(dev_priv->chipset >= 0x20 && dev_priv->chipset <= 0x25))
strap &= 0x00000040;
else
strap &= 0x00400040;
switch (strap) {
case 0x00000000: dev_priv->crystal = 13500; break;
case 0x00000040: dev_priv->crystal = 14318; break;
case 0x00400000: dev_priv->crystal = 27000; break;
case 0x00400040: dev_priv->crystal = 25000; break;
}
NV_DEBUG(dev, "crystal freq: %dKHz\n", dev_priv->crystal);
/* Determine whether we'll attempt acceleration or not, some /* Determine whether we'll attempt acceleration or not, some
* cards are disabled by default here due to them being known * cards are disabled by default here due to them being known
* non-functional, or never been tested due to lack of hw. * non-functional, or never been tested due to lack of hw.
@ -1030,7 +1129,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
ioremap(pci_resource_start(dev->pdev, ramin_bar), ioremap(pci_resource_start(dev->pdev, ramin_bar),
dev_priv->ramin_size); dev_priv->ramin_size);
if (!dev_priv->ramin) { if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to PRAMIN BAR"); NV_ERROR(dev, "Failed to map PRAMIN BAR\n");
ret = -ENOMEM; ret = -ENOMEM;
goto err_mmio; goto err_mmio;
} }
@ -1130,7 +1229,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
getparam->value = 1; getparam->value = 1;
break; break;
case NOUVEAU_GETPARAM_HAS_PAGEFLIP: case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
getparam->value = 1; getparam->value = dev_priv->card_type < NV_D0;
break; break;
case NOUVEAU_GETPARAM_GRAPH_UNITS: case NOUVEAU_GETPARAM_GRAPH_UNITS:
/* NV40 and NV50 versions are quite different, but register /* NV40 and NV50 versions are quite different, but register
@ -1198,6 +1297,23 @@ nouveau_wait_ne(struct drm_device *dev, uint64_t timeout,
return false; return false;
} }
/* Wait until cond(data) == true, up until timeout has hit */
bool
nouveau_wait_cb(struct drm_device *dev, u64 timeout,
bool (*cond)(void *), void *data)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
u64 start = ptimer->read(dev);
do {
if (cond(data) == true)
return true;
} while (ptimer->read(dev) - start < timeout);
return false;
}
/* Waits for PGRAPH to go completely idle */ /* Waits for PGRAPH to go completely idle */
bool nouveau_wait_for_idle(struct drm_device *dev) bool nouveau_wait_for_idle(struct drm_device *dev)
{ {

View file

@ -172,9 +172,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde)
vm->map_pgt(vpgd->obj, pde, vpgt->obj); vm->map_pgt(vpgd->obj, pde, vpgt->obj);
} }
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
nouveau_gpuobj_ref(NULL, &pgt); nouveau_gpuobj_ref(NULL, &pgt);
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
} }
} }
@ -191,18 +191,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type)
pgt_size = (1 << (vm->pgt_bits + 12)) >> type; pgt_size = (1 << (vm->pgt_bits + 12)) >> type;
pgt_size *= 8; pgt_size *= 8;
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000, ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000,
NVOBJ_FLAG_ZERO_ALLOC, &pgt); NVOBJ_FLAG_ZERO_ALLOC, &pgt);
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
if (unlikely(ret)) if (unlikely(ret))
return ret; return ret;
/* someone beat us to filling the PDE while we didn't have the lock */ /* someone beat us to filling the PDE while we didn't have the lock */
if (unlikely(vpgt->refcount[big]++)) { if (unlikely(vpgt->refcount[big]++)) {
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
nouveau_gpuobj_ref(NULL, &pgt); nouveau_gpuobj_ref(NULL, &pgt);
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
return 0; return 0;
} }
@ -223,10 +223,10 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
u32 fpde, lpde, pde; u32 fpde, lpde, pde;
int ret; int ret;
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
ret = nouveau_mm_get(vm->mm, page_shift, msize, 0, align, &vma->node); ret = nouveau_mm_get(&vm->mm, page_shift, msize, 0, align, &vma->node);
if (unlikely(ret != 0)) { if (unlikely(ret != 0)) {
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
return ret; return ret;
} }
@ -245,13 +245,13 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
if (ret) { if (ret) {
if (pde != fpde) if (pde != fpde)
nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1); nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);
nouveau_mm_put(vm->mm, vma->node); nouveau_mm_put(&vm->mm, vma->node);
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
vma->node = NULL; vma->node = NULL;
return ret; return ret;
} }
} }
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
vma->vm = vm; vma->vm = vm;
vma->offset = (u64)vma->node->offset << 12; vma->offset = (u64)vma->node->offset << 12;
@ -270,11 +270,11 @@ nouveau_vm_put(struct nouveau_vma *vma)
fpde = (vma->node->offset >> vm->pgt_bits); fpde = (vma->node->offset >> vm->pgt_bits);
lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits; lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits;
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde); nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde);
nouveau_mm_put(vm->mm, vma->node); nouveau_mm_put(&vm->mm, vma->node);
vma->node = NULL; vma->node = NULL;
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
} }
int int
@ -306,7 +306,7 @@ nouveau_vm_new(struct drm_device *dev, u64 offset, u64 length, u64 mm_offset,
block = length; block = length;
} else } else
if (dev_priv->card_type == NV_C0) { if (dev_priv->card_type >= NV_C0) {
vm->map_pgt = nvc0_vm_map_pgt; vm->map_pgt = nvc0_vm_map_pgt;
vm->map = nvc0_vm_map; vm->map = nvc0_vm_map;
vm->map_sg = nvc0_vm_map_sg; vm->map_sg = nvc0_vm_map_sg;
@ -360,11 +360,11 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)
nouveau_gpuobj_ref(pgd, &vpgd->obj); nouveau_gpuobj_ref(pgd, &vpgd->obj);
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
for (i = vm->fpde; i <= vm->lpde; i++) for (i = vm->fpde; i <= vm->lpde; i++)
vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj); vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj);
list_add(&vpgd->head, &vm->pgd_list); list_add(&vpgd->head, &vm->pgd_list);
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
return 0; return 0;
} }
@ -377,7 +377,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
if (!mpgd) if (!mpgd)
return; return;
mutex_lock(&vm->mm->mutex); mutex_lock(&vm->mm.mutex);
list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) { list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
if (vpgd->obj == mpgd) { if (vpgd->obj == mpgd) {
pgd = vpgd->obj; pgd = vpgd->obj;
@ -386,7 +386,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
break; break;
} }
} }
mutex_unlock(&vm->mm->mutex); mutex_unlock(&vm->mm.mutex);
nouveau_gpuobj_ref(NULL, &pgd); nouveau_gpuobj_ref(NULL, &pgd);
} }

View file

@ -51,7 +51,7 @@ struct nouveau_vma {
struct nouveau_vm { struct nouveau_vm {
struct drm_device *dev; struct drm_device *dev;
struct nouveau_mm *mm; struct nouveau_mm mm;
int refcount; int refcount;
struct list_head pgd_list; struct list_head pgd_list;

View file

@ -27,7 +27,7 @@
#include "nouveau_drv.h" #include "nouveau_drv.h"
#include "nouveau_pm.h" #include "nouveau_pm.h"
static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a }; static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 };
static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]); static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);
int int
@ -170,6 +170,13 @@ nouveau_volt_init(struct drm_device *dev)
*/ */
vidshift = 2; vidshift = 2;
break; break;
case 0x40:
headerlen = volt[1];
recordlen = volt[2];
entries = volt[3]; /* not a clue what the entries are for.. */
vidmask = volt[11]; /* guess.. */
vidshift = 0;
break;
default: default:
NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]); NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]);
return; return;
@ -197,16 +204,37 @@ nouveau_volt_init(struct drm_device *dev)
} }
/* parse vbios entries into common format */ /* parse vbios entries into common format */
voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL); voltage->version = volt[0];
if (voltage->version < 0x40) {
voltage->nr_level = entries;
voltage->level =
kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
if (!voltage->level) if (!voltage->level)
return; return;
entry = volt + headerlen; entry = volt + headerlen;
for (i = 0; i < entries; i++, entry += recordlen) { for (i = 0; i < entries; i++, entry += recordlen) {
voltage->level[i].voltage = entry[0]; voltage->level[i].voltage = entry[0] * 10000;
voltage->level[i].vid = entry[1] >> vidshift; voltage->level[i].vid = entry[1] >> vidshift;
} }
voltage->nr_level = entries; } else {
u32 volt_uv = ROM32(volt[4]);
s16 step_uv = ROM16(volt[8]);
u8 vid;
voltage->nr_level = voltage->vid_mask + 1;
voltage->level = kcalloc(voltage->nr_level,
sizeof(*voltage->level), GFP_KERNEL);
if (!voltage->level)
return;
for (vid = 0; vid <= voltage->vid_mask; vid++) {
voltage->level[vid].voltage = volt_uv;
voltage->level[vid].vid = vid;
volt_uv += step_uv;
}
}
voltage->supported = true; voltage->supported = true;
} }

View file

@ -126,27 +126,6 @@ nv04_display_create(struct drm_device *dev)
nouveau_hw_save_vga_fonts(dev, 1); nouveau_hw_save_vga_fonts(dev, 1);
drm_mode_config_init(dev);
drm_mode_create_scaling_mode_property(dev);
drm_mode_create_dithering_property(dev);
dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
switch (dev_priv->card_type) {
case NV_04:
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
break;
default:
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
break;
}
dev->mode_config.fb_base = dev_priv->fb_phys;
nv04_crtc_create(dev, 0); nv04_crtc_create(dev, 0);
if (nv_two_heads(dev)) if (nv_two_heads(dev))
nv04_crtc_create(dev, 1); nv04_crtc_create(dev, 1);
@ -235,8 +214,6 @@ nv04_display_destroy(struct drm_device *dev)
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
crtc->funcs->restore(crtc); crtc->funcs->restore(crtc);
drm_mode_config_cleanup(dev);
nouveau_hw_save_vga_fonts(dev, 0); nouveau_hw_save_vga_fonts(dev, 0);
} }

View file

@ -68,6 +68,7 @@ void
nv04_pm_clock_set(struct drm_device *dev, void *pre_state) nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
struct nv04_pm_state *state = pre_state; struct nv04_pm_state *state = pre_state;
u32 reg = state->pll.reg; u32 reg = state->pll.reg;
@ -85,6 +86,9 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
nv_mask(dev, 0x1002c0, 0, 1 << 8); nv_mask(dev, 0x1002c0, 0, 1 << 8);
} }
if (reg == NV_PRAMDAC_NVPLL_COEFF)
ptimer->init(dev);
kfree(state); kfree(state);
} }

View file

@ -6,43 +6,75 @@
int int
nv04_timer_init(struct drm_device *dev) nv04_timer_init(struct drm_device *dev)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 m, n, d;
nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000); nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF); nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
/* Just use the pre-existing values when possible for now; these regs /* aim for 31.25MHz, which gives us nanosecond timestamps */
* are not written in nv (driver writer missed a /4 on the address), and d = 1000000 / 32;
* writing 8 and 3 to the correct regs breaks the timings on the LVDS
* hardware sequencing microcode. /* determine base clock for timer source */
* A correct solution (involving calculations with the GPU PLL) can if (dev_priv->chipset < 0x40) {
* be done when kernel modesetting lands n = dev_priv->engine.pm.clock_get(dev, PLL_CORE);
*/ } else
if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) || if (dev_priv->chipset == 0x40) {
!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) { /*XXX: figure this out */
nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008); n = 0;
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003); } else {
n = dev_priv->crystal;
m = 1;
while (n < (d * 2)) {
n += (n / m);
m++;
} }
nv_wr32(dev, 0x009220, m - 1);
}
if (!n) {
NV_WARN(dev, "PTIMER: unknown input clock freq\n");
if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1);
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1);
}
return 0;
}
/* reduce ratio to acceptable values */
while (((n % 5) == 0) && ((d % 5) == 0)) {
n /= 5;
d /= 5;
}
while (((n % 2) == 0) && ((d % 2) == 0)) {
n /= 2;
d /= 2;
}
while (n > 0xffff || d > 0xffff) {
n >>= 1;
d >>= 1;
}
nv_wr32(dev, NV04_PTIMER_NUMERATOR, n);
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d);
return 0; return 0;
} }
uint64_t u64
nv04_timer_read(struct drm_device *dev) nv04_timer_read(struct drm_device *dev)
{ {
uint32_t low; u32 hi, lo;
/* From kmmio dumps on nv28 this looks like how the blob does this.
* It reads the high dword twice, before and after.
* The only explanation seems to be that the 64-bit timer counter
* advances between high and low dword reads and may corrupt the
* result. Not confirmed.
*/
uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
uint32_t high1;
do { do {
high1 = high2; hi = nv_rd32(dev, NV04_PTIMER_TIME_1);
low = nv_rd32(dev, NV04_PTIMER_TIME_0); lo = nv_rd32(dev, NV04_PTIMER_TIME_0);
high2 = nv_rd32(dev, NV04_PTIMER_TIME_1); } while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1));
} while (high1 != high2);
return (((uint64_t)high2) << 32) | (uint64_t)low; return ((u64)hi << 32 | lo);
} }
void void

View file

@ -26,10 +26,32 @@
#include "nouveau_drv.h" #include "nouveau_drv.h"
#include "nouveau_ramht.h" #include "nouveau_ramht.h"
struct nv40_mpeg_engine { struct nv31_mpeg_engine {
struct nouveau_exec_engine base; struct nouveau_exec_engine base;
atomic_t refcount;
}; };
static int
nv31_mpeg_context_new(struct nouveau_channel *chan, int engine)
{
struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine);
if (!atomic_add_unless(&pmpeg->refcount, 1, 1))
return -EBUSY;
chan->engctx[engine] = (void *)0xdeadcafe;
return 0;
}
static void
nv31_mpeg_context_del(struct nouveau_channel *chan, int engine)
{
struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine);
atomic_dec(&pmpeg->refcount);
chan->engctx[engine] = NULL;
}
static int static int
nv40_mpeg_context_new(struct nouveau_channel *chan, int engine) nv40_mpeg_context_new(struct nouveau_channel *chan, int engine)
{ {
@ -81,7 +103,7 @@ nv40_mpeg_context_del(struct nouveau_channel *chan, int engine)
} }
static int static int
nv40_mpeg_object_new(struct nouveau_channel *chan, int engine, nv31_mpeg_object_new(struct nouveau_channel *chan, int engine,
u32 handle, u16 class) u32 handle, u16 class)
{ {
struct drm_device *dev = chan->dev; struct drm_device *dev = chan->dev;
@ -103,10 +125,10 @@ nv40_mpeg_object_new(struct nouveau_channel *chan, int engine,
} }
static int static int
nv40_mpeg_init(struct drm_device *dev, int engine) nv31_mpeg_init(struct drm_device *dev, int engine)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
int i; int i;
/* VPE init */ /* VPE init */
@ -121,7 +143,7 @@ nv40_mpeg_init(struct drm_device *dev, int engine)
/* PMPEG init */ /* PMPEG init */
nv_wr32(dev, 0x00b32c, 0x00000000); nv_wr32(dev, 0x00b32c, 0x00000000);
nv_wr32(dev, 0x00b314, 0x00000100); nv_wr32(dev, 0x00b314, 0x00000100);
nv_wr32(dev, 0x00b220, 0x00000044); nv_wr32(dev, 0x00b220, nv44_graph_class(dev) ? 0x00000044 : 0x00000031);
nv_wr32(dev, 0x00b300, 0x02001ec1); nv_wr32(dev, 0x00b300, 0x02001ec1);
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001); nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
@ -137,7 +159,7 @@ nv40_mpeg_init(struct drm_device *dev, int engine)
} }
static int static int
nv40_mpeg_fini(struct drm_device *dev, int engine, bool suspend) nv31_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
{ {
/*XXX: context save? */ /*XXX: context save? */
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000); nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
@ -146,7 +168,7 @@ nv40_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
} }
static int static int
nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data) nv31_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
{ {
struct drm_device *dev = chan->dev; struct drm_device *dev = chan->dev;
u32 inst = data << 4; u32 inst = data << 4;
@ -184,13 +206,17 @@ nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
} }
static int static int
nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst) nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ctx; struct nouveau_gpuobj *ctx;
unsigned long flags; unsigned long flags;
int i; int i;
/* hardcode drm channel id on nv3x, so swmthd lookup works */
if (dev_priv->card_type < NV_40)
return 0;
spin_lock_irqsave(&dev_priv->channels.lock, flags); spin_lock_irqsave(&dev_priv->channels.lock, flags);
for (i = 0; i < dev_priv->engine.fifo.channels; i++) { for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
if (!dev_priv->channels.ptr[i]) if (!dev_priv->channels.ptr[i])
@ -205,7 +231,7 @@ nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst)
} }
static void static void
nv40_vpe_set_tile_region(struct drm_device *dev, int i) nv31_vpe_set_tile_region(struct drm_device *dev, int i)
{ {
struct drm_nouveau_private *dev_priv = dev->dev_private; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i]; struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
@ -216,10 +242,10 @@ nv40_vpe_set_tile_region(struct drm_device *dev, int i)
} }
static void static void
nv40_mpeg_isr(struct drm_device *dev) nv31_mpeg_isr(struct drm_device *dev)
{ {
u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4; u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4;
u32 chid = nv40_mpeg_isr_chid(dev, inst); u32 chid = nv31_mpeg_isr_chid(dev, inst);
u32 stat = nv_rd32(dev, 0x00b100); u32 stat = nv_rd32(dev, 0x00b100);
u32 type = nv_rd32(dev, 0x00b230); u32 type = nv_rd32(dev, 0x00b230);
u32 mthd = nv_rd32(dev, 0x00b234); u32 mthd = nv_rd32(dev, 0x00b234);
@ -249,10 +275,10 @@ nv40_mpeg_isr(struct drm_device *dev)
} }
static void static void
nv40_vpe_isr(struct drm_device *dev) nv31_vpe_isr(struct drm_device *dev)
{ {
if (nv_rd32(dev, 0x00b100)) if (nv_rd32(dev, 0x00b100))
nv40_mpeg_isr(dev); nv31_mpeg_isr(dev);
if (nv_rd32(dev, 0x00b800)) { if (nv_rd32(dev, 0x00b800)) {
u32 stat = nv_rd32(dev, 0x00b800); u32 stat = nv_rd32(dev, 0x00b800);
@ -262,9 +288,9 @@ nv40_vpe_isr(struct drm_device *dev)
} }
static void static void
nv40_mpeg_destroy(struct drm_device *dev, int engine) nv31_mpeg_destroy(struct drm_device *dev, int engine)
{ {
struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine); struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
nouveau_irq_unregister(dev, 0); nouveau_irq_unregister(dev, 0);
@ -273,34 +299,41 @@ nv40_mpeg_destroy(struct drm_device *dev, int engine)
} }
int int
nv40_mpeg_create(struct drm_device *dev) nv31_mpeg_create(struct drm_device *dev)
{ {
struct nv40_mpeg_engine *pmpeg; struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv31_mpeg_engine *pmpeg;
pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL); pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL);
if (!pmpeg) if (!pmpeg)
return -ENOMEM; return -ENOMEM;
atomic_set(&pmpeg->refcount, 0);
pmpeg->base.destroy = nv40_mpeg_destroy; pmpeg->base.destroy = nv31_mpeg_destroy;
pmpeg->base.init = nv40_mpeg_init; pmpeg->base.init = nv31_mpeg_init;
pmpeg->base.fini = nv40_mpeg_fini; pmpeg->base.fini = nv31_mpeg_fini;
if (dev_priv->card_type < NV_40) {
pmpeg->base.context_new = nv31_mpeg_context_new;
pmpeg->base.context_del = nv31_mpeg_context_del;
} else {
pmpeg->base.context_new = nv40_mpeg_context_new; pmpeg->base.context_new = nv40_mpeg_context_new;
pmpeg->base.context_del = nv40_mpeg_context_del; pmpeg->base.context_del = nv40_mpeg_context_del;
pmpeg->base.object_new = nv40_mpeg_object_new; }
pmpeg->base.object_new = nv31_mpeg_object_new;
/* ISR vector, PMC_ENABLE bit, and TILE regs are shared between /* ISR vector, PMC_ENABLE bit, and TILE regs are shared between
* all VPE engines, for this driver's purposes the PMPEG engine * all VPE engines, for this driver's purposes the PMPEG engine
* will be treated as the "master" and handle the global VPE * will be treated as the "master" and handle the global VPE
* bits too * bits too
*/ */
pmpeg->base.set_tile_region = nv40_vpe_set_tile_region; pmpeg->base.set_tile_region = nv31_vpe_set_tile_region;
nouveau_irq_register(dev, 0, nv40_vpe_isr); nouveau_irq_register(dev, 0, nv31_vpe_isr);
NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base); NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base);
NVOBJ_CLASS(dev, 0x3174, MPEG); NVOBJ_CLASS(dev, 0x3174, MPEG);
NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma); NVOBJ_MTHD (dev, 0x3174, 0x0190, nv31_mpeg_mthd_dma);
NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma); NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv31_mpeg_mthd_dma);
NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma); NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv31_mpeg_mthd_dma);
#if 0 #if 0
NVOBJ_ENGINE_ADD(dev, ME, &pme->base); NVOBJ_ENGINE_ADD(dev, ME, &pme->base);

View file

@ -0,0 +1,338 @@
/*
* Copyright 2011 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_bios.h"
#include "nouveau_pm.h"
#include "nouveau_hw.h"
#define min2(a,b) ((a) < (b) ? (a) : (b))
static u32
read_pll_1(struct drm_device *dev, u32 reg)
{
u32 ctrl = nv_rd32(dev, reg + 0x00);
int P = (ctrl & 0x00070000) >> 16;
int N = (ctrl & 0x0000ff00) >> 8;
int M = (ctrl & 0x000000ff) >> 0;
u32 ref = 27000, clk = 0;
if (ctrl & 0x80000000)
clk = ref * N / M;
return clk >> P;
}
static u32
read_pll_2(struct drm_device *dev, u32 reg)
{
u32 ctrl = nv_rd32(dev, reg + 0x00);
u32 coef = nv_rd32(dev, reg + 0x04);
int N2 = (coef & 0xff000000) >> 24;
int M2 = (coef & 0x00ff0000) >> 16;
int N1 = (coef & 0x0000ff00) >> 8;
int M1 = (coef & 0x000000ff) >> 0;
int P = (ctrl & 0x00070000) >> 16;
u32 ref = 27000, clk = 0;
if (ctrl & 0x80000000)
clk = ref * N1 / M1;
if (!(ctrl & 0x00000100)) {
if (ctrl & 0x40000000)
clk = clk * N2 / M2;
}
return clk >> P;
}
static u32
read_clk(struct drm_device *dev, u32 src)
{
switch (src) {
case 3:
return read_pll_2(dev, 0x004000);
case 2:
return read_pll_1(dev, 0x004008);
default:
break;
}
return 0;
}
int
nv40_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
u32 ctrl = nv_rd32(dev, 0x00c040);
perflvl->core = read_clk(dev, (ctrl & 0x00000003) >> 0);
perflvl->shader = read_clk(dev, (ctrl & 0x00000030) >> 4);
perflvl->memory = read_pll_2(dev, 0x4020);
return 0;
}
struct nv40_pm_state {
u32 ctrl;
u32 npll_ctrl;
u32 npll_coef;
u32 spll;
u32 mpll_ctrl;
u32 mpll_coef;
};
static int
nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)
{
struct nouveau_pll_vals coef;
int ret;
ret = get_pll_limits(dev, reg, pll);
if (ret)
return ret;
if (clk < pll->vco1.maxfreq)
pll->vco2.maxfreq = 0;
ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef);
if (ret == 0)
return -ERANGE;
*N1 = coef.N1;
*M1 = coef.M1;
if (N2 && M2) {
if (pll->vco2.maxfreq) {
*N2 = coef.N2;
*M2 = coef.M2;
} else {
*N2 = 1;
*M2 = 1;
}
}
*log2P = coef.log2P;
return 0;
}
void *
nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct nv40_pm_state *info;
struct pll_lims pll;
int N1, N2, M1, M2, log2P;
int ret;
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
/* core/geometric clock */
ret = nv40_calc_pll(dev, 0x004000, &pll, perflvl->core,
&N1, &M1, &N2, &M2, &log2P);
if (ret < 0)
goto out;
if (N2 == M2) {
info->npll_ctrl = 0x80000100 | (log2P << 16);
info->npll_coef = (N1 << 8) | M1;
} else {
info->npll_ctrl = 0xc0000000 | (log2P << 16);
info->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
}
/* use the second PLL for shader/rop clock, if it differs from core */
if (perflvl->shader && perflvl->shader != perflvl->core) {
ret = nv40_calc_pll(dev, 0x004008, &pll, perflvl->shader,
&N1, &M1, NULL, NULL, &log2P);
if (ret < 0)
goto out;
info->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;
info->ctrl = 0x00000223;
} else {
info->spll = 0x00000000;
info->ctrl = 0x00000333;
}
/* memory clock */
ret = nv40_calc_pll(dev, 0x004020, &pll, perflvl->memory,
&N1, &M1, &N2, &M2, &log2P);
if (ret < 0)
goto out;
info->mpll_ctrl = 0x80000000 | (log2P << 16);
info->mpll_ctrl |= min2(pll.log2p_bias + log2P, pll.max_log2p) << 20;
if (N2 == M2) {
info->mpll_ctrl |= 0x00000100;
info->mpll_coef = (N1 << 8) | M1;
} else {
info->mpll_ctrl |= 0x40000000;
info->mpll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
}
out:
if (ret < 0) {
kfree(info);
info = ERR_PTR(ret);
}
return info;
}
static bool
nv40_pm_gr_idle(void *data)
{
struct drm_device *dev = data;
if ((nv_rd32(dev, 0x400760) & 0x000000f0) >> 4 !=
(nv_rd32(dev, 0x400760) & 0x0000000f))
return false;
if (nv_rd32(dev, 0x400700))
return false;
return true;
}
void
nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv40_pm_state *info = pre_state;
unsigned long flags;
struct bit_entry M;
u32 crtc_mask = 0;
u8 sr1[2];
int i;
/* determine which CRTCs are active, fetch VGA_SR1 for each */
for (i = 0; i < 2; i++) {
u32 vbl = nv_rd32(dev, 0x600808 + (i * 0x2000));
u32 cnt = 0;
do {
if (vbl != nv_rd32(dev, 0x600808 + (i * 0x2000))) {
nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
sr1[i] = nv_rd08(dev, 0x0c03c5 + (i * 0x2000));
if (!(sr1[i] & 0x20))
crtc_mask |= (1 << i);
break;
}
udelay(1);
} while (cnt++ < 32);
}
/* halt and idle engines */
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
nv_mask(dev, 0x002500, 0x00000001, 0x00000000);
if (!nv_wait(dev, 0x002500, 0x00000010, 0x00000000))
goto resume;
nv_mask(dev, 0x003220, 0x00000001, 0x00000000);
if (!nv_wait(dev, 0x003220, 0x00000010, 0x00000000))
goto resume;
nv_mask(dev, 0x003200, 0x00000001, 0x00000000);
nv04_fifo_cache_pull(dev, false);
if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev))
goto resume;
/* set engine clocks */
nv_mask(dev, 0x00c040, 0x00000333, 0x00000000);
nv_wr32(dev, 0x004004, info->npll_coef);
nv_mask(dev, 0x004000, 0xc0070100, info->npll_ctrl);
nv_mask(dev, 0x004008, 0xc007ffff, info->spll);
mdelay(5);
nv_mask(dev, 0x00c040, 0x00000333, info->ctrl);
/* wait for vblank start on active crtcs, disable memory access */
for (i = 0; i < 2; i++) {
if (!(crtc_mask & (1 << i)))
continue;
nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);
nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);
}
/* prepare ram for reclocking */
nv_wr32(dev, 0x1002d4, 0x00000001); /* precharge */
nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */
nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */
nv_mask(dev, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */
nv_wr32(dev, 0x1002dc, 0x00000001); /* enable self-refresh */
/* change the PLL of each memory partition */
nv_mask(dev, 0x00c040, 0x0000c000, 0x00000000);
switch (dev_priv->chipset) {
case 0x40:
case 0x45:
case 0x41:
case 0x42:
case 0x47:
nv_mask(dev, 0x004044, 0xc0771100, info->mpll_ctrl);
nv_mask(dev, 0x00402c, 0xc0771100, info->mpll_ctrl);
nv_wr32(dev, 0x004048, info->mpll_coef);
nv_wr32(dev, 0x004030, info->mpll_coef);
case 0x43:
case 0x49:
case 0x4b:
nv_mask(dev, 0x004038, 0xc0771100, info->mpll_ctrl);
nv_wr32(dev, 0x00403c, info->mpll_coef);
default:
nv_mask(dev, 0x004020, 0xc0771100, info->mpll_ctrl);
nv_wr32(dev, 0x004024, info->mpll_coef);
break;
}
udelay(100);
nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000);
/* re-enable normal operation of memory controller */
nv_wr32(dev, 0x1002dc, 0x00000000);
nv_mask(dev, 0x100210, 0x80000000, 0x80000000);
udelay(100);
/* execute memory reset script from vbios */
if (!bit_table(dev, 'M', &M))
nouveau_bios_init_exec(dev, ROM16(M.data[0]));
/* make sure we're in vblank (hopefully the same one as before), and
* then re-enable crtc memory access
*/
for (i = 0; i < 2; i++) {
if (!(crtc_mask & (1 << i)))
continue;
nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i]);
}
/* resume engines */
resume:
nv_wr32(dev, 0x003250, 0x00000001);
nv_mask(dev, 0x003220, 0x00000001, 0x00000001);
nv_wr32(dev, 0x003200, 0x00000001);
nv_wr32(dev, 0x002500, 0x00000001);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
kfree(info);
}

View file

@ -329,8 +329,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
drm_crtc_cleanup(&nv_crtc->base); drm_crtc_cleanup(&nv_crtc->base);
nv50_cursor_fini(nv_crtc);
nouveau_bo_unmap(nv_crtc->lut.nvbo); nouveau_bo_unmap(nv_crtc->lut.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
nouveau_bo_unmap(nv_crtc->cursor.nvbo); nouveau_bo_unmap(nv_crtc->cursor.nvbo);

Some files were not shown because too many files have changed in this diff Show more