Merge branch 'drm-next' of git://people.freedesktop.org/~airlied/linux
Pull drm merge (part 1) from Dave Airlie: "So first of all my tree and uapi stuff has a conflict mess, its my fault as the nouveau stuff didn't hit -next as were trying to rebase regressions out of it before we merged. Highlights: - SH mobile modesetting driver and associated helpers - some DRM core documentation - i915 modesetting rework, haswell hdmi, haswell and vlv fixes, write combined pte writing, ilk rc6 support, - nouveau: major driver rework into a hw core driver, makes features like SLI a lot saner to implement, - psb: add eDP/DP support for Cedarview - radeon: 2 layer page tables, async VM pte updates, better PLL selection for > 2 screens, better ACPI interactions The rest is general grab bag of fixes. So why part 1? well I have the exynos pull req which came in a bit late but was waiting for me to do something they shouldn't have and it looks fairly safe, and David Howells has some more header cleanups he'd like me to pull, that seem like a good idea, but I'd like to get this merge out of the way so -next dosen't get blocked." Tons of conflicts mostly due to silly include line changes, but mostly mindless. A few other small semantic conflicts too, noted from Dave's pre-merged branch. * 'drm-next' of git://people.freedesktop.org/~airlied/linux: (447 commits) drm/nv98/crypt: fix fuc build with latest envyas drm/nouveau/devinit: fixup various issues with subdev ctor/init ordering drm/nv41/vm: fix and enable use of "real" pciegart drm/nv44/vm: fix and enable use of "real" pciegart drm/nv04/dmaobj: fixup vm target handling in preparation for nv4x pcie drm/nouveau: store supported dma mask in vmmgr drm/nvc0/ibus: initial implementation of subdev drm/nouveau/therm: add support for fan-control modes drm/nouveau/hwmon: rename pwm0* to pmw1* to follow hwmon's rules drm/nouveau/therm: calculate the pwm divisor on nv50+ drm/nouveau/fan: rewrite the fan tachometer driver to get more precision, faster drm/nouveau/therm: move thermal-related functions to the therm subdev drm/nouveau/bios: parse the pwm divisor from the perf table drm/nouveau/therm: use the EXTDEV table to detect i2c monitoring devices drm/nouveau/therm: rework thermal table parsing drm/nouveau/gpio: expose the PWM/TOGGLE parameter found in the gpio vbios table drm/nouveau: fix pm initialization order drm/nouveau/bios: check that fixed tvdac gpio data is valid before using it drm/nouveau: log channel debug/error messages from client object rather than drm client drm/nouveau: have drm debugging macros build on top of core macros ...
This commit is contained in:
commit
612a9aab56
File diff suppressed because it is too large
Load Diff
|
@ -1448,8 +1448,7 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
|
|||
case ACPI_VIDEO_NOTIFY_SWITCH: /* User requested a switch,
|
||||
* most likely via hotkey. */
|
||||
acpi_bus_generate_proc_event(device, event, 0);
|
||||
if (!acpi_notifier_call_chain(device, event, 0))
|
||||
keycode = KEY_SWITCHVIDEOMODE;
|
||||
keycode = KEY_SWITCHVIDEOMODE;
|
||||
break;
|
||||
|
||||
case ACPI_VIDEO_NOTIFY_PROBE: /* User plugged in or removed a video
|
||||
|
@ -1479,8 +1478,9 @@ static void acpi_video_bus_notify(struct acpi_device *device, u32 event)
|
|||
break;
|
||||
}
|
||||
|
||||
if (event != ACPI_VIDEO_NOTIFY_SWITCH)
|
||||
acpi_notifier_call_chain(device, event, 0);
|
||||
if (acpi_notifier_call_chain(device, event, 0))
|
||||
/* Something vetoed the keypress. */
|
||||
keycode = 0;
|
||||
|
||||
if (keycode) {
|
||||
input_report_key(input, keycode, 1);
|
||||
|
|
|
@ -84,40 +84,33 @@ static struct _intel_private {
|
|||
#define IS_IRONLAKE intel_private.driver->is_ironlake
|
||||
#define HAS_PGTBL_EN intel_private.driver->has_pgtbl_enable
|
||||
|
||||
int intel_gtt_map_memory(struct page **pages, unsigned int num_entries,
|
||||
struct scatterlist **sg_list, int *num_sg)
|
||||
static int intel_gtt_map_memory(struct page **pages,
|
||||
unsigned int num_entries,
|
||||
struct sg_table *st)
|
||||
{
|
||||
struct sg_table st;
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
if (*sg_list)
|
||||
return 0; /* already mapped (for e.g. resume */
|
||||
|
||||
DBG("try mapping %lu pages\n", (unsigned long)num_entries);
|
||||
|
||||
if (sg_alloc_table(&st, num_entries, GFP_KERNEL))
|
||||
if (sg_alloc_table(st, num_entries, GFP_KERNEL))
|
||||
goto err;
|
||||
|
||||
*sg_list = sg = st.sgl;
|
||||
|
||||
for (i = 0 ; i < num_entries; i++, sg = sg_next(sg))
|
||||
for_each_sg(st->sgl, sg, num_entries, i)
|
||||
sg_set_page(sg, pages[i], PAGE_SIZE, 0);
|
||||
|
||||
*num_sg = pci_map_sg(intel_private.pcidev, *sg_list,
|
||||
num_entries, PCI_DMA_BIDIRECTIONAL);
|
||||
if (unlikely(!*num_sg))
|
||||
if (!pci_map_sg(intel_private.pcidev,
|
||||
st->sgl, st->nents, PCI_DMA_BIDIRECTIONAL))
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
sg_free_table(&st);
|
||||
sg_free_table(st);
|
||||
return -ENOMEM;
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_map_memory);
|
||||
|
||||
void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
|
||||
static void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
|
||||
{
|
||||
struct sg_table st;
|
||||
DBG("try unmapping %lu pages\n", (unsigned long)mem->page_count);
|
||||
|
@ -130,7 +123,6 @@ void intel_gtt_unmap_memory(struct scatterlist *sg_list, int num_sg)
|
|||
|
||||
sg_free_table(&st);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_unmap_memory);
|
||||
|
||||
static void intel_fake_agp_enable(struct agp_bridge_data *bridge, u32 mode)
|
||||
{
|
||||
|
@ -674,9 +666,14 @@ static int intel_gtt_init(void)
|
|||
|
||||
gtt_map_size = intel_private.base.gtt_total_entries * 4;
|
||||
|
||||
intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
|
||||
gtt_map_size);
|
||||
if (!intel_private.gtt) {
|
||||
intel_private.gtt = NULL;
|
||||
if (INTEL_GTT_GEN < 6)
|
||||
intel_private.gtt = ioremap_wc(intel_private.gtt_bus_addr,
|
||||
gtt_map_size);
|
||||
if (intel_private.gtt == NULL)
|
||||
intel_private.gtt = ioremap(intel_private.gtt_bus_addr,
|
||||
gtt_map_size);
|
||||
if (intel_private.gtt == NULL) {
|
||||
intel_private.driver->cleanup();
|
||||
iounmap(intel_private.registers);
|
||||
return -ENOMEM;
|
||||
|
@ -879,8 +876,7 @@ static bool i830_check_flags(unsigned int flags)
|
|||
return false;
|
||||
}
|
||||
|
||||
void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
|
||||
unsigned int sg_len,
|
||||
void intel_gtt_insert_sg_entries(struct sg_table *st,
|
||||
unsigned int pg_start,
|
||||
unsigned int flags)
|
||||
{
|
||||
|
@ -892,12 +888,11 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
|
|||
|
||||
/* sg may merge pages, but we have to separate
|
||||
* per-page addr for GTT */
|
||||
for_each_sg(sg_list, sg, sg_len, i) {
|
||||
for_each_sg(st->sgl, sg, st->nents, i) {
|
||||
len = sg_dma_len(sg) >> PAGE_SHIFT;
|
||||
for (m = 0; m < len; m++) {
|
||||
dma_addr_t addr = sg_dma_address(sg) + (m << PAGE_SHIFT);
|
||||
intel_private.driver->write_entry(addr,
|
||||
j, flags);
|
||||
intel_private.driver->write_entry(addr, j, flags);
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
@ -905,8 +900,10 @@ void intel_gtt_insert_sg_entries(struct scatterlist *sg_list,
|
|||
}
|
||||
EXPORT_SYMBOL(intel_gtt_insert_sg_entries);
|
||||
|
||||
void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
|
||||
struct page **pages, unsigned int flags)
|
||||
static void intel_gtt_insert_pages(unsigned int first_entry,
|
||||
unsigned int num_entries,
|
||||
struct page **pages,
|
||||
unsigned int flags)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
|
@ -917,7 +914,6 @@ void intel_gtt_insert_pages(unsigned int first_entry, unsigned int num_entries,
|
|||
}
|
||||
readl(intel_private.gtt+j-1);
|
||||
}
|
||||
EXPORT_SYMBOL(intel_gtt_insert_pages);
|
||||
|
||||
static int intel_fake_agp_insert_entries(struct agp_memory *mem,
|
||||
off_t pg_start, int type)
|
||||
|
@ -953,13 +949,15 @@ static int intel_fake_agp_insert_entries(struct agp_memory *mem,
|
|||
global_cache_flush();
|
||||
|
||||
if (intel_private.base.needs_dmar) {
|
||||
ret = intel_gtt_map_memory(mem->pages, mem->page_count,
|
||||
&mem->sg_list, &mem->num_sg);
|
||||
struct sg_table st;
|
||||
|
||||
ret = intel_gtt_map_memory(mem->pages, mem->page_count, &st);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
intel_gtt_insert_sg_entries(mem->sg_list, mem->num_sg,
|
||||
pg_start, type);
|
||||
intel_gtt_insert_sg_entries(&st, pg_start, type);
|
||||
mem->sg_list = st.sgl;
|
||||
mem->num_sg = st.nents;
|
||||
} else
|
||||
intel_gtt_insert_pages(pg_start, mem->page_count, mem->pages,
|
||||
type);
|
||||
|
|
|
@ -22,7 +22,7 @@ menuconfig DRM
|
|||
config DRM_USB
|
||||
tristate
|
||||
depends on DRM
|
||||
depends on USB_ARCH_HAS_HCD
|
||||
depends on USB_SUPPORT && USB_ARCH_HAS_HCD
|
||||
select USB
|
||||
|
||||
config DRM_KMS_HELPER
|
||||
|
@ -54,6 +54,21 @@ config DRM_TTM
|
|||
GPU memory types. Will be enabled automatically if a device driver
|
||||
uses it.
|
||||
|
||||
config DRM_GEM_CMA_HELPER
|
||||
bool
|
||||
depends on DRM
|
||||
help
|
||||
Choose this if you need the GEM CMA helper functions
|
||||
|
||||
config DRM_KMS_CMA_HELPER
|
||||
bool
|
||||
select DRM_GEM_CMA_HELPER
|
||||
select FB_SYS_FILLRECT
|
||||
select FB_SYS_COPYAREA
|
||||
select FB_SYS_IMAGEBLIT
|
||||
help
|
||||
Choose this if you need the KMS CMA helper functions
|
||||
|
||||
config DRM_TDFX
|
||||
tristate "3dfx Banshee/Voodoo3+"
|
||||
depends on DRM && PCI
|
||||
|
@ -193,3 +208,5 @@ source "drivers/gpu/drm/ast/Kconfig"
|
|||
source "drivers/gpu/drm/mgag200/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/cirrus/Kconfig"
|
||||
|
||||
source "drivers/gpu/drm/shmobile/Kconfig"
|
||||
|
|
|
@ -15,11 +15,13 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \
|
|||
drm_trace_points.o drm_global.o drm_prime.o
|
||||
|
||||
drm-$(CONFIG_COMPAT) += drm_ioc32.o
|
||||
drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o
|
||||
|
||||
drm-usb-y := drm_usb.o
|
||||
|
||||
drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
|
||||
drm_kms_helper-$(CONFIG_DRM_LOAD_EDID_FIRMWARE) += drm_edid_load.o
|
||||
drm_kms_helper-$(CONFIG_DRM_KMS_CMA_HELPER) += drm_fb_cma_helper.o
|
||||
|
||||
obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
|
||||
|
||||
|
@ -45,4 +47,5 @@ obj-$(CONFIG_DRM_EXYNOS) +=exynos/
|
|||
obj-$(CONFIG_DRM_GMA500) += gma500/
|
||||
obj-$(CONFIG_DRM_UDL) += udl/
|
||||
obj-$(CONFIG_DRM_AST) += ast/
|
||||
obj-$(CONFIG_DRM_SHMOBILE) +=shmobile/
|
||||
obj-y += i2c/
|
||||
|
|
|
@ -94,7 +94,6 @@ struct ast_private {
|
|||
struct drm_global_reference mem_global_ref;
|
||||
struct ttm_bo_global_ref bo_global_ref;
|
||||
struct ttm_bo_device bdev;
|
||||
atomic_t validate_sequence;
|
||||
} ttm;
|
||||
|
||||
struct drm_gem_object *cursor_cache;
|
||||
|
|
|
@ -582,7 +582,6 @@ static const struct drm_crtc_helper_funcs ast_crtc_helper_funcs = {
|
|||
.mode_set_base = ast_crtc_mode_set_base,
|
||||
.disable = ast_crtc_disable,
|
||||
.load_lut = ast_crtc_load_lut,
|
||||
.disable = ast_crtc_disable,
|
||||
.prepare = ast_crtc_prepare,
|
||||
.commit = ast_crtc_commit,
|
||||
|
||||
|
@ -737,6 +736,7 @@ static int ast_get_modes(struct drm_connector *connector)
|
|||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(&ast_connector->base, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
return ret;
|
||||
} else
|
||||
drm_mode_connector_update_edid_property(&ast_connector->base, NULL);
|
||||
|
|
|
@ -143,7 +143,6 @@ struct cirrus_device {
|
|||
struct drm_global_reference mem_global_ref;
|
||||
struct ttm_bo_global_ref bo_global_ref;
|
||||
struct ttm_bo_device bdev;
|
||||
atomic_t validate_sequence;
|
||||
} ttm;
|
||||
bool mm_inited;
|
||||
};
|
||||
|
|
|
@ -37,12 +37,13 @@ drm_clflush_page(struct page *page)
|
|||
{
|
||||
uint8_t *page_virtual;
|
||||
unsigned int i;
|
||||
const int size = boot_cpu_data.x86_clflush_size;
|
||||
|
||||
if (unlikely(page == NULL))
|
||||
return;
|
||||
|
||||
page_virtual = kmap_atomic(page);
|
||||
for (i = 0; i < PAGE_SIZE; i += boot_cpu_data.x86_clflush_size)
|
||||
for (i = 0; i < PAGE_SIZE; i += size)
|
||||
clflush(page_virtual + i);
|
||||
kunmap_atomic(page_virtual);
|
||||
}
|
||||
|
@ -99,6 +100,31 @@ drm_clflush_pages(struct page *pages[], unsigned long num_pages)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_clflush_pages);
|
||||
|
||||
void
|
||||
drm_clflush_sg(struct sg_table *st)
|
||||
{
|
||||
#if defined(CONFIG_X86)
|
||||
if (cpu_has_clflush) {
|
||||
struct scatterlist *sg;
|
||||
int i;
|
||||
|
||||
mb();
|
||||
for_each_sg(st->sgl, sg, st->nents, i)
|
||||
drm_clflush_page(sg_page(sg));
|
||||
mb();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (on_each_cpu(drm_clflush_ipi_handler, NULL, 1) != 0)
|
||||
printk(KERN_ERR "Timed out waiting for cache flush.\n");
|
||||
#else
|
||||
printk(KERN_ERR "Architecture has no drm_cache.c support\n");
|
||||
WARN_ON_ONCE(1);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(drm_clflush_sg);
|
||||
|
||||
void
|
||||
drm_clflush_virt_range(char *addr, unsigned long length)
|
||||
{
|
||||
|
|
|
@ -293,6 +293,8 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
|
|||
{
|
||||
int ret;
|
||||
|
||||
kref_init(&fb->refcount);
|
||||
|
||||
ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -306,6 +308,38 @@ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
|
|||
}
|
||||
EXPORT_SYMBOL(drm_framebuffer_init);
|
||||
|
||||
static void drm_framebuffer_free(struct kref *kref)
|
||||
{
|
||||
struct drm_framebuffer *fb =
|
||||
container_of(kref, struct drm_framebuffer, refcount);
|
||||
fb->funcs->destroy(fb);
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_framebuffer_unreference - unref a framebuffer
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*/
|
||||
void drm_framebuffer_unreference(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct drm_device *dev = fb->dev;
|
||||
DRM_DEBUG("FB ID: %d\n", fb->base.id);
|
||||
WARN_ON(!mutex_is_locked(&dev->mode_config.mutex));
|
||||
kref_put(&fb->refcount, drm_framebuffer_free);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_framebuffer_unreference);
|
||||
|
||||
/**
|
||||
* drm_framebuffer_reference - incr the fb refcnt
|
||||
*/
|
||||
void drm_framebuffer_reference(struct drm_framebuffer *fb)
|
||||
{
|
||||
DRM_DEBUG("FB ID: %d\n", fb->base.id);
|
||||
kref_get(&fb->refcount);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_framebuffer_reference);
|
||||
|
||||
/**
|
||||
* drm_framebuffer_cleanup - remove a framebuffer object
|
||||
* @fb: framebuffer to remove
|
||||
|
@ -317,6 +351,32 @@ EXPORT_SYMBOL(drm_framebuffer_init);
|
|||
* it, setting it to NULL.
|
||||
*/
|
||||
void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct drm_device *dev = fb->dev;
|
||||
/*
|
||||
* This could be moved to drm_framebuffer_remove(), but for
|
||||
* debugging is nice to keep around the list of fb's that are
|
||||
* no longer associated w/ a drm_file but are not unreferenced
|
||||
* yet. (i915 and omapdrm have debugfs files which will show
|
||||
* this.)
|
||||
*/
|
||||
drm_mode_object_put(dev, &fb->base);
|
||||
list_del(&fb->head);
|
||||
dev->mode_config.num_fb--;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_framebuffer_cleanup);
|
||||
|
||||
/**
|
||||
* drm_framebuffer_remove - remove and unreference a framebuffer object
|
||||
* @fb: framebuffer to remove
|
||||
*
|
||||
* LOCKING:
|
||||
* Caller must hold mode config lock.
|
||||
*
|
||||
* Scans all the CRTCs and planes in @dev's mode_config. If they're
|
||||
* using @fb, removes it, setting it to NULL.
|
||||
*/
|
||||
void drm_framebuffer_remove(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct drm_device *dev = fb->dev;
|
||||
struct drm_crtc *crtc;
|
||||
|
@ -349,11 +409,11 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb)
|
|||
}
|
||||
}
|
||||
|
||||
drm_mode_object_put(dev, &fb->base);
|
||||
list_del(&fb->head);
|
||||
dev->mode_config.num_fb--;
|
||||
list_del(&fb->filp_head);
|
||||
|
||||
drm_framebuffer_unreference(fb);
|
||||
}
|
||||
EXPORT_SYMBOL(drm_framebuffer_cleanup);
|
||||
EXPORT_SYMBOL(drm_framebuffer_remove);
|
||||
|
||||
/**
|
||||
* drm_crtc_init - Initialise a new CRTC object
|
||||
|
@ -376,6 +436,7 @@ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc,
|
|||
|
||||
crtc->dev = dev;
|
||||
crtc->funcs = funcs;
|
||||
crtc->invert_dimensions = false;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
|
||||
|
@ -1030,11 +1091,7 @@ void drm_mode_config_cleanup(struct drm_device *dev)
|
|||
}
|
||||
|
||||
list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) {
|
||||
fb->funcs->destroy(fb);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
|
||||
crtc->funcs->destroy(crtc);
|
||||
drm_framebuffer_remove(fb);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list,
|
||||
|
@ -1042,6 +1099,10 @@ void drm_mode_config_cleanup(struct drm_device *dev)
|
|||
plane->funcs->destroy(plane);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) {
|
||||
crtc->funcs->destroy(crtc);
|
||||
}
|
||||
|
||||
idr_remove_all(&dev->mode_config.crtc_idr);
|
||||
idr_destroy(&dev->mode_config.crtc_idr);
|
||||
}
|
||||
|
@ -1851,6 +1912,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
|||
DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
|
||||
|
||||
if (crtc_req->mode_valid) {
|
||||
int hdisplay, vdisplay;
|
||||
/* If we have a mode we need a framebuffer. */
|
||||
/* If we pass -1, set the mode with the currently bound fb */
|
||||
if (crtc_req->fb_id == -1) {
|
||||
|
@ -1886,14 +1948,20 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
|
|||
|
||||
drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
|
||||
|
||||
if (mode->hdisplay > fb->width ||
|
||||
mode->vdisplay > fb->height ||
|
||||
crtc_req->x > fb->width - mode->hdisplay ||
|
||||
crtc_req->y > fb->height - mode->vdisplay) {
|
||||
DRM_DEBUG_KMS("Invalid CRTC viewport %ux%u+%u+%u for fb size %ux%u.\n",
|
||||
mode->hdisplay, mode->vdisplay,
|
||||
crtc_req->x, crtc_req->y,
|
||||
fb->width, fb->height);
|
||||
hdisplay = mode->hdisplay;
|
||||
vdisplay = mode->vdisplay;
|
||||
|
||||
if (crtc->invert_dimensions)
|
||||
swap(hdisplay, vdisplay);
|
||||
|
||||
if (hdisplay > fb->width ||
|
||||
vdisplay > fb->height ||
|
||||
crtc_req->x > fb->width - hdisplay ||
|
||||
crtc_req->y > fb->height - vdisplay) {
|
||||
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
|
||||
fb->width, fb->height,
|
||||
hdisplay, vdisplay, crtc_req->x, crtc_req->y,
|
||||
crtc->invert_dimensions ? " (inverted)" : "");
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
@ -2168,6 +2236,8 @@ static int format_check(const struct drm_mode_fb_cmd2 *r)
|
|||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_NV24:
|
||||
case DRM_FORMAT_NV42:
|
||||
case DRM_FORMAT_YUV410:
|
||||
case DRM_FORMAT_YVU410:
|
||||
case DRM_FORMAT_YUV411:
|
||||
|
@ -2334,11 +2404,7 @@ int drm_mode_rmfb(struct drm_device *dev,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* TODO release all crtc connected to the framebuffer */
|
||||
/* TODO unhock the destructor from the buffer object */
|
||||
|
||||
list_del(&fb->filp_head);
|
||||
fb->funcs->destroy(fb);
|
||||
drm_framebuffer_remove(fb);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
@ -2488,8 +2554,7 @@ void drm_fb_release(struct drm_file *priv)
|
|||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) {
|
||||
list_del(&fb->filp_head);
|
||||
fb->funcs->destroy(fb);
|
||||
drm_framebuffer_remove(fb);
|
||||
}
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
}
|
||||
|
@ -3488,6 +3553,7 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
|||
struct drm_framebuffer *fb;
|
||||
struct drm_pending_vblank_event *e = NULL;
|
||||
unsigned long flags;
|
||||
int hdisplay, vdisplay;
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
|
||||
|
@ -3517,14 +3583,19 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
|
|||
goto out;
|
||||
fb = obj_to_fb(obj);
|
||||
|
||||
if (crtc->mode.hdisplay > fb->width ||
|
||||
crtc->mode.vdisplay > fb->height ||
|
||||
crtc->x > fb->width - crtc->mode.hdisplay ||
|
||||
crtc->y > fb->height - crtc->mode.vdisplay) {
|
||||
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d.\n",
|
||||
fb->width, fb->height,
|
||||
crtc->mode.hdisplay, crtc->mode.vdisplay,
|
||||
crtc->x, crtc->y);
|
||||
hdisplay = crtc->mode.hdisplay;
|
||||
vdisplay = crtc->mode.vdisplay;
|
||||
|
||||
if (crtc->invert_dimensions)
|
||||
swap(hdisplay, vdisplay);
|
||||
|
||||
if (hdisplay > fb->width ||
|
||||
vdisplay > fb->height ||
|
||||
crtc->x > fb->width - hdisplay ||
|
||||
crtc->y > fb->height - vdisplay) {
|
||||
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
|
||||
fb->width, fb->height, hdisplay, vdisplay, crtc->x, crtc->y,
|
||||
crtc->invert_dimensions ? " (inverted)" : "");
|
||||
ret = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
@ -3717,6 +3788,8 @@ int drm_format_num_planes(uint32_t format)
|
|||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_NV24:
|
||||
case DRM_FORMAT_NV42:
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
|
@ -3750,6 +3823,8 @@ int drm_format_plane_cpp(uint32_t format, int plane)
|
|||
case DRM_FORMAT_NV21:
|
||||
case DRM_FORMAT_NV16:
|
||||
case DRM_FORMAT_NV61:
|
||||
case DRM_FORMAT_NV24:
|
||||
case DRM_FORMAT_NV42:
|
||||
return plane ? 2 : 1;
|
||||
case DRM_FORMAT_YUV410:
|
||||
case DRM_FORMAT_YVU410:
|
||||
|
|
|
@ -140,10 +140,10 @@ static struct drm_ioctl_desc drm_ioctls[] = {
|
|||
DRM_IOCTL_DEF(DRM_IOCTL_PRIME_HANDLE_TO_FD, drm_prime_handle_to_fd_ioctl, DRM_AUTH|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_PRIME_FD_TO_HANDLE, drm_prime_fd_to_handle_ioctl, DRM_AUTH|DRM_UNLOCKED),
|
||||
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANERESOURCES, drm_mode_getplane_res, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPLANE, drm_mode_getplane, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPLANE, drm_mode_setplane, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CURSOR, drm_mode_cursor_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETGAMMA, drm_mode_gamma_get_ioctl, DRM_UNLOCKED),
|
||||
|
@ -152,19 +152,19 @@ static struct drm_ioctl_desc drm_ioctls[] = {
|
|||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCONNECTOR, drm_mode_getconnector, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ATTACHMODE, drm_mode_attachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DETACHMODE, drm_mode_detachmode_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_MASTER | DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPERTY, drm_mode_getproperty_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETPROPERTY, drm_mode_connector_property_set_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETPROPBLOB, drm_mode_getblob_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_CREATE_DUMB, drm_mode_create_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_MAP_DUMB, drm_mode_mmap_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_DESTROY_DUMB, drm_mode_destroy_dumb_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_GETPROPERTIES, drm_mode_obj_get_properties_ioctl, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF(DRM_IOCTL_MODE_OBJ_SETPROPERTY, drm_mode_obj_set_property_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
|
||||
};
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ MODULE_PARM_DESC(edid_fixup,
|
|||
* Sanity check the EDID block (base or extension). Return 0 if the block
|
||||
* doesn't check out, or 1 if it's valid.
|
||||
*/
|
||||
bool drm_edid_block_valid(u8 *raw_edid, int block)
|
||||
bool drm_edid_block_valid(u8 *raw_edid, int block, bool print_bad_edid)
|
||||
{
|
||||
int i;
|
||||
u8 csum = 0;
|
||||
|
@ -184,7 +184,9 @@ bool drm_edid_block_valid(u8 *raw_edid, int block)
|
|||
for (i = 0; i < EDID_LENGTH; i++)
|
||||
csum += raw_edid[i];
|
||||
if (csum) {
|
||||
DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
|
||||
if (print_bad_edid) {
|
||||
DRM_ERROR("EDID checksum is invalid, remainder is %d\n", csum);
|
||||
}
|
||||
|
||||
/* allow CEA to slide through, switches mangle this */
|
||||
if (raw_edid[0] != 0x02)
|
||||
|
@ -210,7 +212,7 @@ bool drm_edid_block_valid(u8 *raw_edid, int block)
|
|||
return 1;
|
||||
|
||||
bad:
|
||||
if (raw_edid) {
|
||||
if (raw_edid && print_bad_edid) {
|
||||
printk(KERN_ERR "Raw EDID:\n");
|
||||
print_hex_dump(KERN_ERR, " \t", DUMP_PREFIX_NONE, 16, 1,
|
||||
raw_edid, EDID_LENGTH, false);
|
||||
|
@ -234,7 +236,7 @@ bool drm_edid_is_valid(struct edid *edid)
|
|||
return false;
|
||||
|
||||
for (i = 0; i <= edid->extensions; i++)
|
||||
if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i))
|
||||
if (!drm_edid_block_valid(raw + i * EDID_LENGTH, i, true))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -257,6 +259,8 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
|
|||
int block, int len)
|
||||
{
|
||||
unsigned char start = block * EDID_LENGTH;
|
||||
unsigned char segment = block >> 1;
|
||||
unsigned char xfers = segment ? 3 : 2;
|
||||
int ret, retries = 5;
|
||||
|
||||
/* The core i2c driver will automatically retry the transfer if the
|
||||
|
@ -268,6 +272,11 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
|
|||
do {
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = DDC_SEGMENT_ADDR,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = &segment,
|
||||
}, {
|
||||
.addr = DDC_ADDR,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
|
@ -279,15 +288,21 @@ drm_do_probe_ddc_edid(struct i2c_adapter *adapter, unsigned char *buf,
|
|||
.buf = buf,
|
||||
}
|
||||
};
|
||||
ret = i2c_transfer(adapter, msgs, 2);
|
||||
|
||||
/*
|
||||
* Avoid sending the segment addr to not upset non-compliant ddc
|
||||
* monitors.
|
||||
*/
|
||||
ret = i2c_transfer(adapter, &msgs[3 - xfers], xfers);
|
||||
|
||||
if (ret == -ENXIO) {
|
||||
DRM_DEBUG_KMS("drm: skipping non-existent adapter %s\n",
|
||||
adapter->name);
|
||||
break;
|
||||
}
|
||||
} while (ret != 2 && --retries);
|
||||
} while (ret != xfers && --retries);
|
||||
|
||||
return ret == 2 ? 0 : -1;
|
||||
return ret == xfers ? 0 : -1;
|
||||
}
|
||||
|
||||
static bool drm_edid_is_zero(u8 *in_edid, int length)
|
||||
|
@ -306,6 +321,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
|
|||
{
|
||||
int i, j = 0, valid_extensions = 0;
|
||||
u8 *block, *new;
|
||||
bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
|
||||
|
||||
if ((block = kmalloc(EDID_LENGTH, GFP_KERNEL)) == NULL)
|
||||
return NULL;
|
||||
|
@ -314,7 +330,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
|
|||
for (i = 0; i < 4; i++) {
|
||||
if (drm_do_probe_ddc_edid(adapter, block, 0, EDID_LENGTH))
|
||||
goto out;
|
||||
if (drm_edid_block_valid(block, 0))
|
||||
if (drm_edid_block_valid(block, 0, print_bad_edid))
|
||||
break;
|
||||
if (i == 0 && drm_edid_is_zero(block, EDID_LENGTH)) {
|
||||
connector->null_edid_counter++;
|
||||
|
@ -339,7 +355,7 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
|
|||
block + (valid_extensions + 1) * EDID_LENGTH,
|
||||
j, EDID_LENGTH))
|
||||
goto out;
|
||||
if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j)) {
|
||||
if (drm_edid_block_valid(block + (valid_extensions + 1) * EDID_LENGTH, j, print_bad_edid)) {
|
||||
valid_extensions++;
|
||||
break;
|
||||
}
|
||||
|
@ -362,8 +378,11 @@ drm_do_get_edid(struct drm_connector *connector, struct i2c_adapter *adapter)
|
|||
return block;
|
||||
|
||||
carp:
|
||||
dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
|
||||
drm_get_connector_name(connector), j);
|
||||
if (print_bad_edid) {
|
||||
dev_warn(connector->dev->dev, "%s: EDID block %d invalid.\n",
|
||||
drm_get_connector_name(connector), j);
|
||||
}
|
||||
connector->bad_edid_counter++;
|
||||
|
||||
out:
|
||||
kfree(block);
|
||||
|
@ -402,10 +421,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
|
|||
if (drm_probe_ddc(adapter))
|
||||
edid = (struct edid *)drm_do_get_edid(connector, adapter);
|
||||
|
||||
connector->display_info.raw_edid = (char *)edid;
|
||||
|
||||
return edid;
|
||||
|
||||
}
|
||||
EXPORT_SYMBOL(drm_get_edid);
|
||||
|
||||
|
@ -1522,6 +1538,40 @@ do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
|
|||
return modes;
|
||||
}
|
||||
|
||||
static int
|
||||
cea_db_payload_len(const u8 *db)
|
||||
{
|
||||
return db[0] & 0x1f;
|
||||
}
|
||||
|
||||
static int
|
||||
cea_db_tag(const u8 *db)
|
||||
{
|
||||
return db[0] >> 5;
|
||||
}
|
||||
|
||||
static int
|
||||
cea_revision(const u8 *cea)
|
||||
{
|
||||
return cea[1];
|
||||
}
|
||||
|
||||
static int
|
||||
cea_db_offsets(const u8 *cea, int *start, int *end)
|
||||
{
|
||||
/* Data block offset in CEA extension block */
|
||||
*start = 4;
|
||||
*end = cea[2];
|
||||
if (*end == 0)
|
||||
*end = 127;
|
||||
if (*end < 4 || *end > 127)
|
||||
return -ERANGE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define for_each_cea_db(cea, i, start, end) \
|
||||
for ((i) = (start); (i) < (end) && (i) + cea_db_payload_len(&(cea)[(i)]) < (end); (i) += cea_db_payload_len(&(cea)[(i)]) + 1)
|
||||
|
||||
static int
|
||||
add_cea_modes(struct drm_connector *connector, struct edid *edid)
|
||||
{
|
||||
|
@ -1529,10 +1579,17 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
|
|||
u8 * db, dbl;
|
||||
int modes = 0;
|
||||
|
||||
if (cea && cea[1] >= 3) {
|
||||
for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
|
||||
dbl = db[0] & 0x1f;
|
||||
if (((db[0] & 0xe0) >> 5) == VIDEO_BLOCK)
|
||||
if (cea && cea_revision(cea) >= 3) {
|
||||
int i, start, end;
|
||||
|
||||
if (cea_db_offsets(cea, &start, &end))
|
||||
return 0;
|
||||
|
||||
for_each_cea_db(cea, i, start, end) {
|
||||
db = &cea[i];
|
||||
dbl = cea_db_payload_len(db);
|
||||
|
||||
if (cea_db_tag(db) == VIDEO_BLOCK)
|
||||
modes += do_cea_modes (connector, db+1, dbl);
|
||||
}
|
||||
}
|
||||
|
@ -1541,19 +1598,28 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
|
|||
}
|
||||
|
||||
static void
|
||||
parse_hdmi_vsdb(struct drm_connector *connector, uint8_t *db)
|
||||
parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db)
|
||||
{
|
||||
connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */
|
||||
u8 len = cea_db_payload_len(db);
|
||||
|
||||
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];
|
||||
if (len >= 6) {
|
||||
connector->eld[5] |= (db[6] >> 7) << 1; /* Supports_AI */
|
||||
connector->dvi_dual = db[6] & 1;
|
||||
}
|
||||
if (len >= 7)
|
||||
connector->max_tmds_clock = db[7] * 5;
|
||||
if (len >= 8) {
|
||||
connector->latency_present[0] = db[8] >> 7;
|
||||
connector->latency_present[1] = (db[8] >> 6) & 1;
|
||||
}
|
||||
if (len >= 9)
|
||||
connector->video_latency[0] = db[9];
|
||||
if (len >= 10)
|
||||
connector->audio_latency[0] = db[10];
|
||||
if (len >= 11)
|
||||
connector->video_latency[1] = db[11];
|
||||
if (len >= 12)
|
||||
connector->audio_latency[1] = db[12];
|
||||
|
||||
DRM_LOG_KMS("HDMI: DVI dual %d, "
|
||||
"max TMDS clock %d, "
|
||||
|
@ -1577,6 +1643,21 @@ monitor_name(struct detailed_timing *t, void *data)
|
|||
*(u8 **)data = t->data.other_data.data.str.str;
|
||||
}
|
||||
|
||||
static bool cea_db_is_hdmi_vsdb(const u8 *db)
|
||||
{
|
||||
int hdmi_id;
|
||||
|
||||
if (cea_db_tag(db) != VENDOR_BLOCK)
|
||||
return false;
|
||||
|
||||
if (cea_db_payload_len(db) < 5)
|
||||
return false;
|
||||
|
||||
hdmi_id = db[1] | (db[2] << 8) | (db[3] << 16);
|
||||
|
||||
return hdmi_id == HDMI_IDENTIFIER;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_edid_to_eld - build ELD from EDID
|
||||
* @connector: connector corresponding to the HDMI/DP sink
|
||||
|
@ -1623,29 +1704,40 @@ void drm_edid_to_eld(struct drm_connector *connector, struct edid *edid)
|
|||
eld[18] = edid->prod_code[0];
|
||||
eld[19] = edid->prod_code[1];
|
||||
|
||||
if (cea[1] >= 3)
|
||||
for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
|
||||
dbl = db[0] & 0x1f;
|
||||
|
||||
switch ((db[0] & 0xe0) >> 5) {
|
||||
if (cea_revision(cea) >= 3) {
|
||||
int i, start, end;
|
||||
|
||||
if (cea_db_offsets(cea, &start, &end)) {
|
||||
start = 0;
|
||||
end = 0;
|
||||
}
|
||||
|
||||
for_each_cea_db(cea, i, start, end) {
|
||||
db = &cea[i];
|
||||
dbl = cea_db_payload_len(db);
|
||||
|
||||
switch (cea_db_tag(db)) {
|
||||
case AUDIO_BLOCK:
|
||||
/* Audio Data Block, contains SADs */
|
||||
sad_count = dbl / 3;
|
||||
memcpy(eld + 20 + mnl, &db[1], dbl);
|
||||
if (dbl >= 1)
|
||||
memcpy(eld + 20 + mnl, &db[1], dbl);
|
||||
break;
|
||||
case SPEAKER_BLOCK:
|
||||
/* Speaker Allocation Data Block */
|
||||
eld[7] = db[1];
|
||||
/* Speaker Allocation Data Block */
|
||||
if (dbl >= 1)
|
||||
eld[7] = db[1];
|
||||
break;
|
||||
case VENDOR_BLOCK:
|
||||
/* HDMI Vendor-Specific Data Block */
|
||||
if (db[1] == 0x03 && db[2] == 0x0c && db[3] == 0)
|
||||
if (cea_db_is_hdmi_vsdb(db))
|
||||
parse_hdmi_vsdb(connector, db);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
eld[5] |= sad_count << 4;
|
||||
eld[2] = (20 + mnl + sad_count * 3 + 3) / 4;
|
||||
|
||||
|
@ -1723,38 +1815,26 @@ EXPORT_SYMBOL(drm_select_eld);
|
|||
bool drm_detect_hdmi_monitor(struct edid *edid)
|
||||
{
|
||||
u8 *edid_ext;
|
||||
int i, hdmi_id;
|
||||
int i;
|
||||
int start_offset, end_offset;
|
||||
bool is_hdmi = false;
|
||||
|
||||
edid_ext = drm_find_cea_extension(edid);
|
||||
if (!edid_ext)
|
||||
goto end;
|
||||
return false;
|
||||
|
||||
/* Data block offset in CEA extension block */
|
||||
start_offset = 4;
|
||||
end_offset = edid_ext[2];
|
||||
if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Because HDMI identifier is in Vendor Specific Block,
|
||||
* search it from all data blocks of CEA extension.
|
||||
*/
|
||||
for (i = start_offset; i < end_offset;
|
||||
/* Increased by data block len */
|
||||
i += ((edid_ext[i] & 0x1f) + 1)) {
|
||||
/* Find vendor specific block */
|
||||
if ((edid_ext[i] >> 5) == VENDOR_BLOCK) {
|
||||
hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) |
|
||||
edid_ext[i + 3] << 16;
|
||||
/* Find HDMI identifier */
|
||||
if (hdmi_id == HDMI_IDENTIFIER)
|
||||
is_hdmi = true;
|
||||
break;
|
||||
}
|
||||
for_each_cea_db(edid_ext, i, start_offset, end_offset) {
|
||||
if (cea_db_is_hdmi_vsdb(&edid_ext[i]))
|
||||
return true;
|
||||
}
|
||||
|
||||
end:
|
||||
return is_hdmi;
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_detect_hdmi_monitor);
|
||||
|
||||
|
@ -1786,15 +1866,13 @@ bool drm_detect_monitor_audio(struct edid *edid)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Data block offset in CEA extension block */
|
||||
start_offset = 4;
|
||||
end_offset = edid_ext[2];
|
||||
if (cea_db_offsets(edid_ext, &start_offset, &end_offset))
|
||||
goto end;
|
||||
|
||||
for (i = start_offset; i < end_offset;
|
||||
i += ((edid_ext[i] & 0x1f) + 1)) {
|
||||
if ((edid_ext[i] >> 5) == AUDIO_BLOCK) {
|
||||
for_each_cea_db(edid_ext, i, start_offset, end_offset) {
|
||||
if (cea_db_tag(&edid_ext[i]) == AUDIO_BLOCK) {
|
||||
has_audio = true;
|
||||
for (j = 1; j < (edid_ext[i] & 0x1f); j += 3)
|
||||
for (j = 1; j < cea_db_payload_len(&edid_ext[i]) + 1; j += 3)
|
||||
DRM_DEBUG_KMS("CEA audio format %d\n",
|
||||
(edid_ext[i + j] >> 3) & 0xf);
|
||||
goto end;
|
||||
|
|
|
@ -114,8 +114,8 @@ static u8 generic_edid[GENERIC_EDIDS][128] = {
|
|||
},
|
||||
};
|
||||
|
||||
static int edid_load(struct drm_connector *connector, char *name,
|
||||
char *connector_name)
|
||||
static u8 *edid_load(struct drm_connector *connector, char *name,
|
||||
char *connector_name)
|
||||
{
|
||||
const struct firmware *fw;
|
||||
struct platform_device *pdev;
|
||||
|
@ -123,6 +123,7 @@ static int edid_load(struct drm_connector *connector, char *name,
|
|||
int fwsize, expected;
|
||||
int builtin = 0, err = 0;
|
||||
int i, valid_extensions = 0;
|
||||
bool print_bad_edid = !connector->bad_edid_counter || (drm_debug & DRM_UT_KMS);
|
||||
|
||||
pdev = platform_device_register_simple(connector_name, -1, NULL, 0);
|
||||
if (IS_ERR(pdev)) {
|
||||
|
@ -173,7 +174,8 @@ static int edid_load(struct drm_connector *connector, char *name,
|
|||
}
|
||||
memcpy(edid, fwdata, fwsize);
|
||||
|
||||
if (!drm_edid_block_valid(edid, 0)) {
|
||||
if (!drm_edid_block_valid(edid, 0, print_bad_edid)) {
|
||||
connector->bad_edid_counter++;
|
||||
DRM_ERROR("Base block of EDID firmware \"%s\" is invalid ",
|
||||
name);
|
||||
kfree(edid);
|
||||
|
@ -185,7 +187,7 @@ static int edid_load(struct drm_connector *connector, char *name,
|
|||
if (i != valid_extensions + 1)
|
||||
memcpy(edid + (valid_extensions + 1) * EDID_LENGTH,
|
||||
edid + i * EDID_LENGTH, EDID_LENGTH);
|
||||
if (drm_edid_block_valid(edid + i * EDID_LENGTH, i))
|
||||
if (drm_edid_block_valid(edid + i * EDID_LENGTH, i, print_bad_edid))
|
||||
valid_extensions++;
|
||||
}
|
||||
|
||||
|
@ -205,7 +207,6 @@ static int edid_load(struct drm_connector *connector, char *name,
|
|||
edid = new_edid;
|
||||
}
|
||||
|
||||
connector->display_info.raw_edid = edid;
|
||||
DRM_INFO("Got %s EDID base block and %d extension%s from "
|
||||
"\"%s\" for connector \"%s\"\n", builtin ? "built-in" :
|
||||
"external", valid_extensions, valid_extensions == 1 ? "" : "s",
|
||||
|
@ -215,7 +216,10 @@ relfw_out:
|
|||
release_firmware(fw);
|
||||
|
||||
out:
|
||||
return err;
|
||||
if (err)
|
||||
return ERR_PTR(err);
|
||||
|
||||
return edid;
|
||||
}
|
||||
|
||||
int drm_load_edid_firmware(struct drm_connector *connector)
|
||||
|
@ -223,6 +227,7 @@ int drm_load_edid_firmware(struct drm_connector *connector)
|
|||
char *connector_name = drm_get_connector_name(connector);
|
||||
char *edidname = edid_firmware, *last, *colon;
|
||||
int ret;
|
||||
struct edid *edid;
|
||||
|
||||
if (*edidname == '\0')
|
||||
return 0;
|
||||
|
@ -240,13 +245,13 @@ int drm_load_edid_firmware(struct drm_connector *connector)
|
|||
if (*last == '\n')
|
||||
*last = '\0';
|
||||
|
||||
ret = edid_load(connector, edidname, connector_name);
|
||||
if (ret)
|
||||
edid = (struct edid *) edid_load(connector, edidname, connector_name);
|
||||
if (IS_ERR_OR_NULL(edid))
|
||||
return 0;
|
||||
|
||||
drm_mode_connector_update_edid_property(connector,
|
||||
(struct edid *) connector->display_info.raw_edid);
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
kfree(edid);
|
||||
|
||||
return drm_add_edid_modes(connector, (struct edid *)
|
||||
connector->display_info.raw_edid);
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ static const struct drm_display_mode drm_dmt_modes[] = {
|
|||
976, 1088, 0, 480, 486, 494, 517, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 1024x768@43Hz, interlace */
|
||||
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
|
||||
{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER, 44900, 1024, 1032,
|
||||
1208, 1264, 0, 768, 768, 772, 817, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
|
@ -395,7 +395,7 @@ static const struct drm_display_mode edid_est_modes[] = {
|
|||
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
|
||||
1184, 1344, 0, 768, 771, 777, 806, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) }, /* 1024x768@60Hz */
|
||||
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
|
||||
{ DRM_MODE("1024x768i", DRM_MODE_TYPE_DRIVER,44900, 1024, 1032,
|
||||
1208, 1264, 0, 768, 768, 776, 817, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_INTERLACE) }, /* 1024x768@43Hz */
|
||||
{ DRM_MODE("832x624", DRM_MODE_TYPE_DRIVER, 57284, 832, 864,
|
||||
|
@ -506,17 +506,17 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
1430, 1650, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 5 - 1920x1080i@60Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
|
||||
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 6 - 1440x480i@60Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 7 - 1440x480i@60Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
|
@ -531,12 +531,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 10 - 2880x480i@60Hz */
|
||||
{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
3204, 3432, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 11 - 2880x480i@60Hz */
|
||||
{ DRM_MODE("2880x480", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
{ DRM_MODE("2880x480i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2956,
|
||||
3204, 3432, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
|
@ -573,17 +573,17 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
1760, 1980, 0, 720, 725, 730, 750, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
|
||||
/* 20 - 1920x1080i@50Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
|
||||
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 74250, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 21 - 1440x576i@50Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 22 - 1440x576i@50Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 27000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
|
@ -598,12 +598,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 25 - 2880x576i@50Hz */
|
||||
{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
3180, 3456, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 26 - 2880x576i@50Hz */
|
||||
{ DRM_MODE("2880x576", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
{ DRM_MODE("2880x576i", DRM_MODE_TYPE_DRIVER, 54000, 2880, 2928,
|
||||
3180, 3456, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
|
@ -656,12 +656,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
3184, 3456, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 39 - 1920x1080i@50Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
|
||||
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 72000, 1920, 1952,
|
||||
2120, 2304, 0, 1080, 1126, 1136, 1250, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
/* 40 - 1920x1080i@100Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
|
||||
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2448,
|
||||
2492, 2640, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
|
@ -688,7 +688,7 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 46 - 1920x1080i@120Hz */
|
||||
{ DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
|
||||
{ DRM_MODE("1920x1080i", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008,
|
||||
2052, 2200, 0, 1080, 1084, 1094, 1125, 0,
|
||||
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE) },
|
||||
|
@ -705,12 +705,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 50 - 1440x480i@120Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
|
||||
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 51 - 1440x480i@120Hz */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
|
||||
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 54000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
|
@ -723,12 +723,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
796, 864, 0, 576, 581, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 54 - 1440x576i@200Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
|
||||
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 55 - 1440x576i@200Hz */
|
||||
{ DRM_MODE("1440x576", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
|
||||
{ DRM_MODE("1440x576i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1464,
|
||||
1590, 1728, 0, 576, 580, 586, 625, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
|
@ -741,12 +741,12 @@ static const struct drm_display_mode edid_cea_modes[] = {
|
|||
798, 858, 0, 480, 489, 495, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
|
||||
/* 58 - 1440x480i@240 */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
|
||||
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
/* 59 - 1440x480i@240 */
|
||||
{ DRM_MODE("1440x480", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
|
||||
{ DRM_MODE("1440x480i", DRM_MODE_TYPE_DRIVER, 108000, 1440, 1478,
|
||||
1602, 1716, 0, 480, 488, 494, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC |
|
||||
DRM_MODE_FLAG_INTERLACE | DRM_MODE_FLAG_DBLCLK) },
|
||||
|
|
|
@ -0,0 +1,406 @@
|
|||
/*
|
||||
* drm kms/fb cma (contiguous memory allocator) helper functions
|
||||
*
|
||||
* Copyright (C) 2012 Analog Device Inc.
|
||||
* Author: Lars-Peter Clausen <lars@metafoo.de>
|
||||
*
|
||||
* Based on udl_fbdev.c
|
||||
* Copyright (C) 2012 Red Hat
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
#include <drm/drm_fb_cma_helper.h>
|
||||
#include <linux/module.h>
|
||||
|
||||
struct drm_fb_cma {
|
||||
struct drm_framebuffer fb;
|
||||
struct drm_gem_cma_object *obj[4];
|
||||
};
|
||||
|
||||
struct drm_fbdev_cma {
|
||||
struct drm_fb_helper fb_helper;
|
||||
struct drm_fb_cma *fb;
|
||||
};
|
||||
|
||||
static inline struct drm_fbdev_cma *to_fbdev_cma(struct drm_fb_helper *helper)
|
||||
{
|
||||
return container_of(helper, struct drm_fbdev_cma, fb_helper);
|
||||
}
|
||||
|
||||
static inline struct drm_fb_cma *to_fb_cma(struct drm_framebuffer *fb)
|
||||
{
|
||||
return container_of(fb, struct drm_fb_cma, fb);
|
||||
}
|
||||
|
||||
static void drm_fb_cma_destroy(struct drm_framebuffer *fb)
|
||||
{
|
||||
struct drm_fb_cma *fb_cma = to_fb_cma(fb);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
if (fb_cma->obj[i])
|
||||
drm_gem_object_unreference_unlocked(&fb_cma->obj[i]->base);
|
||||
}
|
||||
|
||||
drm_framebuffer_cleanup(fb);
|
||||
kfree(fb_cma);
|
||||
}
|
||||
|
||||
static int drm_fb_cma_create_handle(struct drm_framebuffer *fb,
|
||||
struct drm_file *file_priv, unsigned int *handle)
|
||||
{
|
||||
struct drm_fb_cma *fb_cma = to_fb_cma(fb);
|
||||
|
||||
return drm_gem_handle_create(file_priv,
|
||||
&fb_cma->obj[0]->base, handle);
|
||||
}
|
||||
|
||||
static struct drm_framebuffer_funcs drm_fb_cma_funcs = {
|
||||
.destroy = drm_fb_cma_destroy,
|
||||
.create_handle = drm_fb_cma_create_handle,
|
||||
};
|
||||
|
||||
static struct drm_fb_cma *drm_fb_cma_alloc(struct drm_device *dev,
|
||||
struct drm_mode_fb_cmd2 *mode_cmd, struct drm_gem_cma_object **obj,
|
||||
unsigned int num_planes)
|
||||
{
|
||||
struct drm_fb_cma *fb_cma;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
fb_cma = kzalloc(sizeof(*fb_cma), GFP_KERNEL);
|
||||
if (!fb_cma)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = drm_framebuffer_init(dev, &fb_cma->fb, &drm_fb_cma_funcs);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to initalize framebuffer: %d\n", ret);
|
||||
kfree(fb_cma);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
drm_helper_mode_fill_fb_struct(&fb_cma->fb, mode_cmd);
|
||||
|
||||
for (i = 0; i < num_planes; i++)
|
||||
fb_cma->obj[i] = obj[i];
|
||||
|
||||
return fb_cma;
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_fb_cma_create() - (struct drm_mode_config_funcs *)->fb_create callback function
|
||||
*
|
||||
* If your hardware has special alignment or pitch requirements these should be
|
||||
* checked before calling this function.
|
||||
*/
|
||||
struct drm_framebuffer *drm_fb_cma_create(struct drm_device *dev,
|
||||
struct drm_file *file_priv, struct drm_mode_fb_cmd2 *mode_cmd)
|
||||
{
|
||||
struct drm_fb_cma *fb_cma;
|
||||
struct drm_gem_cma_object *objs[4];
|
||||
struct drm_gem_object *obj;
|
||||
unsigned int hsub;
|
||||
unsigned int vsub;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
hsub = drm_format_horz_chroma_subsampling(mode_cmd->pixel_format);
|
||||
vsub = drm_format_vert_chroma_subsampling(mode_cmd->pixel_format);
|
||||
|
||||
for (i = 0; i < drm_format_num_planes(mode_cmd->pixel_format); i++) {
|
||||
unsigned int width = mode_cmd->width / (i ? hsub : 1);
|
||||
unsigned int height = mode_cmd->height / (i ? vsub : 1);
|
||||
unsigned int min_size;
|
||||
|
||||
obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handles[i]);
|
||||
if (!obj) {
|
||||
dev_err(dev->dev, "Failed to lookup GEM object\n");
|
||||
ret = -ENXIO;
|
||||
goto err_gem_object_unreference;
|
||||
}
|
||||
|
||||
min_size = (height - 1) * mode_cmd->pitches[i]
|
||||
+ width * drm_format_plane_cpp(mode_cmd->pixel_format, i)
|
||||
+ mode_cmd->offsets[i];
|
||||
|
||||
if (obj->size < min_size) {
|
||||
drm_gem_object_unreference_unlocked(obj);
|
||||
ret = -EINVAL;
|
||||
goto err_gem_object_unreference;
|
||||
}
|
||||
objs[i] = to_drm_gem_cma_obj(obj);
|
||||
}
|
||||
|
||||
fb_cma = drm_fb_cma_alloc(dev, mode_cmd, objs, i);
|
||||
if (IS_ERR(fb_cma)) {
|
||||
ret = PTR_ERR(fb_cma);
|
||||
goto err_gem_object_unreference;
|
||||
}
|
||||
|
||||
return &fb_cma->fb;
|
||||
|
||||
err_gem_object_unreference:
|
||||
for (i--; i >= 0; i--)
|
||||
drm_gem_object_unreference_unlocked(&objs[i]->base);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_create);
|
||||
|
||||
/**
|
||||
* drm_fb_cma_get_gem_obj() - Get CMA GEM object for framebuffer
|
||||
* @fb: The framebuffer
|
||||
* @plane: Which plane
|
||||
*
|
||||
* Return the CMA GEM object for given framebuffer.
|
||||
*
|
||||
* This function will usually be called from the CRTC callback functions.
|
||||
*/
|
||||
struct drm_gem_cma_object *drm_fb_cma_get_gem_obj(struct drm_framebuffer *fb,
|
||||
unsigned int plane)
|
||||
{
|
||||
struct drm_fb_cma *fb_cma = to_fb_cma(fb);
|
||||
|
||||
if (plane >= 4)
|
||||
return NULL;
|
||||
|
||||
return fb_cma->obj[plane];
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fb_cma_get_gem_obj);
|
||||
|
||||
static struct fb_ops drm_fbdev_cma_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_fillrect = sys_fillrect,
|
||||
.fb_copyarea = sys_copyarea,
|
||||
.fb_imageblit = sys_imageblit,
|
||||
.fb_check_var = drm_fb_helper_check_var,
|
||||
.fb_set_par = drm_fb_helper_set_par,
|
||||
.fb_blank = drm_fb_helper_blank,
|
||||
.fb_pan_display = drm_fb_helper_pan_display,
|
||||
.fb_setcmap = drm_fb_helper_setcmap,
|
||||
};
|
||||
|
||||
static int drm_fbdev_cma_create(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
struct drm_fbdev_cma *fbdev_cma = to_fbdev_cma(helper);
|
||||
struct drm_mode_fb_cmd2 mode_cmd = { 0 };
|
||||
struct drm_device *dev = helper->dev;
|
||||
struct drm_gem_cma_object *obj;
|
||||
struct drm_framebuffer *fb;
|
||||
unsigned int bytes_per_pixel;
|
||||
unsigned long offset;
|
||||
struct fb_info *fbi;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_KMS("surface width(%d), height(%d) and bpp(%d\n",
|
||||
sizes->surface_width, sizes->surface_height,
|
||||
sizes->surface_bpp);
|
||||
|
||||
bytes_per_pixel = DIV_ROUND_UP(sizes->surface_bpp, 8);
|
||||
|
||||
mode_cmd.width = sizes->surface_width;
|
||||
mode_cmd.height = sizes->surface_height;
|
||||
mode_cmd.pitches[0] = sizes->surface_width * bytes_per_pixel;
|
||||
mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp,
|
||||
sizes->surface_depth);
|
||||
|
||||
size = mode_cmd.pitches[0] * mode_cmd.height;
|
||||
obj = drm_gem_cma_create(dev, size);
|
||||
if (!obj)
|
||||
return -ENOMEM;
|
||||
|
||||
fbi = framebuffer_alloc(0, dev->dev);
|
||||
if (!fbi) {
|
||||
dev_err(dev->dev, "Failed to allocate framebuffer info.\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_drm_gem_cma_free_object;
|
||||
}
|
||||
|
||||
fbdev_cma->fb = drm_fb_cma_alloc(dev, &mode_cmd, &obj, 1);
|
||||
if (IS_ERR(fbdev_cma->fb)) {
|
||||
dev_err(dev->dev, "Failed to allocate DRM framebuffer.\n");
|
||||
ret = PTR_ERR(fbdev_cma->fb);
|
||||
goto err_framebuffer_release;
|
||||
}
|
||||
|
||||
fb = &fbdev_cma->fb->fb;
|
||||
helper->fb = fb;
|
||||
helper->fbdev = fbi;
|
||||
|
||||
fbi->par = helper;
|
||||
fbi->flags = FBINFO_FLAG_DEFAULT;
|
||||
fbi->fbops = &drm_fbdev_cma_ops;
|
||||
|
||||
ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to allocate color map.\n");
|
||||
goto err_drm_fb_cma_destroy;
|
||||
}
|
||||
|
||||
drm_fb_helper_fill_fix(fbi, fb->pitches[0], fb->depth);
|
||||
drm_fb_helper_fill_var(fbi, helper, fb->width, fb->height);
|
||||
|
||||
offset = fbi->var.xoffset * bytes_per_pixel;
|
||||
offset += fbi->var.yoffset * fb->pitches[0];
|
||||
|
||||
dev->mode_config.fb_base = (resource_size_t)obj->paddr;
|
||||
fbi->screen_base = obj->vaddr + offset;
|
||||
fbi->fix.smem_start = (unsigned long)(obj->paddr + offset);
|
||||
fbi->screen_size = size;
|
||||
fbi->fix.smem_len = size;
|
||||
|
||||
return 0;
|
||||
|
||||
err_drm_fb_cma_destroy:
|
||||
drm_fb_cma_destroy(fb);
|
||||
err_framebuffer_release:
|
||||
framebuffer_release(fbi);
|
||||
err_drm_gem_cma_free_object:
|
||||
drm_gem_cma_free_object(&obj->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int drm_fbdev_cma_probe(struct drm_fb_helper *helper,
|
||||
struct drm_fb_helper_surface_size *sizes)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!helper->fb) {
|
||||
ret = drm_fbdev_cma_create(helper, sizes);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct drm_fb_helper_funcs drm_fb_cma_helper_funcs = {
|
||||
.fb_probe = drm_fbdev_cma_probe,
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_fbdev_cma_init() - Allocate and initializes a drm_fbdev_cma struct
|
||||
* @dev: DRM device
|
||||
* @preferred_bpp: Preferred bits per pixel for the device
|
||||
* @num_crtc: Number of CRTCs
|
||||
* @max_conn_count: Maximum number of connectors
|
||||
*
|
||||
* Returns a newly allocated drm_fbdev_cma struct or a ERR_PTR.
|
||||
*/
|
||||
struct drm_fbdev_cma *drm_fbdev_cma_init(struct drm_device *dev,
|
||||
unsigned int preferred_bpp, unsigned int num_crtc,
|
||||
unsigned int max_conn_count)
|
||||
{
|
||||
struct drm_fbdev_cma *fbdev_cma;
|
||||
struct drm_fb_helper *helper;
|
||||
int ret;
|
||||
|
||||
fbdev_cma = kzalloc(sizeof(*fbdev_cma), GFP_KERNEL);
|
||||
if (!fbdev_cma) {
|
||||
dev_err(dev->dev, "Failed to allocate drm fbdev.\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
fbdev_cma->fb_helper.funcs = &drm_fb_cma_helper_funcs;
|
||||
helper = &fbdev_cma->fb_helper;
|
||||
|
||||
ret = drm_fb_helper_init(dev, helper, num_crtc, max_conn_count);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to initialize drm fb helper.\n");
|
||||
goto err_free;
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_single_add_all_connectors(helper);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to add connectors.\n");
|
||||
goto err_drm_fb_helper_fini;
|
||||
|
||||
}
|
||||
|
||||
ret = drm_fb_helper_initial_config(helper, preferred_bpp);
|
||||
if (ret < 0) {
|
||||
dev_err(dev->dev, "Failed to set inital hw configuration.\n");
|
||||
goto err_drm_fb_helper_fini;
|
||||
}
|
||||
|
||||
return fbdev_cma;
|
||||
|
||||
err_drm_fb_helper_fini:
|
||||
drm_fb_helper_fini(helper);
|
||||
err_free:
|
||||
kfree(fbdev_cma);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_init);
|
||||
|
||||
/**
|
||||
* drm_fbdev_cma_fini() - Free drm_fbdev_cma struct
|
||||
* @fbdev_cma: The drm_fbdev_cma struct
|
||||
*/
|
||||
void drm_fbdev_cma_fini(struct drm_fbdev_cma *fbdev_cma)
|
||||
{
|
||||
if (fbdev_cma->fb_helper.fbdev) {
|
||||
struct fb_info *info;
|
||||
int ret;
|
||||
|
||||
info = fbdev_cma->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);
|
||||
}
|
||||
|
||||
if (fbdev_cma->fb)
|
||||
drm_fb_cma_destroy(&fbdev_cma->fb->fb);
|
||||
|
||||
drm_fb_helper_fini(&fbdev_cma->fb_helper);
|
||||
kfree(fbdev_cma);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_fini);
|
||||
|
||||
/**
|
||||
* drm_fbdev_cma_restore_mode() - Restores initial framebuffer mode
|
||||
* @fbdev_cma: The drm_fbdev_cma struct, may be NULL
|
||||
*
|
||||
* This function is usually called from the DRM drivers lastclose callback.
|
||||
*/
|
||||
void drm_fbdev_cma_restore_mode(struct drm_fbdev_cma *fbdev_cma)
|
||||
{
|
||||
if (fbdev_cma)
|
||||
drm_fb_helper_restore_fbdev_mode(&fbdev_cma->fb_helper);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_restore_mode);
|
||||
|
||||
/**
|
||||
* drm_fbdev_cma_hotplug_event() - Poll for hotpulug events
|
||||
* @fbdev_cma: The drm_fbdev_cma struct, may be NULL
|
||||
*
|
||||
* This function is usually called from the DRM drivers output_poll_changed
|
||||
* callback.
|
||||
*/
|
||||
void drm_fbdev_cma_hotplug_event(struct drm_fbdev_cma *fbdev_cma)
|
||||
{
|
||||
if (fbdev_cma)
|
||||
drm_fb_helper_hotplug_event(&fbdev_cma->fb_helper);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_fbdev_cma_hotplug_event);
|
|
@ -236,7 +236,7 @@ bool drm_fb_helper_restore_fbdev_mode(struct drm_fb_helper *fb_helper)
|
|||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode);
|
||||
|
||||
bool drm_fb_helper_force_kernel_mode(void)
|
||||
static bool drm_fb_helper_force_kernel_mode(void)
|
||||
{
|
||||
bool ret, error = false;
|
||||
struct drm_fb_helper *helper;
|
||||
|
@ -330,7 +330,7 @@ static void drm_fb_helper_dpms(struct fb_info *info, int dpms_mode)
|
|||
/* Walk the connectors & encoders on this fb turning them on/off */
|
||||
for (j = 0; j < fb_helper->connector_count; j++) {
|
||||
connector = fb_helper->connector_info[j]->connector;
|
||||
drm_helper_connector_dpms(connector, dpms_mode);
|
||||
connector->funcs->dpms(connector, dpms_mode);
|
||||
drm_connector_property_set_value(connector,
|
||||
dev->mode_config.dpms_property, dpms_mode);
|
||||
}
|
||||
|
@ -1230,7 +1230,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
|
|||
struct drm_device *dev = fb_helper->dev;
|
||||
struct drm_fb_helper_crtc **crtcs;
|
||||
struct drm_display_mode **modes;
|
||||
struct drm_encoder *encoder;
|
||||
struct drm_mode_set *modeset;
|
||||
bool *enabled;
|
||||
int width, height;
|
||||
|
@ -1241,11 +1240,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
|
|||
width = dev->mode_config.max_width;
|
||||
height = dev->mode_config.max_height;
|
||||
|
||||
/* clean out all the encoder/crtc combos */
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
encoder->crtc = NULL;
|
||||
}
|
||||
|
||||
crtcs = kcalloc(dev->mode_config.num_connector,
|
||||
sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
|
||||
modes = kcalloc(dev->mode_config.num_connector,
|
||||
|
|
|
@ -0,0 +1,251 @@
|
|||
/*
|
||||
* drm gem CMA (contiguous memory allocator) helper functions
|
||||
*
|
||||
* Copyright (C) 2012 Sascha Hauer, Pengutronix
|
||||
*
|
||||
* Based on Samsung Exynos code
|
||||
*
|
||||
* Copyright (c) 2011 Samsung Electronics Co., Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include <drm/drm_gem_cma_helper.h>
|
||||
|
||||
static unsigned int get_gem_mmap_offset(struct drm_gem_object *obj)
|
||||
{
|
||||
return (unsigned int)obj->map_list.hash.key << PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static void drm_gem_cma_buf_destroy(struct drm_device *drm,
|
||||
struct drm_gem_cma_object *cma_obj)
|
||||
{
|
||||
dma_free_writecombine(drm->dev, cma_obj->base.size, cma_obj->vaddr,
|
||||
cma_obj->paddr);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gem_cma_create - allocate an object with the given size
|
||||
*
|
||||
* returns a struct drm_gem_cma_object* on success or ERR_PTR values
|
||||
* on failure.
|
||||
*/
|
||||
struct drm_gem_cma_object *drm_gem_cma_create(struct drm_device *drm,
|
||||
unsigned int size)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct drm_gem_object *gem_obj;
|
||||
int ret;
|
||||
|
||||
size = round_up(size, PAGE_SIZE);
|
||||
|
||||
cma_obj = kzalloc(sizeof(*cma_obj), GFP_KERNEL);
|
||||
if (!cma_obj)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
cma_obj->vaddr = dma_alloc_writecombine(drm->dev, size,
|
||||
&cma_obj->paddr, GFP_KERNEL | __GFP_NOWARN);
|
||||
if (!cma_obj->vaddr) {
|
||||
dev_err(drm->dev, "failed to allocate buffer with size %d\n", size);
|
||||
ret = -ENOMEM;
|
||||
goto err_dma_alloc;
|
||||
}
|
||||
|
||||
gem_obj = &cma_obj->base;
|
||||
|
||||
ret = drm_gem_object_init(drm, gem_obj, size);
|
||||
if (ret)
|
||||
goto err_obj_init;
|
||||
|
||||
ret = drm_gem_create_mmap_offset(gem_obj);
|
||||
if (ret)
|
||||
goto err_create_mmap_offset;
|
||||
|
||||
return cma_obj;
|
||||
|
||||
err_create_mmap_offset:
|
||||
drm_gem_object_release(gem_obj);
|
||||
|
||||
err_obj_init:
|
||||
drm_gem_cma_buf_destroy(drm, cma_obj);
|
||||
|
||||
err_dma_alloc:
|
||||
kfree(cma_obj);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_create);
|
||||
|
||||
/*
|
||||
* drm_gem_cma_create_with_handle - allocate an object with the given
|
||||
* size and create a gem handle on it
|
||||
*
|
||||
* returns a struct drm_gem_cma_object* on success or ERR_PTR values
|
||||
* on failure.
|
||||
*/
|
||||
static struct drm_gem_cma_object *drm_gem_cma_create_with_handle(
|
||||
struct drm_file *file_priv,
|
||||
struct drm_device *drm, unsigned int size,
|
||||
unsigned int *handle)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
struct drm_gem_object *gem_obj;
|
||||
int ret;
|
||||
|
||||
cma_obj = drm_gem_cma_create(drm, size);
|
||||
if (IS_ERR(cma_obj))
|
||||
return cma_obj;
|
||||
|
||||
gem_obj = &cma_obj->base;
|
||||
|
||||
/*
|
||||
* 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, gem_obj, handle);
|
||||
if (ret)
|
||||
goto err_handle_create;
|
||||
|
||||
/* drop reference from allocate - handle holds it now. */
|
||||
drm_gem_object_unreference_unlocked(gem_obj);
|
||||
|
||||
return cma_obj;
|
||||
|
||||
err_handle_create:
|
||||
drm_gem_cma_free_object(gem_obj);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* drm_gem_cma_free_object - (struct drm_driver)->gem_free_object callback
|
||||
* function
|
||||
*/
|
||||
void drm_gem_cma_free_object(struct drm_gem_object *gem_obj)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
|
||||
if (gem_obj->map_list.map)
|
||||
drm_gem_free_mmap_offset(gem_obj);
|
||||
|
||||
drm_gem_object_release(gem_obj);
|
||||
|
||||
cma_obj = to_drm_gem_cma_obj(gem_obj);
|
||||
|
||||
drm_gem_cma_buf_destroy(gem_obj->dev, cma_obj);
|
||||
|
||||
kfree(cma_obj);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_free_object);
|
||||
|
||||
/*
|
||||
* drm_gem_cma_dumb_create - (struct drm_driver)->dumb_create callback
|
||||
* function
|
||||
*
|
||||
* This aligns the pitch and size arguments to the minimum required. wrap
|
||||
* this into your own function if you need bigger alignment.
|
||||
*/
|
||||
int drm_gem_cma_dumb_create(struct drm_file *file_priv,
|
||||
struct drm_device *dev, struct drm_mode_create_dumb *args)
|
||||
{
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
int min_pitch = DIV_ROUND_UP(args->width * args->bpp, 8);
|
||||
|
||||
if (args->pitch < min_pitch)
|
||||
args->pitch = min_pitch;
|
||||
|
||||
if (args->size < args->pitch * args->height)
|
||||
args->size = args->pitch * args->height;
|
||||
|
||||
cma_obj = drm_gem_cma_create_with_handle(file_priv, dev,
|
||||
args->size, &args->handle);
|
||||
if (IS_ERR(cma_obj))
|
||||
return PTR_ERR(cma_obj);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_create);
|
||||
|
||||
/*
|
||||
* drm_gem_cma_dumb_map_offset - (struct drm_driver)->dumb_map_offset callback
|
||||
* function
|
||||
*/
|
||||
int drm_gem_cma_dumb_map_offset(struct drm_file *file_priv,
|
||||
struct drm_device *drm, uint32_t handle, uint64_t *offset)
|
||||
{
|
||||
struct drm_gem_object *gem_obj;
|
||||
|
||||
mutex_lock(&drm->struct_mutex);
|
||||
|
||||
gem_obj = drm_gem_object_lookup(drm, file_priv, handle);
|
||||
if (!gem_obj) {
|
||||
dev_err(drm->dev, "failed to lookup gem object\n");
|
||||
mutex_unlock(&drm->struct_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*offset = get_gem_mmap_offset(gem_obj);
|
||||
|
||||
drm_gem_object_unreference(gem_obj);
|
||||
|
||||
mutex_unlock(&drm->struct_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_map_offset);
|
||||
|
||||
const struct vm_operations_struct drm_gem_cma_vm_ops = {
|
||||
.open = drm_gem_vm_open,
|
||||
.close = drm_gem_vm_close,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_vm_ops);
|
||||
|
||||
/*
|
||||
* drm_gem_cma_mmap - (struct file_operation)->mmap callback function
|
||||
*/
|
||||
int drm_gem_cma_mmap(struct file *filp, struct vm_area_struct *vma)
|
||||
{
|
||||
struct drm_gem_object *gem_obj;
|
||||
struct drm_gem_cma_object *cma_obj;
|
||||
int ret;
|
||||
|
||||
ret = drm_gem_mmap(filp, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gem_obj = vma->vm_private_data;
|
||||
cma_obj = to_drm_gem_cma_obj(gem_obj);
|
||||
|
||||
ret = remap_pfn_range(vma, vma->vm_start, cma_obj->paddr >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start, vma->vm_page_prot);
|
||||
if (ret)
|
||||
drm_gem_vm_close(vma);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_mmap);
|
||||
|
||||
/*
|
||||
* drm_gem_cma_dumb_destroy - (struct drm_driver)->dumb_destroy callback function
|
||||
*/
|
||||
int drm_gem_cma_dumb_destroy(struct drm_file *file_priv,
|
||||
struct drm_device *drm, unsigned int handle)
|
||||
{
|
||||
return drm_gem_handle_delete(file_priv, handle);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(drm_gem_cma_dumb_destroy);
|
|
@ -1236,7 +1236,7 @@ done:
|
|||
return ret;
|
||||
}
|
||||
|
||||
void drm_handle_vblank_events(struct drm_device *dev, int crtc)
|
||||
static void drm_handle_vblank_events(struct drm_device *dev, int crtc)
|
||||
{
|
||||
struct drm_pending_vblank_event *e, *t;
|
||||
struct timeval now;
|
||||
|
|
|
@ -62,7 +62,7 @@ static pgprot_t drm_io_prot(uint32_t map_type, struct vm_area_struct *vma)
|
|||
tmp = pgprot_writecombine(tmp);
|
||||
else
|
||||
tmp = pgprot_noncached(tmp);
|
||||
#elif defined(__sparc__) || defined(__arm__)
|
||||
#elif defined(__sparc__) || defined(__arm__) || defined(__mips__)
|
||||
tmp = pgprot_noncached(tmp);
|
||||
#endif
|
||||
return tmp;
|
||||
|
@ -619,20 +619,11 @@ int drm_mmap_locked(struct file *filp, struct vm_area_struct *vma)
|
|||
offset = drm_core_get_reg_ofs(dev);
|
||||
vma->vm_flags |= VM_IO; /* not in core dump */
|
||||
vma->vm_page_prot = drm_io_prot(map->type, vma);
|
||||
#if !defined(__arm__)
|
||||
if (io_remap_pfn_range(vma, vma->vm_start,
|
||||
(map->offset + offset) >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
#else
|
||||
if (remap_pfn_range(vma, vma->vm_start,
|
||||
(map->offset + offset) >> PAGE_SHIFT,
|
||||
vma->vm_end - vma->vm_start,
|
||||
vma->vm_page_prot))
|
||||
return -EAGAIN;
|
||||
#endif
|
||||
|
||||
DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx,"
|
||||
" offset = 0x%llx\n",
|
||||
map->type,
|
||||
|
|
|
@ -147,9 +147,7 @@ static int exynos_drm_connector_get_modes(struct drm_connector *connector)
|
|||
|
||||
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;
|
||||
kfree(edid);
|
||||
} else {
|
||||
struct drm_display_mode *mode = drm_mode_create(connector->dev);
|
||||
struct exynos_drm_panel_info *panel;
|
||||
|
|
|
@ -266,8 +266,8 @@ static void exynos_drm_fbdev_destroy(struct drm_device *dev,
|
|||
/* 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);
|
||||
if (fb)
|
||||
drm_framebuffer_remove(fb);
|
||||
}
|
||||
|
||||
/* release linux framebuffer */
|
||||
|
|
|
@ -102,7 +102,6 @@ static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
|
|||
u8 *edid, int len)
|
||||
{
|
||||
struct vidi_context *ctx = get_vidi_context(dev);
|
||||
struct edid *raw_edid;
|
||||
|
||||
DRM_DEBUG_KMS("%s\n", __FILE__);
|
||||
|
||||
|
@ -115,18 +114,6 @@ static int vidi_get_edid(struct device *dev, struct drm_connector *connector,
|
|||
return -EFAULT;
|
||||
}
|
||||
|
||||
raw_edid = kzalloc(len, GFP_KERNEL);
|
||||
if (!raw_edid) {
|
||||
DRM_DEBUG_KMS("failed to allocate raw_edid.\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
memcpy(raw_edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
|
||||
* EDID_LENGTH, len));
|
||||
|
||||
/* attach the edid data to connector. */
|
||||
connector->display_info.raw_edid = (char *)raw_edid;
|
||||
|
||||
memcpy(edid, ctx->raw_edid, min((1 + ctx->raw_edid->extensions)
|
||||
* EDID_LENGTH, len));
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
ccflags-y += -I$(srctree)/include/drm
|
||||
|
||||
gma500_gfx-y += gem_glue.o \
|
||||
gma500_gfx-y += \
|
||||
accel_2d.o \
|
||||
backlight.o \
|
||||
framebuffer.o \
|
||||
|
@ -30,7 +30,8 @@ gma500_gfx-$(CONFIG_DRM_GMA3600) += cdv_device.o \
|
|||
cdv_intel_crt.o \
|
||||
cdv_intel_display.o \
|
||||
cdv_intel_hdmi.o \
|
||||
cdv_intel_lvds.o
|
||||
cdv_intel_lvds.o \
|
||||
cdv_intel_dp.o
|
||||
|
||||
gma500_gfx-$(CONFIG_DRM_GMA600) += oaktrail_device.o \
|
||||
oaktrail_crtc.o \
|
||||
|
|
|
@ -26,10 +26,55 @@
|
|||
#include "intel_bios.h"
|
||||
#include "power.h"
|
||||
|
||||
static void do_gma_backlight_set(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
backlight_update_status(dev_priv->backlight_device);
|
||||
#endif
|
||||
}
|
||||
|
||||
void gma_backlight_enable(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
dev_priv->backlight_enabled = true;
|
||||
if (dev_priv->backlight_device) {
|
||||
dev_priv->backlight_device->props.brightness = dev_priv->backlight_level;
|
||||
do_gma_backlight_set(dev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gma_backlight_disable(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
dev_priv->backlight_enabled = false;
|
||||
if (dev_priv->backlight_device) {
|
||||
dev_priv->backlight_device->props.brightness = 0;
|
||||
do_gma_backlight_set(dev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void gma_backlight_set(struct drm_device *dev, int v)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
dev_priv->backlight_level = v;
|
||||
if (dev_priv->backlight_device && dev_priv->backlight_enabled) {
|
||||
dev_priv->backlight_device->props.brightness = v;
|
||||
do_gma_backlight_set(dev);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int gma_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
dev_priv->backlight_enabled = true;
|
||||
return dev_priv->ops->backlight_init(dev);
|
||||
#else
|
||||
return 0;
|
||||
|
|
|
@ -58,10 +58,17 @@ static int cdv_output_init(struct drm_device *dev)
|
|||
cdv_intel_lvds_init(dev, &dev_priv->mode_dev);
|
||||
|
||||
/* These bits indicate HDMI not SDVO on CDV */
|
||||
if (REG_READ(SDVOB) & SDVO_DETECTED)
|
||||
if (REG_READ(SDVOB) & SDVO_DETECTED) {
|
||||
cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOB);
|
||||
if (REG_READ(SDVOC) & SDVO_DETECTED)
|
||||
if (REG_READ(DP_B) & DP_DETECTED)
|
||||
cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_B);
|
||||
}
|
||||
|
||||
if (REG_READ(SDVOC) & SDVO_DETECTED) {
|
||||
cdv_hdmi_init(dev, &dev_priv->mode_dev, SDVOC);
|
||||
if (REG_READ(DP_C) & DP_DETECTED)
|
||||
cdv_intel_dp_init(dev, &dev_priv->mode_dev, DP_C);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -163,6 +170,7 @@ static int cdv_backlight_init(struct drm_device *dev)
|
|||
cdv_get_brightness(cdv_backlight_device);
|
||||
backlight_update_status(cdv_backlight_device);
|
||||
dev_priv->backlight_device = cdv_backlight_device;
|
||||
dev_priv->backlight_enabled = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -449,6 +457,7 @@ static void cdv_get_core_freq(struct drm_device *dev)
|
|||
case 6:
|
||||
case 7:
|
||||
dev_priv->core_freq = 266;
|
||||
break;
|
||||
default:
|
||||
dev_priv->core_freq = 0;
|
||||
}
|
||||
|
@ -488,6 +497,65 @@ static void cdv_hotplug_enable(struct drm_device *dev, bool on)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *force_audio_names[] = {
|
||||
"off",
|
||||
"auto",
|
||||
"on",
|
||||
};
|
||||
|
||||
void cdv_intel_attach_force_audio_property(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_property *prop;
|
||||
int i;
|
||||
|
||||
prop = dev_priv->force_audio_property;
|
||||
if (prop == NULL) {
|
||||
prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
|
||||
"audio",
|
||||
ARRAY_SIZE(force_audio_names));
|
||||
if (prop == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(force_audio_names); i++)
|
||||
drm_property_add_enum(prop, i, i-1, force_audio_names[i]);
|
||||
|
||||
dev_priv->force_audio_property = prop;
|
||||
}
|
||||
drm_connector_attach_property(connector, prop, 0);
|
||||
}
|
||||
|
||||
|
||||
static const char *broadcast_rgb_names[] = {
|
||||
"Full",
|
||||
"Limited 16:235",
|
||||
};
|
||||
|
||||
void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_psb_private *dev_priv = dev->dev_private;
|
||||
struct drm_property *prop;
|
||||
int i;
|
||||
|
||||
prop = dev_priv->broadcast_rgb_property;
|
||||
if (prop == NULL) {
|
||||
prop = drm_property_create(dev, DRM_MODE_PROP_ENUM,
|
||||
"Broadcast RGB",
|
||||
ARRAY_SIZE(broadcast_rgb_names));
|
||||
if (prop == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(broadcast_rgb_names); i++)
|
||||
drm_property_add_enum(prop, i, i, broadcast_rgb_names[i]);
|
||||
|
||||
dev_priv->broadcast_rgb_property = prop;
|
||||
}
|
||||
|
||||
drm_connector_attach_property(connector, prop, 0);
|
||||
}
|
||||
|
||||
/* Cedarview */
|
||||
static const struct psb_offset cdv_regmap[2] = {
|
||||
{
|
||||
|
|
|
@ -57,15 +57,26 @@ struct cdv_intel_clock_t {
|
|||
struct cdv_intel_limit_t {
|
||||
struct cdv_intel_range_t dot, vco, n, m, m1, m2, p, p1;
|
||||
struct cdv_intel_p2_t p2;
|
||||
bool (*find_pll)(const struct cdv_intel_limit_t *, struct drm_crtc *,
|
||||
int, int, struct cdv_intel_clock_t *);
|
||||
};
|
||||
|
||||
static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit,
|
||||
struct drm_crtc *crtc, int target, int refclk,
|
||||
struct cdv_intel_clock_t *best_clock);
|
||||
static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target,
|
||||
int refclk,
|
||||
struct cdv_intel_clock_t *best_clock);
|
||||
|
||||
#define CDV_LIMIT_SINGLE_LVDS_96 0
|
||||
#define CDV_LIMIT_SINGLE_LVDS_100 1
|
||||
#define CDV_LIMIT_DAC_HDMI_27 2
|
||||
#define CDV_LIMIT_DAC_HDMI_96 3
|
||||
#define CDV_LIMIT_DP_27 4
|
||||
#define CDV_LIMIT_DP_100 5
|
||||
|
||||
static const struct cdv_intel_limit_t cdv_intel_limits[] = {
|
||||
{ /* CDV_SIGNLE_LVDS_96MHz */
|
||||
{ /* CDV_SINGLE_LVDS_96MHz */
|
||||
.dot = {.min = 20000, .max = 115500},
|
||||
.vco = {.min = 1800000, .max = 3600000},
|
||||
.n = {.min = 2, .max = 6},
|
||||
|
@ -76,6 +87,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
|
|||
.p1 = {.min = 2, .max = 10},
|
||||
.p2 = {.dot_limit = 200000,
|
||||
.p2_slow = 14, .p2_fast = 14},
|
||||
.find_pll = cdv_intel_find_best_PLL,
|
||||
},
|
||||
{ /* CDV_SINGLE_LVDS_100MHz */
|
||||
.dot = {.min = 20000, .max = 115500},
|
||||
|
@ -90,6 +102,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
|
|||
* is 80-224Mhz. Prefer single channel as much as possible.
|
||||
*/
|
||||
.p2 = {.dot_limit = 200000, .p2_slow = 14, .p2_fast = 14},
|
||||
.find_pll = cdv_intel_find_best_PLL,
|
||||
},
|
||||
{ /* CDV_DAC_HDMI_27MHz */
|
||||
.dot = {.min = 20000, .max = 400000},
|
||||
|
@ -101,6 +114,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
|
|||
.p = {.min = 5, .max = 90},
|
||||
.p1 = {.min = 1, .max = 9},
|
||||
.p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5},
|
||||
.find_pll = cdv_intel_find_best_PLL,
|
||||
},
|
||||
{ /* CDV_DAC_HDMI_96MHz */
|
||||
.dot = {.min = 20000, .max = 400000},
|
||||
|
@ -112,7 +126,32 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
|
|||
.p = {.min = 5, .max = 100},
|
||||
.p1 = {.min = 1, .max = 10},
|
||||
.p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 5},
|
||||
.find_pll = cdv_intel_find_best_PLL,
|
||||
},
|
||||
{ /* CDV_DP_27MHz */
|
||||
.dot = {.min = 160000, .max = 272000},
|
||||
.vco = {.min = 1809000, .max = 3564000},
|
||||
.n = {.min = 1, .max = 1},
|
||||
.m = {.min = 67, .max = 132},
|
||||
.m1 = {.min = 0, .max = 0},
|
||||
.m2 = {.min = 65, .max = 130},
|
||||
.p = {.min = 5, .max = 90},
|
||||
.p1 = {.min = 1, .max = 9},
|
||||
.p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10},
|
||||
.find_pll = cdv_intel_find_dp_pll,
|
||||
},
|
||||
{ /* CDV_DP_100MHz */
|
||||
.dot = {.min = 160000, .max = 272000},
|
||||
.vco = {.min = 1800000, .max = 3600000},
|
||||
.n = {.min = 2, .max = 6},
|
||||
.m = {.min = 60, .max = 164},
|
||||
.m1 = {.min = 0, .max = 0},
|
||||
.m2 = {.min = 58, .max = 162},
|
||||
.p = {.min = 5, .max = 100},
|
||||
.p1 = {.min = 1, .max = 10},
|
||||
.p2 = {.dot_limit = 225000, .p2_slow = 10, .p2_fast = 10},
|
||||
.find_pll = cdv_intel_find_dp_pll,
|
||||
}
|
||||
};
|
||||
|
||||
#define _wait_for(COND, MS, W) ({ \
|
||||
|
@ -132,7 +171,7 @@ static const struct cdv_intel_limit_t cdv_intel_limits[] = {
|
|||
#define wait_for(COND, MS) _wait_for(COND, MS, 1)
|
||||
|
||||
|
||||
static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val)
|
||||
int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val)
|
||||
{
|
||||
int ret;
|
||||
|
||||
|
@ -159,7 +198,7 @@ static int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val)
|
||||
int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val)
|
||||
{
|
||||
int ret;
|
||||
static bool dpio_debug = true;
|
||||
|
@ -201,7 +240,7 @@ static int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val)
|
|||
/* Reset the DPIO configuration register. The BIOS does this at every
|
||||
* mode set.
|
||||
*/
|
||||
static void cdv_sb_reset(struct drm_device *dev)
|
||||
void cdv_sb_reset(struct drm_device *dev)
|
||||
{
|
||||
|
||||
REG_WRITE(DPIO_CFG, 0);
|
||||
|
@ -216,7 +255,7 @@ static void cdv_sb_reset(struct drm_device *dev)
|
|||
*/
|
||||
static int
|
||||
cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
|
||||
struct cdv_intel_clock_t *clock, bool is_lvds)
|
||||
struct cdv_intel_clock_t *clock, bool is_lvds, u32 ddi_select)
|
||||
{
|
||||
struct psb_intel_crtc *psb_crtc = to_psb_intel_crtc(crtc);
|
||||
int pipe = psb_crtc->pipe;
|
||||
|
@ -259,7 +298,7 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
|
|||
ref_value &= ~(REF_CLK_MASK);
|
||||
|
||||
/* use DPLL_A for pipeB on CRT/HDMI */
|
||||
if (pipe == 1 && !is_lvds) {
|
||||
if (pipe == 1 && !is_lvds && !(ddi_select & DP_MASK)) {
|
||||
DRM_DEBUG_KMS("use DPLLA for pipe B\n");
|
||||
ref_value |= REF_CLK_DPLLA;
|
||||
} else {
|
||||
|
@ -336,30 +375,33 @@ cdv_dpll_set_clock_cdv(struct drm_device *dev, struct drm_crtc *crtc,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
lane_reg = PSB_LANE0;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
if (ddi_select) {
|
||||
if ((ddi_select & DDI_MASK) == DDI0_SELECT) {
|
||||
lane_reg = PSB_LANE0;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
|
||||
lane_reg = PSB_LANE1;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
|
||||
lane_reg = PSB_LANE2;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
|
||||
lane_reg = PSB_LANE3;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
lane_reg = PSB_LANE1;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
} else {
|
||||
lane_reg = PSB_LANE2;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
|
||||
lane_reg = PSB_LANE3;
|
||||
cdv_sb_read(dev, lane_reg, &lane_value);
|
||||
lane_value &= ~(LANE_PLL_MASK);
|
||||
lane_value |= LANE_PLL_ENABLE | LANE_PLL_PIPE(pipe);
|
||||
cdv_sb_write(dev, lane_reg, lane_value);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -396,6 +438,12 @@ static const struct cdv_intel_limit_t *cdv_intel_limit(struct drm_crtc *crtc,
|
|||
limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_96];
|
||||
else
|
||||
limit = &cdv_intel_limits[CDV_LIMIT_SINGLE_LVDS_100];
|
||||
} else if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
|
||||
psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) {
|
||||
if (refclk == 27000)
|
||||
limit = &cdv_intel_limits[CDV_LIMIT_DP_27];
|
||||
else
|
||||
limit = &cdv_intel_limits[CDV_LIMIT_DP_100];
|
||||
} else {
|
||||
if (refclk == 27000)
|
||||
limit = &cdv_intel_limits[CDV_LIMIT_DAC_HDMI_27];
|
||||
|
@ -438,13 +486,12 @@ static bool cdv_intel_PLL_is_valid(struct drm_crtc *crtc,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target,
|
||||
int refclk,
|
||||
struct cdv_intel_clock_t *best_clock)
|
||||
static bool cdv_intel_find_best_PLL(const struct cdv_intel_limit_t *limit,
|
||||
struct drm_crtc *crtc, int target, int refclk,
|
||||
struct cdv_intel_clock_t *best_clock)
|
||||
{
|
||||
struct drm_device *dev = crtc->dev;
|
||||
struct cdv_intel_clock_t clock;
|
||||
const struct cdv_intel_limit_t *limit = cdv_intel_limit(crtc, refclk);
|
||||
int err = target;
|
||||
|
||||
|
||||
|
@ -498,6 +545,49 @@ static bool cdv_intel_find_best_PLL(struct drm_crtc *crtc, int target,
|
|||
return err != target;
|
||||
}
|
||||
|
||||
static bool cdv_intel_find_dp_pll(const struct cdv_intel_limit_t *limit, struct drm_crtc *crtc, int target,
|
||||
int refclk,
|
||||
struct cdv_intel_clock_t *best_clock)
|
||||
{
|
||||
struct cdv_intel_clock_t clock;
|
||||
if (refclk == 27000) {
|
||||
if (target < 200000) {
|
||||
clock.p1 = 2;
|
||||
clock.p2 = 10;
|
||||
clock.n = 1;
|
||||
clock.m1 = 0;
|
||||
clock.m2 = 118;
|
||||
} else {
|
||||
clock.p1 = 1;
|
||||
clock.p2 = 10;
|
||||
clock.n = 1;
|
||||
clock.m1 = 0;
|
||||
clock.m2 = 98;
|
||||
}
|
||||
} else if (refclk == 100000) {
|
||||
if (target < 200000) {
|
||||
clock.p1 = 2;
|
||||
clock.p2 = 10;
|
||||
clock.n = 5;
|
||||
clock.m1 = 0;
|
||||
clock.m2 = 160;
|
||||
} else {
|
||||
clock.p1 = 1;
|
||||
clock.p2 = 10;
|
||||
clock.n = 5;
|
||||
clock.m1 = 0;
|
||||
clock.m2 = 133;
|
||||
}
|
||||
} else
|
||||
return false;
|
||||
clock.m = clock.m2 + 2;
|
||||
clock.p = clock.p1 * clock.p2;
|
||||
clock.vco = (refclk * clock.m) / clock.n;
|
||||
clock.dot = clock.vco / clock.p;
|
||||
memcpy(best_clock, &clock, sizeof(struct cdv_intel_clock_t));
|
||||
return true;
|
||||
}
|
||||
|
||||
static int cdv_intel_pipe_set_base(struct drm_crtc *crtc,
|
||||
int x, int y, struct drm_framebuffer *old_fb)
|
||||
{
|
||||
|
@ -791,7 +881,7 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
if (psb_intel_crtc->active)
|
||||
return;
|
||||
break;
|
||||
|
||||
psb_intel_crtc->active = true;
|
||||
|
||||
|
@ -835,17 +925,15 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||
REG_WRITE(map->status, temp);
|
||||
REG_READ(map->status);
|
||||
|
||||
cdv_intel_update_watermark(dev, crtc);
|
||||
cdv_intel_crtc_load_lut(crtc);
|
||||
|
||||
/* Give the overlay scaler a chance to enable
|
||||
* if it's on this pipe */
|
||||
/* psb_intel_crtc_dpms_video(crtc, true); TODO */
|
||||
psb_intel_crtc->crtc_enable = true;
|
||||
break;
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
if (!psb_intel_crtc->active)
|
||||
return;
|
||||
break;
|
||||
|
||||
psb_intel_crtc->active = false;
|
||||
|
||||
|
@ -892,10 +980,9 @@ static void cdv_intel_crtc_dpms(struct drm_crtc *crtc, int mode)
|
|||
|
||||
/* Wait for the clocks to turn off. */
|
||||
udelay(150);
|
||||
cdv_intel_update_watermark(dev, crtc);
|
||||
psb_intel_crtc->crtc_enable = false;
|
||||
break;
|
||||
}
|
||||
cdv_intel_update_watermark(dev, crtc);
|
||||
/*Set FIFO Watermarks*/
|
||||
REG_WRITE(DSPARB, 0x3F3E);
|
||||
}
|
||||
|
@ -952,9 +1039,12 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
u32 dpll = 0, dspcntr, pipeconf;
|
||||
bool ok;
|
||||
bool is_crt = false, is_lvds = false, is_tv = false;
|
||||
bool is_hdmi = false;
|
||||
bool is_hdmi = false, is_dp = false;
|
||||
struct drm_mode_config *mode_config = &dev->mode_config;
|
||||
struct drm_connector *connector;
|
||||
const struct cdv_intel_limit_t *limit;
|
||||
u32 ddi_select = 0;
|
||||
bool is_edp = false;
|
||||
|
||||
list_for_each_entry(connector, &mode_config->connector_list, head) {
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
|
@ -964,6 +1054,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
|| connector->encoder->crtc != crtc)
|
||||
continue;
|
||||
|
||||
ddi_select = psb_intel_encoder->ddi_select;
|
||||
switch (psb_intel_encoder->type) {
|
||||
case INTEL_OUTPUT_LVDS:
|
||||
is_lvds = true;
|
||||
|
@ -977,6 +1068,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
case INTEL_OUTPUT_HDMI:
|
||||
is_hdmi = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_DISPLAYPORT:
|
||||
is_dp = true;
|
||||
break;
|
||||
case INTEL_OUTPUT_EDP:
|
||||
is_edp = true;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("invalid output type.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -986,6 +1086,20 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
else
|
||||
/* high-end sku, 27/100 mhz */
|
||||
refclk = 27000;
|
||||
if (is_dp || is_edp) {
|
||||
/*
|
||||
* Based on the spec the low-end SKU has only CRT/LVDS. So it is
|
||||
* unnecessary to consider it for DP/eDP.
|
||||
* On the high-end SKU, it will use the 27/100M reference clk
|
||||
* for DP/eDP. When using SSC clock, the ref clk is 100MHz.Otherwise
|
||||
* it will be 27MHz. From the VBIOS code it seems that the pipe A choose
|
||||
* 27MHz for DP/eDP while the Pipe B chooses the 100MHz.
|
||||
*/
|
||||
if (pipe == 0)
|
||||
refclk = 27000;
|
||||
else
|
||||
refclk = 100000;
|
||||
}
|
||||
|
||||
if (is_lvds && dev_priv->lvds_use_ssc) {
|
||||
refclk = dev_priv->lvds_ssc_freq * 1000;
|
||||
|
@ -993,8 +1107,10 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
}
|
||||
|
||||
drm_mode_debug_printmodeline(adjusted_mode);
|
||||
|
||||
limit = cdv_intel_limit(crtc, refclk);
|
||||
|
||||
ok = cdv_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk,
|
||||
ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk,
|
||||
&clock);
|
||||
if (!ok) {
|
||||
dev_err(dev->dev, "Couldn't find PLL settings for mode!\n");
|
||||
|
@ -1009,6 +1125,15 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
}
|
||||
/* dpll |= PLL_REF_INPUT_DREFCLK; */
|
||||
|
||||
if (is_dp || is_edp) {
|
||||
cdv_intel_dp_set_m_n(crtc, mode, adjusted_mode);
|
||||
} else {
|
||||
REG_WRITE(PIPE_GMCH_DATA_M(pipe), 0);
|
||||
REG_WRITE(PIPE_GMCH_DATA_N(pipe), 0);
|
||||
REG_WRITE(PIPE_DP_LINK_M(pipe), 0);
|
||||
REG_WRITE(PIPE_DP_LINK_N(pipe), 0);
|
||||
}
|
||||
|
||||
dpll |= DPLL_SYNCLOCK_ENABLE;
|
||||
/* if (is_lvds)
|
||||
dpll |= DPLLB_MODE_LVDS;
|
||||
|
@ -1019,6 +1144,31 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
/* setup pipeconf */
|
||||
pipeconf = REG_READ(map->conf);
|
||||
|
||||
pipeconf &= ~(PIPE_BPC_MASK);
|
||||
if (is_edp) {
|
||||
switch (dev_priv->edp.bpp) {
|
||||
case 24:
|
||||
pipeconf |= PIPE_8BPC;
|
||||
break;
|
||||
case 18:
|
||||
pipeconf |= PIPE_6BPC;
|
||||
break;
|
||||
case 30:
|
||||
pipeconf |= PIPE_10BPC;
|
||||
break;
|
||||
default:
|
||||
pipeconf |= PIPE_8BPC;
|
||||
break;
|
||||
}
|
||||
} else if (is_lvds) {
|
||||
/* the BPC will be 6 if it is 18-bit LVDS panel */
|
||||
if ((REG_READ(LVDS) & LVDS_A3_POWER_MASK) == LVDS_A3_POWER_UP)
|
||||
pipeconf |= PIPE_8BPC;
|
||||
else
|
||||
pipeconf |= PIPE_6BPC;
|
||||
} else
|
||||
pipeconf |= PIPE_8BPC;
|
||||
|
||||
/* Set up the display plane register */
|
||||
dspcntr = DISPPLANE_GAMMA_ENABLE;
|
||||
|
||||
|
@ -1033,7 +1183,7 @@ static int cdv_intel_crtc_mode_set(struct drm_crtc *crtc,
|
|||
REG_WRITE(map->dpll, dpll | DPLL_VGA_MODE_DIS | DPLL_SYNCLOCK_ENABLE);
|
||||
REG_READ(map->dpll);
|
||||
|
||||
cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds);
|
||||
cdv_dpll_set_clock_cdv(dev, crtc, &clock, is_lvds, ddi_select);
|
||||
|
||||
udelay(150);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -139,8 +139,6 @@ static enum drm_connector_status cdv_hdmi_detect(
|
|||
{
|
||||
struct psb_intel_encoder *psb_intel_encoder =
|
||||
psb_intel_attached_encoder(connector);
|
||||
struct psb_intel_connector *psb_intel_connector =
|
||||
to_psb_intel_connector(connector);
|
||||
struct mid_intel_hdmi_priv *hdmi_priv = psb_intel_encoder->dev_priv;
|
||||
struct edid *edid = NULL;
|
||||
enum drm_connector_status status = connector_status_disconnected;
|
||||
|
@ -157,8 +155,6 @@ static enum drm_connector_status cdv_hdmi_detect(
|
|||
hdmi_priv->has_hdmi_audio =
|
||||
drm_detect_monitor_audio(edid);
|
||||
}
|
||||
|
||||
psb_intel_connector->base.display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
return status;
|
||||
|
@ -352,9 +348,11 @@ void cdv_hdmi_init(struct drm_device *dev,
|
|||
switch (reg) {
|
||||
case SDVOB:
|
||||
ddc_bus = GPIOE;
|
||||
psb_intel_encoder->ddi_select = DDI0_SELECT;
|
||||
break;
|
||||
case SDVOC:
|
||||
ddc_bus = GPIOD;
|
||||
psb_intel_encoder->ddi_select = DDI1_SELECT;
|
||||
break;
|
||||
default:
|
||||
DRM_ERROR("unknown reg 0x%x for HDMI\n", reg);
|
||||
|
|
|
@ -506,16 +506,8 @@ static int cdv_intel_lvds_set_property(struct drm_connector *connector,
|
|||
property,
|
||||
value))
|
||||
return -1;
|
||||
else {
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *dev_priv =
|
||||
encoder->dev->dev_private;
|
||||
struct backlight_device *bd =
|
||||
dev_priv->backlight_device;
|
||||
bd->props.brightness = value;
|
||||
backlight_update_status(bd);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
gma_backlight_set(encoder->dev, value);
|
||||
} else if (!strcmp(property->name, "DPMS") && encoder) {
|
||||
struct drm_encoder_helper_funcs *helpers =
|
||||
encoder->helper_private;
|
||||
|
|
|
@ -764,6 +764,13 @@ static void psb_setup_outputs(struct drm_device *dev)
|
|||
crtc_mask = dev_priv->ops->hdmi_mask;
|
||||
clone_mask = (1 << INTEL_OUTPUT_HDMI);
|
||||
break;
|
||||
case INTEL_OUTPUT_DISPLAYPORT:
|
||||
crtc_mask = (1 << 0) | (1 << 1);
|
||||
clone_mask = (1 << INTEL_OUTPUT_DISPLAYPORT);
|
||||
break;
|
||||
case INTEL_OUTPUT_EDP:
|
||||
crtc_mask = (1 << 1);
|
||||
clone_mask = (1 << INTEL_OUTPUT_EDP);
|
||||
}
|
||||
encoder->possible_crtcs = crtc_mask;
|
||||
encoder->possible_clones =
|
||||
|
|
|
@ -36,7 +36,12 @@ int psb_gem_init_object(struct drm_gem_object *obj)
|
|||
void psb_gem_free_object(struct drm_gem_object *obj)
|
||||
{
|
||||
struct gtt_range *gtt = container_of(obj, struct gtt_range, gem);
|
||||
drm_gem_object_release_wrap(obj);
|
||||
|
||||
/* Remove the list map if one is present */
|
||||
if (obj->map_list.map)
|
||||
drm_gem_free_mmap_offset(obj);
|
||||
drm_gem_object_release(obj);
|
||||
|
||||
/* This must occur last as it frees up the memory of the GEM object */
|
||||
psb_gtt_free_range(obj->dev, gtt);
|
||||
}
|
||||
|
@ -77,7 +82,7 @@ int psb_gem_dumb_map_gtt(struct drm_file *file, struct drm_device *dev,
|
|||
|
||||
/* Make it mmapable */
|
||||
if (!obj->map_list.map) {
|
||||
ret = gem_create_mmap_offset(obj);
|
||||
ret = drm_gem_create_mmap_offset(obj);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -1,90 +0,0 @@
|
|||
/**************************************************************************
|
||||
* Copyright (c) 2011, Intel Corporation.
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with
|
||||
* this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
**************************************************************************/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm.h>
|
||||
#include "gem_glue.h"
|
||||
|
||||
void drm_gem_object_release_wrap(struct drm_gem_object *obj)
|
||||
{
|
||||
/* Remove the list map if one is present */
|
||||
if (obj->map_list.map) {
|
||||
struct drm_gem_mm *mm = obj->dev->mm_private;
|
||||
struct drm_map_list *list = &obj->map_list;
|
||||
drm_ht_remove_item(&mm->offset_hash, &list->hash);
|
||||
drm_mm_put_block(list->file_offset_node);
|
||||
kfree(list->map);
|
||||
list->map = NULL;
|
||||
}
|
||||
drm_gem_object_release(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* gem_create_mmap_offset - invent an mmap offset
|
||||
* @obj: our object
|
||||
*
|
||||
* Standard implementation of offset generation for mmap as is
|
||||
* duplicated in several drivers. This belongs in GEM.
|
||||
*/
|
||||
int gem_create_mmap_offset(struct drm_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->dev;
|
||||
struct drm_gem_mm *mm = dev->mm_private;
|
||||
struct drm_map_list *list;
|
||||
struct drm_local_map *map;
|
||||
int ret;
|
||||
|
||||
list = &obj->map_list;
|
||||
list->map = kzalloc(sizeof(struct drm_map_list), GFP_KERNEL);
|
||||
if (list->map == NULL)
|
||||
return -ENOMEM;
|
||||
map = list->map;
|
||||
map->type = _DRM_GEM;
|
||||
map->size = obj->size;
|
||||
map->handle = obj;
|
||||
|
||||
list->file_offset_node = drm_mm_search_free(&mm->offset_manager,
|
||||
obj->size / PAGE_SIZE, 0, 0);
|
||||
if (!list->file_offset_node) {
|
||||
dev_err(dev->dev, "failed to allocate offset for bo %d\n",
|
||||
obj->name);
|
||||
ret = -ENOSPC;
|
||||
goto free_it;
|
||||
}
|
||||
list->file_offset_node = drm_mm_get_block(list->file_offset_node,
|
||||
obj->size / PAGE_SIZE, 0);
|
||||
if (!list->file_offset_node) {
|
||||
ret = -ENOMEM;
|
||||
goto free_it;
|
||||
}
|
||||
list->hash.key = list->file_offset_node->start;
|
||||
ret = drm_ht_insert_item(&mm->offset_hash, &list->hash);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "failed to add to map hash\n");
|
||||
goto free_mm;
|
||||
}
|
||||
return 0;
|
||||
|
||||
free_mm:
|
||||
drm_mm_put_block(list->file_offset_node);
|
||||
free_it:
|
||||
kfree(list->map);
|
||||
list->map = NULL;
|
||||
return ret;
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
extern void drm_gem_object_release_wrap(struct drm_gem_object *obj);
|
||||
extern int gem_create_mmap_offset(struct drm_gem_object *obj);
|
|
@ -54,6 +54,98 @@ static void *find_section(struct bdb_header *bdb, int section_id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
parse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb)
|
||||
{
|
||||
struct bdb_edp *edp;
|
||||
struct edp_power_seq *edp_pps;
|
||||
struct edp_link_params *edp_link_params;
|
||||
uint8_t panel_type;
|
||||
|
||||
edp = find_section(bdb, BDB_EDP);
|
||||
|
||||
dev_priv->edp.bpp = 18;
|
||||
if (!edp) {
|
||||
if (dev_priv->edp.support) {
|
||||
DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n",
|
||||
dev_priv->edp.bpp);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
panel_type = dev_priv->panel_type;
|
||||
switch ((edp->color_depth >> (panel_type * 2)) & 3) {
|
||||
case EDP_18BPP:
|
||||
dev_priv->edp.bpp = 18;
|
||||
break;
|
||||
case EDP_24BPP:
|
||||
dev_priv->edp.bpp = 24;
|
||||
break;
|
||||
case EDP_30BPP:
|
||||
dev_priv->edp.bpp = 30;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Get the eDP sequencing and link info */
|
||||
edp_pps = &edp->power_seqs[panel_type];
|
||||
edp_link_params = &edp->link_params[panel_type];
|
||||
|
||||
dev_priv->edp.pps = *edp_pps;
|
||||
|
||||
DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
|
||||
dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8,
|
||||
dev_priv->edp.pps.t9, dev_priv->edp.pps.t10,
|
||||
dev_priv->edp.pps.t11_t12);
|
||||
|
||||
dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
|
||||
DP_LINK_BW_1_62;
|
||||
switch (edp_link_params->lanes) {
|
||||
case 0:
|
||||
dev_priv->edp.lanes = 1;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->edp.lanes = 2;
|
||||
break;
|
||||
case 3:
|
||||
default:
|
||||
dev_priv->edp.lanes = 4;
|
||||
break;
|
||||
}
|
||||
DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n",
|
||||
dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp);
|
||||
|
||||
switch (edp_link_params->preemphasis) {
|
||||
case 0:
|
||||
dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_0;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_3_5;
|
||||
break;
|
||||
case 2:
|
||||
dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_6;
|
||||
break;
|
||||
case 3:
|
||||
dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPHASIS_9_5;
|
||||
break;
|
||||
}
|
||||
switch (edp_link_params->vswing) {
|
||||
case 0:
|
||||
dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_400;
|
||||
break;
|
||||
case 1:
|
||||
dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_600;
|
||||
break;
|
||||
case 2:
|
||||
dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_800;
|
||||
break;
|
||||
case 3:
|
||||
dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_1200;
|
||||
break;
|
||||
}
|
||||
DRM_DEBUG_KMS("VBT reports EDP: VSwing %d, Preemph %d\n",
|
||||
dev_priv->edp.vswing, dev_priv->edp.preemphasis);
|
||||
}
|
||||
|
||||
static u16
|
||||
get_blocksize(void *p)
|
||||
{
|
||||
|
@ -154,6 +246,8 @@ static void parse_lfp_panel_data(struct drm_psb_private *dev_priv,
|
|||
return;
|
||||
|
||||
dev_priv->lvds_dither = lvds_options->pixel_dither;
|
||||
dev_priv->panel_type = lvds_options->panel_type;
|
||||
|
||||
if (lvds_options->panel_type == 0xff)
|
||||
return;
|
||||
|
||||
|
@ -340,6 +434,9 @@ parse_driver_features(struct drm_psb_private *dev_priv,
|
|||
if (!driver)
|
||||
return;
|
||||
|
||||
if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
|
||||
dev_priv->edp.support = 1;
|
||||
|
||||
/* This bit means to use 96Mhz for DPLL_A or not */
|
||||
if (driver->primary_lfp_id)
|
||||
dev_priv->dplla_96mhz = true;
|
||||
|
@ -437,6 +534,9 @@ int psb_intel_init_bios(struct drm_device *dev)
|
|||
size_t size;
|
||||
int i;
|
||||
|
||||
|
||||
dev_priv->panel_type = 0xff;
|
||||
|
||||
/* XXX Should this validation be moved to intel_opregion.c? */
|
||||
if (dev_priv->opregion.vbt) {
|
||||
struct vbt_header *vbt = dev_priv->opregion.vbt;
|
||||
|
@ -477,6 +577,7 @@ int psb_intel_init_bios(struct drm_device *dev)
|
|||
parse_sdvo_device_mapping(dev_priv, bdb);
|
||||
parse_device_mapping(dev_priv, bdb);
|
||||
parse_backlight_data(dev_priv, bdb);
|
||||
parse_edp(dev_priv, bdb);
|
||||
|
||||
if (bios)
|
||||
pci_unmap_rom(pdev, bios);
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#define _I830_BIOS_H_
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
struct vbt_header {
|
||||
u8 signature[20]; /**< Always starts with 'VBT$' */
|
||||
|
@ -93,6 +94,7 @@ struct vbios_data {
|
|||
#define BDB_SDVO_LVDS_PNP_IDS 24
|
||||
#define BDB_SDVO_LVDS_POWER_SEQ 25
|
||||
#define BDB_TV_OPTIONS 26
|
||||
#define BDB_EDP 27
|
||||
#define BDB_LVDS_OPTIONS 40
|
||||
#define BDB_LVDS_LFP_DATA_PTRS 41
|
||||
#define BDB_LVDS_LFP_DATA 42
|
||||
|
@ -391,6 +393,11 @@ struct bdb_sdvo_lvds_options {
|
|||
u8 panel_misc_bits_4;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define BDB_DRIVER_FEATURE_NO_LVDS 0
|
||||
#define BDB_DRIVER_FEATURE_INT_LVDS 1
|
||||
#define BDB_DRIVER_FEATURE_SDVO_LVDS 2
|
||||
#define BDB_DRIVER_FEATURE_EDP 3
|
||||
|
||||
struct bdb_driver_features {
|
||||
u8 boot_dev_algorithm:1;
|
||||
u8 block_display_switch:1;
|
||||
|
@ -431,6 +438,45 @@ struct bdb_driver_features {
|
|||
u8 custom_vbt_version;
|
||||
} __attribute__((packed));
|
||||
|
||||
#define EDP_18BPP 0
|
||||
#define EDP_24BPP 1
|
||||
#define EDP_30BPP 2
|
||||
#define EDP_RATE_1_62 0
|
||||
#define EDP_RATE_2_7 1
|
||||
#define EDP_LANE_1 0
|
||||
#define EDP_LANE_2 1
|
||||
#define EDP_LANE_4 3
|
||||
#define EDP_PREEMPHASIS_NONE 0
|
||||
#define EDP_PREEMPHASIS_3_5dB 1
|
||||
#define EDP_PREEMPHASIS_6dB 2
|
||||
#define EDP_PREEMPHASIS_9_5dB 3
|
||||
#define EDP_VSWING_0_4V 0
|
||||
#define EDP_VSWING_0_6V 1
|
||||
#define EDP_VSWING_0_8V 2
|
||||
#define EDP_VSWING_1_2V 3
|
||||
|
||||
struct edp_power_seq {
|
||||
u16 t1_t3;
|
||||
u16 t8;
|
||||
u16 t9;
|
||||
u16 t10;
|
||||
u16 t11_t12;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct edp_link_params {
|
||||
u8 rate:4;
|
||||
u8 lanes:4;
|
||||
u8 preemphasis:4;
|
||||
u8 vswing:4;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bdb_edp {
|
||||
struct edp_power_seq power_seqs[16];
|
||||
u32 color_depth;
|
||||
u32 sdrrs_msa_timing_delay;
|
||||
struct edp_link_params link_params[16];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
extern int psb_intel_init_bios(struct drm_device *dev);
|
||||
extern void psb_intel_destroy_bios(struct drm_device *dev);
|
||||
|
||||
|
|
|
@ -299,17 +299,8 @@ static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
|
|||
if (drm_connector_property_set_value(connector, property,
|
||||
value))
|
||||
goto set_prop_error;
|
||||
else {
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct backlight_device *psb_bd;
|
||||
|
||||
psb_bd = mdfld_get_backlight_device();
|
||||
if (psb_bd) {
|
||||
psb_bd->props.brightness = value;
|
||||
mdfld_set_brightness(psb_bd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
gma_backlight_set(encoder->dev, value);
|
||||
}
|
||||
set_prop_done:
|
||||
return 0;
|
||||
|
|
|
@ -118,20 +118,20 @@ static void mid_get_pci_revID(struct drm_psb_private *dev_priv)
|
|||
dev_priv->platform_rev_id);
|
||||
}
|
||||
|
||||
struct vbt_header {
|
||||
struct mid_vbt_header {
|
||||
u32 signature;
|
||||
u8 revision;
|
||||
} __packed;
|
||||
|
||||
/* The same for r0 and r1 */
|
||||
struct vbt_r0 {
|
||||
struct vbt_header vbt_header;
|
||||
struct mid_vbt_header vbt_header;
|
||||
u8 size;
|
||||
u8 checksum;
|
||||
} __packed;
|
||||
|
||||
struct vbt_r10 {
|
||||
struct vbt_header vbt_header;
|
||||
struct mid_vbt_header vbt_header;
|
||||
u8 checksum;
|
||||
u16 size;
|
||||
u8 panel_count;
|
||||
|
@ -281,7 +281,7 @@ static void mid_get_vbt_data(struct drm_psb_private *dev_priv)
|
|||
struct drm_device *dev = dev_priv->dev;
|
||||
u32 addr;
|
||||
u8 __iomem *vbt_virtual;
|
||||
struct vbt_header vbt_header;
|
||||
struct mid_vbt_header vbt_header;
|
||||
struct pci_dev *pci_gfx_root = pci_get_bus_and_slot(0, PCI_DEVFN(2, 0));
|
||||
int ret = -1;
|
||||
|
||||
|
|
|
@ -252,7 +252,6 @@ static int oaktrail_hdmi_get_modes(struct drm_connector *connector)
|
|||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
connector->display_info.raw_edid = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -166,8 +166,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
|
|||
|
||||
if (config_enabled(CONFIG_BACKLIGHT_CLASS_DEVICE)) {
|
||||
int max = bd->props.max_brightness;
|
||||
bd->props.brightness = bclp * max / 255;
|
||||
backlight_update_status(bd);
|
||||
gma_backlight_set(dev, bclp * max / 255);
|
||||
}
|
||||
|
||||
asle->cblv = (bclp * 0x64) / 0xff | ASLE_CBLV_VALID;
|
||||
|
|
|
@ -290,6 +290,7 @@ static void psb_get_core_freq(struct drm_device *dev)
|
|||
case 6:
|
||||
case 7:
|
||||
dev_priv->core_freq = 266;
|
||||
break;
|
||||
default:
|
||||
dev_priv->core_freq = 0;
|
||||
}
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
|
||||
#include <drm/drmP.h>
|
||||
#include <drm/drm_global.h>
|
||||
#include "gem_glue.h"
|
||||
#include <drm/gma_drm.h>
|
||||
#include "psb_reg.h"
|
||||
#include "psb_intel_drv.h"
|
||||
#include "intel_bios.h"
|
||||
#include "gtt.h"
|
||||
#include "power.h"
|
||||
#include "opregion.h"
|
||||
|
@ -613,6 +613,8 @@ struct drm_psb_private {
|
|||
*/
|
||||
struct backlight_device *backlight_device;
|
||||
struct drm_property *backlight_property;
|
||||
bool backlight_enabled;
|
||||
int backlight_level;
|
||||
uint32_t blc_adj1;
|
||||
uint32_t blc_adj2;
|
||||
|
||||
|
@ -640,6 +642,19 @@ struct drm_psb_private {
|
|||
int mdfld_panel_id;
|
||||
|
||||
bool dplla_96mhz; /* DPLL data from the VBT */
|
||||
|
||||
struct {
|
||||
int rate;
|
||||
int lanes;
|
||||
int preemphasis;
|
||||
int vswing;
|
||||
|
||||
bool initialized;
|
||||
bool support;
|
||||
int bpp;
|
||||
struct edp_power_seq pps;
|
||||
} edp;
|
||||
uint8_t panel_type;
|
||||
};
|
||||
|
||||
|
||||
|
@ -796,6 +811,9 @@ extern int psb_fbdev_init(struct drm_device *dev);
|
|||
/* backlight.c */
|
||||
int gma_backlight_init(struct drm_device *dev);
|
||||
void gma_backlight_exit(struct drm_device *dev);
|
||||
void gma_backlight_disable(struct drm_device *dev);
|
||||
void gma_backlight_enable(struct drm_device *dev);
|
||||
void gma_backlight_set(struct drm_device *dev, int v);
|
||||
|
||||
/* oaktrail_crtc.c */
|
||||
extern const struct drm_crtc_helper_funcs oaktrail_helper_funcs;
|
||||
|
|
|
@ -29,10 +29,6 @@
|
|||
* Display related stuff
|
||||
*/
|
||||
|
||||
/* store information about an Ixxx DVO */
|
||||
/* The i830->i865 use multiple DVOs with multiple i2cs */
|
||||
/* the i915, i945 have a single sDVO i2c bus - which is different */
|
||||
#define MAX_OUTPUTS 6
|
||||
/* maximum connectors per crtcs in the mode set */
|
||||
#define INTELFB_CONN_LIMIT 4
|
||||
|
||||
|
@ -69,6 +65,8 @@
|
|||
#define INTEL_OUTPUT_HDMI 6
|
||||
#define INTEL_OUTPUT_MIPI 7
|
||||
#define INTEL_OUTPUT_MIPI2 8
|
||||
#define INTEL_OUTPUT_DISPLAYPORT 9
|
||||
#define INTEL_OUTPUT_EDP 10
|
||||
|
||||
#define INTEL_DVO_CHIP_NONE 0
|
||||
#define INTEL_DVO_CHIP_LVDS 1
|
||||
|
@ -133,6 +131,11 @@ struct psb_intel_encoder {
|
|||
void (*hot_plug)(struct psb_intel_encoder *);
|
||||
int crtc_mask;
|
||||
int clone_mask;
|
||||
u32 ddi_select; /* Channel info */
|
||||
#define DDI0_SELECT 0x01
|
||||
#define DDI1_SELECT 0x02
|
||||
#define DP_MASK 0x8000
|
||||
#define DDI_MASK 0x03
|
||||
void *dev_priv; /* For sdvo_priv, lvds_priv, etc... */
|
||||
|
||||
/* FIXME: Either make SDVO and LVDS store it's i2c here or give CDV it's
|
||||
|
@ -190,7 +193,6 @@ struct psb_intel_crtc {
|
|||
u32 mode_flags;
|
||||
|
||||
bool active;
|
||||
bool crtc_enable;
|
||||
|
||||
/* Saved Crtc HW states */
|
||||
struct psb_intel_crtc_state *crtc_state;
|
||||
|
@ -285,4 +287,20 @@ extern void gma_intel_gmbus_set_speed(struct i2c_adapter *adapter, int speed);
|
|||
extern void gma_intel_gmbus_force_bit(struct i2c_adapter *adapter, bool force_bit);
|
||||
extern void gma_intel_teardown_gmbus(struct drm_device *dev);
|
||||
|
||||
/* DP support */
|
||||
extern void cdv_intel_dp_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev, int output_reg);
|
||||
extern void cdv_intel_dp_set_m_n(struct drm_crtc *crtc,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
|
||||
extern void psb_intel_attach_force_audio_property(struct drm_connector *connector);
|
||||
extern void psb_intel_attach_broadcast_rgb_property(struct drm_connector *connector);
|
||||
|
||||
extern int cdv_sb_read(struct drm_device *dev, u32 reg, u32 *val);
|
||||
extern int cdv_sb_write(struct drm_device *dev, u32 reg, u32 val);
|
||||
extern void cdv_sb_reset(struct drm_device *dev);
|
||||
|
||||
extern void cdv_intel_attach_force_audio_property(struct drm_connector *connector);
|
||||
extern void cdv_intel_attach_broadcast_rgb_property(struct drm_connector *connector);
|
||||
|
||||
#endif /* __INTEL_DRV_H__ */
|
||||
|
|
|
@ -630,17 +630,8 @@ int psb_intel_lvds_set_property(struct drm_connector *connector,
|
|||
property,
|
||||
value))
|
||||
goto set_prop_error;
|
||||
else {
|
||||
#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
|
||||
struct drm_psb_private *devp =
|
||||
encoder->dev->dev_private;
|
||||
struct backlight_device *bd = devp->backlight_device;
|
||||
if (bd) {
|
||||
bd->props.brightness = value;
|
||||
backlight_update_status(bd);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
gma_backlight_set(encoder->dev, value);
|
||||
} else if (!strcmp(property->name, "DPMS")) {
|
||||
struct drm_encoder_helper_funcs *hfuncs
|
||||
= encoder->helper_private;
|
||||
|
|
|
@ -173,15 +173,46 @@
|
|||
#define PP_SEQUENCE_ON (1 << 28)
|
||||
#define PP_SEQUENCE_OFF (2 << 28)
|
||||
#define PP_SEQUENCE_MASK 0x30000000
|
||||
#define PP_CYCLE_DELAY_ACTIVE (1 << 27)
|
||||
#define PP_SEQUENCE_STATE_ON_IDLE (1 << 3)
|
||||
#define PP_SEQUENCE_STATE_MASK 0x0000000f
|
||||
|
||||
#define PP_CONTROL 0x61204
|
||||
#define POWER_TARGET_ON (1 << 0)
|
||||
#define PANEL_UNLOCK_REGS (0xabcd << 16)
|
||||
#define PANEL_UNLOCK_MASK (0xffff << 16)
|
||||
#define EDP_FORCE_VDD (1 << 3)
|
||||
#define EDP_BLC_ENABLE (1 << 2)
|
||||
#define PANEL_POWER_RESET (1 << 1)
|
||||
#define PANEL_POWER_OFF (0 << 0)
|
||||
#define PANEL_POWER_ON (1 << 0)
|
||||
|
||||
/* Poulsbo/Oaktrail */
|
||||
#define LVDSPP_ON 0x61208
|
||||
#define LVDSPP_OFF 0x6120c
|
||||
#define PP_CYCLE 0x61210
|
||||
|
||||
/* Cedartrail */
|
||||
#define PP_ON_DELAYS 0x61208 /* Cedartrail */
|
||||
#define PANEL_PORT_SELECT_MASK (3 << 30)
|
||||
#define PANEL_PORT_SELECT_LVDS (0 << 30)
|
||||
#define PANEL_PORT_SELECT_EDP (1 << 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 PP_OFF_DELAYS 0x6120c /* Cedartrail */
|
||||
#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 PP_DIVISOR 0x61210 /* Cedartrail */
|
||||
#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 PFIT_CONTROL 0x61230
|
||||
#define PFIT_ENABLE (1 << 31)
|
||||
|
@ -1282,6 +1313,10 @@ No status bits are changed.
|
|||
# define VRHUNIT_CLOCK_GATE_DISABLE (1 << 28) /* Fixed value on CDV */
|
||||
# define DPOUNIT_CLOCK_GATE_DISABLE (1 << 11)
|
||||
# define DPIOUNIT_CLOCK_GATE_DISABLE (1 << 6)
|
||||
# define DPUNIT_PIPEB_GATE_DISABLE (1 << 30)
|
||||
# define DPUNIT_PIPEA_GATE_DISABLE (1 << 25)
|
||||
# define DPCUNIT_CLOCK_GATE_DISABLE (1 << 24)
|
||||
# define DPLSUNIT_CLOCK_GATE_DISABLE (1 << 13)
|
||||
|
||||
#define RAMCLK_GATE_D 0x6210
|
||||
|
||||
|
@ -1347,5 +1382,165 @@ No status bits are changed.
|
|||
#define LANE_PLL_ENABLE (0x3 << 20)
|
||||
#define LANE_PLL_PIPE(p) (((p) == 0) ? (1 << 21) : (0 << 21))
|
||||
|
||||
#define DP_B 0x64100
|
||||
#define DP_C 0x64200
|
||||
|
||||
#define DP_PORT_EN (1 << 31)
|
||||
#define DP_PIPEB_SELECT (1 << 30)
|
||||
#define DP_PIPE_MASK (1 << 30)
|
||||
|
||||
/* Link training mode - select a suitable mode for each stage */
|
||||
#define DP_LINK_TRAIN_PAT_1 (0 << 28)
|
||||
#define DP_LINK_TRAIN_PAT_2 (1 << 28)
|
||||
#define DP_LINK_TRAIN_PAT_IDLE (2 << 28)
|
||||
#define DP_LINK_TRAIN_OFF (3 << 28)
|
||||
#define DP_LINK_TRAIN_MASK (3 << 28)
|
||||
#define DP_LINK_TRAIN_SHIFT 28
|
||||
|
||||
/* Signal voltages. These are mostly controlled by the other end */
|
||||
#define DP_VOLTAGE_0_4 (0 << 25)
|
||||
#define DP_VOLTAGE_0_6 (1 << 25)
|
||||
#define DP_VOLTAGE_0_8 (2 << 25)
|
||||
#define DP_VOLTAGE_1_2 (3 << 25)
|
||||
#define DP_VOLTAGE_MASK (7 << 25)
|
||||
#define DP_VOLTAGE_SHIFT 25
|
||||
|
||||
/* Signal pre-emphasis levels, like voltages, the other end tells us what
|
||||
* they want
|
||||
*/
|
||||
#define DP_PRE_EMPHASIS_0 (0 << 22)
|
||||
#define DP_PRE_EMPHASIS_3_5 (1 << 22)
|
||||
#define DP_PRE_EMPHASIS_6 (2 << 22)
|
||||
#define DP_PRE_EMPHASIS_9_5 (3 << 22)
|
||||
#define DP_PRE_EMPHASIS_MASK (7 << 22)
|
||||
#define DP_PRE_EMPHASIS_SHIFT 22
|
||||
|
||||
/* How many wires to use. I guess 3 was too hard */
|
||||
#define DP_PORT_WIDTH_1 (0 << 19)
|
||||
#define DP_PORT_WIDTH_2 (1 << 19)
|
||||
#define DP_PORT_WIDTH_4 (3 << 19)
|
||||
#define DP_PORT_WIDTH_MASK (7 << 19)
|
||||
|
||||
/* Mystic DPCD version 1.1 special mode */
|
||||
#define DP_ENHANCED_FRAMING (1 << 18)
|
||||
|
||||
/** locked once port is enabled */
|
||||
#define DP_PORT_REVERSAL (1 << 15)
|
||||
|
||||
/** sends the clock on lane 15 of the PEG for debug */
|
||||
#define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
|
||||
|
||||
#define DP_SCRAMBLING_DISABLE (1 << 12)
|
||||
#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7)
|
||||
|
||||
/** limit RGB values to avoid confusing TVs */
|
||||
#define DP_COLOR_RANGE_16_235 (1 << 8)
|
||||
|
||||
/** Turn on the audio link */
|
||||
#define DP_AUDIO_OUTPUT_ENABLE (1 << 6)
|
||||
|
||||
/** vs and hs sync polarity */
|
||||
#define DP_SYNC_VS_HIGH (1 << 4)
|
||||
#define DP_SYNC_HS_HIGH (1 << 3)
|
||||
|
||||
/** A fantasy */
|
||||
#define DP_DETECTED (1 << 2)
|
||||
|
||||
/** The aux channel provides a way to talk to the
|
||||
* signal sink for DDC etc. Max packet size supported
|
||||
* is 20 bytes in each direction, hence the 5 fixed
|
||||
* data registers
|
||||
*/
|
||||
#define DPB_AUX_CH_CTL 0x64110
|
||||
#define DPB_AUX_CH_DATA1 0x64114
|
||||
#define DPB_AUX_CH_DATA2 0x64118
|
||||
#define DPB_AUX_CH_DATA3 0x6411c
|
||||
#define DPB_AUX_CH_DATA4 0x64120
|
||||
#define DPB_AUX_CH_DATA5 0x64124
|
||||
|
||||
#define DPC_AUX_CH_CTL 0x64210
|
||||
#define DPC_AUX_CH_DATA1 0x64214
|
||||
#define DPC_AUX_CH_DATA2 0x64218
|
||||
#define DPC_AUX_CH_DATA3 0x6421c
|
||||
#define DPC_AUX_CH_DATA4 0x64220
|
||||
#define DPC_AUX_CH_DATA5 0x64224
|
||||
|
||||
#define DP_AUX_CH_CTL_SEND_BUSY (1 << 31)
|
||||
#define DP_AUX_CH_CTL_DONE (1 << 30)
|
||||
#define DP_AUX_CH_CTL_INTERRUPT (1 << 29)
|
||||
#define DP_AUX_CH_CTL_TIME_OUT_ERROR (1 << 28)
|
||||
#define DP_AUX_CH_CTL_TIME_OUT_400us (0 << 26)
|
||||
#define DP_AUX_CH_CTL_TIME_OUT_600us (1 << 26)
|
||||
#define DP_AUX_CH_CTL_TIME_OUT_800us (2 << 26)
|
||||
#define DP_AUX_CH_CTL_TIME_OUT_1600us (3 << 26)
|
||||
#define DP_AUX_CH_CTL_TIME_OUT_MASK (3 << 26)
|
||||
#define DP_AUX_CH_CTL_RECEIVE_ERROR (1 << 25)
|
||||
#define DP_AUX_CH_CTL_MESSAGE_SIZE_MASK (0x1f << 20)
|
||||
#define DP_AUX_CH_CTL_MESSAGE_SIZE_SHIFT 20
|
||||
#define DP_AUX_CH_CTL_PRECHARGE_2US_MASK (0xf << 16)
|
||||
#define DP_AUX_CH_CTL_PRECHARGE_2US_SHIFT 16
|
||||
#define DP_AUX_CH_CTL_AUX_AKSV_SELECT (1 << 15)
|
||||
#define DP_AUX_CH_CTL_MANCHESTER_TEST (1 << 14)
|
||||
#define DP_AUX_CH_CTL_SYNC_TEST (1 << 13)
|
||||
#define DP_AUX_CH_CTL_DEGLITCH_TEST (1 << 12)
|
||||
#define DP_AUX_CH_CTL_PRECHARGE_TEST (1 << 11)
|
||||
#define DP_AUX_CH_CTL_BIT_CLOCK_2X_MASK (0x7ff)
|
||||
#define DP_AUX_CH_CTL_BIT_CLOCK_2X_SHIFT 0
|
||||
|
||||
/*
|
||||
* Computing GMCH M and N values for the Display Port link
|
||||
*
|
||||
* GMCH M/N = dot clock * bytes per pixel / ls_clk * # of lanes
|
||||
*
|
||||
* ls_clk (we assume) is the DP link clock (1.62 or 2.7 GHz)
|
||||
*
|
||||
* The GMCH value is used internally
|
||||
*
|
||||
* bytes_per_pixel is the number of bytes coming out of the plane,
|
||||
* which is after the LUTs, so we want the bytes for our color format.
|
||||
* For our current usage, this is always 3, one byte for R, G and B.
|
||||
*/
|
||||
|
||||
#define _PIPEA_GMCH_DATA_M 0x70050
|
||||
#define _PIPEB_GMCH_DATA_M 0x71050
|
||||
|
||||
/* Transfer unit size for display port - 1, default is 0x3f (for TU size 64) */
|
||||
#define PIPE_GMCH_DATA_M_TU_SIZE_MASK (0x3f << 25)
|
||||
#define PIPE_GMCH_DATA_M_TU_SIZE_SHIFT 25
|
||||
|
||||
#define PIPE_GMCH_DATA_M_MASK (0xffffff)
|
||||
|
||||
#define _PIPEA_GMCH_DATA_N 0x70054
|
||||
#define _PIPEB_GMCH_DATA_N 0x71054
|
||||
#define PIPE_GMCH_DATA_N_MASK (0xffffff)
|
||||
|
||||
/*
|
||||
* Computing Link M and N values for the Display Port link
|
||||
*
|
||||
* Link M / N = pixel_clock / ls_clk
|
||||
*
|
||||
* (the DP spec calls pixel_clock the 'strm_clk')
|
||||
*
|
||||
* The Link value is transmitted in the Main Stream
|
||||
* Attributes and VB-ID.
|
||||
*/
|
||||
|
||||
#define _PIPEA_DP_LINK_M 0x70060
|
||||
#define _PIPEB_DP_LINK_M 0x71060
|
||||
#define PIPEA_DP_LINK_M_MASK (0xffffff)
|
||||
|
||||
#define _PIPEA_DP_LINK_N 0x70064
|
||||
#define _PIPEB_DP_LINK_N 0x71064
|
||||
#define PIPEA_DP_LINK_N_MASK (0xffffff)
|
||||
|
||||
#define PIPE_GMCH_DATA_M(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_M, _PIPEB_GMCH_DATA_M)
|
||||
#define PIPE_GMCH_DATA_N(pipe) _PIPE(pipe, _PIPEA_GMCH_DATA_N, _PIPEB_GMCH_DATA_N)
|
||||
#define PIPE_DP_LINK_M(pipe) _PIPE(pipe, _PIPEA_DP_LINK_M, _PIPEB_DP_LINK_M)
|
||||
#define PIPE_DP_LINK_N(pipe) _PIPE(pipe, _PIPEA_DP_LINK_N, _PIPEB_DP_LINK_N)
|
||||
|
||||
#define PIPE_BPC_MASK (7 << 5)
|
||||
#define PIPE_8BPC (0 << 5)
|
||||
#define PIPE_10BPC (1 << 5)
|
||||
#define PIPE_6BPC (2 << 5)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1291,7 +1291,6 @@ psb_intel_sdvo_get_analog_edid(struct drm_connector *connector)
|
|||
|
||||
return drm_get_edid(connector,
|
||||
&dev_priv->gmbus[dev_priv->crt_ddc_pin].adapter);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static enum drm_connector_status
|
||||
|
@ -1342,7 +1341,6 @@ psb_intel_sdvo_hdmi_sink_detect(struct drm_connector *connector)
|
|||
}
|
||||
} else
|
||||
status = connector_status_disconnected;
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
|
@ -1403,7 +1401,6 @@ psb_intel_sdvo_detect(struct drm_connector *connector, bool force)
|
|||
ret = connector_status_disconnected;
|
||||
else
|
||||
ret = connector_status_connected;
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
} else
|
||||
ret = connector_status_connected;
|
||||
|
@ -1452,7 +1449,6 @@ static void psb_intel_sdvo_get_ddc_modes(struct drm_connector *connector)
|
|||
drm_add_edid_modes(connector, edid);
|
||||
}
|
||||
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -427,15 +427,10 @@ static int ch7006_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ch7006_suspend(struct i2c_client *client, pm_message_t mesg)
|
||||
static int ch7006_resume(struct device *dev)
|
||||
{
|
||||
ch7006_dbg(client, "\n");
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ch7006_resume(struct i2c_client *client)
|
||||
{
|
||||
ch7006_dbg(client, "\n");
|
||||
|
||||
ch7006_write(client, 0x3d, 0x0);
|
||||
|
@ -499,15 +494,18 @@ static struct i2c_device_id ch7006_ids[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ch7006_ids);
|
||||
|
||||
static const struct dev_pm_ops ch7006_pm_ops = {
|
||||
.resume = ch7006_resume,
|
||||
};
|
||||
|
||||
static struct drm_i2c_encoder_driver ch7006_driver = {
|
||||
.i2c_driver = {
|
||||
.probe = ch7006_probe,
|
||||
.remove = ch7006_remove,
|
||||
.suspend = ch7006_suspend,
|
||||
.resume = ch7006_resume,
|
||||
|
||||
.driver = {
|
||||
.name = "ch7006",
|
||||
.pm = &ch7006_pm_ops,
|
||||
},
|
||||
|
||||
.id_table = ch7006_ids,
|
||||
|
|
|
@ -40,6 +40,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
|
|||
dvo_ivch.o \
|
||||
dvo_tfp410.o \
|
||||
dvo_sil164.o \
|
||||
dvo_ns2501.o \
|
||||
i915_gem_dmabuf.o
|
||||
|
||||
i915-$(CONFIG_COMPAT) += i915_ioc32.o
|
||||
|
|
|
@ -57,13 +57,12 @@ struct intel_dvo_dev_ops {
|
|||
void (*create_resources)(struct intel_dvo_device *dvo);
|
||||
|
||||
/*
|
||||
* Turn on/off output or set intermediate power levels if available.
|
||||
* Turn on/off output.
|
||||
*
|
||||
* Unsupported intermediate modes drop to the lower power setting.
|
||||
* If the mode is DPMSModeOff, the output must be disabled,
|
||||
* as the DPLL may be disabled afterwards.
|
||||
* Because none of our dvo drivers support an intermediate power levels,
|
||||
* we don't expose this in the interfac.
|
||||
*/
|
||||
void (*dpms)(struct intel_dvo_device *dvo, int mode);
|
||||
void (*dpms)(struct intel_dvo_device *dvo, bool enable);
|
||||
|
||||
/*
|
||||
* Callback for testing a video mode for a given output.
|
||||
|
@ -114,6 +113,12 @@ struct intel_dvo_dev_ops {
|
|||
*/
|
||||
enum drm_connector_status (*detect)(struct intel_dvo_device *dvo);
|
||||
|
||||
/*
|
||||
* Probe the current hw status, returning true if the connected output
|
||||
* is active.
|
||||
*/
|
||||
bool (*get_hw_state)(struct intel_dvo_device *dev);
|
||||
|
||||
/**
|
||||
* Query the device for the modes it provides.
|
||||
*
|
||||
|
@ -139,5 +144,6 @@ extern struct intel_dvo_dev_ops ch7xxx_ops;
|
|||
extern struct intel_dvo_dev_ops ivch_ops;
|
||||
extern struct intel_dvo_dev_ops tfp410_ops;
|
||||
extern struct intel_dvo_dev_ops ch7017_ops;
|
||||
extern struct intel_dvo_dev_ops ns2501_ops;
|
||||
|
||||
#endif /* _INTEL_DVO_H */
|
||||
|
|
|
@ -163,7 +163,7 @@ struct ch7017_priv {
|
|||
};
|
||||
|
||||
static void ch7017_dump_regs(struct intel_dvo_device *dvo);
|
||||
static void ch7017_dpms(struct intel_dvo_device *dvo, int mode);
|
||||
static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable);
|
||||
|
||||
static bool ch7017_read(struct intel_dvo_device *dvo, u8 addr, u8 *val)
|
||||
{
|
||||
|
@ -309,7 +309,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
|
|||
lvds_power_down = CH7017_LVDS_POWER_DOWN_DEFAULT_RESERVED |
|
||||
(mode->hdisplay & 0x0700) >> 8;
|
||||
|
||||
ch7017_dpms(dvo, DRM_MODE_DPMS_OFF);
|
||||
ch7017_dpms(dvo, false);
|
||||
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT,
|
||||
horizontal_active_pixel_input);
|
||||
ch7017_write(dvo, CH7017_HORIZONTAL_ACTIVE_PIXEL_OUTPUT,
|
||||
|
@ -331,7 +331,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
|
|||
}
|
||||
|
||||
/* set the CH7017 power state */
|
||||
static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
|
||||
static void ch7017_dpms(struct intel_dvo_device *dvo, bool enable)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
|
@ -345,7 +345,7 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
CH7017_DAC3_POWER_DOWN |
|
||||
CH7017_TV_POWER_DOWN_EN);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
if (enable) {
|
||||
/* Turn on the LVDS */
|
||||
ch7017_write(dvo, CH7017_LVDS_POWER_DOWN,
|
||||
val & ~CH7017_LVDS_POWER_DOWN_EN);
|
||||
|
@ -359,6 +359,18 @@ static void ch7017_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
msleep(20);
|
||||
}
|
||||
|
||||
static bool ch7017_get_hw_state(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
ch7017_read(dvo, CH7017_LVDS_POWER_DOWN, &val);
|
||||
|
||||
if (val & CH7017_LVDS_POWER_DOWN_EN)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ch7017_dump_regs(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint8_t val;
|
||||
|
@ -396,6 +408,7 @@ struct intel_dvo_dev_ops ch7017_ops = {
|
|||
.mode_valid = ch7017_mode_valid,
|
||||
.mode_set = ch7017_mode_set,
|
||||
.dpms = ch7017_dpms,
|
||||
.get_hw_state = ch7017_get_hw_state,
|
||||
.dump_regs = ch7017_dump_regs,
|
||||
.destroy = ch7017_destroy,
|
||||
};
|
||||
|
|
|
@ -289,14 +289,26 @@ static void ch7xxx_mode_set(struct intel_dvo_device *dvo,
|
|||
}
|
||||
|
||||
/* set the CH7xxx power state */
|
||||
static void ch7xxx_dpms(struct intel_dvo_device *dvo, int mode)
|
||||
static void ch7xxx_dpms(struct intel_dvo_device *dvo, bool enable)
|
||||
{
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
if (enable)
|
||||
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_DVIL | CH7xxx_PM_DVIP);
|
||||
else
|
||||
ch7xxx_writeb(dvo, CH7xxx_PM, CH7xxx_PM_FPD);
|
||||
}
|
||||
|
||||
static bool ch7xxx_get_hw_state(struct intel_dvo_device *dvo)
|
||||
{
|
||||
u8 val;
|
||||
|
||||
ch7xxx_readb(dvo, CH7xxx_PM, &val);
|
||||
|
||||
if (val & CH7xxx_PM_FPD)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
|
||||
{
|
||||
int i;
|
||||
|
@ -326,6 +338,7 @@ struct intel_dvo_dev_ops ch7xxx_ops = {
|
|||
.mode_valid = ch7xxx_mode_valid,
|
||||
.mode_set = ch7xxx_mode_set,
|
||||
.dpms = ch7xxx_dpms,
|
||||
.get_hw_state = ch7xxx_get_hw_state,
|
||||
.dump_regs = ch7xxx_dump_regs,
|
||||
.destroy = ch7xxx_destroy,
|
||||
};
|
||||
|
|
|
@ -288,7 +288,7 @@ static enum drm_mode_status ivch_mode_valid(struct intel_dvo_device *dvo,
|
|||
}
|
||||
|
||||
/** Sets the power state of the panel connected to the ivch */
|
||||
static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
|
||||
static void ivch_dpms(struct intel_dvo_device *dvo, bool enable)
|
||||
{
|
||||
int i;
|
||||
uint16_t vr01, vr30, backlight;
|
||||
|
@ -297,13 +297,13 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
if (!ivch_read(dvo, VR01, &vr01))
|
||||
return;
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
if (enable)
|
||||
backlight = 1;
|
||||
else
|
||||
backlight = 0;
|
||||
ivch_write(dvo, VR80, backlight);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
if (enable)
|
||||
vr01 |= VR01_LCD_ENABLE | VR01_DVO_ENABLE;
|
||||
else
|
||||
vr01 &= ~(VR01_LCD_ENABLE | VR01_DVO_ENABLE);
|
||||
|
@ -315,7 +315,7 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
if (!ivch_read(dvo, VR30, &vr30))
|
||||
break;
|
||||
|
||||
if (((vr30 & VR30_PANEL_ON) != 0) == (mode == DRM_MODE_DPMS_ON))
|
||||
if (((vr30 & VR30_PANEL_ON) != 0) == enable)
|
||||
break;
|
||||
udelay(1000);
|
||||
}
|
||||
|
@ -323,6 +323,20 @@ static void ivch_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
udelay(16 * 1000);
|
||||
}
|
||||
|
||||
static bool ivch_get_hw_state(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint16_t vr01;
|
||||
|
||||
/* Set the new power state of the panel. */
|
||||
if (!ivch_read(dvo, VR01, &vr01))
|
||||
return false;
|
||||
|
||||
if (vr01 & VR01_LCD_ENABLE)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void ivch_mode_set(struct intel_dvo_device *dvo,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
|
@ -413,6 +427,7 @@ static void ivch_destroy(struct intel_dvo_device *dvo)
|
|||
struct intel_dvo_dev_ops ivch_ops = {
|
||||
.init = ivch_init,
|
||||
.dpms = ivch_dpms,
|
||||
.get_hw_state = ivch_get_hw_state,
|
||||
.mode_valid = ivch_mode_valid,
|
||||
.mode_set = ivch_mode_set,
|
||||
.detect = ivch_detect,
|
||||
|
|
|
@ -0,0 +1,588 @@
|
|||
/*
|
||||
*
|
||||
* Copyright (c) 2012 Gilles Dartiguelongue, Thomas Richter
|
||||
*
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sub license, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The 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 NON-INFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHOR 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 "dvo.h"
|
||||
#include "i915_reg.h"
|
||||
#include "i915_drv.h"
|
||||
|
||||
#define NS2501_VID 0x1305
|
||||
#define NS2501_DID 0x6726
|
||||
|
||||
#define NS2501_VID_LO 0x00
|
||||
#define NS2501_VID_HI 0x01
|
||||
#define NS2501_DID_LO 0x02
|
||||
#define NS2501_DID_HI 0x03
|
||||
#define NS2501_REV 0x04
|
||||
#define NS2501_RSVD 0x05
|
||||
#define NS2501_FREQ_LO 0x06
|
||||
#define NS2501_FREQ_HI 0x07
|
||||
|
||||
#define NS2501_REG8 0x08
|
||||
#define NS2501_8_VEN (1<<5)
|
||||
#define NS2501_8_HEN (1<<4)
|
||||
#define NS2501_8_DSEL (1<<3)
|
||||
#define NS2501_8_BPAS (1<<2)
|
||||
#define NS2501_8_RSVD (1<<1)
|
||||
#define NS2501_8_PD (1<<0)
|
||||
|
||||
#define NS2501_REG9 0x09
|
||||
#define NS2501_9_VLOW (1<<7)
|
||||
#define NS2501_9_MSEL_MASK (0x7<<4)
|
||||
#define NS2501_9_TSEL (1<<3)
|
||||
#define NS2501_9_RSEN (1<<2)
|
||||
#define NS2501_9_RSVD (1<<1)
|
||||
#define NS2501_9_MDI (1<<0)
|
||||
|
||||
#define NS2501_REGC 0x0c
|
||||
|
||||
struct ns2501_priv {
|
||||
//I2CDevRec d;
|
||||
bool quiet;
|
||||
int reg_8_shadow;
|
||||
int reg_8_set;
|
||||
// Shadow registers for i915
|
||||
int dvoc;
|
||||
int pll_a;
|
||||
int srcdim;
|
||||
int fw_blc;
|
||||
};
|
||||
|
||||
#define NSPTR(d) ((NS2501Ptr)(d->DriverPrivate.ptr))
|
||||
|
||||
/*
|
||||
* For reasons unclear to me, the ns2501 at least on the Fujitsu/Siemens
|
||||
* laptops does not react on the i2c bus unless
|
||||
* both the PLL is running and the display is configured in its native
|
||||
* resolution.
|
||||
* This function forces the DVO on, and stores the registers it touches.
|
||||
* Afterwards, registers are restored to regular values.
|
||||
*
|
||||
* This is pretty much a hack, though it works.
|
||||
* Without that, ns2501_readb and ns2501_writeb fail
|
||||
* when switching the resolution.
|
||||
*/
|
||||
|
||||
static void enable_dvo(struct intel_dvo_device *dvo)
|
||||
{
|
||||
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
|
||||
struct i2c_adapter *adapter = dvo->i2c_bus;
|
||||
struct intel_gmbus *bus = container_of(adapter,
|
||||
struct intel_gmbus,
|
||||
adapter);
|
||||
struct drm_i915_private *dev_priv = bus->dev_priv;
|
||||
|
||||
DRM_DEBUG_KMS("%s: Trying to re-enable the DVO\n", __FUNCTION__);
|
||||
|
||||
ns->dvoc = I915_READ(DVO_C);
|
||||
ns->pll_a = I915_READ(_DPLL_A);
|
||||
ns->srcdim = I915_READ(DVOC_SRCDIM);
|
||||
ns->fw_blc = I915_READ(FW_BLC);
|
||||
|
||||
I915_WRITE(DVOC, 0x10004084);
|
||||
I915_WRITE(_DPLL_A, 0xd0820000);
|
||||
I915_WRITE(DVOC_SRCDIM, 0x400300); // 1024x768
|
||||
I915_WRITE(FW_BLC, 0x1080304);
|
||||
|
||||
I915_WRITE(DVOC, 0x90004084);
|
||||
}
|
||||
|
||||
/*
|
||||
* Restore the I915 registers modified by the above
|
||||
* trigger function.
|
||||
*/
|
||||
static void restore_dvo(struct intel_dvo_device *dvo)
|
||||
{
|
||||
struct i2c_adapter *adapter = dvo->i2c_bus;
|
||||
struct intel_gmbus *bus = container_of(adapter,
|
||||
struct intel_gmbus,
|
||||
adapter);
|
||||
struct drm_i915_private *dev_priv = bus->dev_priv;
|
||||
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
|
||||
|
||||
I915_WRITE(DVOC, ns->dvoc);
|
||||
I915_WRITE(_DPLL_A, ns->pll_a);
|
||||
I915_WRITE(DVOC_SRCDIM, ns->srcdim);
|
||||
I915_WRITE(FW_BLC, ns->fw_blc);
|
||||
}
|
||||
|
||||
/*
|
||||
** Read a register from the ns2501.
|
||||
** Returns true if successful, false otherwise.
|
||||
** If it returns false, it might be wise to enable the
|
||||
** DVO with the above function.
|
||||
*/
|
||||
static bool ns2501_readb(struct intel_dvo_device *dvo, int addr, uint8_t * ch)
|
||||
{
|
||||
struct ns2501_priv *ns = dvo->dev_priv;
|
||||
struct i2c_adapter *adapter = dvo->i2c_bus;
|
||||
u8 out_buf[2];
|
||||
u8 in_buf[2];
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = dvo->slave_addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = out_buf,
|
||||
},
|
||||
{
|
||||
.addr = dvo->slave_addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = in_buf,
|
||||
}
|
||||
};
|
||||
|
||||
out_buf[0] = addr;
|
||||
out_buf[1] = 0;
|
||||
|
||||
if (i2c_transfer(adapter, msgs, 2) == 2) {
|
||||
*ch = in_buf[0];
|
||||
return true;
|
||||
};
|
||||
|
||||
if (!ns->quiet) {
|
||||
DRM_DEBUG_KMS
|
||||
("Unable to read register 0x%02x from %s:0x%02x.\n", addr,
|
||||
adapter->name, dvo->slave_addr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
** Write a register to the ns2501.
|
||||
** Returns true if successful, false otherwise.
|
||||
** If it returns false, it might be wise to enable the
|
||||
** DVO with the above function.
|
||||
*/
|
||||
static bool ns2501_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
|
||||
{
|
||||
struct ns2501_priv *ns = dvo->dev_priv;
|
||||
struct i2c_adapter *adapter = dvo->i2c_bus;
|
||||
uint8_t out_buf[2];
|
||||
|
||||
struct i2c_msg msg = {
|
||||
.addr = dvo->slave_addr,
|
||||
.flags = 0,
|
||||
.len = 2,
|
||||
.buf = out_buf,
|
||||
};
|
||||
|
||||
out_buf[0] = addr;
|
||||
out_buf[1] = ch;
|
||||
|
||||
if (i2c_transfer(adapter, &msg, 1) == 1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!ns->quiet) {
|
||||
DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d\n",
|
||||
addr, adapter->name, dvo->slave_addr);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* National Semiconductor 2501 driver for chip on i2c bus
|
||||
* scan for the chip on the bus.
|
||||
* Hope the VBIOS initialized the PLL correctly so we can
|
||||
* talk to it. If not, it will not be seen and not detected.
|
||||
* Bummer!
|
||||
*/
|
||||
static bool ns2501_init(struct intel_dvo_device *dvo,
|
||||
struct i2c_adapter *adapter)
|
||||
{
|
||||
/* this will detect the NS2501 chip on the specified i2c bus */
|
||||
struct ns2501_priv *ns;
|
||||
unsigned char ch;
|
||||
|
||||
ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL);
|
||||
if (ns == NULL)
|
||||
return false;
|
||||
|
||||
dvo->i2c_bus = adapter;
|
||||
dvo->dev_priv = ns;
|
||||
ns->quiet = true;
|
||||
|
||||
if (!ns2501_readb(dvo, NS2501_VID_LO, &ch))
|
||||
goto out;
|
||||
|
||||
if (ch != (NS2501_VID & 0xff)) {
|
||||
DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
|
||||
ch, adapter->name, dvo->slave_addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!ns2501_readb(dvo, NS2501_DID_LO, &ch))
|
||||
goto out;
|
||||
|
||||
if (ch != (NS2501_DID & 0xff)) {
|
||||
DRM_DEBUG_KMS("ns2501 not detected got %d: from %s Slave %d.\n",
|
||||
ch, adapter->name, dvo->slave_addr);
|
||||
goto out;
|
||||
}
|
||||
ns->quiet = false;
|
||||
ns->reg_8_set = 0;
|
||||
ns->reg_8_shadow =
|
||||
NS2501_8_PD | NS2501_8_BPAS | NS2501_8_VEN | NS2501_8_HEN;
|
||||
|
||||
DRM_DEBUG_KMS("init ns2501 dvo controller successfully!\n");
|
||||
return true;
|
||||
|
||||
out:
|
||||
kfree(ns);
|
||||
return false;
|
||||
}
|
||||
|
||||
static enum drm_connector_status ns2501_detect(struct intel_dvo_device *dvo)
|
||||
{
|
||||
/*
|
||||
* This is a Laptop display, it doesn't have hotplugging.
|
||||
* Even if not, the detection bit of the 2501 is unreliable as
|
||||
* it only works for some display types.
|
||||
* It is even more unreliable as the PLL must be active for
|
||||
* allowing reading from the chiop.
|
||||
*/
|
||||
return connector_status_connected;
|
||||
}
|
||||
|
||||
static enum drm_mode_status ns2501_mode_valid(struct intel_dvo_device *dvo,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
DRM_DEBUG_KMS
|
||||
("%s: is mode valid (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d)\n",
|
||||
__FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
|
||||
mode->vtotal);
|
||||
|
||||
/*
|
||||
* Currently, these are all the modes I have data from.
|
||||
* More might exist. Unclear how to find the native resolution
|
||||
* of the panel in here so we could always accept it
|
||||
* by disabling the scaler.
|
||||
*/
|
||||
if ((mode->hdisplay == 800 && mode->vdisplay == 600) ||
|
||||
(mode->hdisplay == 640 && mode->vdisplay == 480) ||
|
||||
(mode->hdisplay == 1024 && mode->vdisplay == 768)) {
|
||||
return MODE_OK;
|
||||
} else {
|
||||
return MODE_ONE_SIZE; /* Is this a reasonable error? */
|
||||
}
|
||||
}
|
||||
|
||||
static void ns2501_mode_set(struct intel_dvo_device *dvo,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
bool ok;
|
||||
bool restore = false;
|
||||
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
|
||||
|
||||
DRM_DEBUG_KMS
|
||||
("%s: set mode (hdisplay=%d,htotal=%d,vdisplay=%d,vtotal=%d).\n",
|
||||
__FUNCTION__, mode->hdisplay, mode->htotal, mode->vdisplay,
|
||||
mode->vtotal);
|
||||
|
||||
/*
|
||||
* Where do I find the native resolution for which scaling is not required???
|
||||
*
|
||||
* First trigger the DVO on as otherwise the chip does not appear on the i2c
|
||||
* bus.
|
||||
*/
|
||||
do {
|
||||
ok = true;
|
||||
|
||||
if (mode->hdisplay == 800 && mode->vdisplay == 600) {
|
||||
/* mode 277 */
|
||||
ns->reg_8_shadow &= ~NS2501_8_BPAS;
|
||||
DRM_DEBUG_KMS("%s: switching to 800x600\n",
|
||||
__FUNCTION__);
|
||||
|
||||
/*
|
||||
* No, I do not know where this data comes from.
|
||||
* It is just what the video bios left in the DVO, so
|
||||
* I'm just copying it here over.
|
||||
* This also means that I cannot support any other modes
|
||||
* except the ones supported by the bios.
|
||||
*/
|
||||
ok &= ns2501_writeb(dvo, 0x11, 0xc8); // 0xc7 also works.
|
||||
ok &= ns2501_writeb(dvo, 0x1b, 0x19);
|
||||
ok &= ns2501_writeb(dvo, 0x1c, 0x62); // VBIOS left 0x64 here, but 0x62 works nicer
|
||||
ok &= ns2501_writeb(dvo, 0x1d, 0x02);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x34, 0x03);
|
||||
ok &= ns2501_writeb(dvo, 0x35, 0xff);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x80, 0x27);
|
||||
ok &= ns2501_writeb(dvo, 0x81, 0x03);
|
||||
ok &= ns2501_writeb(dvo, 0x82, 0x41);
|
||||
ok &= ns2501_writeb(dvo, 0x83, 0x05);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x8d, 0x02);
|
||||
ok &= ns2501_writeb(dvo, 0x8e, 0x04);
|
||||
ok &= ns2501_writeb(dvo, 0x8f, 0x00);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x90, 0xfe); /* vertical. VBIOS left 0xff here, but 0xfe works better */
|
||||
ok &= ns2501_writeb(dvo, 0x91, 0x07);
|
||||
ok &= ns2501_writeb(dvo, 0x94, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0x95, 0x00);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x96, 0x00);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x99, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0x9a, 0x88);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x9c, 0x23); /* Looks like first and last line of the image. */
|
||||
ok &= ns2501_writeb(dvo, 0x9d, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0x9e, 0x25);
|
||||
ok &= ns2501_writeb(dvo, 0x9f, 0x03);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xa4, 0x80);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xb6, 0x00);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xb9, 0xc8); /* horizontal? */
|
||||
ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */
|
||||
ok &= ns2501_writeb(dvo, 0xc1, 0xd7);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc2, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0xc3, 0xf8);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc4, 0x03);
|
||||
ok &= ns2501_writeb(dvo, 0xc5, 0x1a);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc6, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0xc7, 0x73);
|
||||
ok &= ns2501_writeb(dvo, 0xc8, 0x02);
|
||||
|
||||
} else if (mode->hdisplay == 640 && mode->vdisplay == 480) {
|
||||
/* mode 274 */
|
||||
DRM_DEBUG_KMS("%s: switching to 640x480\n",
|
||||
__FUNCTION__);
|
||||
/*
|
||||
* No, I do not know where this data comes from.
|
||||
* It is just what the video bios left in the DVO, so
|
||||
* I'm just copying it here over.
|
||||
* This also means that I cannot support any other modes
|
||||
* except the ones supported by the bios.
|
||||
*/
|
||||
ns->reg_8_shadow &= ~NS2501_8_BPAS;
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x11, 0xa0);
|
||||
ok &= ns2501_writeb(dvo, 0x1b, 0x11);
|
||||
ok &= ns2501_writeb(dvo, 0x1c, 0x54);
|
||||
ok &= ns2501_writeb(dvo, 0x1d, 0x03);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x34, 0x03);
|
||||
ok &= ns2501_writeb(dvo, 0x35, 0xff);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x80, 0xff);
|
||||
ok &= ns2501_writeb(dvo, 0x81, 0x07);
|
||||
ok &= ns2501_writeb(dvo, 0x82, 0x3d);
|
||||
ok &= ns2501_writeb(dvo, 0x83, 0x05);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x8d, 0x02);
|
||||
ok &= ns2501_writeb(dvo, 0x8e, 0x10);
|
||||
ok &= ns2501_writeb(dvo, 0x8f, 0x00);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x90, 0xff); /* vertical */
|
||||
ok &= ns2501_writeb(dvo, 0x91, 0x07);
|
||||
ok &= ns2501_writeb(dvo, 0x94, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0x95, 0x00);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x96, 0x05);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x99, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0x9a, 0x88);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0x9c, 0x24);
|
||||
ok &= ns2501_writeb(dvo, 0x9d, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0x9e, 0x25);
|
||||
ok &= ns2501_writeb(dvo, 0x9f, 0x03);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xa4, 0x84);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xb6, 0x09);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xb9, 0xa0); /* horizontal? */
|
||||
ok &= ns2501_writeb(dvo, 0xba, 0x00); /* horizontal? */
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc0, 0x05); /* horizontal? */
|
||||
ok &= ns2501_writeb(dvo, 0xc1, 0x90);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc2, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0xc3, 0x0f);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc4, 0x03);
|
||||
ok &= ns2501_writeb(dvo, 0xc5, 0x16);
|
||||
|
||||
ok &= ns2501_writeb(dvo, 0xc6, 0x00);
|
||||
ok &= ns2501_writeb(dvo, 0xc7, 0x02);
|
||||
ok &= ns2501_writeb(dvo, 0xc8, 0x02);
|
||||
|
||||
} else if (mode->hdisplay == 1024 && mode->vdisplay == 768) {
|
||||
/* mode 280 */
|
||||
DRM_DEBUG_KMS("%s: switching to 1024x768\n",
|
||||
__FUNCTION__);
|
||||
/*
|
||||
* This might or might not work, actually. I'm silently
|
||||
* assuming here that the native panel resolution is
|
||||
* 1024x768. If not, then this leaves the scaler disabled
|
||||
* generating a picture that is likely not the expected.
|
||||
*
|
||||
* Problem is that I do not know where to take the panel
|
||||
* dimensions from.
|
||||
*
|
||||
* Enable the bypass, scaling not required.
|
||||
*
|
||||
* The scaler registers are irrelevant here....
|
||||
*
|
||||
*/
|
||||
ns->reg_8_shadow |= NS2501_8_BPAS;
|
||||
ok &= ns2501_writeb(dvo, 0x37, 0x44);
|
||||
} else {
|
||||
/*
|
||||
* Data not known. Bummer!
|
||||
* Hopefully, the code should not go here
|
||||
* as mode_OK delivered no other modes.
|
||||
*/
|
||||
ns->reg_8_shadow |= NS2501_8_BPAS;
|
||||
}
|
||||
ok &= ns2501_writeb(dvo, NS2501_REG8, ns->reg_8_shadow);
|
||||
|
||||
if (!ok) {
|
||||
if (restore)
|
||||
restore_dvo(dvo);
|
||||
enable_dvo(dvo);
|
||||
restore = true;
|
||||
}
|
||||
} while (!ok);
|
||||
/*
|
||||
* Restore the old i915 registers before
|
||||
* forcing the ns2501 on.
|
||||
*/
|
||||
if (restore)
|
||||
restore_dvo(dvo);
|
||||
}
|
||||
|
||||
/* set the NS2501 power state */
|
||||
static bool ns2501_get_hw_state(struct intel_dvo_device *dvo)
|
||||
{
|
||||
unsigned char ch;
|
||||
|
||||
if (!ns2501_readb(dvo, NS2501_REG8, &ch))
|
||||
return false;
|
||||
|
||||
if (ch & NS2501_8_PD)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/* set the NS2501 power state */
|
||||
static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable)
|
||||
{
|
||||
bool ok;
|
||||
bool restore = false;
|
||||
struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv);
|
||||
unsigned char ch;
|
||||
|
||||
DRM_DEBUG_KMS("%s: Trying set the dpms of the DVO to %i\n",
|
||||
__FUNCTION__, enable);
|
||||
|
||||
ch = ns->reg_8_shadow;
|
||||
|
||||
if (enable)
|
||||
ch |= NS2501_8_PD;
|
||||
else
|
||||
ch &= ~NS2501_8_PD;
|
||||
|
||||
if (ns->reg_8_set == 0 || ns->reg_8_shadow != ch) {
|
||||
ns->reg_8_set = 1;
|
||||
ns->reg_8_shadow = ch;
|
||||
|
||||
do {
|
||||
ok = true;
|
||||
ok &= ns2501_writeb(dvo, NS2501_REG8, ch);
|
||||
ok &=
|
||||
ns2501_writeb(dvo, 0x34,
|
||||
enable ? 0x03 : 0x00);
|
||||
ok &=
|
||||
ns2501_writeb(dvo, 0x35,
|
||||
enable ? 0xff : 0x00);
|
||||
if (!ok) {
|
||||
if (restore)
|
||||
restore_dvo(dvo);
|
||||
enable_dvo(dvo);
|
||||
restore = true;
|
||||
}
|
||||
} while (!ok);
|
||||
|
||||
if (restore)
|
||||
restore_dvo(dvo);
|
||||
}
|
||||
}
|
||||
|
||||
static void ns2501_dump_regs(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint8_t val;
|
||||
|
||||
ns2501_readb(dvo, NS2501_FREQ_LO, &val);
|
||||
DRM_LOG_KMS("NS2501_FREQ_LO: 0x%02x\n", val);
|
||||
ns2501_readb(dvo, NS2501_FREQ_HI, &val);
|
||||
DRM_LOG_KMS("NS2501_FREQ_HI: 0x%02x\n", val);
|
||||
ns2501_readb(dvo, NS2501_REG8, &val);
|
||||
DRM_LOG_KMS("NS2501_REG8: 0x%02x\n", val);
|
||||
ns2501_readb(dvo, NS2501_REG9, &val);
|
||||
DRM_LOG_KMS("NS2501_REG9: 0x%02x\n", val);
|
||||
ns2501_readb(dvo, NS2501_REGC, &val);
|
||||
DRM_LOG_KMS("NS2501_REGC: 0x%02x\n", val);
|
||||
}
|
||||
|
||||
static void ns2501_destroy(struct intel_dvo_device *dvo)
|
||||
{
|
||||
struct ns2501_priv *ns = dvo->dev_priv;
|
||||
|
||||
if (ns) {
|
||||
kfree(ns);
|
||||
dvo->dev_priv = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
struct intel_dvo_dev_ops ns2501_ops = {
|
||||
.init = ns2501_init,
|
||||
.detect = ns2501_detect,
|
||||
.mode_valid = ns2501_mode_valid,
|
||||
.mode_set = ns2501_mode_set,
|
||||
.dpms = ns2501_dpms,
|
||||
.get_hw_state = ns2501_get_hw_state,
|
||||
.dump_regs = ns2501_dump_regs,
|
||||
.destroy = ns2501_destroy,
|
||||
};
|
|
@ -208,7 +208,7 @@ static void sil164_mode_set(struct intel_dvo_device *dvo,
|
|||
}
|
||||
|
||||
/* set the SIL164 power state */
|
||||
static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
|
||||
static void sil164_dpms(struct intel_dvo_device *dvo, bool enable)
|
||||
{
|
||||
int ret;
|
||||
unsigned char ch;
|
||||
|
@ -217,7 +217,7 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
if (ret == false)
|
||||
return;
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
if (enable)
|
||||
ch |= SIL164_8_PD;
|
||||
else
|
||||
ch &= ~SIL164_8_PD;
|
||||
|
@ -226,6 +226,21 @@ static void sil164_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
return;
|
||||
}
|
||||
|
||||
static bool sil164_get_hw_state(struct intel_dvo_device *dvo)
|
||||
{
|
||||
int ret;
|
||||
unsigned char ch;
|
||||
|
||||
ret = sil164_readb(dvo, SIL164_REG8, &ch);
|
||||
if (ret == false)
|
||||
return false;
|
||||
|
||||
if (ch & SIL164_8_PD)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void sil164_dump_regs(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint8_t val;
|
||||
|
@ -258,6 +273,7 @@ struct intel_dvo_dev_ops sil164_ops = {
|
|||
.mode_valid = sil164_mode_valid,
|
||||
.mode_set = sil164_mode_set,
|
||||
.dpms = sil164_dpms,
|
||||
.get_hw_state = sil164_get_hw_state,
|
||||
.dump_regs = sil164_dump_regs,
|
||||
.destroy = sil164_destroy,
|
||||
};
|
||||
|
|
|
@ -234,14 +234,14 @@ static void tfp410_mode_set(struct intel_dvo_device *dvo,
|
|||
}
|
||||
|
||||
/* set the tfp410 power state */
|
||||
static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
|
||||
static void tfp410_dpms(struct intel_dvo_device *dvo, bool enable)
|
||||
{
|
||||
uint8_t ctl1;
|
||||
|
||||
if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
|
||||
return;
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
if (enable)
|
||||
ctl1 |= TFP410_CTL_1_PD;
|
||||
else
|
||||
ctl1 &= ~TFP410_CTL_1_PD;
|
||||
|
@ -249,6 +249,19 @@ static void tfp410_dpms(struct intel_dvo_device *dvo, int mode)
|
|||
tfp410_writeb(dvo, TFP410_CTL_1, ctl1);
|
||||
}
|
||||
|
||||
static bool tfp410_get_hw_state(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint8_t ctl1;
|
||||
|
||||
if (!tfp410_readb(dvo, TFP410_CTL_1, &ctl1))
|
||||
return false;
|
||||
|
||||
if (ctl1 & TFP410_CTL_1_PD)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static void tfp410_dump_regs(struct intel_dvo_device *dvo)
|
||||
{
|
||||
uint8_t val, val2;
|
||||
|
@ -299,6 +312,7 @@ struct intel_dvo_dev_ops tfp410_ops = {
|
|||
.mode_valid = tfp410_mode_valid,
|
||||
.mode_set = tfp410_mode_set,
|
||||
.dpms = tfp410_dpms,
|
||||
.get_hw_state = tfp410_get_hw_state,
|
||||
.dump_regs = tfp410_dump_regs,
|
||||
.destroy = tfp410_destroy,
|
||||
};
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
|
||||
enum {
|
||||
ACTIVE_LIST,
|
||||
FLUSHING_LIST,
|
||||
INACTIVE_LIST,
|
||||
PINNED_LIST,
|
||||
};
|
||||
|
@ -61,28 +60,11 @@ static int i915_capabilities(struct seq_file *m, void *data)
|
|||
|
||||
seq_printf(m, "gen: %d\n", info->gen);
|
||||
seq_printf(m, "pch: %d\n", INTEL_PCH_TYPE(dev));
|
||||
#define B(x) seq_printf(m, #x ": %s\n", yesno(info->x))
|
||||
B(is_mobile);
|
||||
B(is_i85x);
|
||||
B(is_i915g);
|
||||
B(is_i945gm);
|
||||
B(is_g33);
|
||||
B(need_gfx_hws);
|
||||
B(is_g4x);
|
||||
B(is_pineview);
|
||||
B(is_broadwater);
|
||||
B(is_crestline);
|
||||
B(has_fbc);
|
||||
B(has_pipe_cxsr);
|
||||
B(has_hotplug);
|
||||
B(cursor_needs_physical);
|
||||
B(has_overlay);
|
||||
B(overlay_needs_physical);
|
||||
B(supports_tv);
|
||||
B(has_bsd_ring);
|
||||
B(has_blt_ring);
|
||||
B(has_llc);
|
||||
#undef B
|
||||
#define DEV_INFO_FLAG(x) seq_printf(m, #x ": %s\n", yesno(info->x))
|
||||
#define DEV_INFO_SEP ;
|
||||
DEV_INFO_FLAGS;
|
||||
#undef DEV_INFO_FLAG
|
||||
#undef DEV_INFO_SEP
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -120,20 +102,23 @@ static const char *cache_level_str(int type)
|
|||
static void
|
||||
describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)
|
||||
{
|
||||
seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d%s%s%s",
|
||||
seq_printf(m, "%p: %s%s %8zdKiB %04x %04x %d %d %d%s%s%s",
|
||||
&obj->base,
|
||||
get_pin_flag(obj),
|
||||
get_tiling_flag(obj),
|
||||
obj->base.size / 1024,
|
||||
obj->base.read_domains,
|
||||
obj->base.write_domain,
|
||||
obj->last_rendering_seqno,
|
||||
obj->last_read_seqno,
|
||||
obj->last_write_seqno,
|
||||
obj->last_fenced_seqno,
|
||||
cache_level_str(obj->cache_level),
|
||||
obj->dirty ? " dirty" : "",
|
||||
obj->madv == I915_MADV_DONTNEED ? " purgeable" : "");
|
||||
if (obj->base.name)
|
||||
seq_printf(m, " (name: %d)", obj->base.name);
|
||||
if (obj->pin_count)
|
||||
seq_printf(m, " (pinned x %d)", obj->pin_count);
|
||||
if (obj->fence_reg != I915_FENCE_REG_NONE)
|
||||
seq_printf(m, " (fence: %d)", obj->fence_reg);
|
||||
if (obj->gtt_space != NULL)
|
||||
|
@ -176,10 +161,6 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
|
|||
seq_printf(m, "Inactive:\n");
|
||||
head = &dev_priv->mm.inactive_list;
|
||||
break;
|
||||
case FLUSHING_LIST:
|
||||
seq_printf(m, "Flushing:\n");
|
||||
head = &dev_priv->mm.flushing_list;
|
||||
break;
|
||||
default:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return -EINVAL;
|
||||
|
@ -217,8 +198,8 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
|
|||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 count, mappable_count;
|
||||
size_t size, mappable_size;
|
||||
u32 count, mappable_count, purgeable_count;
|
||||
size_t size, mappable_size, purgeable_size;
|
||||
struct drm_i915_gem_object *obj;
|
||||
int ret;
|
||||
|
||||
|
@ -231,13 +212,12 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
|
|||
dev_priv->mm.object_memory);
|
||||
|
||||
size = count = mappable_size = mappable_count = 0;
|
||||
count_objects(&dev_priv->mm.gtt_list, gtt_list);
|
||||
count_objects(&dev_priv->mm.bound_list, gtt_list);
|
||||
seq_printf(m, "%u [%u] objects, %zu [%zu] bytes in gtt\n",
|
||||
count, mappable_count, size, mappable_size);
|
||||
|
||||
size = count = mappable_size = mappable_count = 0;
|
||||
count_objects(&dev_priv->mm.active_list, mm_list);
|
||||
count_objects(&dev_priv->mm.flushing_list, mm_list);
|
||||
seq_printf(m, " %u [%u] active objects, %zu [%zu] bytes\n",
|
||||
count, mappable_count, size, mappable_size);
|
||||
|
||||
|
@ -246,8 +226,16 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
|
|||
seq_printf(m, " %u [%u] inactive objects, %zu [%zu] bytes\n",
|
||||
count, mappable_count, size, mappable_size);
|
||||
|
||||
size = count = purgeable_size = purgeable_count = 0;
|
||||
list_for_each_entry(obj, &dev_priv->mm.unbound_list, gtt_list) {
|
||||
size += obj->base.size, ++count;
|
||||
if (obj->madv == I915_MADV_DONTNEED)
|
||||
purgeable_size += obj->base.size, ++purgeable_count;
|
||||
}
|
||||
seq_printf(m, "%u unbound objects, %zu bytes\n", count, size);
|
||||
|
||||
size = count = mappable_size = mappable_count = 0;
|
||||
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
|
||||
if (obj->fault_mappable) {
|
||||
size += obj->gtt_space->size;
|
||||
++count;
|
||||
|
@ -256,7 +244,13 @@ static int i915_gem_object_info(struct seq_file *m, void* data)
|
|||
mappable_size += obj->gtt_space->size;
|
||||
++mappable_count;
|
||||
}
|
||||
if (obj->madv == I915_MADV_DONTNEED) {
|
||||
purgeable_size += obj->base.size;
|
||||
++purgeable_count;
|
||||
}
|
||||
}
|
||||
seq_printf(m, "%u purgeable objects, %zu bytes\n",
|
||||
purgeable_count, purgeable_size);
|
||||
seq_printf(m, "%u pinned mappable objects, %zu bytes\n",
|
||||
mappable_count, mappable_size);
|
||||
seq_printf(m, "%u fault mappable objects, %zu bytes\n",
|
||||
|
@ -285,7 +279,7 @@ static int i915_gem_gtt_info(struct seq_file *m, void* data)
|
|||
return ret;
|
||||
|
||||
total_obj_size = total_gtt_size = count = 0;
|
||||
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
|
||||
if (list == PINNED_LIST && obj->pin_count == 0)
|
||||
continue;
|
||||
|
||||
|
@ -358,40 +352,22 @@ static int i915_gem_request_info(struct seq_file *m, void *data)
|
|||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct intel_ring_buffer *ring;
|
||||
struct drm_i915_gem_request *gem_request;
|
||||
int ret, count;
|
||||
int ret, count, i;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
count = 0;
|
||||
if (!list_empty(&dev_priv->ring[RCS].request_list)) {
|
||||
seq_printf(m, "Render requests:\n");
|
||||
for_each_ring(ring, dev_priv, i) {
|
||||
if (list_empty(&ring->request_list))
|
||||
continue;
|
||||
|
||||
seq_printf(m, "%s requests:\n", ring->name);
|
||||
list_for_each_entry(gem_request,
|
||||
&dev_priv->ring[RCS].request_list,
|
||||
list) {
|
||||
seq_printf(m, " %d @ %d\n",
|
||||
gem_request->seqno,
|
||||
(int) (jiffies - gem_request->emitted_jiffies));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!list_empty(&dev_priv->ring[VCS].request_list)) {
|
||||
seq_printf(m, "BSD requests:\n");
|
||||
list_for_each_entry(gem_request,
|
||||
&dev_priv->ring[VCS].request_list,
|
||||
list) {
|
||||
seq_printf(m, " %d @ %d\n",
|
||||
gem_request->seqno,
|
||||
(int) (jiffies - gem_request->emitted_jiffies));
|
||||
}
|
||||
count++;
|
||||
}
|
||||
if (!list_empty(&dev_priv->ring[BCS].request_list)) {
|
||||
seq_printf(m, "BLT requests:\n");
|
||||
list_for_each_entry(gem_request,
|
||||
&dev_priv->ring[BCS].request_list,
|
||||
&ring->request_list,
|
||||
list) {
|
||||
seq_printf(m, " %d @ %d\n",
|
||||
gem_request->seqno,
|
||||
|
@ -412,7 +388,7 @@ static void i915_ring_seqno_info(struct seq_file *m,
|
|||
{
|
||||
if (ring->get_seqno) {
|
||||
seq_printf(m, "Current sequence (%s): %d\n",
|
||||
ring->name, ring->get_seqno(ring));
|
||||
ring->name, ring->get_seqno(ring, false));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,14 +397,15 @@ static int i915_gem_seqno_info(struct seq_file *m, void *data)
|
|||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct intel_ring_buffer *ring;
|
||||
int ret, i;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < I915_NUM_RINGS; i++)
|
||||
i915_ring_seqno_info(m, &dev_priv->ring[i]);
|
||||
for_each_ring(ring, dev_priv, i)
|
||||
i915_ring_seqno_info(m, ring);
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
|
@ -441,6 +418,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
|
|||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct intel_ring_buffer *ring;
|
||||
int ret, i, pipe;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
|
@ -518,13 +496,13 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
|
|||
}
|
||||
seq_printf(m, "Interrupts received: %d\n",
|
||||
atomic_read(&dev_priv->irq_received));
|
||||
for (i = 0; i < I915_NUM_RINGS; i++) {
|
||||
for_each_ring(ring, dev_priv, i) {
|
||||
if (IS_GEN6(dev) || IS_GEN7(dev)) {
|
||||
seq_printf(m, "Graphics Interrupt mask (%s): %08x\n",
|
||||
dev_priv->ring[i].name,
|
||||
I915_READ_IMR(&dev_priv->ring[i]));
|
||||
seq_printf(m,
|
||||
"Graphics Interrupt mask (%s): %08x\n",
|
||||
ring->name, I915_READ_IMR(ring));
|
||||
}
|
||||
i915_ring_seqno_info(m, &dev_priv->ring[i]);
|
||||
i915_ring_seqno_info(m, ring);
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
|
@ -547,7 +525,8 @@ static int i915_gem_fence_regs_info(struct seq_file *m, void *data)
|
|||
for (i = 0; i < dev_priv->num_fence_regs; i++) {
|
||||
struct drm_i915_gem_object *obj = dev_priv->fence_regs[i].obj;
|
||||
|
||||
seq_printf(m, "Fenced object[%2d] = ", i);
|
||||
seq_printf(m, "Fence %d, pin count = %d, object = ",
|
||||
i, dev_priv->fence_regs[i].pin_count);
|
||||
if (obj == NULL)
|
||||
seq_printf(m, "unused");
|
||||
else
|
||||
|
@ -629,12 +608,12 @@ static void print_error_buffers(struct seq_file *m,
|
|||
seq_printf(m, "%s [%d]:\n", name, count);
|
||||
|
||||
while (count--) {
|
||||
seq_printf(m, " %08x %8u %04x %04x %08x%s%s%s%s%s%s%s",
|
||||
seq_printf(m, " %08x %8u %04x %04x %x %x%s%s%s%s%s%s%s",
|
||||
err->gtt_offset,
|
||||
err->size,
|
||||
err->read_domains,
|
||||
err->write_domain,
|
||||
err->seqno,
|
||||
err->rseqno, err->wseqno,
|
||||
pin_flag(err->pinned),
|
||||
tiling_flag(err->tiling),
|
||||
dirty_flag(err->dirty),
|
||||
|
@ -666,10 +645,9 @@ static void i915_ring_error_state(struct seq_file *m,
|
|||
seq_printf(m, " IPEIR: 0x%08x\n", error->ipeir[ring]);
|
||||
seq_printf(m, " IPEHR: 0x%08x\n", error->ipehr[ring]);
|
||||
seq_printf(m, " INSTDONE: 0x%08x\n", error->instdone[ring]);
|
||||
if (ring == RCS && INTEL_INFO(dev)->gen >= 4) {
|
||||
seq_printf(m, " INSTDONE1: 0x%08x\n", error->instdone1);
|
||||
if (ring == RCS && INTEL_INFO(dev)->gen >= 4)
|
||||
seq_printf(m, " BBADDR: 0x%08llx\n", error->bbaddr);
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 4)
|
||||
seq_printf(m, " INSTPS: 0x%08x\n", error->instps[ring]);
|
||||
seq_printf(m, " INSTPM: 0x%08x\n", error->instpm[ring]);
|
||||
|
@ -718,11 +696,17 @@ static int i915_error_state(struct seq_file *m, void *unused)
|
|||
for (i = 0; i < dev_priv->num_fence_regs; i++)
|
||||
seq_printf(m, " fence[%d] = %08llx\n", i, error->fence[i]);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(error->extra_instdone); i++)
|
||||
seq_printf(m, " INSTDONE_%d: 0x%08x\n", i, error->extra_instdone[i]);
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
seq_printf(m, "ERROR: 0x%08x\n", error->error);
|
||||
seq_printf(m, "DONE_REG: 0x%08x\n", error->done_reg);
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen == 7)
|
||||
seq_printf(m, "ERR_INT: 0x%08x\n", error->err_int);
|
||||
|
||||
for_each_ring(ring, dev_priv, i)
|
||||
i915_ring_error_state(m, dev, error, i);
|
||||
|
||||
|
@ -798,10 +782,14 @@ i915_error_state_write(struct file *filp,
|
|||
struct seq_file *m = filp->private_data;
|
||||
struct i915_error_state_file_priv *error_priv = m->private;
|
||||
struct drm_device *dev = error_priv->dev;
|
||||
int ret;
|
||||
|
||||
DRM_DEBUG_DRIVER("Resetting error state\n");
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
i915_destroy_error_state(dev);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
|
@ -925,7 +913,7 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
|
|||
seq_printf(m, "Render p-state limit: %d\n",
|
||||
rp_state_limits & 0xff);
|
||||
seq_printf(m, "CAGF: %dMHz\n", ((rpstat & GEN6_CAGF_MASK) >>
|
||||
GEN6_CAGF_SHIFT) * 50);
|
||||
GEN6_CAGF_SHIFT) * GT_FREQUENCY_MULTIPLIER);
|
||||
seq_printf(m, "RP CUR UP EI: %dus\n", rpupei &
|
||||
GEN6_CURICONT_MASK);
|
||||
seq_printf(m, "RP CUR UP: %dus\n", rpcurup &
|
||||
|
@ -941,15 +929,15 @@ static int i915_cur_delayinfo(struct seq_file *m, void *unused)
|
|||
|
||||
max_freq = (rp_state_cap & 0xff0000) >> 16;
|
||||
seq_printf(m, "Lowest (RPN) frequency: %dMHz\n",
|
||||
max_freq * 50);
|
||||
max_freq * GT_FREQUENCY_MULTIPLIER);
|
||||
|
||||
max_freq = (rp_state_cap & 0xff00) >> 8;
|
||||
seq_printf(m, "Nominal (RP1) frequency: %dMHz\n",
|
||||
max_freq * 50);
|
||||
max_freq * GT_FREQUENCY_MULTIPLIER);
|
||||
|
||||
max_freq = rp_state_cap & 0xff;
|
||||
seq_printf(m, "Max non-overclocked (RP0) frequency: %dMHz\n",
|
||||
max_freq * 50);
|
||||
max_freq * GT_FREQUENCY_MULTIPLIER);
|
||||
} else {
|
||||
seq_printf(m, "no P-state info available\n");
|
||||
}
|
||||
|
@ -1291,7 +1279,8 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
|
|||
|
||||
seq_printf(m, "GPU freq (MHz)\tEffective CPU freq (MHz)\n");
|
||||
|
||||
for (gpu_freq = dev_priv->min_delay; gpu_freq <= dev_priv->max_delay;
|
||||
for (gpu_freq = dev_priv->rps.min_delay;
|
||||
gpu_freq <= dev_priv->rps.max_delay;
|
||||
gpu_freq++) {
|
||||
I915_WRITE(GEN6_PCODE_DATA, gpu_freq);
|
||||
I915_WRITE(GEN6_PCODE_MAILBOX, GEN6_PCODE_READY |
|
||||
|
@ -1302,7 +1291,7 @@ static int i915_ring_freq_table(struct seq_file *m, void *unused)
|
|||
continue;
|
||||
}
|
||||
ia_freq = I915_READ(GEN6_PCODE_DATA);
|
||||
seq_printf(m, "%d\t\t%d\n", gpu_freq * 50, ia_freq * 100);
|
||||
seq_printf(m, "%d\t\t%d\n", gpu_freq * GT_FREQUENCY_MULTIPLIER, ia_freq * 100);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
@ -1471,8 +1460,12 @@ static int i915_swizzle_info(struct seq_file *m, void *data)
|
|||
struct drm_info_node *node = (struct drm_info_node *) m->private;
|
||||
struct drm_device *dev = node->minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
seq_printf(m, "bit6 swizzle for X-tiling = %s\n",
|
||||
swizzle_string(dev_priv->mm.bit_6_swizzle_x));
|
||||
seq_printf(m, "bit6 swizzle for Y-tiling = %s\n",
|
||||
|
@ -1519,9 +1512,7 @@ static int i915_ppgtt_info(struct seq_file *m, void *data)
|
|||
if (INTEL_INFO(dev)->gen == 6)
|
||||
seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(GFX_MODE));
|
||||
|
||||
for (i = 0; i < I915_NUM_RINGS; i++) {
|
||||
ring = &dev_priv->ring[i];
|
||||
|
||||
for_each_ring(ring, dev_priv, i) {
|
||||
seq_printf(m, "%s\n", ring->name);
|
||||
if (INTEL_INFO(dev)->gen == 7)
|
||||
seq_printf(m, "GFX_MODE: 0x%08x\n", I915_READ(RING_MODE_GEN7(ring)));
|
||||
|
@ -1673,7 +1664,7 @@ i915_ring_stop_write(struct file *filp,
|
|||
struct drm_device *dev = filp->private_data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
char buf[20];
|
||||
int val = 0;
|
||||
int val = 0, ret;
|
||||
|
||||
if (cnt > 0) {
|
||||
if (cnt > sizeof(buf) - 1)
|
||||
|
@ -1688,7 +1679,10 @@ i915_ring_stop_write(struct file *filp,
|
|||
|
||||
DRM_DEBUG_DRIVER("Stopping rings 0x%08x\n", val);
|
||||
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_priv->stop_rings = val;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
|
@ -1712,10 +1706,18 @@ i915_max_freq_read(struct file *filp,
|
|||
struct drm_device *dev = filp->private_data;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
char buf[80];
|
||||
int len;
|
||||
int len, ret;
|
||||
|
||||
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
|
||||
return -ENODEV;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"max freq: %d\n", dev_priv->max_delay * 50);
|
||||
"max freq: %d\n", dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (len > sizeof(buf))
|
||||
len = sizeof(buf);
|
||||
|
@ -1732,7 +1734,10 @@ i915_max_freq_write(struct file *filp,
|
|||
struct drm_device *dev = filp->private_data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
char buf[20];
|
||||
int val = 1;
|
||||
int val = 1, ret;
|
||||
|
||||
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
|
||||
return -ENODEV;
|
||||
|
||||
if (cnt > 0) {
|
||||
if (cnt > sizeof(buf) - 1)
|
||||
|
@ -1747,12 +1752,17 @@ i915_max_freq_write(struct file *filp,
|
|||
|
||||
DRM_DEBUG_DRIVER("Manually setting max freq to %d\n", val);
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Turbo will still be enabled, but won't go above the set value.
|
||||
*/
|
||||
dev_priv->max_delay = val / 50;
|
||||
dev_priv->rps.max_delay = val / GT_FREQUENCY_MULTIPLIER;
|
||||
|
||||
gen6_set_rps(dev, val / 50);
|
||||
gen6_set_rps(dev, val / GT_FREQUENCY_MULTIPLIER);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
@ -1772,10 +1782,18 @@ i915_min_freq_read(struct file *filp, char __user *ubuf, size_t max,
|
|||
struct drm_device *dev = filp->private_data;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
char buf[80];
|
||||
int len;
|
||||
int len, ret;
|
||||
|
||||
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
|
||||
return -ENODEV;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
len = snprintf(buf, sizeof(buf),
|
||||
"min freq: %d\n", dev_priv->min_delay * 50);
|
||||
"min freq: %d\n", dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (len > sizeof(buf))
|
||||
len = sizeof(buf);
|
||||
|
@ -1790,7 +1808,10 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|||
struct drm_device *dev = filp->private_data;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
char buf[20];
|
||||
int val = 1;
|
||||
int val = 1, ret;
|
||||
|
||||
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
|
||||
return -ENODEV;
|
||||
|
||||
if (cnt > 0) {
|
||||
if (cnt > sizeof(buf) - 1)
|
||||
|
@ -1805,12 +1826,17 @@ i915_min_freq_write(struct file *filp, const char __user *ubuf, size_t cnt,
|
|||
|
||||
DRM_DEBUG_DRIVER("Manually setting min freq to %d\n", val);
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Turbo will still be enabled, but won't go below the set value.
|
||||
*/
|
||||
dev_priv->min_delay = val / 50;
|
||||
dev_priv->rps.min_delay = val / GT_FREQUENCY_MULTIPLIER;
|
||||
|
||||
gen6_set_rps(dev, val / 50);
|
||||
gen6_set_rps(dev, val / GT_FREQUENCY_MULTIPLIER);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return cnt;
|
||||
}
|
||||
|
@ -1833,9 +1859,15 @@ i915_cache_sharing_read(struct file *filp,
|
|||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
char buf[80];
|
||||
u32 snpcr;
|
||||
int len;
|
||||
int len, ret;
|
||||
|
||||
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
|
||||
return -ENODEV;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_lock(&dev_priv->dev->struct_mutex);
|
||||
snpcr = I915_READ(GEN6_MBCUNIT_SNPCR);
|
||||
mutex_unlock(&dev_priv->dev->struct_mutex);
|
||||
|
||||
|
@ -1861,6 +1893,9 @@ i915_cache_sharing_write(struct file *filp,
|
|||
u32 snpcr;
|
||||
int val = 1;
|
||||
|
||||
if (!(IS_GEN6(dev) || IS_GEN7(dev)))
|
||||
return -ENODEV;
|
||||
|
||||
if (cnt > 0) {
|
||||
if (cnt > sizeof(buf) - 1)
|
||||
return -EINVAL;
|
||||
|
@ -1924,16 +1959,11 @@ static int i915_forcewake_open(struct inode *inode, struct file *file)
|
|||
{
|
||||
struct drm_device *dev = inode->i_private;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
if (INTEL_INFO(dev)->gen < 6)
|
||||
return 0;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
gen6_gt_force_wake_get(dev_priv);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -1946,16 +1976,7 @@ static int i915_forcewake_release(struct inode *inode, struct file *file)
|
|||
if (INTEL_INFO(dev)->gen < 6)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* It's bad that we can potentially hang userspace if struct_mutex gets
|
||||
* forever stuck. However, if we cannot acquire this lock it means that
|
||||
* almost certainly the driver has hung, is not unload-able. Therefore
|
||||
* hanging here is probably a minor inconvenience not to be seen my
|
||||
* almost every user.
|
||||
*/
|
||||
mutex_lock(&dev->struct_mutex);
|
||||
gen6_gt_force_wake_put(dev_priv);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -2005,7 +2026,6 @@ static struct drm_info_list i915_debugfs_list[] = {
|
|||
{"i915_gem_gtt", i915_gem_gtt_info, 0},
|
||||
{"i915_gem_pinned", i915_gem_gtt_info, 0, (void *) PINNED_LIST},
|
||||
{"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
|
||||
{"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
|
||||
{"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
|
||||
{"i915_gem_pageflip", i915_gem_pageflip_info, 0},
|
||||
{"i915_gem_request", i915_gem_request_info, 0},
|
||||
|
@ -2066,6 +2086,7 @@ int i915_debugfs_init(struct drm_minor *minor)
|
|||
&i915_cache_sharing_fops);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i915_debugfs_create(minor->debugfs_root, minor,
|
||||
"i915_ring_stop",
|
||||
&i915_ring_stop_fops);
|
||||
|
|
|
@ -234,10 +234,10 @@ static int i915_initialize(struct drm_device * dev, drm_i915_init_t * init)
|
|||
}
|
||||
}
|
||||
|
||||
dev_priv->cpp = init->cpp;
|
||||
dev_priv->back_offset = init->back_offset;
|
||||
dev_priv->front_offset = init->front_offset;
|
||||
dev_priv->current_page = 0;
|
||||
dev_priv->dri1.cpp = init->cpp;
|
||||
dev_priv->dri1.back_offset = init->back_offset;
|
||||
dev_priv->dri1.front_offset = init->front_offset;
|
||||
dev_priv->dri1.current_page = 0;
|
||||
if (master_priv->sarea_priv)
|
||||
master_priv->sarea_priv->pf_current_page = 0;
|
||||
|
||||
|
@ -574,7 +574,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
|
|||
|
||||
DRM_DEBUG_DRIVER("%s: page=%d pfCurrentPage=%d\n",
|
||||
__func__,
|
||||
dev_priv->current_page,
|
||||
dev_priv->dri1.current_page,
|
||||
master_priv->sarea_priv->pf_current_page);
|
||||
|
||||
i915_kernel_lost_context(dev);
|
||||
|
@ -588,12 +588,12 @@ static int i915_dispatch_flip(struct drm_device * dev)
|
|||
|
||||
OUT_RING(CMD_OP_DISPLAYBUFFER_INFO | ASYNC_FLIP);
|
||||
OUT_RING(0);
|
||||
if (dev_priv->current_page == 0) {
|
||||
OUT_RING(dev_priv->back_offset);
|
||||
dev_priv->current_page = 1;
|
||||
if (dev_priv->dri1.current_page == 0) {
|
||||
OUT_RING(dev_priv->dri1.back_offset);
|
||||
dev_priv->dri1.current_page = 1;
|
||||
} else {
|
||||
OUT_RING(dev_priv->front_offset);
|
||||
dev_priv->current_page = 0;
|
||||
OUT_RING(dev_priv->dri1.front_offset);
|
||||
dev_priv->dri1.current_page = 0;
|
||||
}
|
||||
OUT_RING(0);
|
||||
|
||||
|
@ -612,7 +612,7 @@ static int i915_dispatch_flip(struct drm_device * dev)
|
|||
ADVANCE_LP_RING();
|
||||
}
|
||||
|
||||
master_priv->sarea_priv->pf_current_page = dev_priv->current_page;
|
||||
master_priv->sarea_priv->pf_current_page = dev_priv->dri1.current_page;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1008,6 +1008,12 @@ static int i915_getparam(struct drm_device *dev, void *data,
|
|||
case I915_PARAM_HAS_WAIT_TIMEOUT:
|
||||
value = 1;
|
||||
break;
|
||||
case I915_PARAM_HAS_SEMAPHORES:
|
||||
value = i915_semaphore_is_enabled(dev);
|
||||
break;
|
||||
case I915_PARAM_HAS_PRIME_VMAP_FLUSH:
|
||||
value = 1;
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DRIVER("Unknown parameter %d\n",
|
||||
param->param);
|
||||
|
@ -1424,6 +1430,21 @@ static void i915_kick_out_firmware_fb(struct drm_i915_private *dev_priv)
|
|||
kfree(ap);
|
||||
}
|
||||
|
||||
static void i915_dump_device_info(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
const struct intel_device_info *info = dev_priv->info;
|
||||
|
||||
#define DEV_INFO_FLAG(name) info->name ? #name "," : ""
|
||||
#define DEV_INFO_SEP ,
|
||||
DRM_DEBUG_DRIVER("i915 device info: gen=%i, pciid=0x%04x flags="
|
||||
"%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
|
||||
info->gen,
|
||||
dev_priv->dev->pdev->device,
|
||||
DEV_INFO_FLAGS);
|
||||
#undef DEV_INFO_FLAG
|
||||
#undef DEV_INFO_SEP
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_driver_load - setup chip and create an initial config
|
||||
* @dev: DRM device
|
||||
|
@ -1439,7 +1460,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
{
|
||||
struct drm_i915_private *dev_priv;
|
||||
struct intel_device_info *info;
|
||||
int ret = 0, mmio_bar;
|
||||
int ret = 0, mmio_bar, mmio_size;
|
||||
uint32_t aperture_size;
|
||||
|
||||
info = (struct intel_device_info *) flags;
|
||||
|
@ -1448,7 +1469,6 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
if (info->gen >= 6 && !drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
return -ENODEV;
|
||||
|
||||
|
||||
/* i915 has 4 more counters */
|
||||
dev->counters += 4;
|
||||
dev->types[6] = _DRM_STAT_IRQ;
|
||||
|
@ -1464,6 +1484,8 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
dev_priv->dev = dev;
|
||||
dev_priv->info = info;
|
||||
|
||||
i915_dump_device_info(dev_priv);
|
||||
|
||||
if (i915_get_bridge_dev(dev)) {
|
||||
ret = -EIO;
|
||||
goto free_priv;
|
||||
|
@ -1503,7 +1525,19 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
dma_set_coherent_mask(&dev->pdev->dev, DMA_BIT_MASK(32));
|
||||
|
||||
mmio_bar = IS_GEN2(dev) ? 1 : 0;
|
||||
dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, 0);
|
||||
/* Before gen4, the registers and the GTT are behind different BARs.
|
||||
* However, from gen4 onwards, the registers and the GTT are shared
|
||||
* in the same BAR, so we want to restrict this ioremap from
|
||||
* clobbering the GTT which we want ioremap_wc instead. Fortunately,
|
||||
* the register BAR remains the same size for all the earlier
|
||||
* generations up to Ironlake.
|
||||
*/
|
||||
if (info->gen < 5)
|
||||
mmio_size = 512*1024;
|
||||
else
|
||||
mmio_size = 2*1024*1024;
|
||||
|
||||
dev_priv->regs = pci_iomap(dev->pdev, mmio_bar, mmio_size);
|
||||
if (!dev_priv->regs) {
|
||||
DRM_ERROR("failed to map registers\n");
|
||||
ret = -EIO;
|
||||
|
@ -1535,11 +1569,9 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
*
|
||||
* All tasks on the workqueue are expected to acquire the dev mutex
|
||||
* so there is no point in running more than one instance of the
|
||||
* workqueue at any time: max_active = 1 and NON_REENTRANT.
|
||||
* workqueue at any time. Use an ordered one.
|
||||
*/
|
||||
dev_priv->wq = alloc_workqueue("i915",
|
||||
WQ_UNBOUND | WQ_NON_REENTRANT,
|
||||
1);
|
||||
dev_priv->wq = alloc_ordered_workqueue("i915", 0);
|
||||
if (dev_priv->wq == NULL) {
|
||||
DRM_ERROR("Failed to create our workqueue.\n");
|
||||
ret = -ENOMEM;
|
||||
|
@ -1585,7 +1617,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
|
|||
|
||||
spin_lock_init(&dev_priv->irq_lock);
|
||||
spin_lock_init(&dev_priv->error_lock);
|
||||
spin_lock_init(&dev_priv->rps_lock);
|
||||
spin_lock_init(&dev_priv->rps.lock);
|
||||
spin_lock_init(&dev_priv->dpio_lock);
|
||||
|
||||
if (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
|
||||
|
@ -1835,6 +1867,8 @@ struct drm_ioctl_desc i915_ioctls[] = {
|
|||
DRM_IOCTL_DEF_DRV(I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_SET_CACHING, i915_gem_set_caching_ioctl, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_GET_CACHING, i915_gem_get_caching_ioctl, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_THROTTLE, i915_gem_throttle_ioctl, DRM_AUTH|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_ENTERVT, i915_gem_entervt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_LEAVEVT, i915_gem_leavevt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
|
||||
|
@ -1857,6 +1891,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
|
|||
DRM_IOCTL_DEF_DRV(I915_GEM_WAIT, i915_gem_wait_ioctl, DRM_AUTH|DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_CREATE, i915_gem_context_create_ioctl, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_GEM_CONTEXT_DESTROY, i915_gem_context_destroy_ioctl, DRM_UNLOCKED),
|
||||
DRM_IOCTL_DEF_DRV(I915_REG_READ, i915_reg_read_ioctl, DRM_UNLOCKED),
|
||||
};
|
||||
|
||||
int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
|
||||
|
|
|
@ -469,6 +469,9 @@ static int i915_drm_freeze(struct drm_device *dev)
|
|||
"GEM idle failed, resume might fail\n");
|
||||
return error;
|
||||
}
|
||||
|
||||
intel_modeset_disable(dev);
|
||||
|
||||
drm_irq_uninstall(dev);
|
||||
}
|
||||
|
||||
|
@ -542,13 +545,9 @@ static int i915_drm_thaw(struct drm_device *dev)
|
|||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
intel_modeset_init_hw(dev);
|
||||
intel_modeset_setup_hw_state(dev);
|
||||
drm_mode_config_reset(dev);
|
||||
drm_irq_install(dev);
|
||||
|
||||
/* Resume the modeset for every activated CRTC */
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
drm_helper_resume_force_mode(dev);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
}
|
||||
|
||||
intel_opregion_init(dev);
|
||||
|
@ -1059,7 +1058,7 @@ static bool IS_DISPLAYREG(u32 reg)
|
|||
* This should make it easier to transition modules over to the
|
||||
* new register block scheme, since we can do it incrementally.
|
||||
*/
|
||||
if (reg >= 0x180000)
|
||||
if (reg >= VLV_DISPLAY_BASE)
|
||||
return false;
|
||||
|
||||
if (reg >= RENDER_RING_BASE &&
|
||||
|
@ -1173,9 +1172,59 @@ void i915_write##x(struct drm_i915_private *dev_priv, u32 reg, u##x val) { \
|
|||
if (unlikely(__fifo_ret)) { \
|
||||
gen6_gt_check_fifodbg(dev_priv); \
|
||||
} \
|
||||
if (IS_HASWELL(dev_priv->dev) && (I915_READ_NOTRACE(GEN7_ERR_INT) & ERR_INT_MMIO_UNCLAIMED)) { \
|
||||
DRM_ERROR("Unclaimed write to %x\n", reg); \
|
||||
writel(ERR_INT_MMIO_UNCLAIMED, dev_priv->regs + GEN7_ERR_INT); \
|
||||
} \
|
||||
}
|
||||
__i915_write(8, b)
|
||||
__i915_write(16, w)
|
||||
__i915_write(32, l)
|
||||
__i915_write(64, q)
|
||||
#undef __i915_write
|
||||
|
||||
static const struct register_whitelist {
|
||||
uint64_t offset;
|
||||
uint32_t size;
|
||||
uint32_t gen_bitmask; /* support gens, 0x10 for 4, 0x30 for 4 and 5, etc. */
|
||||
} whitelist[] = {
|
||||
{ RING_TIMESTAMP(RENDER_RING_BASE), 8, 0xF0 },
|
||||
};
|
||||
|
||||
int i915_reg_read_ioctl(struct drm_device *dev,
|
||||
void *data, struct drm_file *file)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_i915_reg_read *reg = data;
|
||||
struct register_whitelist const *entry = whitelist;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(whitelist); i++, entry++) {
|
||||
if (entry->offset == reg->offset &&
|
||||
(1 << INTEL_INFO(dev)->gen & entry->gen_bitmask))
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == ARRAY_SIZE(whitelist))
|
||||
return -EINVAL;
|
||||
|
||||
switch (entry->size) {
|
||||
case 8:
|
||||
reg->val = I915_READ64(reg->offset);
|
||||
break;
|
||||
case 4:
|
||||
reg->val = I915_READ(reg->offset);
|
||||
break;
|
||||
case 2:
|
||||
reg->val = I915_READ16(reg->offset);
|
||||
break;
|
||||
case 1:
|
||||
reg->val = I915_READ8(reg->offset);
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -109,6 +109,7 @@ struct intel_pch_pll {
|
|||
|
||||
#define WATCH_COHERENCY 0
|
||||
#define WATCH_LISTS 0
|
||||
#define WATCH_GTT 0
|
||||
|
||||
#define I915_GEM_PHYS_CURSOR_0 1
|
||||
#define I915_GEM_PHYS_CURSOR_1 2
|
||||
|
@ -195,9 +196,10 @@ struct drm_i915_error_state {
|
|||
u32 cpu_ring_head[I915_NUM_RINGS];
|
||||
u32 cpu_ring_tail[I915_NUM_RINGS];
|
||||
u32 error; /* gen6+ */
|
||||
u32 err_int; /* gen7 */
|
||||
u32 instpm[I915_NUM_RINGS];
|
||||
u32 instps[I915_NUM_RINGS];
|
||||
u32 instdone1;
|
||||
u32 extra_instdone[I915_NUM_INSTDONE_REG];
|
||||
u32 seqno[I915_NUM_RINGS];
|
||||
u64 bbaddr;
|
||||
u32 fault_reg[I915_NUM_RINGS];
|
||||
|
@ -221,7 +223,7 @@ struct drm_i915_error_state {
|
|||
struct drm_i915_error_buffer {
|
||||
u32 size;
|
||||
u32 name;
|
||||
u32 seqno;
|
||||
u32 rseqno, wseqno;
|
||||
u32 gtt_offset;
|
||||
u32 read_domains;
|
||||
u32 write_domain;
|
||||
|
@ -239,7 +241,6 @@ struct drm_i915_error_state {
|
|||
};
|
||||
|
||||
struct drm_i915_display_funcs {
|
||||
void (*dpms)(struct drm_crtc *crtc, int mode);
|
||||
bool (*fbc_enabled)(struct drm_device *dev);
|
||||
void (*enable_fbc)(struct drm_crtc *crtc, unsigned long interval);
|
||||
void (*disable_fbc)(struct drm_device *dev);
|
||||
|
@ -248,7 +249,6 @@ struct drm_i915_display_funcs {
|
|||
void (*update_wm)(struct drm_device *dev);
|
||||
void (*update_sprite_wm)(struct drm_device *dev, int pipe,
|
||||
uint32_t sprite_width, int pixel_size);
|
||||
void (*sanitize_pm)(struct drm_device *dev);
|
||||
void (*update_linetime_wm)(struct drm_device *dev, int pipe,
|
||||
struct drm_display_mode *mode);
|
||||
int (*crtc_mode_set)(struct drm_crtc *crtc,
|
||||
|
@ -256,6 +256,8 @@ struct drm_i915_display_funcs {
|
|||
struct drm_display_mode *adjusted_mode,
|
||||
int x, int y,
|
||||
struct drm_framebuffer *old_fb);
|
||||
void (*crtc_enable)(struct drm_crtc *crtc);
|
||||
void (*crtc_disable)(struct drm_crtc *crtc);
|
||||
void (*off)(struct drm_crtc *crtc);
|
||||
void (*write_eld)(struct drm_connector *connector,
|
||||
struct drm_crtc *crtc);
|
||||
|
@ -279,6 +281,32 @@ struct drm_i915_gt_funcs {
|
|||
void (*force_wake_put)(struct drm_i915_private *dev_priv);
|
||||
};
|
||||
|
||||
#define DEV_INFO_FLAGS \
|
||||
DEV_INFO_FLAG(is_mobile) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_i85x) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_i915g) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_i945gm) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_g33) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(need_gfx_hws) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_g4x) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_pineview) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_broadwater) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_crestline) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_ivybridge) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_valleyview) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(is_haswell) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_force_wake) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_fbc) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_pipe_cxsr) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_hotplug) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(cursor_needs_physical) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_overlay) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(overlay_needs_physical) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(supports_tv) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_bsd_ring) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_blt_ring) DEV_INFO_SEP \
|
||||
DEV_INFO_FLAG(has_llc)
|
||||
|
||||
struct intel_device_info {
|
||||
u8 gen;
|
||||
u8 is_mobile:1;
|
||||
|
@ -402,12 +430,6 @@ typedef struct drm_i915_private {
|
|||
|
||||
struct resource mch_res;
|
||||
|
||||
unsigned int cpp;
|
||||
int back_offset;
|
||||
int front_offset;
|
||||
int current_page;
|
||||
int page_flipping;
|
||||
|
||||
atomic_t irq_received;
|
||||
|
||||
/* protects the irq masks */
|
||||
|
@ -425,7 +447,6 @@ typedef struct drm_i915_private {
|
|||
u32 hotplug_supported_mask;
|
||||
struct work_struct hotplug_work;
|
||||
|
||||
unsigned int sr01, adpa, ppcr, dvob, dvoc, lvds;
|
||||
int num_pipe;
|
||||
int num_pch_pll;
|
||||
|
||||
|
@ -434,8 +455,7 @@ typedef struct drm_i915_private {
|
|||
struct timer_list hangcheck_timer;
|
||||
int hangcheck_count;
|
||||
uint32_t last_acthd[I915_NUM_RINGS];
|
||||
uint32_t last_instdone;
|
||||
uint32_t last_instdone1;
|
||||
uint32_t prev_instdone[I915_NUM_INSTDONE_REG];
|
||||
|
||||
unsigned int stop_rings;
|
||||
|
||||
|
@ -666,7 +686,13 @@ typedef struct drm_i915_private {
|
|||
struct drm_mm gtt_space;
|
||||
/** List of all objects in gtt_space. Used to restore gtt
|
||||
* mappings on resume */
|
||||
struct list_head gtt_list;
|
||||
struct list_head bound_list;
|
||||
/**
|
||||
* List of objects which are not bound to the GTT (thus
|
||||
* are idle and not used by the GPU) but still have
|
||||
* (presumably uncached) pages still attached.
|
||||
*/
|
||||
struct list_head unbound_list;
|
||||
|
||||
/** Usable portion of the GTT for GEM */
|
||||
unsigned long gtt_start;
|
||||
|
@ -695,17 +721,6 @@ typedef struct drm_i915_private {
|
|||
*/
|
||||
struct list_head active_list;
|
||||
|
||||
/**
|
||||
* List of objects which are not in the ringbuffer but which
|
||||
* still have a write_domain which needs to be flushed before
|
||||
* unbinding.
|
||||
*
|
||||
* last_rendering_seqno is 0 while an object is in this list.
|
||||
*
|
||||
* A reference is held on the buffer while on this list.
|
||||
*/
|
||||
struct list_head flushing_list;
|
||||
|
||||
/**
|
||||
* LRU list of objects which are not in the ringbuffer and
|
||||
* are ready to unbind, but are still in the GTT.
|
||||
|
@ -775,6 +790,12 @@ typedef struct drm_i915_private {
|
|||
struct {
|
||||
unsigned allow_batchbuffer : 1;
|
||||
u32 __iomem *gfx_hws_cpu_addr;
|
||||
|
||||
unsigned int cpp;
|
||||
int back_offset;
|
||||
int front_offset;
|
||||
int current_page;
|
||||
int page_flipping;
|
||||
} dri1;
|
||||
|
||||
/* Kernel Modesetting */
|
||||
|
@ -796,9 +817,6 @@ typedef struct drm_i915_private {
|
|||
bool lvds_downclock_avail;
|
||||
/* indicates the reduced downclock for LVDS*/
|
||||
int lvds_downclock;
|
||||
struct work_struct idle_work;
|
||||
struct timer_list idle_timer;
|
||||
bool busy;
|
||||
u16 orig_clock;
|
||||
int child_dev_num;
|
||||
struct child_device_config *child_dev;
|
||||
|
@ -807,26 +825,41 @@ typedef struct drm_i915_private {
|
|||
|
||||
bool mchbar_need_disable;
|
||||
|
||||
struct work_struct rps_work;
|
||||
spinlock_t rps_lock;
|
||||
u32 pm_iir;
|
||||
/* gen6+ rps state */
|
||||
struct {
|
||||
struct work_struct work;
|
||||
u32 pm_iir;
|
||||
/* lock - irqsave spinlock that protectects the work_struct and
|
||||
* pm_iir. */
|
||||
spinlock_t lock;
|
||||
|
||||
u8 cur_delay;
|
||||
u8 min_delay;
|
||||
u8 max_delay;
|
||||
u8 fmax;
|
||||
u8 fstart;
|
||||
/* The below variables an all the rps hw state are protected by
|
||||
* dev->struct mutext. */
|
||||
u8 cur_delay;
|
||||
u8 min_delay;
|
||||
u8 max_delay;
|
||||
} rps;
|
||||
|
||||
u64 last_count1;
|
||||
unsigned long last_time1;
|
||||
unsigned long chipset_power;
|
||||
u64 last_count2;
|
||||
struct timespec last_time2;
|
||||
unsigned long gfx_power;
|
||||
int c_m;
|
||||
int r_t;
|
||||
u8 corr;
|
||||
spinlock_t *mchdev_lock;
|
||||
/* ilk-only ips/rps state. Everything in here is protected by the global
|
||||
* mchdev_lock in intel_pm.c */
|
||||
struct {
|
||||
u8 cur_delay;
|
||||
u8 min_delay;
|
||||
u8 max_delay;
|
||||
u8 fmax;
|
||||
u8 fstart;
|
||||
|
||||
u64 last_count1;
|
||||
unsigned long last_time1;
|
||||
unsigned long chipset_power;
|
||||
u64 last_count2;
|
||||
struct timespec last_time2;
|
||||
unsigned long gfx_power;
|
||||
u8 corr;
|
||||
|
||||
int c_m;
|
||||
int r_t;
|
||||
} ips;
|
||||
|
||||
enum no_fbc_reason no_fbc_reason;
|
||||
|
||||
|
@ -861,30 +894,48 @@ enum hdmi_force_audio {
|
|||
};
|
||||
|
||||
enum i915_cache_level {
|
||||
I915_CACHE_NONE,
|
||||
I915_CACHE_NONE = 0,
|
||||
I915_CACHE_LLC,
|
||||
I915_CACHE_LLC_MLC, /* gen6+ */
|
||||
I915_CACHE_LLC_MLC, /* gen6+, in docs at least! */
|
||||
};
|
||||
|
||||
struct drm_i915_gem_object_ops {
|
||||
/* Interface between the GEM object and its backing storage.
|
||||
* get_pages() is called once prior to the use of the associated set
|
||||
* of pages before to binding them into the GTT, and put_pages() is
|
||||
* called after we no longer need them. As we expect there to be
|
||||
* associated cost with migrating pages between the backing storage
|
||||
* and making them available for the GPU (e.g. clflush), we may hold
|
||||
* onto the pages after they are no longer referenced by the GPU
|
||||
* in case they may be used again shortly (for example migrating the
|
||||
* pages to a different memory domain within the GTT). put_pages()
|
||||
* will therefore most likely be called when the object itself is
|
||||
* being released or under memory pressure (where we attempt to
|
||||
* reap pages for the shrinker).
|
||||
*/
|
||||
int (*get_pages)(struct drm_i915_gem_object *);
|
||||
void (*put_pages)(struct drm_i915_gem_object *);
|
||||
};
|
||||
|
||||
struct drm_i915_gem_object {
|
||||
struct drm_gem_object base;
|
||||
|
||||
const struct drm_i915_gem_object_ops *ops;
|
||||
|
||||
/** Current space allocated to this object in the GTT, if any. */
|
||||
struct drm_mm_node *gtt_space;
|
||||
struct list_head gtt_list;
|
||||
|
||||
/** This object's place on the active/flushing/inactive lists */
|
||||
/** This object's place on the active/inactive lists */
|
||||
struct list_head ring_list;
|
||||
struct list_head mm_list;
|
||||
/** This object's place on GPU write list */
|
||||
struct list_head gpu_write_list;
|
||||
/** This object's place in the batchbuffer or on the eviction list */
|
||||
struct list_head exec_list;
|
||||
|
||||
/**
|
||||
* This is set if the object is on the active or flushing lists
|
||||
* (has pending rendering), and is not set if it's on inactive (ready
|
||||
* to be unbound).
|
||||
* This is set if the object is on the active lists (has pending
|
||||
* rendering and so a non-zero seqno), and is not set if it i s on
|
||||
* inactive (ready to be unbound) list.
|
||||
*/
|
||||
unsigned int active:1;
|
||||
|
||||
|
@ -894,12 +945,6 @@ struct drm_i915_gem_object {
|
|||
*/
|
||||
unsigned int dirty:1;
|
||||
|
||||
/**
|
||||
* This is set if the object has been written to since the last
|
||||
* GPU flush.
|
||||
*/
|
||||
unsigned int pending_gpu_write:1;
|
||||
|
||||
/**
|
||||
* Fence register bits (if any) for this object. Will be set
|
||||
* as needed when mapped into the GTT.
|
||||
|
@ -961,17 +1006,12 @@ struct drm_i915_gem_object {
|
|||
|
||||
unsigned int has_aliasing_ppgtt_mapping:1;
|
||||
unsigned int has_global_gtt_mapping:1;
|
||||
unsigned int has_dma_mapping:1;
|
||||
|
||||
struct page **pages;
|
||||
|
||||
/**
|
||||
* DMAR support
|
||||
*/
|
||||
struct scatterlist *sg_list;
|
||||
int num_sg;
|
||||
struct sg_table *pages;
|
||||
int pages_pin_count;
|
||||
|
||||
/* prime dma-buf support */
|
||||
struct sg_table *sg_table;
|
||||
void *dma_buf_vmapping;
|
||||
int vmapping_count;
|
||||
|
||||
|
@ -992,7 +1032,8 @@ struct drm_i915_gem_object {
|
|||
struct intel_ring_buffer *ring;
|
||||
|
||||
/** Breadcrumb of last rendering to the buffer. */
|
||||
uint32_t last_rendering_seqno;
|
||||
uint32_t last_read_seqno;
|
||||
uint32_t last_write_seqno;
|
||||
/** Breadcrumb of last fenced GPU access to the buffer. */
|
||||
uint32_t last_fenced_seqno;
|
||||
|
||||
|
@ -1135,6 +1176,10 @@ struct drm_i915_file_private {
|
|||
|
||||
#define HAS_FORCE_WAKE(dev) (INTEL_INFO(dev)->has_force_wake)
|
||||
|
||||
#define HAS_L3_GPU_CACHE(dev) (IS_IVYBRIDGE(dev) || IS_HASWELL(dev))
|
||||
|
||||
#define GT_FREQUENCY_MULTIPLIER 50
|
||||
|
||||
#include "i915_trace.h"
|
||||
|
||||
/**
|
||||
|
@ -1256,6 +1301,10 @@ int i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
|
|||
struct drm_file *file_priv);
|
||||
int i915_gem_busy_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
int i915_gem_set_caching_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
int i915_gem_throttle_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file_priv);
|
||||
int i915_gem_madvise_ioctl(struct drm_device *dev, void *data,
|
||||
|
@ -1274,24 +1323,42 @@ int i915_gem_wait_ioctl(struct drm_device *dev, void *data,
|
|||
struct drm_file *file_priv);
|
||||
void i915_gem_load(struct drm_device *dev);
|
||||
int i915_gem_init_object(struct drm_gem_object *obj);
|
||||
int __must_check i915_gem_flush_ring(struct intel_ring_buffer *ring,
|
||||
uint32_t invalidate_domains,
|
||||
uint32_t flush_domains);
|
||||
void i915_gem_object_init(struct drm_i915_gem_object *obj,
|
||||
const struct drm_i915_gem_object_ops *ops);
|
||||
struct drm_i915_gem_object *i915_gem_alloc_object(struct drm_device *dev,
|
||||
size_t size);
|
||||
void i915_gem_free_object(struct drm_gem_object *obj);
|
||||
int __must_check i915_gem_object_pin(struct drm_i915_gem_object *obj,
|
||||
uint32_t alignment,
|
||||
bool map_and_fenceable);
|
||||
bool map_and_fenceable,
|
||||
bool nonblocking);
|
||||
void i915_gem_object_unpin(struct drm_i915_gem_object *obj);
|
||||
int __must_check i915_gem_object_unbind(struct drm_i915_gem_object *obj);
|
||||
void i915_gem_release_mmap(struct drm_i915_gem_object *obj);
|
||||
void i915_gem_lastclose(struct drm_device *dev);
|
||||
|
||||
int i915_gem_object_get_pages_gtt(struct drm_i915_gem_object *obj,
|
||||
gfp_t gfpmask);
|
||||
int __must_check i915_gem_object_get_pages(struct drm_i915_gem_object *obj);
|
||||
static inline struct page *i915_gem_object_get_page(struct drm_i915_gem_object *obj, int n)
|
||||
{
|
||||
struct scatterlist *sg = obj->pages->sgl;
|
||||
while (n >= SG_MAX_SINGLE_ALLOC) {
|
||||
sg = sg_chain_ptr(sg + SG_MAX_SINGLE_ALLOC - 1);
|
||||
n -= SG_MAX_SINGLE_ALLOC - 1;
|
||||
}
|
||||
return sg_page(sg+n);
|
||||
}
|
||||
static inline void i915_gem_object_pin_pages(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
BUG_ON(obj->pages == NULL);
|
||||
obj->pages_pin_count++;
|
||||
}
|
||||
static inline void i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
BUG_ON(obj->pages_pin_count == 0);
|
||||
obj->pages_pin_count--;
|
||||
}
|
||||
|
||||
int __must_check i915_mutex_lock_interruptible(struct drm_device *dev);
|
||||
int __must_check i915_gem_object_wait_rendering(struct drm_i915_gem_object *obj);
|
||||
int i915_gem_object_sync(struct drm_i915_gem_object *obj,
|
||||
struct intel_ring_buffer *to);
|
||||
void i915_gem_object_move_to_active(struct drm_i915_gem_object *obj,
|
||||
|
@ -1358,9 +1425,9 @@ void i915_gem_init_ppgtt(struct drm_device *dev);
|
|||
void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
|
||||
int __must_check i915_gpu_idle(struct drm_device *dev);
|
||||
int __must_check i915_gem_idle(struct drm_device *dev);
|
||||
int __must_check i915_add_request(struct intel_ring_buffer *ring,
|
||||
struct drm_file *file,
|
||||
struct drm_i915_gem_request *request);
|
||||
int i915_add_request(struct intel_ring_buffer *ring,
|
||||
struct drm_file *file,
|
||||
struct drm_i915_gem_request *request);
|
||||
int __must_check i915_wait_seqno(struct intel_ring_buffer *ring,
|
||||
uint32_t seqno);
|
||||
int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
|
||||
|
@ -1429,8 +1496,11 @@ void i915_gem_init_global_gtt(struct drm_device *dev,
|
|||
|
||||
/* i915_gem_evict.c */
|
||||
int __must_check i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
unsigned alignment, bool mappable);
|
||||
int i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only);
|
||||
unsigned alignment,
|
||||
unsigned cache_level,
|
||||
bool mappable,
|
||||
bool nonblock);
|
||||
int i915_gem_evict_everything(struct drm_device *dev);
|
||||
|
||||
/* i915_gem_stolen.c */
|
||||
int i915_gem_init_stolen(struct drm_device *dev);
|
||||
|
@ -1519,6 +1589,7 @@ extern void intel_modeset_init(struct drm_device *dev);
|
|||
extern void intel_modeset_gem_init(struct drm_device *dev);
|
||||
extern void intel_modeset_cleanup(struct drm_device *dev);
|
||||
extern int intel_modeset_vga_set_state(struct drm_device *dev, bool state);
|
||||
extern void intel_modeset_setup_hw_state(struct drm_device *dev);
|
||||
extern bool intel_fbc_enabled(struct drm_device *dev);
|
||||
extern void intel_disable_fbc(struct drm_device *dev);
|
||||
extern bool ironlake_set_drps(struct drm_device *dev, u8 val);
|
||||
|
@ -1529,6 +1600,8 @@ extern int intel_trans_dp_port_sel(struct drm_crtc *crtc);
|
|||
extern int intel_enable_rc6(const struct drm_device *dev);
|
||||
|
||||
extern bool i915_semaphore_is_enabled(struct drm_device *dev);
|
||||
int i915_reg_read_ioctl(struct drm_device *dev, void *data,
|
||||
struct drm_file *file);
|
||||
|
||||
/* overlay */
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -97,8 +97,7 @@
|
|||
|
||||
static struct i915_hw_context *
|
||||
i915_gem_context_get(struct drm_i915_file_private *file_priv, u32 id);
|
||||
static int do_switch(struct drm_i915_gem_object *from_obj,
|
||||
struct i915_hw_context *to, u32 seqno);
|
||||
static int do_switch(struct i915_hw_context *to);
|
||||
|
||||
static int get_context_size(struct drm_device *dev)
|
||||
{
|
||||
|
@ -113,7 +112,10 @@ static int get_context_size(struct drm_device *dev)
|
|||
break;
|
||||
case 7:
|
||||
reg = I915_READ(GEN7_CXT_SIZE);
|
||||
ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
|
||||
if (IS_HASWELL(dev))
|
||||
ret = HSW_CXT_TOTAL_SIZE(reg) * 64;
|
||||
else
|
||||
ret = GEN7_CXT_TOTAL_SIZE(reg) * 64;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
|
@ -219,20 +221,21 @@ static int create_default_context(struct drm_i915_private *dev_priv)
|
|||
* default context.
|
||||
*/
|
||||
dev_priv->ring[RCS].default_context = ctx;
|
||||
ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false);
|
||||
if (ret) {
|
||||
do_destroy(ctx);
|
||||
return ret;
|
||||
}
|
||||
ret = i915_gem_object_pin(ctx->obj, CONTEXT_ALIGN, false, false);
|
||||
if (ret)
|
||||
goto err_destroy;
|
||||
|
||||
ret = do_switch(NULL, ctx, 0);
|
||||
if (ret) {
|
||||
i915_gem_object_unpin(ctx->obj);
|
||||
do_destroy(ctx);
|
||||
} else {
|
||||
DRM_DEBUG_DRIVER("Default HW context loaded\n");
|
||||
}
|
||||
ret = do_switch(ctx);
|
||||
if (ret)
|
||||
goto err_unpin;
|
||||
|
||||
DRM_DEBUG_DRIVER("Default HW context loaded\n");
|
||||
return 0;
|
||||
|
||||
err_unpin:
|
||||
i915_gem_object_unpin(ctx->obj);
|
||||
err_destroy:
|
||||
do_destroy(ctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -359,18 +362,19 @@ mi_set_context(struct intel_ring_buffer *ring,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int do_switch(struct drm_i915_gem_object *from_obj,
|
||||
struct i915_hw_context *to,
|
||||
u32 seqno)
|
||||
static int do_switch(struct i915_hw_context *to)
|
||||
{
|
||||
struct intel_ring_buffer *ring = NULL;
|
||||
struct intel_ring_buffer *ring = to->ring;
|
||||
struct drm_i915_gem_object *from_obj = ring->last_context_obj;
|
||||
u32 hw_flags = 0;
|
||||
int ret;
|
||||
|
||||
BUG_ON(to == NULL);
|
||||
BUG_ON(from_obj != NULL && from_obj->pin_count == 0);
|
||||
|
||||
ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false);
|
||||
if (from_obj == to->obj)
|
||||
return 0;
|
||||
|
||||
ret = i915_gem_object_pin(to->obj, CONTEXT_ALIGN, false, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -393,7 +397,6 @@ static int do_switch(struct drm_i915_gem_object *from_obj,
|
|||
else if (WARN_ON_ONCE(from_obj == to->obj)) /* not yet expected */
|
||||
hw_flags |= MI_FORCE_RESTORE;
|
||||
|
||||
ring = to->ring;
|
||||
ret = mi_set_context(ring, to, hw_flags);
|
||||
if (ret) {
|
||||
i915_gem_object_unpin(to->obj);
|
||||
|
@ -407,6 +410,7 @@ static int do_switch(struct drm_i915_gem_object *from_obj,
|
|||
* MI_SET_CONTEXT instead of when the next seqno has completed.
|
||||
*/
|
||||
if (from_obj != NULL) {
|
||||
u32 seqno = i915_gem_next_request_seqno(ring);
|
||||
from_obj->base.read_domains = I915_GEM_DOMAIN_INSTRUCTION;
|
||||
i915_gem_object_move_to_active(from_obj, ring, seqno);
|
||||
/* As long as MI_SET_CONTEXT is serializing, ie. it flushes the
|
||||
|
@ -417,7 +421,7 @@ static int do_switch(struct drm_i915_gem_object *from_obj,
|
|||
* swapped, but there is no way to do that yet.
|
||||
*/
|
||||
from_obj->dirty = 1;
|
||||
BUG_ON(from_obj->ring != to->ring);
|
||||
BUG_ON(from_obj->ring != ring);
|
||||
i915_gem_object_unpin(from_obj);
|
||||
|
||||
drm_gem_object_unreference(&from_obj->base);
|
||||
|
@ -448,9 +452,7 @@ int i915_switch_context(struct intel_ring_buffer *ring,
|
|||
int to_id)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = ring->dev->dev_private;
|
||||
struct drm_i915_file_private *file_priv = NULL;
|
||||
struct i915_hw_context *to;
|
||||
struct drm_i915_gem_object *from_obj = ring->last_context_obj;
|
||||
|
||||
if (dev_priv->hw_contexts_disabled)
|
||||
return 0;
|
||||
|
@ -458,21 +460,18 @@ int i915_switch_context(struct intel_ring_buffer *ring,
|
|||
if (ring != &dev_priv->ring[RCS])
|
||||
return 0;
|
||||
|
||||
if (file)
|
||||
file_priv = file->driver_priv;
|
||||
|
||||
if (to_id == DEFAULT_CONTEXT_ID) {
|
||||
to = ring->default_context;
|
||||
} else {
|
||||
to = i915_gem_context_get(file_priv, to_id);
|
||||
if (file == NULL)
|
||||
return -EINVAL;
|
||||
|
||||
to = i915_gem_context_get(file->driver_priv, to_id);
|
||||
if (to == NULL)
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (from_obj == to->obj)
|
||||
return 0;
|
||||
|
||||
return do_switch(from_obj, to, i915_gem_next_request_seqno(to->ring));
|
||||
return do_switch(to);
|
||||
}
|
||||
|
||||
int i915_gem_context_create_ioctl(struct drm_device *dev, void *data,
|
||||
|
|
|
@ -28,35 +28,62 @@
|
|||
#include <linux/dma-buf.h>
|
||||
|
||||
static struct sg_table *i915_gem_map_dma_buf(struct dma_buf_attachment *attachment,
|
||||
enum dma_data_direction dir)
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct drm_i915_gem_object *obj = attachment->dmabuf->priv;
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
int npages = obj->base.size / PAGE_SIZE;
|
||||
struct sg_table *sg = NULL;
|
||||
int ret;
|
||||
int nents;
|
||||
struct sg_table *st;
|
||||
struct scatterlist *src, *dst;
|
||||
int ret, i;
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
ret = i915_mutex_lock_interruptible(obj->base.dev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (!obj->pages) {
|
||||
ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN);
|
||||
if (ret)
|
||||
goto out;
|
||||
ret = i915_gem_object_get_pages(obj);
|
||||
if (ret) {
|
||||
st = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* link the pages into an SG then map the sg */
|
||||
sg = drm_prime_pages_to_sg(obj->pages, npages);
|
||||
nents = dma_map_sg(attachment->dev, sg->sgl, sg->nents, dir);
|
||||
/* Copy sg so that we make an independent mapping */
|
||||
st = kmalloc(sizeof(struct sg_table), GFP_KERNEL);
|
||||
if (st == NULL) {
|
||||
st = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = sg_alloc_table(st, obj->pages->nents, GFP_KERNEL);
|
||||
if (ret) {
|
||||
kfree(st);
|
||||
st = ERR_PTR(ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
src = obj->pages->sgl;
|
||||
dst = st->sgl;
|
||||
for (i = 0; i < obj->pages->nents; i++) {
|
||||
sg_set_page(dst, sg_page(src), PAGE_SIZE, 0);
|
||||
dst = sg_next(dst);
|
||||
src = sg_next(src);
|
||||
}
|
||||
|
||||
if (!dma_map_sg(attachment->dev, st->sgl, st->nents, dir)) {
|
||||
sg_free_table(st);
|
||||
kfree(st);
|
||||
st = ERR_PTR(-ENOMEM);
|
||||
goto out;
|
||||
}
|
||||
|
||||
i915_gem_object_pin_pages(obj);
|
||||
|
||||
out:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return sg;
|
||||
mutex_unlock(&obj->base.dev->struct_mutex);
|
||||
return st;
|
||||
}
|
||||
|
||||
static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
|
||||
struct sg_table *sg, enum dma_data_direction dir)
|
||||
struct sg_table *sg,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
|
||||
sg_free_table(sg);
|
||||
|
@ -78,7 +105,9 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf)
|
|||
{
|
||||
struct drm_i915_gem_object *obj = dma_buf->priv;
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
int ret;
|
||||
struct scatterlist *sg;
|
||||
struct page **pages;
|
||||
int ret, i;
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
|
@ -89,24 +118,34 @@ static void *i915_gem_dmabuf_vmap(struct dma_buf *dma_buf)
|
|||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (!obj->pages) {
|
||||
ret = i915_gem_object_get_pages_gtt(obj, __GFP_NORETRY | __GFP_NOWARN);
|
||||
if (ret) {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
}
|
||||
ret = i915_gem_object_get_pages(obj);
|
||||
if (ret)
|
||||
goto error;
|
||||
|
||||
obj->dma_buf_vmapping = vmap(obj->pages, obj->base.size / PAGE_SIZE, 0, PAGE_KERNEL);
|
||||
if (!obj->dma_buf_vmapping) {
|
||||
DRM_ERROR("failed to vmap object\n");
|
||||
goto out_unlock;
|
||||
}
|
||||
ret = -ENOMEM;
|
||||
|
||||
pages = drm_malloc_ab(obj->pages->nents, sizeof(struct page *));
|
||||
if (pages == NULL)
|
||||
goto error;
|
||||
|
||||
for_each_sg(obj->pages->sgl, sg, obj->pages->nents, i)
|
||||
pages[i] = sg_page(sg);
|
||||
|
||||
obj->dma_buf_vmapping = vmap(pages, obj->pages->nents, 0, PAGE_KERNEL);
|
||||
drm_free_large(pages);
|
||||
|
||||
if (!obj->dma_buf_vmapping)
|
||||
goto error;
|
||||
|
||||
obj->vmapping_count = 1;
|
||||
i915_gem_object_pin_pages(obj);
|
||||
out_unlock:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return obj->dma_buf_vmapping;
|
||||
|
||||
error:
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
|
||||
|
@ -119,10 +158,11 @@ static void i915_gem_dmabuf_vunmap(struct dma_buf *dma_buf, void *vaddr)
|
|||
if (ret)
|
||||
return;
|
||||
|
||||
--obj->vmapping_count;
|
||||
if (obj->vmapping_count == 0) {
|
||||
if (--obj->vmapping_count == 0) {
|
||||
vunmap(obj->dma_buf_vmapping);
|
||||
obj->dma_buf_vmapping = NULL;
|
||||
|
||||
i915_gem_object_unpin_pages(obj);
|
||||
}
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
}
|
||||
|
@ -151,6 +191,22 @@ static int i915_gem_dmabuf_mmap(struct dma_buf *dma_buf, struct vm_area_struct *
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int i915_gem_begin_cpu_access(struct dma_buf *dma_buf, size_t start, size_t length, enum dma_data_direction direction)
|
||||
{
|
||||
struct drm_i915_gem_object *obj = dma_buf->priv;
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
int ret;
|
||||
bool write = (direction == DMA_BIDIRECTIONAL || direction == DMA_TO_DEVICE);
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = i915_gem_object_set_to_cpu_domain(obj, write);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct dma_buf_ops i915_dmabuf_ops = {
|
||||
.map_dma_buf = i915_gem_map_dma_buf,
|
||||
.unmap_dma_buf = i915_gem_unmap_dma_buf,
|
||||
|
@ -162,25 +218,47 @@ static const struct dma_buf_ops i915_dmabuf_ops = {
|
|||
.mmap = i915_gem_dmabuf_mmap,
|
||||
.vmap = i915_gem_dmabuf_vmap,
|
||||
.vunmap = i915_gem_dmabuf_vunmap,
|
||||
.begin_cpu_access = i915_gem_begin_cpu_access,
|
||||
};
|
||||
|
||||
struct dma_buf *i915_gem_prime_export(struct drm_device *dev,
|
||||
struct drm_gem_object *gem_obj, int flags)
|
||||
struct drm_gem_object *gem_obj, int flags)
|
||||
{
|
||||
struct drm_i915_gem_object *obj = to_intel_bo(gem_obj);
|
||||
|
||||
return dma_buf_export(obj, &i915_dmabuf_ops,
|
||||
obj->base.size, 0600);
|
||||
return dma_buf_export(obj, &i915_dmabuf_ops, obj->base.size, 0600);
|
||||
}
|
||||
|
||||
static int i915_gem_object_get_pages_dmabuf(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct sg_table *sg;
|
||||
|
||||
sg = dma_buf_map_attachment(obj->base.import_attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(sg))
|
||||
return PTR_ERR(sg);
|
||||
|
||||
obj->pages = sg;
|
||||
obj->has_dma_mapping = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i915_gem_object_put_pages_dmabuf(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
dma_buf_unmap_attachment(obj->base.import_attach,
|
||||
obj->pages, DMA_BIDIRECTIONAL);
|
||||
obj->has_dma_mapping = false;
|
||||
}
|
||||
|
||||
static const struct drm_i915_gem_object_ops i915_gem_object_dmabuf_ops = {
|
||||
.get_pages = i915_gem_object_get_pages_dmabuf,
|
||||
.put_pages = i915_gem_object_put_pages_dmabuf,
|
||||
};
|
||||
|
||||
struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
|
||||
struct dma_buf *dma_buf)
|
||||
struct dma_buf *dma_buf)
|
||||
{
|
||||
struct dma_buf_attachment *attach;
|
||||
struct sg_table *sg;
|
||||
struct drm_i915_gem_object *obj;
|
||||
int npages;
|
||||
int size;
|
||||
int ret;
|
||||
|
||||
/* is this one of own objects? */
|
||||
|
@ -198,34 +276,24 @@ struct drm_gem_object *i915_gem_prime_import(struct drm_device *dev,
|
|||
if (IS_ERR(attach))
|
||||
return ERR_CAST(attach);
|
||||
|
||||
sg = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL);
|
||||
if (IS_ERR(sg)) {
|
||||
ret = PTR_ERR(sg);
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
size = dma_buf->size;
|
||||
npages = size / PAGE_SIZE;
|
||||
|
||||
obj = kzalloc(sizeof(*obj), GFP_KERNEL);
|
||||
if (obj == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto fail_unmap;
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
ret = drm_gem_private_object_init(dev, &obj->base, size);
|
||||
ret = drm_gem_private_object_init(dev, &obj->base, dma_buf->size);
|
||||
if (ret) {
|
||||
kfree(obj);
|
||||
goto fail_unmap;
|
||||
goto fail_detach;
|
||||
}
|
||||
|
||||
obj->sg_table = sg;
|
||||
i915_gem_object_init(obj, &i915_gem_object_dmabuf_ops);
|
||||
obj->base.import_attach = attach;
|
||||
|
||||
return &obj->base;
|
||||
|
||||
fail_unmap:
|
||||
dma_buf_unmap_attachment(attach, sg, DMA_BIDIRECTIONAL);
|
||||
fail_detach:
|
||||
dma_buf_detach(dma_buf, attach);
|
||||
return ERR_PTR(ret);
|
||||
|
|
|
@ -43,7 +43,8 @@ mark_free(struct drm_i915_gem_object *obj, struct list_head *unwind)
|
|||
|
||||
int
|
||||
i915_gem_evict_something(struct drm_device *dev, int min_size,
|
||||
unsigned alignment, bool mappable)
|
||||
unsigned alignment, unsigned cache_level,
|
||||
bool mappable, bool nonblocking)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct list_head eviction_list, unwind_list;
|
||||
|
@ -78,11 +79,11 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
|||
INIT_LIST_HEAD(&unwind_list);
|
||||
if (mappable)
|
||||
drm_mm_init_scan_with_range(&dev_priv->mm.gtt_space,
|
||||
min_size, alignment, 0,
|
||||
min_size, alignment, cache_level,
|
||||
0, dev_priv->mm.gtt_mappable_end);
|
||||
else
|
||||
drm_mm_init_scan(&dev_priv->mm.gtt_space,
|
||||
min_size, alignment, 0);
|
||||
min_size, alignment, cache_level);
|
||||
|
||||
/* First see if there is a large enough contiguous idle region... */
|
||||
list_for_each_entry(obj, &dev_priv->mm.inactive_list, mm_list) {
|
||||
|
@ -90,29 +91,16 @@ i915_gem_evict_something(struct drm_device *dev, int min_size,
|
|||
goto found;
|
||||
}
|
||||
|
||||
if (nonblocking)
|
||||
goto none;
|
||||
|
||||
/* Now merge in the soon-to-be-expired objects... */
|
||||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
|
||||
/* Does the object require an outstanding flush? */
|
||||
if (obj->base.write_domain)
|
||||
continue;
|
||||
|
||||
if (mark_free(obj, &unwind_list))
|
||||
goto found;
|
||||
}
|
||||
|
||||
/* Finally add anything with a pending flush (in order of retirement) */
|
||||
list_for_each_entry(obj, &dev_priv->mm.flushing_list, mm_list) {
|
||||
if (mark_free(obj, &unwind_list))
|
||||
goto found;
|
||||
}
|
||||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
|
||||
if (!obj->base.write_domain)
|
||||
continue;
|
||||
|
||||
if (mark_free(obj, &unwind_list))
|
||||
goto found;
|
||||
}
|
||||
|
||||
none:
|
||||
/* Nothing found, clean up and bail out! */
|
||||
while (!list_empty(&unwind_list)) {
|
||||
obj = list_first_entry(&unwind_list,
|
||||
|
@ -163,7 +151,7 @@ found:
|
|||
}
|
||||
|
||||
int
|
||||
i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
|
||||
i915_gem_evict_everything(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct drm_i915_gem_object *obj, *next;
|
||||
|
@ -171,12 +159,11 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
|
|||
int ret;
|
||||
|
||||
lists_empty = (list_empty(&dev_priv->mm.inactive_list) &&
|
||||
list_empty(&dev_priv->mm.flushing_list) &&
|
||||
list_empty(&dev_priv->mm.active_list));
|
||||
if (lists_empty)
|
||||
return -ENOSPC;
|
||||
|
||||
trace_i915_gem_evict_everything(dev, purgeable_only);
|
||||
trace_i915_gem_evict_everything(dev);
|
||||
|
||||
/* The gpu_idle will flush everything in the write domain to the
|
||||
* active list. Then we must move everything off the active list
|
||||
|
@ -188,16 +175,11 @@ i915_gem_evict_everything(struct drm_device *dev, bool purgeable_only)
|
|||
|
||||
i915_gem_retire_requests(dev);
|
||||
|
||||
BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
|
||||
|
||||
/* Having flushed everything, unbind() should never raise an error */
|
||||
list_for_each_entry_safe(obj, next,
|
||||
&dev_priv->mm.inactive_list, mm_list) {
|
||||
if (!purgeable_only || obj->madv != I915_MADV_WILLNEED) {
|
||||
if (obj->pin_count == 0)
|
||||
WARN_ON(i915_gem_object_unbind(obj));
|
||||
}
|
||||
}
|
||||
&dev_priv->mm.inactive_list, mm_list)
|
||||
if (obj->pin_count == 0)
|
||||
WARN_ON(i915_gem_object_unbind(obj));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -33,180 +33,6 @@
|
|||
#include "intel_drv.h"
|
||||
#include <linux/dma_remapping.h>
|
||||
|
||||
struct change_domains {
|
||||
uint32_t invalidate_domains;
|
||||
uint32_t flush_domains;
|
||||
uint32_t flush_rings;
|
||||
uint32_t flips;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set the next domain for the specified object. This
|
||||
* may not actually perform the necessary flushing/invaliding though,
|
||||
* as that may want to be batched with other set_domain operations
|
||||
*
|
||||
* This is (we hope) the only really tricky part of gem. The goal
|
||||
* is fairly simple -- track which caches hold bits of the object
|
||||
* and make sure they remain coherent. A few concrete examples may
|
||||
* help to explain how it works. For shorthand, we use the notation
|
||||
* (read_domains, write_domain), e.g. (CPU, CPU) to indicate the
|
||||
* a pair of read and write domain masks.
|
||||
*
|
||||
* Case 1: the batch buffer
|
||||
*
|
||||
* 1. Allocated
|
||||
* 2. Written by CPU
|
||||
* 3. Mapped to GTT
|
||||
* 4. Read by GPU
|
||||
* 5. Unmapped from GTT
|
||||
* 6. Freed
|
||||
*
|
||||
* Let's take these a step at a time
|
||||
*
|
||||
* 1. Allocated
|
||||
* Pages allocated from the kernel may still have
|
||||
* cache contents, so we set them to (CPU, CPU) always.
|
||||
* 2. Written by CPU (using pwrite)
|
||||
* The pwrite function calls set_domain (CPU, CPU) and
|
||||
* this function does nothing (as nothing changes)
|
||||
* 3. Mapped by GTT
|
||||
* This function asserts that the object is not
|
||||
* currently in any GPU-based read or write domains
|
||||
* 4. Read by GPU
|
||||
* i915_gem_execbuffer calls set_domain (COMMAND, 0).
|
||||
* As write_domain is zero, this function adds in the
|
||||
* current read domains (CPU+COMMAND, 0).
|
||||
* flush_domains is set to CPU.
|
||||
* invalidate_domains is set to COMMAND
|
||||
* clflush is run to get data out of the CPU caches
|
||||
* then i915_dev_set_domain calls i915_gem_flush to
|
||||
* emit an MI_FLUSH and drm_agp_chipset_flush
|
||||
* 5. Unmapped from GTT
|
||||
* i915_gem_object_unbind calls set_domain (CPU, CPU)
|
||||
* flush_domains and invalidate_domains end up both zero
|
||||
* so no flushing/invalidating happens
|
||||
* 6. Freed
|
||||
* yay, done
|
||||
*
|
||||
* Case 2: The shared render buffer
|
||||
*
|
||||
* 1. Allocated
|
||||
* 2. Mapped to GTT
|
||||
* 3. Read/written by GPU
|
||||
* 4. set_domain to (CPU,CPU)
|
||||
* 5. Read/written by CPU
|
||||
* 6. Read/written by GPU
|
||||
*
|
||||
* 1. Allocated
|
||||
* Same as last example, (CPU, CPU)
|
||||
* 2. Mapped to GTT
|
||||
* Nothing changes (assertions find that it is not in the GPU)
|
||||
* 3. Read/written by GPU
|
||||
* execbuffer calls set_domain (RENDER, RENDER)
|
||||
* flush_domains gets CPU
|
||||
* invalidate_domains gets GPU
|
||||
* clflush (obj)
|
||||
* MI_FLUSH and drm_agp_chipset_flush
|
||||
* 4. set_domain (CPU, CPU)
|
||||
* flush_domains gets GPU
|
||||
* invalidate_domains gets CPU
|
||||
* wait_rendering (obj) to make sure all drawing is complete.
|
||||
* This will include an MI_FLUSH to get the data from GPU
|
||||
* to memory
|
||||
* clflush (obj) to invalidate the CPU cache
|
||||
* Another MI_FLUSH in i915_gem_flush (eliminate this somehow?)
|
||||
* 5. Read/written by CPU
|
||||
* cache lines are loaded and dirtied
|
||||
* 6. Read written by GPU
|
||||
* Same as last GPU access
|
||||
*
|
||||
* Case 3: The constant buffer
|
||||
*
|
||||
* 1. Allocated
|
||||
* 2. Written by CPU
|
||||
* 3. Read by GPU
|
||||
* 4. Updated (written) by CPU again
|
||||
* 5. Read by GPU
|
||||
*
|
||||
* 1. Allocated
|
||||
* (CPU, CPU)
|
||||
* 2. Written by CPU
|
||||
* (CPU, CPU)
|
||||
* 3. Read by GPU
|
||||
* (CPU+RENDER, 0)
|
||||
* flush_domains = CPU
|
||||
* invalidate_domains = RENDER
|
||||
* clflush (obj)
|
||||
* MI_FLUSH
|
||||
* drm_agp_chipset_flush
|
||||
* 4. Updated (written) by CPU again
|
||||
* (CPU, CPU)
|
||||
* flush_domains = 0 (no previous write domain)
|
||||
* invalidate_domains = 0 (no new read domains)
|
||||
* 5. Read by GPU
|
||||
* (CPU+RENDER, 0)
|
||||
* flush_domains = CPU
|
||||
* invalidate_domains = RENDER
|
||||
* clflush (obj)
|
||||
* MI_FLUSH
|
||||
* drm_agp_chipset_flush
|
||||
*/
|
||||
static void
|
||||
i915_gem_object_set_to_gpu_domain(struct drm_i915_gem_object *obj,
|
||||
struct intel_ring_buffer *ring,
|
||||
struct change_domains *cd)
|
||||
{
|
||||
uint32_t invalidate_domains = 0, flush_domains = 0;
|
||||
|
||||
/*
|
||||
* If the object isn't moving to a new write domain,
|
||||
* let the object stay in multiple read domains
|
||||
*/
|
||||
if (obj->base.pending_write_domain == 0)
|
||||
obj->base.pending_read_domains |= obj->base.read_domains;
|
||||
|
||||
/*
|
||||
* Flush the current write domain if
|
||||
* the new read domains don't match. Invalidate
|
||||
* any read domains which differ from the old
|
||||
* write domain
|
||||
*/
|
||||
if (obj->base.write_domain &&
|
||||
(((obj->base.write_domain != obj->base.pending_read_domains ||
|
||||
obj->ring != ring)) ||
|
||||
(obj->fenced_gpu_access && !obj->pending_fenced_gpu_access))) {
|
||||
flush_domains |= obj->base.write_domain;
|
||||
invalidate_domains |=
|
||||
obj->base.pending_read_domains & ~obj->base.write_domain;
|
||||
}
|
||||
/*
|
||||
* Invalidate any read caches which may have
|
||||
* stale data. That is, any new read domains.
|
||||
*/
|
||||
invalidate_domains |= obj->base.pending_read_domains & ~obj->base.read_domains;
|
||||
if ((flush_domains | invalidate_domains) & I915_GEM_DOMAIN_CPU)
|
||||
i915_gem_clflush_object(obj);
|
||||
|
||||
if (obj->base.pending_write_domain)
|
||||
cd->flips |= atomic_read(&obj->pending_flip);
|
||||
|
||||
/* The actual obj->write_domain will be updated with
|
||||
* pending_write_domain after we emit the accumulated flush for all
|
||||
* of our domain changes in execbuffers (which clears objects'
|
||||
* write_domains). So if we have a current write domain that we
|
||||
* aren't changing, set pending_write_domain to that.
|
||||
*/
|
||||
if (flush_domains == 0 && obj->base.pending_write_domain == 0)
|
||||
obj->base.pending_write_domain = obj->base.write_domain;
|
||||
|
||||
cd->invalidate_domains |= invalidate_domains;
|
||||
cd->flush_domains |= flush_domains;
|
||||
if (flush_domains & I915_GEM_GPU_DOMAINS)
|
||||
cd->flush_rings |= intel_ring_flag(obj->ring);
|
||||
if (invalidate_domains & I915_GEM_GPU_DOMAINS)
|
||||
cd->flush_rings |= intel_ring_flag(ring);
|
||||
}
|
||||
|
||||
struct eb_objects {
|
||||
int and;
|
||||
struct hlist_head buckets[0];
|
||||
|
@ -217,6 +43,7 @@ eb_create(int size)
|
|||
{
|
||||
struct eb_objects *eb;
|
||||
int count = PAGE_SIZE / sizeof(struct hlist_head) / 2;
|
||||
BUILD_BUG_ON(!is_power_of_2(PAGE_SIZE / sizeof(struct hlist_head)));
|
||||
while (count > size)
|
||||
count >>= 1;
|
||||
eb = kzalloc(count*sizeof(struct hlist_head) +
|
||||
|
@ -268,6 +95,7 @@ eb_destroy(struct eb_objects *eb)
|
|||
static inline int use_cpu_reloc(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
return (obj->base.write_domain == I915_GEM_DOMAIN_CPU ||
|
||||
!obj->map_and_fenceable ||
|
||||
obj->cache_level != I915_CACHE_NONE);
|
||||
}
|
||||
|
||||
|
@ -382,7 +210,8 @@ i915_gem_execbuffer_relocate_entry(struct drm_i915_gem_object *obj,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
vaddr = kmap_atomic(obj->pages[reloc->offset >> PAGE_SHIFT]);
|
||||
vaddr = kmap_atomic(i915_gem_object_get_page(obj,
|
||||
reloc->offset >> PAGE_SHIFT));
|
||||
*(uint32_t *)(vaddr + page_offset) = reloc->delta;
|
||||
kunmap_atomic(vaddr);
|
||||
} else {
|
||||
|
@ -503,7 +332,8 @@ i915_gem_execbuffer_relocate(struct drm_device *dev,
|
|||
return ret;
|
||||
}
|
||||
|
||||
#define __EXEC_OBJECT_HAS_FENCE (1<<31)
|
||||
#define __EXEC_OBJECT_HAS_PIN (1<<31)
|
||||
#define __EXEC_OBJECT_HAS_FENCE (1<<30)
|
||||
|
||||
static int
|
||||
need_reloc_mappable(struct drm_i915_gem_object *obj)
|
||||
|
@ -513,9 +343,10 @@ need_reloc_mappable(struct drm_i915_gem_object *obj)
|
|||
}
|
||||
|
||||
static int
|
||||
pin_and_fence_object(struct drm_i915_gem_object *obj,
|
||||
struct intel_ring_buffer *ring)
|
||||
i915_gem_execbuffer_reserve_object(struct drm_i915_gem_object *obj,
|
||||
struct intel_ring_buffer *ring)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = obj->base.dev->dev_private;
|
||||
struct drm_i915_gem_exec_object2 *entry = obj->exec_entry;
|
||||
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
|
||||
bool need_fence, need_mappable;
|
||||
|
@ -527,15 +358,17 @@ pin_and_fence_object(struct drm_i915_gem_object *obj,
|
|||
obj->tiling_mode != I915_TILING_NONE;
|
||||
need_mappable = need_fence || need_reloc_mappable(obj);
|
||||
|
||||
ret = i915_gem_object_pin(obj, entry->alignment, need_mappable);
|
||||
ret = i915_gem_object_pin(obj, entry->alignment, need_mappable, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
entry->flags |= __EXEC_OBJECT_HAS_PIN;
|
||||
|
||||
if (has_fenced_gpu_access) {
|
||||
if (entry->flags & EXEC_OBJECT_NEEDS_FENCE) {
|
||||
ret = i915_gem_object_get_fence(obj);
|
||||
if (ret)
|
||||
goto err_unpin;
|
||||
return ret;
|
||||
|
||||
if (i915_gem_object_pin_fence(obj))
|
||||
entry->flags |= __EXEC_OBJECT_HAS_FENCE;
|
||||
|
@ -544,12 +377,35 @@ pin_and_fence_object(struct drm_i915_gem_object *obj,
|
|||
}
|
||||
}
|
||||
|
||||
/* Ensure ppgtt mapping exists if needed */
|
||||
if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) {
|
||||
i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
|
||||
obj, obj->cache_level);
|
||||
|
||||
obj->has_aliasing_ppgtt_mapping = 1;
|
||||
}
|
||||
|
||||
entry->offset = obj->gtt_offset;
|
||||
return 0;
|
||||
}
|
||||
|
||||
err_unpin:
|
||||
i915_gem_object_unpin(obj);
|
||||
return ret;
|
||||
static void
|
||||
i915_gem_execbuffer_unreserve_object(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct drm_i915_gem_exec_object2 *entry;
|
||||
|
||||
if (!obj->gtt_space)
|
||||
return;
|
||||
|
||||
entry = obj->exec_entry;
|
||||
|
||||
if (entry->flags & __EXEC_OBJECT_HAS_FENCE)
|
||||
i915_gem_object_unpin_fence(obj);
|
||||
|
||||
if (entry->flags & __EXEC_OBJECT_HAS_PIN)
|
||||
i915_gem_object_unpin(obj);
|
||||
|
||||
entry->flags &= ~(__EXEC_OBJECT_HAS_FENCE | __EXEC_OBJECT_HAS_PIN);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -557,11 +413,10 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
|
|||
struct drm_file *file,
|
||||
struct list_head *objects)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = ring->dev->dev_private;
|
||||
struct drm_i915_gem_object *obj;
|
||||
int ret, retry;
|
||||
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
|
||||
struct list_head ordered_objects;
|
||||
bool has_fenced_gpu_access = INTEL_INFO(ring->dev)->gen < 4;
|
||||
int retry;
|
||||
|
||||
INIT_LIST_HEAD(&ordered_objects);
|
||||
while (!list_empty(objects)) {
|
||||
|
@ -586,6 +441,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
|
|||
|
||||
obj->base.pending_read_domains = 0;
|
||||
obj->base.pending_write_domain = 0;
|
||||
obj->pending_fenced_gpu_access = false;
|
||||
}
|
||||
list_splice(&ordered_objects, objects);
|
||||
|
||||
|
@ -598,12 +454,12 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
|
|||
* 2. Bind new objects.
|
||||
* 3. Decrement pin count.
|
||||
*
|
||||
* This avoid unnecessary unbinding of later objects in order to makr
|
||||
* This avoid unnecessary unbinding of later objects in order to make
|
||||
* room for the earlier objects *unless* we need to defragment.
|
||||
*/
|
||||
retry = 0;
|
||||
do {
|
||||
ret = 0;
|
||||
int ret = 0;
|
||||
|
||||
/* Unbind any ill-fitting objects or pin. */
|
||||
list_for_each_entry(obj, objects, exec_list) {
|
||||
|
@ -623,7 +479,7 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
|
|||
(need_mappable && !obj->map_and_fenceable))
|
||||
ret = i915_gem_object_unbind(obj);
|
||||
else
|
||||
ret = pin_and_fence_object(obj, ring);
|
||||
ret = i915_gem_execbuffer_reserve_object(obj, ring);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
@ -633,77 +489,22 @@ i915_gem_execbuffer_reserve(struct intel_ring_buffer *ring,
|
|||
if (obj->gtt_space)
|
||||
continue;
|
||||
|
||||
ret = pin_and_fence_object(obj, ring);
|
||||
if (ret) {
|
||||
int ret_ignore;
|
||||
|
||||
/* This can potentially raise a harmless
|
||||
* -EINVAL if we failed to bind in the above
|
||||
* call. It cannot raise -EINTR since we know
|
||||
* that the bo is freshly bound and so will
|
||||
* not need to be flushed or waited upon.
|
||||
*/
|
||||
ret_ignore = i915_gem_object_unbind(obj);
|
||||
(void)ret_ignore;
|
||||
WARN_ON(obj->gtt_space);
|
||||
break;
|
||||
}
|
||||
ret = i915_gem_execbuffer_reserve_object(obj, ring);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Decrement pin count for bound objects */
|
||||
list_for_each_entry(obj, objects, exec_list) {
|
||||
struct drm_i915_gem_exec_object2 *entry;
|
||||
err: /* Decrement pin count for bound objects */
|
||||
list_for_each_entry(obj, objects, exec_list)
|
||||
i915_gem_execbuffer_unreserve_object(obj);
|
||||
|
||||
if (!obj->gtt_space)
|
||||
continue;
|
||||
|
||||
entry = obj->exec_entry;
|
||||
if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
|
||||
i915_gem_object_unpin_fence(obj);
|
||||
entry->flags &= ~__EXEC_OBJECT_HAS_FENCE;
|
||||
}
|
||||
|
||||
i915_gem_object_unpin(obj);
|
||||
|
||||
/* ... and ensure ppgtt mapping exist if needed. */
|
||||
if (dev_priv->mm.aliasing_ppgtt && !obj->has_aliasing_ppgtt_mapping) {
|
||||
i915_ppgtt_bind_object(dev_priv->mm.aliasing_ppgtt,
|
||||
obj, obj->cache_level);
|
||||
|
||||
obj->has_aliasing_ppgtt_mapping = 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != -ENOSPC || retry > 1)
|
||||
if (ret != -ENOSPC || retry++)
|
||||
return ret;
|
||||
|
||||
/* First attempt, just clear anything that is purgeable.
|
||||
* Second attempt, clear the entire GTT.
|
||||
*/
|
||||
ret = i915_gem_evict_everything(ring->dev, retry == 0);
|
||||
ret = i915_gem_evict_everything(ring->dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
retry++;
|
||||
} while (1);
|
||||
|
||||
err:
|
||||
list_for_each_entry_continue_reverse(obj, objects, exec_list) {
|
||||
struct drm_i915_gem_exec_object2 *entry;
|
||||
|
||||
if (!obj->gtt_space)
|
||||
continue;
|
||||
|
||||
entry = obj->exec_entry;
|
||||
if (entry->flags & __EXEC_OBJECT_HAS_FENCE) {
|
||||
i915_gem_object_unpin_fence(obj);
|
||||
entry->flags &= ~__EXEC_OBJECT_HAS_FENCE;
|
||||
}
|
||||
|
||||
i915_gem_object_unpin(obj);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -809,18 +610,6 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
i915_gem_execbuffer_flush(struct drm_device *dev,
|
||||
uint32_t invalidate_domains,
|
||||
uint32_t flush_domains)
|
||||
{
|
||||
if (flush_domains & I915_GEM_DOMAIN_CPU)
|
||||
intel_gtt_chipset_flush();
|
||||
|
||||
if (flush_domains & I915_GEM_DOMAIN_GTT)
|
||||
wmb();
|
||||
}
|
||||
|
||||
static int
|
||||
i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips)
|
||||
{
|
||||
|
@ -853,48 +642,45 @@ i915_gem_execbuffer_wait_for_flips(struct intel_ring_buffer *ring, u32 flips)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
i915_gem_execbuffer_move_to_gpu(struct intel_ring_buffer *ring,
|
||||
struct list_head *objects)
|
||||
{
|
||||
struct drm_i915_gem_object *obj;
|
||||
struct change_domains cd;
|
||||
uint32_t flush_domains = 0;
|
||||
uint32_t flips = 0;
|
||||
int ret;
|
||||
|
||||
memset(&cd, 0, sizeof(cd));
|
||||
list_for_each_entry(obj, objects, exec_list)
|
||||
i915_gem_object_set_to_gpu_domain(obj, ring, &cd);
|
||||
|
||||
if (cd.invalidate_domains | cd.flush_domains) {
|
||||
i915_gem_execbuffer_flush(ring->dev,
|
||||
cd.invalidate_domains,
|
||||
cd.flush_domains);
|
||||
}
|
||||
|
||||
if (cd.flips) {
|
||||
ret = i915_gem_execbuffer_wait_for_flips(ring, cd.flips);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
list_for_each_entry(obj, objects, exec_list) {
|
||||
ret = i915_gem_object_sync(obj, ring);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (obj->base.write_domain & I915_GEM_DOMAIN_CPU)
|
||||
i915_gem_clflush_object(obj);
|
||||
|
||||
if (obj->base.pending_write_domain)
|
||||
flips |= atomic_read(&obj->pending_flip);
|
||||
|
||||
flush_domains |= obj->base.write_domain;
|
||||
}
|
||||
|
||||
if (flips) {
|
||||
ret = i915_gem_execbuffer_wait_for_flips(ring, flips);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (flush_domains & I915_GEM_DOMAIN_CPU)
|
||||
intel_gtt_chipset_flush();
|
||||
|
||||
if (flush_domains & I915_GEM_DOMAIN_GTT)
|
||||
wmb();
|
||||
|
||||
/* Unconditionally invalidate gpu caches and ensure that we do flush
|
||||
* any residual writes from the previous batch.
|
||||
*/
|
||||
ret = i915_gem_flush_ring(ring,
|
||||
I915_GEM_GPU_DOMAINS,
|
||||
ring->gpu_caches_dirty ? I915_GEM_GPU_DOMAINS : 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ring->gpu_caches_dirty = false;
|
||||
return 0;
|
||||
return intel_ring_invalidate_all_caches(ring);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -942,9 +728,8 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
|
|||
struct drm_i915_gem_object *obj;
|
||||
|
||||
list_for_each_entry(obj, objects, exec_list) {
|
||||
u32 old_read = obj->base.read_domains;
|
||||
u32 old_write = obj->base.write_domain;
|
||||
|
||||
u32 old_read = obj->base.read_domains;
|
||||
u32 old_write = obj->base.write_domain;
|
||||
|
||||
obj->base.read_domains = obj->base.pending_read_domains;
|
||||
obj->base.write_domain = obj->base.pending_write_domain;
|
||||
|
@ -953,17 +738,13 @@ i915_gem_execbuffer_move_to_active(struct list_head *objects,
|
|||
i915_gem_object_move_to_active(obj, ring, seqno);
|
||||
if (obj->base.write_domain) {
|
||||
obj->dirty = 1;
|
||||
obj->pending_gpu_write = true;
|
||||
list_move_tail(&obj->gpu_write_list,
|
||||
&ring->gpu_write_list);
|
||||
obj->last_write_seqno = seqno;
|
||||
if (obj->pin_count) /* check for potential scanout */
|
||||
intel_mark_busy(ring->dev, obj);
|
||||
intel_mark_fb_busy(obj);
|
||||
}
|
||||
|
||||
trace_i915_gem_object_change_domain(obj, old_read, old_write);
|
||||
}
|
||||
|
||||
intel_mark_busy(ring->dev, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -971,16 +752,11 @@ i915_gem_execbuffer_retire_commands(struct drm_device *dev,
|
|||
struct drm_file *file,
|
||||
struct intel_ring_buffer *ring)
|
||||
{
|
||||
struct drm_i915_gem_request *request;
|
||||
|
||||
/* Unconditionally force add_request to emit a full flush. */
|
||||
ring->gpu_caches_dirty = true;
|
||||
|
||||
/* Add a breadcrumb for the completion of the batch buffer */
|
||||
request = kzalloc(sizeof(*request), GFP_KERNEL);
|
||||
if (request == NULL || i915_add_request(ring, file, request)) {
|
||||
kfree(request);
|
||||
}
|
||||
(void)i915_add_request(ring, file, NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1326,8 +1102,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|||
return -ENOMEM;
|
||||
}
|
||||
ret = copy_from_user(exec_list,
|
||||
(struct drm_i915_relocation_entry __user *)
|
||||
(uintptr_t) args->buffers_ptr,
|
||||
(void __user *)(uintptr_t)args->buffers_ptr,
|
||||
sizeof(*exec_list) * args->buffer_count);
|
||||
if (ret != 0) {
|
||||
DRM_DEBUG("copy %d exec entries failed %d\n",
|
||||
|
@ -1366,8 +1141,7 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
|
|||
for (i = 0; i < args->buffer_count; i++)
|
||||
exec_list[i].offset = exec2_list[i].offset;
|
||||
/* ... and back out to userspace */
|
||||
ret = copy_to_user((struct drm_i915_relocation_entry __user *)
|
||||
(uintptr_t) args->buffers_ptr,
|
||||
ret = copy_to_user((void __user *)(uintptr_t)args->buffers_ptr,
|
||||
exec_list,
|
||||
sizeof(*exec_list) * args->buffer_count);
|
||||
if (ret) {
|
||||
|
@ -1421,8 +1195,7 @@ i915_gem_execbuffer2(struct drm_device *dev, void *data,
|
|||
ret = i915_gem_do_execbuffer(dev, data, file, args, exec2_list);
|
||||
if (!ret) {
|
||||
/* Copy the new buffer offsets back to the user's exec list. */
|
||||
ret = copy_to_user((struct drm_i915_relocation_entry __user *)
|
||||
(uintptr_t) args->buffers_ptr,
|
||||
ret = copy_to_user((void __user *)(uintptr_t)args->buffers_ptr,
|
||||
exec2_list,
|
||||
sizeof(*exec2_list) * args->buffer_count);
|
||||
if (ret) {
|
||||
|
|
|
@ -166,8 +166,7 @@ void i915_gem_cleanup_aliasing_ppgtt(struct drm_device *dev)
|
|||
}
|
||||
|
||||
static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
|
||||
struct scatterlist *sg_list,
|
||||
unsigned sg_len,
|
||||
const struct sg_table *pages,
|
||||
unsigned first_entry,
|
||||
uint32_t pte_flags)
|
||||
{
|
||||
|
@ -179,12 +178,12 @@ static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
|
|||
struct scatterlist *sg;
|
||||
|
||||
/* init sg walking */
|
||||
sg = sg_list;
|
||||
sg = pages->sgl;
|
||||
i = 0;
|
||||
segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
|
||||
m = 0;
|
||||
|
||||
while (i < sg_len) {
|
||||
while (i < pages->nents) {
|
||||
pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
|
||||
|
||||
for (j = first_pte; j < I915_PPGTT_PT_ENTRIES; j++) {
|
||||
|
@ -193,13 +192,11 @@ static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
|
|||
pt_vaddr[j] = pte | pte_flags;
|
||||
|
||||
/* grab the next page */
|
||||
m++;
|
||||
if (m == segment_len) {
|
||||
sg = sg_next(sg);
|
||||
i++;
|
||||
if (i == sg_len)
|
||||
if (++m == segment_len) {
|
||||
if (++i == pages->nents)
|
||||
break;
|
||||
|
||||
sg = sg_next(sg);
|
||||
segment_len = sg_dma_len(sg) >> PAGE_SHIFT;
|
||||
m = 0;
|
||||
}
|
||||
|
@ -212,44 +209,10 @@ static void i915_ppgtt_insert_sg_entries(struct i915_hw_ppgtt *ppgtt,
|
|||
}
|
||||
}
|
||||
|
||||
static void i915_ppgtt_insert_pages(struct i915_hw_ppgtt *ppgtt,
|
||||
unsigned first_entry, unsigned num_entries,
|
||||
struct page **pages, uint32_t pte_flags)
|
||||
{
|
||||
uint32_t *pt_vaddr, pte;
|
||||
unsigned act_pd = first_entry / I915_PPGTT_PT_ENTRIES;
|
||||
unsigned first_pte = first_entry % I915_PPGTT_PT_ENTRIES;
|
||||
unsigned last_pte, i;
|
||||
dma_addr_t page_addr;
|
||||
|
||||
while (num_entries) {
|
||||
last_pte = first_pte + num_entries;
|
||||
last_pte = min_t(unsigned, last_pte, I915_PPGTT_PT_ENTRIES);
|
||||
|
||||
pt_vaddr = kmap_atomic(ppgtt->pt_pages[act_pd]);
|
||||
|
||||
for (i = first_pte; i < last_pte; i++) {
|
||||
page_addr = page_to_phys(*pages);
|
||||
pte = GEN6_PTE_ADDR_ENCODE(page_addr);
|
||||
pt_vaddr[i] = pte | pte_flags;
|
||||
|
||||
pages++;
|
||||
}
|
||||
|
||||
kunmap_atomic(pt_vaddr);
|
||||
|
||||
num_entries -= last_pte - first_pte;
|
||||
first_pte = 0;
|
||||
act_pd++;
|
||||
}
|
||||
}
|
||||
|
||||
void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
|
||||
struct drm_i915_gem_object *obj,
|
||||
enum i915_cache_level cache_level)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t pte_flags = GEN6_PTE_VALID;
|
||||
|
||||
switch (cache_level) {
|
||||
|
@ -260,7 +223,7 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
|
|||
pte_flags |= GEN6_PTE_CACHE_LLC;
|
||||
break;
|
||||
case I915_CACHE_NONE:
|
||||
if (IS_HASWELL(dev))
|
||||
if (IS_HASWELL(obj->base.dev))
|
||||
pte_flags |= HSW_PTE_UNCACHED;
|
||||
else
|
||||
pte_flags |= GEN6_PTE_UNCACHED;
|
||||
|
@ -269,26 +232,10 @@ void i915_ppgtt_bind_object(struct i915_hw_ppgtt *ppgtt,
|
|||
BUG();
|
||||
}
|
||||
|
||||
if (obj->sg_table) {
|
||||
i915_ppgtt_insert_sg_entries(ppgtt,
|
||||
obj->sg_table->sgl,
|
||||
obj->sg_table->nents,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
pte_flags);
|
||||
} else if (dev_priv->mm.gtt->needs_dmar) {
|
||||
BUG_ON(!obj->sg_list);
|
||||
|
||||
i915_ppgtt_insert_sg_entries(ppgtt,
|
||||
obj->sg_list,
|
||||
obj->num_sg,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
pte_flags);
|
||||
} else
|
||||
i915_ppgtt_insert_pages(ppgtt,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
obj->base.size >> PAGE_SHIFT,
|
||||
obj->pages,
|
||||
pte_flags);
|
||||
i915_ppgtt_insert_sg_entries(ppgtt,
|
||||
obj->pages,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
pte_flags);
|
||||
}
|
||||
|
||||
void i915_ppgtt_unbind_object(struct i915_hw_ppgtt *ppgtt,
|
||||
|
@ -350,7 +297,7 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
|
|||
intel_gtt_clear_range(dev_priv->mm.gtt_start / PAGE_SIZE,
|
||||
(dev_priv->mm.gtt_end - dev_priv->mm.gtt_start) / PAGE_SIZE);
|
||||
|
||||
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list) {
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list) {
|
||||
i915_gem_clflush_object(obj);
|
||||
i915_gem_gtt_bind_object(obj, obj->cache_level);
|
||||
}
|
||||
|
@ -360,44 +307,26 @@ void i915_gem_restore_gtt_mappings(struct drm_device *dev)
|
|||
|
||||
int i915_gem_gtt_prepare_object(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
/* don't map imported dma buf objects */
|
||||
if (dev_priv->mm.gtt->needs_dmar && !obj->sg_table)
|
||||
return intel_gtt_map_memory(obj->pages,
|
||||
obj->base.size >> PAGE_SHIFT,
|
||||
&obj->sg_list,
|
||||
&obj->num_sg);
|
||||
else
|
||||
if (obj->has_dma_mapping)
|
||||
return 0;
|
||||
|
||||
if (!dma_map_sg(&obj->base.dev->pdev->dev,
|
||||
obj->pages->sgl, obj->pages->nents,
|
||||
PCI_DMA_BIDIRECTIONAL))
|
||||
return -ENOSPC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i915_gem_gtt_bind_object(struct drm_i915_gem_object *obj,
|
||||
enum i915_cache_level cache_level)
|
||||
{
|
||||
struct drm_device *dev = obj->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
unsigned int agp_type = cache_level_to_agp_type(dev, cache_level);
|
||||
|
||||
if (obj->sg_table) {
|
||||
intel_gtt_insert_sg_entries(obj->sg_table->sgl,
|
||||
obj->sg_table->nents,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
agp_type);
|
||||
} else if (dev_priv->mm.gtt->needs_dmar) {
|
||||
BUG_ON(!obj->sg_list);
|
||||
|
||||
intel_gtt_insert_sg_entries(obj->sg_list,
|
||||
obj->num_sg,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
agp_type);
|
||||
} else
|
||||
intel_gtt_insert_pages(obj->gtt_space->start >> PAGE_SHIFT,
|
||||
obj->base.size >> PAGE_SHIFT,
|
||||
obj->pages,
|
||||
agp_type);
|
||||
|
||||
intel_gtt_insert_sg_entries(obj->pages,
|
||||
obj->gtt_space->start >> PAGE_SHIFT,
|
||||
agp_type);
|
||||
obj->has_global_gtt_mapping = 1;
|
||||
}
|
||||
|
||||
|
@ -417,14 +346,31 @@ void i915_gem_gtt_finish_object(struct drm_i915_gem_object *obj)
|
|||
|
||||
interruptible = do_idling(dev_priv);
|
||||
|
||||
if (obj->sg_list) {
|
||||
intel_gtt_unmap_memory(obj->sg_list, obj->num_sg);
|
||||
obj->sg_list = NULL;
|
||||
}
|
||||
if (!obj->has_dma_mapping)
|
||||
dma_unmap_sg(&dev->pdev->dev,
|
||||
obj->pages->sgl, obj->pages->nents,
|
||||
PCI_DMA_BIDIRECTIONAL);
|
||||
|
||||
undo_idling(dev_priv, interruptible);
|
||||
}
|
||||
|
||||
static void i915_gtt_color_adjust(struct drm_mm_node *node,
|
||||
unsigned long color,
|
||||
unsigned long *start,
|
||||
unsigned long *end)
|
||||
{
|
||||
if (node->color != color)
|
||||
*start += 4096;
|
||||
|
||||
if (!list_empty(&node->node_list)) {
|
||||
node = list_entry(node->node_list.next,
|
||||
struct drm_mm_node,
|
||||
node_list);
|
||||
if (node->allocated && node->color != color)
|
||||
*end -= 4096;
|
||||
}
|
||||
}
|
||||
|
||||
void i915_gem_init_global_gtt(struct drm_device *dev,
|
||||
unsigned long start,
|
||||
unsigned long mappable_end,
|
||||
|
@ -434,6 +380,8 @@ void i915_gem_init_global_gtt(struct drm_device *dev,
|
|||
|
||||
/* Substract the guard page ... */
|
||||
drm_mm_init(&dev_priv->mm.gtt_space, start, end - start - PAGE_SIZE);
|
||||
if (!HAS_LLC(dev))
|
||||
dev_priv->mm.gtt_space.color_adjust = i915_gtt_color_adjust;
|
||||
|
||||
dev_priv->mm.gtt_start = start;
|
||||
dev_priv->mm.gtt_mappable_end = mappable_end;
|
||||
|
|
|
@ -469,18 +469,20 @@ i915_gem_swizzle_page(struct page *page)
|
|||
void
|
||||
i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int page_count = obj->base.size >> PAGE_SHIFT;
|
||||
int i;
|
||||
|
||||
if (obj->bit_17 == NULL)
|
||||
return;
|
||||
|
||||
for (i = 0; i < page_count; i++) {
|
||||
char new_bit_17 = page_to_phys(obj->pages[i]) >> 17;
|
||||
for_each_sg(obj->pages->sgl, sg, page_count, i) {
|
||||
struct page *page = sg_page(sg);
|
||||
char new_bit_17 = page_to_phys(page) >> 17;
|
||||
if ((new_bit_17 & 0x1) !=
|
||||
(test_bit(i, obj->bit_17) != 0)) {
|
||||
i915_gem_swizzle_page(obj->pages[i]);
|
||||
set_page_dirty(obj->pages[i]);
|
||||
i915_gem_swizzle_page(page);
|
||||
set_page_dirty(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -488,6 +490,7 @@ i915_gem_object_do_bit_17_swizzle(struct drm_i915_gem_object *obj)
|
|||
void
|
||||
i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
int page_count = obj->base.size >> PAGE_SHIFT;
|
||||
int i;
|
||||
|
||||
|
@ -501,8 +504,9 @@ i915_gem_object_save_bit_17_swizzle(struct drm_i915_gem_object *obj)
|
|||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < page_count; i++) {
|
||||
if (page_to_phys(obj->pages[i]) & (1 << 17))
|
||||
for_each_sg(obj->pages->sgl, sg, page_count, i) {
|
||||
struct page *page = sg_page(sg);
|
||||
if (page_to_phys(page) & (1 << 17))
|
||||
__set_bit(i, obj->bit_17);
|
||||
else
|
||||
__clear_bit(i, obj->bit_17);
|
||||
|
|
|
@ -295,11 +295,21 @@ static void i915_hotplug_work_func(struct work_struct *work)
|
|||
drm_helper_hpd_irq_event(dev);
|
||||
}
|
||||
|
||||
static void i915_handle_rps_change(struct drm_device *dev)
|
||||
/* defined intel_pm.c */
|
||||
extern spinlock_t mchdev_lock;
|
||||
|
||||
static void ironlake_handle_rps_change(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
u32 busy_up, busy_down, max_avg, min_avg;
|
||||
u8 new_delay = dev_priv->cur_delay;
|
||||
u8 new_delay;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&mchdev_lock, flags);
|
||||
|
||||
I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
|
||||
|
||||
new_delay = dev_priv->ips.cur_delay;
|
||||
|
||||
I915_WRITE16(MEMINTRSTS, MEMINT_EVAL_CHG);
|
||||
busy_up = I915_READ(RCPREVBSYTUPAVG);
|
||||
|
@ -309,19 +319,21 @@ static void i915_handle_rps_change(struct drm_device *dev)
|
|||
|
||||
/* Handle RCS change request from hw */
|
||||
if (busy_up > max_avg) {
|
||||
if (dev_priv->cur_delay != dev_priv->max_delay)
|
||||
new_delay = dev_priv->cur_delay - 1;
|
||||
if (new_delay < dev_priv->max_delay)
|
||||
new_delay = dev_priv->max_delay;
|
||||
if (dev_priv->ips.cur_delay != dev_priv->ips.max_delay)
|
||||
new_delay = dev_priv->ips.cur_delay - 1;
|
||||
if (new_delay < dev_priv->ips.max_delay)
|
||||
new_delay = dev_priv->ips.max_delay;
|
||||
} else if (busy_down < min_avg) {
|
||||
if (dev_priv->cur_delay != dev_priv->min_delay)
|
||||
new_delay = dev_priv->cur_delay + 1;
|
||||
if (new_delay > dev_priv->min_delay)
|
||||
new_delay = dev_priv->min_delay;
|
||||
if (dev_priv->ips.cur_delay != dev_priv->ips.min_delay)
|
||||
new_delay = dev_priv->ips.cur_delay + 1;
|
||||
if (new_delay > dev_priv->ips.min_delay)
|
||||
new_delay = dev_priv->ips.min_delay;
|
||||
}
|
||||
|
||||
if (ironlake_set_drps(dev, new_delay))
|
||||
dev_priv->cur_delay = new_delay;
|
||||
dev_priv->ips.cur_delay = new_delay;
|
||||
|
||||
spin_unlock_irqrestore(&mchdev_lock, flags);
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -334,7 +346,7 @@ static void notify_ring(struct drm_device *dev,
|
|||
if (ring->obj == NULL)
|
||||
return;
|
||||
|
||||
trace_i915_gem_request_complete(ring, ring->get_seqno(ring));
|
||||
trace_i915_gem_request_complete(ring, ring->get_seqno(ring, false));
|
||||
|
||||
wake_up_all(&ring->irq_queue);
|
||||
if (i915_enable_hangcheck) {
|
||||
|
@ -348,16 +360,16 @@ static void notify_ring(struct drm_device *dev,
|
|||
static void gen6_pm_rps_work(struct work_struct *work)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = container_of(work, drm_i915_private_t,
|
||||
rps_work);
|
||||
rps.work);
|
||||
u32 pm_iir, pm_imr;
|
||||
u8 new_delay;
|
||||
|
||||
spin_lock_irq(&dev_priv->rps_lock);
|
||||
pm_iir = dev_priv->pm_iir;
|
||||
dev_priv->pm_iir = 0;
|
||||
spin_lock_irq(&dev_priv->rps.lock);
|
||||
pm_iir = dev_priv->rps.pm_iir;
|
||||
dev_priv->rps.pm_iir = 0;
|
||||
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 & GEN6_PM_DEFERRED_EVENTS) == 0)
|
||||
return;
|
||||
|
@ -365,11 +377,17 @@ static void gen6_pm_rps_work(struct work_struct *work)
|
|||
mutex_lock(&dev_priv->dev->struct_mutex);
|
||||
|
||||
if (pm_iir & GEN6_PM_RP_UP_THRESHOLD)
|
||||
new_delay = dev_priv->cur_delay + 1;
|
||||
new_delay = dev_priv->rps.cur_delay + 1;
|
||||
else
|
||||
new_delay = dev_priv->cur_delay - 1;
|
||||
new_delay = dev_priv->rps.cur_delay - 1;
|
||||
|
||||
gen6_set_rps(dev_priv->dev, new_delay);
|
||||
/* sysfs frequency interfaces may have snuck in while servicing the
|
||||
* interrupt
|
||||
*/
|
||||
if (!(new_delay > dev_priv->rps.max_delay ||
|
||||
new_delay < dev_priv->rps.min_delay)) {
|
||||
gen6_set_rps(dev_priv->dev, new_delay);
|
||||
}
|
||||
|
||||
mutex_unlock(&dev_priv->dev->struct_mutex);
|
||||
}
|
||||
|
@ -443,7 +461,7 @@ static void ivybridge_handle_parity_error(struct drm_device *dev)
|
|||
drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
|
||||
unsigned long flags;
|
||||
|
||||
if (!IS_IVYBRIDGE(dev))
|
||||
if (!HAS_L3_GPU_CACHE(dev))
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, flags);
|
||||
|
@ -487,19 +505,19 @@ static void gen6_queue_rps_work(struct drm_i915_private *dev_priv,
|
|||
* IIR bits should never already be set because IMR should
|
||||
* prevent an interrupt from being shown in IIR. The warning
|
||||
* displays a case where we've unsafely cleared
|
||||
* dev_priv->pm_iir. Although missing an interrupt of the same
|
||||
* dev_priv->rps.pm_iir. Although missing an interrupt of the same
|
||||
* type is not a problem, it displays a problem in the logic.
|
||||
*
|
||||
* The mask bit in IMR is cleared by rps_work.
|
||||
* The mask bit in IMR is cleared by dev_priv->rps.work.
|
||||
*/
|
||||
|
||||
spin_lock_irqsave(&dev_priv->rps_lock, flags);
|
||||
dev_priv->pm_iir |= pm_iir;
|
||||
I915_WRITE(GEN6_PMIMR, dev_priv->pm_iir);
|
||||
spin_lock_irqsave(&dev_priv->rps.lock, flags);
|
||||
dev_priv->rps.pm_iir |= pm_iir;
|
||||
I915_WRITE(GEN6_PMIMR, dev_priv->rps.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);
|
||||
}
|
||||
|
||||
static irqreturn_t valleyview_irq_handler(DRM_IRQ_ARGS)
|
||||
|
@ -792,10 +810,8 @@ static irqreturn_t ironlake_irq_handler(DRM_IRQ_ARGS)
|
|||
ibx_irq_handler(dev, pch_iir);
|
||||
}
|
||||
|
||||
if (de_iir & DE_PCU_EVENT) {
|
||||
I915_WRITE16(MEMINTRSTS, I915_READ(MEMINTRSTS));
|
||||
i915_handle_rps_change(dev);
|
||||
}
|
||||
if (IS_GEN5(dev) && de_iir & DE_PCU_EVENT)
|
||||
ironlake_handle_rps_change(dev);
|
||||
|
||||
if (IS_GEN6(dev) && pm_iir & GEN6_PM_DEFERRED_EVENTS)
|
||||
gen6_queue_rps_work(dev_priv, pm_iir);
|
||||
|
@ -842,26 +858,55 @@ static void i915_error_work_func(struct work_struct *work)
|
|||
}
|
||||
}
|
||||
|
||||
/* NB: please notice the memset */
|
||||
static void i915_get_extra_instdone(struct drm_device *dev,
|
||||
uint32_t *instdone)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
memset(instdone, 0, sizeof(*instdone) * I915_NUM_INSTDONE_REG);
|
||||
|
||||
switch(INTEL_INFO(dev)->gen) {
|
||||
case 2:
|
||||
case 3:
|
||||
instdone[0] = I915_READ(INSTDONE);
|
||||
break;
|
||||
case 4:
|
||||
case 5:
|
||||
case 6:
|
||||
instdone[0] = I915_READ(INSTDONE_I965);
|
||||
instdone[1] = I915_READ(INSTDONE1);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Unsupported platform\n");
|
||||
case 7:
|
||||
instdone[0] = I915_READ(GEN7_INSTDONE_1);
|
||||
instdone[1] = I915_READ(GEN7_SC_INSTDONE);
|
||||
instdone[2] = I915_READ(GEN7_SAMPLER_INSTDONE);
|
||||
instdone[3] = I915_READ(GEN7_ROW_INSTDONE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
static struct drm_i915_error_object *
|
||||
i915_error_object_create(struct drm_i915_private *dev_priv,
|
||||
struct drm_i915_gem_object *src)
|
||||
{
|
||||
struct drm_i915_error_object *dst;
|
||||
int page, page_count;
|
||||
int i, count;
|
||||
u32 reloc_offset;
|
||||
|
||||
if (src == NULL || src->pages == NULL)
|
||||
return NULL;
|
||||
|
||||
page_count = src->base.size / PAGE_SIZE;
|
||||
count = src->base.size / PAGE_SIZE;
|
||||
|
||||
dst = kmalloc(sizeof(*dst) + page_count * sizeof(u32 *), GFP_ATOMIC);
|
||||
dst = kmalloc(sizeof(*dst) + count * sizeof(u32 *), GFP_ATOMIC);
|
||||
if (dst == NULL)
|
||||
return NULL;
|
||||
|
||||
reloc_offset = src->gtt_offset;
|
||||
for (page = 0; page < page_count; page++) {
|
||||
for (i = 0; i < count; i++) {
|
||||
unsigned long flags;
|
||||
void *d;
|
||||
|
||||
|
@ -884,30 +929,33 @@ i915_error_object_create(struct drm_i915_private *dev_priv,
|
|||
memcpy_fromio(d, s, PAGE_SIZE);
|
||||
io_mapping_unmap_atomic(s);
|
||||
} else {
|
||||
struct page *page;
|
||||
void *s;
|
||||
|
||||
drm_clflush_pages(&src->pages[page], 1);
|
||||
page = i915_gem_object_get_page(src, i);
|
||||
|
||||
s = kmap_atomic(src->pages[page]);
|
||||
drm_clflush_pages(&page, 1);
|
||||
|
||||
s = kmap_atomic(page);
|
||||
memcpy(d, s, PAGE_SIZE);
|
||||
kunmap_atomic(s);
|
||||
|
||||
drm_clflush_pages(&src->pages[page], 1);
|
||||
drm_clflush_pages(&page, 1);
|
||||
}
|
||||
local_irq_restore(flags);
|
||||
|
||||
dst->pages[page] = d;
|
||||
dst->pages[i] = d;
|
||||
|
||||
reloc_offset += PAGE_SIZE;
|
||||
}
|
||||
dst->page_count = page_count;
|
||||
dst->page_count = count;
|
||||
dst->gtt_offset = src->gtt_offset;
|
||||
|
||||
return dst;
|
||||
|
||||
unwind:
|
||||
while (page--)
|
||||
kfree(dst->pages[page]);
|
||||
while (i--)
|
||||
kfree(dst->pages[i]);
|
||||
kfree(dst);
|
||||
return NULL;
|
||||
}
|
||||
|
@ -948,7 +996,8 @@ static void capture_bo(struct drm_i915_error_buffer *err,
|
|||
{
|
||||
err->size = obj->base.size;
|
||||
err->name = obj->base.name;
|
||||
err->seqno = obj->last_rendering_seqno;
|
||||
err->rseqno = obj->last_read_seqno;
|
||||
err->wseqno = obj->last_write_seqno;
|
||||
err->gtt_offset = obj->gtt_offset;
|
||||
err->read_domains = obj->base.read_domains;
|
||||
err->write_domain = obj->base.write_domain;
|
||||
|
@ -1038,12 +1087,12 @@ i915_error_first_batchbuffer(struct drm_i915_private *dev_priv,
|
|||
if (!ring->get_seqno)
|
||||
return NULL;
|
||||
|
||||
seqno = ring->get_seqno(ring);
|
||||
seqno = ring->get_seqno(ring, false);
|
||||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list) {
|
||||
if (obj->ring != ring)
|
||||
continue;
|
||||
|
||||
if (i915_seqno_passed(seqno, obj->last_rendering_seqno))
|
||||
if (i915_seqno_passed(seqno, obj->last_read_seqno))
|
||||
continue;
|
||||
|
||||
if ((obj->base.read_domains & I915_GEM_DOMAIN_COMMAND) == 0)
|
||||
|
@ -1079,10 +1128,8 @@ static void i915_record_ring_state(struct drm_device *dev,
|
|||
error->ipehr[ring->id] = I915_READ(RING_IPEHR(ring->mmio_base));
|
||||
error->instdone[ring->id] = I915_READ(RING_INSTDONE(ring->mmio_base));
|
||||
error->instps[ring->id] = I915_READ(RING_INSTPS(ring->mmio_base));
|
||||
if (ring->id == RCS) {
|
||||
error->instdone1 = I915_READ(INSTDONE1);
|
||||
if (ring->id == RCS)
|
||||
error->bbaddr = I915_READ64(BB_ADDR);
|
||||
}
|
||||
} else {
|
||||
error->faddr[ring->id] = I915_READ(DMA_FADD_I8XX);
|
||||
error->ipeir[ring->id] = I915_READ(IPEIR);
|
||||
|
@ -1092,7 +1139,7 @@ static void i915_record_ring_state(struct drm_device *dev,
|
|||
|
||||
error->waiting[ring->id] = waitqueue_active(&ring->irq_queue);
|
||||
error->instpm[ring->id] = I915_READ(RING_INSTPM(ring->mmio_base));
|
||||
error->seqno[ring->id] = ring->get_seqno(ring);
|
||||
error->seqno[ring->id] = ring->get_seqno(ring, false);
|
||||
error->acthd[ring->id] = intel_ring_get_active_head(ring);
|
||||
error->head[ring->id] = I915_READ_HEAD(ring);
|
||||
error->tail[ring->id] = I915_READ_TAIL(ring);
|
||||
|
@ -1198,6 +1245,11 @@ static void i915_capture_error_state(struct drm_device *dev)
|
|||
error->done_reg = I915_READ(DONE_REG);
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen == 7)
|
||||
error->err_int = I915_READ(GEN7_ERR_INT);
|
||||
|
||||
i915_get_extra_instdone(dev, error->extra_instdone);
|
||||
|
||||
i915_gem_record_fences(dev, error);
|
||||
i915_gem_record_rings(dev, error);
|
||||
|
||||
|
@ -1209,7 +1261,7 @@ static void i915_capture_error_state(struct drm_device *dev)
|
|||
list_for_each_entry(obj, &dev_priv->mm.active_list, mm_list)
|
||||
i++;
|
||||
error->active_bo_count = i;
|
||||
list_for_each_entry(obj, &dev_priv->mm.gtt_list, gtt_list)
|
||||
list_for_each_entry(obj, &dev_priv->mm.bound_list, gtt_list)
|
||||
if (obj->pin_count)
|
||||
i++;
|
||||
error->pinned_bo_count = i - error->active_bo_count;
|
||||
|
@ -1234,7 +1286,7 @@ static void i915_capture_error_state(struct drm_device *dev)
|
|||
error->pinned_bo_count =
|
||||
capture_pinned_bo(error->pinned_bo,
|
||||
error->pinned_bo_count,
|
||||
&dev_priv->mm.gtt_list);
|
||||
&dev_priv->mm.bound_list);
|
||||
|
||||
do_gettimeofday(&error->time);
|
||||
|
||||
|
@ -1273,24 +1325,26 @@ void i915_destroy_error_state(struct drm_device *dev)
|
|||
static void i915_report_and_clear_eir(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t instdone[I915_NUM_INSTDONE_REG];
|
||||
u32 eir = I915_READ(EIR);
|
||||
int pipe;
|
||||
int pipe, i;
|
||||
|
||||
if (!eir)
|
||||
return;
|
||||
|
||||
pr_err("render error detected, EIR: 0x%08x\n", eir);
|
||||
|
||||
i915_get_extra_instdone(dev, instdone);
|
||||
|
||||
if (IS_G4X(dev)) {
|
||||
if (eir & (GM45_ERROR_MEM_PRIV | GM45_ERROR_CP_PRIV)) {
|
||||
u32 ipeir = I915_READ(IPEIR_I965);
|
||||
|
||||
pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965));
|
||||
pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965));
|
||||
pr_err(" INSTDONE: 0x%08x\n",
|
||||
I915_READ(INSTDONE_I965));
|
||||
for (i = 0; i < ARRAY_SIZE(instdone); i++)
|
||||
pr_err(" INSTDONE_%d: 0x%08x\n", i, instdone[i]);
|
||||
pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS));
|
||||
pr_err(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1));
|
||||
pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965));
|
||||
I915_WRITE(IPEIR_I965, ipeir);
|
||||
POSTING_READ(IPEIR_I965);
|
||||
|
@ -1324,12 +1378,13 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
|
|||
if (eir & I915_ERROR_INSTRUCTION) {
|
||||
pr_err("instruction error\n");
|
||||
pr_err(" INSTPM: 0x%08x\n", I915_READ(INSTPM));
|
||||
for (i = 0; i < ARRAY_SIZE(instdone); i++)
|
||||
pr_err(" INSTDONE_%d: 0x%08x\n", i, instdone[i]);
|
||||
if (INTEL_INFO(dev)->gen < 4) {
|
||||
u32 ipeir = I915_READ(IPEIR);
|
||||
|
||||
pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR));
|
||||
pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR));
|
||||
pr_err(" INSTDONE: 0x%08x\n", I915_READ(INSTDONE));
|
||||
pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD));
|
||||
I915_WRITE(IPEIR, ipeir);
|
||||
POSTING_READ(IPEIR);
|
||||
|
@ -1338,10 +1393,7 @@ static void i915_report_and_clear_eir(struct drm_device *dev)
|
|||
|
||||
pr_err(" IPEIR: 0x%08x\n", I915_READ(IPEIR_I965));
|
||||
pr_err(" IPEHR: 0x%08x\n", I915_READ(IPEHR_I965));
|
||||
pr_err(" INSTDONE: 0x%08x\n",
|
||||
I915_READ(INSTDONE_I965));
|
||||
pr_err(" INSTPS: 0x%08x\n", I915_READ(INSTPS));
|
||||
pr_err(" INSTDONE1: 0x%08x\n", I915_READ(INSTDONE1));
|
||||
pr_err(" ACTHD: 0x%08x\n", I915_READ(ACTHD_I965));
|
||||
I915_WRITE(IPEIR_I965, ipeir);
|
||||
POSTING_READ(IPEIR_I965);
|
||||
|
@ -1589,7 +1641,8 @@ ring_last_seqno(struct intel_ring_buffer *ring)
|
|||
static bool i915_hangcheck_ring_idle(struct intel_ring_buffer *ring, bool *err)
|
||||
{
|
||||
if (list_empty(&ring->request_list) ||
|
||||
i915_seqno_passed(ring->get_seqno(ring), ring_last_seqno(ring))) {
|
||||
i915_seqno_passed(ring->get_seqno(ring, false),
|
||||
ring_last_seqno(ring))) {
|
||||
/* Issue a wake-up to catch stuck h/w. */
|
||||
if (waitqueue_active(&ring->irq_queue)) {
|
||||
DRM_ERROR("Hangcheck timer elapsed... %s idle\n",
|
||||
|
@ -1655,7 +1708,7 @@ void i915_hangcheck_elapsed(unsigned long data)
|
|||
{
|
||||
struct drm_device *dev = (struct drm_device *)data;
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
uint32_t acthd[I915_NUM_RINGS], instdone, instdone1;
|
||||
uint32_t acthd[I915_NUM_RINGS], instdone[I915_NUM_INSTDONE_REG];
|
||||
struct intel_ring_buffer *ring;
|
||||
bool err = false, idle;
|
||||
int i;
|
||||
|
@ -1683,25 +1736,16 @@ void i915_hangcheck_elapsed(unsigned long data)
|
|||
return;
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen < 4) {
|
||||
instdone = I915_READ(INSTDONE);
|
||||
instdone1 = 0;
|
||||
} else {
|
||||
instdone = I915_READ(INSTDONE_I965);
|
||||
instdone1 = I915_READ(INSTDONE1);
|
||||
}
|
||||
|
||||
i915_get_extra_instdone(dev, instdone);
|
||||
if (memcmp(dev_priv->last_acthd, acthd, sizeof(acthd)) == 0 &&
|
||||
dev_priv->last_instdone == instdone &&
|
||||
dev_priv->last_instdone1 == instdone1) {
|
||||
memcmp(dev_priv->prev_instdone, instdone, sizeof(instdone)) == 0) {
|
||||
if (i915_hangcheck_hung(dev))
|
||||
return;
|
||||
} else {
|
||||
dev_priv->hangcheck_count = 0;
|
||||
|
||||
memcpy(dev_priv->last_acthd, acthd, sizeof(acthd));
|
||||
dev_priv->last_instdone = instdone;
|
||||
dev_priv->last_instdone1 = instdone1;
|
||||
memcpy(dev_priv->prev_instdone, instdone, sizeof(instdone));
|
||||
}
|
||||
|
||||
repeat:
|
||||
|
@ -2646,7 +2690,7 @@ void intel_irq_init(struct drm_device *dev)
|
|||
|
||||
INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
|
||||
INIT_WORK(&dev_priv->error_work, i915_error_work_func);
|
||||
INIT_WORK(&dev_priv->rps_work, gen6_pm_rps_work);
|
||||
INIT_WORK(&dev_priv->rps.work, gen6_pm_rps_work);
|
||||
INIT_WORK(&dev_priv->parity_error_work, ivybridge_parity_work);
|
||||
|
||||
dev->driver->get_vblank_counter = i915_get_vblank_counter;
|
||||
|
|
|
@ -450,6 +450,7 @@
|
|||
#define RING_ACTHD(base) ((base)+0x74)
|
||||
#define RING_NOPID(base) ((base)+0x94)
|
||||
#define RING_IMR(base) ((base)+0xa8)
|
||||
#define RING_TIMESTAMP(base) ((base)+0x358)
|
||||
#define TAIL_ADDR 0x001FFFF8
|
||||
#define HEAD_WRAP_COUNT 0xFFE00000
|
||||
#define HEAD_WRAP_ONE 0x00200000
|
||||
|
@ -478,6 +479,11 @@
|
|||
#define IPEIR_I965 0x02064
|
||||
#define IPEHR_I965 0x02068
|
||||
#define INSTDONE_I965 0x0206c
|
||||
#define GEN7_INSTDONE_1 0x0206c
|
||||
#define GEN7_SC_INSTDONE 0x07100
|
||||
#define GEN7_SAMPLER_INSTDONE 0x0e160
|
||||
#define GEN7_ROW_INSTDONE 0x0e164
|
||||
#define I915_NUM_INSTDONE_REG 4
|
||||
#define RING_IPEIR(base) ((base)+0x64)
|
||||
#define RING_IPEHR(base) ((base)+0x68)
|
||||
#define RING_INSTDONE(base) ((base)+0x6c)
|
||||
|
@ -500,6 +506,8 @@
|
|||
#define DMA_FADD_I8XX 0x020d0
|
||||
|
||||
#define ERROR_GEN6 0x040a0
|
||||
#define GEN7_ERR_INT 0x44040
|
||||
#define ERR_INT_MMIO_UNCLAIMED (1<<13)
|
||||
|
||||
/* GM45+ chicken bits -- debug workaround bits that may be required
|
||||
* for various sorts of correct behavior. The top 16 bits of each are
|
||||
|
@ -529,6 +537,8 @@
|
|||
#define GFX_PSMI_GRANULARITY (1<<10)
|
||||
#define GFX_PPGTT_ENABLE (1<<9)
|
||||
|
||||
#define VLV_DISPLAY_BASE 0x180000
|
||||
|
||||
#define SCPD0 0x0209c /* 915+ only */
|
||||
#define IER 0x020a0
|
||||
#define IIR 0x020a4
|
||||
|
@ -1496,6 +1506,14 @@
|
|||
GEN7_CXT_EXTENDED_SIZE(ctx_reg) + \
|
||||
GEN7_CXT_GT1_SIZE(ctx_reg) + \
|
||||
GEN7_CXT_VFSTATE_SIZE(ctx_reg))
|
||||
#define HSW_CXT_POWER_SIZE(ctx_reg) ((ctx_reg >> 26) & 0x3f)
|
||||
#define HSW_CXT_RING_SIZE(ctx_reg) ((ctx_reg >> 23) & 0x7)
|
||||
#define HSW_CXT_RENDER_SIZE(ctx_reg) ((ctx_reg >> 15) & 0xff)
|
||||
#define HSW_CXT_TOTAL_SIZE(ctx_reg) (HSW_CXT_POWER_SIZE(ctx_reg) + \
|
||||
HSW_CXT_RING_SIZE(ctx_reg) + \
|
||||
HSW_CXT_RENDER_SIZE(ctx_reg) + \
|
||||
GEN7_CXT_VFSTATE_SIZE(ctx_reg))
|
||||
|
||||
|
||||
/*
|
||||
* Overlay regs
|
||||
|
@ -1549,12 +1567,35 @@
|
|||
|
||||
/* VGA port control */
|
||||
#define ADPA 0x61100
|
||||
#define PCH_ADPA 0xe1100
|
||||
#define VLV_ADPA (VLV_DISPLAY_BASE + ADPA)
|
||||
|
||||
#define ADPA_DAC_ENABLE (1<<31)
|
||||
#define ADPA_DAC_DISABLE 0
|
||||
#define ADPA_PIPE_SELECT_MASK (1<<30)
|
||||
#define ADPA_PIPE_A_SELECT 0
|
||||
#define ADPA_PIPE_B_SELECT (1<<30)
|
||||
#define ADPA_PIPE_SELECT(pipe) ((pipe) << 30)
|
||||
/* CPT uses bits 29:30 for pch transcoder select */
|
||||
#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24)
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24)
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24)
|
||||
#define ADPA_CRT_HOTPLUG_ENABLE (1<<23)
|
||||
#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22)
|
||||
#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22)
|
||||
#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21)
|
||||
#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21)
|
||||
#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20)
|
||||
#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17)
|
||||
#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17)
|
||||
#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
|
||||
#define ADPA_USE_VGA_HVPOLARITY (1<<15)
|
||||
#define ADPA_SETS_HVPOLARITY 0
|
||||
#define ADPA_VSYNC_CNTL_DISABLE (1<<11)
|
||||
|
@ -1753,6 +1794,10 @@
|
|||
|
||||
/* Video Data Island Packet control */
|
||||
#define VIDEO_DIP_DATA 0x61178
|
||||
/* Read the description of VIDEO_DIP_DATA (before Haswel) or VIDEO_DIP_ECC
|
||||
* (Haswell and newer) to see which VIDEO_DIP_DATA byte corresponds to each byte
|
||||
* of the infoframe structure specified by CEA-861. */
|
||||
#define VIDEO_DIP_DATA_SIZE 32
|
||||
#define VIDEO_DIP_CTL 0x61170
|
||||
/* Pre HSW: */
|
||||
#define VIDEO_DIP_ENABLE (1 << 31)
|
||||
|
@ -3889,31 +3934,6 @@
|
|||
#define FDI_PLL_CTL_1 0xfe000
|
||||
#define FDI_PLL_CTL_2 0xfe004
|
||||
|
||||
/* CRT */
|
||||
#define PCH_ADPA 0xe1100
|
||||
#define ADPA_TRANS_SELECT_MASK (1<<30)
|
||||
#define ADPA_TRANS_A_SELECT 0
|
||||
#define ADPA_TRANS_B_SELECT (1<<30)
|
||||
#define ADPA_CRT_HOTPLUG_MASK 0x03ff0000 /* bit 25-16 */
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_NONE (0<<24)
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_MASK (3<<24)
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_COLOR (3<<24)
|
||||
#define ADPA_CRT_HOTPLUG_MONITOR_MONO (2<<24)
|
||||
#define ADPA_CRT_HOTPLUG_ENABLE (1<<23)
|
||||
#define ADPA_CRT_HOTPLUG_PERIOD_64 (0<<22)
|
||||
#define ADPA_CRT_HOTPLUG_PERIOD_128 (1<<22)
|
||||
#define ADPA_CRT_HOTPLUG_WARMUP_5MS (0<<21)
|
||||
#define ADPA_CRT_HOTPLUG_WARMUP_10MS (1<<21)
|
||||
#define ADPA_CRT_HOTPLUG_SAMPLE_2S (0<<20)
|
||||
#define ADPA_CRT_HOTPLUG_SAMPLE_4S (1<<20)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_40 (0<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_50 (1<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_60 (2<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLTAGE_70 (3<<18)
|
||||
#define ADPA_CRT_HOTPLUG_VOLREF_325MV (0<<17)
|
||||
#define ADPA_CRT_HOTPLUG_VOLREF_475MV (1<<17)
|
||||
#define ADPA_CRT_HOTPLUG_FORCE_TRIGGER (1<<16)
|
||||
|
||||
/* or SDVOB */
|
||||
#define HDMIB 0xe1140
|
||||
#define PORT_ENABLE (1 << 31)
|
||||
|
@ -4021,6 +4041,8 @@
|
|||
#define PORT_TRANS_C_SEL_CPT (2<<29)
|
||||
#define PORT_TRANS_SEL_MASK (3<<29)
|
||||
#define PORT_TRANS_SEL_CPT(pipe) ((pipe) << 29)
|
||||
#define PORT_TO_PIPE(val) (((val) & (1<<30)) >> 30)
|
||||
#define PORT_TO_PIPE_CPT(val) (((val) & PORT_TRANS_SEL_MASK) >> 29)
|
||||
|
||||
#define TRANS_DP_CTL_A 0xe0300
|
||||
#define TRANS_DP_CTL_B 0xe1300
|
||||
|
@ -4239,7 +4261,15 @@
|
|||
#define G4X_HDMIW_HDMIEDID 0x6210C
|
||||
|
||||
#define IBX_HDMIW_HDMIEDID_A 0xE2050
|
||||
#define IBX_HDMIW_HDMIEDID_B 0xE2150
|
||||
#define IBX_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \
|
||||
IBX_HDMIW_HDMIEDID_A, \
|
||||
IBX_HDMIW_HDMIEDID_B)
|
||||
#define IBX_AUD_CNTL_ST_A 0xE20B4
|
||||
#define IBX_AUD_CNTL_ST_B 0xE21B4
|
||||
#define IBX_AUD_CNTL_ST(pipe) _PIPE(pipe, \
|
||||
IBX_AUD_CNTL_ST_A, \
|
||||
IBX_AUD_CNTL_ST_B)
|
||||
#define IBX_ELD_BUFFER_SIZE (0x1f << 10)
|
||||
#define IBX_ELD_ADDRESS (0x1f << 5)
|
||||
#define IBX_ELD_ACK (1 << 4)
|
||||
|
@ -4248,7 +4278,15 @@
|
|||
#define IBX_CP_READYB (1 << 1)
|
||||
|
||||
#define CPT_HDMIW_HDMIEDID_A 0xE5050
|
||||
#define CPT_HDMIW_HDMIEDID_B 0xE5150
|
||||
#define CPT_HDMIW_HDMIEDID(pipe) _PIPE(pipe, \
|
||||
CPT_HDMIW_HDMIEDID_A, \
|
||||
CPT_HDMIW_HDMIEDID_B)
|
||||
#define CPT_AUD_CNTL_ST_A 0xE50B4
|
||||
#define CPT_AUD_CNTL_ST_B 0xE51B4
|
||||
#define CPT_AUD_CNTL_ST(pipe) _PIPE(pipe, \
|
||||
CPT_AUD_CNTL_ST_A, \
|
||||
CPT_AUD_CNTL_ST_B)
|
||||
#define CPT_AUD_CNTRL_ST2 0xE50C0
|
||||
|
||||
/* These are the 4 32-bit write offset registers for each stream
|
||||
|
@ -4258,7 +4296,15 @@
|
|||
#define GEN7_SO_WRITE_OFFSET(n) (0x5280 + (n) * 4)
|
||||
|
||||
#define IBX_AUD_CONFIG_A 0xe2000
|
||||
#define IBX_AUD_CONFIG_B 0xe2100
|
||||
#define IBX_AUD_CFG(pipe) _PIPE(pipe, \
|
||||
IBX_AUD_CONFIG_A, \
|
||||
IBX_AUD_CONFIG_B)
|
||||
#define CPT_AUD_CONFIG_A 0xe5000
|
||||
#define CPT_AUD_CONFIG_B 0xe5100
|
||||
#define CPT_AUD_CFG(pipe) _PIPE(pipe, \
|
||||
CPT_AUD_CONFIG_A, \
|
||||
CPT_AUD_CONFIG_B)
|
||||
#define AUD_CONFIG_N_VALUE_INDEX (1 << 29)
|
||||
#define AUD_CONFIG_N_PROG_ENABLE (1 << 28)
|
||||
#define AUD_CONFIG_UPPER_N_SHIFT 20
|
||||
|
@ -4269,195 +4315,233 @@
|
|||
#define AUD_CONFIG_PIXEL_CLOCK_HDMI (0xf << 16)
|
||||
#define AUD_CONFIG_DISABLE_NCTS (1 << 3)
|
||||
|
||||
/* HSW Audio */
|
||||
#define HSW_AUD_CONFIG_A 0x65000 /* Audio Configuration Transcoder A */
|
||||
#define HSW_AUD_CONFIG_B 0x65100 /* Audio Configuration Transcoder B */
|
||||
#define HSW_AUD_CFG(pipe) _PIPE(pipe, \
|
||||
HSW_AUD_CONFIG_A, \
|
||||
HSW_AUD_CONFIG_B)
|
||||
|
||||
#define HSW_AUD_MISC_CTRL_A 0x65010 /* Audio Misc Control Convert 1 */
|
||||
#define HSW_AUD_MISC_CTRL_B 0x65110 /* Audio Misc Control Convert 2 */
|
||||
#define HSW_AUD_MISC_CTRL(pipe) _PIPE(pipe, \
|
||||
HSW_AUD_MISC_CTRL_A, \
|
||||
HSW_AUD_MISC_CTRL_B)
|
||||
|
||||
#define HSW_AUD_DIP_ELD_CTRL_ST_A 0x650b4 /* Audio DIP and ELD Control State Transcoder A */
|
||||
#define HSW_AUD_DIP_ELD_CTRL_ST_B 0x651b4 /* Audio DIP and ELD Control State Transcoder B */
|
||||
#define HSW_AUD_DIP_ELD_CTRL(pipe) _PIPE(pipe, \
|
||||
HSW_AUD_DIP_ELD_CTRL_ST_A, \
|
||||
HSW_AUD_DIP_ELD_CTRL_ST_B)
|
||||
|
||||
/* Audio Digital Converter */
|
||||
#define HSW_AUD_DIG_CNVT_1 0x65080 /* Audio Converter 1 */
|
||||
#define HSW_AUD_DIG_CNVT_2 0x65180 /* Audio Converter 1 */
|
||||
#define AUD_DIG_CNVT(pipe) _PIPE(pipe, \
|
||||
HSW_AUD_DIG_CNVT_1, \
|
||||
HSW_AUD_DIG_CNVT_2)
|
||||
#define DIP_PORT_SEL_MASK 0x3
|
||||
|
||||
#define HSW_AUD_EDID_DATA_A 0x65050
|
||||
#define HSW_AUD_EDID_DATA_B 0x65150
|
||||
#define HSW_AUD_EDID_DATA(pipe) _PIPE(pipe, \
|
||||
HSW_AUD_EDID_DATA_A, \
|
||||
HSW_AUD_EDID_DATA_B)
|
||||
|
||||
#define HSW_AUD_PIPE_CONV_CFG 0x6507c /* Audio pipe and converter configs */
|
||||
#define HSW_AUD_PIN_ELD_CP_VLD 0x650c0 /* Audio ELD and CP Ready Status */
|
||||
#define AUDIO_INACTIVE_C (1<<11)
|
||||
#define AUDIO_INACTIVE_B (1<<7)
|
||||
#define AUDIO_INACTIVE_A (1<<3)
|
||||
#define AUDIO_OUTPUT_ENABLE_A (1<<2)
|
||||
#define AUDIO_OUTPUT_ENABLE_B (1<<6)
|
||||
#define AUDIO_OUTPUT_ENABLE_C (1<<10)
|
||||
#define AUDIO_ELD_VALID_A (1<<0)
|
||||
#define AUDIO_ELD_VALID_B (1<<4)
|
||||
#define AUDIO_ELD_VALID_C (1<<8)
|
||||
#define AUDIO_CP_READY_A (1<<1)
|
||||
#define AUDIO_CP_READY_B (1<<5)
|
||||
#define AUDIO_CP_READY_C (1<<9)
|
||||
|
||||
/* HSW Power Wells */
|
||||
#define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */
|
||||
#define HSW_PWR_WELL_CTL2 0x45404 /* Driver */
|
||||
#define HSW_PWR_WELL_CTL3 0x45408 /* KVMR */
|
||||
#define HSW_PWR_WELL_CTL4 0x4540C /* Debug */
|
||||
#define HSW_PWR_WELL_ENABLE (1<<31)
|
||||
#define HSW_PWR_WELL_STATE (1<<30)
|
||||
#define HSW_PWR_WELL_CTL5 0x45410
|
||||
#define HSW_PWR_WELL_CTL1 0x45400 /* BIOS */
|
||||
#define HSW_PWR_WELL_CTL2 0x45404 /* Driver */
|
||||
#define HSW_PWR_WELL_CTL3 0x45408 /* KVMR */
|
||||
#define HSW_PWR_WELL_CTL4 0x4540C /* Debug */
|
||||
#define HSW_PWR_WELL_ENABLE (1<<31)
|
||||
#define HSW_PWR_WELL_STATE (1<<30)
|
||||
#define HSW_PWR_WELL_CTL5 0x45410
|
||||
#define HSW_PWR_WELL_ENABLE_SINGLE_STEP (1<<31)
|
||||
#define HSW_PWR_WELL_PWR_GATE_OVERRIDE (1<<20)
|
||||
#define HSW_PWR_WELL_FORCE_ON (1<<19)
|
||||
#define HSW_PWR_WELL_CTL6 0x45414
|
||||
#define HSW_PWR_WELL_FORCE_ON (1<<19)
|
||||
#define HSW_PWR_WELL_CTL6 0x45414
|
||||
|
||||
/* Per-pipe DDI Function Control */
|
||||
#define PIPE_DDI_FUNC_CTL_A 0x60400
|
||||
#define PIPE_DDI_FUNC_CTL_B 0x61400
|
||||
#define PIPE_DDI_FUNC_CTL_C 0x62400
|
||||
#define PIPE_DDI_FUNC_CTL_A 0x60400
|
||||
#define PIPE_DDI_FUNC_CTL_B 0x61400
|
||||
#define PIPE_DDI_FUNC_CTL_C 0x62400
|
||||
#define PIPE_DDI_FUNC_CTL_EDP 0x6F400
|
||||
#define DDI_FUNC_CTL(pipe) _PIPE(pipe, \
|
||||
PIPE_DDI_FUNC_CTL_A, \
|
||||
PIPE_DDI_FUNC_CTL_B)
|
||||
#define DDI_FUNC_CTL(pipe) _PIPE(pipe, PIPE_DDI_FUNC_CTL_A, \
|
||||
PIPE_DDI_FUNC_CTL_B)
|
||||
#define PIPE_DDI_FUNC_ENABLE (1<<31)
|
||||
/* Those bits are ignored by pipe EDP since it can only connect to DDI A */
|
||||
#define PIPE_DDI_PORT_MASK (7<<28)
|
||||
#define PIPE_DDI_SELECT_PORT(x) ((x)<<28)
|
||||
#define PIPE_DDI_MODE_SELECT_HDMI (0<<24)
|
||||
#define PIPE_DDI_MODE_SELECT_DVI (1<<24)
|
||||
#define PIPE_DDI_PORT_MASK (7<<28)
|
||||
#define PIPE_DDI_SELECT_PORT(x) ((x)<<28)
|
||||
#define PIPE_DDI_MODE_SELECT_MASK (7<<24)
|
||||
#define PIPE_DDI_MODE_SELECT_HDMI (0<<24)
|
||||
#define PIPE_DDI_MODE_SELECT_DVI (1<<24)
|
||||
#define PIPE_DDI_MODE_SELECT_DP_SST (2<<24)
|
||||
#define PIPE_DDI_MODE_SELECT_DP_MST (3<<24)
|
||||
#define PIPE_DDI_MODE_SELECT_FDI (4<<24)
|
||||
#define PIPE_DDI_BPC_8 (0<<20)
|
||||
#define PIPE_DDI_BPC_10 (1<<20)
|
||||
#define PIPE_DDI_BPC_6 (2<<20)
|
||||
#define PIPE_DDI_BPC_12 (3<<20)
|
||||
#define PIPE_DDI_BFI_ENABLE (1<<4)
|
||||
#define PIPE_DDI_PORT_WIDTH_X1 (0<<1)
|
||||
#define PIPE_DDI_PORT_WIDTH_X2 (1<<1)
|
||||
#define PIPE_DDI_PORT_WIDTH_X4 (3<<1)
|
||||
#define PIPE_DDI_MODE_SELECT_FDI (4<<24)
|
||||
#define PIPE_DDI_BPC_MASK (7<<20)
|
||||
#define PIPE_DDI_BPC_8 (0<<20)
|
||||
#define PIPE_DDI_BPC_10 (1<<20)
|
||||
#define PIPE_DDI_BPC_6 (2<<20)
|
||||
#define PIPE_DDI_BPC_12 (3<<20)
|
||||
#define PIPE_DDI_PVSYNC (1<<17)
|
||||
#define PIPE_DDI_PHSYNC (1<<16)
|
||||
#define PIPE_DDI_BFI_ENABLE (1<<4)
|
||||
#define PIPE_DDI_PORT_WIDTH_X1 (0<<1)
|
||||
#define PIPE_DDI_PORT_WIDTH_X2 (1<<1)
|
||||
#define PIPE_DDI_PORT_WIDTH_X4 (3<<1)
|
||||
|
||||
/* DisplayPort Transport Control */
|
||||
#define DP_TP_CTL_A 0x64040
|
||||
#define DP_TP_CTL_B 0x64140
|
||||
#define DP_TP_CTL(port) _PORT(port, \
|
||||
DP_TP_CTL_A, \
|
||||
DP_TP_CTL_B)
|
||||
#define DP_TP_CTL_ENABLE (1<<31)
|
||||
#define DP_TP_CTL_MODE_SST (0<<27)
|
||||
#define DP_TP_CTL_MODE_MST (1<<27)
|
||||
#define DP_TP_CTL(port) _PORT(port, DP_TP_CTL_A, DP_TP_CTL_B)
|
||||
#define DP_TP_CTL_ENABLE (1<<31)
|
||||
#define DP_TP_CTL_MODE_SST (0<<27)
|
||||
#define DP_TP_CTL_MODE_MST (1<<27)
|
||||
#define DP_TP_CTL_ENHANCED_FRAME_ENABLE (1<<18)
|
||||
#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15)
|
||||
#define DP_TP_CTL_FDI_AUTOTRAIN (1<<15)
|
||||
#define DP_TP_CTL_LINK_TRAIN_MASK (7<<8)
|
||||
#define DP_TP_CTL_LINK_TRAIN_PAT1 (0<<8)
|
||||
#define DP_TP_CTL_LINK_TRAIN_PAT2 (1<<8)
|
||||
#define DP_TP_CTL_LINK_TRAIN_NORMAL (3<<8)
|
||||
#define DP_TP_CTL_LINK_TRAIN_NORMAL (3<<8)
|
||||
|
||||
/* DisplayPort Transport Status */
|
||||
#define DP_TP_STATUS_A 0x64044
|
||||
#define DP_TP_STATUS_B 0x64144
|
||||
#define DP_TP_STATUS(port) _PORT(port, \
|
||||
DP_TP_STATUS_A, \
|
||||
DP_TP_STATUS_B)
|
||||
#define DP_TP_STATUS(port) _PORT(port, DP_TP_STATUS_A, DP_TP_STATUS_B)
|
||||
#define DP_TP_STATUS_AUTOTRAIN_DONE (1<<12)
|
||||
|
||||
/* DDI Buffer Control */
|
||||
#define DDI_BUF_CTL_A 0x64000
|
||||
#define DDI_BUF_CTL_B 0x64100
|
||||
#define DDI_BUF_CTL(port) _PORT(port, \
|
||||
DDI_BUF_CTL_A, \
|
||||
DDI_BUF_CTL_B)
|
||||
#define DDI_BUF_CTL_ENABLE (1<<31)
|
||||
#define DDI_BUF_CTL(port) _PORT(port, DDI_BUF_CTL_A, DDI_BUF_CTL_B)
|
||||
#define DDI_BUF_CTL_ENABLE (1<<31)
|
||||
#define DDI_BUF_EMP_400MV_0DB_HSW (0<<24) /* Sel0 */
|
||||
#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */
|
||||
#define DDI_BUF_EMP_400MV_3_5DB_HSW (1<<24) /* Sel1 */
|
||||
#define DDI_BUF_EMP_400MV_6DB_HSW (2<<24) /* Sel2 */
|
||||
#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */
|
||||
#define DDI_BUF_EMP_400MV_9_5DB_HSW (3<<24) /* Sel3 */
|
||||
#define DDI_BUF_EMP_600MV_0DB_HSW (4<<24) /* Sel4 */
|
||||
#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */
|
||||
#define DDI_BUF_EMP_600MV_3_5DB_HSW (5<<24) /* Sel5 */
|
||||
#define DDI_BUF_EMP_600MV_6DB_HSW (6<<24) /* Sel6 */
|
||||
#define DDI_BUF_EMP_800MV_0DB_HSW (7<<24) /* Sel7 */
|
||||
#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */
|
||||
#define DDI_BUF_EMP_MASK (0xf<<24)
|
||||
#define DDI_BUF_IS_IDLE (1<<7)
|
||||
#define DDI_PORT_WIDTH_X1 (0<<1)
|
||||
#define DDI_PORT_WIDTH_X2 (1<<1)
|
||||
#define DDI_PORT_WIDTH_X4 (3<<1)
|
||||
#define DDI_BUF_EMP_800MV_3_5DB_HSW (8<<24) /* Sel8 */
|
||||
#define DDI_BUF_EMP_MASK (0xf<<24)
|
||||
#define DDI_BUF_IS_IDLE (1<<7)
|
||||
#define DDI_PORT_WIDTH_X1 (0<<1)
|
||||
#define DDI_PORT_WIDTH_X2 (1<<1)
|
||||
#define DDI_PORT_WIDTH_X4 (3<<1)
|
||||
#define DDI_INIT_DISPLAY_DETECTED (1<<0)
|
||||
|
||||
/* DDI Buffer Translations */
|
||||
#define DDI_BUF_TRANS_A 0x64E00
|
||||
#define DDI_BUF_TRANS_B 0x64E60
|
||||
#define DDI_BUF_TRANS(port) _PORT(port, \
|
||||
DDI_BUF_TRANS_A, \
|
||||
DDI_BUF_TRANS_B)
|
||||
#define DDI_BUF_TRANS(port) _PORT(port, DDI_BUF_TRANS_A, DDI_BUF_TRANS_B)
|
||||
|
||||
/* Sideband Interface (SBI) is programmed indirectly, via
|
||||
* SBI_ADDR, which contains the register offset; and SBI_DATA,
|
||||
* which contains the payload */
|
||||
#define SBI_ADDR 0xC6000
|
||||
#define SBI_DATA 0xC6004
|
||||
#define SBI_ADDR 0xC6000
|
||||
#define SBI_DATA 0xC6004
|
||||
#define SBI_CTL_STAT 0xC6008
|
||||
#define SBI_CTL_OP_CRRD (0x6<<8)
|
||||
#define SBI_CTL_OP_CRWR (0x7<<8)
|
||||
#define SBI_RESPONSE_FAIL (0x1<<1)
|
||||
#define SBI_RESPONSE_SUCCESS (0x0<<1)
|
||||
#define SBI_BUSY (0x1<<0)
|
||||
#define SBI_READY (0x0<<0)
|
||||
#define SBI_RESPONSE_SUCCESS (0x0<<1)
|
||||
#define SBI_BUSY (0x1<<0)
|
||||
#define SBI_READY (0x0<<0)
|
||||
|
||||
/* SBI offsets */
|
||||
#define SBI_SSCDIVINTPHASE6 0x0600
|
||||
#define SBI_SSCDIVINTPHASE6 0x0600
|
||||
#define SBI_SSCDIVINTPHASE_DIVSEL_MASK ((0x7f)<<1)
|
||||
#define SBI_SSCDIVINTPHASE_DIVSEL(x) ((x)<<1)
|
||||
#define SBI_SSCDIVINTPHASE_INCVAL_MASK ((0x7f)<<8)
|
||||
#define SBI_SSCDIVINTPHASE_INCVAL(x) ((x)<<8)
|
||||
#define SBI_SSCDIVINTPHASE_DIR(x) ((x)<<15)
|
||||
#define SBI_SSCDIVINTPHASE_DIR(x) ((x)<<15)
|
||||
#define SBI_SSCDIVINTPHASE_PROPAGATE (1<<0)
|
||||
#define SBI_SSCCTL 0x020c
|
||||
#define SBI_SSCCTL 0x020c
|
||||
#define SBI_SSCCTL6 0x060C
|
||||
#define SBI_SSCCTL_DISABLE (1<<0)
|
||||
#define SBI_SSCCTL_DISABLE (1<<0)
|
||||
#define SBI_SSCAUXDIV6 0x0610
|
||||
#define SBI_SSCAUXDIV_FINALDIV2SEL(x) ((x)<<4)
|
||||
#define SBI_DBUFF0 0x2a00
|
||||
#define SBI_DBUFF0 0x2a00
|
||||
|
||||
/* LPT PIXCLK_GATE */
|
||||
#define PIXCLK_GATE 0xC6020
|
||||
#define PIXCLK_GATE_UNGATE 1<<0
|
||||
#define PIXCLK_GATE_GATE 0<<0
|
||||
#define PIXCLK_GATE 0xC6020
|
||||
#define PIXCLK_GATE_UNGATE (1<<0)
|
||||
#define PIXCLK_GATE_GATE (0<<0)
|
||||
|
||||
/* SPLL */
|
||||
#define SPLL_CTL 0x46020
|
||||
#define SPLL_CTL 0x46020
|
||||
#define SPLL_PLL_ENABLE (1<<31)
|
||||
#define SPLL_PLL_SCC (1<<28)
|
||||
#define SPLL_PLL_NON_SCC (2<<28)
|
||||
#define SPLL_PLL_FREQ_810MHz (0<<26)
|
||||
#define SPLL_PLL_FREQ_1350MHz (1<<26)
|
||||
#define SPLL_PLL_FREQ_810MHz (0<<26)
|
||||
#define SPLL_PLL_FREQ_1350MHz (1<<26)
|
||||
|
||||
/* WRPLL */
|
||||
#define WRPLL_CTL1 0x46040
|
||||
#define WRPLL_CTL2 0x46060
|
||||
#define WRPLL_PLL_ENABLE (1<<31)
|
||||
#define WRPLL_PLL_SELECT_SSC (0x01<<28)
|
||||
#define WRPLL_PLL_SELECT_NON_SCC (0x02<<28)
|
||||
#define WRPLL_CTL1 0x46040
|
||||
#define WRPLL_CTL2 0x46060
|
||||
#define WRPLL_PLL_ENABLE (1<<31)
|
||||
#define WRPLL_PLL_SELECT_SSC (0x01<<28)
|
||||
#define WRPLL_PLL_SELECT_NON_SCC (0x02<<28)
|
||||
#define WRPLL_PLL_SELECT_LCPLL_2700 (0x03<<28)
|
||||
/* WRPLL divider programming */
|
||||
#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0)
|
||||
#define WRPLL_DIVIDER_POST(x) ((x)<<8)
|
||||
#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16)
|
||||
#define WRPLL_DIVIDER_REFERENCE(x) ((x)<<0)
|
||||
#define WRPLL_DIVIDER_POST(x) ((x)<<8)
|
||||
#define WRPLL_DIVIDER_FEEDBACK(x) ((x)<<16)
|
||||
|
||||
/* Port clock selection */
|
||||
#define PORT_CLK_SEL_A 0x46100
|
||||
#define PORT_CLK_SEL_B 0x46104
|
||||
#define PORT_CLK_SEL(port) _PORT(port, \
|
||||
PORT_CLK_SEL_A, \
|
||||
PORT_CLK_SEL_B)
|
||||
#define PORT_CLK_SEL(port) _PORT(port, PORT_CLK_SEL_A, PORT_CLK_SEL_B)
|
||||
#define PORT_CLK_SEL_LCPLL_2700 (0<<29)
|
||||
#define PORT_CLK_SEL_LCPLL_1350 (1<<29)
|
||||
#define PORT_CLK_SEL_LCPLL_810 (2<<29)
|
||||
#define PORT_CLK_SEL_SPLL (3<<29)
|
||||
#define PORT_CLK_SEL_SPLL (3<<29)
|
||||
#define PORT_CLK_SEL_WRPLL1 (4<<29)
|
||||
#define PORT_CLK_SEL_WRPLL2 (5<<29)
|
||||
|
||||
/* Pipe clock selection */
|
||||
#define PIPE_CLK_SEL_A 0x46140
|
||||
#define PIPE_CLK_SEL_B 0x46144
|
||||
#define PIPE_CLK_SEL(pipe) _PIPE(pipe, \
|
||||
PIPE_CLK_SEL_A, \
|
||||
PIPE_CLK_SEL_B)
|
||||
#define PIPE_CLK_SEL(pipe) _PIPE(pipe, PIPE_CLK_SEL_A, PIPE_CLK_SEL_B)
|
||||
/* For each pipe, we need to select the corresponding port clock */
|
||||
#define PIPE_CLK_SEL_DISABLED (0x0<<29)
|
||||
#define PIPE_CLK_SEL_PORT(x) ((x+1)<<29)
|
||||
#define PIPE_CLK_SEL_DISABLED (0x0<<29)
|
||||
#define PIPE_CLK_SEL_PORT(x) ((x+1)<<29)
|
||||
|
||||
/* LCPLL Control */
|
||||
#define LCPLL_CTL 0x130040
|
||||
#define LCPLL_CTL 0x130040
|
||||
#define LCPLL_PLL_DISABLE (1<<31)
|
||||
#define LCPLL_PLL_LOCK (1<<30)
|
||||
#define LCPLL_CD_CLOCK_DISABLE (1<<25)
|
||||
#define LCPLL_CD_CLOCK_DISABLE (1<<25)
|
||||
#define LCPLL_CD2X_CLOCK_DISABLE (1<<23)
|
||||
|
||||
/* Pipe WM_LINETIME - watermark line time */
|
||||
#define PIPE_WM_LINETIME_A 0x45270
|
||||
#define PIPE_WM_LINETIME_B 0x45274
|
||||
#define PIPE_WM_LINETIME(pipe) _PIPE(pipe, \
|
||||
PIPE_WM_LINETIME_A, \
|
||||
PIPE_WM_LINETIME_B)
|
||||
#define PIPE_WM_LINETIME_MASK (0x1ff)
|
||||
#define PIPE_WM_LINETIME_TIME(x) ((x))
|
||||
#define PIPE_WM_LINETIME(pipe) _PIPE(pipe, PIPE_WM_LINETIME_A, \
|
||||
PIPE_WM_LINETIME_B)
|
||||
#define PIPE_WM_LINETIME_MASK (0x1ff)
|
||||
#define PIPE_WM_LINETIME_TIME(x) ((x))
|
||||
#define PIPE_WM_LINETIME_IPS_LINETIME_MASK (0x1ff<<16)
|
||||
#define PIPE_WM_LINETIME_IPS_LINETIME(x) ((x)<<16)
|
||||
#define PIPE_WM_LINETIME_IPS_LINETIME(x) ((x)<<16)
|
||||
|
||||
/* SFUSE_STRAP */
|
||||
#define SFUSE_STRAP 0xc2014
|
||||
#define SFUSE_STRAP 0xc2014
|
||||
#define SFUSE_STRAP_DDIB_DETECTED (1<<2)
|
||||
#define SFUSE_STRAP_DDIC_DETECTED (1<<1)
|
||||
#define SFUSE_STRAP_DDID_DETECTED (1<<0)
|
||||
|
|
|
@ -46,32 +46,32 @@ static u32 calc_residency(struct drm_device *dev, const u32 reg)
|
|||
}
|
||||
|
||||
static ssize_t
|
||||
show_rc6_mask(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
show_rc6_mask(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
|
||||
struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
|
||||
return snprintf(buf, PAGE_SIZE, "%x", intel_enable_rc6(dminor->dev));
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_rc6_ms(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
show_rc6_ms(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
|
||||
struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
|
||||
u32 rc6_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6);
|
||||
return snprintf(buf, PAGE_SIZE, "%u", rc6_residency);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_rc6p_ms(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
show_rc6p_ms(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
|
||||
struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
|
||||
u32 rc6p_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6p);
|
||||
return snprintf(buf, PAGE_SIZE, "%u", rc6p_residency);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
show_rc6pp_ms(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
show_rc6pp_ms(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *dminor = container_of(dev, struct drm_minor, kdev);
|
||||
struct drm_minor *dminor = container_of(kdev, struct drm_minor, kdev);
|
||||
u32 rc6pp_residency = calc_residency(dminor->dev, GEN6_GT_GFX_RC6pp);
|
||||
return snprintf(buf, PAGE_SIZE, "%u", rc6pp_residency);
|
||||
}
|
||||
|
@ -93,6 +93,7 @@ static struct attribute_group rc6_attr_group = {
|
|||
.name = power_group_name,
|
||||
.attrs = rc6_attrs
|
||||
};
|
||||
#endif
|
||||
|
||||
static int l3_access_valid(struct drm_device *dev, loff_t offset)
|
||||
{
|
||||
|
@ -202,37 +203,214 @@ static struct bin_attribute dpf_attrs = {
|
|||
.mmap = NULL
|
||||
};
|
||||
|
||||
static ssize_t gt_cur_freq_mhz_show(struct device *kdev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dev_priv->rps.cur_delay * GT_FREQUENCY_MULTIPLIER;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d", ret);
|
||||
}
|
||||
|
||||
static ssize_t gt_max_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dev_priv->rps.max_delay * GT_FREQUENCY_MULTIPLIER;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d", ret);
|
||||
}
|
||||
|
||||
static ssize_t gt_max_freq_mhz_store(struct device *kdev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 val, rp_state_cap, hw_max, hw_min;
|
||||
ssize_t ret;
|
||||
|
||||
ret = kstrtou32(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val /= GT_FREQUENCY_MULTIPLIER;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
||||
hw_max = (rp_state_cap & 0xff);
|
||||
hw_min = ((rp_state_cap & 0xff0000) >> 16);
|
||||
|
||||
if (val < hw_min || val > hw_max || val < dev_priv->rps.min_delay) {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev_priv->rps.cur_delay > val)
|
||||
gen6_set_rps(dev_priv->dev, val);
|
||||
|
||||
dev_priv->rps.max_delay = val;
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static ssize_t gt_min_freq_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
ret = i915_mutex_lock_interruptible(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = dev_priv->rps.min_delay * GT_FREQUENCY_MULTIPLIER;
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return snprintf(buf, PAGE_SIZE, "%d", ret);
|
||||
}
|
||||
|
||||
static ssize_t gt_min_freq_mhz_store(struct device *kdev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 val, rp_state_cap, hw_max, hw_min;
|
||||
ssize_t ret;
|
||||
|
||||
ret = kstrtou32(buf, 0, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val /= GT_FREQUENCY_MULTIPLIER;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
||||
hw_max = (rp_state_cap & 0xff);
|
||||
hw_min = ((rp_state_cap & 0xff0000) >> 16);
|
||||
|
||||
if (val < hw_min || val > hw_max || val > dev_priv->rps.max_delay) {
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (dev_priv->rps.cur_delay < val)
|
||||
gen6_set_rps(dev_priv->dev, val);
|
||||
|
||||
dev_priv->rps.min_delay = val;
|
||||
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
return count;
|
||||
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(gt_cur_freq_mhz, S_IRUGO, gt_cur_freq_mhz_show, NULL);
|
||||
static DEVICE_ATTR(gt_max_freq_mhz, S_IRUGO | S_IWUSR, gt_max_freq_mhz_show, gt_max_freq_mhz_store);
|
||||
static DEVICE_ATTR(gt_min_freq_mhz, S_IRUGO | S_IWUSR, gt_min_freq_mhz_show, gt_min_freq_mhz_store);
|
||||
|
||||
|
||||
static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf);
|
||||
static DEVICE_ATTR(gt_RP0_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
|
||||
static DEVICE_ATTR(gt_RP1_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
|
||||
static DEVICE_ATTR(gt_RPn_freq_mhz, S_IRUGO, gt_rp_mhz_show, NULL);
|
||||
|
||||
/* For now we have a static number of RP states */
|
||||
static ssize_t gt_rp_mhz_show(struct device *kdev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct drm_minor *minor = container_of(kdev, struct drm_minor, kdev);
|
||||
struct drm_device *dev = minor->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 val, rp_state_cap;
|
||||
ssize_t ret;
|
||||
|
||||
ret = mutex_lock_interruptible(&dev->struct_mutex);
|
||||
if (ret)
|
||||
return ret;
|
||||
rp_state_cap = I915_READ(GEN6_RP_STATE_CAP);
|
||||
mutex_unlock(&dev->struct_mutex);
|
||||
|
||||
if (attr == &dev_attr_gt_RP0_freq_mhz) {
|
||||
val = ((rp_state_cap & 0x0000ff) >> 0) * GT_FREQUENCY_MULTIPLIER;
|
||||
} else if (attr == &dev_attr_gt_RP1_freq_mhz) {
|
||||
val = ((rp_state_cap & 0x00ff00) >> 8) * GT_FREQUENCY_MULTIPLIER;
|
||||
} else if (attr == &dev_attr_gt_RPn_freq_mhz) {
|
||||
val = ((rp_state_cap & 0xff0000) >> 16) * GT_FREQUENCY_MULTIPLIER;
|
||||
} else {
|
||||
BUG();
|
||||
}
|
||||
return snprintf(buf, PAGE_SIZE, "%d", val);
|
||||
}
|
||||
|
||||
static const struct attribute *gen6_attrs[] = {
|
||||
&dev_attr_gt_cur_freq_mhz.attr,
|
||||
&dev_attr_gt_max_freq_mhz.attr,
|
||||
&dev_attr_gt_min_freq_mhz.attr,
|
||||
&dev_attr_gt_RP0_freq_mhz.attr,
|
||||
&dev_attr_gt_RP1_freq_mhz.attr,
|
||||
&dev_attr_gt_RPn_freq_mhz.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
void i915_setup_sysfs(struct drm_device *dev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
ret = sysfs_merge_group(&dev->primary->kdev.kobj,
|
||||
&rc6_attr_group);
|
||||
if (ret)
|
||||
DRM_ERROR("RC6 residency sysfs setup failed\n");
|
||||
}
|
||||
|
||||
if (IS_IVYBRIDGE(dev)) {
|
||||
#endif
|
||||
if (HAS_L3_GPU_CACHE(dev)) {
|
||||
ret = device_create_bin_file(&dev->primary->kdev, &dpf_attrs);
|
||||
if (ret)
|
||||
DRM_ERROR("l3 parity sysfs setup failed\n");
|
||||
}
|
||||
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
ret = sysfs_create_files(&dev->primary->kdev.kobj, gen6_attrs);
|
||||
if (ret)
|
||||
DRM_ERROR("gen6 sysfs setup failed\n");
|
||||
}
|
||||
}
|
||||
|
||||
void i915_teardown_sysfs(struct drm_device *dev)
|
||||
{
|
||||
sysfs_remove_files(&dev->primary->kdev.kobj, gen6_attrs);
|
||||
device_remove_bin_file(&dev->primary->kdev, &dpf_attrs);
|
||||
#ifdef CONFIG_PM
|
||||
sysfs_unmerge_group(&dev->primary->kdev.kobj, &rc6_attr_group);
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
void i915_setup_sysfs(struct drm_device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
void i915_teardown_sysfs(struct drm_device *dev)
|
||||
{
|
||||
return;
|
||||
}
|
||||
#endif /* CONFIG_PM */
|
||||
|
|
|
@ -214,22 +214,18 @@ TRACE_EVENT(i915_gem_evict,
|
|||
);
|
||||
|
||||
TRACE_EVENT(i915_gem_evict_everything,
|
||||
TP_PROTO(struct drm_device *dev, bool purgeable),
|
||||
TP_ARGS(dev, purgeable),
|
||||
TP_PROTO(struct drm_device *dev),
|
||||
TP_ARGS(dev),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, dev)
|
||||
__field(bool, purgeable)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->dev = dev->primary->index;
|
||||
__entry->purgeable = purgeable;
|
||||
),
|
||||
|
||||
TP_printk("dev=%d%s",
|
||||
__entry->dev,
|
||||
__entry->purgeable ? ", purgeable only" : "")
|
||||
TP_printk("dev=%d", __entry->dev)
|
||||
);
|
||||
|
||||
TRACE_EVENT(i915_gem_ring_dispatch,
|
||||
|
@ -434,6 +430,21 @@ TRACE_EVENT(i915_reg_rw,
|
|||
(u32)(__entry->val >> 32))
|
||||
);
|
||||
|
||||
TRACE_EVENT(intel_gpu_freq_change,
|
||||
TP_PROTO(u32 freq),
|
||||
TP_ARGS(freq),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
__field(u32, freq)
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
__entry->freq = freq;
|
||||
),
|
||||
|
||||
TP_printk("new_freq=%u", __entry->freq)
|
||||
);
|
||||
|
||||
#endif /* _I915_TRACE_H_ */
|
||||
|
||||
/* This part must be outside protection */
|
||||
|
|
|
@ -46,6 +46,7 @@
|
|||
struct intel_crt {
|
||||
struct intel_encoder base;
|
||||
bool force_hotplug_required;
|
||||
u32 adpa_reg;
|
||||
};
|
||||
|
||||
static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
|
||||
|
@ -54,41 +55,67 @@ static struct intel_crt *intel_attached_crt(struct drm_connector *connector)
|
|||
struct intel_crt, base);
|
||||
}
|
||||
|
||||
static void pch_crt_dpms(struct drm_encoder *encoder, int mode)
|
||||
static struct intel_crt *intel_encoder_to_crt(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 temp;
|
||||
|
||||
temp = I915_READ(PCH_ADPA);
|
||||
temp &= ~ADPA_DAC_ENABLE;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
temp |= ADPA_DAC_ENABLE;
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
/* Just leave port enable cleared */
|
||||
break;
|
||||
}
|
||||
|
||||
I915_WRITE(PCH_ADPA, temp);
|
||||
return container_of(encoder, struct intel_crt, base);
|
||||
}
|
||||
|
||||
static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
|
||||
static bool intel_crt_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_crt *crt = intel_encoder_to_crt(encoder);
|
||||
u32 tmp;
|
||||
|
||||
tmp = I915_READ(crt->adpa_reg);
|
||||
|
||||
if (!(tmp & ADPA_DAC_ENABLE))
|
||||
return false;
|
||||
|
||||
if (HAS_PCH_CPT(dev))
|
||||
*pipe = PORT_TO_PIPE_CPT(tmp);
|
||||
else
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void intel_disable_crt(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
|
||||
struct intel_crt *crt = intel_encoder_to_crt(encoder);
|
||||
u32 temp;
|
||||
|
||||
temp = I915_READ(ADPA);
|
||||
temp = I915_READ(crt->adpa_reg);
|
||||
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
|
||||
temp &= ~ADPA_DAC_ENABLE;
|
||||
I915_WRITE(crt->adpa_reg, temp);
|
||||
}
|
||||
|
||||
if (IS_VALLEYVIEW(dev) && mode != DRM_MODE_DPMS_ON)
|
||||
mode = DRM_MODE_DPMS_OFF;
|
||||
static void intel_enable_crt(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
|
||||
struct intel_crt *crt = intel_encoder_to_crt(encoder);
|
||||
u32 temp;
|
||||
|
||||
temp = I915_READ(crt->adpa_reg);
|
||||
temp |= ADPA_DAC_ENABLE;
|
||||
I915_WRITE(crt->adpa_reg, temp);
|
||||
}
|
||||
|
||||
/* Note: The caller is required to filter out dpms modes not supported by the
|
||||
* platform. */
|
||||
static void intel_crt_set_dpms(struct intel_encoder *encoder, int mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_crt *crt = intel_encoder_to_crt(encoder);
|
||||
u32 temp;
|
||||
|
||||
temp = I915_READ(crt->adpa_reg);
|
||||
temp &= ~(ADPA_HSYNC_CNTL_DISABLE | ADPA_VSYNC_CNTL_DISABLE);
|
||||
temp &= ~ADPA_DAC_ENABLE;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
|
@ -105,7 +132,51 @@ static void gmch_crt_dpms(struct drm_encoder *encoder, int mode)
|
|||
break;
|
||||
}
|
||||
|
||||
I915_WRITE(ADPA, temp);
|
||||
I915_WRITE(crt->adpa_reg, temp);
|
||||
}
|
||||
|
||||
static void intel_crt_dpms(struct drm_connector *connector, int mode)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct intel_encoder *encoder = intel_attached_encoder(connector);
|
||||
struct drm_crtc *crtc;
|
||||
int old_dpms;
|
||||
|
||||
/* PCH platforms and VLV only support on/off. */
|
||||
if (INTEL_INFO(dev)->gen < 5 && mode != DRM_MODE_DPMS_ON)
|
||||
mode = DRM_MODE_DPMS_OFF;
|
||||
|
||||
if (mode == connector->dpms)
|
||||
return;
|
||||
|
||||
old_dpms = connector->dpms;
|
||||
connector->dpms = mode;
|
||||
|
||||
/* Only need to change hw state when actually enabled */
|
||||
crtc = encoder->base.crtc;
|
||||
if (!crtc) {
|
||||
encoder->connectors_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/* We need the pipe to run for anything but OFF. */
|
||||
if (mode == DRM_MODE_DPMS_OFF)
|
||||
encoder->connectors_active = false;
|
||||
else
|
||||
encoder->connectors_active = true;
|
||||
|
||||
if (mode < old_dpms) {
|
||||
/* From off to on, enable the pipe first. */
|
||||
intel_crtc_update_dpms(crtc);
|
||||
|
||||
intel_crt_set_dpms(encoder, mode);
|
||||
} else {
|
||||
intel_crt_set_dpms(encoder, mode);
|
||||
|
||||
intel_crtc_update_dpms(crtc);
|
||||
}
|
||||
|
||||
intel_modeset_check_state(connector->dev);
|
||||
}
|
||||
|
||||
static int intel_crt_mode_valid(struct drm_connector *connector,
|
||||
|
@ -144,19 +215,15 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
|
|||
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
struct intel_crt *crt =
|
||||
intel_encoder_to_crt(to_intel_encoder(encoder));
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int dpll_md_reg;
|
||||
u32 adpa, dpll_md;
|
||||
u32 adpa_reg;
|
||||
|
||||
dpll_md_reg = DPLL_MD(intel_crtc->pipe);
|
||||
|
||||
if (HAS_PCH_SPLIT(dev))
|
||||
adpa_reg = PCH_ADPA;
|
||||
else
|
||||
adpa_reg = ADPA;
|
||||
|
||||
/*
|
||||
* Disable separate mode multiplier used when cloning SDVO to CRT
|
||||
* XXX this needs to be adjusted when we really are cloning
|
||||
|
@ -184,7 +251,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
|
|||
if (!HAS_PCH_SPLIT(dev))
|
||||
I915_WRITE(BCLRPAT(intel_crtc->pipe), 0);
|
||||
|
||||
I915_WRITE(adpa_reg, adpa);
|
||||
I915_WRITE(crt->adpa_reg, adpa);
|
||||
}
|
||||
|
||||
static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
|
||||
|
@ -544,14 +611,12 @@ intel_crt_detect(struct drm_connector *connector, bool force)
|
|||
return connector->status;
|
||||
|
||||
/* for pre-945g platforms use load detect */
|
||||
if (intel_get_load_detect_pipe(&crt->base, connector, NULL,
|
||||
&tmp)) {
|
||||
if (intel_get_load_detect_pipe(connector, NULL, &tmp)) {
|
||||
if (intel_crt_detect_ddc(connector))
|
||||
status = connector_status_connected;
|
||||
else
|
||||
status = intel_crt_load_detect(crt);
|
||||
intel_release_load_detect_pipe(&crt->base, connector,
|
||||
&tmp);
|
||||
intel_release_load_detect_pipe(connector, &tmp);
|
||||
} else
|
||||
status = connector_status_unknown;
|
||||
|
||||
|
@ -602,25 +667,15 @@ static void intel_crt_reset(struct drm_connector *connector)
|
|||
* Routines for controlling stuff on the analog port
|
||||
*/
|
||||
|
||||
static const struct drm_encoder_helper_funcs pch_encoder_funcs = {
|
||||
static const struct drm_encoder_helper_funcs crt_encoder_funcs = {
|
||||
.mode_fixup = intel_crt_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.commit = intel_encoder_commit,
|
||||
.mode_set = intel_crt_mode_set,
|
||||
.dpms = pch_crt_dpms,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs gmch_encoder_funcs = {
|
||||
.mode_fixup = intel_crt_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.commit = intel_encoder_commit,
|
||||
.mode_set = intel_crt_mode_set,
|
||||
.dpms = gmch_crt_dpms,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_crt_connector_funcs = {
|
||||
.reset = intel_crt_reset,
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_crt_dpms,
|
||||
.detect = intel_crt_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.destroy = intel_crt_destroy,
|
||||
|
@ -661,7 +716,6 @@ void intel_crt_init(struct drm_device *dev)
|
|||
struct intel_crt *crt;
|
||||
struct intel_connector *intel_connector;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
const struct drm_encoder_helper_funcs *encoder_helper_funcs;
|
||||
|
||||
/* Skip machines without VGA that falsely report hotplug events */
|
||||
if (dmi_check_system(intel_no_crt))
|
||||
|
@ -687,13 +741,11 @@ void intel_crt_init(struct drm_device *dev)
|
|||
intel_connector_attach_encoder(intel_connector, &crt->base);
|
||||
|
||||
crt->base.type = INTEL_OUTPUT_ANALOG;
|
||||
crt->base.clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT |
|
||||
1 << INTEL_ANALOG_CLONE_BIT |
|
||||
1 << INTEL_SDVO_LVDS_CLONE_BIT);
|
||||
crt->base.cloneable = true;
|
||||
if (IS_HASWELL(dev))
|
||||
crt->base.crtc_mask = (1 << 0);
|
||||
else
|
||||
crt->base.crtc_mask = (1 << 0) | (1 << 1);
|
||||
crt->base.crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
|
||||
|
||||
if (IS_GEN2(dev))
|
||||
connector->interlace_allowed = 0;
|
||||
|
@ -702,11 +754,18 @@ void intel_crt_init(struct drm_device *dev)
|
|||
connector->doublescan_allowed = 0;
|
||||
|
||||
if (HAS_PCH_SPLIT(dev))
|
||||
encoder_helper_funcs = &pch_encoder_funcs;
|
||||
crt->adpa_reg = PCH_ADPA;
|
||||
else if (IS_VALLEYVIEW(dev))
|
||||
crt->adpa_reg = VLV_ADPA;
|
||||
else
|
||||
encoder_helper_funcs = &gmch_encoder_funcs;
|
||||
crt->adpa_reg = ADPA;
|
||||
|
||||
drm_encoder_helper_add(&crt->base.base, encoder_helper_funcs);
|
||||
crt->base.disable = intel_disable_crt;
|
||||
crt->base.enable = intel_enable_crt;
|
||||
crt->base.get_hw_state = intel_crt_get_hw_state;
|
||||
intel_connector->get_hw_state = intel_connector_get_hw_state;
|
||||
|
||||
drm_encoder_helper_add(&crt->base.base, &crt_encoder_funcs);
|
||||
drm_connector_helper_add(connector, &intel_crt_connector_helper_funcs);
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
|
|
@ -250,7 +250,7 @@ void intel_ddi_init(struct drm_device *dev, enum port port)
|
|||
case PORT_B:
|
||||
case PORT_C:
|
||||
case PORT_D:
|
||||
intel_hdmi_init(dev, DDI_BUF_CTL(port));
|
||||
intel_hdmi_init(dev, DDI_BUF_CTL(port), port);
|
||||
break;
|
||||
default:
|
||||
DRM_DEBUG_DRIVER("No handlers defined for port %d, skipping DDI initialization\n",
|
||||
|
@ -267,7 +267,8 @@ struct wrpll_tmds_clock {
|
|||
u16 r2; /* Reference divider */
|
||||
};
|
||||
|
||||
/* Table of matching values for WRPLL clocks programming for each frequency */
|
||||
/* Table of matching values for WRPLL clocks programming for each frequency.
|
||||
* The code assumes this table is sorted. */
|
||||
static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
|
||||
{19750, 38, 25, 18},
|
||||
{20000, 48, 32, 18},
|
||||
|
@ -277,7 +278,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
|
|||
{23000, 36, 23, 15},
|
||||
{23500, 40, 40, 23},
|
||||
{23750, 26, 16, 14},
|
||||
{23750, 26, 16, 14},
|
||||
{24000, 36, 24, 15},
|
||||
{25000, 36, 25, 15},
|
||||
{25175, 26, 40, 33},
|
||||
|
@ -437,7 +437,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
|
|||
{108000, 8, 24, 15},
|
||||
{108108, 8, 173, 108},
|
||||
{109000, 6, 23, 19},
|
||||
{109000, 6, 23, 19},
|
||||
{110000, 6, 22, 18},
|
||||
{110013, 6, 22, 18},
|
||||
{110250, 8, 49, 30},
|
||||
|
@ -614,7 +613,6 @@ static const struct wrpll_tmds_clock wrpll_tmds_clock_table[] = {
|
|||
{218250, 4, 42, 26},
|
||||
{218750, 4, 34, 21},
|
||||
{219000, 4, 47, 29},
|
||||
{219000, 4, 47, 29},
|
||||
{220000, 4, 44, 27},
|
||||
{220640, 4, 49, 30},
|
||||
{220750, 4, 36, 22},
|
||||
|
@ -658,7 +656,7 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
|
|||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
|
||||
int port = intel_hdmi->ddi_port;
|
||||
int pipe = intel_crtc->pipe;
|
||||
int p, n2, r2, valid=0;
|
||||
int p, n2, r2;
|
||||
u32 temp, i;
|
||||
|
||||
/* On Haswell, we need to enable the clocks and prepare DDI function to
|
||||
|
@ -666,26 +664,23 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
|
|||
*/
|
||||
DRM_DEBUG_KMS("Preparing HDMI DDI mode for Haswell on port %c, pipe %c\n", port_name(port), pipe_name(pipe));
|
||||
|
||||
for (i=0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++) {
|
||||
if (crtc->mode.clock == wrpll_tmds_clock_table[i].clock) {
|
||||
p = wrpll_tmds_clock_table[i].p;
|
||||
n2 = wrpll_tmds_clock_table[i].n2;
|
||||
r2 = wrpll_tmds_clock_table[i].r2;
|
||||
|
||||
DRM_DEBUG_KMS("WR PLL clock: found settings for %dKHz refresh rate: p=%d, n2=%d, r2=%d\n",
|
||||
crtc->mode.clock,
|
||||
p, n2, r2);
|
||||
|
||||
valid = 1;
|
||||
for (i = 0; i < ARRAY_SIZE(wrpll_tmds_clock_table); i++)
|
||||
if (crtc->mode.clock <= wrpll_tmds_clock_table[i].clock)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!valid) {
|
||||
DRM_ERROR("Unable to find WR PLL clock settings for %dKHz refresh rate\n",
|
||||
crtc->mode.clock);
|
||||
return;
|
||||
}
|
||||
if (i == ARRAY_SIZE(wrpll_tmds_clock_table))
|
||||
i--;
|
||||
|
||||
p = wrpll_tmds_clock_table[i].p;
|
||||
n2 = wrpll_tmds_clock_table[i].n2;
|
||||
r2 = wrpll_tmds_clock_table[i].r2;
|
||||
|
||||
if (wrpll_tmds_clock_table[i].clock != crtc->mode.clock)
|
||||
DRM_INFO("WR PLL: using settings for %dKHz on %dKHz mode\n",
|
||||
wrpll_tmds_clock_table[i].clock, crtc->mode.clock);
|
||||
|
||||
DRM_DEBUG_KMS("WR PLL: %dKHz refresh rate with p=%d, n2=%d r2=%d\n",
|
||||
crtc->mode.clock, p, n2, r2);
|
||||
|
||||
/* Enable LCPLL if disabled */
|
||||
temp = I915_READ(LCPLL_CTL);
|
||||
|
@ -718,46 +713,107 @@ void intel_ddi_mode_set(struct drm_encoder *encoder,
|
|||
/* Proper support for digital audio needs a new logic and a new set
|
||||
* of registers, so we leave it for future patch bombing.
|
||||
*/
|
||||
DRM_DEBUG_DRIVER("HDMI audio on pipe %c not yet supported on DDI\n",
|
||||
DRM_DEBUG_DRIVER("HDMI audio on pipe %c on DDI\n",
|
||||
pipe_name(intel_crtc->pipe));
|
||||
|
||||
/* write eld */
|
||||
DRM_DEBUG_DRIVER("HDMI audio: write eld information\n");
|
||||
intel_write_eld(encoder, adjusted_mode);
|
||||
}
|
||||
|
||||
/* Enable PIPE_DDI_FUNC_CTL for the pipe to work in HDMI mode */
|
||||
temp = I915_READ(DDI_FUNC_CTL(pipe));
|
||||
temp &= ~PIPE_DDI_PORT_MASK;
|
||||
temp &= ~PIPE_DDI_BPC_12;
|
||||
temp |= PIPE_DDI_SELECT_PORT(port) |
|
||||
PIPE_DDI_MODE_SELECT_HDMI |
|
||||
((intel_crtc->bpp > 24) ?
|
||||
PIPE_DDI_BPC_12 :
|
||||
PIPE_DDI_BPC_8) |
|
||||
PIPE_DDI_FUNC_ENABLE;
|
||||
temp = PIPE_DDI_FUNC_ENABLE | PIPE_DDI_SELECT_PORT(port);
|
||||
|
||||
switch (intel_crtc->bpp) {
|
||||
case 18:
|
||||
temp |= PIPE_DDI_BPC_6;
|
||||
break;
|
||||
case 24:
|
||||
temp |= PIPE_DDI_BPC_8;
|
||||
break;
|
||||
case 30:
|
||||
temp |= PIPE_DDI_BPC_10;
|
||||
break;
|
||||
case 36:
|
||||
temp |= PIPE_DDI_BPC_12;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "%d bpp unsupported by pipe DDI function\n",
|
||||
intel_crtc->bpp);
|
||||
}
|
||||
|
||||
if (intel_hdmi->has_hdmi_sink)
|
||||
temp |= PIPE_DDI_MODE_SELECT_HDMI;
|
||||
else
|
||||
temp |= PIPE_DDI_MODE_SELECT_DVI;
|
||||
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
|
||||
temp |= PIPE_DDI_PVSYNC;
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
|
||||
temp |= PIPE_DDI_PHSYNC;
|
||||
|
||||
I915_WRITE(DDI_FUNC_CTL(pipe), temp);
|
||||
|
||||
intel_hdmi->set_infoframes(encoder, adjusted_mode);
|
||||
}
|
||||
|
||||
void intel_ddi_dpms(struct drm_encoder *encoder, int mode)
|
||||
bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
|
||||
u32 tmp;
|
||||
int i;
|
||||
|
||||
tmp = I915_READ(DDI_BUF_CTL(intel_hdmi->ddi_port));
|
||||
|
||||
if (!(tmp & DDI_BUF_CTL_ENABLE))
|
||||
return false;
|
||||
|
||||
for_each_pipe(i) {
|
||||
tmp = I915_READ(DDI_FUNC_CTL(i));
|
||||
|
||||
if ((tmp & PIPE_DDI_PORT_MASK)
|
||||
== PIPE_DDI_SELECT_PORT(intel_hdmi->ddi_port)) {
|
||||
*pipe = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("No pipe for ddi port %i found\n", intel_hdmi->ddi_port);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void intel_enable_ddi(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
|
||||
int port = intel_hdmi->ddi_port;
|
||||
u32 temp;
|
||||
|
||||
temp = I915_READ(DDI_BUF_CTL(port));
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON) {
|
||||
temp &= ~DDI_BUF_CTL_ENABLE;
|
||||
} else {
|
||||
temp |= DDI_BUF_CTL_ENABLE;
|
||||
}
|
||||
temp |= DDI_BUF_CTL_ENABLE;
|
||||
|
||||
/* Enable DDI_BUF_CTL. In HDMI/DVI mode, the port width,
|
||||
* and swing/emphasis values are ignored so nothing special needs
|
||||
* to be done besides enabling the port.
|
||||
*/
|
||||
I915_WRITE(DDI_BUF_CTL(port),
|
||||
temp);
|
||||
I915_WRITE(DDI_BUF_CTL(port), temp);
|
||||
}
|
||||
|
||||
void intel_disable_ddi(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
|
||||
int port = intel_hdmi->ddi_port;
|
||||
u32 temp;
|
||||
|
||||
temp = I915_READ(DDI_BUF_CTL(port));
|
||||
temp &= ~DDI_BUF_CTL_ENABLE;
|
||||
|
||||
I915_WRITE(DDI_BUF_CTL(port), temp);
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -35,42 +35,10 @@
|
|||
#include "intel_drv.h"
|
||||
#include <drm/i915_drm.h>
|
||||
#include "i915_drv.h"
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
#define DP_RECEIVER_CAP_SIZE 0xf
|
||||
#define DP_LINK_STATUS_SIZE 6
|
||||
#define DP_LINK_CHECK_TIMEOUT (10 * 1000)
|
||||
|
||||
#define DP_LINK_CONFIGURATION_SIZE 9
|
||||
|
||||
struct intel_dp {
|
||||
struct intel_encoder base;
|
||||
uint32_t output_reg;
|
||||
uint32_t DP;
|
||||
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
|
||||
bool has_audio;
|
||||
enum hdmi_force_audio force_audio;
|
||||
uint32_t color_range;
|
||||
int dpms_mode;
|
||||
uint8_t link_bw;
|
||||
uint8_t lane_count;
|
||||
uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_algo_dp_aux_data algo;
|
||||
bool is_pch_edp;
|
||||
uint8_t train_set[4];
|
||||
int panel_power_up_delay;
|
||||
int panel_power_down_delay;
|
||||
int panel_power_cycle_delay;
|
||||
int backlight_on_delay;
|
||||
int backlight_off_delay;
|
||||
struct drm_display_mode *panel_fixed_mode; /* for eDP */
|
||||
struct delayed_work panel_vdd_work;
|
||||
bool want_panel_vdd;
|
||||
struct edid *edid; /* cached EDID for eDP */
|
||||
int edid_mode_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* is_edp - is the given port attached to an eDP panel (either CPU or PCH)
|
||||
* @intel_dp: DP struct
|
||||
|
@ -839,9 +807,6 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
|||
}
|
||||
}
|
||||
|
||||
static void ironlake_edp_pll_on(struct drm_encoder *encoder);
|
||||
static void ironlake_edp_pll_off(struct drm_encoder *encoder);
|
||||
|
||||
static void
|
||||
intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
|
@ -852,14 +817,6 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|||
struct drm_crtc *crtc = intel_dp->base.base.crtc;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
|
||||
|
||||
/* Turn on the eDP PLL if needed */
|
||||
if (is_edp(intel_dp)) {
|
||||
if (!is_pch_edp(intel_dp))
|
||||
ironlake_edp_pll_on(encoder);
|
||||
else
|
||||
ironlake_edp_pll_off(encoder);
|
||||
}
|
||||
|
||||
/*
|
||||
* There are four kinds of DP registers:
|
||||
*
|
||||
|
@ -881,10 +838,8 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|||
* supposed to be read-only.
|
||||
*/
|
||||
intel_dp->DP = I915_READ(intel_dp->output_reg) & DP_DETECTED;
|
||||
intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
|
||||
|
||||
/* Handle DP bits in common between all three register formats */
|
||||
|
||||
intel_dp->DP |= DP_VOLTAGE_0_4 | DP_PRE_EMPHASIS_0;
|
||||
|
||||
switch (intel_dp->lane_count) {
|
||||
|
@ -931,7 +886,6 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|||
intel_dp->DP |= intel_crtc->pipe << 29;
|
||||
|
||||
/* don't miss out required setting for eDP */
|
||||
intel_dp->DP |= DP_PLL_ENABLE;
|
||||
if (adjusted_mode->clock < 200000)
|
||||
intel_dp->DP |= DP_PLL_FREQ_160MHZ;
|
||||
else
|
||||
|
@ -953,7 +907,6 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
|||
|
||||
if (is_cpu_edp(intel_dp)) {
|
||||
/* don't miss out required setting for eDP */
|
||||
intel_dp->DP |= DP_PLL_ENABLE;
|
||||
if (adjusted_mode->clock < 200000)
|
||||
intel_dp->DP |= DP_PLL_FREQ_160MHZ;
|
||||
else
|
||||
|
@ -1224,27 +1177,49 @@ static void ironlake_edp_backlight_off(struct intel_dp *intel_dp)
|
|||
msleep(intel_dp->backlight_off_delay);
|
||||
}
|
||||
|
||||
static void ironlake_edp_pll_on(struct drm_encoder *encoder)
|
||||
static void ironlake_edp_pll_on(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_device *dev = intel_dp->base.base.dev;
|
||||
struct drm_crtc *crtc = intel_dp->base.base.crtc;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 dpa_ctl;
|
||||
|
||||
assert_pipe_disabled(dev_priv,
|
||||
to_intel_crtc(crtc)->pipe);
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
dpa_ctl = I915_READ(DP_A);
|
||||
dpa_ctl |= DP_PLL_ENABLE;
|
||||
I915_WRITE(DP_A, dpa_ctl);
|
||||
WARN(dpa_ctl & DP_PLL_ENABLE, "dp pll on, should be off\n");
|
||||
WARN(dpa_ctl & DP_PORT_EN, "dp port still on, should be off\n");
|
||||
|
||||
/* We don't adjust intel_dp->DP while tearing down the link, to
|
||||
* facilitate link retraining (e.g. after hotplug). Hence clear all
|
||||
* enable bits here to ensure that we don't enable too much. */
|
||||
intel_dp->DP &= ~(DP_PORT_EN | DP_AUDIO_OUTPUT_ENABLE);
|
||||
intel_dp->DP |= DP_PLL_ENABLE;
|
||||
I915_WRITE(DP_A, intel_dp->DP);
|
||||
POSTING_READ(DP_A);
|
||||
udelay(200);
|
||||
}
|
||||
|
||||
static void ironlake_edp_pll_off(struct drm_encoder *encoder)
|
||||
static void ironlake_edp_pll_off(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_device *dev = intel_dp->base.base.dev;
|
||||
struct drm_crtc *crtc = intel_dp->base.base.crtc;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 dpa_ctl;
|
||||
|
||||
assert_pipe_disabled(dev_priv,
|
||||
to_intel_crtc(crtc)->pipe);
|
||||
|
||||
dpa_ctl = I915_READ(DP_A);
|
||||
WARN((dpa_ctl & DP_PLL_ENABLE) == 0,
|
||||
"dp pll off, should be on\n");
|
||||
WARN(dpa_ctl & DP_PORT_EN, "dp port still on, should be off\n");
|
||||
|
||||
/* We can't rely on the value tracked for the DP register in
|
||||
* intel_dp->DP because link_down must not change that (otherwise link
|
||||
* re-training will fail. */
|
||||
dpa_ctl &= ~DP_PLL_ENABLE;
|
||||
I915_WRITE(DP_A, dpa_ctl);
|
||||
POSTING_READ(DP_A);
|
||||
|
@ -1281,10 +1256,57 @@ static void intel_dp_sink_dpms(struct intel_dp *intel_dp, int mode)
|
|||
}
|
||||
}
|
||||
|
||||
static void intel_dp_prepare(struct drm_encoder *encoder)
|
||||
static bool intel_dp_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 tmp = I915_READ(intel_dp->output_reg);
|
||||
|
||||
if (!(tmp & DP_PORT_EN))
|
||||
return false;
|
||||
|
||||
if (is_cpu_edp(intel_dp) && IS_GEN7(dev)) {
|
||||
*pipe = PORT_TO_PIPE_CPT(tmp);
|
||||
} else if (!HAS_PCH_CPT(dev) || is_cpu_edp(intel_dp)) {
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
} else {
|
||||
u32 trans_sel;
|
||||
u32 trans_dp;
|
||||
int i;
|
||||
|
||||
switch (intel_dp->output_reg) {
|
||||
case PCH_DP_B:
|
||||
trans_sel = TRANS_DP_PORT_SEL_B;
|
||||
break;
|
||||
case PCH_DP_C:
|
||||
trans_sel = TRANS_DP_PORT_SEL_C;
|
||||
break;
|
||||
case PCH_DP_D:
|
||||
trans_sel = TRANS_DP_PORT_SEL_D;
|
||||
break;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
for_each_pipe(i) {
|
||||
trans_dp = I915_READ(TRANS_DP_CTL(i));
|
||||
if ((trans_dp & TRANS_DP_PORT_SEL_MASK) == trans_sel) {
|
||||
*pipe = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DRM_DEBUG_KMS("No pipe for dp port 0x%x found\n", intel_dp->output_reg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void intel_disable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
|
||||
/* Make sure the panel is off before trying to change the mode. But also
|
||||
* ensure that we have vdd while we switch off the panel. */
|
||||
|
@ -1292,14 +1314,31 @@ static void intel_dp_prepare(struct drm_encoder *encoder)
|
|||
ironlake_edp_backlight_off(intel_dp);
|
||||
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
|
||||
ironlake_edp_panel_off(intel_dp);
|
||||
intel_dp_link_down(intel_dp);
|
||||
|
||||
/* cpu edp my only be disable _after_ the cpu pipe/plane is disabled. */
|
||||
if (!is_cpu_edp(intel_dp))
|
||||
intel_dp_link_down(intel_dp);
|
||||
}
|
||||
|
||||
static void intel_dp_commit(struct drm_encoder *encoder)
|
||||
static void intel_post_disable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
|
||||
if (is_cpu_edp(intel_dp)) {
|
||||
intel_dp_link_down(intel_dp);
|
||||
ironlake_edp_pll_off(intel_dp);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_enable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
|
||||
|
||||
if (WARN_ON(dp_reg & DP_PORT_EN))
|
||||
return;
|
||||
|
||||
ironlake_edp_panel_vdd_on(intel_dp);
|
||||
intel_dp_sink_dpms(intel_dp, DRM_MODE_DPMS_ON);
|
||||
|
@ -1308,47 +1347,14 @@ static void intel_dp_commit(struct drm_encoder *encoder)
|
|||
ironlake_edp_panel_vdd_off(intel_dp, true);
|
||||
intel_dp_complete_link_train(intel_dp);
|
||||
ironlake_edp_backlight_on(intel_dp);
|
||||
|
||||
intel_dp->dpms_mode = DRM_MODE_DPMS_ON;
|
||||
|
||||
if (HAS_PCH_CPT(dev))
|
||||
intel_cpt_verify_modeset(dev, intel_crtc->pipe);
|
||||
}
|
||||
|
||||
static void
|
||||
intel_dp_dpms(struct drm_encoder *encoder, int mode)
|
||||
static void intel_pre_enable_dp(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t dp_reg = I915_READ(intel_dp->output_reg);
|
||||
struct intel_dp *intel_dp = enc_to_intel_dp(&encoder->base);
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON) {
|
||||
/* Switching the panel off requires vdd. */
|
||||
ironlake_edp_panel_vdd_on(intel_dp);
|
||||
ironlake_edp_backlight_off(intel_dp);
|
||||
intel_dp_sink_dpms(intel_dp, mode);
|
||||
ironlake_edp_panel_off(intel_dp);
|
||||
intel_dp_link_down(intel_dp);
|
||||
|
||||
if (is_cpu_edp(intel_dp))
|
||||
ironlake_edp_pll_off(encoder);
|
||||
} else {
|
||||
if (is_cpu_edp(intel_dp))
|
||||
ironlake_edp_pll_on(encoder);
|
||||
|
||||
ironlake_edp_panel_vdd_on(intel_dp);
|
||||
intel_dp_sink_dpms(intel_dp, mode);
|
||||
if (!(dp_reg & DP_PORT_EN)) {
|
||||
intel_dp_start_link_train(intel_dp);
|
||||
ironlake_edp_panel_on(intel_dp);
|
||||
ironlake_edp_panel_vdd_off(intel_dp, true);
|
||||
intel_dp_complete_link_train(intel_dp);
|
||||
} else
|
||||
ironlake_edp_panel_vdd_off(intel_dp, false);
|
||||
ironlake_edp_backlight_on(intel_dp);
|
||||
}
|
||||
intel_dp->dpms_mode = mode;
|
||||
if (is_cpu_edp(intel_dp))
|
||||
ironlake_edp_pll_on(intel_dp);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1667,6 +1673,45 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
|
|||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) {
|
||||
dp_reg_value &= ~DP_LINK_TRAIN_MASK_CPT;
|
||||
|
||||
switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
|
||||
case DP_TRAINING_PATTERN_DISABLE:
|
||||
dp_reg_value |= DP_LINK_TRAIN_OFF_CPT;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_1:
|
||||
dp_reg_value |= DP_LINK_TRAIN_PAT_1_CPT;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_2:
|
||||
dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_3:
|
||||
DRM_ERROR("DP training pattern 3 not supported\n");
|
||||
dp_reg_value |= DP_LINK_TRAIN_PAT_2_CPT;
|
||||
break;
|
||||
}
|
||||
|
||||
} else {
|
||||
dp_reg_value &= ~DP_LINK_TRAIN_MASK;
|
||||
|
||||
switch (dp_train_pat & DP_TRAINING_PATTERN_MASK) {
|
||||
case DP_TRAINING_PATTERN_DISABLE:
|
||||
dp_reg_value |= DP_LINK_TRAIN_OFF;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_1:
|
||||
dp_reg_value |= DP_LINK_TRAIN_PAT_1;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_2:
|
||||
dp_reg_value |= DP_LINK_TRAIN_PAT_2;
|
||||
break;
|
||||
case DP_TRAINING_PATTERN_3:
|
||||
DRM_ERROR("DP training pattern 3 not supported\n");
|
||||
dp_reg_value |= DP_LINK_TRAIN_PAT_2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
I915_WRITE(intel_dp->output_reg, dp_reg_value);
|
||||
POSTING_READ(intel_dp->output_reg);
|
||||
|
||||
|
@ -1674,12 +1719,15 @@ intel_dp_set_link_train(struct intel_dp *intel_dp,
|
|||
DP_TRAINING_PATTERN_SET,
|
||||
dp_train_pat);
|
||||
|
||||
ret = intel_dp_aux_native_write(intel_dp,
|
||||
DP_TRAINING_LANE0_SET,
|
||||
intel_dp->train_set,
|
||||
intel_dp->lane_count);
|
||||
if (ret != intel_dp->lane_count)
|
||||
return false;
|
||||
if ((dp_train_pat & DP_TRAINING_PATTERN_MASK) !=
|
||||
DP_TRAINING_PATTERN_DISABLE) {
|
||||
ret = intel_dp_aux_native_write(intel_dp,
|
||||
DP_TRAINING_LANE0_SET,
|
||||
intel_dp->train_set,
|
||||
intel_dp->lane_count);
|
||||
if (ret != intel_dp->lane_count)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1689,26 +1737,12 @@ static void
|
|||
intel_dp_start_link_train(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(intel_dp->base.base.crtc);
|
||||
int i;
|
||||
uint8_t voltage;
|
||||
bool clock_recovery = false;
|
||||
int voltage_tries, loop_tries;
|
||||
u32 reg;
|
||||
uint32_t DP = intel_dp->DP;
|
||||
|
||||
/*
|
||||
* On CPT we have to enable the port in training pattern 1, which
|
||||
* will happen below in intel_dp_set_link_train. Otherwise, enable
|
||||
* the port and wait for it to become active.
|
||||
*/
|
||||
if (!HAS_PCH_CPT(dev)) {
|
||||
I915_WRITE(intel_dp->output_reg, intel_dp->DP);
|
||||
POSTING_READ(intel_dp->output_reg);
|
||||
intel_wait_for_vblank(dev, intel_crtc->pipe);
|
||||
}
|
||||
|
||||
/* Write the link configuration data */
|
||||
intel_dp_aux_native_write(intel_dp, DP_LINK_BW_SET,
|
||||
intel_dp->link_configuration,
|
||||
|
@ -1716,10 +1750,6 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
|
|||
|
||||
DP |= DP_PORT_EN;
|
||||
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
|
||||
DP &= ~DP_LINK_TRAIN_MASK_CPT;
|
||||
else
|
||||
DP &= ~DP_LINK_TRAIN_MASK;
|
||||
memset(intel_dp->train_set, 0, 4);
|
||||
voltage = 0xff;
|
||||
voltage_tries = 0;
|
||||
|
@ -1743,12 +1773,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)
|
|||
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
|
||||
}
|
||||
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
|
||||
reg = DP | DP_LINK_TRAIN_PAT_1_CPT;
|
||||
else
|
||||
reg = DP | DP_LINK_TRAIN_PAT_1;
|
||||
|
||||
if (!intel_dp_set_link_train(intel_dp, reg,
|
||||
if (!intel_dp_set_link_train(intel_dp, DP,
|
||||
DP_TRAINING_PATTERN_1 |
|
||||
DP_LINK_SCRAMBLING_DISABLE))
|
||||
break;
|
||||
|
@ -1803,10 +1828,8 @@ static void
|
|||
intel_dp_complete_link_train(struct intel_dp *intel_dp)
|
||||
{
|
||||
struct drm_device *dev = intel_dp->base.base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
bool channel_eq = false;
|
||||
int tries, cr_tries;
|
||||
u32 reg;
|
||||
uint32_t DP = intel_dp->DP;
|
||||
|
||||
/* channel equalization */
|
||||
|
@ -1835,13 +1858,8 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
|
|||
DP = (DP & ~(DP_VOLTAGE_MASK|DP_PRE_EMPHASIS_MASK)) | signal_levels;
|
||||
}
|
||||
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
|
||||
reg = DP | DP_LINK_TRAIN_PAT_2_CPT;
|
||||
else
|
||||
reg = DP | DP_LINK_TRAIN_PAT_2;
|
||||
|
||||
/* channel eq pattern */
|
||||
if (!intel_dp_set_link_train(intel_dp, reg,
|
||||
if (!intel_dp_set_link_train(intel_dp, DP,
|
||||
DP_TRAINING_PATTERN_2 |
|
||||
DP_LINK_SCRAMBLING_DISABLE))
|
||||
break;
|
||||
|
@ -1876,15 +1894,7 @@ intel_dp_complete_link_train(struct intel_dp *intel_dp)
|
|||
++tries;
|
||||
}
|
||||
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
|
||||
reg = DP | DP_LINK_TRAIN_OFF_CPT;
|
||||
else
|
||||
reg = DP | DP_LINK_TRAIN_OFF;
|
||||
|
||||
I915_WRITE(intel_dp->output_reg, reg);
|
||||
POSTING_READ(intel_dp->output_reg);
|
||||
intel_dp_aux_native_write_1(intel_dp,
|
||||
DP_TRAINING_PATTERN_SET, DP_TRAINING_PATTERN_DISABLE);
|
||||
intel_dp_set_link_train(intel_dp, DP, DP_TRAINING_PATTERN_DISABLE);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -1894,18 +1904,11 @@ intel_dp_link_down(struct intel_dp *intel_dp)
|
|||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
uint32_t DP = intel_dp->DP;
|
||||
|
||||
if ((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0)
|
||||
if (WARN_ON((I915_READ(intel_dp->output_reg) & DP_PORT_EN) == 0))
|
||||
return;
|
||||
|
||||
DRM_DEBUG_KMS("\n");
|
||||
|
||||
if (is_edp(intel_dp)) {
|
||||
DP &= ~DP_PLL_ENABLE;
|
||||
I915_WRITE(intel_dp->output_reg, DP);
|
||||
POSTING_READ(intel_dp->output_reg);
|
||||
udelay(100);
|
||||
}
|
||||
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp))) {
|
||||
DP &= ~DP_LINK_TRAIN_MASK_CPT;
|
||||
I915_WRITE(intel_dp->output_reg, DP | DP_LINK_TRAIN_PAT_IDLE_CPT);
|
||||
|
@ -1917,13 +1920,6 @@ intel_dp_link_down(struct intel_dp *intel_dp)
|
|||
|
||||
msleep(17);
|
||||
|
||||
if (is_edp(intel_dp)) {
|
||||
if (HAS_PCH_CPT(dev) && (IS_GEN7(dev) || !is_cpu_edp(intel_dp)))
|
||||
DP |= DP_LINK_TRAIN_OFF_CPT;
|
||||
else
|
||||
DP |= DP_LINK_TRAIN_OFF;
|
||||
}
|
||||
|
||||
if (HAS_PCH_IBX(dev) &&
|
||||
I915_READ(intel_dp->output_reg) & DP_PIPEB_SELECT) {
|
||||
struct drm_crtc *crtc = intel_dp->base.base.crtc;
|
||||
|
@ -2032,10 +2028,10 @@ intel_dp_check_link_status(struct intel_dp *intel_dp)
|
|||
u8 sink_irq_vector;
|
||||
u8 link_status[DP_LINK_STATUS_SIZE];
|
||||
|
||||
if (intel_dp->dpms_mode != DRM_MODE_DPMS_ON)
|
||||
if (!intel_dp->base.connectors_active)
|
||||
return;
|
||||
|
||||
if (!intel_dp->base.base.crtc)
|
||||
if (WARN_ON(!intel_dp->base.base.crtc))
|
||||
return;
|
||||
|
||||
/* Try to read receiver status if the link appears to be up */
|
||||
|
@ -2159,7 +2155,6 @@ intel_dp_get_edid_modes(struct drm_connector *connector, struct i2c_adapter *ada
|
|||
ret = drm_add_edid_modes(connector, intel_dp->edid);
|
||||
drm_edid_to_eld(connector,
|
||||
intel_dp->edid);
|
||||
connector->display_info.raw_edid = NULL;
|
||||
return intel_dp->edid_mode_count;
|
||||
}
|
||||
|
||||
|
@ -2205,7 +2200,6 @@ intel_dp_detect(struct drm_connector *connector, bool force)
|
|||
edid = intel_dp_get_edid(connector, &intel_dp->adapter);
|
||||
if (edid) {
|
||||
intel_dp->has_audio = drm_detect_monitor_audio(edid);
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
}
|
||||
|
@ -2270,8 +2264,6 @@ intel_dp_detect_audio(struct drm_connector *connector)
|
|||
edid = intel_dp_get_edid(connector, &intel_dp->adapter);
|
||||
if (edid) {
|
||||
has_audio = drm_detect_monitor_audio(edid);
|
||||
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
|
@ -2325,9 +2317,8 @@ intel_dp_set_property(struct drm_connector *connector,
|
|||
done:
|
||||
if (intel_dp->base.base.crtc) {
|
||||
struct drm_crtc *crtc = intel_dp->base.base.crtc;
|
||||
drm_crtc_helper_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y,
|
||||
crtc->fb);
|
||||
intel_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y, crtc->fb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -2361,15 +2352,13 @@ static void intel_dp_encoder_destroy(struct drm_encoder *encoder)
|
|||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_dp_helper_funcs = {
|
||||
.dpms = intel_dp_dpms,
|
||||
.mode_fixup = intel_dp_mode_fixup,
|
||||
.prepare = intel_dp_prepare,
|
||||
.mode_set = intel_dp_mode_set,
|
||||
.commit = intel_dp_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_dp_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_connector_dpms,
|
||||
.detect = intel_dp_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = intel_dp_set_property,
|
||||
|
@ -2440,7 +2429,7 @@ intel_dp_add_properties(struct intel_dp *intel_dp, struct drm_connector *connect
|
|||
}
|
||||
|
||||
void
|
||||
intel_dp_init(struct drm_device *dev, int output_reg)
|
||||
intel_dp_init(struct drm_device *dev, int output_reg, enum port port)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_connector *connector;
|
||||
|
@ -2455,7 +2444,9 @@ intel_dp_init(struct drm_device *dev, int output_reg)
|
|||
return;
|
||||
|
||||
intel_dp->output_reg = output_reg;
|
||||
intel_dp->dpms_mode = -1;
|
||||
intel_dp->port = port;
|
||||
/* Preserve the current hw state. */
|
||||
intel_dp->DP = I915_READ(intel_dp->output_reg);
|
||||
|
||||
intel_connector = kzalloc(sizeof(struct intel_connector), GFP_KERNEL);
|
||||
if (!intel_connector) {
|
||||
|
@ -2482,18 +2473,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
|
|||
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
|
||||
if (output_reg == DP_B || output_reg == PCH_DP_B)
|
||||
intel_encoder->clone_mask = (1 << INTEL_DP_B_CLONE_BIT);
|
||||
else if (output_reg == DP_C || output_reg == PCH_DP_C)
|
||||
intel_encoder->clone_mask = (1 << INTEL_DP_C_CLONE_BIT);
|
||||
else if (output_reg == DP_D || output_reg == PCH_DP_D)
|
||||
intel_encoder->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
|
||||
intel_encoder->cloneable = false;
|
||||
|
||||
if (is_edp(intel_dp)) {
|
||||
intel_encoder->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
|
||||
INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
|
||||
ironlake_panel_vdd_work);
|
||||
}
|
||||
INIT_DELAYED_WORK(&intel_dp->panel_vdd_work,
|
||||
ironlake_panel_vdd_work);
|
||||
|
||||
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
|
||||
|
||||
|
@ -2507,29 +2490,33 @@ intel_dp_init(struct drm_device *dev, int output_reg)
|
|||
intel_connector_attach_encoder(intel_connector, intel_encoder);
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
||||
intel_encoder->enable = intel_enable_dp;
|
||||
intel_encoder->pre_enable = intel_pre_enable_dp;
|
||||
intel_encoder->disable = intel_disable_dp;
|
||||
intel_encoder->post_disable = intel_post_disable_dp;
|
||||
intel_encoder->get_hw_state = intel_dp_get_hw_state;
|
||||
intel_connector->get_hw_state = intel_connector_get_hw_state;
|
||||
|
||||
/* Set up the DDC bus. */
|
||||
switch (output_reg) {
|
||||
case DP_A:
|
||||
name = "DPDDC-A";
|
||||
break;
|
||||
case DP_B:
|
||||
case PCH_DP_B:
|
||||
dev_priv->hotplug_supported_mask |=
|
||||
DPB_HOTPLUG_INT_STATUS;
|
||||
name = "DPDDC-B";
|
||||
break;
|
||||
case DP_C:
|
||||
case PCH_DP_C:
|
||||
dev_priv->hotplug_supported_mask |=
|
||||
DPC_HOTPLUG_INT_STATUS;
|
||||
name = "DPDDC-C";
|
||||
break;
|
||||
case DP_D:
|
||||
case PCH_DP_D:
|
||||
dev_priv->hotplug_supported_mask |=
|
||||
DPD_HOTPLUG_INT_STATUS;
|
||||
name = "DPDDC-D";
|
||||
break;
|
||||
switch (port) {
|
||||
case PORT_A:
|
||||
name = "DPDDC-A";
|
||||
break;
|
||||
case PORT_B:
|
||||
dev_priv->hotplug_supported_mask |= DPB_HOTPLUG_INT_STATUS;
|
||||
name = "DPDDC-B";
|
||||
break;
|
||||
case PORT_C:
|
||||
dev_priv->hotplug_supported_mask |= DPC_HOTPLUG_INT_STATUS;
|
||||
name = "DPDDC-C";
|
||||
break;
|
||||
case PORT_D:
|
||||
dev_priv->hotplug_supported_mask |= DPD_HOTPLUG_INT_STATUS;
|
||||
name = "DPDDC-D";
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Invalid port %c\n", port_name(port));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Cache some DPCD data in the eDP case */
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#include <drm/drm_crtc.h>
|
||||
#include <drm/drm_crtc_helper.h>
|
||||
#include <drm/drm_fb_helper.h>
|
||||
#include <drm/drm_dp_helper.h>
|
||||
|
||||
#define _wait_for(COND, MS, W) ({ \
|
||||
unsigned long timeout__ = jiffies + msecs_to_jiffies(MS); \
|
||||
|
@ -40,7 +41,11 @@
|
|||
ret__ = -ETIMEDOUT; \
|
||||
break; \
|
||||
} \
|
||||
if (W && drm_can_sleep()) msleep(W); \
|
||||
if (W && drm_can_sleep()) { \
|
||||
msleep(W); \
|
||||
} else { \
|
||||
cpu_relax(); \
|
||||
} \
|
||||
} \
|
||||
ret__; \
|
||||
})
|
||||
|
@ -90,25 +95,6 @@
|
|||
#define INTEL_OUTPUT_DISPLAYPORT 7
|
||||
#define INTEL_OUTPUT_EDP 8
|
||||
|
||||
/* Intel Pipe Clone Bit */
|
||||
#define INTEL_HDMIB_CLONE_BIT 1
|
||||
#define INTEL_HDMIC_CLONE_BIT 2
|
||||
#define INTEL_HDMID_CLONE_BIT 3
|
||||
#define INTEL_HDMIE_CLONE_BIT 4
|
||||
#define INTEL_HDMIF_CLONE_BIT 5
|
||||
#define INTEL_SDVO_NON_TV_CLONE_BIT 6
|
||||
#define INTEL_SDVO_TV_CLONE_BIT 7
|
||||
#define INTEL_SDVO_LVDS_CLONE_BIT 8
|
||||
#define INTEL_ANALOG_CLONE_BIT 9
|
||||
#define INTEL_TV_CLONE_BIT 10
|
||||
#define INTEL_DP_B_CLONE_BIT 11
|
||||
#define INTEL_DP_C_CLONE_BIT 12
|
||||
#define INTEL_DP_D_CLONE_BIT 13
|
||||
#define INTEL_LVDS_CLONE_BIT 14
|
||||
#define INTEL_DVO_TMDS_CLONE_BIT 15
|
||||
#define INTEL_DVO_LVDS_CLONE_BIT 16
|
||||
#define INTEL_EDP_CLONE_BIT 17
|
||||
|
||||
#define INTEL_DVO_CHIP_NONE 0
|
||||
#define INTEL_DVO_CHIP_LVDS 1
|
||||
#define INTEL_DVO_CHIP_TMDS 2
|
||||
|
@ -151,16 +137,48 @@ struct intel_fbdev {
|
|||
|
||||
struct intel_encoder {
|
||||
struct drm_encoder base;
|
||||
/*
|
||||
* The new crtc this encoder will be driven from. Only differs from
|
||||
* base->crtc while a modeset is in progress.
|
||||
*/
|
||||
struct intel_crtc *new_crtc;
|
||||
|
||||
int type;
|
||||
bool needs_tv_clock;
|
||||
/*
|
||||
* Intel hw has only one MUX where encoders could be clone, hence a
|
||||
* simple flag is enough to compute the possible_clones mask.
|
||||
*/
|
||||
bool cloneable;
|
||||
bool connectors_active;
|
||||
void (*hot_plug)(struct intel_encoder *);
|
||||
void (*pre_enable)(struct intel_encoder *);
|
||||
void (*enable)(struct intel_encoder *);
|
||||
void (*disable)(struct intel_encoder *);
|
||||
void (*post_disable)(struct intel_encoder *);
|
||||
/* Read out the current hw state of this connector, returning true if
|
||||
* the encoder is active. If the encoder is enabled it also set the pipe
|
||||
* it is connected to in the pipe parameter. */
|
||||
bool (*get_hw_state)(struct intel_encoder *, enum pipe *pipe);
|
||||
int crtc_mask;
|
||||
int clone_mask;
|
||||
};
|
||||
|
||||
struct intel_connector {
|
||||
struct drm_connector base;
|
||||
/*
|
||||
* The fixed encoder this connector is connected to.
|
||||
*/
|
||||
struct intel_encoder *encoder;
|
||||
|
||||
/*
|
||||
* The new encoder this connector will be driven. Only differs from
|
||||
* encoder while a modeset is in progress.
|
||||
*/
|
||||
struct intel_encoder *new_encoder;
|
||||
|
||||
/* Reads out the current hw, returning true if the connector is enabled
|
||||
* and active (i.e. dpms ON state). */
|
||||
bool (*get_hw_state)(struct intel_connector *);
|
||||
};
|
||||
|
||||
struct intel_crtc {
|
||||
|
@ -168,11 +186,13 @@ struct intel_crtc {
|
|||
enum pipe pipe;
|
||||
enum plane plane;
|
||||
u8 lut_r[256], lut_g[256], lut_b[256];
|
||||
int dpms_mode;
|
||||
bool active; /* is the crtc on? independent of the dpms mode */
|
||||
/*
|
||||
* Whether the crtc and the connected output pipeline is active. Implies
|
||||
* that crtc->enabled is set, i.e. the current mode configuration has
|
||||
* some outputs connected to this crtc.
|
||||
*/
|
||||
bool active;
|
||||
bool primary_disabled; /* is the crtc obscured by a plane? */
|
||||
bool busy; /* is scanout buffer being updated frequently? */
|
||||
struct timer_list idle_timer;
|
||||
bool lowfreq_avail;
|
||||
struct intel_overlay *overlay;
|
||||
struct intel_unpin_work *unpin_work;
|
||||
|
@ -311,6 +331,37 @@ struct intel_hdmi {
|
|||
struct drm_display_mode *adjusted_mode);
|
||||
};
|
||||
|
||||
#define DP_RECEIVER_CAP_SIZE 0xf
|
||||
#define DP_LINK_CONFIGURATION_SIZE 9
|
||||
|
||||
struct intel_dp {
|
||||
struct intel_encoder base;
|
||||
uint32_t output_reg;
|
||||
uint32_t DP;
|
||||
uint8_t link_configuration[DP_LINK_CONFIGURATION_SIZE];
|
||||
bool has_audio;
|
||||
enum hdmi_force_audio force_audio;
|
||||
enum port port;
|
||||
uint32_t color_range;
|
||||
uint8_t link_bw;
|
||||
uint8_t lane_count;
|
||||
uint8_t dpcd[DP_RECEIVER_CAP_SIZE];
|
||||
struct i2c_adapter adapter;
|
||||
struct i2c_algo_dp_aux_data algo;
|
||||
bool is_pch_edp;
|
||||
uint8_t train_set[4];
|
||||
int panel_power_up_delay;
|
||||
int panel_power_down_delay;
|
||||
int panel_power_cycle_delay;
|
||||
int backlight_on_delay;
|
||||
int backlight_off_delay;
|
||||
struct drm_display_mode *panel_fixed_mode; /* for eDP */
|
||||
struct delayed_work panel_vdd_work;
|
||||
bool want_panel_vdd;
|
||||
struct edid *edid; /* cached EDID for eDP */
|
||||
int edid_mode_count;
|
||||
};
|
||||
|
||||
static inline struct drm_crtc *
|
||||
intel_get_crtc_for_pipe(struct drm_device *dev, int pipe)
|
||||
{
|
||||
|
@ -350,17 +401,21 @@ extern void intel_attach_force_audio_property(struct drm_connector *connector);
|
|||
extern void intel_attach_broadcast_rgb_property(struct drm_connector *connector);
|
||||
|
||||
extern void intel_crt_init(struct drm_device *dev);
|
||||
extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
|
||||
extern void intel_hdmi_init(struct drm_device *dev,
|
||||
int sdvox_reg, enum port port);
|
||||
extern struct intel_hdmi *enc_to_intel_hdmi(struct drm_encoder *encoder);
|
||||
extern void intel_dip_infoframe_csum(struct dip_infoframe *avi_if);
|
||||
extern bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg,
|
||||
bool is_sdvob);
|
||||
extern void intel_dvo_init(struct drm_device *dev);
|
||||
extern void intel_tv_init(struct drm_device *dev);
|
||||
extern void intel_mark_busy(struct drm_device *dev,
|
||||
struct drm_i915_gem_object *obj);
|
||||
extern void intel_mark_busy(struct drm_device *dev);
|
||||
extern void intel_mark_idle(struct drm_device *dev);
|
||||
extern void intel_mark_fb_busy(struct drm_i915_gem_object *obj);
|
||||
extern void intel_mark_fb_idle(struct drm_i915_gem_object *obj);
|
||||
extern bool intel_lvds_init(struct drm_device *dev);
|
||||
extern void intel_dp_init(struct drm_device *dev, int dp_reg);
|
||||
extern void intel_dp_init(struct drm_device *dev, int output_reg,
|
||||
enum port port);
|
||||
void
|
||||
intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
|
@ -373,8 +428,6 @@ extern int intel_plane_init(struct drm_device *dev, enum pipe pipe);
|
|||
extern void intel_flush_display_plane(struct drm_i915_private *dev_priv,
|
||||
enum plane plane);
|
||||
|
||||
void intel_sanitize_pm(struct drm_device *dev);
|
||||
|
||||
/* intel_panel.c */
|
||||
extern void intel_fixed_panel_mode(struct drm_display_mode *fixed_mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
|
@ -391,10 +444,27 @@ extern void intel_panel_disable_backlight(struct drm_device *dev);
|
|||
extern void intel_panel_destroy_backlight(struct drm_device *dev);
|
||||
extern enum drm_connector_status intel_panel_detect(struct drm_device *dev);
|
||||
|
||||
struct intel_set_config {
|
||||
struct drm_encoder **save_connector_encoders;
|
||||
struct drm_crtc **save_encoder_crtcs;
|
||||
|
||||
bool fb_changed;
|
||||
bool mode_changed;
|
||||
};
|
||||
|
||||
extern bool intel_set_mode(struct drm_crtc *crtc, struct drm_display_mode *mode,
|
||||
int x, int y, struct drm_framebuffer *old_fb);
|
||||
extern void intel_modeset_disable(struct drm_device *dev);
|
||||
extern void intel_crtc_load_lut(struct drm_crtc *crtc);
|
||||
extern void intel_encoder_prepare(struct drm_encoder *encoder);
|
||||
extern void intel_encoder_commit(struct drm_encoder *encoder);
|
||||
extern void intel_crtc_update_dpms(struct drm_crtc *crtc);
|
||||
extern void intel_encoder_noop(struct drm_encoder *encoder);
|
||||
extern void intel_encoder_destroy(struct drm_encoder *encoder);
|
||||
extern void intel_encoder_dpms(struct intel_encoder *encoder, int mode);
|
||||
extern bool intel_encoder_check_is_cloned(struct intel_encoder *encoder);
|
||||
extern void intel_connector_dpms(struct drm_connector *, int mode);
|
||||
extern bool intel_connector_get_hw_state(struct intel_connector *connector);
|
||||
extern void intel_modeset_check_state(struct drm_device *dev);
|
||||
|
||||
|
||||
static inline struct intel_encoder *intel_attached_encoder(struct drm_connector *connector)
|
||||
{
|
||||
|
@ -417,12 +487,10 @@ struct intel_load_detect_pipe {
|
|||
bool load_detect_temp;
|
||||
int dpms_mode;
|
||||
};
|
||||
extern bool intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
|
||||
struct drm_connector *connector,
|
||||
extern bool intel_get_load_detect_pipe(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode,
|
||||
struct intel_load_detect_pipe *old);
|
||||
extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
|
||||
struct drm_connector *connector,
|
||||
extern void intel_release_load_detect_pipe(struct drm_connector *connector,
|
||||
struct intel_load_detect_pipe *old);
|
||||
|
||||
extern void intelfb_restore(void);
|
||||
|
@ -503,7 +571,10 @@ extern void intel_disable_gt_powersave(struct drm_device *dev);
|
|||
extern void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv);
|
||||
extern void ironlake_teardown_rc6(struct drm_device *dev);
|
||||
|
||||
extern void intel_ddi_dpms(struct drm_encoder *encoder, int mode);
|
||||
extern void intel_enable_ddi(struct intel_encoder *encoder);
|
||||
extern void intel_disable_ddi(struct intel_encoder *encoder);
|
||||
extern bool intel_ddi_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe);
|
||||
extern void intel_ddi_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode);
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#define SIL164_ADDR 0x38
|
||||
#define CH7xxx_ADDR 0x76
|
||||
#define TFP410_ADDR 0x38
|
||||
#define NS2501_ADDR 0x38
|
||||
|
||||
static const struct intel_dvo_device intel_dvo_devices[] = {
|
||||
{
|
||||
|
@ -73,7 +74,14 @@ static const struct intel_dvo_device intel_dvo_devices[] = {
|
|||
.slave_addr = 0x75,
|
||||
.gpio = GMBUS_PORT_DPB,
|
||||
.dev_ops = &ch7017_ops,
|
||||
}
|
||||
},
|
||||
{
|
||||
.type = INTEL_DVO_CHIP_TMDS,
|
||||
.name = "ns2501",
|
||||
.dvo_reg = DVOC,
|
||||
.slave_addr = NS2501_ADDR,
|
||||
.dev_ops = &ns2501_ops,
|
||||
}
|
||||
};
|
||||
|
||||
struct intel_dvo {
|
||||
|
@ -96,22 +104,91 @@ static struct intel_dvo *intel_attached_dvo(struct drm_connector *connector)
|
|||
struct intel_dvo, base);
|
||||
}
|
||||
|
||||
static void intel_dvo_dpms(struct drm_encoder *encoder, int mode)
|
||||
static bool intel_dvo_connector_get_hw_state(struct intel_connector *connector)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->dev->dev_private;
|
||||
struct intel_dvo *intel_dvo = enc_to_intel_dvo(encoder);
|
||||
struct intel_dvo *intel_dvo = intel_attached_dvo(&connector->base);
|
||||
|
||||
return intel_dvo->dev.dev_ops->get_hw_state(&intel_dvo->dev);
|
||||
}
|
||||
|
||||
static bool intel_dvo_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
|
||||
u32 tmp;
|
||||
|
||||
tmp = I915_READ(intel_dvo->dev.dvo_reg);
|
||||
|
||||
if (!(tmp & DVO_ENABLE))
|
||||
return false;
|
||||
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void intel_disable_dvo(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
|
||||
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
|
||||
u32 dvo_reg = intel_dvo->dev.dvo_reg;
|
||||
u32 temp = I915_READ(dvo_reg);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
I915_WRITE(dvo_reg, temp | DVO_ENABLE);
|
||||
I915_READ(dvo_reg);
|
||||
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode);
|
||||
} else {
|
||||
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, mode);
|
||||
I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
|
||||
I915_READ(dvo_reg);
|
||||
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
|
||||
I915_WRITE(dvo_reg, temp & ~DVO_ENABLE);
|
||||
I915_READ(dvo_reg);
|
||||
}
|
||||
|
||||
static void intel_enable_dvo(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
|
||||
struct intel_dvo *intel_dvo = enc_to_intel_dvo(&encoder->base);
|
||||
u32 dvo_reg = intel_dvo->dev.dvo_reg;
|
||||
u32 temp = I915_READ(dvo_reg);
|
||||
|
||||
I915_WRITE(dvo_reg, temp | DVO_ENABLE);
|
||||
I915_READ(dvo_reg);
|
||||
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
|
||||
}
|
||||
|
||||
static void intel_dvo_dpms(struct drm_connector *connector, int mode)
|
||||
{
|
||||
struct intel_dvo *intel_dvo = intel_attached_dvo(connector);
|
||||
struct drm_crtc *crtc;
|
||||
|
||||
/* dvo supports only 2 dpms states. */
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
mode = DRM_MODE_DPMS_OFF;
|
||||
|
||||
if (mode == connector->dpms)
|
||||
return;
|
||||
|
||||
connector->dpms = mode;
|
||||
|
||||
/* Only need to change hw state when actually enabled */
|
||||
crtc = intel_dvo->base.base.crtc;
|
||||
if (!crtc) {
|
||||
intel_dvo->base.connectors_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
intel_dvo->base.connectors_active = true;
|
||||
|
||||
intel_crtc_update_dpms(crtc);
|
||||
|
||||
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, true);
|
||||
} else {
|
||||
intel_dvo->dev.dev_ops->dpms(&intel_dvo->dev, false);
|
||||
|
||||
intel_dvo->base.connectors_active = false;
|
||||
|
||||
intel_crtc_update_dpms(crtc);
|
||||
}
|
||||
|
||||
intel_modeset_check_state(connector->dev);
|
||||
}
|
||||
|
||||
static int intel_dvo_mode_valid(struct drm_connector *connector,
|
||||
|
@ -266,15 +343,13 @@ static void intel_dvo_destroy(struct drm_connector *connector)
|
|||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_dvo_helper_funcs = {
|
||||
.dpms = intel_dvo_dpms,
|
||||
.mode_fixup = intel_dvo_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.mode_set = intel_dvo_mode_set,
|
||||
.commit = intel_encoder_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_dvo_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_dvo_dpms,
|
||||
.detect = intel_dvo_detect,
|
||||
.destroy = intel_dvo_destroy,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
|
@ -363,6 +438,11 @@ void intel_dvo_init(struct drm_device *dev)
|
|||
drm_encoder_init(dev, &intel_encoder->base,
|
||||
&intel_dvo_enc_funcs, encoder_type);
|
||||
|
||||
intel_encoder->disable = intel_disable_dvo;
|
||||
intel_encoder->enable = intel_enable_dvo;
|
||||
intel_encoder->get_hw_state = intel_dvo_get_hw_state;
|
||||
intel_connector->get_hw_state = intel_dvo_connector_get_hw_state;
|
||||
|
||||
/* Now, try to find a controller */
|
||||
for (i = 0; i < ARRAY_SIZE(intel_dvo_devices); i++) {
|
||||
struct drm_connector *connector = &intel_connector->base;
|
||||
|
@ -395,17 +475,14 @@ void intel_dvo_init(struct drm_device *dev)
|
|||
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
|
||||
switch (dvo->type) {
|
||||
case INTEL_DVO_CHIP_TMDS:
|
||||
intel_encoder->clone_mask =
|
||||
(1 << INTEL_DVO_TMDS_CLONE_BIT) |
|
||||
(1 << INTEL_ANALOG_CLONE_BIT);
|
||||
intel_encoder->cloneable = true;
|
||||
drm_connector_init(dev, connector,
|
||||
&intel_dvo_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_DVII);
|
||||
encoder_type = DRM_MODE_ENCODER_TMDS;
|
||||
break;
|
||||
case INTEL_DVO_CHIP_LVDS:
|
||||
intel_encoder->clone_mask =
|
||||
(1 << INTEL_DVO_LVDS_CLONE_BIT);
|
||||
intel_encoder->cloneable = false;
|
||||
drm_connector_init(dev, connector,
|
||||
&intel_dvo_connector_funcs,
|
||||
DRM_MODE_CONNECTOR_LVDS);
|
||||
|
|
|
@ -150,6 +150,9 @@ static void g4x_write_infoframe(struct drm_encoder *encoder,
|
|||
I915_WRITE(VIDEO_DIP_DATA, *data);
|
||||
data++;
|
||||
}
|
||||
/* Write every possible data byte to force correct ECC calculation. */
|
||||
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
|
||||
I915_WRITE(VIDEO_DIP_DATA, 0);
|
||||
mmiowb();
|
||||
|
||||
val |= g4x_infoframe_enable(frame);
|
||||
|
@ -185,6 +188,9 @@ static void ibx_write_infoframe(struct drm_encoder *encoder,
|
|||
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
|
||||
data++;
|
||||
}
|
||||
/* Write every possible data byte to force correct ECC calculation. */
|
||||
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
|
||||
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
|
||||
mmiowb();
|
||||
|
||||
val |= g4x_infoframe_enable(frame);
|
||||
|
@ -223,6 +229,9 @@ static void cpt_write_infoframe(struct drm_encoder *encoder,
|
|||
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
|
||||
data++;
|
||||
}
|
||||
/* Write every possible data byte to force correct ECC calculation. */
|
||||
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
|
||||
I915_WRITE(TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
|
||||
mmiowb();
|
||||
|
||||
val |= g4x_infoframe_enable(frame);
|
||||
|
@ -258,6 +267,9 @@ static void vlv_write_infoframe(struct drm_encoder *encoder,
|
|||
I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), *data);
|
||||
data++;
|
||||
}
|
||||
/* Write every possible data byte to force correct ECC calculation. */
|
||||
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
|
||||
I915_WRITE(VLV_TVIDEO_DIP_DATA(intel_crtc->pipe), 0);
|
||||
mmiowb();
|
||||
|
||||
val |= g4x_infoframe_enable(frame);
|
||||
|
@ -291,6 +303,9 @@ static void hsw_write_infoframe(struct drm_encoder *encoder,
|
|||
I915_WRITE(data_reg + i, *data);
|
||||
data++;
|
||||
}
|
||||
/* Write every possible data byte to force correct ECC calculation. */
|
||||
for (; i < VIDEO_DIP_DATA_SIZE; i += 4)
|
||||
I915_WRITE(data_reg + i, 0);
|
||||
mmiowb();
|
||||
|
||||
val |= hsw_infoframe_enable(frame);
|
||||
|
@ -376,6 +391,7 @@ static void g4x_set_infoframes(struct drm_encoder *encoder,
|
|||
port = VIDEO_DIP_PORT_C;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -434,6 +450,7 @@ static void ibx_set_infoframes(struct drm_encoder *encoder,
|
|||
port = VIDEO_DIP_PORT_D;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -600,15 +617,36 @@ static void intel_hdmi_mode_set(struct drm_encoder *encoder,
|
|||
intel_hdmi->set_infoframes(encoder, adjusted_mode);
|
||||
}
|
||||
|
||||
static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
||||
static bool intel_hdmi_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
|
||||
u32 tmp;
|
||||
|
||||
tmp = I915_READ(intel_hdmi->sdvox_reg);
|
||||
|
||||
if (!(tmp & SDVO_ENABLE))
|
||||
return false;
|
||||
|
||||
if (HAS_PCH_CPT(dev))
|
||||
*pipe = PORT_TO_PIPE_CPT(tmp);
|
||||
else
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void intel_enable_hdmi(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
|
||||
u32 temp;
|
||||
u32 enable_bits = SDVO_ENABLE;
|
||||
|
||||
if (intel_hdmi->has_audio || mode != DRM_MODE_DPMS_ON)
|
||||
if (intel_hdmi->has_audio)
|
||||
enable_bits |= SDVO_AUDIO_ENABLE;
|
||||
|
||||
temp = I915_READ(intel_hdmi->sdvox_reg);
|
||||
|
@ -616,30 +654,67 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
|||
/* HW workaround for IBX, we need to move the port to transcoder A
|
||||
* before disabling it. */
|
||||
if (HAS_PCH_IBX(dev)) {
|
||||
struct drm_crtc *crtc = encoder->crtc;
|
||||
struct drm_crtc *crtc = encoder->base.crtc;
|
||||
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON) {
|
||||
if (temp & SDVO_PIPE_B_SELECT) {
|
||||
temp &= ~SDVO_PIPE_B_SELECT;
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
/* Restore the transcoder select bit. */
|
||||
if (pipe == PIPE_B)
|
||||
enable_bits |= SDVO_PIPE_B_SELECT;
|
||||
}
|
||||
|
||||
/* Again we need to write this twice. */
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
/* HW workaround, need to toggle enable bit off and on for 12bpc, but
|
||||
* we do this anyway which shows more stable in testing.
|
||||
*/
|
||||
if (HAS_PCH_SPLIT(dev)) {
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp & ~SDVO_ENABLE);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
}
|
||||
|
||||
/* Transcoder selection bits only update
|
||||
* effectively on vblank. */
|
||||
if (crtc)
|
||||
intel_wait_for_vblank(dev, pipe);
|
||||
else
|
||||
msleep(50);
|
||||
}
|
||||
} else {
|
||||
/* Restore the transcoder select bit. */
|
||||
if (pipe == PIPE_B)
|
||||
enable_bits |= SDVO_PIPE_B_SELECT;
|
||||
temp |= enable_bits;
|
||||
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
|
||||
/* HW workaround, need to write this twice for issue that may result
|
||||
* in first write getting masked.
|
||||
*/
|
||||
if (HAS_PCH_SPLIT(dev)) {
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_disable_hdmi(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(&encoder->base);
|
||||
u32 temp;
|
||||
u32 enable_bits = SDVO_ENABLE | SDVO_AUDIO_ENABLE;
|
||||
|
||||
temp = I915_READ(intel_hdmi->sdvox_reg);
|
||||
|
||||
/* HW workaround for IBX, we need to move the port to transcoder A
|
||||
* before disabling it. */
|
||||
if (HAS_PCH_IBX(dev)) {
|
||||
struct drm_crtc *crtc = encoder->base.crtc;
|
||||
int pipe = crtc ? to_intel_crtc(crtc)->pipe : -1;
|
||||
|
||||
if (temp & SDVO_PIPE_B_SELECT) {
|
||||
temp &= ~SDVO_PIPE_B_SELECT;
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
|
||||
/* Again we need to write this twice. */
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
|
||||
/* Transcoder selection bits only update
|
||||
* effectively on vblank. */
|
||||
if (crtc)
|
||||
intel_wait_for_vblank(dev, pipe);
|
||||
else
|
||||
msleep(50);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -651,11 +726,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
|
|||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
}
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON) {
|
||||
temp &= ~enable_bits;
|
||||
} else {
|
||||
temp |= enable_bits;
|
||||
}
|
||||
temp &= ~enable_bits;
|
||||
|
||||
I915_WRITE(intel_hdmi->sdvox_reg, temp);
|
||||
POSTING_READ(intel_hdmi->sdvox_reg);
|
||||
|
@ -736,7 +807,6 @@ intel_hdmi_detect(struct drm_connector *connector, bool force)
|
|||
drm_detect_hdmi_monitor(edid);
|
||||
intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
|
||||
}
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
|
@ -777,8 +847,6 @@ intel_hdmi_detect_audio(struct drm_connector *connector)
|
|||
if (edid) {
|
||||
if (edid->input & DRM_EDID_INPUT_DIGITAL)
|
||||
has_audio = drm_detect_monitor_audio(edid);
|
||||
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
|
@ -832,9 +900,8 @@ intel_hdmi_set_property(struct drm_connector *connector,
|
|||
done:
|
||||
if (intel_hdmi->base.base.crtc) {
|
||||
struct drm_crtc *crtc = intel_hdmi->base.base.crtc;
|
||||
drm_crtc_helper_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y,
|
||||
crtc->fb);
|
||||
intel_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y, crtc->fb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -848,23 +915,19 @@ static void intel_hdmi_destroy(struct drm_connector *connector)
|
|||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs_hsw = {
|
||||
.dpms = intel_ddi_dpms,
|
||||
.mode_fixup = intel_hdmi_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.mode_set = intel_ddi_mode_set,
|
||||
.commit = intel_encoder_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_hdmi_helper_funcs = {
|
||||
.dpms = intel_hdmi_dpms,
|
||||
.mode_fixup = intel_hdmi_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.mode_set = intel_hdmi_mode_set,
|
||||
.commit = intel_encoder_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_hdmi_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_connector_dpms,
|
||||
.detect = intel_hdmi_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = intel_hdmi_set_property,
|
||||
|
@ -888,7 +951,7 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *c
|
|||
intel_attach_broadcast_rgb_property(connector);
|
||||
}
|
||||
|
||||
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
|
||||
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg, enum port port)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct drm_connector *connector;
|
||||
|
@ -922,48 +985,25 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
|
|||
connector->doublescan_allowed = 0;
|
||||
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
|
||||
|
||||
/* Set up the DDC bus. */
|
||||
if (sdvox_reg == SDVOB) {
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
|
||||
intel_encoder->cloneable = false;
|
||||
|
||||
intel_hdmi->ddi_port = port;
|
||||
switch (port) {
|
||||
case PORT_B:
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
|
||||
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == SDVOC) {
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
|
||||
break;
|
||||
case PORT_C:
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
|
||||
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == HDMIB) {
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
|
||||
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == HDMIC) {
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT);
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
|
||||
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == HDMID) {
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT);
|
||||
break;
|
||||
case PORT_D:
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
|
||||
dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == DDI_BUF_CTL(PORT_B)) {
|
||||
DRM_DEBUG_DRIVER("LPT: detected output on DDI B\n");
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT);
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPB;
|
||||
intel_hdmi->ddi_port = PORT_B;
|
||||
dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == DDI_BUF_CTL(PORT_C)) {
|
||||
DRM_DEBUG_DRIVER("LPT: detected output on DDI C\n");
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT);
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPC;
|
||||
intel_hdmi->ddi_port = PORT_C;
|
||||
dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS;
|
||||
} else if (sdvox_reg == DDI_BUF_CTL(PORT_D)) {
|
||||
DRM_DEBUG_DRIVER("LPT: detected output on DDI D\n");
|
||||
intel_encoder->clone_mask = (1 << INTEL_HDMID_CLONE_BIT);
|
||||
intel_hdmi->ddc_bus = GMBUS_PORT_DPD;
|
||||
intel_hdmi->ddi_port = PORT_D;
|
||||
dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS;
|
||||
} else {
|
||||
/* If we got an unknown sdvox_reg, things are pretty much broken
|
||||
* in a way that we should let the kernel know about it */
|
||||
break;
|
||||
case PORT_A:
|
||||
/* Internal port only for eDP. */
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
|
@ -986,10 +1026,21 @@ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
|
|||
intel_hdmi->set_infoframes = cpt_set_infoframes;
|
||||
}
|
||||
|
||||
if (IS_HASWELL(dev))
|
||||
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs_hsw);
|
||||
else
|
||||
drm_encoder_helper_add(&intel_encoder->base, &intel_hdmi_helper_funcs);
|
||||
if (IS_HASWELL(dev)) {
|
||||
intel_encoder->enable = intel_enable_ddi;
|
||||
intel_encoder->disable = intel_disable_ddi;
|
||||
intel_encoder->get_hw_state = intel_ddi_get_hw_state;
|
||||
drm_encoder_helper_add(&intel_encoder->base,
|
||||
&intel_hdmi_helper_funcs_hsw);
|
||||
} else {
|
||||
intel_encoder->enable = intel_enable_hdmi;
|
||||
intel_encoder->disable = intel_disable_hdmi;
|
||||
intel_encoder->get_hw_state = intel_hdmi_get_hw_state;
|
||||
drm_encoder_helper_add(&intel_encoder->base,
|
||||
&intel_hdmi_helper_funcs);
|
||||
}
|
||||
intel_connector->get_hw_state = intel_connector_get_hw_state;
|
||||
|
||||
|
||||
intel_hdmi_add_properties(intel_hdmi, connector);
|
||||
|
||||
|
|
|
@ -64,13 +64,40 @@ static struct intel_lvds *intel_attached_lvds(struct drm_connector *connector)
|
|||
struct intel_lvds, base);
|
||||
}
|
||||
|
||||
static bool intel_lvds_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 lvds_reg, tmp;
|
||||
|
||||
if (HAS_PCH_SPLIT(dev)) {
|
||||
lvds_reg = PCH_LVDS;
|
||||
} else {
|
||||
lvds_reg = LVDS;
|
||||
}
|
||||
|
||||
tmp = I915_READ(lvds_reg);
|
||||
|
||||
if (!(tmp & LVDS_PORT_EN))
|
||||
return false;
|
||||
|
||||
if (HAS_PCH_CPT(dev))
|
||||
*pipe = PORT_TO_PIPE_CPT(tmp);
|
||||
else
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the power state for the panel.
|
||||
*/
|
||||
static void intel_lvds_enable(struct intel_lvds *intel_lvds)
|
||||
static void intel_enable_lvds(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = intel_lvds->base.base.dev;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(intel_lvds->base.base.crtc);
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base);
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 ctl_reg, lvds_reg, stat_reg;
|
||||
|
||||
|
@ -110,9 +137,10 @@ static void intel_lvds_enable(struct intel_lvds *intel_lvds)
|
|||
intel_panel_enable_backlight(dev, intel_crtc->pipe);
|
||||
}
|
||||
|
||||
static void intel_lvds_disable(struct intel_lvds *intel_lvds)
|
||||
static void intel_disable_lvds(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = intel_lvds->base.base.dev;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct intel_lvds *intel_lvds = to_intel_lvds(&encoder->base);
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 ctl_reg, lvds_reg, stat_reg;
|
||||
|
||||
|
@ -141,18 +169,6 @@ static void intel_lvds_disable(struct intel_lvds *intel_lvds)
|
|||
POSTING_READ(lvds_reg);
|
||||
}
|
||||
|
||||
static void intel_lvds_dpms(struct drm_encoder *encoder, int mode)
|
||||
{
|
||||
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_ON)
|
||||
intel_lvds_enable(intel_lvds);
|
||||
else
|
||||
intel_lvds_disable(intel_lvds);
|
||||
|
||||
/* XXX: We never power down the LVDS pairs. */
|
||||
}
|
||||
|
||||
static int intel_lvds_mode_valid(struct drm_connector *connector,
|
||||
struct drm_display_mode *mode)
|
||||
{
|
||||
|
@ -233,9 +249,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
|||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
|
||||
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
|
||||
struct intel_encoder *tmp_encoder;
|
||||
struct intel_crtc *intel_crtc = intel_lvds->base.new_crtc;
|
||||
u32 pfit_control = 0, pfit_pgm_ratios = 0, border = 0;
|
||||
int pipe;
|
||||
|
||||
|
@ -245,14 +260,8 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Should never happen!! */
|
||||
for_each_encoder_on_crtc(dev, encoder->crtc, tmp_encoder) {
|
||||
if (&tmp_encoder->base != encoder) {
|
||||
DRM_ERROR("Can't enable LVDS and another "
|
||||
"encoder on the same pipe\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (intel_encoder_check_is_cloned(&intel_lvds->base))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* We have timings from the BIOS for the panel, put them in
|
||||
|
@ -404,23 +413,6 @@ out:
|
|||
return true;
|
||||
}
|
||||
|
||||
static void intel_lvds_prepare(struct drm_encoder *encoder)
|
||||
{
|
||||
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
|
||||
|
||||
intel_lvds_disable(intel_lvds);
|
||||
}
|
||||
|
||||
static void intel_lvds_commit(struct drm_encoder *encoder)
|
||||
{
|
||||
struct intel_lvds *intel_lvds = to_intel_lvds(encoder);
|
||||
|
||||
/* Always do a full power on as we do not know what state
|
||||
* we were left in.
|
||||
*/
|
||||
intel_lvds_enable(intel_lvds);
|
||||
}
|
||||
|
||||
static void intel_lvds_mode_set(struct drm_encoder *encoder,
|
||||
struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
|
@ -534,7 +526,7 @@ static int intel_lid_notify(struct notifier_block *nb, unsigned long val,
|
|||
dev_priv->modeset_on_lid = 0;
|
||||
|
||||
mutex_lock(&dev->mode_config.mutex);
|
||||
drm_helper_resume_force_mode(dev);
|
||||
intel_modeset_check_state(dev);
|
||||
mutex_unlock(&dev->mode_config.mutex);
|
||||
|
||||
return NOTIFY_OK;
|
||||
|
@ -586,8 +578,8 @@ static int intel_lvds_set_property(struct drm_connector *connector,
|
|||
* If the CRTC is enabled, the display will be changed
|
||||
* according to the new panel fitting mode.
|
||||
*/
|
||||
drm_crtc_helper_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y, crtc->fb);
|
||||
intel_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y, crtc->fb);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -595,11 +587,9 @@ static int intel_lvds_set_property(struct drm_connector *connector,
|
|||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_lvds_helper_funcs = {
|
||||
.dpms = intel_lvds_dpms,
|
||||
.mode_fixup = intel_lvds_mode_fixup,
|
||||
.prepare = intel_lvds_prepare,
|
||||
.mode_set = intel_lvds_mode_set,
|
||||
.commit = intel_lvds_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs = {
|
||||
|
@ -609,7 +599,7 @@ static const struct drm_connector_helper_funcs intel_lvds_connector_helper_funcs
|
|||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_lvds_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_connector_dpms,
|
||||
.detect = intel_lvds_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = intel_lvds_set_property,
|
||||
|
@ -971,10 +961,15 @@ bool intel_lvds_init(struct drm_device *dev)
|
|||
drm_encoder_init(dev, &intel_encoder->base, &intel_lvds_enc_funcs,
|
||||
DRM_MODE_ENCODER_LVDS);
|
||||
|
||||
intel_encoder->enable = intel_enable_lvds;
|
||||
intel_encoder->disable = intel_disable_lvds;
|
||||
intel_encoder->get_hw_state = intel_lvds_get_hw_state;
|
||||
intel_connector->get_hw_state = intel_connector_get_hw_state;
|
||||
|
||||
intel_connector_attach_encoder(intel_connector, intel_encoder);
|
||||
intel_encoder->type = INTEL_OUTPUT_LVDS;
|
||||
|
||||
intel_encoder->clone_mask = (1 << INTEL_LVDS_CLONE_BIT);
|
||||
intel_encoder->cloneable = false;
|
||||
if (HAS_PCH_SPLIT(dev))
|
||||
intel_encoder->crtc_mask = (1 << 0) | (1 << 1) | (1 << 2);
|
||||
else if (IS_GEN4(dev))
|
||||
|
|
|
@ -45,7 +45,6 @@ int intel_connector_update_modes(struct drm_connector *connector,
|
|||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
drm_edid_to_eld(connector, edid);
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
|
||||
return ret;
|
||||
|
|
|
@ -427,6 +427,25 @@ blind_set:
|
|||
goto end;
|
||||
}
|
||||
|
||||
static void intel_setup_cadls(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_opregion *opregion = &dev_priv->opregion;
|
||||
int i = 0;
|
||||
u32 disp_id;
|
||||
|
||||
/* Initialize the CADL field by duplicating the DIDL values.
|
||||
* Technically, this is not always correct as display outputs may exist,
|
||||
* but not active. This initialization is necessary for some Clevo
|
||||
* laptops that check this field before processing the brightness and
|
||||
* display switching hotkeys. Just like DIDL, CADL is NULL-terminated if
|
||||
* there are less than eight devices. */
|
||||
do {
|
||||
disp_id = ioread32(&opregion->acpi->didl[i]);
|
||||
iowrite32(disp_id, &opregion->acpi->cadl[i]);
|
||||
} while (++i < 8 && disp_id != 0);
|
||||
}
|
||||
|
||||
void intel_opregion_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
@ -436,8 +455,10 @@ void intel_opregion_init(struct drm_device *dev)
|
|||
return;
|
||||
|
||||
if (opregion->acpi) {
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET))
|
||||
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
|
||||
intel_didl_outputs(dev);
|
||||
intel_setup_cadls(dev);
|
||||
}
|
||||
|
||||
/* Notify BIOS we are ready to handle ACPI video ext notifs.
|
||||
* Right now, all the events are handled by the ACPI video module.
|
||||
|
|
|
@ -234,54 +234,6 @@ static int intel_overlay_do_wait_request(struct intel_overlay *overlay,
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Workaround for i830 bug where pipe a must be enable to change control regs */
|
||||
static int
|
||||
i830_activate_pipe_a(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct intel_crtc *crtc;
|
||||
struct drm_crtc_helper_funcs *crtc_funcs;
|
||||
struct drm_display_mode vesa_640x480 = {
|
||||
DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
|
||||
752, 800, 0, 480, 489, 492, 525, 0,
|
||||
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC)
|
||||
}, *mode;
|
||||
|
||||
crtc = to_intel_crtc(dev_priv->pipe_to_crtc_mapping[0]);
|
||||
if (crtc->dpms_mode == DRM_MODE_DPMS_ON)
|
||||
return 0;
|
||||
|
||||
/* most i8xx have pipe a forced on, so don't trust dpms mode */
|
||||
if (I915_READ(_PIPEACONF) & PIPECONF_ENABLE)
|
||||
return 0;
|
||||
|
||||
crtc_funcs = crtc->base.helper_private;
|
||||
if (crtc_funcs->dpms == NULL)
|
||||
return 0;
|
||||
|
||||
DRM_DEBUG_DRIVER("Enabling pipe A in order to enable overlay\n");
|
||||
|
||||
mode = drm_mode_duplicate(dev, &vesa_640x480);
|
||||
|
||||
if (!drm_crtc_helper_set_mode(&crtc->base, mode,
|
||||
crtc->base.x, crtc->base.y,
|
||||
crtc->base.fb))
|
||||
return 0;
|
||||
|
||||
crtc_funcs->dpms(&crtc->base, DRM_MODE_DPMS_ON);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
i830_deactivate_pipe_a(struct drm_device *dev)
|
||||
{
|
||||
drm_i915_private_t *dev_priv = dev->dev_private;
|
||||
struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[0];
|
||||
struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
|
||||
|
||||
crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
|
||||
/* overlay needs to be disable in OCMD reg */
|
||||
static int intel_overlay_on(struct intel_overlay *overlay)
|
||||
{
|
||||
|
@ -289,17 +241,12 @@ static int intel_overlay_on(struct intel_overlay *overlay)
|
|||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_ring_buffer *ring = &dev_priv->ring[RCS];
|
||||
struct drm_i915_gem_request *request;
|
||||
int pipe_a_quirk = 0;
|
||||
int ret;
|
||||
|
||||
BUG_ON(overlay->active);
|
||||
overlay->active = 1;
|
||||
|
||||
if (IS_I830(dev)) {
|
||||
pipe_a_quirk = i830_activate_pipe_a(dev);
|
||||
if (pipe_a_quirk < 0)
|
||||
return pipe_a_quirk;
|
||||
}
|
||||
WARN_ON(IS_I830(dev) && !(dev_priv->quirks & QUIRK_PIPEA_FORCE));
|
||||
|
||||
request = kzalloc(sizeof(*request), GFP_KERNEL);
|
||||
if (request == NULL) {
|
||||
|
@ -321,9 +268,6 @@ static int intel_overlay_on(struct intel_overlay *overlay)
|
|||
|
||||
ret = intel_overlay_do_wait_request(overlay, request, NULL);
|
||||
out:
|
||||
if (pipe_a_quirk)
|
||||
i830_deactivate_pipe_a(dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -1438,7 +1382,7 @@ void intel_setup_overlay(struct drm_device *dev)
|
|||
}
|
||||
overlay->flip_addr = reg_bo->phys_obj->handle->busaddr;
|
||||
} else {
|
||||
ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true);
|
||||
ret = i915_gem_object_pin(reg_bo, PAGE_SIZE, true, false);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to pin overlay register bo\n");
|
||||
goto out_free_bo;
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "../../../platform/x86/intel_ips.h"
|
||||
#include <linux/module.h>
|
||||
|
||||
#define FORCEWAKE_ACK_TIMEOUT_MS 2
|
||||
|
||||
/* FBC, or Frame Buffer Compression, is a technique employed to compress the
|
||||
* framebuffer contents in-memory, aiming at reducing the required bandwidth
|
||||
* during in-memory transfers and, therefore, reduce the power packet.
|
||||
|
@ -593,7 +595,7 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev)
|
|||
break;
|
||||
}
|
||||
|
||||
dev_priv->r_t = dev_priv->mem_freq;
|
||||
dev_priv->ips.r_t = dev_priv->mem_freq;
|
||||
|
||||
switch (csipll & 0x3ff) {
|
||||
case 0x00c:
|
||||
|
@ -625,11 +627,11 @@ static void i915_ironlake_get_mem_freq(struct drm_device *dev)
|
|||
}
|
||||
|
||||
if (dev_priv->fsb_freq == 3200) {
|
||||
dev_priv->c_m = 0;
|
||||
dev_priv->ips.c_m = 0;
|
||||
} else if (dev_priv->fsb_freq > 3200 && dev_priv->fsb_freq <= 4800) {
|
||||
dev_priv->c_m = 1;
|
||||
dev_priv->ips.c_m = 1;
|
||||
} else {
|
||||
dev_priv->c_m = 2;
|
||||
dev_priv->ips.c_m = 2;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2138,7 +2140,7 @@ intel_alloc_context_page(struct drm_device *dev)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
ret = i915_gem_object_pin(ctx, 4096, true);
|
||||
ret = i915_gem_object_pin(ctx, 4096, true, false);
|
||||
if (ret) {
|
||||
DRM_ERROR("failed to pin power context: %d\n", ret);
|
||||
goto err_unref;
|
||||
|
@ -2160,11 +2162,22 @@ err_unref:
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lock protecting IPS related data structures
|
||||
*/
|
||||
DEFINE_SPINLOCK(mchdev_lock);
|
||||
|
||||
/* Global for IPS driver to get at the current i915 device. Protected by
|
||||
* mchdev_lock. */
|
||||
static struct drm_i915_private *i915_mch_dev;
|
||||
|
||||
bool ironlake_set_drps(struct drm_device *dev, u8 val)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u16 rgvswctl;
|
||||
|
||||
assert_spin_locked(&mchdev_lock);
|
||||
|
||||
rgvswctl = I915_READ16(MEMSWCTL);
|
||||
if (rgvswctl & MEMCTL_CMD_STS) {
|
||||
DRM_DEBUG("gpu busy, RCS change rejected\n");
|
||||
|
@ -2188,6 +2201,8 @@ static void ironlake_enable_drps(struct drm_device *dev)
|
|||
u32 rgvmodectl = I915_READ(MEMMODECTL);
|
||||
u8 fmax, fmin, fstart, vstart;
|
||||
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
|
||||
/* Enable temp reporting */
|
||||
I915_WRITE16(PMMISC, I915_READ(PMMISC) | MCPPCE_EN);
|
||||
I915_WRITE16(TSC1, I915_READ(TSC1) | TSE);
|
||||
|
@ -2211,12 +2226,12 @@ static void ironlake_enable_drps(struct drm_device *dev)
|
|||
vstart = (I915_READ(PXVFREQ_BASE + (fstart * 4)) & PXVFREQ_PX_MASK) >>
|
||||
PXVFREQ_PX_SHIFT;
|
||||
|
||||
dev_priv->fmax = fmax; /* IPS callback will increase this */
|
||||
dev_priv->fstart = fstart;
|
||||
dev_priv->ips.fmax = fmax; /* IPS callback will increase this */
|
||||
dev_priv->ips.fstart = fstart;
|
||||
|
||||
dev_priv->max_delay = fstart;
|
||||
dev_priv->min_delay = fmin;
|
||||
dev_priv->cur_delay = fstart;
|
||||
dev_priv->ips.max_delay = fstart;
|
||||
dev_priv->ips.min_delay = fmin;
|
||||
dev_priv->ips.cur_delay = fstart;
|
||||
|
||||
DRM_DEBUG_DRIVER("fmax: %d, fmin: %d, fstart: %d\n",
|
||||
fmax, fmin, fstart);
|
||||
|
@ -2233,23 +2248,29 @@ static void ironlake_enable_drps(struct drm_device *dev)
|
|||
rgvmodectl |= MEMMODE_SWMODE_EN;
|
||||
I915_WRITE(MEMMODECTL, rgvmodectl);
|
||||
|
||||
if (wait_for((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
|
||||
if (wait_for_atomic((I915_READ(MEMSWCTL) & MEMCTL_CMD_STS) == 0, 10))
|
||||
DRM_ERROR("stuck trying to change perf mode\n");
|
||||
msleep(1);
|
||||
mdelay(1);
|
||||
|
||||
ironlake_set_drps(dev, fstart);
|
||||
|
||||
dev_priv->last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
|
||||
dev_priv->ips.last_count1 = I915_READ(0x112e4) + I915_READ(0x112e8) +
|
||||
I915_READ(0x112e0);
|
||||
dev_priv->last_time1 = jiffies_to_msecs(jiffies);
|
||||
dev_priv->last_count2 = I915_READ(0x112f4);
|
||||
getrawmonotonic(&dev_priv->last_time2);
|
||||
dev_priv->ips.last_time1 = jiffies_to_msecs(jiffies);
|
||||
dev_priv->ips.last_count2 = I915_READ(0x112f4);
|
||||
getrawmonotonic(&dev_priv->ips.last_time2);
|
||||
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
}
|
||||
|
||||
static void ironlake_disable_drps(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u16 rgvswctl = I915_READ16(MEMSWCTL);
|
||||
u16 rgvswctl;
|
||||
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
|
||||
rgvswctl = I915_READ16(MEMSWCTL);
|
||||
|
||||
/* Ack interrupts, disable EFC interrupt */
|
||||
I915_WRITE(MEMINTREN, I915_READ(MEMINTREN) & ~MEMINT_EVAL_CHG_EN);
|
||||
|
@ -2259,31 +2280,54 @@ static void ironlake_disable_drps(struct drm_device *dev)
|
|||
I915_WRITE(DEIMR, I915_READ(DEIMR) | DE_PCU_EVENT);
|
||||
|
||||
/* Go back to the starting frequency */
|
||||
ironlake_set_drps(dev, dev_priv->fstart);
|
||||
msleep(1);
|
||||
ironlake_set_drps(dev, dev_priv->ips.fstart);
|
||||
mdelay(1);
|
||||
rgvswctl |= MEMCTL_CMD_STS;
|
||||
I915_WRITE(MEMSWCTL, rgvswctl);
|
||||
msleep(1);
|
||||
mdelay(1);
|
||||
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
}
|
||||
|
||||
/* There's a funny hw issue where the hw returns all 0 when reading from
|
||||
* GEN6_RP_INTERRUPT_LIMITS. Hence we always need to compute the desired value
|
||||
* ourselves, instead of doing a rmw cycle (which might result in us clearing
|
||||
* all limits and the gpu stuck at whatever frequency it is at atm).
|
||||
*/
|
||||
static u32 gen6_rps_limits(struct drm_i915_private *dev_priv, u8 *val)
|
||||
{
|
||||
u32 limits;
|
||||
|
||||
limits = 0;
|
||||
|
||||
if (*val >= dev_priv->rps.max_delay)
|
||||
*val = dev_priv->rps.max_delay;
|
||||
limits |= dev_priv->rps.max_delay << 24;
|
||||
|
||||
/* Only set the down limit when we've reached the lowest level to avoid
|
||||
* getting more interrupts, otherwise leave this clear. This prevents a
|
||||
* race in the hw when coming out of rc6: There's a tiny window where
|
||||
* the hw runs at the minimal clock before selecting the desired
|
||||
* frequency, if the down threshold expires in that window we will not
|
||||
* receive a down interrupt. */
|
||||
if (*val <= dev_priv->rps.min_delay) {
|
||||
*val = dev_priv->rps.min_delay;
|
||||
limits |= dev_priv->rps.min_delay << 16;
|
||||
}
|
||||
|
||||
return limits;
|
||||
}
|
||||
|
||||
void gen6_set_rps(struct drm_device *dev, u8 val)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 limits;
|
||||
u32 limits = gen6_rps_limits(dev_priv, &val);
|
||||
|
||||
limits = 0;
|
||||
if (val >= dev_priv->max_delay)
|
||||
val = dev_priv->max_delay;
|
||||
else
|
||||
limits |= dev_priv->max_delay << 24;
|
||||
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
|
||||
WARN_ON(val > dev_priv->rps.max_delay);
|
||||
WARN_ON(val < dev_priv->rps.min_delay);
|
||||
|
||||
if (val <= dev_priv->min_delay)
|
||||
val = dev_priv->min_delay;
|
||||
else
|
||||
limits |= dev_priv->min_delay << 16;
|
||||
|
||||
if (val == dev_priv->cur_delay)
|
||||
if (val == dev_priv->rps.cur_delay)
|
||||
return;
|
||||
|
||||
I915_WRITE(GEN6_RPNSWREQ,
|
||||
|
@ -2296,7 +2340,11 @@ void gen6_set_rps(struct drm_device *dev, u8 val)
|
|||
*/
|
||||
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits);
|
||||
|
||||
dev_priv->cur_delay = val;
|
||||
POSTING_READ(GEN6_RPNSWREQ);
|
||||
|
||||
dev_priv->rps.cur_delay = val;
|
||||
|
||||
trace_intel_gpu_freq_change(val * 50);
|
||||
}
|
||||
|
||||
static void gen6_disable_rps(struct drm_device *dev)
|
||||
|
@ -2312,40 +2360,40 @@ static void gen6_disable_rps(struct drm_device *dev)
|
|||
* 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);
|
||||
dev_priv->pm_iir = 0;
|
||||
spin_unlock_irq(&dev_priv->rps_lock);
|
||||
spin_lock_irq(&dev_priv->rps.lock);
|
||||
dev_priv->rps.pm_iir = 0;
|
||||
spin_unlock_irq(&dev_priv->rps.lock);
|
||||
|
||||
I915_WRITE(GEN6_PMIIR, I915_READ(GEN6_PMIIR));
|
||||
}
|
||||
|
||||
int intel_enable_rc6(const struct drm_device *dev)
|
||||
{
|
||||
/*
|
||||
* Respect the kernel parameter if it is set
|
||||
*/
|
||||
/* Respect the kernel parameter if it is set */
|
||||
if (i915_enable_rc6 >= 0)
|
||||
return i915_enable_rc6;
|
||||
|
||||
/*
|
||||
* Disable RC6 on Ironlake
|
||||
*/
|
||||
if (INTEL_INFO(dev)->gen == 5)
|
||||
return 0;
|
||||
|
||||
/* On Haswell, only RC6 is available. So let's enable it by default to
|
||||
* provide better testing and coverage since the beginning.
|
||||
*/
|
||||
if (IS_HASWELL(dev))
|
||||
if (INTEL_INFO(dev)->gen == 5) {
|
||||
#ifdef CONFIG_INTEL_IOMMU
|
||||
/* Disable rc6 on ilk if VT-d is on. */
|
||||
if (intel_iommu_gfx_mapped)
|
||||
return false;
|
||||
#endif
|
||||
DRM_DEBUG_DRIVER("Ironlake: only RC6 available\n");
|
||||
return INTEL_RC6_ENABLE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable rc6 on Sandybridge
|
||||
*/
|
||||
if (IS_HASWELL(dev)) {
|
||||
DRM_DEBUG_DRIVER("Haswell: only RC6 available\n");
|
||||
return INTEL_RC6_ENABLE;
|
||||
}
|
||||
|
||||
/* snb/ivb have more than one rc6 state. */
|
||||
if (INTEL_INFO(dev)->gen == 6) {
|
||||
DRM_DEBUG_DRIVER("Sandybridge: deep RC6 disabled\n");
|
||||
return INTEL_RC6_ENABLE;
|
||||
}
|
||||
|
||||
DRM_DEBUG_DRIVER("RC6 and deep RC6 enabled\n");
|
||||
return (INTEL_RC6_ENABLE | INTEL_RC6p_ENABLE);
|
||||
}
|
||||
|
@ -2383,9 +2431,9 @@ static void gen6_enable_rps(struct drm_device *dev)
|
|||
gt_perf_status = I915_READ(GEN6_GT_PERF_STATUS);
|
||||
|
||||
/* In units of 100MHz */
|
||||
dev_priv->max_delay = rp_state_cap & 0xff;
|
||||
dev_priv->min_delay = (rp_state_cap & 0xff0000) >> 16;
|
||||
dev_priv->cur_delay = 0;
|
||||
dev_priv->rps.max_delay = rp_state_cap & 0xff;
|
||||
dev_priv->rps.min_delay = (rp_state_cap & 0xff0000) >> 16;
|
||||
dev_priv->rps.cur_delay = 0;
|
||||
|
||||
/* disable the counters and set deterministic thresholds */
|
||||
I915_WRITE(GEN6_RC_CONTROL, 0);
|
||||
|
@ -2438,8 +2486,8 @@ static void gen6_enable_rps(struct drm_device *dev)
|
|||
|
||||
I915_WRITE(GEN6_RP_DOWN_TIMEOUT, 1000000);
|
||||
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS,
|
||||
dev_priv->max_delay << 24 |
|
||||
dev_priv->min_delay << 16);
|
||||
dev_priv->rps.max_delay << 24 |
|
||||
dev_priv->rps.min_delay << 16);
|
||||
|
||||
I915_WRITE(GEN6_RP_UP_THRESHOLD, 59400);
|
||||
I915_WRITE(GEN6_RP_DOWN_THRESHOLD, 245000);
|
||||
|
@ -2477,7 +2525,7 @@ static void gen6_enable_rps(struct drm_device *dev)
|
|||
500))
|
||||
DRM_ERROR("timeout waiting for pcode mailbox to finish\n");
|
||||
if (pcu_mbox & (1<<31)) { /* OC supported */
|
||||
dev_priv->max_delay = pcu_mbox & 0xff;
|
||||
dev_priv->rps.max_delay = pcu_mbox & 0xff;
|
||||
DRM_DEBUG_DRIVER("overclocking supported, adjusting frequency max to %dMHz\n", pcu_mbox * 50);
|
||||
}
|
||||
|
||||
|
@ -2485,10 +2533,10 @@ static void gen6_enable_rps(struct drm_device *dev)
|
|||
|
||||
/* requires MSI enabled */
|
||||
I915_WRITE(GEN6_PMIER, GEN6_PM_DEFERRED_EVENTS);
|
||||
spin_lock_irq(&dev_priv->rps_lock);
|
||||
WARN_ON(dev_priv->pm_iir != 0);
|
||||
spin_lock_irq(&dev_priv->rps.lock);
|
||||
WARN_ON(dev_priv->rps.pm_iir != 0);
|
||||
I915_WRITE(GEN6_PMIMR, 0);
|
||||
spin_unlock_irq(&dev_priv->rps_lock);
|
||||
spin_unlock_irq(&dev_priv->rps.lock);
|
||||
/* enable all PM interrupts */
|
||||
I915_WRITE(GEN6_PMINTRMSK, 0);
|
||||
|
||||
|
@ -2520,9 +2568,9 @@ static void gen6_update_ring_freq(struct drm_device *dev)
|
|||
* to use for memory access. We do this by specifying the IA frequency
|
||||
* the PCU should use as a reference to determine the ring frequency.
|
||||
*/
|
||||
for (gpu_freq = dev_priv->max_delay; gpu_freq >= dev_priv->min_delay;
|
||||
for (gpu_freq = dev_priv->rps.max_delay; gpu_freq >= dev_priv->rps.min_delay;
|
||||
gpu_freq--) {
|
||||
int diff = dev_priv->max_delay - gpu_freq;
|
||||
int diff = dev_priv->rps.max_delay - gpu_freq;
|
||||
|
||||
/*
|
||||
* For GPU frequencies less than 750MHz, just use the lowest
|
||||
|
@ -2686,14 +2734,16 @@ static const struct cparams {
|
|||
{ 0, 800, 231, 23784 },
|
||||
};
|
||||
|
||||
unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
|
||||
static unsigned long __i915_chipset_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
u64 total_count, diff, ret;
|
||||
u32 count1, count2, count3, m = 0, c = 0;
|
||||
unsigned long now = jiffies_to_msecs(jiffies), diff1;
|
||||
int i;
|
||||
|
||||
diff1 = now - dev_priv->last_time1;
|
||||
assert_spin_locked(&mchdev_lock);
|
||||
|
||||
diff1 = now - dev_priv->ips.last_time1;
|
||||
|
||||
/* Prevent division-by-zero if we are asking too fast.
|
||||
* Also, we don't get interesting results if we are polling
|
||||
|
@ -2701,7 +2751,7 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
|
|||
* in such cases.
|
||||
*/
|
||||
if (diff1 <= 10)
|
||||
return dev_priv->chipset_power;
|
||||
return dev_priv->ips.chipset_power;
|
||||
|
||||
count1 = I915_READ(DMIEC);
|
||||
count2 = I915_READ(DDREC);
|
||||
|
@ -2710,16 +2760,16 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
|
|||
total_count = count1 + count2 + count3;
|
||||
|
||||
/* FIXME: handle per-counter overflow */
|
||||
if (total_count < dev_priv->last_count1) {
|
||||
diff = ~0UL - dev_priv->last_count1;
|
||||
if (total_count < dev_priv->ips.last_count1) {
|
||||
diff = ~0UL - dev_priv->ips.last_count1;
|
||||
diff += total_count;
|
||||
} else {
|
||||
diff = total_count - dev_priv->last_count1;
|
||||
diff = total_count - dev_priv->ips.last_count1;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cparams); i++) {
|
||||
if (cparams[i].i == dev_priv->c_m &&
|
||||
cparams[i].t == dev_priv->r_t) {
|
||||
if (cparams[i].i == dev_priv->ips.c_m &&
|
||||
cparams[i].t == dev_priv->ips.r_t) {
|
||||
m = cparams[i].m;
|
||||
c = cparams[i].c;
|
||||
break;
|
||||
|
@ -2730,14 +2780,30 @@ unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
|
|||
ret = ((m * diff) + c);
|
||||
ret = div_u64(ret, 10);
|
||||
|
||||
dev_priv->last_count1 = total_count;
|
||||
dev_priv->last_time1 = now;
|
||||
dev_priv->ips.last_count1 = total_count;
|
||||
dev_priv->ips.last_time1 = now;
|
||||
|
||||
dev_priv->chipset_power = ret;
|
||||
dev_priv->ips.chipset_power = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned long i915_chipset_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
if (dev_priv->info->gen != 5)
|
||||
return 0;
|
||||
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
|
||||
val = __i915_chipset_val(dev_priv);
|
||||
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
unsigned long i915_mch_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long m, x, b;
|
||||
|
@ -2894,18 +2960,17 @@ static u16 pvid_to_extvid(struct drm_i915_private *dev_priv, u8 pxvid)
|
|||
return v_table[pxvid].vd;
|
||||
}
|
||||
|
||||
void i915_update_gfx_val(struct drm_i915_private *dev_priv)
|
||||
static void __i915_update_gfx_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
struct timespec now, diff1;
|
||||
u64 diff;
|
||||
unsigned long diffms;
|
||||
u32 count;
|
||||
|
||||
if (dev_priv->info->gen != 5)
|
||||
return;
|
||||
assert_spin_locked(&mchdev_lock);
|
||||
|
||||
getrawmonotonic(&now);
|
||||
diff1 = timespec_sub(now, dev_priv->last_time2);
|
||||
diff1 = timespec_sub(now, dev_priv->ips.last_time2);
|
||||
|
||||
/* Don't divide by 0 */
|
||||
diffms = diff1.tv_sec * 1000 + diff1.tv_nsec / 1000000;
|
||||
|
@ -2914,28 +2979,42 @@ void i915_update_gfx_val(struct drm_i915_private *dev_priv)
|
|||
|
||||
count = I915_READ(GFXEC);
|
||||
|
||||
if (count < dev_priv->last_count2) {
|
||||
diff = ~0UL - dev_priv->last_count2;
|
||||
if (count < dev_priv->ips.last_count2) {
|
||||
diff = ~0UL - dev_priv->ips.last_count2;
|
||||
diff += count;
|
||||
} else {
|
||||
diff = count - dev_priv->last_count2;
|
||||
diff = count - dev_priv->ips.last_count2;
|
||||
}
|
||||
|
||||
dev_priv->last_count2 = count;
|
||||
dev_priv->last_time2 = now;
|
||||
dev_priv->ips.last_count2 = count;
|
||||
dev_priv->ips.last_time2 = now;
|
||||
|
||||
/* More magic constants... */
|
||||
diff = diff * 1181;
|
||||
diff = div_u64(diff, diffms * 10);
|
||||
dev_priv->gfx_power = diff;
|
||||
dev_priv->ips.gfx_power = diff;
|
||||
}
|
||||
|
||||
unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
|
||||
void i915_update_gfx_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
if (dev_priv->info->gen != 5)
|
||||
return;
|
||||
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
|
||||
__i915_update_gfx_val(dev_priv);
|
||||
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
}
|
||||
|
||||
static unsigned long __i915_gfx_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long t, corr, state1, corr2, state2;
|
||||
u32 pxvid, ext_v;
|
||||
|
||||
pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->cur_delay * 4));
|
||||
assert_spin_locked(&mchdev_lock);
|
||||
|
||||
pxvid = I915_READ(PXVFREQ_BASE + (dev_priv->rps.cur_delay * 4));
|
||||
pxvid = (pxvid >> 24) & 0x7f;
|
||||
ext_v = pvid_to_extvid(dev_priv, pxvid);
|
||||
|
||||
|
@ -2955,27 +3034,31 @@ unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
|
|||
|
||||
corr = corr * ((150142 * state1) / 10000 - 78642);
|
||||
corr /= 100000;
|
||||
corr2 = (corr * dev_priv->corr);
|
||||
corr2 = (corr * dev_priv->ips.corr);
|
||||
|
||||
state2 = (corr2 * state1) / 10000;
|
||||
state2 /= 100; /* convert to mW */
|
||||
|
||||
i915_update_gfx_val(dev_priv);
|
||||
__i915_update_gfx_val(dev_priv);
|
||||
|
||||
return dev_priv->gfx_power + state2;
|
||||
return dev_priv->ips.gfx_power + state2;
|
||||
}
|
||||
|
||||
/* Global for IPS driver to get at the current i915 device */
|
||||
static struct drm_i915_private *i915_mch_dev;
|
||||
/*
|
||||
* Lock protecting IPS related data structures
|
||||
* - i915_mch_dev
|
||||
* - dev_priv->max_delay
|
||||
* - dev_priv->min_delay
|
||||
* - dev_priv->fmax
|
||||
* - dev_priv->gpu_busy
|
||||
*/
|
||||
static DEFINE_SPINLOCK(mchdev_lock);
|
||||
unsigned long i915_gfx_val(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
unsigned long val;
|
||||
|
||||
if (dev_priv->info->gen != 5)
|
||||
return 0;
|
||||
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
|
||||
val = __i915_gfx_val(dev_priv);
|
||||
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* i915_read_mch_val - return value for IPS use
|
||||
|
@ -2988,18 +3071,18 @@ unsigned long i915_read_mch_val(void)
|
|||
struct drm_i915_private *dev_priv;
|
||||
unsigned long chipset_val, graphics_val, ret = 0;
|
||||
|
||||
spin_lock(&mchdev_lock);
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
if (!i915_mch_dev)
|
||||
goto out_unlock;
|
||||
dev_priv = i915_mch_dev;
|
||||
|
||||
chipset_val = i915_chipset_val(dev_priv);
|
||||
graphics_val = i915_gfx_val(dev_priv);
|
||||
chipset_val = __i915_chipset_val(dev_priv);
|
||||
graphics_val = __i915_gfx_val(dev_priv);
|
||||
|
||||
ret = chipset_val + graphics_val;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3015,18 +3098,18 @@ bool i915_gpu_raise(void)
|
|||
struct drm_i915_private *dev_priv;
|
||||
bool ret = true;
|
||||
|
||||
spin_lock(&mchdev_lock);
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
if (!i915_mch_dev) {
|
||||
ret = false;
|
||||
goto out_unlock;
|
||||
}
|
||||
dev_priv = i915_mch_dev;
|
||||
|
||||
if (dev_priv->max_delay > dev_priv->fmax)
|
||||
dev_priv->max_delay--;
|
||||
if (dev_priv->ips.max_delay > dev_priv->ips.fmax)
|
||||
dev_priv->ips.max_delay--;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3043,18 +3126,18 @@ bool i915_gpu_lower(void)
|
|||
struct drm_i915_private *dev_priv;
|
||||
bool ret = true;
|
||||
|
||||
spin_lock(&mchdev_lock);
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
if (!i915_mch_dev) {
|
||||
ret = false;
|
||||
goto out_unlock;
|
||||
}
|
||||
dev_priv = i915_mch_dev;
|
||||
|
||||
if (dev_priv->max_delay < dev_priv->min_delay)
|
||||
dev_priv->max_delay++;
|
||||
if (dev_priv->ips.max_delay < dev_priv->ips.min_delay)
|
||||
dev_priv->ips.max_delay++;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3068,17 +3151,20 @@ EXPORT_SYMBOL_GPL(i915_gpu_lower);
|
|||
bool i915_gpu_busy(void)
|
||||
{
|
||||
struct drm_i915_private *dev_priv;
|
||||
struct intel_ring_buffer *ring;
|
||||
bool ret = false;
|
||||
int i;
|
||||
|
||||
spin_lock(&mchdev_lock);
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
if (!i915_mch_dev)
|
||||
goto out_unlock;
|
||||
dev_priv = i915_mch_dev;
|
||||
|
||||
ret = dev_priv->busy;
|
||||
for_each_ring(ring, dev_priv, i)
|
||||
ret |= !list_empty(&ring->request_list);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3095,20 +3181,20 @@ bool i915_gpu_turbo_disable(void)
|
|||
struct drm_i915_private *dev_priv;
|
||||
bool ret = true;
|
||||
|
||||
spin_lock(&mchdev_lock);
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
if (!i915_mch_dev) {
|
||||
ret = false;
|
||||
goto out_unlock;
|
||||
}
|
||||
dev_priv = i915_mch_dev;
|
||||
|
||||
dev_priv->max_delay = dev_priv->fstart;
|
||||
dev_priv->ips.max_delay = dev_priv->ips.fstart;
|
||||
|
||||
if (!ironlake_set_drps(dev_priv->dev, dev_priv->fstart))
|
||||
if (!ironlake_set_drps(dev_priv->dev, dev_priv->ips.fstart))
|
||||
ret = false;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -3136,19 +3222,20 @@ ips_ping_for_i915_load(void)
|
|||
|
||||
void intel_gpu_ips_init(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
spin_lock(&mchdev_lock);
|
||||
/* We only register the i915 ips part with intel-ips once everything is
|
||||
* set up, to avoid intel-ips sneaking in and reading bogus values. */
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
i915_mch_dev = dev_priv;
|
||||
dev_priv->mchdev_lock = &mchdev_lock;
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
|
||||
ips_ping_for_i915_load();
|
||||
}
|
||||
|
||||
void intel_gpu_ips_teardown(void)
|
||||
{
|
||||
spin_lock(&mchdev_lock);
|
||||
spin_lock_irq(&mchdev_lock);
|
||||
i915_mch_dev = NULL;
|
||||
spin_unlock(&mchdev_lock);
|
||||
spin_unlock_irq(&mchdev_lock);
|
||||
}
|
||||
static void intel_init_emon(struct drm_device *dev)
|
||||
{
|
||||
|
@ -3218,7 +3305,7 @@ static void intel_init_emon(struct drm_device *dev)
|
|||
|
||||
lcfuse = I915_READ(LCFUSE02);
|
||||
|
||||
dev_priv->corr = (lcfuse & LCFUSE_HIV_MASK);
|
||||
dev_priv->ips.corr = (lcfuse & LCFUSE_HIV_MASK);
|
||||
}
|
||||
|
||||
void intel_disable_gt_powersave(struct drm_device *dev)
|
||||
|
@ -3731,42 +3818,6 @@ void intel_init_clock_gating(struct drm_device *dev)
|
|||
dev_priv->display.init_pch_clock_gating(dev);
|
||||
}
|
||||
|
||||
static void gen6_sanitize_pm(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 limits, delay, old;
|
||||
|
||||
gen6_gt_force_wake_get(dev_priv);
|
||||
|
||||
old = limits = I915_READ(GEN6_RP_INTERRUPT_LIMITS);
|
||||
/* Make sure we continue to get interrupts
|
||||
* until we hit the minimum or maximum frequencies.
|
||||
*/
|
||||
limits &= ~(0x3f << 16 | 0x3f << 24);
|
||||
delay = dev_priv->cur_delay;
|
||||
if (delay < dev_priv->max_delay)
|
||||
limits |= (dev_priv->max_delay & 0x3f) << 24;
|
||||
if (delay > dev_priv->min_delay)
|
||||
limits |= (dev_priv->min_delay & 0x3f) << 16;
|
||||
|
||||
if (old != limits) {
|
||||
/* Note that the known failure case is to read back 0. */
|
||||
DRM_DEBUG_DRIVER("Power management discrepancy: GEN6_RP_INTERRUPT_LIMITS "
|
||||
"expected %08x, was %08x\n", limits, old);
|
||||
I915_WRITE(GEN6_RP_INTERRUPT_LIMITS, limits);
|
||||
}
|
||||
|
||||
gen6_gt_force_wake_put(dev_priv);
|
||||
}
|
||||
|
||||
void intel_sanitize_pm(struct drm_device *dev)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (dev_priv->display.sanitize_pm)
|
||||
dev_priv->display.sanitize_pm(dev);
|
||||
}
|
||||
|
||||
/* Starting with Haswell, we have different power wells for
|
||||
* different parts of the GPU. This attempts to enable them all.
|
||||
*/
|
||||
|
@ -3852,7 +3903,6 @@ void intel_init_pm(struct drm_device *dev)
|
|||
dev_priv->display.update_wm = NULL;
|
||||
}
|
||||
dev_priv->display.init_clock_gating = gen6_init_clock_gating;
|
||||
dev_priv->display.sanitize_pm = gen6_sanitize_pm;
|
||||
} else if (IS_IVYBRIDGE(dev)) {
|
||||
/* FIXME: detect B0+ stepping and use auto training */
|
||||
if (SNB_READ_WM0_LATENCY()) {
|
||||
|
@ -3864,7 +3914,6 @@ void intel_init_pm(struct drm_device *dev)
|
|||
dev_priv->display.update_wm = NULL;
|
||||
}
|
||||
dev_priv->display.init_clock_gating = ivybridge_init_clock_gating;
|
||||
dev_priv->display.sanitize_pm = gen6_sanitize_pm;
|
||||
} else if (IS_HASWELL(dev)) {
|
||||
if (SNB_READ_WM0_LATENCY()) {
|
||||
dev_priv->display.update_wm = sandybridge_update_wm;
|
||||
|
@ -3876,7 +3925,6 @@ void intel_init_pm(struct drm_device *dev)
|
|||
dev_priv->display.update_wm = NULL;
|
||||
}
|
||||
dev_priv->display.init_clock_gating = haswell_init_clock_gating;
|
||||
dev_priv->display.sanitize_pm = gen6_sanitize_pm;
|
||||
} else
|
||||
dev_priv->display.update_wm = NULL;
|
||||
} else if (IS_VALLEYVIEW(dev)) {
|
||||
|
@ -3955,14 +4003,16 @@ static void __gen6_gt_force_wake_get(struct drm_i915_private *dev_priv)
|
|||
else
|
||||
forcewake_ack = FORCEWAKE_ACK;
|
||||
|
||||
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500))
|
||||
DRM_ERROR("Force wake wait timed out\n");
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
I915_WRITE_NOTRACE(FORCEWAKE, 1);
|
||||
POSTING_READ(FORCEWAKE);
|
||||
POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */
|
||||
|
||||
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
|
||||
DRM_ERROR("Force wake wait timed out\n");
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
@ -3976,14 +4026,16 @@ static void __gen6_gt_force_wake_mt_get(struct drm_i915_private *dev_priv)
|
|||
else
|
||||
forcewake_ack = FORCEWAKE_MT_ACK;
|
||||
|
||||
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1) == 0, 500))
|
||||
DRM_ERROR("Force wake wait timed out\n");
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_ENABLE(1));
|
||||
POSTING_READ(FORCEWAKE_MT);
|
||||
POSTING_READ(ECOBUS); /* something from same cacheline, but !FORCEWAKE */
|
||||
|
||||
if (wait_for_atomic_us((I915_READ_NOTRACE(forcewake_ack) & 1), 500))
|
||||
DRM_ERROR("Force wake wait timed out\n");
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(forcewake_ack) & 1),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
@ -4016,14 +4068,14 @@ void gen6_gt_check_fifodbg(struct drm_i915_private *dev_priv)
|
|||
static void __gen6_gt_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE, 0);
|
||||
POSTING_READ(FORCEWAKE);
|
||||
/* gen6_gt_check_fifodbg doubles as the POSTING_READ */
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
static void __gen6_gt_force_wake_mt_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_MT, _MASKED_BIT_DISABLE(1));
|
||||
POSTING_READ(FORCEWAKE_MT);
|
||||
/* gen6_gt_check_fifodbg doubles as the POSTING_READ */
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
|
@ -4062,24 +4114,24 @@ int __gen6_gt_wait_for_fifo(struct drm_i915_private *dev_priv)
|
|||
|
||||
static void vlv_force_wake_get(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
/* Already awake? */
|
||||
if ((I915_READ(0x130094) & 0xa1) == 0xa1)
|
||||
return;
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1) == 0,
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake old ack to clear.\n");
|
||||
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffffffff);
|
||||
POSTING_READ(FORCEWAKE_VLV);
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_ENABLE(1));
|
||||
|
||||
if (wait_for_atomic_us((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1), 500))
|
||||
DRM_ERROR("Force wake wait timed out\n");
|
||||
if (wait_for_atomic((I915_READ_NOTRACE(FORCEWAKE_ACK_VLV) & 1),
|
||||
FORCEWAKE_ACK_TIMEOUT_MS))
|
||||
DRM_ERROR("Timed out waiting for forcewake to ack request.\n");
|
||||
|
||||
__gen6_gt_wait_for_thread_c0(dev_priv);
|
||||
}
|
||||
|
||||
static void vlv_force_wake_put(struct drm_i915_private *dev_priv)
|
||||
{
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, 0xffff0000);
|
||||
/* FIXME: confirm VLV behavior with Punit folks */
|
||||
POSTING_READ(FORCEWAKE_VLV);
|
||||
I915_WRITE_NOTRACE(FORCEWAKE_VLV, _MASKED_BIT_DISABLE(1));
|
||||
/* The below doubles as a POSTING_READ */
|
||||
gen6_gt_check_fifodbg(dev_priv);
|
||||
}
|
||||
|
||||
void intel_gt_init(struct drm_device *dev)
|
||||
|
|
|
@ -261,6 +261,83 @@ gen6_render_ring_flush(struct intel_ring_buffer *ring,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gen7_render_ring_cs_stall_wa(struct intel_ring_buffer *ring)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = intel_ring_begin(ring, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
|
||||
intel_ring_emit(ring, PIPE_CONTROL_CS_STALL |
|
||||
PIPE_CONTROL_STALL_AT_SCOREBOARD);
|
||||
intel_ring_emit(ring, 0);
|
||||
intel_ring_emit(ring, 0);
|
||||
intel_ring_advance(ring);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
gen7_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;
|
||||
|
||||
/*
|
||||
* Ensure that any following seqno writes only happen when the render
|
||||
* cache is indeed flushed.
|
||||
*
|
||||
* Workaround: 4th PIPE_CONTROL command (except the ones with only
|
||||
* read-cache invalidate bits set) must have the CS_STALL bit set. We
|
||||
* don't try to be clever and just set it unconditionally.
|
||||
*/
|
||||
flags |= PIPE_CONTROL_CS_STALL;
|
||||
|
||||
/* Just flush everything. Experiments have shown that reducing the
|
||||
* number of bits based on the write domains has little performance
|
||||
* impact.
|
||||
*/
|
||||
if (flush_domains) {
|
||||
flags |= PIPE_CONTROL_RENDER_TARGET_CACHE_FLUSH;
|
||||
flags |= PIPE_CONTROL_DEPTH_CACHE_FLUSH;
|
||||
}
|
||||
if (invalidate_domains) {
|
||||
flags |= PIPE_CONTROL_TLB_INVALIDATE;
|
||||
flags |= PIPE_CONTROL_INSTRUCTION_CACHE_INVALIDATE;
|
||||
flags |= PIPE_CONTROL_TEXTURE_CACHE_INVALIDATE;
|
||||
flags |= PIPE_CONTROL_VF_CACHE_INVALIDATE;
|
||||
flags |= PIPE_CONTROL_CONST_CACHE_INVALIDATE;
|
||||
flags |= PIPE_CONTROL_STATE_CACHE_INVALIDATE;
|
||||
/*
|
||||
* TLB invalidate requires a post-sync write.
|
||||
*/
|
||||
flags |= PIPE_CONTROL_QW_WRITE;
|
||||
|
||||
/* Workaround: we must issue a pipe_control with CS-stall bit
|
||||
* set before a pipe_control command that has the state cache
|
||||
* invalidate bit set. */
|
||||
gen7_render_ring_cs_stall_wa(ring);
|
||||
}
|
||||
|
||||
ret = intel_ring_begin(ring, 4);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
intel_ring_emit(ring, GFX_OP_PIPE_CONTROL(4));
|
||||
intel_ring_emit(ring, flags);
|
||||
intel_ring_emit(ring, scratch_addr | PIPE_CONTROL_GLOBAL_GTT);
|
||||
intel_ring_emit(ring, 0);
|
||||
intel_ring_advance(ring);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ring_write_tail(struct intel_ring_buffer *ring,
|
||||
u32 value)
|
||||
{
|
||||
|
@ -381,12 +458,12 @@ init_pipe_control(struct intel_ring_buffer *ring)
|
|||
|
||||
i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
|
||||
|
||||
ret = i915_gem_object_pin(obj, 4096, true);
|
||||
ret = i915_gem_object_pin(obj, 4096, true, false);
|
||||
if (ret)
|
||||
goto err_unref;
|
||||
|
||||
pc->gtt_offset = obj->gtt_offset;
|
||||
pc->cpu_page = kmap(obj->pages[0]);
|
||||
pc->cpu_page = kmap(sg_page(obj->pages->sgl));
|
||||
if (pc->cpu_page == NULL)
|
||||
goto err_unpin;
|
||||
|
||||
|
@ -413,7 +490,8 @@ cleanup_pipe_control(struct intel_ring_buffer *ring)
|
|||
return;
|
||||
|
||||
obj = pc->obj;
|
||||
kunmap(obj->pages[0]);
|
||||
|
||||
kunmap(sg_page(obj->pages->sgl));
|
||||
i915_gem_object_unpin(obj);
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
|
||||
|
@ -461,7 +539,7 @@ static int init_render_ring(struct intel_ring_buffer *ring)
|
|||
if (INTEL_INFO(dev)->gen >= 6)
|
||||
I915_WRITE(INSTPM, _MASKED_BIT_ENABLE(INSTPM_FORCE_ORDERING));
|
||||
|
||||
if (IS_IVYBRIDGE(dev))
|
||||
if (HAS_L3_GPU_CACHE(dev))
|
||||
I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR);
|
||||
|
||||
return ret;
|
||||
|
@ -627,26 +705,24 @@ pc_render_add_request(struct intel_ring_buffer *ring,
|
|||
}
|
||||
|
||||
static u32
|
||||
gen6_ring_get_seqno(struct intel_ring_buffer *ring)
|
||||
gen6_ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
|
||||
{
|
||||
struct drm_device *dev = ring->dev;
|
||||
|
||||
/* Workaround to force correct ordering between irq and seqno writes on
|
||||
* ivb (and maybe also on snb) by reading from a CS register (like
|
||||
* ACTHD) before reading the status page. */
|
||||
if (IS_GEN6(dev) || IS_GEN7(dev))
|
||||
if (!lazy_coherency)
|
||||
intel_ring_get_active_head(ring);
|
||||
return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
|
||||
}
|
||||
|
||||
static u32
|
||||
ring_get_seqno(struct intel_ring_buffer *ring)
|
||||
ring_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
|
||||
{
|
||||
return intel_read_status_page(ring, I915_GEM_HWS_INDEX);
|
||||
}
|
||||
|
||||
static u32
|
||||
pc_render_get_seqno(struct intel_ring_buffer *ring)
|
||||
pc_render_get_seqno(struct intel_ring_buffer *ring, bool lazy_coherency)
|
||||
{
|
||||
struct pipe_control *pc = ring->private;
|
||||
return pc->cpu_page[0];
|
||||
|
@ -851,7 +927,7 @@ gen6_ring_get_irq(struct intel_ring_buffer *ring)
|
|||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, flags);
|
||||
if (ring->irq_refcount++ == 0) {
|
||||
if (IS_IVYBRIDGE(dev) && ring->id == RCS)
|
||||
if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
|
||||
I915_WRITE_IMR(ring, ~(ring->irq_enable_mask |
|
||||
GEN6_RENDER_L3_PARITY_ERROR));
|
||||
else
|
||||
|
@ -874,7 +950,7 @@ gen6_ring_put_irq(struct intel_ring_buffer *ring)
|
|||
|
||||
spin_lock_irqsave(&dev_priv->irq_lock, flags);
|
||||
if (--ring->irq_refcount == 0) {
|
||||
if (IS_IVYBRIDGE(dev) && ring->id == RCS)
|
||||
if (HAS_L3_GPU_CACHE(dev) && ring->id == RCS)
|
||||
I915_WRITE_IMR(ring, ~GEN6_RENDER_L3_PARITY_ERROR);
|
||||
else
|
||||
I915_WRITE_IMR(ring, ~0);
|
||||
|
@ -950,7 +1026,7 @@ static void cleanup_status_page(struct intel_ring_buffer *ring)
|
|||
if (obj == NULL)
|
||||
return;
|
||||
|
||||
kunmap(obj->pages[0]);
|
||||
kunmap(sg_page(obj->pages->sgl));
|
||||
i915_gem_object_unpin(obj);
|
||||
drm_gem_object_unreference(&obj->base);
|
||||
ring->status_page.obj = NULL;
|
||||
|
@ -971,13 +1047,13 @@ static int init_status_page(struct intel_ring_buffer *ring)
|
|||
|
||||
i915_gem_object_set_cache_level(obj, I915_CACHE_LLC);
|
||||
|
||||
ret = i915_gem_object_pin(obj, 4096, true);
|
||||
ret = i915_gem_object_pin(obj, 4096, true, false);
|
||||
if (ret != 0) {
|
||||
goto err_unref;
|
||||
}
|
||||
|
||||
ring->status_page.gfx_addr = obj->gtt_offset;
|
||||
ring->status_page.page_addr = kmap(obj->pages[0]);
|
||||
ring->status_page.page_addr = kmap(sg_page(obj->pages->sgl));
|
||||
if (ring->status_page.page_addr == NULL) {
|
||||
ret = -ENOMEM;
|
||||
goto err_unpin;
|
||||
|
@ -1009,7 +1085,6 @@ static int intel_init_ring_buffer(struct drm_device *dev,
|
|||
ring->dev = dev;
|
||||
INIT_LIST_HEAD(&ring->active_list);
|
||||
INIT_LIST_HEAD(&ring->request_list);
|
||||
INIT_LIST_HEAD(&ring->gpu_write_list);
|
||||
ring->size = 32 * PAGE_SIZE;
|
||||
|
||||
init_waitqueue_head(&ring->irq_queue);
|
||||
|
@ -1029,7 +1104,7 @@ static int intel_init_ring_buffer(struct drm_device *dev,
|
|||
|
||||
ring->obj = obj;
|
||||
|
||||
ret = i915_gem_object_pin(obj, PAGE_SIZE, true);
|
||||
ret = i915_gem_object_pin(obj, PAGE_SIZE, true, false);
|
||||
if (ret)
|
||||
goto err_unref;
|
||||
|
||||
|
@ -1378,7 +1453,9 @@ int intel_init_render_ring_buffer(struct drm_device *dev)
|
|||
|
||||
if (INTEL_INFO(dev)->gen >= 6) {
|
||||
ring->add_request = gen6_add_request;
|
||||
ring->flush = gen6_render_ring_flush;
|
||||
ring->flush = gen7_render_ring_flush;
|
||||
if (INTEL_INFO(dev)->gen == 6)
|
||||
ring->flush = gen6_render_ring_flush;
|
||||
ring->irq_get = gen6_ring_get_irq;
|
||||
ring->irq_put = gen6_ring_put_irq;
|
||||
ring->irq_enable_mask = GT_USER_INTERRUPT;
|
||||
|
@ -1480,7 +1557,6 @@ int intel_render_ring_init_dri(struct drm_device *dev, u64 start, u32 size)
|
|||
ring->dev = dev;
|
||||
INIT_LIST_HEAD(&ring->active_list);
|
||||
INIT_LIST_HEAD(&ring->request_list);
|
||||
INIT_LIST_HEAD(&ring->gpu_write_list);
|
||||
|
||||
ring->size = size;
|
||||
ring->effective_size = ring->size;
|
||||
|
@ -1573,3 +1649,41 @@ int intel_init_blt_ring_buffer(struct drm_device *dev)
|
|||
|
||||
return intel_init_ring_buffer(dev, ring);
|
||||
}
|
||||
|
||||
int
|
||||
intel_ring_flush_all_caches(struct intel_ring_buffer *ring)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!ring->gpu_caches_dirty)
|
||||
return 0;
|
||||
|
||||
ret = ring->flush(ring, 0, I915_GEM_GPU_DOMAINS);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
trace_i915_gem_ring_flush(ring, 0, I915_GEM_GPU_DOMAINS);
|
||||
|
||||
ring->gpu_caches_dirty = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring)
|
||||
{
|
||||
uint32_t flush_domains;
|
||||
int ret;
|
||||
|
||||
flush_domains = 0;
|
||||
if (ring->gpu_caches_dirty)
|
||||
flush_domains = I915_GEM_GPU_DOMAINS;
|
||||
|
||||
ret = ring->flush(ring, I915_GEM_GPU_DOMAINS, flush_domains);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
trace_i915_gem_ring_flush(ring, I915_GEM_GPU_DOMAINS, flush_domains);
|
||||
|
||||
ring->gpu_caches_dirty = false;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -72,7 +72,14 @@ struct intel_ring_buffer {
|
|||
u32 flush_domains);
|
||||
int (*add_request)(struct intel_ring_buffer *ring,
|
||||
u32 *seqno);
|
||||
u32 (*get_seqno)(struct intel_ring_buffer *ring);
|
||||
/* Some chipsets are not quite as coherent as advertised and need
|
||||
* an expensive kick to force a true read of the up-to-date seqno.
|
||||
* However, the up-to-date seqno is not always required and the last
|
||||
* seen value is good enough. Note that the seqno will always be
|
||||
* monotonic, even if not coherent.
|
||||
*/
|
||||
u32 (*get_seqno)(struct intel_ring_buffer *ring,
|
||||
bool lazy_coherency);
|
||||
int (*dispatch_execbuffer)(struct intel_ring_buffer *ring,
|
||||
u32 offset, u32 length);
|
||||
void (*cleanup)(struct intel_ring_buffer *ring);
|
||||
|
@ -100,15 +107,6 @@ struct intel_ring_buffer {
|
|||
*/
|
||||
struct list_head request_list;
|
||||
|
||||
/**
|
||||
* List of objects currently pending a GPU write flush.
|
||||
*
|
||||
* All elements on this list will belong to either the
|
||||
* active_list or flushing_list, last_rendering_seqno can
|
||||
* be used to differentiate between the two elements.
|
||||
*/
|
||||
struct list_head gpu_write_list;
|
||||
|
||||
/**
|
||||
* Do we have some not yet emitted requests outstanding?
|
||||
*/
|
||||
|
@ -204,6 +202,8 @@ static inline void intel_ring_emit(struct intel_ring_buffer *ring,
|
|||
void intel_ring_advance(struct intel_ring_buffer *ring);
|
||||
|
||||
u32 intel_ring_get_seqno(struct intel_ring_buffer *ring);
|
||||
int intel_ring_flush_all_caches(struct intel_ring_buffer *ring);
|
||||
int intel_ring_invalidate_all_caches(struct intel_ring_buffer *ring);
|
||||
|
||||
int intel_init_render_ring_buffer(struct drm_device *dev);
|
||||
int intel_init_bsd_ring_buffer(struct drm_device *dev);
|
||||
|
|
|
@ -96,7 +96,7 @@ struct intel_sdvo {
|
|||
/*
|
||||
* Hotplug activation bits for this device
|
||||
*/
|
||||
uint8_t hotplug_active[2];
|
||||
uint16_t hotplug_active;
|
||||
|
||||
/**
|
||||
* This is used to select the color range of RBG outputs in HDMI mode.
|
||||
|
@ -627,6 +627,14 @@ static bool intel_sdvo_set_active_outputs(struct intel_sdvo *intel_sdvo,
|
|||
&outputs, sizeof(outputs));
|
||||
}
|
||||
|
||||
static bool intel_sdvo_get_active_outputs(struct intel_sdvo *intel_sdvo,
|
||||
u16 *outputs)
|
||||
{
|
||||
return intel_sdvo_get_value(intel_sdvo,
|
||||
SDVO_CMD_GET_ACTIVE_OUTPUTS,
|
||||
outputs, sizeof(*outputs));
|
||||
}
|
||||
|
||||
static bool intel_sdvo_set_encoder_power_state(struct intel_sdvo *intel_sdvo,
|
||||
int mode)
|
||||
{
|
||||
|
@ -1141,51 +1149,132 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
|
|||
intel_sdvo_write_sdvox(intel_sdvo, sdvox);
|
||||
}
|
||||
|
||||
static void intel_sdvo_dpms(struct drm_encoder *encoder, int mode)
|
||||
static bool intel_sdvo_connector_get_hw_state(struct intel_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct intel_sdvo_connector *intel_sdvo_connector =
|
||||
to_intel_sdvo_connector(&connector->base);
|
||||
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(&connector->base);
|
||||
u16 active_outputs;
|
||||
|
||||
intel_sdvo_get_active_outputs(intel_sdvo, &active_outputs);
|
||||
|
||||
if (active_outputs & intel_sdvo_connector->output_flag)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool intel_sdvo_get_hw_state(struct intel_encoder *encoder,
|
||||
enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc);
|
||||
struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
|
||||
u32 tmp;
|
||||
|
||||
tmp = I915_READ(intel_sdvo->sdvo_reg);
|
||||
|
||||
if (!(tmp & SDVO_ENABLE))
|
||||
return false;
|
||||
|
||||
if (HAS_PCH_CPT(dev))
|
||||
*pipe = PORT_TO_PIPE_CPT(tmp);
|
||||
else
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void intel_disable_sdvo(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_i915_private *dev_priv = encoder->base.dev->dev_private;
|
||||
struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
|
||||
u32 temp;
|
||||
|
||||
intel_sdvo_set_active_outputs(intel_sdvo, 0);
|
||||
if (0)
|
||||
intel_sdvo_set_encoder_power_state(intel_sdvo,
|
||||
DRM_MODE_DPMS_OFF);
|
||||
|
||||
temp = I915_READ(intel_sdvo->sdvo_reg);
|
||||
if ((temp & SDVO_ENABLE) != 0) {
|
||||
intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
|
||||
}
|
||||
}
|
||||
|
||||
static void intel_enable_sdvo(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
|
||||
struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
|
||||
u32 temp;
|
||||
bool input1, input2;
|
||||
int i;
|
||||
u8 status;
|
||||
|
||||
temp = I915_READ(intel_sdvo->sdvo_reg);
|
||||
if ((temp & SDVO_ENABLE) == 0)
|
||||
intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE);
|
||||
for (i = 0; i < 2; i++)
|
||||
intel_wait_for_vblank(dev, intel_crtc->pipe);
|
||||
|
||||
status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
|
||||
/* Warn if the device reported failure to sync.
|
||||
* A lot of SDVO devices fail to notify of sync, but it's
|
||||
* a given it the status is a success, we succeeded.
|
||||
*/
|
||||
if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
|
||||
DRM_DEBUG_KMS("First %s output reported failure to "
|
||||
"sync\n", SDVO_NAME(intel_sdvo));
|
||||
}
|
||||
|
||||
if (0)
|
||||
intel_sdvo_set_encoder_power_state(intel_sdvo,
|
||||
DRM_MODE_DPMS_ON);
|
||||
intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
|
||||
}
|
||||
|
||||
static void intel_sdvo_dpms(struct drm_connector *connector, int mode)
|
||||
{
|
||||
struct drm_crtc *crtc;
|
||||
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
|
||||
|
||||
/* dvo supports only 2 dpms states. */
|
||||
if (mode != DRM_MODE_DPMS_ON)
|
||||
mode = DRM_MODE_DPMS_OFF;
|
||||
|
||||
if (mode == connector->dpms)
|
||||
return;
|
||||
|
||||
connector->dpms = mode;
|
||||
|
||||
/* Only need to change hw state when actually enabled */
|
||||
crtc = intel_sdvo->base.base.crtc;
|
||||
if (!crtc) {
|
||||
intel_sdvo->base.connectors_active = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode != DRM_MODE_DPMS_ON) {
|
||||
intel_sdvo_set_active_outputs(intel_sdvo, 0);
|
||||
if (0)
|
||||
intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
|
||||
|
||||
if (mode == DRM_MODE_DPMS_OFF) {
|
||||
temp = I915_READ(intel_sdvo->sdvo_reg);
|
||||
if ((temp & SDVO_ENABLE) != 0) {
|
||||
intel_sdvo_write_sdvox(intel_sdvo, temp & ~SDVO_ENABLE);
|
||||
}
|
||||
}
|
||||
intel_sdvo->base.connectors_active = false;
|
||||
|
||||
intel_crtc_update_dpms(crtc);
|
||||
} else {
|
||||
bool input1, input2;
|
||||
int i;
|
||||
u8 status;
|
||||
intel_sdvo->base.connectors_active = true;
|
||||
|
||||
temp = I915_READ(intel_sdvo->sdvo_reg);
|
||||
if ((temp & SDVO_ENABLE) == 0)
|
||||
intel_sdvo_write_sdvox(intel_sdvo, temp | SDVO_ENABLE);
|
||||
for (i = 0; i < 2; i++)
|
||||
intel_wait_for_vblank(dev, intel_crtc->pipe);
|
||||
|
||||
status = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
|
||||
/* Warn if the device reported failure to sync.
|
||||
* A lot of SDVO devices fail to notify of sync, but it's
|
||||
* a given it the status is a success, we succeeded.
|
||||
*/
|
||||
if (status == SDVO_CMD_STATUS_SUCCESS && !input1) {
|
||||
DRM_DEBUG_KMS("First %s output reported failure to "
|
||||
"sync\n", SDVO_NAME(intel_sdvo));
|
||||
}
|
||||
intel_crtc_update_dpms(crtc);
|
||||
|
||||
if (0)
|
||||
intel_sdvo_set_encoder_power_state(intel_sdvo, mode);
|
||||
intel_sdvo_set_active_outputs(intel_sdvo, intel_sdvo->attached_output);
|
||||
}
|
||||
return;
|
||||
|
||||
intel_modeset_check_state(connector->dev);
|
||||
}
|
||||
|
||||
static int intel_sdvo_mode_valid(struct drm_connector *connector,
|
||||
|
@ -1250,25 +1339,29 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in
|
|||
return true;
|
||||
}
|
||||
|
||||
static int intel_sdvo_supports_hotplug(struct intel_sdvo *intel_sdvo)
|
||||
static uint16_t intel_sdvo_get_hotplug_support(struct intel_sdvo *intel_sdvo)
|
||||
{
|
||||
struct drm_device *dev = intel_sdvo->base.base.dev;
|
||||
u8 response[2];
|
||||
uint16_t hotplug;
|
||||
|
||||
/* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise
|
||||
* on the line. */
|
||||
if (IS_I945G(dev) || IS_I945GM(dev))
|
||||
return false;
|
||||
return 0;
|
||||
|
||||
return intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
|
||||
&response, 2) && response[0];
|
||||
if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT,
|
||||
&hotplug, sizeof(hotplug)))
|
||||
return 0;
|
||||
|
||||
return hotplug;
|
||||
}
|
||||
|
||||
static void intel_sdvo_enable_hotplug(struct intel_encoder *encoder)
|
||||
{
|
||||
struct intel_sdvo *intel_sdvo = to_intel_sdvo(&encoder->base);
|
||||
|
||||
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG, &intel_sdvo->hotplug_active, 2);
|
||||
intel_sdvo_write_cmd(intel_sdvo, SDVO_CMD_SET_ACTIVE_HOT_PLUG,
|
||||
&intel_sdvo->hotplug_active, 2);
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -1344,7 +1437,6 @@ intel_sdvo_tmds_sink_detect(struct drm_connector *connector)
|
|||
}
|
||||
} else
|
||||
status = connector_status_disconnected;
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
|
||||
|
@ -1418,7 +1510,6 @@ intel_sdvo_detect(struct drm_connector *connector, bool force)
|
|||
else
|
||||
ret = connector_status_disconnected;
|
||||
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
} else
|
||||
ret = connector_status_connected;
|
||||
|
@ -1464,7 +1555,6 @@ static void intel_sdvo_get_ddc_modes(struct drm_connector *connector)
|
|||
drm_add_edid_modes(connector, edid);
|
||||
}
|
||||
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
}
|
||||
|
@ -1836,8 +1926,8 @@ set_value:
|
|||
done:
|
||||
if (intel_sdvo->base.base.crtc) {
|
||||
struct drm_crtc *crtc = intel_sdvo->base.base.crtc;
|
||||
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
|
||||
crtc->y, crtc->fb);
|
||||
intel_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y, crtc->fb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -1845,15 +1935,13 @@ done:
|
|||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_sdvo_helper_funcs = {
|
||||
.dpms = intel_sdvo_dpms,
|
||||
.mode_fixup = intel_sdvo_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.mode_set = intel_sdvo_mode_set,
|
||||
.commit = intel_encoder_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_sdvo_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_sdvo_dpms,
|
||||
.detect = intel_sdvo_detect,
|
||||
.fill_modes = drm_helper_probe_single_connector_modes,
|
||||
.set_property = intel_sdvo_set_property,
|
||||
|
@ -2025,6 +2113,7 @@ intel_sdvo_connector_init(struct intel_sdvo_connector *connector,
|
|||
connector->base.base.interlace_allowed = 1;
|
||||
connector->base.base.doublescan_allowed = 0;
|
||||
connector->base.base.display_info.subpixel_order = SubPixelHorizontalRGB;
|
||||
connector->base.get_hw_state = intel_sdvo_connector_get_hw_state;
|
||||
|
||||
intel_connector_attach_encoder(&connector->base, &encoder->base);
|
||||
drm_sysfs_connector_add(&connector->base.base);
|
||||
|
@ -2063,17 +2152,18 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
|
|||
|
||||
intel_connector = &intel_sdvo_connector->base;
|
||||
connector = &intel_connector->base;
|
||||
if (intel_sdvo_supports_hotplug(intel_sdvo) & (1 << device)) {
|
||||
if (intel_sdvo_get_hotplug_support(intel_sdvo) &
|
||||
intel_sdvo_connector->output_flag) {
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
intel_sdvo->hotplug_active[0] |= 1 << device;
|
||||
intel_sdvo->hotplug_active |= intel_sdvo_connector->output_flag;
|
||||
/* Some SDVO devices have one-shot hotplug interrupts.
|
||||
* Ensure that they get re-enabled when an interrupt happens.
|
||||
*/
|
||||
intel_encoder->hot_plug = intel_sdvo_enable_hotplug;
|
||||
intel_sdvo_enable_hotplug(intel_encoder);
|
||||
}
|
||||
else
|
||||
} else {
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT;
|
||||
}
|
||||
encoder->encoder_type = DRM_MODE_ENCODER_TMDS;
|
||||
connector->connector_type = DRM_MODE_CONNECTOR_DVID;
|
||||
|
||||
|
@ -2081,8 +2171,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, int device)
|
|||
connector->connector_type = DRM_MODE_CONNECTOR_HDMIA;
|
||||
intel_sdvo->is_hdmi = true;
|
||||
}
|
||||
intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
|
||||
(1 << INTEL_ANALOG_CLONE_BIT));
|
||||
intel_sdvo->base.cloneable = true;
|
||||
|
||||
intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
|
||||
if (intel_sdvo->is_hdmi)
|
||||
|
@ -2113,7 +2202,7 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, int type)
|
|||
|
||||
intel_sdvo->is_tv = true;
|
||||
intel_sdvo->base.needs_tv_clock = true;
|
||||
intel_sdvo->base.clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
|
||||
intel_sdvo->base.cloneable = false;
|
||||
|
||||
intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
|
||||
|
||||
|
@ -2156,8 +2245,7 @@ intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, int device)
|
|||
intel_sdvo_connector->output_flag = SDVO_OUTPUT_RGB1;
|
||||
}
|
||||
|
||||
intel_sdvo->base.clone_mask = ((1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
|
||||
(1 << INTEL_ANALOG_CLONE_BIT));
|
||||
intel_sdvo->base.cloneable = true;
|
||||
|
||||
intel_sdvo_connector_init(intel_sdvo_connector,
|
||||
intel_sdvo);
|
||||
|
@ -2189,8 +2277,10 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, int device)
|
|||
intel_sdvo_connector->output_flag = SDVO_OUTPUT_LVDS1;
|
||||
}
|
||||
|
||||
intel_sdvo->base.clone_mask = ((1 << INTEL_ANALOG_CLONE_BIT) |
|
||||
(1 << INTEL_SDVO_LVDS_CLONE_BIT));
|
||||
/* SDVO LVDS is cloneable because the SDVO encoder does the upscaling,
|
||||
* as opposed to native LVDS, where we upscale with the panel-fitter
|
||||
* (and hence only the native LVDS resolution could be cloned). */
|
||||
intel_sdvo->base.cloneable = true;
|
||||
|
||||
intel_sdvo_connector_init(intel_sdvo_connector, intel_sdvo);
|
||||
if (!intel_sdvo_create_enhance_property(intel_sdvo, intel_sdvo_connector))
|
||||
|
@ -2575,6 +2665,10 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
|
|||
|
||||
drm_encoder_helper_add(&intel_encoder->base, &intel_sdvo_helper_funcs);
|
||||
|
||||
intel_encoder->disable = intel_disable_sdvo;
|
||||
intel_encoder->enable = intel_enable_sdvo;
|
||||
intel_encoder->get_hw_state = intel_sdvo_get_hw_state;
|
||||
|
||||
/* In default case sdvo lvds is false */
|
||||
if (!intel_sdvo_get_capabilities(intel_sdvo, &intel_sdvo->caps))
|
||||
goto err;
|
||||
|
@ -2589,7 +2683,7 @@ bool intel_sdvo_init(struct drm_device *dev, uint32_t sdvo_reg, bool is_sdvob)
|
|||
/* Only enable the hotplug irq if we need it, to work around noisy
|
||||
* hotplug lines.
|
||||
*/
|
||||
if (intel_sdvo->hotplug_active[0])
|
||||
if (intel_sdvo->hotplug_active)
|
||||
dev_priv->hotplug_supported_mask |= hotplug_mask;
|
||||
|
||||
intel_sdvo_select_ddc_bus(dev_priv, intel_sdvo, sdvo_reg);
|
||||
|
|
|
@ -835,22 +835,37 @@ static struct intel_tv *intel_attached_tv(struct drm_connector *connector)
|
|||
base);
|
||||
}
|
||||
|
||||
static void
|
||||
intel_tv_dpms(struct drm_encoder *encoder, int mode)
|
||||
static bool
|
||||
intel_tv_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
u32 tmp = I915_READ(TV_CTL);
|
||||
|
||||
if (!(tmp & TV_ENC_ENABLE))
|
||||
return false;
|
||||
|
||||
*pipe = PORT_TO_PIPE(tmp);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
intel_enable_tv(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
switch (mode) {
|
||||
case DRM_MODE_DPMS_ON:
|
||||
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
|
||||
break;
|
||||
case DRM_MODE_DPMS_STANDBY:
|
||||
case DRM_MODE_DPMS_SUSPEND:
|
||||
case DRM_MODE_DPMS_OFF:
|
||||
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
|
||||
break;
|
||||
}
|
||||
I915_WRITE(TV_CTL, I915_READ(TV_CTL) | TV_ENC_ENABLE);
|
||||
}
|
||||
|
||||
static void
|
||||
intel_disable_tv(struct intel_encoder *encoder)
|
||||
{
|
||||
struct drm_device *dev = encoder->base.dev;
|
||||
struct drm_i915_private *dev_priv = dev->dev_private;
|
||||
|
||||
I915_WRITE(TV_CTL, I915_READ(TV_CTL) & ~TV_ENC_ENABLE);
|
||||
}
|
||||
|
||||
static const struct tv_mode *
|
||||
|
@ -894,17 +909,14 @@ intel_tv_mode_fixup(struct drm_encoder *encoder,
|
|||
const struct drm_display_mode *mode,
|
||||
struct drm_display_mode *adjusted_mode)
|
||||
{
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct intel_tv *intel_tv = enc_to_intel_tv(encoder);
|
||||
const struct tv_mode *tv_mode = intel_tv_mode_find(intel_tv);
|
||||
struct intel_encoder *other_encoder;
|
||||
|
||||
if (!tv_mode)
|
||||
return false;
|
||||
|
||||
for_each_encoder_on_crtc(dev, encoder->crtc, other_encoder)
|
||||
if (&other_encoder->base != encoder)
|
||||
return false;
|
||||
if (intel_encoder_check_is_cloned(&intel_tv->base))
|
||||
return false;
|
||||
|
||||
adjusted_mode->clock = tv_mode->clock;
|
||||
return true;
|
||||
|
@ -1302,12 +1314,9 @@ intel_tv_detect(struct drm_connector *connector, bool force)
|
|||
if (force) {
|
||||
struct intel_load_detect_pipe tmp;
|
||||
|
||||
if (intel_get_load_detect_pipe(&intel_tv->base, connector,
|
||||
&mode, &tmp)) {
|
||||
if (intel_get_load_detect_pipe(connector, &mode, &tmp)) {
|
||||
type = intel_tv_detect_type(intel_tv, connector);
|
||||
intel_release_load_detect_pipe(&intel_tv->base,
|
||||
connector,
|
||||
&tmp);
|
||||
intel_release_load_detect_pipe(connector, &tmp);
|
||||
} else
|
||||
return connector_status_unknown;
|
||||
} else
|
||||
|
@ -1473,22 +1482,20 @@ intel_tv_set_property(struct drm_connector *connector, struct drm_property *prop
|
|||
}
|
||||
|
||||
if (changed && crtc)
|
||||
drm_crtc_helper_set_mode(crtc, &crtc->mode, crtc->x,
|
||||
crtc->y, crtc->fb);
|
||||
intel_set_mode(crtc, &crtc->mode,
|
||||
crtc->x, crtc->y, crtc->fb);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct drm_encoder_helper_funcs intel_tv_helper_funcs = {
|
||||
.dpms = intel_tv_dpms,
|
||||
.mode_fixup = intel_tv_mode_fixup,
|
||||
.prepare = intel_encoder_prepare,
|
||||
.mode_set = intel_tv_mode_set,
|
||||
.commit = intel_encoder_commit,
|
||||
.disable = intel_encoder_noop,
|
||||
};
|
||||
|
||||
static const struct drm_connector_funcs intel_tv_connector_funcs = {
|
||||
.dpms = drm_helper_connector_dpms,
|
||||
.dpms = intel_connector_dpms,
|
||||
.detect = intel_tv_detect,
|
||||
.destroy = intel_tv_destroy,
|
||||
.set_property = intel_tv_set_property,
|
||||
|
@ -1618,10 +1625,15 @@ intel_tv_init(struct drm_device *dev)
|
|||
drm_encoder_init(dev, &intel_encoder->base, &intel_tv_enc_funcs,
|
||||
DRM_MODE_ENCODER_TVDAC);
|
||||
|
||||
intel_encoder->enable = intel_enable_tv;
|
||||
intel_encoder->disable = intel_disable_tv;
|
||||
intel_encoder->get_hw_state = intel_tv_get_hw_state;
|
||||
intel_connector->get_hw_state = intel_connector_get_hw_state;
|
||||
|
||||
intel_connector_attach_encoder(intel_connector, intel_encoder);
|
||||
intel_encoder->type = INTEL_OUTPUT_TVOUT;
|
||||
intel_encoder->crtc_mask = (1 << 0) | (1 << 1);
|
||||
intel_encoder->clone_mask = (1 << INTEL_TV_CLONE_BIT);
|
||||
intel_encoder->cloneable = false;
|
||||
intel_encoder->base.possible_crtcs = ((1 << 0) | (1 << 1));
|
||||
intel_encoder->base.possible_clones = (1 << INTEL_OUTPUT_TVOUT);
|
||||
intel_tv->type = DRM_MODE_CONNECTOR_Unknown;
|
||||
|
|
|
@ -195,7 +195,6 @@ struct mga_device {
|
|||
struct drm_global_reference mem_global_ref;
|
||||
struct ttm_bo_global_ref bo_global_ref;
|
||||
struct ttm_bo_device bdev;
|
||||
atomic_t validate_sequence;
|
||||
} ttm;
|
||||
|
||||
u32 reg_1e24; /* SE model number */
|
||||
|
|
|
@ -1398,7 +1398,6 @@ static int mga_vga_get_modes(struct drm_connector *connector)
|
|||
if (edid) {
|
||||
drm_mode_connector_update_edid_property(connector, edid);
|
||||
ret = drm_add_edid_modes(connector, edid);
|
||||
connector->display_info.raw_edid = NULL;
|
||||
kfree(edid);
|
||||
}
|
||||
return ret;
|
||||
|
|
|
@ -17,6 +17,34 @@ config DRM_NOUVEAU
|
|||
help
|
||||
Choose this option for open-source nVidia support.
|
||||
|
||||
config NOUVEAU_DEBUG
|
||||
int "Maximum debug level"
|
||||
depends on DRM_NOUVEAU
|
||||
range 0 7
|
||||
default 5
|
||||
help
|
||||
Selects the maximum debug level to compile support for.
|
||||
|
||||
0 - fatal
|
||||
1 - error
|
||||
2 - warning
|
||||
3 - info
|
||||
4 - debug
|
||||
5 - trace (recommended)
|
||||
6 - paranoia
|
||||
7 - spam
|
||||
|
||||
The paranoia and spam levels will add a lot of extra checks which
|
||||
may potentially slow down driver operation.
|
||||
|
||||
config NOUVEAU_DEBUG_DEFAULT
|
||||
int "Default debug level"
|
||||
depends on DRM_NOUVEAU
|
||||
range 0 7
|
||||
default 3
|
||||
help
|
||||
Selects the default debug level
|
||||
|
||||
config DRM_NOUVEAU_BACKLIGHT
|
||||
bool "Support for backlight control"
|
||||
depends on DRM_NOUVEAU
|
||||
|
@ -25,14 +53,6 @@ config DRM_NOUVEAU_BACKLIGHT
|
|||
Say Y here if you want to control the backlight of your display
|
||||
(e.g. a laptop panel).
|
||||
|
||||
config DRM_NOUVEAU_DEBUG
|
||||
bool "Build in Nouveau's debugfs support"
|
||||
depends on DRM_NOUVEAU && DEBUG_FS
|
||||
default y
|
||||
help
|
||||
Say Y here if you want Nouveau to output debugging information
|
||||
via debugfs.
|
||||
|
||||
menu "I2C encoder or helper chips"
|
||||
depends on DRM && DRM_KMS_HELPER && I2C
|
||||
|
||||
|
|
|
@ -3,49 +3,190 @@
|
|||
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
|
||||
|
||||
ccflags-y := -Iinclude/drm
|
||||
nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
|
||||
nouveau_gpuobj.o nouveau_irq.o nouveau_notifier.o \
|
||||
nouveau_sgdma.o nouveau_dma.o nouveau_util.o \
|
||||
nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
|
||||
nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
|
||||
nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
|
||||
nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \
|
||||
nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \
|
||||
nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \
|
||||
nouveau_abi16.o \
|
||||
nv04_timer.o \
|
||||
nv04_mc.o nv40_mc.o nv50_mc.o \
|
||||
nv04_fb.o nv10_fb.o nv20_fb.o nv30_fb.o nv40_fb.o \
|
||||
nv50_fb.o nvc0_fb.o \
|
||||
nv04_fifo.o nv10_fifo.o nv17_fifo.o nv40_fifo.o nv50_fifo.o \
|
||||
nv84_fifo.o nvc0_fifo.o nve0_fifo.o \
|
||||
nv04_fence.o nv10_fence.o nv84_fence.o nvc0_fence.o \
|
||||
nv04_software.o nv50_software.o nvc0_software.o \
|
||||
nv04_graph.o nv10_graph.o nv20_graph.o \
|
||||
nv40_graph.o nv50_graph.o nvc0_graph.o nve0_graph.o \
|
||||
nv40_grctx.o nv50_grctx.o nvc0_grctx.o nve0_grctx.o \
|
||||
nv84_crypt.o nv98_crypt.o \
|
||||
nva3_copy.o nvc0_copy.o \
|
||||
nv31_mpeg.o nv50_mpeg.o \
|
||||
nv84_bsp.o \
|
||||
nv84_vp.o \
|
||||
nv98_ppp.o \
|
||||
nv04_instmem.o nv50_instmem.o nvc0_instmem.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 \
|
||||
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 \
|
||||
nv10_gpio.o nv50_gpio.o \
|
||||
nv50_calc.o \
|
||||
nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \
|
||||
nv50_vram.o nvc0_vram.o \
|
||||
nv50_vm.o nvc0_vm.o nouveau_prime.o
|
||||
ccflags-y += -I$(src)/core/include
|
||||
ccflags-y += -I$(src)/core
|
||||
ccflags-y += -I$(src)
|
||||
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
|
||||
nouveau-y := core/core/client.o
|
||||
nouveau-y += core/core/engctx.o
|
||||
nouveau-y += core/core/engine.o
|
||||
nouveau-y += core/core/enum.o
|
||||
nouveau-y += core/core/gpuobj.o
|
||||
nouveau-y += core/core/handle.o
|
||||
nouveau-y += core/core/mm.o
|
||||
nouveau-y += core/core/namedb.o
|
||||
nouveau-y += core/core/object.o
|
||||
nouveau-y += core/core/option.o
|
||||
nouveau-y += core/core/parent.o
|
||||
nouveau-y += core/core/printk.o
|
||||
nouveau-y += core/core/ramht.o
|
||||
nouveau-y += core/core/subdev.o
|
||||
|
||||
nouveau-y += core/subdev/bar/base.o
|
||||
nouveau-y += core/subdev/bar/nv50.o
|
||||
nouveau-y += core/subdev/bar/nvc0.o
|
||||
nouveau-y += core/subdev/bios/base.o
|
||||
nouveau-y += core/subdev/bios/bit.o
|
||||
nouveau-y += core/subdev/bios/conn.o
|
||||
nouveau-y += core/subdev/bios/dcb.o
|
||||
nouveau-y += core/subdev/bios/dp.o
|
||||
nouveau-y += core/subdev/bios/extdev.o
|
||||
nouveau-y += core/subdev/bios/gpio.o
|
||||
nouveau-y += core/subdev/bios/i2c.o
|
||||
nouveau-y += core/subdev/bios/init.o
|
||||
nouveau-y += core/subdev/bios/mxm.o
|
||||
nouveau-y += core/subdev/bios/perf.o
|
||||
nouveau-y += core/subdev/bios/pll.o
|
||||
nouveau-y += core/subdev/bios/therm.o
|
||||
nouveau-y += core/subdev/clock/nv04.o
|
||||
nouveau-y += core/subdev/clock/nv40.o
|
||||
nouveau-y += core/subdev/clock/nv50.o
|
||||
nouveau-y += core/subdev/clock/nva3.o
|
||||
nouveau-y += core/subdev/clock/nvc0.o
|
||||
nouveau-y += core/subdev/clock/pllnv04.o
|
||||
nouveau-y += core/subdev/clock/pllnva3.o
|
||||
nouveau-y += core/subdev/device/base.o
|
||||
nouveau-y += core/subdev/device/nv04.o
|
||||
nouveau-y += core/subdev/device/nv10.o
|
||||
nouveau-y += core/subdev/device/nv20.o
|
||||
nouveau-y += core/subdev/device/nv30.o
|
||||
nouveau-y += core/subdev/device/nv40.o
|
||||
nouveau-y += core/subdev/device/nv50.o
|
||||
nouveau-y += core/subdev/device/nvc0.o
|
||||
nouveau-y += core/subdev/device/nve0.o
|
||||
nouveau-y += core/subdev/devinit/base.o
|
||||
nouveau-y += core/subdev/devinit/nv04.o
|
||||
nouveau-y += core/subdev/devinit/nv05.o
|
||||
nouveau-y += core/subdev/devinit/nv10.o
|
||||
nouveau-y += core/subdev/devinit/nv1a.o
|
||||
nouveau-y += core/subdev/devinit/nv20.o
|
||||
nouveau-y += core/subdev/devinit/nv50.o
|
||||
nouveau-y += core/subdev/fb/base.o
|
||||
nouveau-y += core/subdev/fb/nv04.o
|
||||
nouveau-y += core/subdev/fb/nv10.o
|
||||
nouveau-y += core/subdev/fb/nv20.o
|
||||
nouveau-y += core/subdev/fb/nv30.o
|
||||
nouveau-y += core/subdev/fb/nv40.o
|
||||
nouveau-y += core/subdev/fb/nv50.o
|
||||
nouveau-y += core/subdev/fb/nvc0.o
|
||||
nouveau-y += core/subdev/gpio/base.o
|
||||
nouveau-y += core/subdev/gpio/nv10.o
|
||||
nouveau-y += core/subdev/gpio/nv50.o
|
||||
nouveau-y += core/subdev/gpio/nvd0.o
|
||||
nouveau-y += core/subdev/i2c/base.o
|
||||
nouveau-y += core/subdev/i2c/aux.o
|
||||
nouveau-y += core/subdev/i2c/bit.o
|
||||
nouveau-y += core/subdev/ibus/nvc0.o
|
||||
nouveau-y += core/subdev/ibus/nve0.o
|
||||
nouveau-y += core/subdev/instmem/base.o
|
||||
nouveau-y += core/subdev/instmem/nv04.o
|
||||
nouveau-y += core/subdev/instmem/nv40.o
|
||||
nouveau-y += core/subdev/instmem/nv50.o
|
||||
nouveau-y += core/subdev/ltcg/nvc0.o
|
||||
nouveau-y += core/subdev/mc/base.o
|
||||
nouveau-y += core/subdev/mc/nv04.o
|
||||
nouveau-y += core/subdev/mc/nv44.o
|
||||
nouveau-y += core/subdev/mc/nv50.o
|
||||
nouveau-y += core/subdev/mc/nv98.o
|
||||
nouveau-y += core/subdev/mc/nvc0.o
|
||||
nouveau-y += core/subdev/mxm/base.o
|
||||
nouveau-y += core/subdev/mxm/mxms.o
|
||||
nouveau-y += core/subdev/mxm/nv50.o
|
||||
nouveau-y += core/subdev/therm/base.o
|
||||
nouveau-y += core/subdev/therm/fan.o
|
||||
nouveau-y += core/subdev/therm/ic.o
|
||||
nouveau-y += core/subdev/therm/nv40.o
|
||||
nouveau-y += core/subdev/therm/nv50.o
|
||||
nouveau-y += core/subdev/therm/temp.o
|
||||
nouveau-y += core/subdev/timer/base.o
|
||||
nouveau-y += core/subdev/timer/nv04.o
|
||||
nouveau-y += core/subdev/vm/base.o
|
||||
nouveau-y += core/subdev/vm/nv04.o
|
||||
nouveau-y += core/subdev/vm/nv41.o
|
||||
nouveau-y += core/subdev/vm/nv44.o
|
||||
nouveau-y += core/subdev/vm/nv50.o
|
||||
nouveau-y += core/subdev/vm/nvc0.o
|
||||
|
||||
nouveau-y += core/engine/dmaobj/base.o
|
||||
nouveau-y += core/engine/dmaobj/nv04.o
|
||||
nouveau-y += core/engine/dmaobj/nv50.o
|
||||
nouveau-y += core/engine/dmaobj/nvc0.o
|
||||
nouveau-y += core/engine/bsp/nv84.o
|
||||
nouveau-y += core/engine/copy/nva3.o
|
||||
nouveau-y += core/engine/copy/nvc0.o
|
||||
nouveau-y += core/engine/copy/nve0.o
|
||||
nouveau-y += core/engine/crypt/nv84.o
|
||||
nouveau-y += core/engine/crypt/nv98.o
|
||||
nouveau-y += core/engine/disp/nv04.o
|
||||
nouveau-y += core/engine/disp/nv50.o
|
||||
nouveau-y += core/engine/disp/nvd0.o
|
||||
nouveau-y += core/engine/disp/vga.o
|
||||
nouveau-y += core/engine/fifo/base.o
|
||||
nouveau-y += core/engine/fifo/nv04.o
|
||||
nouveau-y += core/engine/fifo/nv10.o
|
||||
nouveau-y += core/engine/fifo/nv17.o
|
||||
nouveau-y += core/engine/fifo/nv40.o
|
||||
nouveau-y += core/engine/fifo/nv50.o
|
||||
nouveau-y += core/engine/fifo/nv84.o
|
||||
nouveau-y += core/engine/fifo/nvc0.o
|
||||
nouveau-y += core/engine/fifo/nve0.o
|
||||
nouveau-y += core/engine/graph/ctxnv40.o
|
||||
nouveau-y += core/engine/graph/ctxnv50.o
|
||||
nouveau-y += core/engine/graph/ctxnvc0.o
|
||||
nouveau-y += core/engine/graph/ctxnve0.o
|
||||
nouveau-y += core/engine/graph/nv04.o
|
||||
nouveau-y += core/engine/graph/nv10.o
|
||||
nouveau-y += core/engine/graph/nv20.o
|
||||
nouveau-y += core/engine/graph/nv25.o
|
||||
nouveau-y += core/engine/graph/nv2a.o
|
||||
nouveau-y += core/engine/graph/nv30.o
|
||||
nouveau-y += core/engine/graph/nv34.o
|
||||
nouveau-y += core/engine/graph/nv35.o
|
||||
nouveau-y += core/engine/graph/nv40.o
|
||||
nouveau-y += core/engine/graph/nv50.o
|
||||
nouveau-y += core/engine/graph/nvc0.o
|
||||
nouveau-y += core/engine/graph/nve0.o
|
||||
nouveau-y += core/engine/mpeg/nv31.o
|
||||
nouveau-y += core/engine/mpeg/nv40.o
|
||||
nouveau-y += core/engine/mpeg/nv50.o
|
||||
nouveau-y += core/engine/mpeg/nv84.o
|
||||
nouveau-y += core/engine/ppp/nv98.o
|
||||
nouveau-y += core/engine/software/nv04.o
|
||||
nouveau-y += core/engine/software/nv10.o
|
||||
nouveau-y += core/engine/software/nv50.o
|
||||
nouveau-y += core/engine/software/nvc0.o
|
||||
nouveau-y += core/engine/vp/nv84.o
|
||||
|
||||
# drm/core
|
||||
nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o
|
||||
nouveau-y += nouveau_irq.o nouveau_vga.o nouveau_agp.o
|
||||
nouveau-y += nouveau_ttm.o nouveau_sgdma.o nouveau_bo.o nouveau_gem.o
|
||||
nouveau-y += nouveau_prime.o nouveau_abi16.o
|
||||
nouveau-y += nv04_fence.o nv10_fence.o nv50_fence.o nv84_fence.o nvc0_fence.o
|
||||
|
||||
# drm/kms
|
||||
nouveau-y += nouveau_bios.o nouveau_fbcon.o nouveau_display.o
|
||||
nouveau-y += nouveau_connector.o nouveau_hdmi.o nouveau_dp.o
|
||||
nouveau-y += nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o
|
||||
|
||||
# drm/kms/nv04:nv50
|
||||
nouveau-y += nouveau_hw.o nouveau_calc.o
|
||||
nouveau-y += nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o
|
||||
nouveau-y += nv04_crtc.o nv04_display.o nv04_cursor.o
|
||||
|
||||
# drm/kms/nv50-
|
||||
nouveau-y += nv50_display.o nvd0_display.o
|
||||
nouveau-y += nv50_crtc.o nv50_dac.o nv50_sor.o nv50_cursor.o
|
||||
nouveau-y += nv50_evo.o
|
||||
|
||||
# drm/pm
|
||||
nouveau-y += nouveau_pm.o nouveau_volt.o nouveau_perf.o
|
||||
nouveau-y += nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o
|
||||
nouveau-y += nouveau_mem.o
|
||||
|
||||
# other random bits
|
||||
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
|
||||
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
|
||||
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
|
||||
|
||||
obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/client.h>
|
||||
#include <core/handle.h>
|
||||
#include <core/option.h>
|
||||
|
||||
#include <subdev/device.h>
|
||||
|
||||
static void
|
||||
nouveau_client_dtor(struct nouveau_object *object)
|
||||
{
|
||||
struct nouveau_client *client = (void *)object;
|
||||
nouveau_object_ref(NULL, &client->device);
|
||||
nouveau_handle_destroy(client->root);
|
||||
nouveau_namedb_destroy(&client->base);
|
||||
}
|
||||
|
||||
static struct nouveau_oclass
|
||||
nouveau_client_oclass = {
|
||||
.ofuncs = &(struct nouveau_ofuncs) {
|
||||
.dtor = nouveau_client_dtor,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
nouveau_client_create_(const char *name, u64 devname, const char *cfg,
|
||||
const char *dbg, int length, void **pobject)
|
||||
{
|
||||
struct nouveau_object *device;
|
||||
struct nouveau_client *client;
|
||||
int ret;
|
||||
|
||||
device = (void *)nouveau_device_find(devname);
|
||||
if (!device)
|
||||
return -ENODEV;
|
||||
|
||||
ret = nouveau_namedb_create_(NULL, NULL, &nouveau_client_oclass,
|
||||
NV_CLIENT_CLASS, nouveau_device_sclass,
|
||||
0, length, pobject);
|
||||
client = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = nouveau_handle_create(nv_object(client), ~0, ~0,
|
||||
nv_object(client), &client->root);
|
||||
if (ret) {
|
||||
nouveau_namedb_destroy(&client->base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* prevent init/fini being called, os in in charge of this */
|
||||
atomic_set(&nv_object(client)->usecount, 2);
|
||||
|
||||
nouveau_object_ref(device, &client->device);
|
||||
snprintf(client->name, sizeof(client->name), "%s", name);
|
||||
client->debug = nouveau_dbgopt(dbg, "CLIENT");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_client_init(struct nouveau_client *client)
|
||||
{
|
||||
int ret;
|
||||
nv_debug(client, "init running\n");
|
||||
ret = nouveau_handle_init(client->root);
|
||||
nv_debug(client, "init completed with %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_client_fini(struct nouveau_client *client, bool suspend)
|
||||
{
|
||||
const char *name[2] = { "fini", "suspend" };
|
||||
int ret;
|
||||
|
||||
nv_debug(client, "%s running\n", name[suspend]);
|
||||
ret = nouveau_handle_fini(client->root, suspend);
|
||||
nv_debug(client, "%s completed with %d\n", name[suspend], ret);
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/namedb.h>
|
||||
#include <core/handle.h>
|
||||
#include <core/client.h>
|
||||
#include <core/engctx.h>
|
||||
|
||||
#include <subdev/vm.h>
|
||||
|
||||
static inline int
|
||||
nouveau_engctx_exists(struct nouveau_object *parent,
|
||||
struct nouveau_engine *engine, void **pobject)
|
||||
{
|
||||
struct nouveau_engctx *engctx;
|
||||
struct nouveau_object *parctx;
|
||||
|
||||
list_for_each_entry(engctx, &engine->contexts, head) {
|
||||
parctx = nv_pclass(nv_object(engctx), NV_PARENT_CLASS);
|
||||
if (parctx == parent) {
|
||||
atomic_inc(&nv_object(engctx)->refcount);
|
||||
*pobject = engctx;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_engctx_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engobj,
|
||||
struct nouveau_oclass *oclass,
|
||||
struct nouveau_object *pargpu,
|
||||
u32 size, u32 align, u32 flags,
|
||||
int length, void **pobject)
|
||||
{
|
||||
struct nouveau_client *client = nouveau_client(parent);
|
||||
struct nouveau_engine *engine = nv_engine(engobj);
|
||||
struct nouveau_object *engctx;
|
||||
unsigned long save;
|
||||
int ret;
|
||||
|
||||
/* check if this engine already has a context for the parent object,
|
||||
* and reference it instead of creating a new one
|
||||
*/
|
||||
spin_lock_irqsave(&engine->lock, save);
|
||||
ret = nouveau_engctx_exists(parent, engine, pobject);
|
||||
spin_unlock_irqrestore(&engine->lock, save);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* create the new context, supports creating both raw objects and
|
||||
* objects backed by instance memory
|
||||
*/
|
||||
if (size) {
|
||||
ret = nouveau_gpuobj_create_(parent, engobj, oclass,
|
||||
NV_ENGCTX_CLASS,
|
||||
pargpu, size, align, flags,
|
||||
length, pobject);
|
||||
} else {
|
||||
ret = nouveau_object_create_(parent, engobj, oclass,
|
||||
NV_ENGCTX_CLASS, length, pobject);
|
||||
}
|
||||
|
||||
engctx = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* must take the lock again and re-check a context doesn't already
|
||||
* exist (in case of a race) - the lock had to be dropped before as
|
||||
* it's not possible to allocate the object with it held.
|
||||
*/
|
||||
spin_lock_irqsave(&engine->lock, save);
|
||||
ret = nouveau_engctx_exists(parent, engine, pobject);
|
||||
if (ret) {
|
||||
spin_unlock_irqrestore(&engine->lock, save);
|
||||
nouveau_object_ref(NULL, &engctx);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (client->vm)
|
||||
atomic_inc(&client->vm->engref[nv_engidx(engobj)]);
|
||||
list_add(&nv_engctx(engctx)->head, &engine->contexts);
|
||||
nv_engctx(engctx)->addr = ~0ULL;
|
||||
spin_unlock_irqrestore(&engine->lock, save);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_engctx_destroy(struct nouveau_engctx *engctx)
|
||||
{
|
||||
struct nouveau_object *engobj = nv_object(engctx)->engine;
|
||||
struct nouveau_engine *engine = nv_engine(engobj);
|
||||
struct nouveau_client *client = nouveau_client(engctx);
|
||||
unsigned long save;
|
||||
|
||||
nouveau_gpuobj_unmap(&engctx->vma);
|
||||
spin_lock_irqsave(&engine->lock, save);
|
||||
list_del(&engctx->head);
|
||||
spin_unlock_irqrestore(&engine->lock, save);
|
||||
|
||||
if (client->vm)
|
||||
atomic_dec(&client->vm->engref[nv_engidx(engobj)]);
|
||||
|
||||
if (engctx->base.size)
|
||||
nouveau_gpuobj_destroy(&engctx->base);
|
||||
else
|
||||
nouveau_object_destroy(&engctx->base.base);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_engctx_init(struct nouveau_engctx *engctx)
|
||||
{
|
||||
struct nouveau_object *object = nv_object(engctx);
|
||||
struct nouveau_subdev *subdev = nv_subdev(object->engine);
|
||||
struct nouveau_object *parent;
|
||||
struct nouveau_subdev *pardev;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_gpuobj_init(&engctx->base);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parent = nv_pclass(object->parent, NV_PARENT_CLASS);
|
||||
pardev = nv_subdev(parent->engine);
|
||||
if (nv_parent(parent)->context_attach) {
|
||||
mutex_lock(&pardev->mutex);
|
||||
ret = nv_parent(parent)->context_attach(parent, object);
|
||||
mutex_unlock(&pardev->mutex);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
nv_error(parent, "failed to attach %s context, %d\n",
|
||||
subdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nv_debug(parent, "attached %s context\n", subdev->name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_engctx_fini(struct nouveau_engctx *engctx, bool suspend)
|
||||
{
|
||||
struct nouveau_object *object = nv_object(engctx);
|
||||
struct nouveau_subdev *subdev = nv_subdev(object->engine);
|
||||
struct nouveau_object *parent;
|
||||
struct nouveau_subdev *pardev;
|
||||
int ret = 0;
|
||||
|
||||
parent = nv_pclass(object->parent, NV_PARENT_CLASS);
|
||||
pardev = nv_subdev(parent->engine);
|
||||
if (nv_parent(parent)->context_detach) {
|
||||
mutex_lock(&pardev->mutex);
|
||||
ret = nv_parent(parent)->context_detach(parent, suspend, object);
|
||||
mutex_unlock(&pardev->mutex);
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
nv_error(parent, "failed to detach %s context, %d\n",
|
||||
subdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
nv_debug(parent, "detached %s context\n", subdev->name);
|
||||
return nouveau_gpuobj_fini(&engctx->base, suspend);
|
||||
}
|
||||
|
||||
void
|
||||
_nouveau_engctx_dtor(struct nouveau_object *object)
|
||||
{
|
||||
nouveau_engctx_destroy(nv_engctx(object));
|
||||
}
|
||||
|
||||
int
|
||||
_nouveau_engctx_init(struct nouveau_object *object)
|
||||
{
|
||||
return nouveau_engctx_init(nv_engctx(object));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
_nouveau_engctx_fini(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
return nouveau_engctx_fini(nv_engctx(object), suspend);
|
||||
}
|
||||
|
||||
struct nouveau_object *
|
||||
nouveau_engctx_get(struct nouveau_engine *engine, u64 addr)
|
||||
{
|
||||
struct nouveau_engctx *engctx;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&engine->lock, flags);
|
||||
list_for_each_entry(engctx, &engine->contexts, head) {
|
||||
if (engctx->addr == addr) {
|
||||
engctx->save = flags;
|
||||
return nv_object(engctx);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&engine->lock, flags);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_engctx_put(struct nouveau_object *object)
|
||||
{
|
||||
if (object) {
|
||||
struct nouveau_engine *engine = nv_engine(object->engine);
|
||||
struct nouveau_engctx *engctx = nv_engctx(object);
|
||||
spin_unlock_irqrestore(&engine->lock, engctx->save);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/device.h>
|
||||
#include <core/engine.h>
|
||||
#include <core/option.h>
|
||||
|
||||
int
|
||||
nouveau_engine_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engobj,
|
||||
struct nouveau_oclass *oclass, bool enable,
|
||||
const char *iname, const char *fname,
|
||||
int length, void **pobject)
|
||||
{
|
||||
struct nouveau_device *device = nv_device(parent);
|
||||
struct nouveau_engine *engine;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_subdev_create_(parent, engobj, oclass, NV_ENGINE_CLASS,
|
||||
iname, fname, length, pobject);
|
||||
engine = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!nouveau_boolopt(device->cfgopt, iname, enable)) {
|
||||
if (!enable)
|
||||
nv_warn(engine, "disabled, %s=1 to enable\n", iname);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&engine->contexts);
|
||||
spin_lock_init(&engine->lock);
|
||||
return 0;
|
||||
}
|
|
@ -25,27 +25,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <linux/ratelimit.h>
|
||||
|
||||
#include "nouveau_util.h"
|
||||
|
||||
static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20);
|
||||
|
||||
void
|
||||
nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
|
||||
{
|
||||
while (bf->name) {
|
||||
if (value & bf->mask) {
|
||||
printk(" %s", bf->name);
|
||||
value &= ~bf->mask;
|
||||
}
|
||||
|
||||
bf++;
|
||||
}
|
||||
|
||||
if (value)
|
||||
printk(" (unknown bits 0x%08x)", value);
|
||||
}
|
||||
#include <core/os.h>
|
||||
#include <core/enum.h>
|
||||
|
||||
const struct nouveau_enum *
|
||||
nouveau_enum_find(const struct nouveau_enum *en, u32 value)
|
||||
|
@ -63,16 +44,24 @@ void
|
|||
nouveau_enum_print(const struct nouveau_enum *en, u32 value)
|
||||
{
|
||||
en = nouveau_enum_find(en, value);
|
||||
if (en) {
|
||||
if (en)
|
||||
printk("%s", en->name);
|
||||
return;
|
||||
else
|
||||
printk("(unknown enum 0x%08x)", value);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_bitfield_print(const struct nouveau_bitfield *bf, u32 value)
|
||||
{
|
||||
while (bf->name) {
|
||||
if (value & bf->mask) {
|
||||
printk(" %s", bf->name);
|
||||
value &= ~bf->mask;
|
||||
}
|
||||
|
||||
bf++;
|
||||
}
|
||||
|
||||
printk("(unknown enum 0x%08x)", value);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_ratelimit(void)
|
||||
{
|
||||
return __ratelimit(&nouveau_ratelimit_state);
|
||||
if (value)
|
||||
printk(" (unknown bits 0x%08x)", value);
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/gpuobj.h>
|
||||
|
||||
#include <subdev/instmem.h>
|
||||
#include <subdev/bar.h>
|
||||
#include <subdev/vm.h>
|
||||
|
||||
void
|
||||
nouveau_gpuobj_destroy(struct nouveau_gpuobj *gpuobj)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE) {
|
||||
for (i = 0; i < gpuobj->size; i += 4)
|
||||
nv_wo32(gpuobj, i, 0x00000000);
|
||||
}
|
||||
|
||||
if (gpuobj->heap.block_size)
|
||||
nouveau_mm_fini(&gpuobj->heap);
|
||||
|
||||
nouveau_object_destroy(&gpuobj->base);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_gpuobj_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, u32 pclass,
|
||||
struct nouveau_object *pargpu,
|
||||
u32 size, u32 align, u32 flags,
|
||||
int length, void **pobject)
|
||||
{
|
||||
struct nouveau_instmem *imem = nouveau_instmem(parent);
|
||||
struct nouveau_bar *bar = nouveau_bar(parent);
|
||||
struct nouveau_gpuobj *gpuobj;
|
||||
struct nouveau_mm *heap = NULL;
|
||||
int ret, i;
|
||||
u64 addr;
|
||||
|
||||
*pobject = NULL;
|
||||
|
||||
if (pargpu) {
|
||||
while ((pargpu = nv_pclass(pargpu, NV_GPUOBJ_CLASS))) {
|
||||
if (nv_gpuobj(pargpu)->heap.block_size)
|
||||
break;
|
||||
pargpu = pargpu->parent;
|
||||
}
|
||||
|
||||
if (unlikely(pargpu == NULL)) {
|
||||
nv_error(parent, "no gpuobj heap\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
addr = nv_gpuobj(pargpu)->addr;
|
||||
heap = &nv_gpuobj(pargpu)->heap;
|
||||
atomic_inc(&parent->refcount);
|
||||
} else {
|
||||
ret = imem->alloc(imem, parent, size, align, &parent);
|
||||
pargpu = parent;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
addr = nv_memobj(pargpu)->addr;
|
||||
size = nv_memobj(pargpu)->size;
|
||||
|
||||
if (bar && bar->alloc) {
|
||||
struct nouveau_instobj *iobj = (void *)parent;
|
||||
struct nouveau_mem **mem = (void *)(iobj + 1);
|
||||
struct nouveau_mem *node = *mem;
|
||||
if (!bar->alloc(bar, parent, node, &pargpu)) {
|
||||
nouveau_object_ref(NULL, &parent);
|
||||
parent = pargpu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret = nouveau_object_create_(parent, engine, oclass, pclass |
|
||||
NV_GPUOBJ_CLASS, length, pobject);
|
||||
nouveau_object_ref(NULL, &parent);
|
||||
gpuobj = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpuobj->parent = pargpu;
|
||||
gpuobj->flags = flags;
|
||||
gpuobj->addr = addr;
|
||||
gpuobj->size = size;
|
||||
|
||||
if (heap) {
|
||||
ret = nouveau_mm_head(heap, 1, size, size,
|
||||
max(align, (u32)1), &gpuobj->node);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
gpuobj->addr += gpuobj->node->offset;
|
||||
}
|
||||
|
||||
if (gpuobj->flags & NVOBJ_FLAG_HEAP) {
|
||||
ret = nouveau_mm_init(&gpuobj->heap, 0, gpuobj->size, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (flags & NVOBJ_FLAG_ZERO_ALLOC) {
|
||||
for (i = 0; i < gpuobj->size; i += 4)
|
||||
nv_wo32(gpuobj, i, 0x00000000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct nouveau_gpuobj_class {
|
||||
struct nouveau_object *pargpu;
|
||||
u64 size;
|
||||
u32 align;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
static int
|
||||
_nouveau_gpuobj_ctor(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_gpuobj_class *args = data;
|
||||
struct nouveau_gpuobj *object;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_gpuobj_create(parent, engine, oclass, 0, args->pargpu,
|
||||
args->size, args->align, args->flags,
|
||||
&object);
|
||||
*pobject = nv_object(object);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
_nouveau_gpuobj_dtor(struct nouveau_object *object)
|
||||
{
|
||||
nouveau_gpuobj_destroy(nv_gpuobj(object));
|
||||
}
|
||||
|
||||
int
|
||||
_nouveau_gpuobj_init(struct nouveau_object *object)
|
||||
{
|
||||
return nouveau_gpuobj_init(nv_gpuobj(object));
|
||||
}
|
||||
|
||||
int
|
||||
_nouveau_gpuobj_fini(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
return nouveau_gpuobj_fini(nv_gpuobj(object), suspend);
|
||||
}
|
||||
|
||||
u32
|
||||
_nouveau_gpuobj_rd32(struct nouveau_object *object, u32 addr)
|
||||
{
|
||||
struct nouveau_gpuobj *gpuobj = nv_gpuobj(object);
|
||||
struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent);
|
||||
if (gpuobj->node)
|
||||
addr += gpuobj->node->offset;
|
||||
return pfuncs->rd32(gpuobj->parent, addr);
|
||||
}
|
||||
|
||||
void
|
||||
_nouveau_gpuobj_wr32(struct nouveau_object *object, u32 addr, u32 data)
|
||||
{
|
||||
struct nouveau_gpuobj *gpuobj = nv_gpuobj(object);
|
||||
struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent);
|
||||
if (gpuobj->node)
|
||||
addr += gpuobj->node->offset;
|
||||
pfuncs->wr32(gpuobj->parent, addr, data);
|
||||
}
|
||||
|
||||
static struct nouveau_oclass
|
||||
_nouveau_gpuobj_oclass = {
|
||||
.handle = 0x00000000,
|
||||
.ofuncs = &(struct nouveau_ofuncs) {
|
||||
.ctor = _nouveau_gpuobj_ctor,
|
||||
.dtor = _nouveau_gpuobj_dtor,
|
||||
.init = _nouveau_gpuobj_init,
|
||||
.fini = _nouveau_gpuobj_fini,
|
||||
.rd32 = _nouveau_gpuobj_rd32,
|
||||
.wr32 = _nouveau_gpuobj_wr32,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
nouveau_gpuobj_new(struct nouveau_object *parent, struct nouveau_object *pargpu,
|
||||
u32 size, u32 align, u32 flags,
|
||||
struct nouveau_gpuobj **pgpuobj)
|
||||
{
|
||||
struct nouveau_object *engine = parent;
|
||||
struct nouveau_gpuobj_class args = {
|
||||
.pargpu = pargpu,
|
||||
.size = size,
|
||||
.align = align,
|
||||
.flags = flags,
|
||||
};
|
||||
|
||||
if (!nv_iclass(engine, NV_SUBDEV_CLASS))
|
||||
engine = engine->engine;
|
||||
BUG_ON(engine == NULL);
|
||||
|
||||
return nouveau_object_ctor(parent, engine, &_nouveau_gpuobj_oclass,
|
||||
&args, sizeof(args),
|
||||
(struct nouveau_object **)pgpuobj);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_gpuobj_map(struct nouveau_gpuobj *gpuobj, u32 access,
|
||||
struct nouveau_vma *vma)
|
||||
{
|
||||
struct nouveau_bar *bar = nouveau_bar(gpuobj);
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (bar && bar->umap) {
|
||||
struct nouveau_instobj *iobj = (void *)
|
||||
nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS);
|
||||
struct nouveau_mem **mem = (void *)(iobj + 1);
|
||||
ret = bar->umap(bar, *mem, access, vma);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_gpuobj_map_vm(struct nouveau_gpuobj *gpuobj, struct nouveau_vm *vm,
|
||||
u32 access, struct nouveau_vma *vma)
|
||||
{
|
||||
struct nouveau_instobj *iobj = (void *)
|
||||
nv_pclass(nv_object(gpuobj), NV_MEMOBJ_CLASS);
|
||||
struct nouveau_mem **mem = (void *)(iobj + 1);
|
||||
int ret;
|
||||
|
||||
ret = nouveau_vm_get(vm, gpuobj->size, 12, access, vma);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nouveau_vm_map(vma, *mem);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_gpuobj_unmap(struct nouveau_vma *vma)
|
||||
{
|
||||
if (vma->node) {
|
||||
nouveau_vm_unmap(vma);
|
||||
nouveau_vm_put(vma);
|
||||
}
|
||||
}
|
||||
|
||||
/* the below is basically only here to support sharing the paged dma object
|
||||
* for PCI(E)GART on <=nv4x chipsets, and should *not* be expected to work
|
||||
* anywhere else.
|
||||
*/
|
||||
|
||||
static void
|
||||
nouveau_gpudup_dtor(struct nouveau_object *object)
|
||||
{
|
||||
struct nouveau_gpuobj *gpuobj = (void *)object;
|
||||
nouveau_object_ref(NULL, &gpuobj->parent);
|
||||
nouveau_object_destroy(&gpuobj->base);
|
||||
}
|
||||
|
||||
static struct nouveau_oclass
|
||||
nouveau_gpudup_oclass = {
|
||||
.handle = NV_GPUOBJ_CLASS,
|
||||
.ofuncs = &(struct nouveau_ofuncs) {
|
||||
.dtor = nouveau_gpudup_dtor,
|
||||
.init = nouveau_object_init,
|
||||
.fini = nouveau_object_fini,
|
||||
},
|
||||
};
|
||||
|
||||
int
|
||||
nouveau_gpuobj_dup(struct nouveau_object *parent, struct nouveau_gpuobj *base,
|
||||
struct nouveau_gpuobj **pgpuobj)
|
||||
{
|
||||
struct nouveau_gpuobj *gpuobj;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_object_create(parent, parent->engine,
|
||||
&nouveau_gpudup_oclass, 0, &gpuobj);
|
||||
*pgpuobj = gpuobj;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nouveau_object_ref(nv_object(base), &gpuobj->parent);
|
||||
gpuobj->addr = base->addr;
|
||||
gpuobj->size = base->size;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/handle.h>
|
||||
#include <core/client.h>
|
||||
|
||||
#define hprintk(h,l,f,a...) do { \
|
||||
struct nouveau_client *c = nouveau_client((h)->object); \
|
||||
struct nouveau_handle *p = (h)->parent; u32 n = p ? p->name : ~0; \
|
||||
nv_printk((c), l, "0x%08x:0x%08x "f, n, (h)->name, ##a); \
|
||||
} while(0)
|
||||
|
||||
int
|
||||
nouveau_handle_init(struct nouveau_handle *handle)
|
||||
{
|
||||
struct nouveau_handle *item;
|
||||
int ret;
|
||||
|
||||
hprintk(handle, TRACE, "init running\n");
|
||||
ret = nouveau_object_inc(handle->object);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hprintk(handle, TRACE, "init children\n");
|
||||
list_for_each_entry(item, &handle->tree, head) {
|
||||
ret = nouveau_handle_init(item);
|
||||
if (ret)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hprintk(handle, TRACE, "init completed\n");
|
||||
return 0;
|
||||
fail:
|
||||
hprintk(handle, ERROR, "init failed with %d\n", ret);
|
||||
list_for_each_entry_continue_reverse(item, &handle->tree, head) {
|
||||
nouveau_handle_fini(item, false);
|
||||
}
|
||||
|
||||
nouveau_object_dec(handle->object, false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_handle_fini(struct nouveau_handle *handle, bool suspend)
|
||||
{
|
||||
static char *name[2] = { "fini", "suspend" };
|
||||
struct nouveau_handle *item;
|
||||
int ret;
|
||||
|
||||
hprintk(handle, TRACE, "%s children\n", name[suspend]);
|
||||
list_for_each_entry(item, &handle->tree, head) {
|
||||
ret = nouveau_handle_fini(item, suspend);
|
||||
if (ret && suspend)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hprintk(handle, TRACE, "%s running\n", name[suspend]);
|
||||
if (handle->object) {
|
||||
ret = nouveau_object_dec(handle->object, suspend);
|
||||
if (ret && suspend)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
hprintk(handle, TRACE, "%s completed\n", name[suspend]);
|
||||
return 0;
|
||||
fail:
|
||||
hprintk(handle, ERROR, "%s failed with %d\n", name[suspend], ret);
|
||||
list_for_each_entry_continue_reverse(item, &handle->tree, head) {
|
||||
int rret = nouveau_handle_init(item);
|
||||
if (rret)
|
||||
hprintk(handle, FATAL, "failed to restart, %d\n", rret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_handle_create(struct nouveau_object *parent, u32 _parent, u32 _handle,
|
||||
struct nouveau_object *object,
|
||||
struct nouveau_handle **phandle)
|
||||
{
|
||||
struct nouveau_object *namedb;
|
||||
struct nouveau_handle *handle;
|
||||
int ret;
|
||||
|
||||
namedb = parent;
|
||||
while (!nv_iclass(namedb, NV_NAMEDB_CLASS))
|
||||
namedb = namedb->parent;
|
||||
|
||||
handle = *phandle = kzalloc(sizeof(*handle), GFP_KERNEL);
|
||||
if (!handle)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&handle->head);
|
||||
INIT_LIST_HEAD(&handle->tree);
|
||||
handle->name = _handle;
|
||||
handle->priv = ~0;
|
||||
|
||||
ret = nouveau_namedb_insert(nv_namedb(namedb), _handle, object, handle);
|
||||
if (ret) {
|
||||
kfree(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (nv_parent(parent)->object_attach) {
|
||||
ret = nv_parent(parent)->object_attach(parent, object, _handle);
|
||||
if (ret < 0) {
|
||||
nouveau_handle_destroy(handle);
|
||||
return ret;
|
||||
}
|
||||
|
||||
handle->priv = ret;
|
||||
}
|
||||
|
||||
if (object != namedb) {
|
||||
while (!nv_iclass(namedb, NV_CLIENT_CLASS))
|
||||
namedb = namedb->parent;
|
||||
|
||||
handle->parent = nouveau_namedb_get(nv_namedb(namedb), _parent);
|
||||
if (handle->parent) {
|
||||
list_add(&handle->head, &handle->parent->tree);
|
||||
nouveau_namedb_put(handle->parent);
|
||||
}
|
||||
}
|
||||
|
||||
hprintk(handle, TRACE, "created\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_handle_destroy(struct nouveau_handle *handle)
|
||||
{
|
||||
struct nouveau_handle *item, *temp;
|
||||
|
||||
hprintk(handle, TRACE, "destroy running\n");
|
||||
list_for_each_entry_safe(item, temp, &handle->tree, head) {
|
||||
nouveau_handle_destroy(item);
|
||||
}
|
||||
list_del(&handle->head);
|
||||
|
||||
if (handle->priv != ~0) {
|
||||
struct nouveau_object *parent = handle->parent->object;
|
||||
nv_parent(parent)->object_detach(parent, handle->priv);
|
||||
}
|
||||
|
||||
hprintk(handle, TRACE, "destroy completed\n");
|
||||
nouveau_namedb_remove(handle);
|
||||
kfree(handle);
|
||||
}
|
||||
|
||||
struct nouveau_object *
|
||||
nouveau_handle_ref(struct nouveau_object *parent, u32 name)
|
||||
{
|
||||
struct nouveau_object *object = NULL;
|
||||
struct nouveau_handle *handle;
|
||||
|
||||
while (!nv_iclass(parent, NV_NAMEDB_CLASS))
|
||||
parent = parent->parent;
|
||||
|
||||
handle = nouveau_namedb_get(nv_namedb(parent), name);
|
||||
if (handle) {
|
||||
nouveau_object_ref(handle->object, &object);
|
||||
nouveau_namedb_put(handle);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_handle_get_class(struct nouveau_object *engctx, u16 oclass)
|
||||
{
|
||||
struct nouveau_namedb *namedb;
|
||||
if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
|
||||
return nouveau_namedb_get_class(namedb, oclass);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_handle_get_vinst(struct nouveau_object *engctx, u64 vinst)
|
||||
{
|
||||
struct nouveau_namedb *namedb;
|
||||
if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
|
||||
return nouveau_namedb_get_vinst(namedb, vinst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_handle_get_cinst(struct nouveau_object *engctx, u32 cinst)
|
||||
{
|
||||
struct nouveau_namedb *namedb;
|
||||
if (engctx && (namedb = (void *)nv_pclass(engctx, NV_NAMEDB_CLASS)))
|
||||
return nouveau_namedb_get_cinst(namedb, cinst);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_handle_put(struct nouveau_handle *handle)
|
||||
{
|
||||
if (handle)
|
||||
nouveau_namedb_put(handle);
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2010 Red Hat Inc.
|
||||
* Copyright 2012 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"),
|
||||
|
@ -22,20 +22,52 @@
|
|||
* Authors: Ben Skeggs
|
||||
*/
|
||||
|
||||
#include <drm/drmP.h>
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_mm.h"
|
||||
#include "core/os.h"
|
||||
#include "core/mm.h"
|
||||
|
||||
static inline void
|
||||
region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
|
||||
#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
|
||||
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
|
||||
|
||||
void
|
||||
nouveau_mm_free(struct nouveau_mm *mm, struct nouveau_mm_node **pthis)
|
||||
{
|
||||
list_del(&a->nl_entry);
|
||||
list_del(&a->fl_entry);
|
||||
kfree(a);
|
||||
struct nouveau_mm_node *this = *pthis;
|
||||
|
||||
if (this) {
|
||||
struct nouveau_mm_node *prev = node(this, prev);
|
||||
struct nouveau_mm_node *next = node(this, next);
|
||||
|
||||
if (prev && prev->type == 0) {
|
||||
prev->length += this->length;
|
||||
list_del(&this->nl_entry);
|
||||
kfree(this); this = prev;
|
||||
}
|
||||
|
||||
if (next && next->type == 0) {
|
||||
next->offset = this->offset;
|
||||
next->length += this->length;
|
||||
if (this->type == 0)
|
||||
list_del(&this->fl_entry);
|
||||
list_del(&this->nl_entry);
|
||||
kfree(this); this = NULL;
|
||||
}
|
||||
|
||||
if (this && this->type != 0) {
|
||||
list_for_each_entry(prev, &mm->free, fl_entry) {
|
||||
if (this->offset < prev->offset)
|
||||
break;
|
||||
}
|
||||
|
||||
list_add_tail(&this->fl_entry, &prev->fl_entry);
|
||||
this->type = 0;
|
||||
}
|
||||
}
|
||||
|
||||
*pthis = NULL;
|
||||
}
|
||||
|
||||
static struct nouveau_mm_node *
|
||||
region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
|
||||
region_head(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
|
||||
{
|
||||
struct nouveau_mm_node *b;
|
||||
|
||||
|
@ -57,38 +89,12 @@ region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
|
|||
return b;
|
||||
}
|
||||
|
||||
#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
|
||||
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
|
||||
|
||||
void
|
||||
nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
|
||||
{
|
||||
struct nouveau_mm_node *prev = node(this, prev);
|
||||
struct nouveau_mm_node *next = node(this, next);
|
||||
|
||||
list_add(&this->fl_entry, &mm->free);
|
||||
this->type = 0;
|
||||
|
||||
if (prev && prev->type == 0) {
|
||||
prev->length += this->length;
|
||||
region_put(mm, this);
|
||||
this = prev;
|
||||
}
|
||||
|
||||
if (next && next->type == 0) {
|
||||
next->offset = this->offset;
|
||||
next->length += this->length;
|
||||
region_put(mm, this);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
|
||||
u32 align, struct nouveau_mm_node **pnode)
|
||||
nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
|
||||
u32 align, struct nouveau_mm_node **pnode)
|
||||
{
|
||||
struct nouveau_mm_node *prev, *this, *next;
|
||||
u32 min = size_nc ? size_nc : size;
|
||||
u32 align_mask = align - 1;
|
||||
u32 mask = align - 1;
|
||||
u32 splitoff;
|
||||
u32 s, e;
|
||||
|
||||
|
@ -104,16 +110,86 @@ nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
|
|||
if (next && next->type != type)
|
||||
e = rounddown(e, mm->block_size);
|
||||
|
||||
s = (s + align_mask) & ~align_mask;
|
||||
e &= ~align_mask;
|
||||
if (s > e || e - s < min)
|
||||
s = (s + mask) & ~mask;
|
||||
e &= ~mask;
|
||||
if (s > e || e - s < size_min)
|
||||
continue;
|
||||
|
||||
splitoff = s - this->offset;
|
||||
if (splitoff && !region_split(mm, this, splitoff))
|
||||
if (splitoff && !region_head(mm, this, splitoff))
|
||||
return -ENOMEM;
|
||||
|
||||
this = region_split(mm, this, min(size, e - s));
|
||||
this = region_head(mm, this, min(size_max, e - s));
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
this->type = type;
|
||||
list_del(&this->fl_entry);
|
||||
*pnode = this;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static struct nouveau_mm_node *
|
||||
region_tail(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
|
||||
{
|
||||
struct nouveau_mm_node *b;
|
||||
|
||||
if (a->length == size)
|
||||
return a;
|
||||
|
||||
b = kmalloc(sizeof(*b), GFP_KERNEL);
|
||||
if (unlikely(b == NULL))
|
||||
return NULL;
|
||||
|
||||
a->length -= size;
|
||||
b->offset = a->offset + a->length;
|
||||
b->length = size;
|
||||
b->type = a->type;
|
||||
|
||||
list_add(&b->nl_entry, &a->nl_entry);
|
||||
if (b->type == 0)
|
||||
list_add(&b->fl_entry, &a->fl_entry);
|
||||
return b;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
|
||||
u32 align, struct nouveau_mm_node **pnode)
|
||||
{
|
||||
struct nouveau_mm_node *prev, *this, *next;
|
||||
u32 mask = align - 1;
|
||||
|
||||
list_for_each_entry_reverse(this, &mm->free, fl_entry) {
|
||||
u32 e = this->offset + this->length;
|
||||
u32 s = this->offset;
|
||||
u32 c = 0, a;
|
||||
|
||||
prev = node(this, prev);
|
||||
if (prev && prev->type != type)
|
||||
s = roundup(s, mm->block_size);
|
||||
|
||||
next = node(this, next);
|
||||
if (next && next->type != type) {
|
||||
e = rounddown(e, mm->block_size);
|
||||
c = next->offset - e;
|
||||
}
|
||||
|
||||
s = (s + mask) & ~mask;
|
||||
a = e - s;
|
||||
if (s > e || a < size_min)
|
||||
continue;
|
||||
|
||||
a = min(a, size_max);
|
||||
s = (e - a) & ~mask;
|
||||
c += (e - s) - a;
|
||||
|
||||
if (c && !region_tail(mm, this, c))
|
||||
return -ENOMEM;
|
||||
|
||||
this = region_tail(mm, this, a);
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
|
@ -148,6 +224,7 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
|
|||
list_add_tail(&node->nl_entry, &mm->nodes);
|
||||
list_add_tail(&node->fl_entry, &mm->free);
|
||||
mm->heap_nodes++;
|
||||
mm->heap_size += length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -159,15 +236,8 @@ nouveau_mm_fini(struct nouveau_mm *mm)
|
|||
int nodes = 0;
|
||||
|
||||
list_for_each_entry(node, &mm->nodes, nl_entry) {
|
||||
if (nodes++ == mm->heap_nodes) {
|
||||
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",
|
||||
node->type, node->offset, node->length);
|
||||
}
|
||||
WARN_ON(1);
|
||||
if (nodes++ == mm->heap_nodes)
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
kfree(heap);
|
|
@ -0,0 +1,203 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/namedb.h>
|
||||
#include <core/handle.h>
|
||||
#include <core/gpuobj.h>
|
||||
|
||||
static struct nouveau_handle *
|
||||
nouveau_namedb_lookup(struct nouveau_namedb *namedb, u32 name)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
|
||||
list_for_each_entry(handle, &namedb->list, node) {
|
||||
if (handle->name == name)
|
||||
return handle;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nouveau_handle *
|
||||
nouveau_namedb_lookup_class(struct nouveau_namedb *namedb, u16 oclass)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
|
||||
list_for_each_entry(handle, &namedb->list, node) {
|
||||
if (nv_mclass(handle->object) == oclass)
|
||||
return handle;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nouveau_handle *
|
||||
nouveau_namedb_lookup_vinst(struct nouveau_namedb *namedb, u64 vinst)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
|
||||
list_for_each_entry(handle, &namedb->list, node) {
|
||||
if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) {
|
||||
if (nv_gpuobj(handle->object)->addr == vinst)
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct nouveau_handle *
|
||||
nouveau_namedb_lookup_cinst(struct nouveau_namedb *namedb, u32 cinst)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
|
||||
list_for_each_entry(handle, &namedb->list, node) {
|
||||
if (nv_iclass(handle->object, NV_GPUOBJ_CLASS)) {
|
||||
if (nv_gpuobj(handle->object)->node &&
|
||||
nv_gpuobj(handle->object)->node->offset == cinst)
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_namedb_insert(struct nouveau_namedb *namedb, u32 name,
|
||||
struct nouveau_object *object,
|
||||
struct nouveau_handle *handle)
|
||||
{
|
||||
int ret = -EEXIST;
|
||||
write_lock_irq(&namedb->lock);
|
||||
if (!nouveau_namedb_lookup(namedb, name)) {
|
||||
nouveau_object_ref(object, &handle->object);
|
||||
handle->namedb = namedb;
|
||||
list_add(&handle->node, &namedb->list);
|
||||
ret = 0;
|
||||
}
|
||||
write_unlock_irq(&namedb->lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_namedb_remove(struct nouveau_handle *handle)
|
||||
{
|
||||
struct nouveau_namedb *namedb = handle->namedb;
|
||||
struct nouveau_object *object = handle->object;
|
||||
write_lock_irq(&namedb->lock);
|
||||
list_del(&handle->node);
|
||||
write_unlock_irq(&namedb->lock);
|
||||
nouveau_object_ref(NULL, &object);
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_namedb_get(struct nouveau_namedb *namedb, u32 name)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
read_lock(&namedb->lock);
|
||||
handle = nouveau_namedb_lookup(namedb, name);
|
||||
if (handle == NULL)
|
||||
read_unlock(&namedb->lock);
|
||||
return handle;
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_namedb_get_class(struct nouveau_namedb *namedb, u16 oclass)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
read_lock(&namedb->lock);
|
||||
handle = nouveau_namedb_lookup_class(namedb, oclass);
|
||||
if (handle == NULL)
|
||||
read_unlock(&namedb->lock);
|
||||
return handle;
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_namedb_get_vinst(struct nouveau_namedb *namedb, u64 vinst)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
read_lock(&namedb->lock);
|
||||
handle = nouveau_namedb_lookup_vinst(namedb, vinst);
|
||||
if (handle == NULL)
|
||||
read_unlock(&namedb->lock);
|
||||
return handle;
|
||||
}
|
||||
|
||||
struct nouveau_handle *
|
||||
nouveau_namedb_get_cinst(struct nouveau_namedb *namedb, u32 cinst)
|
||||
{
|
||||
struct nouveau_handle *handle;
|
||||
read_lock(&namedb->lock);
|
||||
handle = nouveau_namedb_lookup_cinst(namedb, cinst);
|
||||
if (handle == NULL)
|
||||
read_unlock(&namedb->lock);
|
||||
return handle;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_namedb_put(struct nouveau_handle *handle)
|
||||
{
|
||||
if (handle)
|
||||
read_unlock(&handle->namedb->lock);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_namedb_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, u32 pclass,
|
||||
struct nouveau_oclass *sclass, u32 engcls,
|
||||
int length, void **pobject)
|
||||
{
|
||||
struct nouveau_namedb *namedb;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_parent_create_(parent, engine, oclass, pclass |
|
||||
NV_NAMEDB_CLASS, sclass, engcls,
|
||||
length, pobject);
|
||||
namedb = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rwlock_init(&namedb->lock);
|
||||
INIT_LIST_HEAD(&namedb->list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_nouveau_namedb_ctor(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_namedb *object;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_namedb_create(parent, engine, oclass, 0, NULL, 0, &object);
|
||||
*pobject = nv_object(object);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,468 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/parent.h>
|
||||
#include <core/namedb.h>
|
||||
#include <core/handle.h>
|
||||
#include <core/engine.h>
|
||||
|
||||
#ifdef NOUVEAU_OBJECT_MAGIC
|
||||
static struct list_head _objlist = LIST_HEAD_INIT(_objlist);
|
||||
static DEFINE_SPINLOCK(_objlist_lock);
|
||||
#endif
|
||||
|
||||
int
|
||||
nouveau_object_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, u32 pclass,
|
||||
int size, void **pobject)
|
||||
{
|
||||
struct nouveau_object *object;
|
||||
|
||||
object = *pobject = kzalloc(size, GFP_KERNEL);
|
||||
if (!object)
|
||||
return -ENOMEM;
|
||||
|
||||
nouveau_object_ref(parent, &object->parent);
|
||||
nouveau_object_ref(engine, &object->engine);
|
||||
object->oclass = oclass;
|
||||
object->oclass->handle |= pclass;
|
||||
atomic_set(&object->refcount, 1);
|
||||
atomic_set(&object->usecount, 0);
|
||||
|
||||
#ifdef NOUVEAU_OBJECT_MAGIC
|
||||
object->_magic = NOUVEAU_OBJECT_MAGIC;
|
||||
spin_lock(&_objlist_lock);
|
||||
list_add(&object->list, &_objlist);
|
||||
spin_unlock(&_objlist_lock);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_nouveau_object_ctor(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_object *object;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_object_create(parent, engine, oclass, 0, &object);
|
||||
*pobject = nv_object(object);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_object_destroy(struct nouveau_object *object)
|
||||
{
|
||||
#ifdef NOUVEAU_OBJECT_MAGIC
|
||||
spin_lock(&_objlist_lock);
|
||||
list_del(&object->list);
|
||||
spin_unlock(&_objlist_lock);
|
||||
#endif
|
||||
nouveau_object_ref(NULL, &object->engine);
|
||||
nouveau_object_ref(NULL, &object->parent);
|
||||
kfree(object);
|
||||
}
|
||||
|
||||
static void
|
||||
_nouveau_object_dtor(struct nouveau_object *object)
|
||||
{
|
||||
nouveau_object_destroy(object);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_object_init(struct nouveau_object *object)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_nouveau_object_init(struct nouveau_object *object)
|
||||
{
|
||||
return nouveau_object_init(object);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_object_fini(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_nouveau_object_fini(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
return nouveau_object_fini(object, suspend);
|
||||
}
|
||||
|
||||
struct nouveau_ofuncs
|
||||
nouveau_object_ofuncs = {
|
||||
.ctor = _nouveau_object_ctor,
|
||||
.dtor = _nouveau_object_dtor,
|
||||
.init = _nouveau_object_init,
|
||||
.fini = _nouveau_object_fini,
|
||||
};
|
||||
|
||||
int
|
||||
nouveau_object_ctor(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_ofuncs *ofuncs = oclass->ofuncs;
|
||||
int ret;
|
||||
|
||||
*pobject = NULL;
|
||||
|
||||
ret = ofuncs->ctor(parent, engine, oclass, data, size, pobject);
|
||||
if (ret < 0) {
|
||||
if (ret != -ENODEV) {
|
||||
nv_error(parent, "failed to create 0x%08x, %d\n",
|
||||
oclass->handle, ret);
|
||||
}
|
||||
|
||||
if (*pobject) {
|
||||
ofuncs->dtor(*pobject);
|
||||
*pobject = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
nv_debug(*pobject, "created\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_object_dtor(struct nouveau_object *object)
|
||||
{
|
||||
nv_debug(object, "destroying\n");
|
||||
nv_ofuncs(object)->dtor(object);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_object_ref(struct nouveau_object *obj, struct nouveau_object **ref)
|
||||
{
|
||||
if (obj) {
|
||||
atomic_inc(&obj->refcount);
|
||||
nv_trace(obj, "inc() == %d\n", atomic_read(&obj->refcount));
|
||||
}
|
||||
|
||||
if (*ref) {
|
||||
int dead = atomic_dec_and_test(&(*ref)->refcount);
|
||||
nv_trace(*ref, "dec() == %d\n", atomic_read(&(*ref)->refcount));
|
||||
if (dead)
|
||||
nouveau_object_dtor(*ref);
|
||||
}
|
||||
|
||||
*ref = obj;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_object_new(struct nouveau_object *client, u32 _parent, u32 _handle,
|
||||
u16 _oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_object *parent = NULL;
|
||||
struct nouveau_object *engctx = NULL;
|
||||
struct nouveau_object *object = NULL;
|
||||
struct nouveau_object *engine;
|
||||
struct nouveau_oclass *oclass;
|
||||
struct nouveau_handle *handle;
|
||||
int ret;
|
||||
|
||||
/* lookup parent object and ensure it *is* a parent */
|
||||
parent = nouveau_handle_ref(client, _parent);
|
||||
if (!parent) {
|
||||
nv_error(client, "parent 0x%08x not found\n", _parent);
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (!nv_iclass(parent, NV_PARENT_CLASS)) {
|
||||
nv_error(parent, "cannot have children\n");
|
||||
ret = -EINVAL;
|
||||
goto fail_class;
|
||||
}
|
||||
|
||||
/* check that parent supports the requested subclass */
|
||||
ret = nouveau_parent_sclass(parent, _oclass, &engine, &oclass);
|
||||
if (ret) {
|
||||
nv_debug(parent, "illegal class 0x%04x\n", _oclass);
|
||||
goto fail_class;
|
||||
}
|
||||
|
||||
/* make sure engine init has been completed *before* any objects
|
||||
* it controls are created - the constructors may depend on
|
||||
* state calculated at init (ie. default context construction)
|
||||
*/
|
||||
if (engine) {
|
||||
ret = nouveau_object_inc(engine);
|
||||
if (ret)
|
||||
goto fail_class;
|
||||
}
|
||||
|
||||
/* if engine requires it, create a context object to insert
|
||||
* between the parent and its children (eg. PGRAPH context)
|
||||
*/
|
||||
if (engine && nv_engine(engine)->cclass) {
|
||||
ret = nouveau_object_ctor(parent, engine,
|
||||
nv_engine(engine)->cclass,
|
||||
data, size, &engctx);
|
||||
if (ret)
|
||||
goto fail_engctx;
|
||||
} else {
|
||||
nouveau_object_ref(parent, &engctx);
|
||||
}
|
||||
|
||||
/* finally, create new object and bind it to its handle */
|
||||
ret = nouveau_object_ctor(engctx, engine, oclass, data, size, &object);
|
||||
*pobject = object;
|
||||
if (ret)
|
||||
goto fail_ctor;
|
||||
|
||||
ret = nouveau_object_inc(object);
|
||||
if (ret)
|
||||
goto fail_init;
|
||||
|
||||
ret = nouveau_handle_create(parent, _parent, _handle, object, &handle);
|
||||
if (ret)
|
||||
goto fail_handle;
|
||||
|
||||
ret = nouveau_handle_init(handle);
|
||||
if (ret)
|
||||
nouveau_handle_destroy(handle);
|
||||
|
||||
fail_handle:
|
||||
nouveau_object_dec(object, false);
|
||||
fail_init:
|
||||
nouveau_object_ref(NULL, &object);
|
||||
fail_ctor:
|
||||
nouveau_object_ref(NULL, &engctx);
|
||||
fail_engctx:
|
||||
if (engine)
|
||||
nouveau_object_dec(engine, false);
|
||||
fail_class:
|
||||
nouveau_object_ref(NULL, &parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)
|
||||
{
|
||||
struct nouveau_object *parent = NULL;
|
||||
struct nouveau_object *namedb = NULL;
|
||||
struct nouveau_handle *handle = NULL;
|
||||
int ret = -EINVAL;
|
||||
|
||||
parent = nouveau_handle_ref(client, _parent);
|
||||
if (!parent)
|
||||
return -ENOENT;
|
||||
|
||||
namedb = nv_pclass(parent, NV_NAMEDB_CLASS);
|
||||
if (namedb) {
|
||||
handle = nouveau_namedb_get(nv_namedb(namedb), _handle);
|
||||
if (handle) {
|
||||
nouveau_namedb_put(handle);
|
||||
nouveau_handle_fini(handle, false);
|
||||
nouveau_handle_destroy(handle);
|
||||
}
|
||||
}
|
||||
|
||||
nouveau_object_ref(NULL, &parent);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_object_inc(struct nouveau_object *object)
|
||||
{
|
||||
int ref = atomic_add_return(1, &object->usecount);
|
||||
int ret;
|
||||
|
||||
nv_trace(object, "use(+1) == %d\n", atomic_read(&object->usecount));
|
||||
if (ref != 1)
|
||||
return 0;
|
||||
|
||||
nv_trace(object, "initialising...\n");
|
||||
if (object->parent) {
|
||||
ret = nouveau_object_inc(object->parent);
|
||||
if (ret) {
|
||||
nv_error(object, "parent failed, %d\n", ret);
|
||||
goto fail_parent;
|
||||
}
|
||||
}
|
||||
|
||||
if (object->engine) {
|
||||
mutex_lock(&nv_subdev(object->engine)->mutex);
|
||||
ret = nouveau_object_inc(object->engine);
|
||||
mutex_unlock(&nv_subdev(object->engine)->mutex);
|
||||
if (ret) {
|
||||
nv_error(object, "engine failed, %d\n", ret);
|
||||
goto fail_engine;
|
||||
}
|
||||
}
|
||||
|
||||
ret = nv_ofuncs(object)->init(object);
|
||||
if (ret) {
|
||||
nv_error(object, "init failed, %d\n", ret);
|
||||
goto fail_self;
|
||||
}
|
||||
|
||||
nv_debug(object, "initialised\n");
|
||||
return 0;
|
||||
|
||||
fail_self:
|
||||
if (object->engine) {
|
||||
mutex_lock(&nv_subdev(object->engine)->mutex);
|
||||
nouveau_object_dec(object->engine, false);
|
||||
mutex_unlock(&nv_subdev(object->engine)->mutex);
|
||||
}
|
||||
fail_engine:
|
||||
if (object->parent)
|
||||
nouveau_object_dec(object->parent, false);
|
||||
fail_parent:
|
||||
atomic_dec(&object->usecount);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_object_decf(struct nouveau_object *object)
|
||||
{
|
||||
int ret;
|
||||
|
||||
nv_trace(object, "stopping...\n");
|
||||
|
||||
ret = nv_ofuncs(object)->fini(object, false);
|
||||
if (ret)
|
||||
nv_warn(object, "failed fini, %d\n", ret);
|
||||
|
||||
if (object->engine) {
|
||||
mutex_lock(&nv_subdev(object->engine)->mutex);
|
||||
nouveau_object_dec(object->engine, false);
|
||||
mutex_unlock(&nv_subdev(object->engine)->mutex);
|
||||
}
|
||||
|
||||
if (object->parent)
|
||||
nouveau_object_dec(object->parent, false);
|
||||
|
||||
nv_debug(object, "stopped\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_object_decs(struct nouveau_object *object)
|
||||
{
|
||||
int ret, rret;
|
||||
|
||||
nv_trace(object, "suspending...\n");
|
||||
|
||||
ret = nv_ofuncs(object)->fini(object, true);
|
||||
if (ret) {
|
||||
nv_error(object, "failed suspend, %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (object->engine) {
|
||||
mutex_lock(&nv_subdev(object->engine)->mutex);
|
||||
ret = nouveau_object_dec(object->engine, true);
|
||||
mutex_unlock(&nv_subdev(object->engine)->mutex);
|
||||
if (ret) {
|
||||
nv_warn(object, "engine failed suspend, %d\n", ret);
|
||||
goto fail_engine;
|
||||
}
|
||||
}
|
||||
|
||||
if (object->parent) {
|
||||
ret = nouveau_object_dec(object->parent, true);
|
||||
if (ret) {
|
||||
nv_warn(object, "parent failed suspend, %d\n", ret);
|
||||
goto fail_parent;
|
||||
}
|
||||
}
|
||||
|
||||
nv_debug(object, "suspended\n");
|
||||
return 0;
|
||||
|
||||
fail_parent:
|
||||
if (object->engine) {
|
||||
mutex_lock(&nv_subdev(object->engine)->mutex);
|
||||
rret = nouveau_object_inc(object->engine);
|
||||
mutex_unlock(&nv_subdev(object->engine)->mutex);
|
||||
if (rret)
|
||||
nv_fatal(object, "engine failed to reinit, %d\n", rret);
|
||||
}
|
||||
|
||||
fail_engine:
|
||||
rret = nv_ofuncs(object)->init(object);
|
||||
if (rret)
|
||||
nv_fatal(object, "failed to reinit, %d\n", rret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_object_dec(struct nouveau_object *object, bool suspend)
|
||||
{
|
||||
int ref = atomic_add_return(-1, &object->usecount);
|
||||
int ret;
|
||||
|
||||
nv_trace(object, "use(-1) == %d\n", atomic_read(&object->usecount));
|
||||
|
||||
if (ref == 0) {
|
||||
if (suspend)
|
||||
ret = nouveau_object_decs(object);
|
||||
else
|
||||
ret = nouveau_object_decf(object);
|
||||
|
||||
if (ret) {
|
||||
atomic_inc(&object->usecount);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_object_debug(void)
|
||||
{
|
||||
#ifdef NOUVEAU_OBJECT_MAGIC
|
||||
struct nouveau_object *object;
|
||||
if (!list_empty(&_objlist)) {
|
||||
nv_fatal(NULL, "*******************************************\n");
|
||||
nv_fatal(NULL, "* AIIIII! object(s) still exist!!!\n");
|
||||
nv_fatal(NULL, "*******************************************\n");
|
||||
list_for_each_entry(object, &_objlist, list) {
|
||||
nv_fatal(object, "%p/%p/%d/%d\n",
|
||||
object->parent, object->engine,
|
||||
atomic_read(&object->refcount),
|
||||
atomic_read(&object->usecount));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/option.h>
|
||||
#include <core/debug.h>
|
||||
|
||||
/* compares unterminated string 'str' with zero-terminated string 'cmp' */
|
||||
static inline int
|
||||
strncasecmpz(const char *str, const char *cmp, size_t len)
|
||||
{
|
||||
if (strlen(cmp) != len)
|
||||
return len;
|
||||
return strncasecmp(str, cmp, len);
|
||||
}
|
||||
|
||||
const char *
|
||||
nouveau_stropt(const char *optstr, const char *opt, int *arglen)
|
||||
{
|
||||
while (optstr && *optstr != '\0') {
|
||||
int len = strcspn(optstr, ",=");
|
||||
switch (optstr[len]) {
|
||||
case '=':
|
||||
if (!strncasecmpz(optstr, opt, len)) {
|
||||
optstr += len + 1;
|
||||
*arglen = strcspn(optstr, ",=");
|
||||
return *arglen ? optstr : NULL;
|
||||
}
|
||||
optstr++;
|
||||
break;
|
||||
case ',':
|
||||
optstr++;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
optstr += len;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool
|
||||
nouveau_boolopt(const char *optstr, const char *opt, bool value)
|
||||
{
|
||||
int arglen;
|
||||
|
||||
optstr = nouveau_stropt(optstr, opt, &arglen);
|
||||
if (optstr) {
|
||||
if (!strncasecmpz(optstr, "0", arglen) ||
|
||||
!strncasecmpz(optstr, "no", arglen) ||
|
||||
!strncasecmpz(optstr, "off", arglen) ||
|
||||
!strncasecmpz(optstr, "false", arglen))
|
||||
value = false;
|
||||
else
|
||||
if (!strncasecmpz(optstr, "1", arglen) ||
|
||||
!strncasecmpz(optstr, "yes", arglen) ||
|
||||
!strncasecmpz(optstr, "on", arglen) ||
|
||||
!strncasecmpz(optstr, "true", arglen))
|
||||
value = true;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_dbgopt(const char *optstr, const char *sub)
|
||||
{
|
||||
int mode = 1, level = CONFIG_NOUVEAU_DEBUG_DEFAULT;
|
||||
|
||||
while (optstr) {
|
||||
int len = strcspn(optstr, ",=");
|
||||
switch (optstr[len]) {
|
||||
case '=':
|
||||
if (strncasecmpz(optstr, sub, len))
|
||||
mode = 0;
|
||||
optstr++;
|
||||
break;
|
||||
default:
|
||||
if (mode) {
|
||||
if (!strncasecmpz(optstr, "fatal", len))
|
||||
level = NV_DBG_FATAL;
|
||||
else if (!strncasecmpz(optstr, "error", len))
|
||||
level = NV_DBG_ERROR;
|
||||
else if (!strncasecmpz(optstr, "warn", len))
|
||||
level = NV_DBG_WARN;
|
||||
else if (!strncasecmpz(optstr, "info", len))
|
||||
level = NV_DBG_INFO;
|
||||
else if (!strncasecmpz(optstr, "debug", len))
|
||||
level = NV_DBG_DEBUG;
|
||||
else if (!strncasecmpz(optstr, "trace", len))
|
||||
level = NV_DBG_TRACE;
|
||||
else if (!strncasecmpz(optstr, "paranoia", len))
|
||||
level = NV_DBG_PARANOIA;
|
||||
else if (!strncasecmpz(optstr, "spam", len))
|
||||
level = NV_DBG_SPAM;
|
||||
}
|
||||
|
||||
if (optstr[len] != '\0') {
|
||||
optstr++;
|
||||
mode = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
optstr += len;
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* Copyright 2012 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 <core/object.h>
|
||||
#include <core/parent.h>
|
||||
|
||||
int
|
||||
nouveau_parent_sclass(struct nouveau_object *parent, u16 handle,
|
||||
struct nouveau_object **pengine,
|
||||
struct nouveau_oclass **poclass)
|
||||
{
|
||||
struct nouveau_sclass *sclass;
|
||||
struct nouveau_engine *engine;
|
||||
struct nouveau_oclass *oclass;
|
||||
u64 mask;
|
||||
|
||||
sclass = nv_parent(parent)->sclass;
|
||||
while (sclass) {
|
||||
if ((sclass->oclass->handle & 0xffff) == handle) {
|
||||
*pengine = parent->engine;
|
||||
*poclass = sclass->oclass;
|
||||
return 0;
|
||||
}
|
||||
|
||||
sclass = sclass->sclass;
|
||||
}
|
||||
|
||||
mask = nv_parent(parent)->engine;
|
||||
while (mask) {
|
||||
int i = ffsll(mask) - 1;
|
||||
|
||||
if ((engine = nouveau_engine(parent, i))) {
|
||||
oclass = engine->sclass;
|
||||
while (oclass->ofuncs) {
|
||||
if ((oclass->handle & 0xffff) == handle) {
|
||||
*pengine = nv_object(engine);
|
||||
*poclass = oclass;
|
||||
return 0;
|
||||
}
|
||||
oclass++;
|
||||
}
|
||||
}
|
||||
|
||||
mask &= ~(1ULL << i);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_parent_create_(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, u32 pclass,
|
||||
struct nouveau_oclass *sclass, u64 engcls,
|
||||
int size, void **pobject)
|
||||
{
|
||||
struct nouveau_parent *object;
|
||||
struct nouveau_sclass *nclass;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_object_create_(parent, engine, oclass, pclass |
|
||||
NV_PARENT_CLASS, size, pobject);
|
||||
object = *pobject;
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
while (sclass && sclass->ofuncs) {
|
||||
nclass = kzalloc(sizeof(*nclass), GFP_KERNEL);
|
||||
if (!nclass)
|
||||
return -ENOMEM;
|
||||
|
||||
nclass->sclass = object->sclass;
|
||||
object->sclass = nclass;
|
||||
nclass->engine = engine ? nv_engine(engine) : NULL;
|
||||
nclass->oclass = sclass;
|
||||
sclass++;
|
||||
}
|
||||
|
||||
object->engine = engcls;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
_nouveau_parent_ctor(struct nouveau_object *parent,
|
||||
struct nouveau_object *engine,
|
||||
struct nouveau_oclass *oclass, void *data, u32 size,
|
||||
struct nouveau_object **pobject)
|
||||
{
|
||||
struct nouveau_parent *object;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_parent_create(parent, engine, oclass, 0, NULL, 0, &object);
|
||||
*pobject = nv_object(object);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_parent_destroy(struct nouveau_parent *parent)
|
||||
{
|
||||
struct nouveau_sclass *sclass;
|
||||
|
||||
while ((sclass = parent->sclass)) {
|
||||
parent->sclass = sclass->sclass;
|
||||
kfree(sclass);
|
||||
}
|
||||
|
||||
nouveau_object_destroy(&parent->base);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_nouveau_parent_dtor(struct nouveau_object *object)
|
||||
{
|
||||
nouveau_parent_destroy(nv_parent(object));
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue