diff options
Diffstat (limited to 'drivers/gpu')
182 files changed, 13275 insertions, 9686 deletions
diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile index ac91a339b04..6f58c81cfcb 100644 --- a/drivers/gpu/drm/Makefile +++ b/drivers/gpu/drm/Makefile @@ -8,7 +8,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm_context.o drm_dma.o \ drm_drv.o drm_fops.o drm_gem.o drm_ioctl.o drm_irq.o \ drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \ - drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \ + drm_agpsupport.o drm_scatter.o drm_pci.o \ drm_platform.o drm_sysfs.o drm_hashtab.o drm_mm.o \ drm_crtc.o drm_modes.o drm_edid.o \ drm_info.o drm_debugfs.o drm_encoder_slave.o \ @@ -16,6 +16,7 @@ drm-y := drm_auth.o drm_buffer.o drm_bufs.o drm_cache.o \ drm-$(CONFIG_COMPAT) += drm_ioc32.o drm-$(CONFIG_DRM_GEM_CMA_HELPER) += drm_gem_cma_helper.o +drm-$(CONFIG_PCI) += ati_pcigart.o drm-usb-y := drm_usb.o diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c index 1fe719fb32e..7b2d378b257 100644 --- a/drivers/gpu/drm/drm_crtc_helper.c +++ b/drivers/gpu/drm/drm_crtc_helper.c @@ -39,6 +39,17 @@ #include <drm/drm_fb_helper.h> #include <drm/drm_edid.h> +/** + * drm_helper_move_panel_connectors_to_head() - move panels to the front in the + * connector list + * @dev: drm device to operate on + * + * Some userspace presumes that the first connected connector is the main + * display, where it's supposed to display e.g. the login screen. For + * laptops, this should be the main panel. Use this function to sort all + * (eDP/LVDS) panels to the front of the connector list, instead of + * painstakingly trying to initialize them in the right order. + */ void drm_helper_move_panel_connectors_to_head(struct drm_device *dev) { struct drm_connector *connector, *tmp; @@ -82,22 +93,21 @@ static void drm_mode_validate_flag(struct drm_connector *connector, /** * drm_helper_probe_single_connector_modes - get complete set of display modes - * @dev: DRM device + * @connector: connector to probe * @maxX: max width for modes * @maxY: max height for modes * * LOCKING: * Caller must hold mode config lock. * - * Based on @dev's mode_config layout, scan all the connectors and try to detect - * modes on them. Modes will first be added to the connector's probed_modes - * list, then culled (based on validity and the @maxX, @maxY parameters) and - * put into the normal modes list. - * - * Intended to be used either at bootup time or when major configuration - * changes have occurred. + * Based on the helper callbacks implemented by @connector try to detect all + * valid modes. Modes will first be added to the connector's probed_modes list, + * then culled (based on validity and the @maxX, @maxY parameters) and put into + * the normal modes list. * - * FIXME: take into account monitor limits + * Intended to be use as a generic implementation of the ->probe() @connector + * callback for drivers that use the crtc helpers for output mode filtering and + * detection. * * RETURNS: * Number of modes found on @connector. @@ -348,17 +358,24 @@ drm_crtc_prepare_encoders(struct drm_device *dev) } /** - * drm_crtc_set_mode - set a mode + * drm_crtc_helper_set_mode - internal helper to set a mode * @crtc: CRTC to program * @mode: mode to use * @x: horizontal offset into the surface * @y: vertical offset into the surface + * @old_fb: old framebuffer, for cleanup * * LOCKING: * Caller must hold mode config lock. * * Try to set @mode on @crtc. Give @crtc and its associated connectors a chance - * to fixup or reject the mode prior to trying to set it. + * to fixup or reject the mode prior to trying to set it. This is an internal + * helper that drivers could e.g. use to update properties that require the + * entire output pipe to be disabled and re-enabled in a new configuration. For + * example for changing whether audio is enabled on a hdmi link or for changing + * panel fitter or dither attributes. It is also called by the + * drm_crtc_helper_set_config() helper function to drive the mode setting + * sequence. * * RETURNS: * True if the mode was set successfully, or false otherwise. @@ -514,20 +531,19 @@ drm_crtc_helper_disable(struct drm_crtc *crtc) /** * drm_crtc_helper_set_config - set a new config from userspace - * @crtc: CRTC to setup - * @crtc_info: user provided configuration - * @new_mode: new mode to set - * @connector_set: set of connectors for the new config - * @fb: new framebuffer + * @set: mode set configuration * * LOCKING: * Caller must hold mode config lock. * - * Setup a new configuration, provided by the user in @crtc_info, and enable - * it. + * Setup a new configuration, provided by the upper layers (either an ioctl call + * from userspace or internally e.g. from the fbdev suppport code) in @set, and + * enable it. This is the main helper functions for drivers that implement + * kernel mode setting with the crtc helper functions and the assorted + * ->prepare(), ->modeset() and ->commit() helper callbacks. * * RETURNS: - * Zero. (FIXME) + * Returns 0 on success, -ERRNO on failure. */ int drm_crtc_helper_set_config(struct drm_mode_set *set) { @@ -823,12 +839,14 @@ static int drm_helper_choose_crtc_dpms(struct drm_crtc *crtc) } /** - * drm_helper_connector_dpms - * @connector affected connector - * @mode DPMS mode + * drm_helper_connector_dpms() - connector dpms helper implementation + * @connector: affected connector + * @mode: DPMS mode * - * Calls the low-level connector DPMS function, then - * calls appropriate encoder and crtc DPMS functions as well + * This is the main helper function provided by the crtc helper framework for + * implementing the DPMS connector attribute. It computes the new desired DPMS + * state for all encoders and crtcs in the output mesh and calls the ->dpms() + * callback provided by the driver appropriately. */ void drm_helper_connector_dpms(struct drm_connector *connector, int mode) { diff --git a/drivers/gpu/drm/drm_dp_helper.c b/drivers/gpu/drm/drm_dp_helper.c index 3c4cccd0d75..89e19662716 100644 --- a/drivers/gpu/drm/drm_dp_helper.c +++ b/drivers/gpu/drm/drm_dp_helper.c @@ -30,6 +30,15 @@ #include <drm/drm_dp_helper.h> #include <drm/drmP.h> +/** + * DOC: dp helpers + * + * These functions contain some common logic and helpers at various abstraction + * levels to deal with Display Port sink devices and related things like DP aux + * channel transfers, EDID reading over DP aux channels, decoding certain DPCD + * blocks, ... + */ + /* Run a single AUX_CH I2C transaction, writing/reading data as necessary */ static int i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode, @@ -193,6 +202,18 @@ i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter) return 0; } +/** + * i2c_dp_aux_add_bus() - register an i2c adapter using the aux ch helper + * @adapter: i2c adapter to register + * + * This registers an i2c adapater that uses dp aux channel as it's underlaying + * transport. The driver needs to fill out the &i2c_algo_dp_aux_data structure + * and store it in the algo_data member of the @adapter argument. This will be + * used by the i2c over dp aux algorithm to drive the hardware. + * + * RETURNS: + * 0 on success, -ERRNO on failure. + */ int i2c_dp_aux_add_bus(struct i2c_adapter *adapter) { diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 16482000354..484c36a4b7a 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1639,7 +1639,7 @@ parse_hdmi_vsdb(struct drm_connector *connector, const u8 *db) if (len >= 12) connector->audio_latency[1] = db[12]; - DRM_LOG_KMS("HDMI: DVI dual %d, " + DRM_DEBUG_KMS("HDMI: DVI dual %d, " "max TMDS clock %d, " "latency present %d %d, " "video latency %d %d, " diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 2c44af691e9..05e623a5d09 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -45,6 +45,15 @@ MODULE_LICENSE("GPL and additional rights"); static LIST_HEAD(kernel_fb_helper_list); +/** + * DOC: fbdev helpers + * + * The fb helper functions are useful to provide an fbdev on top of a drm kernel + * mode setting driver. They can be used mostly independantely from the crtc + * helper functions used by many drivers to implement the kernel mode setting + * interfaces. + */ + /* simple single crtc case helper function */ int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) { @@ -1302,12 +1311,14 @@ out: /** * drm_helper_initial_config - setup a sane initial connector configuration - * @dev: DRM device + * @fb_helper: fb_helper device struct + * @bpp_sel: bpp value to use for the framebuffer configuration * * LOCKING: - * Called at init time, must take mode config lock. + * Called at init time by the driver to set up the @fb_helper initial + * configuration, must take the mode config lock. * - * Scan the CRTCs and connectors and try to put together an initial setup. + * Scans the CRTCs and connectors and tries to put together an initial setup. * At the moment, this is a cloned configuration across all heads with * a new framebuffer object as the backing store. * @@ -1341,7 +1352,7 @@ EXPORT_SYMBOL(drm_fb_helper_initial_config); /** * drm_fb_helper_hotplug_event - respond to a hotplug notification by - * probing all the outputs attached to the fb. + * probing all the outputs attached to the fb * @fb_helper: the drm_fb_helper * * LOCKING: diff --git a/drivers/gpu/drm/drm_hashtab.c b/drivers/gpu/drm/drm_hashtab.c index 5729e390aa4..80254547a3f 100644 --- a/drivers/gpu/drm/drm_hashtab.c +++ b/drivers/gpu/drm/drm_hashtab.c @@ -67,7 +67,7 @@ void drm_ht_verbose_list(struct drm_open_hash *ht, unsigned long key) hashed_key = hash_long(key, ht->order); DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key); h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, list, h_list, head) + hlist_for_each_entry(entry, list, h_list, head) DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key); } @@ -81,7 +81,7 @@ static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; - hlist_for_each_entry_rcu(entry, list, h_list, head) { + hlist_for_each_entry(entry, list, h_list, head) { if (entry->key == key) return list; if (entry->key > key) @@ -90,6 +90,24 @@ static struct hlist_node *drm_ht_find_key(struct drm_open_hash *ht, return NULL; } +static struct hlist_node *drm_ht_find_key_rcu(struct drm_open_hash *ht, + unsigned long key) +{ + struct drm_hash_item *entry; + struct hlist_head *h_list; + struct hlist_node *list; + unsigned int hashed_key; + + hashed_key = hash_long(key, ht->order); + h_list = &ht->table[hashed_key]; + hlist_for_each_entry_rcu(entry, list, h_list, head) { + if (entry->key == key) + return list; + if (entry->key > key) + break; + } + return NULL; +} int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) { @@ -102,7 +120,7 @@ int drm_ht_insert_item(struct drm_open_hash *ht, struct drm_hash_item *item) hashed_key = hash_long(key, ht->order); h_list = &ht->table[hashed_key]; parent = NULL; - hlist_for_each_entry_rcu(entry, list, h_list, head) { + hlist_for_each_entry(entry, list, h_list, head) { if (entry->key == key) return -EINVAL; if (entry->key > key) @@ -152,7 +170,7 @@ int drm_ht_find_item(struct drm_open_hash *ht, unsigned long key, { struct hlist_node *list; - list = drm_ht_find_key(ht, key); + list = drm_ht_find_key_rcu(ht, key); if (!list) return -EINVAL; diff --git a/drivers/gpu/drm/drm_pci.c b/drivers/gpu/drm/drm_pci.c index ba33144257e..754bc96e10c 100644 --- a/drivers/gpu/drm/drm_pci.c +++ b/drivers/gpu/drm/drm_pci.c @@ -470,7 +470,7 @@ int drm_pcie_get_speed_cap_mask(struct drm_device *dev, u32 *mask) { struct pci_dev *root; int pos; - u32 lnkcap, lnkcap2; + u32 lnkcap = 0, lnkcap2 = 0; *mask = 0; if (!dev->pdev) diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile index a990df4d6c0..ab25752a0b1 100644 --- a/drivers/gpu/drm/nouveau/Makefile +++ b/drivers/gpu/drm/nouveau/Makefile @@ -11,6 +11,7 @@ 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/falcon.o nouveau-y += core/core/gpuobj.o nouveau-y += core/core/handle.o nouveau-y += core/core/mm.o @@ -29,6 +30,7 @@ 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/disp.o nouveau-y += core/subdev/bios/dp.o nouveau-y += core/subdev/bios/extdev.o nouveau-y += core/subdev/bios/gpio.o @@ -64,9 +66,19 @@ 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/nv1a.o nouveau-y += core/subdev/fb/nv20.o +nouveau-y += core/subdev/fb/nv25.o nouveau-y += core/subdev/fb/nv30.o +nouveau-y += core/subdev/fb/nv35.o +nouveau-y += core/subdev/fb/nv36.o nouveau-y += core/subdev/fb/nv40.o +nouveau-y += core/subdev/fb/nv41.o +nouveau-y += core/subdev/fb/nv44.o +nouveau-y += core/subdev/fb/nv46.o +nouveau-y += core/subdev/fb/nv47.o +nouveau-y += core/subdev/fb/nv49.o +nouveau-y += core/subdev/fb/nv4e.o nouveau-y += core/subdev/fb/nv50.o nouveau-y += core/subdev/fb/nvc0.o nouveau-y += core/subdev/gpio/base.o @@ -111,7 +123,10 @@ 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/dmaobj/nvd0.o nouveau-y += core/engine/bsp/nv84.o +nouveau-y += core/engine/bsp/nvc0.o +nouveau-y += core/engine/bsp/nve0.o nouveau-y += core/engine/copy/nva3.o nouveau-y += core/engine/copy/nvc0.o nouveau-y += core/engine/copy/nve0.o @@ -119,7 +134,21 @@ 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/nv84.o +nouveau-y += core/engine/disp/nv94.o +nouveau-y += core/engine/disp/nva0.o +nouveau-y += core/engine/disp/nva3.o nouveau-y += core/engine/disp/nvd0.o +nouveau-y += core/engine/disp/nve0.o +nouveau-y += core/engine/disp/dacnv50.o +nouveau-y += core/engine/disp/hdanva3.o +nouveau-y += core/engine/disp/hdanvd0.o +nouveau-y += core/engine/disp/hdminv84.o +nouveau-y += core/engine/disp/hdminva3.o +nouveau-y += core/engine/disp/hdminvd0.o +nouveau-y += core/engine/disp/sornv50.o +nouveau-y += core/engine/disp/sornv94.o +nouveau-y += core/engine/disp/sornvd0.o nouveau-y += core/engine/disp/vga.o nouveau-y += core/engine/fifo/base.o nouveau-y += core/engine/fifo/nv04.o @@ -151,11 +180,14 @@ 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/ppp/nvc0.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 +nouveau-y += core/engine/vp/nvc0.o +nouveau-y += core/engine/vp/nve0.o # drm/core nouveau-y += nouveau_drm.o nouveau_chan.o nouveau_dma.o nouveau_fence.o @@ -166,7 +198,7 @@ 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 += nouveau_connector.o nouveau_dp.o nouveau-y += nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o # drm/kms/nv04:nv50 @@ -175,9 +207,7 @@ 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 +nouveau-y += nv50_display.o # drm/pm nouveau-y += nouveau_pm.o nouveau_volt.o nouveau_perf.o diff --git a/drivers/gpu/drm/nouveau/core/core/engctx.c b/drivers/gpu/drm/nouveau/core/core/engctx.c index e41b10d5eb5..84c71fad2b6 100644 --- a/drivers/gpu/drm/nouveau/core/core/engctx.c +++ b/drivers/gpu/drm/nouveau/core/core/engctx.c @@ -189,6 +189,21 @@ nouveau_engctx_fini(struct nouveau_engctx *engctx, bool suspend) return nouveau_gpuobj_fini(&engctx->base, suspend); } +int +_nouveau_engctx_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_engctx *engctx; + int ret; + + ret = nouveau_engctx_create(parent, engine, oclass, NULL, 256, 256, + NVOBJ_FLAG_ZERO_ALLOC, &engctx); + *pobject = nv_object(engctx); + return ret; +} + void _nouveau_engctx_dtor(struct nouveau_object *object) { diff --git a/drivers/gpu/drm/nouveau/core/core/falcon.c b/drivers/gpu/drm/nouveau/core/core/falcon.c new file mode 100644 index 00000000000..6b0843c3387 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/core/falcon.c @@ -0,0 +1,247 @@ +/* + * 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. + */ + +#include <core/falcon.h> + +#include <subdev/timer.h> + +u32 +_nouveau_falcon_rd32(struct nouveau_object *object, u64 addr) +{ + struct nouveau_falcon *falcon = (void *)object; + return nv_rd32(falcon, falcon->addr + addr); +} + +void +_nouveau_falcon_wr32(struct nouveau_object *object, u64 addr, u32 data) +{ + struct nouveau_falcon *falcon = (void *)object; + nv_wr32(falcon, falcon->addr + addr, data); +} + +int +_nouveau_falcon_init(struct nouveau_object *object) +{ + struct nouveau_device *device = nv_device(object); + struct nouveau_falcon *falcon = (void *)object; + const struct firmware *fw; + char name[32] = "internal"; + int ret, i; + u32 caps; + + /* enable engine, and determine its capabilities */ + ret = nouveau_engine_init(&falcon->base); + if (ret) + return ret; + + if (device->chipset < 0xa3 || + device->chipset == 0xaa || device->chipset == 0xac) { + falcon->version = 0; + falcon->secret = (falcon->addr == 0x087000) ? 1 : 0; + } else { + caps = nv_ro32(falcon, 0x12c); + falcon->version = (caps & 0x0000000f); + falcon->secret = (caps & 0x00000030) >> 4; + } + + caps = nv_ro32(falcon, 0x108); + falcon->code.limit = (caps & 0x000001ff) << 8; + falcon->data.limit = (caps & 0x0003fe00) >> 1; + + nv_debug(falcon, "falcon version: %d\n", falcon->version); + nv_debug(falcon, "secret level: %d\n", falcon->secret); + nv_debug(falcon, "code limit: %d\n", falcon->code.limit); + nv_debug(falcon, "data limit: %d\n", falcon->data.limit); + + /* wait for 'uc halted' to be signalled before continuing */ + if (falcon->secret) { + nv_wait(falcon, 0x008, 0x00000010, 0x00000010); + nv_wo32(falcon, 0x004, 0x00000010); + } + + /* disable all interrupts */ + nv_wo32(falcon, 0x014, 0xffffffff); + + /* no default ucode provided by the engine implementation, try and + * locate a "self-bootstrapping" firmware image for the engine + */ + if (!falcon->code.data) { + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03x", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret == 0) { + falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); + falcon->code.size = fw->size; + falcon->data.data = NULL; + falcon->data.size = 0; + release_firmware(fw); + } + + falcon->external = true; + } + + /* next step is to try and load "static code/data segment" firmware + * images for the engine + */ + if (!falcon->code.data) { + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xd", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret) { + nv_error(falcon, "unable to load firmware data\n"); + return ret; + } + + falcon->data.data = kmemdup(fw->data, fw->size, GFP_KERNEL); + falcon->data.size = fw->size; + release_firmware(fw); + if (!falcon->data.data) + return -ENOMEM; + + snprintf(name, sizeof(name), "nouveau/nv%02x_fuc%03xc", + device->chipset, falcon->addr >> 12); + + ret = request_firmware(&fw, name, &device->pdev->dev); + if (ret) { + nv_error(falcon, "unable to load firmware code\n"); + return ret; + } + + falcon->code.data = kmemdup(fw->data, fw->size, GFP_KERNEL); + falcon->code.size = fw->size; + release_firmware(fw); + if (!falcon->code.data) + return -ENOMEM; + } + + nv_debug(falcon, "firmware: %s (%s)\n", name, falcon->data.data ? + "static code/data segments" : "self-bootstrapping"); + + /* ensure any "self-bootstrapping" firmware image is in vram */ + if (!falcon->data.data && !falcon->core) { + ret = nouveau_gpuobj_new(object->parent, NULL, + falcon->code.size, 256, 0, + &falcon->core); + if (ret) { + nv_error(falcon, "core allocation failed, %d\n", ret); + return ret; + } + + for (i = 0; i < falcon->code.size; i += 4) + nv_wo32(falcon->core, i, falcon->code.data[i / 4]); + } + + /* upload firmware bootloader (or the full code segments) */ + if (falcon->core) { + if (device->card_type < NV_C0) + nv_wo32(falcon, 0x618, 0x04000000); + else + nv_wo32(falcon, 0x618, 0x00000114); + nv_wo32(falcon, 0x11c, 0); + nv_wo32(falcon, 0x110, falcon->core->addr >> 8); + nv_wo32(falcon, 0x114, 0); + nv_wo32(falcon, 0x118, 0x00006610); + } else { + if (falcon->code.size > falcon->code.limit || + falcon->data.size > falcon->data.limit) { + nv_error(falcon, "ucode exceeds falcon limit(s)\n"); + return -EINVAL; + } + + if (falcon->version < 3) { + nv_wo32(falcon, 0xff8, 0x00100000); + for (i = 0; i < falcon->code.size / 4; i++) + nv_wo32(falcon, 0xff4, falcon->code.data[i]); + } else { + nv_wo32(falcon, 0x180, 0x01000000); + for (i = 0; i < falcon->code.size / 4; i++) { + if ((i & 0x3f) == 0) + nv_wo32(falcon, 0x188, i >> 6); + nv_wo32(falcon, 0x184, falcon->code.data[i]); + } + } + } + + /* upload data segment (if necessary), zeroing the remainder */ + if (falcon->version < 3) { + nv_wo32(falcon, 0xff8, 0x00000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nv_wo32(falcon, 0xff4, falcon->data.data[i]); + for (; i < falcon->data.limit; i += 4) + nv_wo32(falcon, 0xff4, 0x00000000); + } else { + nv_wo32(falcon, 0x1c0, 0x01000000); + for (i = 0; !falcon->core && i < falcon->data.size / 4; i++) + nv_wo32(falcon, 0x1c4, falcon->data.data[i]); + for (; i < falcon->data.limit / 4; i++) + nv_wo32(falcon, 0x1c4, 0x00000000); + } + + /* start it running */ + nv_wo32(falcon, 0x10c, 0x00000001); /* BLOCK_ON_FIFO */ + nv_wo32(falcon, 0x104, 0x00000000); /* ENTRY */ + nv_wo32(falcon, 0x100, 0x00000002); /* TRIGGER */ + nv_wo32(falcon, 0x048, 0x00000003); /* FIFO | CHSW */ + return 0; +} + +int +_nouveau_falcon_fini(struct nouveau_object *object, bool suspend) +{ + struct nouveau_falcon *falcon = (void *)object; + + if (!suspend) { + nouveau_gpuobj_ref(NULL, &falcon->core); + if (falcon->external) { + kfree(falcon->data.data); + kfree(falcon->code.data); + falcon->code.data = NULL; + } + } + + nv_mo32(falcon, 0x048, 0x00000003, 0x00000000); + nv_wo32(falcon, 0x014, 0xffffffff); + + return nouveau_engine_fini(&falcon->base, suspend); +} + +int +nouveau_falcon_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, u32 addr, bool enable, + const char *iname, const char *fname, + int length, void **pobject) +{ + struct nouveau_falcon *falcon; + int ret; + + ret = nouveau_engine_create_(parent, engine, oclass, enable, iname, + fname, length, pobject); + falcon = *pobject; + if (ret) + return ret; + + falcon->addr = addr; + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/core/gpuobj.c b/drivers/gpu/drm/nouveau/core/core/gpuobj.c index 70586fde69c..560b2214cf1 100644 --- a/drivers/gpu/drm/nouveau/core/core/gpuobj.c +++ b/drivers/gpu/drm/nouveau/core/core/gpuobj.c @@ -183,7 +183,7 @@ _nouveau_gpuobj_fini(struct nouveau_object *object, bool suspend) } u32 -_nouveau_gpuobj_rd32(struct nouveau_object *object, u32 addr) +_nouveau_gpuobj_rd32(struct nouveau_object *object, u64 addr) { struct nouveau_gpuobj *gpuobj = nv_gpuobj(object); struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent); @@ -193,7 +193,7 @@ _nouveau_gpuobj_rd32(struct nouveau_object *object, u32 addr) } void -_nouveau_gpuobj_wr32(struct nouveau_object *object, u32 addr, u32 data) +_nouveau_gpuobj_wr32(struct nouveau_object *object, u64 addr, u32 data) { struct nouveau_gpuobj *gpuobj = nv_gpuobj(object); struct nouveau_ofuncs *pfuncs = nv_ofuncs(gpuobj->parent); diff --git a/drivers/gpu/drm/nouveau/core/core/mm.c b/drivers/gpu/drm/nouveau/core/core/mm.c index a6d3cd6490f..0261a11b2ae 100644 --- a/drivers/gpu/drm/nouveau/core/core/mm.c +++ b/drivers/gpu/drm/nouveau/core/core/mm.c @@ -234,15 +234,18 @@ nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block) int nouveau_mm_fini(struct nouveau_mm *mm) { - struct nouveau_mm_node *node, *heap = - list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry); - int nodes = 0; + if (nouveau_mm_initialised(mm)) { + struct nouveau_mm_node *node, *heap = + list_first_entry(&mm->nodes, typeof(*heap), nl_entry); + int nodes = 0; + + list_for_each_entry(node, &mm->nodes, nl_entry) { + if (WARN_ON(nodes++ == mm->heap_nodes)) + return -EBUSY; + } - list_for_each_entry(node, &mm->nodes, nl_entry) { - if (WARN_ON(nodes++ == mm->heap_nodes)) - return -EBUSY; + kfree(heap); } - kfree(heap); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c index 66f7dfd907e..1d9f614cb97 100644 --- a/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nv84.c @@ -22,18 +22,13 @@ * Authors: Ben Skeggs */ -#include <core/os.h> -#include <core/class.h> #include <core/engctx.h> +#include <core/class.h> #include <engine/bsp.h> struct nv84_bsp_priv { - struct nouveau_bsp base; -}; - -struct nv84_bsp_chan { - struct nouveau_bsp_chan base; + struct nouveau_engine base; }; /******************************************************************************* @@ -49,61 +44,16 @@ nv84_bsp_sclass[] = { * BSP context ******************************************************************************/ -static int -nv84_bsp_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv84_bsp_chan *priv; - int ret; - - ret = nouveau_bsp_context_create(parent, engine, oclass, NULL, - 0, 0, 0, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -static void -nv84_bsp_context_dtor(struct nouveau_object *object) -{ - struct nv84_bsp_chan *priv = (void *)object; - nouveau_bsp_context_destroy(&priv->base); -} - -static int -nv84_bsp_context_init(struct nouveau_object *object) -{ - struct nv84_bsp_chan *priv = (void *)object; - int ret; - - ret = nouveau_bsp_context_init(&priv->base); - if (ret) - return ret; - - return 0; -} - -static int -nv84_bsp_context_fini(struct nouveau_object *object, bool suspend) -{ - struct nv84_bsp_chan *priv = (void *)object; - return nouveau_bsp_context_fini(&priv->base, suspend); -} - static struct nouveau_oclass nv84_bsp_cclass = { .handle = NV_ENGCTX(BSP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv84_bsp_context_ctor, - .dtor = nv84_bsp_context_dtor, - .init = nv84_bsp_context_init, - .fini = nv84_bsp_context_fini, - .rd32 = _nouveau_bsp_context_rd32, - .wr32 = _nouveau_bsp_context_wr32, + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, }, }; @@ -111,11 +61,6 @@ nv84_bsp_cclass = { * BSP engine/subdev functions ******************************************************************************/ -static void -nv84_bsp_intr(struct nouveau_subdev *subdev) -{ -} - static int nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -124,52 +69,25 @@ nv84_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv84_bsp_priv *priv; int ret; - ret = nouveau_bsp_create(parent, engine, oclass, &priv); + ret = nouveau_engine_create(parent, engine, oclass, true, + "PBSP", "bsp", &priv); *pobject = nv_object(priv); if (ret) return ret; nv_subdev(priv)->unit = 0x04008000; - nv_subdev(priv)->intr = nv84_bsp_intr; nv_engine(priv)->cclass = &nv84_bsp_cclass; nv_engine(priv)->sclass = nv84_bsp_sclass; return 0; } -static void -nv84_bsp_dtor(struct nouveau_object *object) -{ - struct nv84_bsp_priv *priv = (void *)object; - nouveau_bsp_destroy(&priv->base); -} - -static int -nv84_bsp_init(struct nouveau_object *object) -{ - struct nv84_bsp_priv *priv = (void *)object; - int ret; - - ret = nouveau_bsp_init(&priv->base); - if (ret) - return ret; - - return 0; -} - -static int -nv84_bsp_fini(struct nouveau_object *object, bool suspend) -{ - struct nv84_bsp_priv *priv = (void *)object; - return nouveau_bsp_fini(&priv->base, suspend); -} - struct nouveau_oclass nv84_bsp_oclass = { .handle = NV_ENGINE(BSP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv84_bsp_ctor, - .dtor = nv84_bsp_dtor, - .init = nv84_bsp_init, - .fini = nv84_bsp_fini, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c new file mode 100644 index 00000000000..0a5aa6bb087 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nvc0.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Maarten Lankhorst + * + * 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: Maarten Lankhorst + */ + +#include <core/falcon.h> + +#include <engine/bsp.h> + +struct nvc0_bsp_priv { + struct nouveau_falcon base; +}; + +/******************************************************************************* + * BSP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nvc0_bsp_sclass[] = { + { 0x90b1, &nouveau_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PBSP context + ******************************************************************************/ + +static struct nouveau_oclass +nvc0_bsp_cclass = { + .handle = NV_ENGCTX(BSP, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PBSP engine/subdev functions + ******************************************************************************/ + +static int +nvc0_bsp_init(struct nouveau_object *object) +{ + struct nvc0_bsp_priv *priv = (void *)object; + int ret; + + ret = nouveau_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x084010, 0x0000fff2); + nv_wr32(priv, 0x08401c, 0x0000fff2); + return 0; +} + +static int +nvc0_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvc0_bsp_priv *priv; + int ret; + + ret = nouveau_falcon_create(parent, engine, oclass, 0x084000, true, + "PBSP", "bsp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00008000; + nv_engine(priv)->cclass = &nvc0_bsp_cclass; + nv_engine(priv)->sclass = nvc0_bsp_sclass; + return 0; +} + +struct nouveau_oclass +nvc0_bsp_oclass = { + .handle = NV_ENGINE(BSP, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_bsp_ctor, + .dtor = _nouveau_falcon_dtor, + .init = nvc0_bsp_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c new file mode 100644 index 00000000000..d4f23bbd75b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/bsp/nve0.c @@ -0,0 +1,110 @@ +/* + * 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/falcon.h> + +#include <engine/bsp.h> + +struct nve0_bsp_priv { + struct nouveau_falcon base; +}; + +/******************************************************************************* + * BSP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nve0_bsp_sclass[] = { + { 0x95b1, &nouveau_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PBSP context + ******************************************************************************/ + +static struct nouveau_oclass +nve0_bsp_cclass = { + .handle = NV_ENGCTX(BSP, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PBSP engine/subdev functions + ******************************************************************************/ + +static int +nve0_bsp_init(struct nouveau_object *object) +{ + struct nve0_bsp_priv *priv = (void *)object; + int ret; + + ret = nouveau_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x084010, 0x0000fff2); + nv_wr32(priv, 0x08401c, 0x0000fff2); + return 0; +} + +static int +nve0_bsp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nve0_bsp_priv *priv; + int ret; + + ret = nouveau_falcon_create(parent, engine, oclass, 0x084000, true, + "PBSP", "bsp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00008000; + nv_engine(priv)->cclass = &nve0_bsp_cclass; + nv_engine(priv)->sclass = nve0_bsp_sclass; + return 0; +} + +struct nouveau_oclass +nve0_bsp_oclass = { + .handle = NV_ENGINE(BSP, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_bsp_ctor, + .dtor = _nouveau_falcon_dtor, + .init = nve0_bsp_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c index 4df6da0af74..283248c7b05 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nva3.c @@ -22,10 +22,9 @@ * Authors: Ben Skeggs */ -#include <core/os.h> -#include <core/enum.h> +#include <core/falcon.h> #include <core/class.h> -#include <core/engctx.h> +#include <core/enum.h> #include <subdev/fb.h> #include <subdev/vm.h> @@ -36,11 +35,7 @@ #include "fuc/nva3.fuc.h" struct nva3_copy_priv { - struct nouveau_copy base; -}; - -struct nva3_copy_chan { - struct nouveau_copy_chan base; + struct nouveau_falcon base; }; /******************************************************************************* @@ -57,34 +52,16 @@ nva3_copy_sclass[] = { * PCOPY context ******************************************************************************/ -static int -nva3_copy_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nva3_copy_chan *priv; - int ret; - - ret = nouveau_copy_context_create(parent, engine, oclass, NULL, 256, 0, - NVOBJ_FLAG_ZERO_ALLOC, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - static struct nouveau_oclass nva3_copy_cclass = { .handle = NV_ENGCTX(COPY0, 0xa3), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nva3_copy_context_ctor, - .dtor = _nouveau_copy_context_dtor, - .init = _nouveau_copy_context_init, - .fini = _nouveau_copy_context_fini, - .rd32 = _nouveau_copy_context_rd32, - .wr32 = _nouveau_copy_context_wr32, + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, }, }; @@ -100,41 +77,40 @@ static const struct nouveau_enum nva3_copy_isr_error_name[] = { {} }; -static void +void nva3_copy_intr(struct nouveau_subdev *subdev) { struct nouveau_fifo *pfifo = nouveau_fifo(subdev); struct nouveau_engine *engine = nv_engine(subdev); + struct nouveau_falcon *falcon = (void *)subdev; struct nouveau_object *engctx; - struct nva3_copy_priv *priv = (void *)subdev; - u32 dispatch = nv_rd32(priv, 0x10401c); - u32 stat = nv_rd32(priv, 0x104008) & dispatch & ~(dispatch >> 16); - u64 inst = nv_rd32(priv, 0x104050) & 0x3fffffff; - u32 ssta = nv_rd32(priv, 0x104040) & 0x0000ffff; - u32 addr = nv_rd32(priv, 0x104040) >> 16; + u32 dispatch = nv_ro32(falcon, 0x01c); + u32 stat = nv_ro32(falcon, 0x008) & dispatch & ~(dispatch >> 16); + u64 inst = nv_ro32(falcon, 0x050) & 0x3fffffff; + u32 ssta = nv_ro32(falcon, 0x040) & 0x0000ffff; + u32 addr = nv_ro32(falcon, 0x040) >> 16; u32 mthd = (addr & 0x07ff) << 2; u32 subc = (addr & 0x3800) >> 11; - u32 data = nv_rd32(priv, 0x104044); + u32 data = nv_ro32(falcon, 0x044); int chid; engctx = nouveau_engctx_get(engine, inst); chid = pfifo->chid(pfifo, engctx); if (stat & 0x00000040) { - nv_error(priv, "DISPATCH_ERROR ["); + nv_error(falcon, "DISPATCH_ERROR ["); nouveau_enum_print(nva3_copy_isr_error_name, ssta); printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n", chid, inst << 12, subc, mthd, data); - nv_wr32(priv, 0x104004, 0x00000040); + nv_wo32(falcon, 0x004, 0x00000040); stat &= ~0x00000040; } if (stat) { - nv_error(priv, "unhandled intr 0x%08x\n", stat); - nv_wr32(priv, 0x104004, stat); + nv_error(falcon, "unhandled intr 0x%08x\n", stat); + nv_wo32(falcon, 0x004, stat); } - nv50_fb_trap(nouveau_fb(priv), 1); nouveau_engctx_put(engctx); } @@ -154,7 +130,8 @@ nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nva3_copy_priv *priv; int ret; - ret = nouveau_copy_create(parent, engine, oclass, enable, 0, &priv); + ret = nouveau_falcon_create(parent, engine, oclass, 0x104000, enable, + "PCE0", "copy0", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -164,59 +141,22 @@ nva3_copy_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->cclass = &nva3_copy_cclass; nv_engine(priv)->sclass = nva3_copy_sclass; nv_engine(priv)->tlb_flush = nva3_copy_tlb_flush; + nv_falcon(priv)->code.data = nva3_pcopy_code; + nv_falcon(priv)->code.size = sizeof(nva3_pcopy_code); + nv_falcon(priv)->data.data = nva3_pcopy_data; + nv_falcon(priv)->data.size = sizeof(nva3_pcopy_data); return 0; } -static int -nva3_copy_init(struct nouveau_object *object) -{ - struct nva3_copy_priv *priv = (void *)object; - int ret, i; - - ret = nouveau_copy_init(&priv->base); - if (ret) - return ret; - - /* disable all interrupts */ - nv_wr32(priv, 0x104014, 0xffffffff); - - /* upload ucode */ - nv_wr32(priv, 0x1041c0, 0x01000000); - for (i = 0; i < sizeof(nva3_pcopy_data) / 4; i++) - nv_wr32(priv, 0x1041c4, nva3_pcopy_data[i]); - - nv_wr32(priv, 0x104180, 0x01000000); - for (i = 0; i < sizeof(nva3_pcopy_code) / 4; i++) { - if ((i & 0x3f) == 0) - nv_wr32(priv, 0x104188, i >> 6); - nv_wr32(priv, 0x104184, nva3_pcopy_code[i]); - } - - /* start it running */ - nv_wr32(priv, 0x10410c, 0x00000000); - nv_wr32(priv, 0x104104, 0x00000000); /* ENTRY */ - nv_wr32(priv, 0x104100, 0x00000002); /* TRIGGER */ - return 0; -} - -static int -nva3_copy_fini(struct nouveau_object *object, bool suspend) -{ - struct nva3_copy_priv *priv = (void *)object; - - nv_mask(priv, 0x104048, 0x00000003, 0x00000000); - nv_wr32(priv, 0x104014, 0xffffffff); - - return nouveau_copy_fini(&priv->base, suspend); -} - struct nouveau_oclass nva3_copy_oclass = { .handle = NV_ENGINE(COPY0, 0xa3), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nva3_copy_ctor, - .dtor = _nouveau_copy_dtor, - .init = nva3_copy_init, - .fini = nva3_copy_fini, + .dtor = _nouveau_falcon_dtor, + .init = _nouveau_falcon_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c index 06d4a879105..b3ed2737e21 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nvc0.c @@ -22,10 +22,9 @@ * Authors: Ben Skeggs */ -#include <core/os.h> -#include <core/enum.h> +#include <core/falcon.h> #include <core/class.h> -#include <core/engctx.h> +#include <core/enum.h> #include <engine/fifo.h> #include <engine/copy.h> @@ -33,11 +32,7 @@ #include "fuc/nvc0.fuc.h" struct nvc0_copy_priv { - struct nouveau_copy base; -}; - -struct nvc0_copy_chan { - struct nouveau_copy_chan base; + struct nouveau_falcon base; }; /******************************************************************************* @@ -60,32 +55,14 @@ nvc0_copy1_sclass[] = { * PCOPY context ******************************************************************************/ -static int -nvc0_copy_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nvc0_copy_chan *priv; - int ret; - - ret = nouveau_copy_context_create(parent, engine, oclass, NULL, 256, - 256, NVOBJ_FLAG_ZERO_ALLOC, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - static struct nouveau_ofuncs nvc0_copy_context_ofuncs = { - .ctor = nvc0_copy_context_ctor, - .dtor = _nouveau_copy_context_dtor, - .init = _nouveau_copy_context_init, - .fini = _nouveau_copy_context_fini, - .rd32 = _nouveau_copy_context_rd32, - .wr32 = _nouveau_copy_context_wr32, + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, }; static struct nouveau_oclass @@ -104,50 +81,18 @@ nvc0_copy1_cclass = { * PCOPY engine/subdev functions ******************************************************************************/ -static const struct nouveau_enum nvc0_copy_isr_error_name[] = { - { 0x0001, "ILLEGAL_MTHD" }, - { 0x0002, "INVALID_ENUM" }, - { 0x0003, "INVALID_BITFIELD" }, - {} -}; - -static void -nvc0_copy_intr(struct nouveau_subdev *subdev) +static int +nvc0_copy_init(struct nouveau_object *object) { - struct nouveau_fifo *pfifo = nouveau_fifo(subdev); - struct nouveau_engine *engine = nv_engine(subdev); - struct nouveau_object *engctx; - int idx = nv_engidx(nv_object(subdev)) - NVDEV_ENGINE_COPY0; - struct nvc0_copy_priv *priv = (void *)subdev; - u32 disp = nv_rd32(priv, 0x10401c + (idx * 0x1000)); - u32 intr = nv_rd32(priv, 0x104008 + (idx * 0x1000)); - u32 stat = intr & disp & ~(disp >> 16); - u64 inst = nv_rd32(priv, 0x104050 + (idx * 0x1000)) & 0x0fffffff; - u32 ssta = nv_rd32(priv, 0x104040 + (idx * 0x1000)) & 0x0000ffff; - u32 addr = nv_rd32(priv, 0x104040 + (idx * 0x1000)) >> 16; - u32 mthd = (addr & 0x07ff) << 2; - u32 subc = (addr & 0x3800) >> 11; - u32 data = nv_rd32(priv, 0x104044 + (idx * 0x1000)); - int chid; - - engctx = nouveau_engctx_get(engine, inst); - chid = pfifo->chid(pfifo, engctx); - - if (stat & 0x00000040) { - nv_error(priv, "DISPATCH_ERROR ["); - nouveau_enum_print(nvc0_copy_isr_error_name, ssta); - printk("] ch %d [0x%010llx] subc %d mthd 0x%04x data 0x%08x\n", - chid, (u64)inst << 12, subc, mthd, data); - nv_wr32(priv, 0x104004 + (idx * 0x1000), 0x00000040); - stat &= ~0x00000040; - } + struct nvc0_copy_priv *priv = (void *)object; + int ret; - if (stat) { - nv_error(priv, "unhandled intr 0x%08x\n", stat); - nv_wr32(priv, 0x104004 + (idx * 0x1000), stat); - } + ret = nouveau_falcon_init(&priv->base); + if (ret) + return ret; - nouveau_engctx_put(engctx); + nv_wo32(priv, 0x084, nv_engidx(object) - NVDEV_ENGINE_COPY0); + return 0; } static int @@ -161,15 +106,20 @@ nvc0_copy0_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (nv_rd32(parent, 0x022500) & 0x00000100) return -ENODEV; - ret = nouveau_copy_create(parent, engine, oclass, true, 0, &priv); + ret = nouveau_falcon_create(parent, engine, oclass, 0x104000, true, + "PCE0", "copy0", &priv); *pobject = nv_object(priv); if (ret) return ret; nv_subdev(priv)->unit = 0x00000040; - nv_subdev(priv)->intr = nvc0_copy_intr; + nv_subdev(priv)->intr = nva3_copy_intr; nv_engine(priv)->cclass = &nvc0_copy0_cclass; nv_engine(priv)->sclass = nvc0_copy0_sclass; + nv_falcon(priv)->code.data = nvc0_pcopy_code; + nv_falcon(priv)->code.size = sizeof(nvc0_pcopy_code); + nv_falcon(priv)->data.data = nvc0_pcopy_data; + nv_falcon(priv)->data.size = sizeof(nvc0_pcopy_data); return 0; } @@ -184,72 +134,33 @@ nvc0_copy1_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (nv_rd32(parent, 0x022500) & 0x00000200) return -ENODEV; - ret = nouveau_copy_create(parent, engine, oclass, true, 1, &priv); + ret = nouveau_falcon_create(parent, engine, oclass, 0x105000, true, + "PCE1", "copy1", &priv); *pobject = nv_object(priv); if (ret) return ret; nv_subdev(priv)->unit = 0x00000080; - nv_subdev(priv)->intr = nvc0_copy_intr; + nv_subdev(priv)->intr = nva3_copy_intr; nv_engine(priv)->cclass = &nvc0_copy1_cclass; nv_engine(priv)->sclass = nvc0_copy1_sclass; + nv_falcon(priv)->code.data = nvc0_pcopy_code; + nv_falcon(priv)->code.size = sizeof(nvc0_pcopy_code); + nv_falcon(priv)->data.data = nvc0_pcopy_data; + nv_falcon(priv)->data.size = sizeof(nvc0_pcopy_data); return 0; } -static int -nvc0_copy_init(struct nouveau_object *object) -{ - int idx = nv_engidx(object) - NVDEV_ENGINE_COPY0; - struct nvc0_copy_priv *priv = (void *)object; - int ret, i; - - ret = nouveau_copy_init(&priv->base); - if (ret) - return ret; - - /* disable all interrupts */ - nv_wr32(priv, 0x104014 + (idx * 0x1000), 0xffffffff); - - /* upload ucode */ - nv_wr32(priv, 0x1041c0 + (idx * 0x1000), 0x01000000); - for (i = 0; i < sizeof(nvc0_pcopy_data) / 4; i++) - nv_wr32(priv, 0x1041c4 + (idx * 0x1000), nvc0_pcopy_data[i]); - - nv_wr32(priv, 0x104180 + (idx * 0x1000), 0x01000000); - for (i = 0; i < sizeof(nvc0_pcopy_code) / 4; i++) { - if ((i & 0x3f) == 0) - nv_wr32(priv, 0x104188 + (idx * 0x1000), i >> 6); - nv_wr32(priv, 0x104184 + (idx * 0x1000), nvc0_pcopy_code[i]); - } - - /* start it running */ - nv_wr32(priv, 0x104084 + (idx * 0x1000), idx); - nv_wr32(priv, 0x10410c + (idx * 0x1000), 0x00000000); - nv_wr32(priv, 0x104104 + (idx * 0x1000), 0x00000000); /* ENTRY */ - nv_wr32(priv, 0x104100 + (idx * 0x1000), 0x00000002); /* TRIGGER */ - return 0; -} - -static int -nvc0_copy_fini(struct nouveau_object *object, bool suspend) -{ - int idx = nv_engidx(object) - NVDEV_ENGINE_COPY0; - struct nvc0_copy_priv *priv = (void *)object; - - nv_mask(priv, 0x104048 + (idx * 0x1000), 0x00000003, 0x00000000); - nv_wr32(priv, 0x104014 + (idx * 0x1000), 0xffffffff); - - return nouveau_copy_fini(&priv->base, suspend); -} - struct nouveau_oclass nvc0_copy0_oclass = { .handle = NV_ENGINE(COPY0, 0xc0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvc0_copy0_ctor, - .dtor = _nouveau_copy_dtor, + .dtor = _nouveau_falcon_dtor, .init = nvc0_copy_init, - .fini = nvc0_copy_fini, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, }, }; @@ -258,8 +169,10 @@ nvc0_copy1_oclass = { .handle = NV_ENGINE(COPY1, 0xc0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvc0_copy1_ctor, - .dtor = _nouveau_copy_dtor, + .dtor = _nouveau_falcon_dtor, .init = nvc0_copy_init, - .fini = nvc0_copy_fini, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c index 2017c1579ac..dbbe9e8998f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/copy/nve0.c @@ -30,11 +30,7 @@ #include <engine/copy.h> struct nve0_copy_priv { - struct nouveau_copy base; -}; - -struct nve0_copy_chan { - struct nouveau_copy_chan base; + struct nouveau_engine base; }; /******************************************************************************* @@ -51,32 +47,14 @@ nve0_copy_sclass[] = { * PCOPY context ******************************************************************************/ -static int -nve0_copy_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nve0_copy_chan *priv; - int ret; - - ret = nouveau_copy_context_create(parent, engine, oclass, NULL, 256, - 256, NVOBJ_FLAG_ZERO_ALLOC, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - static struct nouveau_ofuncs nve0_copy_context_ofuncs = { - .ctor = nve0_copy_context_ctor, - .dtor = _nouveau_copy_context_dtor, - .init = _nouveau_copy_context_init, - .fini = _nouveau_copy_context_fini, - .rd32 = _nouveau_copy_context_rd32, - .wr32 = _nouveau_copy_context_wr32, + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, }; static struct nouveau_oclass @@ -100,7 +78,8 @@ nve0_copy0_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (nv_rd32(parent, 0x022500) & 0x00000100) return -ENODEV; - ret = nouveau_copy_create(parent, engine, oclass, true, 0, &priv); + ret = nouveau_engine_create(parent, engine, oclass, true, + "PCE0", "copy0", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -122,7 +101,8 @@ nve0_copy1_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (nv_rd32(parent, 0x022500) & 0x00000200) return -ENODEV; - ret = nouveau_copy_create(parent, engine, oclass, true, 1, &priv); + ret = nouveau_engine_create(parent, engine, oclass, true, + "PCE1", "copy1", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -138,9 +118,9 @@ nve0_copy0_oclass = { .handle = NV_ENGINE(COPY0, 0xe0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nve0_copy0_ctor, - .dtor = _nouveau_copy_dtor, - .init = _nouveau_copy_init, - .fini = _nouveau_copy_fini, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, }, }; @@ -149,8 +129,8 @@ nve0_copy1_oclass = { .handle = NV_ENGINE(COPY1, 0xe0), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nve0_copy1_ctor, - .dtor = _nouveau_copy_dtor, - .init = _nouveau_copy_init, - .fini = _nouveau_copy_fini, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c index 1d85e5b66ca..b9749051272 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv84.c @@ -34,11 +34,7 @@ #include <engine/crypt.h> struct nv84_crypt_priv { - struct nouveau_crypt base; -}; - -struct nv84_crypt_chan { - struct nouveau_crypt_chan base; + struct nouveau_engine base; }; /******************************************************************************* @@ -87,34 +83,16 @@ nv84_crypt_sclass[] = { * PCRYPT context ******************************************************************************/ -static int -nv84_crypt_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv84_crypt_chan *priv; - int ret; - - ret = nouveau_crypt_context_create(parent, engine, oclass, NULL, 256, - 0, NVOBJ_FLAG_ZERO_ALLOC, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - static struct nouveau_oclass nv84_crypt_cclass = { .handle = NV_ENGCTX(CRYPT, 0x84), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv84_crypt_context_ctor, - .dtor = _nouveau_crypt_context_dtor, - .init = _nouveau_crypt_context_init, - .fini = _nouveau_crypt_context_fini, - .rd32 = _nouveau_crypt_context_rd32, - .wr32 = _nouveau_crypt_context_wr32, + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, }, }; @@ -157,7 +135,6 @@ nv84_crypt_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x102130, stat); nv_wr32(priv, 0x10200c, 0x10); - nv50_fb_trap(nouveau_fb(priv), 1); nouveau_engctx_put(engctx); } @@ -176,7 +153,8 @@ nv84_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv84_crypt_priv *priv; int ret; - ret = nouveau_crypt_create(parent, engine, oclass, &priv); + ret = nouveau_engine_create(parent, engine, oclass, true, + "PCRYPT", "crypt", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -195,7 +173,7 @@ nv84_crypt_init(struct nouveau_object *object) struct nv84_crypt_priv *priv = (void *)object; int ret; - ret = nouveau_crypt_init(&priv->base); + ret = nouveau_engine_init(&priv->base); if (ret) return ret; @@ -210,8 +188,8 @@ nv84_crypt_oclass = { .handle = NV_ENGINE(CRYPT, 0x84), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv84_crypt_ctor, - .dtor = _nouveau_crypt_dtor, + .dtor = _nouveau_engine_dtor, .init = nv84_crypt_init, - .fini = _nouveau_crypt_fini, + .fini = _nouveau_engine_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c index 9e3876c89b9..21986f3bf0c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c +++ b/drivers/gpu/drm/nouveau/core/engine/crypt/nv98.c @@ -26,6 +26,7 @@ #include <core/enum.h> #include <core/class.h> #include <core/engctx.h> +#include <core/falcon.h> #include <subdev/timer.h> #include <subdev/fb.h> @@ -36,11 +37,7 @@ #include "fuc/nv98.fuc.h" struct nv98_crypt_priv { - struct nouveau_crypt base; -}; - -struct nv98_crypt_chan { - struct nouveau_crypt_chan base; + struct nouveau_falcon base; }; /******************************************************************************* @@ -57,34 +54,16 @@ nv98_crypt_sclass[] = { * PCRYPT context ******************************************************************************/ -static int -nv98_crypt_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv98_crypt_chan *priv; - int ret; - - ret = nouveau_crypt_context_create(parent, engine, oclass, NULL, 256, - 256, NVOBJ_FLAG_ZERO_ALLOC, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - static struct nouveau_oclass nv98_crypt_cclass = { .handle = NV_ENGCTX(CRYPT, 0x98), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv98_crypt_context_ctor, - .dtor = _nouveau_crypt_context_dtor, - .init = _nouveau_crypt_context_init, - .fini = _nouveau_crypt_context_fini, - .rd32 = _nouveau_crypt_context_rd32, - .wr32 = _nouveau_crypt_context_wr32, + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, }, }; @@ -134,7 +113,6 @@ nv98_crypt_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x087004, stat); } - nv50_fb_trap(nouveau_fb(priv), 1); nouveau_engctx_put(engctx); } @@ -153,7 +131,8 @@ nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv98_crypt_priv *priv; int ret; - ret = nouveau_crypt_create(parent, engine, oclass, &priv); + ret = nouveau_falcon_create(parent, engine, oclass, 0x087000, true, + "PCRYPT", "crypt", &priv); *pobject = nv_object(priv); if (ret) return ret; @@ -163,36 +142,10 @@ nv98_crypt_ctor(struct nouveau_object *parent, struct nouveau_object *engine, nv_engine(priv)->cclass = &nv98_crypt_cclass; nv_engine(priv)->sclass = nv98_crypt_sclass; nv_engine(priv)->tlb_flush = nv98_crypt_tlb_flush; - return 0; -} - -static int -nv98_crypt_init(struct nouveau_object *object) -{ - struct nv98_crypt_priv *priv = (void *)object; - int ret, i; - - ret = nouveau_crypt_init(&priv->base); - if (ret) - return ret; - - /* wait for exit interrupt to signal */ - nv_wait(priv, 0x087008, 0x00000010, 0x00000010); - nv_wr32(priv, 0x087004, 0x00000010); - - /* upload microcode code and data segments */ - nv_wr32(priv, 0x087ff8, 0x00100000); - for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_code); i++) - nv_wr32(priv, 0x087ff4, nv98_pcrypt_code[i]); - - nv_wr32(priv, 0x087ff8, 0x00000000); - for (i = 0; i < ARRAY_SIZE(nv98_pcrypt_data); i++) - nv_wr32(priv, 0x087ff4, nv98_pcrypt_data[i]); - - /* start it running */ - nv_wr32(priv, 0x08710c, 0x00000000); - nv_wr32(priv, 0x087104, 0x00000000); /* ENTRY */ - nv_wr32(priv, 0x087100, 0x00000002); /* TRIGGER */ + nv_falcon(priv)->code.data = nv98_pcrypt_code; + nv_falcon(priv)->code.size = sizeof(nv98_pcrypt_code); + nv_falcon(priv)->data.data = nv98_pcrypt_data; + nv_falcon(priv)->data.size = sizeof(nv98_pcrypt_data); return 0; } @@ -201,8 +154,10 @@ nv98_crypt_oclass = { .handle = NV_ENGINE(CRYPT, 0x98), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv98_crypt_ctor, - .dtor = _nouveau_crypt_dtor, - .init = nv98_crypt_init, - .fini = _nouveau_crypt_fini, + .dtor = _nouveau_falcon_dtor, + .init = _nouveau_falcon_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c new file mode 100644 index 00000000000..d0817d94454 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/dacnv50.c @@ -0,0 +1,88 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/timer.h> + +#include "nv50.h" + +int +nv50_dac_power(struct nv50_disp_priv *priv, int or, u32 data) +{ + const u32 stat = (data & NV50_DISP_DAC_PWR_HSYNC) | + (data & NV50_DISP_DAC_PWR_VSYNC) | + (data & NV50_DISP_DAC_PWR_DATA) | + (data & NV50_DISP_DAC_PWR_STATE); + const u32 doff = (or * 0x800); + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); + nv_mask(priv, 0x61a004 + doff, 0xc000007f, 0x80000000 | stat); + nv_wait(priv, 0x61a004 + doff, 0x80000000, 0x00000000); + return 0; +} + +int +nv50_dac_sense(struct nv50_disp_priv *priv, int or, u32 loadval) +{ + const u32 doff = (or * 0x800); + int load = -EINVAL; + nv_wr32(priv, 0x61a00c + doff, 0x00100000 | loadval); + udelay(9500); + nv_wr32(priv, 0x61a00c + doff, 0x80000000); + load = (nv_rd32(priv, 0x61a00c + doff) & 0x38000000) >> 27; + nv_wr32(priv, 0x61a00c + doff, 0x00000000); + return load; +} + +int +nv50_dac_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + const u8 or = (mthd & NV50_DISP_DAC_MTHD_OR); + u32 *data = args; + int ret; + + if (size < sizeof(u32)) + return -EINVAL; + + switch (mthd & ~0x3f) { + case NV50_DISP_DAC_PWR: + ret = priv->dac.power(priv, or, data[0]); + break; + case NV50_DISP_DAC_LOAD: + ret = priv->dac.sense(priv, or, data[0]); + if (ret >= 0) { + data[0] = ret; + ret = 0; + } + break; + default: + BUG_ON(1); + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c new file mode 100644 index 00000000000..373dbcc523b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdanva3.c @@ -0,0 +1,48 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include "nv50.h" + +int +nva3_hda_eld(struct nv50_disp_priv *priv, int or, u8 *data, u32 size) +{ + const u32 soff = (or * 0x800); + int i; + + if (data && data[0]) { + for (i = 0; i < size; i++) + nv_wr32(priv, 0x61c440 + soff, (i << 8) | data[i]); + nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000003); + } else + if (data) { + nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000001); + } else { + nv_mask(priv, 0x61c448 + soff, 0x80000003, 0x80000000); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c new file mode 100644 index 00000000000..dc57e24fc1d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdanvd0.c @@ -0,0 +1,53 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/dp.h> +#include <subdev/bios/init.h> + +#include "nv50.h" + +int +nvd0_hda_eld(struct nv50_disp_priv *priv, int or, u8 *data, u32 size) +{ + const u32 soff = (or * 0x030); + int i; + + if (data && data[0]) { + for (i = 0; i < size; i++) + nv_wr32(priv, 0x10ec00 + soff, (i << 8) | data[i]); + nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000003); + } else + if (data) { + nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000001); + } else { + nv_mask(priv, 0x10ec10 + soff, 0x80000003, 0x80000000); + } + + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c new file mode 100644 index 00000000000..0d36bdc5141 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminv84.c @@ -0,0 +1,66 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include "nv50.h" + +int +nv84_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data) +{ + const u32 hoff = (head * 0x800); + + if (!(data & NV84_DISP_SOR_HDMI_PWR_STATE_ON)) { + nv_mask(priv, 0x6165a4 + hoff, 0x40000000, 0x00000000); + nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000000); + nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x616528 + hoff, 0x000d0282); + nv_wr32(priv, 0x61652c + hoff, 0x0000006f); + nv_wr32(priv, 0x616530 + hoff, 0x00000000); + nv_wr32(priv, 0x616534 + hoff, 0x00000000); + nv_wr32(priv, 0x616538 + hoff, 0x00000000); + nv_mask(priv, 0x616520 + hoff, 0x00000001, 0x00000001); + + /* Audio InfoFrame */ + nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x616508 + hoff, 0x000a0184); + nv_wr32(priv, 0x61650c + hoff, 0x00000071); + nv_wr32(priv, 0x616510 + hoff, 0x00000000); + nv_mask(priv, 0x616500 + hoff, 0x00000001, 0x00000001); + + /* ??? */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nv_mask(priv, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* HDMI_CTRL */ + nv_mask(priv, 0x6165a4 + hoff, 0x5f1f007f, data | 0x1f000000 /* ??? */); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c new file mode 100644 index 00000000000..f065fc248ad --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminva3.c @@ -0,0 +1,66 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include "nv50.h" + +int +nva3_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data) +{ + const u32 soff = (or * 0x800); + + if (!(data & NV84_DISP_SOR_HDMI_PWR_STATE_ON)) { + nv_mask(priv, 0x61c5a4 + soff, 0x40000000, 0x00000000); + nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000000); + nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x61c528 + soff, 0x000d0282); + nv_wr32(priv, 0x61c52c + soff, 0x0000006f); + nv_wr32(priv, 0x61c530 + soff, 0x00000000); + nv_wr32(priv, 0x61c534 + soff, 0x00000000); + nv_wr32(priv, 0x61c538 + soff, 0x00000000); + nv_mask(priv, 0x61c520 + soff, 0x00000001, 0x00000001); + + /* Audio InfoFrame */ + nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x61c508 + soff, 0x000a0184); + nv_wr32(priv, 0x61c50c + soff, 0x00000071); + nv_wr32(priv, 0x61c510 + soff, 0x00000000); + nv_mask(priv, 0x61c500 + soff, 0x00000001, 0x00000001); + + /* ??? */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ + nv_mask(priv, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ + nv_mask(priv, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ + + /* HDMI_CTRL */ + nv_mask(priv, 0x61c5a4 + soff, 0x5f1f007f, data | 0x1f000000 /* ??? */); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c new file mode 100644 index 00000000000..5151bb26183 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/hdminvd0.c @@ -0,0 +1,62 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include "nv50.h" + +int +nvd0_hdmi_ctrl(struct nv50_disp_priv *priv, int head, int or, u32 data) +{ + const u32 hoff = (head * 0x800); + + if (!(data & NV84_DISP_SOR_HDMI_PWR_STATE_ON)) { + nv_mask(priv, 0x616798 + hoff, 0x40000000, 0x00000000); + nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000000); + nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000000); + return 0; + } + + /* AVI InfoFrame */ + nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x61671c + hoff, 0x000d0282); + nv_wr32(priv, 0x616720 + hoff, 0x0000006f); + nv_wr32(priv, 0x616724 + hoff, 0x00000000); + nv_wr32(priv, 0x616728 + hoff, 0x00000000); + nv_wr32(priv, 0x61672c + hoff, 0x00000000); + nv_mask(priv, 0x616714 + hoff, 0x00000001, 0x00000001); + + /* ??? InfoFrame? */ + nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000000); + nv_wr32(priv, 0x6167ac + hoff, 0x00000010); + nv_mask(priv, 0x6167a4 + hoff, 0x00000001, 0x00000001); + + /* HDMI_CTRL */ + nv_mask(priv, 0x616798 + hoff, 0x401f007f, data); + + /* NFI, audio doesn't work without it though.. */ + nv_mask(priv, 0x616548 + hoff, 0x00000070, 0x00000000); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 16a9afb1060..c6f80055e98 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -22,18 +22,740 @@ * Authors: Ben Skeggs */ +#include <core/object.h> +#include <core/parent.h> +#include <core/handle.h> +#include <core/class.h> + #include <engine/software.h> #include <engine/disp.h> -struct nv50_disp_priv { - struct nouveau_disp base; +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/disp.h> +#include <subdev/bios/init.h> +#include <subdev/bios/pll.h> +#include <subdev/timer.h> +#include <subdev/fb.h> +#include <subdev/bar.h> +#include <subdev/clock.h> + +#include "nv50.h" + +/******************************************************************************* + * EVO channel base class + ******************************************************************************/ + +int +nv50_disp_chan_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int chid, + int length, void **pobject) +{ + struct nv50_disp_base *base = (void *)parent; + struct nv50_disp_chan *chan; + int ret; + + if (base->chan & (1 << chid)) + return -EBUSY; + base->chan |= (1 << chid); + + ret = nouveau_namedb_create_(parent, engine, oclass, 0, NULL, + (1ULL << NVDEV_ENGINE_DMAOBJ), + length, pobject); + chan = *pobject; + if (ret) + return ret; + + chan->chid = chid; + return 0; +} + +void +nv50_disp_chan_destroy(struct nv50_disp_chan *chan) +{ + struct nv50_disp_base *base = (void *)nv_object(chan)->parent; + base->chan &= ~(1 << chan->chid); + nouveau_namedb_destroy(&chan->base); +} + +u32 +nv50_disp_chan_rd32(struct nouveau_object *object, u64 addr) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_chan *chan = (void *)object; + return nv_rd32(priv, 0x640000 + (chan->chid * 0x1000) + addr); +} + +void +nv50_disp_chan_wr32(struct nouveau_object *object, u64 addr, u32 data) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_chan *chan = (void *)object; + nv_wr32(priv, 0x640000 + (chan->chid * 0x1000) + addr, data); +} + +/******************************************************************************* + * EVO DMA channel base class + ******************************************************************************/ + +static int +nv50_disp_dmac_object_attach(struct nouveau_object *parent, + struct nouveau_object *object, u32 name) +{ + struct nv50_disp_base *base = (void *)parent->parent; + struct nv50_disp_chan *chan = (void *)parent; + u32 addr = nv_gpuobj(object)->node->offset; + u32 chid = chan->chid; + u32 data = (chid << 28) | (addr << 10) | chid; + return nouveau_ramht_insert(base->ramht, chid, name, data); +} + +static void +nv50_disp_dmac_object_detach(struct nouveau_object *parent, int cookie) +{ + struct nv50_disp_base *base = (void *)parent->parent; + nouveau_ramht_remove(base->ramht, cookie); +} + +int +nv50_disp_dmac_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, u32 pushbuf, int chid, + int length, void **pobject) +{ + struct nv50_disp_dmac *dmac; + int ret; + + ret = nv50_disp_chan_create_(parent, engine, oclass, chid, + length, pobject); + dmac = *pobject; + if (ret) + return ret; + + dmac->pushdma = (void *)nouveau_handle_ref(parent, pushbuf); + if (!dmac->pushdma) + return -ENOENT; + + switch (nv_mclass(dmac->pushdma)) { + case 0x0002: + case 0x003d: + if (dmac->pushdma->limit - dmac->pushdma->start != 0xfff) + return -EINVAL; + + switch (dmac->pushdma->target) { + case NV_MEM_TARGET_VRAM: + dmac->push = 0x00000000 | dmac->pushdma->start >> 8; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + dmac->push = 0x00000003 | dmac->pushdma->start >> 8; + break; + default: + return -EINVAL; + } + break; + default: + return -EINVAL; + } + + return 0; +} + +void +nv50_disp_dmac_dtor(struct nouveau_object *object) +{ + struct nv50_disp_dmac *dmac = (void *)object; + nouveau_object_ref(NULL, (struct nouveau_object **)&dmac->pushdma); + nv50_disp_chan_destroy(&dmac->base); +} + +static int +nv50_disp_dmac_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + int ret; + + ret = nv50_disp_chan_init(&dmac->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00010001 << chid); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610204 + (chid * 0x0010), dmac->push); + nv_wr32(priv, 0x610208 + (chid * 0x0010), 0x00010000); + nv_wr32(priv, 0x61020c + (chid * 0x0010), chid); + nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); + nv_wr32(priv, 0x610200 + (chid * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x80000000, 0x00000000)) { + nv_error(dmac, "init timeout, 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +nv50_disp_dmac_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + + /* deactivate channel */ + nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00001010, 0x00001000); + nv_mask(priv, 0x610200 + (chid * 0x0010), 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x001e0000, 0x00000000)) { + nv_error(dmac, "fini timeout, 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + /* disable error reporting */ + nv_mask(priv, 0x610028, 0x00010001 << chid, 0x00000000 << chid); + + return nv50_disp_chan_fini(&dmac->base, suspend); +} + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +static int +nv50_disp_mast_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_mast_class *args = data; + struct nv50_disp_dmac *mast; + int ret; + + if (size < sizeof(*args)) + return -EINVAL; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, + 0, sizeof(*mast), (void **)&mast); + *pobject = nv_object(mast); + if (ret) + return ret; + + nv_parent(mast)->object_attach = nv50_disp_dmac_object_attach; + nv_parent(mast)->object_detach = nv50_disp_dmac_object_detach; + return 0; +} + +static int +nv50_disp_mast_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + int ret; + + ret = nv50_disp_chan_init(&mast->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610028, 0x00010001, 0x00010001); + + /* attempt to unstick channel from some unknown state */ + if ((nv_rd32(priv, 0x610200) & 0x009f0000) == 0x00020000) + nv_mask(priv, 0x610200, 0x00800000, 0x00800000); + if ((nv_rd32(priv, 0x610200) & 0x003f0000) == 0x00030000) + nv_mask(priv, 0x610200, 0x00600000, 0x00600000); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610204, mast->push); + nv_wr32(priv, 0x610208, 0x00010000); + nv_wr32(priv, 0x61020c, 0x00000000); + nv_mask(priv, 0x610200, 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000, 0x00000000); + nv_wr32(priv, 0x610200, 0x01000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610200, 0x80000000, 0x00000000)) { + nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610200)); + return -EBUSY; + } + + return 0; +} + +static int +nv50_disp_mast_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + + /* deactivate channel */ + nv_mask(priv, 0x610200, 0x00000010, 0x00000000); + nv_mask(priv, 0x610200, 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610200, 0x001e0000, 0x00000000)) { + nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610200)); + if (suspend) + return -EBUSY; + } + + /* disable error reporting */ + nv_mask(priv, 0x610028, 0x00010001, 0x00000000); + + return nv50_disp_chan_fini(&mast->base, suspend); +} + +struct nouveau_ofuncs +nv50_disp_mast_ofuncs = { + .ctor = nv50_disp_mast_ctor, + .dtor = nv50_disp_dmac_dtor, + .init = nv50_disp_mast_init, + .fini = nv50_disp_mast_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO sync channel objects + ******************************************************************************/ + +static int +nv50_disp_sync_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_sync_class *args = data; + struct nv50_disp_dmac *dmac; + int ret; + + if (size < sizeof(*data) || args->head > 1) + return -EINVAL; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, + 1 + args->head, sizeof(*dmac), + (void **)&dmac); + *pobject = nv_object(dmac); + if (ret) + return ret; + + nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach; + nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach; + return 0; +} + +struct nouveau_ofuncs +nv50_disp_sync_ofuncs = { + .ctor = nv50_disp_sync_ctor, + .dtor = nv50_disp_dmac_dtor, + .init = nv50_disp_dmac_init, + .fini = nv50_disp_dmac_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +static int +nv50_disp_ovly_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_ovly_class *args = data; + struct nv50_disp_dmac *dmac; + int ret; + + if (size < sizeof(*data) || args->head > 1) + return -EINVAL; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, + 3 + args->head, sizeof(*dmac), + (void **)&dmac); + *pobject = nv_object(dmac); + if (ret) + return ret; + + nv_parent(dmac)->object_attach = nv50_disp_dmac_object_attach; + nv_parent(dmac)->object_detach = nv50_disp_dmac_object_detach; + return 0; +} + +struct nouveau_ofuncs +nv50_disp_ovly_ofuncs = { + .ctor = nv50_disp_ovly_ctor, + .dtor = nv50_disp_dmac_dtor, + .init = nv50_disp_dmac_init, + .fini = nv50_disp_dmac_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO PIO channel base class + ******************************************************************************/ + +static int +nv50_disp_pioc_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int chid, + int length, void **pobject) +{ + return nv50_disp_chan_create_(parent, engine, oclass, chid, + length, pobject); +} + +static void +nv50_disp_pioc_dtor(struct nouveau_object *object) +{ + struct nv50_disp_pioc *pioc = (void *)object; + nv50_disp_chan_destroy(&pioc->base); +} + +static int +nv50_disp_pioc_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + int ret; + + ret = nv50_disp_chan_init(&pioc->base); + if (ret) + return ret; + + nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00002000); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00000000, 0x00000000)) { + nv_error(pioc, "timeout0: 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + return -EBUSY; + } + + nv_wr32(priv, 0x610200 + (chid * 0x10), 0x00000001); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00010000)) { + nv_error(pioc, "timeout1: 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +nv50_disp_pioc_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + + nv_mask(priv, 0x610200 + (chid * 0x10), 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x610200 + (chid * 0x10), 0x00030000, 0x00000000)) { + nv_error(pioc, "timeout: 0x%08x\n", + nv_rd32(priv, 0x610200 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + return nv50_disp_chan_fini(&pioc->base, suspend); +} + +/******************************************************************************* + * EVO immediate overlay channel objects + ******************************************************************************/ + +static int +nv50_disp_oimm_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_oimm_class *args = data; + struct nv50_disp_pioc *pioc; + int ret; + + if (size < sizeof(*args) || args->head > 1) + return -EINVAL; + + ret = nv50_disp_pioc_create_(parent, engine, oclass, 5 + args->head, + sizeof(*pioc), (void **)&pioc); + *pobject = nv_object(pioc); + if (ret) + return ret; + + return 0; +} + +struct nouveau_ofuncs +nv50_disp_oimm_ofuncs = { + .ctor = nv50_disp_oimm_ctor, + .dtor = nv50_disp_pioc_dtor, + .init = nv50_disp_pioc_init, + .fini = nv50_disp_pioc_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO cursor channel objects + ******************************************************************************/ + +static int +nv50_disp_curs_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_curs_class *args = data; + struct nv50_disp_pioc *pioc; + int ret; + + if (size < sizeof(*args) || args->head > 1) + return -EINVAL; + + ret = nv50_disp_pioc_create_(parent, engine, oclass, 7 + args->head, + sizeof(*pioc), (void **)&pioc); + *pobject = nv_object(pioc); + if (ret) + return ret; + + return 0; +} + +struct nouveau_ofuncs +nv50_disp_curs_ofuncs = { + .ctor = nv50_disp_curs_ctor, + .dtor = nv50_disp_pioc_dtor, + .init = nv50_disp_pioc_init, + .fini = nv50_disp_pioc_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static int +nv50_disp_base_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_base *base; + int ret; + + ret = nouveau_parent_create(parent, engine, oclass, 0, + priv->sclass, 0, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht); +} + +static void +nv50_disp_base_dtor(struct nouveau_object *object) +{ + struct nv50_disp_base *base = (void *)object; + nouveau_ramht_ref(NULL, &base->ramht); + nouveau_parent_destroy(&base->base); +} + +static int +nv50_disp_base_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + int ret, i; + u32 tmp; + + ret = nouveau_parent_init(&base->base); + if (ret) + return ret; + + /* The below segments of code copying values from one register to + * another appear to inform EVO of the display capabilities or + * something similar. NFI what the 0x614004 caps are for.. + */ + tmp = nv_rd32(priv, 0x614004); + nv_wr32(priv, 0x610184, tmp); + + /* ... CRTC caps */ + for (i = 0; i < priv->head.nr; i++) { + tmp = nv_rd32(priv, 0x616100 + (i * 0x800)); + nv_wr32(priv, 0x610190 + (i * 0x10), tmp); + tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); + nv_wr32(priv, 0x610194 + (i * 0x10), tmp); + tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); + nv_wr32(priv, 0x610198 + (i * 0x10), tmp); + tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); + nv_wr32(priv, 0x61019c + (i * 0x10), tmp); + } + + /* ... DAC caps */ + for (i = 0; i < priv->dac.nr; i++) { + tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); + nv_wr32(priv, 0x6101d0 + (i * 0x04), tmp); + } + + /* ... SOR caps */ + for (i = 0; i < priv->sor.nr; i++) { + tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); + nv_wr32(priv, 0x6101e0 + (i * 0x04), tmp); + } + + /* ... EXT caps */ + for (i = 0; i < 3; i++) { + tmp = nv_rd32(priv, 0x61e000 + (i * 0x800)); + nv_wr32(priv, 0x6101f0 + (i * 0x04), tmp); + } + + /* steal display away from vbios, or something like that */ + if (nv_rd32(priv, 0x610024) & 0x00000100) { + nv_wr32(priv, 0x610024, 0x00000100); + nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { + nv_error(priv, "timeout acquiring display\n"); + return -EBUSY; + } + } + + /* point at display engine memory area (hash table, objects) */ + nv_wr32(priv, 0x610010, (nv_gpuobj(base->ramht)->addr >> 8) | 9); + + /* enable supervisor interrupts, disable everything else */ + nv_wr32(priv, 0x61002c, 0x00000370); + nv_wr32(priv, 0x610028, 0x00000000); + return 0; +} + +static int +nv50_disp_base_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + + /* disable all interrupts */ + nv_wr32(priv, 0x610024, 0x00000000); + nv_wr32(priv, 0x610020, 0x00000000); + + return nouveau_parent_fini(&base->base, suspend); +} + +struct nouveau_ofuncs +nv50_disp_base_ofuncs = { + .ctor = nv50_disp_base_ctor, + .dtor = nv50_disp_base_dtor, + .init = nv50_disp_base_init, + .fini = nv50_disp_base_fini, +}; + +static struct nouveau_omthds +nv50_disp_base_omthds[] = { + { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, + { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + {}, +}; + +static struct nouveau_oclass +nv50_disp_base_oclass[] = { + { NV50_DISP_CLASS, &nv50_disp_base_ofuncs, nv50_disp_base_omthds }, + {} }; static struct nouveau_oclass nv50_disp_sclass[] = { - {}, + { NV50_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs }, + { NV50_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs }, + { NV50_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs }, + { NV50_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs }, + { NV50_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs }, + {} +}; + +/******************************************************************************* + * Display context, tracks instmem allocation and prevents more than one + * client using the display hardware at any time. + ******************************************************************************/ + +static int +nv50_disp_data_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv = (void *)engine; + struct nouveau_engctx *ectx; + int ret = -EBUSY; + + /* no context needed for channel objects... */ + if (nv_mclass(parent) != NV_DEVICE_CLASS) { + atomic_inc(&parent->refcount); + *pobject = parent; + return 0; + } + + /* allocate display hardware to client */ + mutex_lock(&nv_subdev(priv)->mutex); + if (list_empty(&nv_engine(priv)->contexts)) { + ret = nouveau_engctx_create(parent, engine, oclass, NULL, + 0x10000, 0x10000, + NVOBJ_FLAG_HEAP, &ectx); + *pobject = nv_object(ectx); + } + mutex_unlock(&nv_subdev(priv)->mutex); + return ret; +} + +struct nouveau_oclass +nv50_disp_cclass = { + .handle = NV_ENGCTX(DISP, 0x50), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_disp_data_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, + }, }; +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static void +nv50_disp_intr_error(struct nv50_disp_priv *priv) +{ + u32 channels = (nv_rd32(priv, 0x610020) & 0x001f0000) >> 16; + u32 addr, data; + int chid; + + for (chid = 0; chid < 5; chid++) { + if (!(channels & (1 << chid))) + continue; + + nv_wr32(priv, 0x610020, 0x00010000 << chid); + addr = nv_rd32(priv, 0x610080 + (chid * 0x08)); + data = nv_rd32(priv, 0x610084 + (chid * 0x08)); + nv_wr32(priv, 0x610080 + (chid * 0x08), 0x90000000); + + nv_error(priv, "chid %d mthd 0x%04x data 0x%08x 0x%08x\n", + chid, addr & 0xffc, data, addr); + } +} + static void nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc) { @@ -71,30 +793,422 @@ nv50_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc) disp->vblank.notify(disp->vblank.data, crtc); } +static u16 +exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, + struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *info) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + u16 mask, type, data; + + if (outp < 4) { + type = DCB_OUTPUT_ANALOG; + mask = 0; + } else { + outp -= 4; + switch (ctrl & 0x00000f00) { + case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; + case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; + case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; + case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; + case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; + case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; + default: + nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); + return 0x0000; + } + } + + mask = 0x00c0 & (mask << 6); + mask |= 0x0001 << outp; + mask |= 0x0100 << head; + + data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); + if (!data) + return 0x0000; + + return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); +} + +static bool +exec_script(struct nv50_disp_priv *priv, int head, int id) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_outp info; + struct dcb_output dcb; + u8 ver, hdr, cnt, len; + u16 data; + u32 ctrl = 0x00000000; + int i; + + for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) + ctrl = nv_rd32(priv, 0x610b5c + (i * 8)); + + if (nv_device(priv)->chipset < 0x90 || + nv_device(priv)->chipset == 0x92 || + nv_device(priv)->chipset == 0xa0) { + for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) + ctrl = nv_rd32(priv, 0x610b74 + (i * 8)); + i += 3; + } else { + for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) + ctrl = nv_rd32(priv, 0x610798 + (i * 8)); + i += 3; + } + + if (!(ctrl & (1 << head))) + return false; + + data = exec_lookup(priv, head, i, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); + if (data) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[id], + .outp = &dcb, + .crtc = head, + .execute = 1, + }; + + return nvbios_exec(&init) == 0; + } + + return false; +} + +static u32 +exec_clkcmp(struct nv50_disp_priv *priv, int head, int id, u32 pclk, + struct dcb_output *outp) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_outp info1; + struct nvbios_ocfg info2; + u8 ver, hdr, cnt, len; + u16 data, conf; + u32 ctrl = 0x00000000; + int i; + + for (i = 0; !(ctrl & (1 << head)) && i < 3; i++) + ctrl = nv_rd32(priv, 0x610b58 + (i * 8)); + + if (nv_device(priv)->chipset < 0x90 || + nv_device(priv)->chipset == 0x92 || + nv_device(priv)->chipset == 0xa0) { + for (i = 0; !(ctrl & (1 << head)) && i < 2; i++) + ctrl = nv_rd32(priv, 0x610b70 + (i * 8)); + i += 3; + } else { + for (i = 0; !(ctrl & (1 << head)) && i < 4; i++) + ctrl = nv_rd32(priv, 0x610794 + (i * 8)); + i += 3; + } + + if (!(ctrl & (1 << head))) + return 0x0000; + + data = exec_lookup(priv, head, i, ctrl, outp, &ver, &hdr, &cnt, &len, &info1); + if (!data) + return 0x0000; + + switch (outp->type) { + case DCB_OUTPUT_TMDS: + conf = (ctrl & 0x00000f00) >> 8; + if (pclk >= 165000) + conf |= 0x0100; + break; + case DCB_OUTPUT_LVDS: + conf = priv->sor.lvdsconf; + break; + case DCB_OUTPUT_DP: + conf = (ctrl & 0x00000f00) >> 8; + break; + case DCB_OUTPUT_ANALOG: + default: + conf = 0x00ff; + break; + } + + data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); + if (data) { + data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); + if (data) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = data, + .outp = outp, + .crtc = head, + .execute = 1, + }; + + if (nvbios_exec(&init)) + return 0x0000; + return conf; + } + } + + return 0x0000; +} + +static void +nv50_disp_intr_unk10(struct nv50_disp_priv *priv, u32 super) +{ + int head = ffs((super & 0x00000060) >> 5) - 1; + if (head >= 0) { + head = ffs((super & 0x00000180) >> 7) - 1; + if (head >= 0) + exec_script(priv, head, 1); + } + + nv_wr32(priv, 0x610024, 0x00000010); + nv_wr32(priv, 0x610030, 0x80000000); +} + +static void +nv50_disp_intr_unk20_dp(struct nv50_disp_priv *priv, + struct dcb_output *outp, u32 pclk) +{ + const int link = !(outp->sorconf.link & 1); + const int or = ffs(outp->or) - 1; + const u32 soff = ( or * 0x800); + const u32 loff = (link * 0x080) + soff; + const u32 ctrl = nv_rd32(priv, 0x610794 + (or * 8)); + const u32 symbol = 100000; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x0000f0000; + u32 clksor = nv_rd32(priv, 0x614300 + soff); + int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; + int TU, VTUi, VTUf, VTUa; + u64 link_data_rate, link_ratio, unk; + u32 best_diff = 64 * symbol; + u32 link_nr, link_bw, bits, r; + + /* calculate packed data rate for each lane */ + if (dpctrl > 0x00030000) link_nr = 4; + else if (dpctrl > 0x00010000) link_nr = 2; + else link_nr = 1; + + if (clksor & 0x000c0000) + link_bw = 270000; + else + link_bw = 162000; + + if ((ctrl & 0xf0000) == 0x60000) bits = 30; + else if ((ctrl & 0xf0000) == 0x50000) bits = 24; + else bits = 18; + + link_data_rate = (pclk * bits / 8) / link_nr; + + /* calculate ratio of packed data rate to link symbol rate */ + link_ratio = link_data_rate * symbol; + r = do_div(link_ratio, link_bw); + + for (TU = 64; TU >= 32; TU--) { + /* calculate average number of valid symbols in each TU */ + u32 tu_valid = link_ratio * TU; + u32 calc, diff; + + /* find a hw representation for the fraction.. */ + VTUi = tu_valid / symbol; + calc = VTUi * symbol; + diff = tu_valid - calc; + if (diff) { + if (diff >= (symbol / 2)) { + VTUf = symbol / (symbol - diff); + if (symbol - (VTUf * diff)) + VTUf++; + + if (VTUf <= 15) { + VTUa = 1; + calc += symbol - (symbol / VTUf); + } else { + VTUa = 0; + VTUf = 1; + calc += symbol; + } + } else { + VTUa = 0; + VTUf = min((int)(symbol / diff), 15); + calc += symbol / VTUf; + } + + diff = calc - tu_valid; + } else { + /* no remainder, but the hw doesn't like the fractional + * part to be zero. decrement the integer part and + * have the fraction add a whole symbol back + */ + VTUa = 0; + VTUf = 1; + VTUi--; + } + + if (diff < best_diff) { + best_diff = diff; + bestTU = TU; + bestVTUa = VTUa; + bestVTUf = VTUf; + bestVTUi = VTUi; + if (diff == 0) + break; + } + } + + if (!bestTU) { + nv_error(priv, "unable to find suitable dp config\n"); + return; + } + + /* XXX close to vbios numbers, but not right */ + unk = (symbol - link_ratio) * bestTU; + unk *= link_ratio; + r = do_div(unk, symbol); + r = do_div(unk, symbol); + unk += 6; + + nv_mask(priv, 0x61c10c + loff, 0x000001fc, bestTU << 2); + nv_mask(priv, 0x61c128 + loff, 0x010f7f3f, bestVTUa << 24 | + bestVTUf << 16 | + bestVTUi << 8 | unk); +} + +static void +nv50_disp_intr_unk20(struct nv50_disp_priv *priv, u32 super) +{ + struct dcb_output outp; + u32 addr, mask, data; + int head; + + /* finish detaching encoder? */ + head = ffs((super & 0x00000180) >> 7) - 1; + if (head >= 0) + exec_script(priv, head, 2); + + /* check whether a vpll change is required */ + head = ffs((super & 0x00000600) >> 9) - 1; + if (head >= 0) { + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + if (pclk) { + struct nouveau_clock *clk = nouveau_clock(priv); + clk->pll_set(clk, PLL_VPLL0 + head, pclk); + } + + nv_mask(priv, 0x614200 + head * 0x800, 0x0000000f, 0x00000000); + } + + /* (re)attach the relevant OR to the head */ + head = ffs((super & 0x00000180) >> 7) - 1; + if (head >= 0) { + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + u32 conf = exec_clkcmp(priv, head, 0, pclk, &outp); + if (conf) { + if (outp.type == DCB_OUTPUT_ANALOG) { + addr = 0x614280 + (ffs(outp.or) - 1) * 0x800; + mask = 0xffffffff; + data = 0x00000000; + } else { + if (outp.type == DCB_OUTPUT_DP) + nv50_disp_intr_unk20_dp(priv, &outp, pclk); + addr = 0x614300 + (ffs(outp.or) - 1) * 0x800; + mask = 0x00000707; + data = (conf & 0x0100) ? 0x0101 : 0x0000; + } + + nv_mask(priv, addr, mask, data); + } + } + + nv_wr32(priv, 0x610024, 0x00000020); + nv_wr32(priv, 0x610030, 0x80000000); +} + +/* If programming a TMDS output on a SOR that can also be configured for + * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. + * + * It looks like the VBIOS TMDS scripts make an attempt at this, however, + * the VBIOS scripts on at least one board I have only switch it off on + * link 0, causing a blank display if the output has previously been + * programmed for DisplayPort. + */ +static void +nv50_disp_intr_unk40_tmds(struct nv50_disp_priv *priv, struct dcb_output *outp) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + const int link = !(outp->sorconf.link & 1); + const int or = ffs(outp->or) - 1; + const u32 loff = (or * 0x800) + (link * 0x80); + const u16 mask = (outp->sorconf.link << 6) | outp->or; + u8 ver, hdr; + + if (dcb_outp_match(bios, DCB_OUTPUT_DP, mask, &ver, &hdr, outp)) + nv_mask(priv, 0x61c10c + loff, 0x00000001, 0x00000000); +} + static void +nv50_disp_intr_unk40(struct nv50_disp_priv *priv, u32 super) +{ + int head = ffs((super & 0x00000180) >> 7) - 1; + if (head >= 0) { + struct dcb_output outp; + u32 pclk = nv_rd32(priv, 0x610ad0 + (head * 0x540)) & 0x3fffff; + if (pclk && exec_clkcmp(priv, head, 1, pclk, &outp)) { + if (outp.type == DCB_OUTPUT_TMDS) + nv50_disp_intr_unk40_tmds(priv, &outp); + } + } + + nv_wr32(priv, 0x610024, 0x00000040); + nv_wr32(priv, 0x610030, 0x80000000); +} + +static void +nv50_disp_intr_super(struct nv50_disp_priv *priv, u32 intr1) +{ + u32 super = nv_rd32(priv, 0x610030); + + nv_debug(priv, "supervisor 0x%08x 0x%08x\n", intr1, super); + + if (intr1 & 0x00000010) + nv50_disp_intr_unk10(priv, super); + if (intr1 & 0x00000020) + nv50_disp_intr_unk20(priv, super); + if (intr1 & 0x00000040) + nv50_disp_intr_unk40(priv, super); +} + +void nv50_disp_intr(struct nouveau_subdev *subdev) { struct nv50_disp_priv *priv = (void *)subdev; - u32 stat1 = nv_rd32(priv, 0x610024); + u32 intr0 = nv_rd32(priv, 0x610020); + u32 intr1 = nv_rd32(priv, 0x610024); - if (stat1 & 0x00000004) { + if (intr0 & 0x001f0000) { + nv50_disp_intr_error(priv); + intr0 &= ~0x001f0000; + } + + if (intr1 & 0x00000004) { nv50_disp_intr_vblank(priv, 0); nv_wr32(priv, 0x610024, 0x00000004); - stat1 &= ~0x00000004; + intr1 &= ~0x00000004; } - if (stat1 & 0x00000008) { + if (intr1 & 0x00000008) { nv50_disp_intr_vblank(priv, 1); nv_wr32(priv, 0x610024, 0x00000008); - stat1 &= ~0x00000008; + intr1 &= ~0x00000008; } + if (intr1 & 0x00000070) { + nv50_disp_intr_super(priv, intr1); + intr1 &= ~0x00000070; + } } static int nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { struct nv50_disp_priv *priv; int ret; @@ -105,8 +1219,16 @@ nv50_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - nv_engine(priv)->sclass = nv50_disp_sclass; + nv_engine(priv)->sclass = nv50_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nv50_disp_intr; + priv->sclass = nv50_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 2; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; INIT_LIST_HEAD(&priv->base.vblank.list); spin_lock_init(&priv->base.vblank.lock); diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h new file mode 100644 index 00000000000..a6bb931450f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.h @@ -0,0 +1,142 @@ +#ifndef __NV50_DISP_H__ +#define __NV50_DISP_H__ + +#include <core/parent.h> +#include <core/namedb.h> +#include <core/ramht.h> + +#include <engine/dmaobj.h> +#include <engine/disp.h> + +struct dcb_output; + +struct nv50_disp_priv { + struct nouveau_disp base; + struct nouveau_oclass *sclass; + struct { + int nr; + } head; + struct { + int nr; + int (*power)(struct nv50_disp_priv *, int dac, u32 data); + int (*sense)(struct nv50_disp_priv *, int dac, u32 load); + } dac; + struct { + int nr; + int (*power)(struct nv50_disp_priv *, int sor, u32 data); + int (*hda_eld)(struct nv50_disp_priv *, int sor, u8 *, u32); + int (*hdmi)(struct nv50_disp_priv *, int head, int sor, u32); + int (*dp_train_init)(struct nv50_disp_priv *, int sor, int link, + int head, u16 type, u16 mask, u32 data, + struct dcb_output *); + int (*dp_train_fini)(struct nv50_disp_priv *, int sor, int link, + int head, u16 type, u16 mask, u32 data, + struct dcb_output *); + int (*dp_train)(struct nv50_disp_priv *, int sor, int link, + u16 type, u16 mask, u32 data, + struct dcb_output *); + int (*dp_lnkctl)(struct nv50_disp_priv *, int sor, int link, + int head, u16 type, u16 mask, u32 data, + struct dcb_output *); + int (*dp_drvctl)(struct nv50_disp_priv *, int sor, int link, + int lane, u16 type, u16 mask, u32 data, + struct dcb_output *); + u32 lvdsconf; + } sor; +}; + +#define DAC_MTHD(n) (n), (n) + 0x03 + +int nv50_dac_mthd(struct nouveau_object *, u32, void *, u32); +int nv50_dac_power(struct nv50_disp_priv *, int, u32); +int nv50_dac_sense(struct nv50_disp_priv *, int, u32); + +#define SOR_MTHD(n) (n), (n) + 0x3f + +int nva3_hda_eld(struct nv50_disp_priv *, int, u8 *, u32); +int nvd0_hda_eld(struct nv50_disp_priv *, int, u8 *, u32); + +int nv84_hdmi_ctrl(struct nv50_disp_priv *, int, int, u32); +int nva3_hdmi_ctrl(struct nv50_disp_priv *, int, int, u32); +int nvd0_hdmi_ctrl(struct nv50_disp_priv *, int, int, u32); + +int nv50_sor_mthd(struct nouveau_object *, u32, void *, u32); +int nv50_sor_power(struct nv50_disp_priv *, int, u32); + +int nv94_sor_dp_train_init(struct nv50_disp_priv *, int, int, int, u16, u16, + u32, struct dcb_output *); +int nv94_sor_dp_train_fini(struct nv50_disp_priv *, int, int, int, u16, u16, + u32, struct dcb_output *); +int nv94_sor_dp_train(struct nv50_disp_priv *, int, int, u16, u16, u32, + struct dcb_output *); +int nv94_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32, + struct dcb_output *); +int nv94_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32, + struct dcb_output *); + +int nvd0_sor_dp_train(struct nv50_disp_priv *, int, int, u16, u16, u32, + struct dcb_output *); +int nvd0_sor_dp_lnkctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32, + struct dcb_output *); +int nvd0_sor_dp_drvctl(struct nv50_disp_priv *, int, int, int, u16, u16, u32, + struct dcb_output *); + +struct nv50_disp_base { + struct nouveau_parent base; + struct nouveau_ramht *ramht; + u32 chan; +}; + +struct nv50_disp_chan { + struct nouveau_namedb base; + int chid; +}; + +int nv50_disp_chan_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, int, int, void **); +void nv50_disp_chan_destroy(struct nv50_disp_chan *); +u32 nv50_disp_chan_rd32(struct nouveau_object *, u64); +void nv50_disp_chan_wr32(struct nouveau_object *, u64, u32); + +#define nv50_disp_chan_init(a) \ + nouveau_namedb_init(&(a)->base) +#define nv50_disp_chan_fini(a,b) \ + nouveau_namedb_fini(&(a)->base, (b)) + +int nv50_disp_dmac_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, u32, int, int, void **); +void nv50_disp_dmac_dtor(struct nouveau_object *); + +struct nv50_disp_dmac { + struct nv50_disp_chan base; + struct nouveau_dmaobj *pushdma; + u32 push; +}; + +struct nv50_disp_pioc { + struct nv50_disp_chan base; +}; + +extern struct nouveau_ofuncs nv50_disp_mast_ofuncs; +extern struct nouveau_ofuncs nv50_disp_sync_ofuncs; +extern struct nouveau_ofuncs nv50_disp_ovly_ofuncs; +extern struct nouveau_ofuncs nv50_disp_oimm_ofuncs; +extern struct nouveau_ofuncs nv50_disp_curs_ofuncs; +extern struct nouveau_ofuncs nv50_disp_base_ofuncs; +extern struct nouveau_oclass nv50_disp_cclass; +void nv50_disp_intr(struct nouveau_subdev *); + +extern struct nouveau_omthds nv84_disp_base_omthds[]; + +extern struct nouveau_omthds nva3_disp_base_omthds[]; + +extern struct nouveau_ofuncs nvd0_disp_mast_ofuncs; +extern struct nouveau_ofuncs nvd0_disp_sync_ofuncs; +extern struct nouveau_ofuncs nvd0_disp_ovly_ofuncs; +extern struct nouveau_ofuncs nvd0_disp_oimm_ofuncs; +extern struct nouveau_ofuncs nvd0_disp_curs_ofuncs; +extern struct nouveau_ofuncs nvd0_disp_base_ofuncs; +extern struct nouveau_oclass nvd0_disp_cclass; +void nvd0_disp_intr(struct nouveau_subdev *); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c new file mode 100644 index 00000000000..fc84eacdfbe --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv84.c @@ -0,0 +1,98 @@ +/* + * 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 <engine/software.h> +#include <engine/disp.h> + +#include <core/class.h> + +#include "nv50.h" + +static struct nouveau_oclass +nv84_disp_sclass[] = { + { NV84_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs }, + { NV84_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs }, + { NV84_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs }, + { NV84_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs }, + { NV84_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs }, + {} +}; + +struct nouveau_omthds +nv84_disp_base_omthds[] = { + { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, + { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + {}, +}; + +static struct nouveau_oclass +nv84_disp_base_oclass[] = { + { NV84_DISP_CLASS, &nv50_disp_base_ofuncs, nv84_disp_base_omthds }, + {} +}; + +static int +nv84_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nv84_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + priv->sclass = nv84_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 2; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hdmi = nv84_hdmi_ctrl; + + INIT_LIST_HEAD(&priv->base.vblank.list); + spin_lock_init(&priv->base.vblank.lock); + return 0; +} + +struct nouveau_oclass +nv84_disp_oclass = { + .handle = NV_ENGINE(DISP, 0x82), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv84_disp_ctor, + .dtor = _nouveau_disp_dtor, + .init = _nouveau_disp_init, + .fini = _nouveau_disp_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c new file mode 100644 index 00000000000..ba9dfd4669a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv94.c @@ -0,0 +1,109 @@ +/* + * 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 <engine/software.h> +#include <engine/disp.h> + +#include <core/class.h> + +#include "nv50.h" + +static struct nouveau_oclass +nv94_disp_sclass[] = { + { NV94_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs }, + { NV94_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs }, + { NV94_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs }, + { NV94_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs }, + { NV94_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs }, + {} +}; + +static struct nouveau_omthds +nv94_disp_base_omthds[] = { + { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd }, + { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, + { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + {}, +}; + +static struct nouveau_oclass +nv94_disp_base_oclass[] = { + { NV94_DISP_CLASS, &nv50_disp_base_ofuncs, nv94_disp_base_omthds }, + {} +}; + +static int +nv94_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nv94_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + priv->sclass = nv94_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hdmi = nv84_hdmi_ctrl; + priv->sor.dp_train = nv94_sor_dp_train; + priv->sor.dp_train_init = nv94_sor_dp_train_init; + priv->sor.dp_train_fini = nv94_sor_dp_train_fini; + priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl; + priv->sor.dp_drvctl = nv94_sor_dp_drvctl; + + INIT_LIST_HEAD(&priv->base.vblank.list); + spin_lock_init(&priv->base.vblank.lock); + return 0; +} + +struct nouveau_oclass +nv94_disp_oclass = { + .handle = NV_ENGINE(DISP, 0x88), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv94_disp_ctor, + .dtor = _nouveau_disp_dtor, + .init = _nouveau_disp_init, + .fini = _nouveau_disp_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c new file mode 100644 index 00000000000..5d63902cded --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva0.c @@ -0,0 +1,88 @@ +/* + * 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 <engine/software.h> +#include <engine/disp.h> + +#include <core/class.h> + +#include "nv50.h" + +static struct nouveau_oclass +nva0_disp_sclass[] = { + { NVA0_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs }, + { NVA0_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs }, + { NVA0_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs }, + { NVA0_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs }, + { NVA0_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs }, + {} +}; + +static struct nouveau_oclass +nva0_disp_base_oclass[] = { + { NVA0_DISP_CLASS, &nv50_disp_base_ofuncs, nv84_disp_base_omthds }, + {} +}; + +static int +nva0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nva0_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + priv->sclass = nva0_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 2; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hdmi = nv84_hdmi_ctrl; + + INIT_LIST_HEAD(&priv->base.vblank.list); + spin_lock_init(&priv->base.vblank.lock); + return 0; +} + +struct nouveau_oclass +nva0_disp_oclass = { + .handle = NV_ENGINE(DISP, 0x83), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nva0_disp_ctor, + .dtor = _nouveau_disp_dtor, + .init = _nouveau_disp_init, + .fini = _nouveau_disp_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c new file mode 100644 index 00000000000..e9192ca389f --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nva3.c @@ -0,0 +1,111 @@ +/* + * 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 <engine/software.h> +#include <engine/disp.h> + +#include <core/class.h> + +#include "nv50.h" + +static struct nouveau_oclass +nva3_disp_sclass[] = { + { NVA3_DISP_MAST_CLASS, &nv50_disp_mast_ofuncs }, + { NVA3_DISP_SYNC_CLASS, &nv50_disp_sync_ofuncs }, + { NVA3_DISP_OVLY_CLASS, &nv50_disp_ovly_ofuncs }, + { NVA3_DISP_OIMM_CLASS, &nv50_disp_oimm_ofuncs }, + { NVA3_DISP_CURS_CLASS, &nv50_disp_curs_ofuncs }, + {} +}; + +struct nouveau_omthds +nva3_disp_base_omthds[] = { + { SOR_MTHD(NV50_DISP_SOR_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NVA3_DISP_SOR_HDA_ELD) , nv50_sor_mthd }, + { SOR_MTHD(NV84_DISP_SOR_HDMI_PWR) , nv50_sor_mthd }, + { SOR_MTHD(NV50_DISP_SOR_LVDS_SCRIPT) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_TRAIN) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_LNKCTL) , nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(0)), nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(1)), nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(2)), nv50_sor_mthd }, + { SOR_MTHD(NV94_DISP_SOR_DP_DRVCTL(3)), nv50_sor_mthd }, + { DAC_MTHD(NV50_DISP_DAC_PWR) , nv50_dac_mthd }, + { DAC_MTHD(NV50_DISP_DAC_LOAD) , nv50_dac_mthd }, + {}, +}; + +static struct nouveau_oclass +nva3_disp_base_oclass[] = { + { NVA3_DISP_CLASS, &nv50_disp_base_ofuncs, nva3_disp_base_omthds }, + {} +}; + +static int +nva3_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nva3_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nv50_disp_intr; + priv->sclass = nva3_disp_sclass; + priv->head.nr = 2; + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = nva3_hda_eld; + priv->sor.hdmi = nva3_hdmi_ctrl; + priv->sor.dp_train = nv94_sor_dp_train; + priv->sor.dp_train_init = nv94_sor_dp_train_init; + priv->sor.dp_train_fini = nv94_sor_dp_train_fini; + priv->sor.dp_lnkctl = nv94_sor_dp_lnkctl; + priv->sor.dp_drvctl = nv94_sor_dp_drvctl; + + INIT_LIST_HEAD(&priv->base.vblank.list); + spin_lock_init(&priv->base.vblank.lock); + return 0; +} + +struct nouveau_oclass +nva3_disp_oclass = { + .handle = NV_ENGINE(DISP, 0x85), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nva3_disp_ctor, + .dtor = _nouveau_disp_dtor, + .init = _nouveau_disp_init, + .fini = _nouveau_disp_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c index d93efbcf75b..9e38ebff5fb 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nvd0.c @@ -22,22 +22,808 @@ * Authors: Ben Skeggs */ -#include <subdev/bar.h> +#include <core/object.h> +#include <core/parent.h> +#include <core/handle.h> +#include <core/class.h> #include <engine/software.h> #include <engine/disp.h> -struct nvd0_disp_priv { - struct nouveau_disp base; +#include <subdev/timer.h> +#include <subdev/fb.h> +#include <subdev/bar.h> +#include <subdev/clock.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/disp.h> +#include <subdev/bios/init.h> +#include <subdev/bios/pll.h> + +#include "nv50.h" + +/******************************************************************************* + * EVO DMA channel base class + ******************************************************************************/ + +static int +nvd0_disp_dmac_object_attach(struct nouveau_object *parent, + struct nouveau_object *object, u32 name) +{ + struct nv50_disp_base *base = (void *)parent->parent; + struct nv50_disp_chan *chan = (void *)parent; + u32 addr = nv_gpuobj(object)->node->offset; + u32 data = (chan->chid << 27) | (addr << 9) | 0x00000001; + return nouveau_ramht_insert(base->ramht, chan->chid, name, data); +} + +static void +nvd0_disp_dmac_object_detach(struct nouveau_object *parent, int cookie) +{ + struct nv50_disp_base *base = (void *)parent->parent; + nouveau_ramht_remove(base->ramht, cookie); +} + +static int +nvd0_disp_dmac_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + int ret; + + ret = nv50_disp_chan_init(&dmac->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610494 + (chid * 0x0010), dmac->push); + nv_wr32(priv, 0x610498 + (chid * 0x0010), 0x00010000); + nv_wr32(priv, 0x61049c + (chid * 0x0010), 0x00000001); + nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000 + (chid * 0x1000), 0x00000000); + nv_wr32(priv, 0x610490 + (chid * 0x0010), 0x00000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x80000000, 0x00000000)) { + nv_error(dmac, "init: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +nvd0_disp_dmac_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *dmac = (void *)object; + int chid = dmac->base.chid; + + /* deactivate channel */ + nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00001010, 0x00001000); + nv_mask(priv, 0x610490 + (chid * 0x0010), 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x001e0000, 0x00000000)) { + nv_error(dmac, "fini: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + /* disable error reporting */ + nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); + + return nv50_disp_chan_fini(&dmac->base, suspend); +} + +/******************************************************************************* + * EVO master channel object + ******************************************************************************/ + +static int +nvd0_disp_mast_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_mast_class *args = data; + struct nv50_disp_dmac *mast; + int ret; + + if (size < sizeof(*args)) + return -EINVAL; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, + 0, sizeof(*mast), (void **)&mast); + *pobject = nv_object(mast); + if (ret) + return ret; + + nv_parent(mast)->object_attach = nvd0_disp_dmac_object_attach; + nv_parent(mast)->object_detach = nvd0_disp_dmac_object_detach; + return 0; +} + +static int +nvd0_disp_mast_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + int ret; + + ret = nv50_disp_chan_init(&mast->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610090, 0x00000001, 0x00000001); + nv_mask(priv, 0x6100a0, 0x00000001, 0x00000001); + + /* initialise channel for dma command submission */ + nv_wr32(priv, 0x610494, mast->push); + nv_wr32(priv, 0x610498, 0x00010000); + nv_wr32(priv, 0x61049c, 0x00000001); + nv_mask(priv, 0x610490, 0x00000010, 0x00000010); + nv_wr32(priv, 0x640000, 0x00000000); + nv_wr32(priv, 0x610490, 0x01000013); + + /* wait for it to go inactive */ + if (!nv_wait(priv, 0x610490, 0x80000000, 0x00000000)) { + nv_error(mast, "init: 0x%08x\n", nv_rd32(priv, 0x610490)); + return -EBUSY; + } + + return 0; +} + +static int +nvd0_disp_mast_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_dmac *mast = (void *)object; + + /* deactivate channel */ + nv_mask(priv, 0x610490, 0x00000010, 0x00000000); + nv_mask(priv, 0x610490, 0x00000003, 0x00000000); + if (!nv_wait(priv, 0x610490, 0x001e0000, 0x00000000)) { + nv_error(mast, "fini: 0x%08x\n", nv_rd32(priv, 0x610490)); + if (suspend) + return -EBUSY; + } + + /* disable error reporting */ + nv_mask(priv, 0x610090, 0x00000001, 0x00000000); + nv_mask(priv, 0x6100a0, 0x00000001, 0x00000000); + + return nv50_disp_chan_fini(&mast->base, suspend); +} + +struct nouveau_ofuncs +nvd0_disp_mast_ofuncs = { + .ctor = nvd0_disp_mast_ctor, + .dtor = nv50_disp_dmac_dtor, + .init = nvd0_disp_mast_init, + .fini = nvd0_disp_mast_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO sync channel objects + ******************************************************************************/ + +static int +nvd0_disp_sync_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_sync_class *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_dmac *dmac; + int ret; + + if (size < sizeof(*data) || args->head >= priv->head.nr) + return -EINVAL; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, + 1 + args->head, sizeof(*dmac), + (void **)&dmac); + *pobject = nv_object(dmac); + if (ret) + return ret; + + nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach; + nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach; + return 0; +} + +struct nouveau_ofuncs +nvd0_disp_sync_ofuncs = { + .ctor = nvd0_disp_sync_ctor, + .dtor = nv50_disp_dmac_dtor, + .init = nvd0_disp_dmac_init, + .fini = nvd0_disp_dmac_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO overlay channel objects + ******************************************************************************/ + +static int +nvd0_disp_ovly_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_ovly_class *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_dmac *dmac; + int ret; + + if (size < sizeof(*data) || args->head >= priv->head.nr) + return -EINVAL; + + ret = nv50_disp_dmac_create_(parent, engine, oclass, args->pushbuf, + 5 + args->head, sizeof(*dmac), + (void **)&dmac); + *pobject = nv_object(dmac); + if (ret) + return ret; + + nv_parent(dmac)->object_attach = nvd0_disp_dmac_object_attach; + nv_parent(dmac)->object_detach = nvd0_disp_dmac_object_detach; + return 0; +} + +struct nouveau_ofuncs +nvd0_disp_ovly_ofuncs = { + .ctor = nvd0_disp_ovly_ctor, + .dtor = nv50_disp_dmac_dtor, + .init = nvd0_disp_dmac_init, + .fini = nvd0_disp_dmac_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO PIO channel base class + ******************************************************************************/ + +static int +nvd0_disp_pioc_create_(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, int chid, + int length, void **pobject) +{ + return nv50_disp_chan_create_(parent, engine, oclass, chid, + length, pobject); +} + +static void +nvd0_disp_pioc_dtor(struct nouveau_object *object) +{ + struct nv50_disp_pioc *pioc = (void *)object; + nv50_disp_chan_destroy(&pioc->base); +} + +static int +nvd0_disp_pioc_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + int ret; + + ret = nv50_disp_chan_init(&pioc->base); + if (ret) + return ret; + + /* enable error reporting */ + nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000001 << chid); + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000001 << chid); + + /* activate channel */ + nv_wr32(priv, 0x610490 + (chid * 0x10), 0x00000001); + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00010000)) { + nv_error(pioc, "init: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + return -EBUSY; + } + + return 0; +} + +static int +nvd0_disp_pioc_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_pioc *pioc = (void *)object; + int chid = pioc->base.chid; + + nv_mask(priv, 0x610490 + (chid * 0x10), 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x610490 + (chid * 0x10), 0x00030000, 0x00000000)) { + nv_error(pioc, "timeout: 0x%08x\n", + nv_rd32(priv, 0x610490 + (chid * 0x10))); + if (suspend) + return -EBUSY; + } + + /* disable error reporting */ + nv_mask(priv, 0x610090, 0x00000001 << chid, 0x00000000); + nv_mask(priv, 0x6100a0, 0x00000001 << chid, 0x00000000); + + return nv50_disp_chan_fini(&pioc->base, suspend); +} + +/******************************************************************************* + * EVO immediate overlay channel objects + ******************************************************************************/ + +static int +nvd0_disp_oimm_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_oimm_class *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_pioc *pioc; + int ret; + + if (size < sizeof(*args) || args->head >= priv->head.nr) + return -EINVAL; + + ret = nvd0_disp_pioc_create_(parent, engine, oclass, 9 + args->head, + sizeof(*pioc), (void **)&pioc); + *pobject = nv_object(pioc); + if (ret) + return ret; + + return 0; +} + +struct nouveau_ofuncs +nvd0_disp_oimm_ofuncs = { + .ctor = nvd0_disp_oimm_ctor, + .dtor = nvd0_disp_pioc_dtor, + .init = nvd0_disp_pioc_init, + .fini = nvd0_disp_pioc_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * EVO cursor channel objects + ******************************************************************************/ + +static int +nvd0_disp_curs_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_display_curs_class *args = data; + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_pioc *pioc; + int ret; + + if (size < sizeof(*args) || args->head >= priv->head.nr) + return -EINVAL; + + ret = nvd0_disp_pioc_create_(parent, engine, oclass, 13 + args->head, + sizeof(*pioc), (void **)&pioc); + *pobject = nv_object(pioc); + if (ret) + return ret; + + return 0; +} + +struct nouveau_ofuncs +nvd0_disp_curs_ofuncs = { + .ctor = nvd0_disp_curs_ctor, + .dtor = nvd0_disp_pioc_dtor, + .init = nvd0_disp_pioc_init, + .fini = nvd0_disp_pioc_fini, + .rd32 = nv50_disp_chan_rd32, + .wr32 = nv50_disp_chan_wr32, +}; + +/******************************************************************************* + * Base display object + ******************************************************************************/ + +static int +nvd0_disp_base_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv = (void *)engine; + struct nv50_disp_base *base; + int ret; + + ret = nouveau_parent_create(parent, engine, oclass, 0, + priv->sclass, 0, &base); + *pobject = nv_object(base); + if (ret) + return ret; + + return nouveau_ramht_new(parent, parent, 0x1000, 0, &base->ramht); +} + +static void +nvd0_disp_base_dtor(struct nouveau_object *object) +{ + struct nv50_disp_base *base = (void *)object; + nouveau_ramht_ref(NULL, &base->ramht); + nouveau_parent_destroy(&base->base); +} + +static int +nvd0_disp_base_init(struct nouveau_object *object) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + int ret, i; + u32 tmp; + + ret = nouveau_parent_init(&base->base); + if (ret) + return ret; + + /* The below segments of code copying values from one register to + * another appear to inform EVO of the display capabilities or + * something similar. + */ + + /* ... CRTC caps */ + for (i = 0; i < priv->head.nr; i++) { + tmp = nv_rd32(priv, 0x616104 + (i * 0x800)); + nv_wr32(priv, 0x6101b4 + (i * 0x800), tmp); + tmp = nv_rd32(priv, 0x616108 + (i * 0x800)); + nv_wr32(priv, 0x6101b8 + (i * 0x800), tmp); + tmp = nv_rd32(priv, 0x61610c + (i * 0x800)); + nv_wr32(priv, 0x6101bc + (i * 0x800), tmp); + } + + /* ... DAC caps */ + for (i = 0; i < priv->dac.nr; i++) { + tmp = nv_rd32(priv, 0x61a000 + (i * 0x800)); + nv_wr32(priv, 0x6101c0 + (i * 0x800), tmp); + } + + /* ... SOR caps */ + for (i = 0; i < priv->sor.nr; i++) { + tmp = nv_rd32(priv, 0x61c000 + (i * 0x800)); + nv_wr32(priv, 0x6301c4 + (i * 0x800), tmp); + } + + /* steal display away from vbios, or something like that */ + if (nv_rd32(priv, 0x6100ac) & 0x00000100) { + nv_wr32(priv, 0x6100ac, 0x00000100); + nv_mask(priv, 0x6194e8, 0x00000001, 0x00000000); + if (!nv_wait(priv, 0x6194e8, 0x00000002, 0x00000000)) { + nv_error(priv, "timeout acquiring display\n"); + return -EBUSY; + } + } + + /* point at display engine memory area (hash table, objects) */ + nv_wr32(priv, 0x610010, (nv_gpuobj(object->parent)->addr >> 8) | 9); + + /* enable supervisor interrupts, disable everything else */ + nv_wr32(priv, 0x610090, 0x00000000); + nv_wr32(priv, 0x6100a0, 0x00000000); + nv_wr32(priv, 0x6100b0, 0x00000307); + + return 0; +} + +static int +nvd0_disp_base_fini(struct nouveau_object *object, bool suspend) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nv50_disp_base *base = (void *)object; + + /* disable all interrupts */ + nv_wr32(priv, 0x6100b0, 0x00000000); + + return nouveau_parent_fini(&base->base, suspend); +} + +struct nouveau_ofuncs +nvd0_disp_base_ofuncs = { + .ctor = nvd0_disp_base_ctor, + .dtor = nvd0_disp_base_dtor, + .init = nvd0_disp_base_init, + .fini = nvd0_disp_base_fini, +}; + +static struct nouveau_oclass +nvd0_disp_base_oclass[] = { + { NVD0_DISP_CLASS, &nvd0_disp_base_ofuncs, nva3_disp_base_omthds }, + {} }; static struct nouveau_oclass nvd0_disp_sclass[] = { - {}, + { NVD0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs }, + { NVD0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs }, + { NVD0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs }, + { NVD0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs }, + { NVD0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs }, + {} }; +/******************************************************************************* + * Display engine implementation + ******************************************************************************/ + +static u16 +exec_lookup(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, + struct dcb_output *dcb, u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *info) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + u16 mask, type, data; + + if (outp < 4) { + type = DCB_OUTPUT_ANALOG; + mask = 0; + } else { + outp -= 4; + switch (ctrl & 0x00000f00) { + case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break; + case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break; + case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break; + case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break; + case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break; + case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break; + default: + nv_error(priv, "unknown SOR mc 0x%08x\n", ctrl); + return 0x0000; + } + dcb->sorconf.link = mask; + } + + mask = 0x00c0 & (mask << 6); + mask |= 0x0001 << outp; + mask |= 0x0100 << head; + + data = dcb_outp_match(bios, type, mask, ver, hdr, dcb); + if (!data) + return 0x0000; + + return nvbios_outp_match(bios, type, mask, ver, hdr, cnt, len, info); +} + +static bool +exec_script(struct nv50_disp_priv *priv, int head, int outp, u32 ctrl, int id) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_outp info; + struct dcb_output dcb; + u8 ver, hdr, cnt, len; + u16 data; + + data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info); + if (data) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[id], + .outp = &dcb, + .crtc = head, + .execute = 1, + }; + + return nvbios_exec(&init) == 0; + } + + return false; +} + +static u32 +exec_clkcmp(struct nv50_disp_priv *priv, int head, int outp, + u32 ctrl, int id, u32 pclk) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_outp info1; + struct nvbios_ocfg info2; + struct dcb_output dcb; + u8 ver, hdr, cnt, len; + u16 data, conf; + + data = exec_lookup(priv, head, outp, ctrl, &dcb, &ver, &hdr, &cnt, &len, &info1); + if (data == 0x0000) + return false; + + switch (dcb.type) { + case DCB_OUTPUT_TMDS: + conf = (ctrl & 0x00000f00) >> 8; + if (pclk >= 165000) + conf |= 0x0100; + break; + case DCB_OUTPUT_LVDS: + conf = priv->sor.lvdsconf; + break; + case DCB_OUTPUT_DP: + conf = (ctrl & 0x00000f00) >> 8; + break; + case DCB_OUTPUT_ANALOG: + default: + conf = 0x00ff; + break; + } + + data = nvbios_ocfg_match(bios, data, conf, &ver, &hdr, &cnt, &len, &info2); + if (data) { + data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk); + if (data) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = data, + .outp = &dcb, + .crtc = head, + .execute = 1, + }; + + if (nvbios_exec(&init)) + return 0x0000; + return conf; + } + } + + return 0x0000; +} + +static void +nvd0_display_unk1_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) +{ + int i; + + for (i = 0; mask && i < 8; i++) { + u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20)); + if (mcc & (1 << head)) + exec_script(priv, head, i, mcc, 1); + } + + nv_wr32(priv, 0x6101d4, 0x00000000); + nv_wr32(priv, 0x6109d4, 0x00000000); + nv_wr32(priv, 0x6101d0, 0x80000000); +} + static void -nvd0_disp_intr_vblank(struct nvd0_disp_priv *priv, int crtc) +nvd0_display_unk2_calc_tu(struct nv50_disp_priv *priv, int head, int or) +{ + const u32 ctrl = nv_rd32(priv, 0x660200 + (or * 0x020)); + const u32 conf = nv_rd32(priv, 0x660404 + (head * 0x300)); + const u32 pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + const u32 link = ((ctrl & 0xf00) == 0x800) ? 0 : 1; + const u32 hoff = (head * 0x800); + const u32 soff = ( or * 0x800); + const u32 loff = (link * 0x080) + soff; + const u32 symbol = 100000; + const u32 TU = 64; + u32 dpctrl = nv_rd32(priv, 0x61c10c + loff) & 0x000f0000; + u32 clksor = nv_rd32(priv, 0x612300 + soff); + u32 datarate, link_nr, link_bw, bits; + u64 ratio, value; + + if ((conf & 0x3c0) == 0x180) bits = 30; + else if ((conf & 0x3c0) == 0x140) bits = 24; + else bits = 18; + datarate = (pclk * bits) / 8; + + if (dpctrl > 0x00030000) link_nr = 4; + else if (dpctrl > 0x00010000) link_nr = 2; + else link_nr = 1; + + link_bw = (clksor & 0x007c0000) >> 18; + link_bw *= 27000; + + ratio = datarate; + ratio *= symbol; + do_div(ratio, link_nr * link_bw); + + value = (symbol - ratio) * TU; + value *= ratio; + do_div(value, symbol); + do_div(value, symbol); + + value += 5; + value |= 0x08000000; + + nv_wr32(priv, 0x616610 + hoff, value); +} + +static void +nvd0_display_unk2_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) +{ + u32 pclk; + int i; + + for (i = 0; mask && i < 8; i++) { + u32 mcc = nv_rd32(priv, 0x640180 + (i * 0x20)); + if (mcc & (1 << head)) + exec_script(priv, head, i, mcc, 2); + } + + pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + nv_debug(priv, "head %d pclk %d mask 0x%08x\n", head, pclk, mask); + if (pclk && (mask & 0x00010000)) { + struct nouveau_clock *clk = nouveau_clock(priv); + clk->pll_set(clk, PLL_VPLL0 + head, pclk); + } + + nv_wr32(priv, 0x612200 + (head * 0x800), 0x00000000); + + for (i = 0; mask && i < 8; i++) { + u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)), cfg; + if (mcp & (1 << head)) { + if ((cfg = exec_clkcmp(priv, head, i, mcp, 0, pclk))) { + u32 addr, mask, data = 0x00000000; + if (i < 4) { + addr = 0x612280 + ((i - 0) * 0x800); + mask = 0xffffffff; + } else { + switch (mcp & 0x00000f00) { + case 0x00000800: + case 0x00000900: + nvd0_display_unk2_calc_tu(priv, head, i - 4); + break; + default: + break; + } + + addr = 0x612300 + ((i - 4) * 0x800); + mask = 0x00000707; + if (cfg & 0x00000100) + data = 0x00000101; + } + nv_mask(priv, addr, mask, data); + } + break; + } + } + + nv_wr32(priv, 0x6101d4, 0x00000000); + nv_wr32(priv, 0x6109d4, 0x00000000); + nv_wr32(priv, 0x6101d0, 0x80000000); +} + +static void +nvd0_display_unk4_handler(struct nv50_disp_priv *priv, u32 head, u32 mask) +{ + int pclk, i; + + pclk = nv_rd32(priv, 0x660450 + (head * 0x300)) / 1000; + + for (i = 0; mask && i < 8; i++) { + u32 mcp = nv_rd32(priv, 0x660180 + (i * 0x20)); + if (mcp & (1 << head)) + exec_clkcmp(priv, head, i, mcp, 1, pclk); + } + + nv_wr32(priv, 0x6101d4, 0x00000000); + nv_wr32(priv, 0x6109d4, 0x00000000); + nv_wr32(priv, 0x6101d0, 0x80000000); +} + +static void +nvd0_disp_intr_vblank(struct nv50_disp_priv *priv, int crtc) { struct nouveau_bar *bar = nouveau_bar(priv); struct nouveau_disp *disp = &priv->base; @@ -65,14 +851,71 @@ nvd0_disp_intr_vblank(struct nvd0_disp_priv *priv, int crtc) disp->vblank.notify(disp->vblank.data, crtc); } -static void +void nvd0_disp_intr(struct nouveau_subdev *subdev) { - struct nvd0_disp_priv *priv = (void *)subdev; + struct nv50_disp_priv *priv = (void *)subdev; u32 intr = nv_rd32(priv, 0x610088); int i; - for (i = 0; i < 4; i++) { + if (intr & 0x00000001) { + u32 stat = nv_rd32(priv, 0x61008c); + nv_wr32(priv, 0x61008c, stat); + intr &= ~0x00000001; + } + + if (intr & 0x00000002) { + u32 stat = nv_rd32(priv, 0x61009c); + int chid = ffs(stat) - 1; + if (chid >= 0) { + u32 mthd = nv_rd32(priv, 0x6101f0 + (chid * 12)); + u32 data = nv_rd32(priv, 0x6101f4 + (chid * 12)); + u32 unkn = nv_rd32(priv, 0x6101f8 + (chid * 12)); + + nv_error(priv, "chid %d mthd 0x%04x data 0x%08x " + "0x%08x 0x%08x\n", + chid, (mthd & 0x0000ffc), data, mthd, unkn); + nv_wr32(priv, 0x61009c, (1 << chid)); + nv_wr32(priv, 0x6101f0 + (chid * 12), 0x90000000); + } + + intr &= ~0x00000002; + } + + if (intr & 0x00100000) { + u32 stat = nv_rd32(priv, 0x6100ac); + u32 mask = 0, crtc = ~0; + + while (!mask && ++crtc < priv->head.nr) + mask = nv_rd32(priv, 0x6101d4 + (crtc * 0x800)); + + if (stat & 0x00000001) { + nv_wr32(priv, 0x6100ac, 0x00000001); + nvd0_display_unk1_handler(priv, crtc, mask); + stat &= ~0x00000001; + } + + if (stat & 0x00000002) { + nv_wr32(priv, 0x6100ac, 0x00000002); + nvd0_display_unk2_handler(priv, crtc, mask); + stat &= ~0x00000002; + } + + if (stat & 0x00000004) { + nv_wr32(priv, 0x6100ac, 0x00000004); + nvd0_display_unk4_handler(priv, crtc, mask); + stat &= ~0x00000004; + } + + if (stat) { + nv_info(priv, "unknown intr24 0x%08x\n", stat); + nv_wr32(priv, 0x6100ac, stat); + } + + intr &= ~0x00100000; + } + + for (i = 0; i < priv->head.nr; i++) { u32 mask = 0x01000000 << i; if (mask & intr) { u32 stat = nv_rd32(priv, 0x6100bc + (i * 0x800)); @@ -86,10 +929,10 @@ nvd0_disp_intr(struct nouveau_subdev *subdev) static int nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { - struct nvd0_disp_priv *priv; + struct nv50_disp_priv *priv; int ret; ret = nouveau_disp_create(parent, engine, oclass, "PDISP", @@ -98,8 +941,23 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - nv_engine(priv)->sclass = nvd0_disp_sclass; + nv_engine(priv)->sclass = nvd0_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; nv_subdev(priv)->intr = nvd0_disp_intr; + priv->sclass = nvd0_disp_sclass; + priv->head.nr = nv_rd32(priv, 0x022448); + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = nvd0_hda_eld; + priv->sor.hdmi = nvd0_hdmi_ctrl; + priv->sor.dp_train = nvd0_sor_dp_train; + priv->sor.dp_train_init = nv94_sor_dp_train_init; + priv->sor.dp_train_fini = nv94_sor_dp_train_fini; + priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl; + priv->sor.dp_drvctl = nvd0_sor_dp_drvctl; INIT_LIST_HEAD(&priv->base.vblank.list); spin_lock_init(&priv->base.vblank.lock); @@ -108,7 +966,7 @@ nvd0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass nvd0_disp_oclass = { - .handle = NV_ENGINE(DISP, 0xd0), + .handle = NV_ENGINE(DISP, 0x90), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nvd0_disp_ctor, .dtor = _nouveau_disp_dtor, diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c new file mode 100644 index 00000000000..259537c4587 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nve0.c @@ -0,0 +1,94 @@ +/* + * 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 <engine/software.h> +#include <engine/disp.h> + +#include <core/class.h> + +#include "nv50.h" + +static struct nouveau_oclass +nve0_disp_sclass[] = { + { NVE0_DISP_MAST_CLASS, &nvd0_disp_mast_ofuncs }, + { NVE0_DISP_SYNC_CLASS, &nvd0_disp_sync_ofuncs }, + { NVE0_DISP_OVLY_CLASS, &nvd0_disp_ovly_ofuncs }, + { NVE0_DISP_OIMM_CLASS, &nvd0_disp_oimm_ofuncs }, + { NVE0_DISP_CURS_CLASS, &nvd0_disp_curs_ofuncs }, + {} +}; + +static struct nouveau_oclass +nve0_disp_base_oclass[] = { + { NVE0_DISP_CLASS, &nvd0_disp_base_ofuncs, nva3_disp_base_omthds }, + {} +}; + +static int +nve0_disp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv50_disp_priv *priv; + int ret; + + ret = nouveau_disp_create(parent, engine, oclass, "PDISP", + "display", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nve0_disp_base_oclass; + nv_engine(priv)->cclass = &nv50_disp_cclass; + nv_subdev(priv)->intr = nvd0_disp_intr; + priv->sclass = nve0_disp_sclass; + priv->head.nr = nv_rd32(priv, 0x022448); + priv->dac.nr = 3; + priv->sor.nr = 4; + priv->dac.power = nv50_dac_power; + priv->dac.sense = nv50_dac_sense; + priv->sor.power = nv50_sor_power; + priv->sor.hda_eld = nvd0_hda_eld; + priv->sor.hdmi = nvd0_hdmi_ctrl; + priv->sor.dp_train = nvd0_sor_dp_train; + priv->sor.dp_train_init = nv94_sor_dp_train_init; + priv->sor.dp_train_fini = nv94_sor_dp_train_fini; + priv->sor.dp_lnkctl = nvd0_sor_dp_lnkctl; + priv->sor.dp_drvctl = nvd0_sor_dp_drvctl; + + INIT_LIST_HEAD(&priv->base.vblank.list); + spin_lock_init(&priv->base.vblank.lock); + return 0; +} + +struct nouveau_oclass +nve0_disp_oclass = { + .handle = NV_ENGINE(DISP, 0x91), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_disp_ctor, + .dtor = _nouveau_disp_dtor, + .init = _nouveau_disp_init, + .fini = _nouveau_disp_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c new file mode 100644 index 00000000000..39b6b67732d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv50.c @@ -0,0 +1,112 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/timer.h> + +#include "nv50.h" + +int +nv50_sor_power(struct nv50_disp_priv *priv, int or, u32 data) +{ + const u32 stat = data & NV50_DISP_SOR_PWR_STATE; + const u32 soff = (or * 0x800); + nv_wait(priv, 0x61c004 + soff, 0x80000000, 0x00000000); + nv_mask(priv, 0x61c004 + soff, 0x80000001, 0x80000000 | stat); + nv_wait(priv, 0x61c004 + soff, 0x80000000, 0x00000000); + nv_wait(priv, 0x61c030 + soff, 0x10000000, 0x00000000); + return 0; +} + +int +nv50_sor_mthd(struct nouveau_object *object, u32 mthd, void *args, u32 size) +{ + struct nv50_disp_priv *priv = (void *)object->engine; + struct nouveau_bios *bios = nouveau_bios(priv); + const u16 type = (mthd & NV50_DISP_SOR_MTHD_TYPE) >> 12; + const u8 head = (mthd & NV50_DISP_SOR_MTHD_HEAD) >> 3; + const u8 link = (mthd & NV50_DISP_SOR_MTHD_LINK) >> 2; + const u8 or = (mthd & NV50_DISP_SOR_MTHD_OR); + const u16 mask = (0x0100 << head) | (0x0040 << link) | (0x0001 << or); + struct dcb_output outp; + u8 ver, hdr; + u32 data; + int ret = -EINVAL; + + if (size < sizeof(u32)) + return -EINVAL; + data = *(u32 *)args; + + if (type && !dcb_outp_match(bios, type, mask, &ver, &hdr, &outp)) + return -ENODEV; + + switch (mthd & ~0x3f) { + case NV50_DISP_SOR_PWR: + ret = priv->sor.power(priv, or, data); + break; + case NVA3_DISP_SOR_HDA_ELD: + ret = priv->sor.hda_eld(priv, or, args, size); + break; + case NV84_DISP_SOR_HDMI_PWR: + ret = priv->sor.hdmi(priv, head, or, data); + break; + case NV50_DISP_SOR_LVDS_SCRIPT: + priv->sor.lvdsconf = data & NV50_DISP_SOR_LVDS_SCRIPT_ID; + ret = 0; + break; + case NV94_DISP_SOR_DP_TRAIN: + switch (data & NV94_DISP_SOR_DP_TRAIN_OP) { + case NV94_DISP_SOR_DP_TRAIN_OP_PATTERN: + ret = priv->sor.dp_train(priv, or, link, type, mask, data, &outp); + break; + case NV94_DISP_SOR_DP_TRAIN_OP_INIT: + ret = priv->sor.dp_train_init(priv, or, link, head, type, mask, data, &outp); + break; + case NV94_DISP_SOR_DP_TRAIN_OP_FINI: + ret = priv->sor.dp_train_fini(priv, or, link, head, type, mask, data, &outp); + break; + default: + break; + } + break; + case NV94_DISP_SOR_DP_LNKCTL: + ret = priv->sor.dp_lnkctl(priv, or, link, head, type, mask, data, &outp); + break; + case NV94_DISP_SOR_DP_DRVCTL(0): + case NV94_DISP_SOR_DP_DRVCTL(1): + case NV94_DISP_SOR_DP_DRVCTL(2): + case NV94_DISP_SOR_DP_DRVCTL(3): + ret = priv->sor.dp_drvctl(priv, or, link, (mthd & 0xc0) >> 6, + type, mask, data, &outp); + break; + default: + BUG_ON(1); + } + + return ret; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c new file mode 100644 index 00000000000..f6edd009762 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornv94.c @@ -0,0 +1,190 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/dp.h> +#include <subdev/bios/init.h> + +#include "nv50.h" + +static inline u32 +nv94_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) +{ + static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ + static const u8 nv94[] = { 16, 8, 0, 24 }; + if (nv_device(priv)->chipset == 0xaf) + return nvaf[lane]; + return nv94[lane]; +} + +int +nv94_sor_dp_train_init(struct nv50_disp_priv *priv, int or, int link, int head, + u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_dpout info; + u8 ver, hdr, cnt, len; + u16 outp; + + outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); + if (outp) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .outp = dcbo, + .crtc = head, + .execute = 1, + }; + + if (data & NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON) + init.offset = info.script[2]; + else + init.offset = info.script[3]; + nvbios_exec(&init); + + init.offset = info.script[0]; + nvbios_exec(&init); + } + + return 0; +} + +int +nv94_sor_dp_train_fini(struct nv50_disp_priv *priv, int or, int link, int head, + u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + struct nvbios_dpout info; + u8 ver, hdr, cnt, len; + u16 outp; + + outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); + if (outp) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[1], + .outp = dcbo, + .crtc = head, + .execute = 1, + }; + + nvbios_exec(&init); + } + + return 0; +} + +int +nv94_sor_dp_train(struct nv50_disp_priv *priv, int or, int link, + u16 type, u16 mask, u32 data, struct dcb_output *info) +{ + const u32 loff = (or * 0x800) + (link * 0x80); + const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN); + nv_mask(priv, 0x61c10c + loff, 0x0f000000, patt << 24); + return 0; +} + +int +nv94_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, + u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 loff = (or * 0x800) + (link * 0x80); + const u32 soff = (or * 0x800); + u16 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8; + u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + u32 outp, lane = 0; + u8 ver, hdr, cnt, len; + struct nvbios_dpout info; + int i; + + /* -> 10Khz units */ + link_bw *= 2700; + + outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); + if (outp && info.lnkcmp) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = 0x0000, + .outp = dcbo, + .crtc = head, + .execute = 1, + }; + + while (link_bw < nv_ro16(bios, info.lnkcmp)) + info.lnkcmp += 4; + init.offset = nv_ro16(bios, info.lnkcmp + 2); + + nvbios_exec(&init); + } + + dpctrl |= ((1 << link_nr) - 1) << 16; + if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH) + dpctrl |= 0x00004000; + if (link_bw > 16200) + clksor |= 0x00040000; + + for (i = 0; i < link_nr; i++) + lane |= 1 << (nv94_sor_dp_lane_map(priv, i) >> 3); + + nv_mask(priv, 0x614300 + soff, 0x000c0000, clksor); + nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); + nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane); + return 0; +} + +int +nv94_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, + u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 loff = (or * 0x800) + (link * 0x80); + const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8; + const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE); + u32 addr, shift = nv94_sor_dp_lane_map(priv, lane); + u8 ver, hdr, cnt, len; + struct nvbios_dpout outp; + struct nvbios_dpcfg ocfg; + + addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp); + if (!addr) + return -ENODEV; + + addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg); + if (!addr) + return -EINVAL; + + nv_mask(priv, 0x61c118 + loff, 0x000000ff << shift, ocfg.drv << shift); + nv_mask(priv, 0x61c120 + loff, 0x000000ff << shift, ocfg.pre << shift); + nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c new file mode 100644 index 00000000000..c37ce7e29f5 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/disp/sornvd0.c @@ -0,0 +1,126 @@ +/* + * 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/os.h> +#include <core/class.h> + +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/dp.h> +#include <subdev/bios/init.h> + +#include "nv50.h" + +static inline u32 +nvd0_sor_dp_lane_map(struct nv50_disp_priv *priv, u8 lane) +{ + static const u8 nvd0[] = { 16, 8, 0, 24 }; + return nvd0[lane]; +} + +int +nvd0_sor_dp_train(struct nv50_disp_priv *priv, int or, int link, + u16 type, u16 mask, u32 data, struct dcb_output *info) +{ + const u32 loff = (or * 0x800) + (link * 0x80); + const u32 patt = (data & NV94_DISP_SOR_DP_TRAIN_PATTERN); + nv_mask(priv, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * patt); + return 0; +} + +int +nvd0_sor_dp_lnkctl(struct nv50_disp_priv *priv, int or, int link, int head, + u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 loff = (or * 0x800) + (link * 0x80); + const u32 soff = (or * 0x800); + const u8 link_bw = (data & NV94_DISP_SOR_DP_LNKCTL_WIDTH) >> 8; + const u8 link_nr = (data & NV94_DISP_SOR_DP_LNKCTL_COUNT); + u32 dpctrl = 0x00000000; + u32 clksor = 0x00000000; + u32 outp, lane = 0; + u8 ver, hdr, cnt, len; + struct nvbios_dpout info; + int i; + + outp = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &info); + if (outp && info.lnkcmp) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = 0x0000, + .outp = dcbo, + .crtc = head, + .execute = 1, + }; + + while (nv_ro08(bios, info.lnkcmp) < link_bw) + info.lnkcmp += 3; + init.offset = nv_ro16(bios, info.lnkcmp + 1); + + nvbios_exec(&init); + } + + clksor |= link_bw << 18; + dpctrl |= ((1 << link_nr) - 1) << 16; + if (data & NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH) + dpctrl |= 0x00004000; + + for (i = 0; i < link_nr; i++) + lane |= 1 << (nvd0_sor_dp_lane_map(priv, i) >> 3); + + nv_mask(priv, 0x612300 + soff, 0x007c0000, clksor); + nv_mask(priv, 0x61c10c + loff, 0x001f4000, dpctrl); + nv_mask(priv, 0x61c130 + loff, 0x0000000f, lane); + return 0; +} + +int +nvd0_sor_dp_drvctl(struct nv50_disp_priv *priv, int or, int link, int lane, + u16 type, u16 mask, u32 data, struct dcb_output *dcbo) +{ + struct nouveau_bios *bios = nouveau_bios(priv); + const u32 loff = (or * 0x800) + (link * 0x80); + const u8 swing = (data & NV94_DISP_SOR_DP_DRVCTL_VS) >> 8; + const u8 preem = (data & NV94_DISP_SOR_DP_DRVCTL_PE); + u32 addr, shift = nvd0_sor_dp_lane_map(priv, lane); + u8 ver, hdr, cnt, len; + struct nvbios_dpout outp; + struct nvbios_dpcfg ocfg; + + addr = nvbios_dpout_match(bios, type, mask, &ver, &hdr, &cnt, &len, &outp); + if (!addr) + return -ENODEV; + + addr = nvbios_dpcfg_match(bios, addr, 0, swing, preem, &ver, &hdr, &cnt, &len, &ocfg); + if (!addr) + return -EINVAL; + + nv_mask(priv, 0x61c118 + loff, 0x000000ff << shift, ocfg.drv << shift); + nv_mask(priv, 0x61c120 + loff, 0x000000ff << shift, ocfg.pre << shift); + nv_mask(priv, 0x61c130 + loff, 0x0000ff00, ocfg.unk << 8); + nv_mask(priv, 0x61c13c + loff, 0x00000000, 0x00000000); + return 0; +} diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c index e1f013d3976..5103e88d187 100644 --- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/base.c @@ -28,37 +28,39 @@ #include <subdev/fb.h> #include <engine/dmaobj.h> -int -nouveau_dmaobj_create_(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, - void *data, u32 size, int len, void **pobject) +static int +nouveau_dmaobj_ctor(struct nouveau_object *parent, + struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) { + struct nouveau_dmaeng *dmaeng = (void *)engine; + struct nouveau_dmaobj *dmaobj; + struct nouveau_gpuobj *gpuobj; struct nv_dma_class *args = data; - struct nouveau_dmaobj *object; int ret; if (size < sizeof(*args)) return -EINVAL; - ret = nouveau_object_create_(parent, engine, oclass, 0, len, pobject); - object = *pobject; + ret = nouveau_object_create(parent, engine, oclass, 0, &dmaobj); + *pobject = nv_object(dmaobj); if (ret) return ret; switch (args->flags & NV_DMA_TARGET_MASK) { case NV_DMA_TARGET_VM: - object->target = NV_MEM_TARGET_VM; + dmaobj->target = NV_MEM_TARGET_VM; break; case NV_DMA_TARGET_VRAM: - object->target = NV_MEM_TARGET_VRAM; + dmaobj->target = NV_MEM_TARGET_VRAM; break; case NV_DMA_TARGET_PCI: - object->target = NV_MEM_TARGET_PCI; + dmaobj->target = NV_MEM_TARGET_PCI; break; case NV_DMA_TARGET_PCI_US: case NV_DMA_TARGET_AGP: - object->target = NV_MEM_TARGET_PCI_NOSNOOP; + dmaobj->target = NV_MEM_TARGET_PCI_NOSNOOP; break; default: return -EINVAL; @@ -66,22 +68,53 @@ nouveau_dmaobj_create_(struct nouveau_object *parent, switch (args->flags & NV_DMA_ACCESS_MASK) { case NV_DMA_ACCESS_VM: - object->access = NV_MEM_ACCESS_VM; + dmaobj->access = NV_MEM_ACCESS_VM; break; case NV_DMA_ACCESS_RD: - object->access = NV_MEM_ACCESS_RO; + dmaobj->access = NV_MEM_ACCESS_RO; break; case NV_DMA_ACCESS_WR: - object->access = NV_MEM_ACCESS_WO; + dmaobj->access = NV_MEM_ACCESS_WO; break; case NV_DMA_ACCESS_RDWR: - object->access = NV_MEM_ACCESS_RW; + dmaobj->access = NV_MEM_ACCESS_RW; break; default: return -EINVAL; } - object->start = args->start; - object->limit = args->limit; - return 0; + dmaobj->start = args->start; + dmaobj->limit = args->limit; + dmaobj->conf0 = args->conf0; + + switch (nv_mclass(parent)) { + case NV_DEVICE_CLASS: + /* delayed, or no, binding */ + break; + default: + ret = dmaeng->bind(dmaeng, *pobject, dmaobj, &gpuobj); + if (ret == 0) { + nouveau_object_ref(NULL, pobject); + *pobject = nv_object(gpuobj); + } + break; + } + + return ret; } + +static struct nouveau_ofuncs +nouveau_dmaobj_ofuncs = { + .ctor = nouveau_dmaobj_ctor, + .dtor = nouveau_object_destroy, + .init = nouveau_object_init, + .fini = nouveau_object_fini, +}; + +struct nouveau_oclass +nouveau_dmaobj_sclass[] = { + { NV_DMA_FROM_MEMORY_CLASS, &nouveau_dmaobj_ofuncs }, + { NV_DMA_TO_MEMORY_CLASS, &nouveau_dmaobj_ofuncs }, + { NV_DMA_IN_MEMORY_CLASS, &nouveau_dmaobj_ofuncs }, + {} +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c index 9f4cc2f3199..027d8217c0f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv04.c @@ -34,10 +34,6 @@ struct nv04_dmaeng_priv { struct nouveau_dmaeng base; }; -struct nv04_dmaobj_priv { - struct nouveau_dmaobj base; -}; - static int nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng, struct nouveau_object *parent, @@ -53,6 +49,18 @@ nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng, u32 length = dmaobj->limit - dmaobj->start; int ret; + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case NV03_CHANNEL_DMA_CLASS: + case NV10_CHANNEL_DMA_CLASS: + case NV17_CHANNEL_DMA_CLASS: + case NV40_CHANNEL_DMA_CLASS: + break; + default: + return -EINVAL; + } + } + if (dmaobj->target == NV_MEM_TARGET_VM) { if (nv_object(vmm)->oclass == &nv04_vmmgr_oclass) { struct nouveau_gpuobj *pgt = vmm->vm->pgt[0].obj[0]; @@ -106,56 +114,6 @@ nv04_dmaobj_bind(struct nouveau_dmaeng *dmaeng, } static int -nv04_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nouveau_dmaeng *dmaeng = (void *)engine; - struct nv04_dmaobj_priv *dmaobj; - struct nouveau_gpuobj *gpuobj; - int ret; - - ret = nouveau_dmaobj_create(parent, engine, oclass, - data, size, &dmaobj); - *pobject = nv_object(dmaobj); - if (ret) - return ret; - - switch (nv_mclass(parent)) { - case NV_DEVICE_CLASS: - break; - case NV03_CHANNEL_DMA_CLASS: - case NV10_CHANNEL_DMA_CLASS: - case NV17_CHANNEL_DMA_CLASS: - case NV40_CHANNEL_DMA_CLASS: - ret = dmaeng->bind(dmaeng, *pobject, &dmaobj->base, &gpuobj); - nouveau_object_ref(NULL, pobject); - *pobject = nv_object(gpuobj); - break; - default: - return -EINVAL; - } - - return ret; -} - -static struct nouveau_ofuncs -nv04_dmaobj_ofuncs = { - .ctor = nv04_dmaobj_ctor, - .dtor = _nouveau_dmaobj_dtor, - .init = _nouveau_dmaobj_init, - .fini = _nouveau_dmaobj_fini, -}; - -static struct nouveau_oclass -nv04_dmaobj_sclass[] = { - { 0x0002, &nv04_dmaobj_ofuncs }, - { 0x0003, &nv04_dmaobj_ofuncs }, - { 0x003d, &nv04_dmaobj_ofuncs }, - {} -}; - -static int nv04_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) @@ -168,7 +126,7 @@ nv04_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.base.sclass = nv04_dmaobj_sclass; + nv_engine(priv)->sclass = nouveau_dmaobj_sclass; priv->base.bind = nv04_dmaobj_bind; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c index 045d2565e28..750183f7c05 100644 --- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nv50.c @@ -32,36 +32,74 @@ struct nv50_dmaeng_priv { struct nouveau_dmaeng base; }; -struct nv50_dmaobj_priv { - struct nouveau_dmaobj base; -}; - static int nv50_dmaobj_bind(struct nouveau_dmaeng *dmaeng, struct nouveau_object *parent, struct nouveau_dmaobj *dmaobj, struct nouveau_gpuobj **pgpuobj) { - u32 flags = nv_mclass(dmaobj); + u32 flags0 = nv_mclass(dmaobj); + u32 flags5 = 0x00000000; int ret; + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case NV50_CHANNEL_DMA_CLASS: + case NV84_CHANNEL_DMA_CLASS: + case NV50_CHANNEL_IND_CLASS: + case NV84_CHANNEL_IND_CLASS: + case NV50_DISP_MAST_CLASS: + case NV84_DISP_MAST_CLASS: + case NV94_DISP_MAST_CLASS: + case NVA0_DISP_MAST_CLASS: + case NVA3_DISP_MAST_CLASS: + case NV50_DISP_SYNC_CLASS: + case NV84_DISP_SYNC_CLASS: + case NV94_DISP_SYNC_CLASS: + case NVA0_DISP_SYNC_CLASS: + case NVA3_DISP_SYNC_CLASS: + case NV50_DISP_OVLY_CLASS: + case NV84_DISP_OVLY_CLASS: + case NV94_DISP_OVLY_CLASS: + case NVA0_DISP_OVLY_CLASS: + case NVA3_DISP_OVLY_CLASS: + break; + default: + return -EINVAL; + } + } + + if (!(dmaobj->conf0 & NV50_DMA_CONF0_ENABLE)) { + if (dmaobj->target == NV_MEM_TARGET_VM) { + dmaobj->conf0 = NV50_DMA_CONF0_PRIV_VM; + dmaobj->conf0 |= NV50_DMA_CONF0_PART_VM; + dmaobj->conf0 |= NV50_DMA_CONF0_COMP_VM; + dmaobj->conf0 |= NV50_DMA_CONF0_TYPE_VM; + } else { + dmaobj->conf0 = NV50_DMA_CONF0_PRIV_US; + dmaobj->conf0 |= NV50_DMA_CONF0_PART_256; + dmaobj->conf0 |= NV50_DMA_CONF0_COMP_NONE; + dmaobj->conf0 |= NV50_DMA_CONF0_TYPE_LINEAR; + } + } + + flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_COMP) << 22; + flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_TYPE) << 22; + flags0 |= (dmaobj->conf0 & NV50_DMA_CONF0_PRIV); + flags5 |= (dmaobj->conf0 & NV50_DMA_CONF0_PART); + switch (dmaobj->target) { case NV_MEM_TARGET_VM: - flags |= 0x00000000; - flags |= 0x60000000; /* COMPRESSION_USEVM */ - flags |= 0x1fc00000; /* STORAGE_TYPE_USEVM */ + flags0 |= 0x00000000; break; case NV_MEM_TARGET_VRAM: - flags |= 0x00010000; - flags |= 0x00100000; /* ACCESSUS_USER_SYSTEM */ + flags0 |= 0x00010000; break; case NV_MEM_TARGET_PCI: - flags |= 0x00020000; - flags |= 0x00100000; /* ACCESSUS_USER_SYSTEM */ + flags0 |= 0x00020000; break; case NV_MEM_TARGET_PCI_NOSNOOP: - flags |= 0x00030000; - flags |= 0x00100000; /* ACCESSUS_USER_SYSTEM */ + flags0 |= 0x00030000; break; default: return -EINVAL; @@ -71,79 +109,29 @@ nv50_dmaobj_bind(struct nouveau_dmaeng *dmaeng, case NV_MEM_ACCESS_VM: break; case NV_MEM_ACCESS_RO: - flags |= 0x00040000; + flags0 |= 0x00040000; break; case NV_MEM_ACCESS_WO: case NV_MEM_ACCESS_RW: - flags |= 0x00080000; + flags0 |= 0x00080000; break; } ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj); if (ret == 0) { - nv_wo32(*pgpuobj, 0x00, flags); + nv_wo32(*pgpuobj, 0x00, flags0); nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit)); nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start)); nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 | upper_32_bits(dmaobj->start)); nv_wo32(*pgpuobj, 0x10, 0x00000000); - nv_wo32(*pgpuobj, 0x14, 0x00000000); + nv_wo32(*pgpuobj, 0x14, flags5); } return ret; } static int -nv50_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nouveau_dmaeng *dmaeng = (void *)engine; - struct nv50_dmaobj_priv *dmaobj; - struct nouveau_gpuobj *gpuobj; - int ret; - - ret = nouveau_dmaobj_create(parent, engine, oclass, - data, size, &dmaobj); - *pobject = nv_object(dmaobj); - if (ret) - return ret; - - switch (nv_mclass(parent)) { - case NV_DEVICE_CLASS: - break; - case NV50_CHANNEL_DMA_CLASS: - case NV84_CHANNEL_DMA_CLASS: - case NV50_CHANNEL_IND_CLASS: - case NV84_CHANNEL_IND_CLASS: - ret = dmaeng->bind(dmaeng, *pobject, &dmaobj->base, &gpuobj); - nouveau_object_ref(NULL, pobject); - *pobject = nv_object(gpuobj); - break; - default: - return -EINVAL; - } - - return ret; -} - -static struct nouveau_ofuncs -nv50_dmaobj_ofuncs = { - .ctor = nv50_dmaobj_ctor, - .dtor = _nouveau_dmaobj_dtor, - .init = _nouveau_dmaobj_init, - .fini = _nouveau_dmaobj_fini, -}; - -static struct nouveau_oclass -nv50_dmaobj_sclass[] = { - { 0x0002, &nv50_dmaobj_ofuncs }, - { 0x0003, &nv50_dmaobj_ofuncs }, - { 0x003d, &nv50_dmaobj_ofuncs }, - {} -}; - -static int nv50_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) @@ -156,7 +144,7 @@ nv50_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.base.sclass = nv50_dmaobj_sclass; + nv_engine(priv)->sclass = nouveau_dmaobj_sclass; priv->base.bind = nv50_dmaobj_bind; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c index 5baa0869553..cd3970d03b8 100644 --- a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvc0.c @@ -22,7 +22,9 @@ * Authors: Ben Skeggs */ +#include <core/device.h> #include <core/gpuobj.h> +#include <core/class.h> #include <subdev/fb.h> #include <engine/dmaobj.h> @@ -31,44 +33,85 @@ struct nvc0_dmaeng_priv { struct nouveau_dmaeng base; }; -struct nvc0_dmaobj_priv { - struct nouveau_dmaobj base; -}; - static int -nvc0_dmaobj_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) +nvc0_dmaobj_bind(struct nouveau_dmaeng *dmaeng, + struct nouveau_object *parent, + struct nouveau_dmaobj *dmaobj, + struct nouveau_gpuobj **pgpuobj) { - struct nvc0_dmaobj_priv *dmaobj; + u32 flags0 = nv_mclass(dmaobj); + u32 flags5 = 0x00000000; int ret; - ret = nouveau_dmaobj_create(parent, engine, oclass, data, size, &dmaobj); - *pobject = nv_object(dmaobj); - if (ret) - return ret; + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case NVA3_DISP_MAST_CLASS: + case NVA3_DISP_SYNC_CLASS: + case NVA3_DISP_OVLY_CLASS: + break; + default: + return -EINVAL; + } + } else + return 0; + + if (!(dmaobj->conf0 & NVC0_DMA_CONF0_ENABLE)) { + if (dmaobj->target == NV_MEM_TARGET_VM) { + dmaobj->conf0 = NVC0_DMA_CONF0_PRIV_VM; + dmaobj->conf0 |= NVC0_DMA_CONF0_TYPE_VM; + } else { + dmaobj->conf0 = NVC0_DMA_CONF0_PRIV_US; + dmaobj->conf0 |= NVC0_DMA_CONF0_TYPE_LINEAR; + dmaobj->conf0 |= 0x00020000; + } + } - if (dmaobj->base.target != NV_MEM_TARGET_VM || dmaobj->base.start) + flags0 |= (dmaobj->conf0 & NVC0_DMA_CONF0_TYPE) << 22; + flags0 |= (dmaobj->conf0 & NVC0_DMA_CONF0_PRIV); + flags5 |= (dmaobj->conf0 & NVC0_DMA_CONF0_UNKN); + + switch (dmaobj->target) { + case NV_MEM_TARGET_VM: + flags0 |= 0x00000000; + break; + case NV_MEM_TARGET_VRAM: + flags0 |= 0x00010000; + break; + case NV_MEM_TARGET_PCI: + flags0 |= 0x00020000; + break; + case NV_MEM_TARGET_PCI_NOSNOOP: + flags0 |= 0x00030000; + break; + default: return -EINVAL; + } - return 0; -} + switch (dmaobj->access) { + case NV_MEM_ACCESS_VM: + break; + case NV_MEM_ACCESS_RO: + flags0 |= 0x00040000; + break; + case NV_MEM_ACCESS_WO: + case NV_MEM_ACCESS_RW: + flags0 |= 0x00080000; + break; + } -static struct nouveau_ofuncs -nvc0_dmaobj_ofuncs = { - .ctor = nvc0_dmaobj_ctor, - .dtor = _nouveau_dmaobj_dtor, - .init = _nouveau_dmaobj_init, - .fini = _nouveau_dmaobj_fini, -}; + ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj); + if (ret == 0) { + nv_wo32(*pgpuobj, 0x00, flags0); + nv_wo32(*pgpuobj, 0x04, lower_32_bits(dmaobj->limit)); + nv_wo32(*pgpuobj, 0x08, lower_32_bits(dmaobj->start)); + nv_wo32(*pgpuobj, 0x0c, upper_32_bits(dmaobj->limit) << 24 | + upper_32_bits(dmaobj->start)); + nv_wo32(*pgpuobj, 0x10, 0x00000000); + nv_wo32(*pgpuobj, 0x14, flags5); + } -static struct nouveau_oclass -nvc0_dmaobj_sclass[] = { - { 0x0002, &nvc0_dmaobj_ofuncs }, - { 0x0003, &nvc0_dmaobj_ofuncs }, - { 0x003d, &nvc0_dmaobj_ofuncs }, - {} -}; + return ret; +} static int nvc0_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, @@ -83,7 +126,8 @@ nvc0_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - priv->base.base.sclass = nvc0_dmaobj_sclass; + nv_engine(priv)->sclass = nouveau_dmaobj_sclass; + priv->base.bind = nvc0_dmaobj_bind; return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c new file mode 100644 index 00000000000..d1528752980 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/dmaobj/nvd0.c @@ -0,0 +1,122 @@ +/* + * 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/gpuobj.h> +#include <core/class.h> + +#include <subdev/fb.h> +#include <engine/dmaobj.h> + +struct nvd0_dmaeng_priv { + struct nouveau_dmaeng base; +}; + +static int +nvd0_dmaobj_bind(struct nouveau_dmaeng *dmaeng, + struct nouveau_object *parent, + struct nouveau_dmaobj *dmaobj, + struct nouveau_gpuobj **pgpuobj) +{ + u32 flags0 = 0x00000000; + int ret; + + if (!nv_iclass(parent, NV_ENGCTX_CLASS)) { + switch (nv_mclass(parent->parent)) { + case NVD0_DISP_MAST_CLASS: + case NVD0_DISP_SYNC_CLASS: + case NVD0_DISP_OVLY_CLASS: + case NVE0_DISP_MAST_CLASS: + case NVE0_DISP_SYNC_CLASS: + case NVE0_DISP_OVLY_CLASS: + break; + default: + return -EINVAL; + } + } else + return 0; + + if (!(dmaobj->conf0 & NVD0_DMA_CONF0_ENABLE)) { + if (dmaobj->target == NV_MEM_TARGET_VM) { + dmaobj->conf0 |= NVD0_DMA_CONF0_TYPE_VM; + dmaobj->conf0 |= NVD0_DMA_CONF0_PAGE_LP; + } else { + dmaobj->conf0 |= NVD0_DMA_CONF0_TYPE_LINEAR; + dmaobj->conf0 |= NVD0_DMA_CONF0_PAGE_SP; + } + } + + flags0 |= (dmaobj->conf0 & NVD0_DMA_CONF0_TYPE) << 20; + flags0 |= (dmaobj->conf0 & NVD0_DMA_CONF0_PAGE) >> 4; + + switch (dmaobj->target) { + case NV_MEM_TARGET_VRAM: + flags0 |= 0x00000009; + break; + default: + return -EINVAL; + break; + } + + ret = nouveau_gpuobj_new(parent, parent, 24, 32, 0, pgpuobj); + if (ret == 0) { + nv_wo32(*pgpuobj, 0x00, flags0); + nv_wo32(*pgpuobj, 0x04, dmaobj->start >> 8); + nv_wo32(*pgpuobj, 0x08, dmaobj->limit >> 8); + nv_wo32(*pgpuobj, 0x0c, 0x00000000); + nv_wo32(*pgpuobj, 0x10, 0x00000000); + nv_wo32(*pgpuobj, 0x14, 0x00000000); + } + + return ret; +} + +static int +nvd0_dmaeng_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvd0_dmaeng_priv *priv; + int ret; + + ret = nouveau_dmaeng_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_engine(priv)->sclass = nouveau_dmaobj_sclass; + priv->base.bind = nvd0_dmaobj_bind; + return 0; +} + +struct nouveau_oclass +nvd0_dmaeng_oclass = { + .handle = NV_ENGINE(DMAOBJ, 0xd0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvd0_dmaeng_ctor, + .dtor = _nouveau_dmaeng_dtor, + .init = _nouveau_dmaeng_init, + .fini = _nouveau_dmaeng_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c index bbb43c67c2a..c2b9db33581 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/base.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/base.c @@ -24,6 +24,7 @@ #include <core/object.h> #include <core/handle.h> +#include <core/class.h> #include <engine/dmaobj.h> #include <engine/fifo.h> @@ -33,7 +34,7 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, int bar, u32 addr, u32 size, u32 pushbuf, - u32 engmask, int len, void **ptr) + u64 engmask, int len, void **ptr) { struct nouveau_device *device = nv_device(engine); struct nouveau_fifo *priv = (void *)engine; @@ -56,18 +57,16 @@ nouveau_fifo_channel_create_(struct nouveau_object *parent, dmaeng = (void *)chan->pushdma->base.engine; switch (chan->pushdma->base.oclass->handle) { - case 0x0002: - case 0x003d: + case NV_DMA_FROM_MEMORY_CLASS: + case NV_DMA_IN_MEMORY_CLASS: break; default: return -EINVAL; } - if (dmaeng->bind) { - ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu); - if (ret) - return ret; - } + ret = dmaeng->bind(dmaeng, parent, chan->pushdma, &chan->pushgpu); + if (ret) + return ret; /* find a free fifo channel */ spin_lock_irqsave(&priv->lock, flags); @@ -119,14 +118,14 @@ _nouveau_fifo_channel_dtor(struct nouveau_object *object) } u32 -_nouveau_fifo_channel_rd32(struct nouveau_object *object, u32 addr) +_nouveau_fifo_channel_rd32(struct nouveau_object *object, u64 addr) { struct nouveau_fifo_chan *chan = (void *)object; return ioread32_native(chan->user + addr); } void -_nouveau_fifo_channel_wr32(struct nouveau_object *object, u32 addr, u32 data) +_nouveau_fifo_channel_wr32(struct nouveau_object *object, u64 addr, u32 data) { struct nouveau_fifo_chan *chan = (void *)object; iowrite32_native(data, chan->user + addr); diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c index ea76e3e8c9c..a47a8548f9e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv04.c @@ -126,9 +126,9 @@ nv04_fifo_chan_ctor(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000, 0x10000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR), &chan); *pobject = nv_object(chan); if (ret) return ret; @@ -440,7 +440,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (!nv04_fifo_swmthd(priv, chid, mthd, data)) { - nv_info(priv, "CACHE_ERROR - Ch %d/%d " + nv_error(priv, "CACHE_ERROR - Ch %d/%d " "Mthd 0x%04x Data 0x%08x\n", chid, (mthd >> 13) & 7, mthd & 0x1ffc, data); @@ -476,7 +476,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) u32 ib_get = nv_rd32(priv, 0x003334); u32 ib_put = nv_rd32(priv, 0x003330); - nv_info(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x " + nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%02x%08x " "Put 0x%02x%08x IbGet 0x%08x IbPut 0x%08x " "State 0x%08x (err: %s) Push 0x%08x\n", chid, ho_get, dma_get, ho_put, @@ -494,7 +494,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x003334, ib_put); } } else { - nv_info(priv, "DMA_PUSHER - Ch %d Get 0x%08x " + nv_error(priv, "DMA_PUSHER - Ch %d Get 0x%08x " "Put 0x%08x State 0x%08x (err: %s) Push 0x%08x\n", chid, dma_get, dma_put, state, nv_dma_state_err(state), push); @@ -525,14 +525,13 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) if (device->card_type == NV_50) { if (status & 0x00000010) { - nv50_fb_trap(nouveau_fb(priv), 1); status &= ~0x00000010; nv_wr32(priv, 0x002100, 0x00000010); } } if (status) { - nv_info(priv, "unknown intr 0x%08x, ch %d\n", + nv_warn(priv, "unknown intr 0x%08x, ch %d\n", status, chid); nv_wr32(priv, NV03_PFIFO_INTR_0, status); status = 0; @@ -542,7 +541,7 @@ nv04_fifo_intr(struct nouveau_subdev *subdev) } if (status) { - nv_info(priv, "still angry after %d spins, halt\n", cnt); + nv_error(priv, "still angry after %d spins, halt\n", cnt); nv_wr32(priv, 0x002140, 0); nv_wr32(priv, 0x000140, 0); } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c index 4ba75422b89..2c927c1d173 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv10.c @@ -69,9 +69,9 @@ nv10_fifo_chan_ctor(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000, 0x10000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR), &chan); *pobject = nv_object(chan); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c index b96e6b0ae2b..a9cb51d38c5 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv17.c @@ -74,10 +74,10 @@ nv17_fifo_chan_ctor(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0x800000, 0x10000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_MPEG), /* NV31- */ + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), /* NV31- */ &chan); *pobject = nv_object(chan); if (ret) diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c index 559c3b4e1b8..2b1f9172122 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv40.c @@ -192,10 +192,10 @@ nv40_fifo_chan_ctor(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, 0x1000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_MPEG), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), &chan); *pobject = nv_object(chan); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c index 536e7634a00..bd096364f68 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv50.c @@ -112,14 +112,6 @@ nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend, return -EINVAL; } - nv_wo32(base->eng, addr + 0x00, 0x00000000); - nv_wo32(base->eng, addr + 0x04, 0x00000000); - nv_wo32(base->eng, addr + 0x08, 0x00000000); - nv_wo32(base->eng, addr + 0x0c, 0x00000000); - nv_wo32(base->eng, addr + 0x10, 0x00000000); - nv_wo32(base->eng, addr + 0x14, 0x00000000); - bar->flush(bar); - /* HW bug workaround: * * PFIFO will hang forever if the connected engines don't report @@ -141,8 +133,18 @@ nv50_fifo_context_detach(struct nouveau_object *parent, bool suspend, if (suspend) ret = -EBUSY; } - nv_wr32(priv, 0x00b860, me); + + if (ret == 0) { + nv_wo32(base->eng, addr + 0x00, 0x00000000); + nv_wo32(base->eng, addr + 0x04, 0x00000000); + nv_wo32(base->eng, addr + 0x08, 0x00000000); + nv_wo32(base->eng, addr + 0x0c, 0x00000000); + nv_wo32(base->eng, addr + 0x10, 0x00000000); + nv_wo32(base->eng, addr + 0x14, 0x00000000); + bar->flush(bar); + } + return ret; } @@ -194,10 +196,10 @@ nv50_fifo_chan_ctor_dma(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, 0x2000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_MPEG), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), &chan); *pobject = nv_object(chan); if (ret) return ret; @@ -247,10 +249,10 @@ nv50_fifo_chan_ctor_ind(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, 0x2000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_MPEG), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG), &chan); *pobject = nv_object(chan); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c index b4fd26d8f16..1eb1c512f50 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nv84.c @@ -95,14 +95,6 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend, return -EINVAL; } - nv_wo32(base->eng, addr + 0x00, 0x00000000); - nv_wo32(base->eng, addr + 0x04, 0x00000000); - nv_wo32(base->eng, addr + 0x08, 0x00000000); - nv_wo32(base->eng, addr + 0x0c, 0x00000000); - nv_wo32(base->eng, addr + 0x10, 0x00000000); - nv_wo32(base->eng, addr + 0x14, 0x00000000); - bar->flush(bar); - save = nv_mask(priv, 0x002520, 0x0000003f, 1 << engn); nv_wr32(priv, 0x0032fc, nv_gpuobj(base)->addr >> 12); done = nv_wait_ne(priv, 0x0032fc, 0xffffffff, 0xffffffff); @@ -112,6 +104,14 @@ nv84_fifo_context_detach(struct nouveau_object *parent, bool suspend, if (suspend) return -EBUSY; } + + nv_wo32(base->eng, addr + 0x00, 0x00000000); + nv_wo32(base->eng, addr + 0x04, 0x00000000); + nv_wo32(base->eng, addr + 0x08, 0x00000000); + nv_wo32(base->eng, addr + 0x0c, 0x00000000); + nv_wo32(base->eng, addr + 0x10, 0x00000000); + nv_wo32(base->eng, addr + 0x14, 0x00000000); + bar->flush(bar); return 0; } @@ -163,17 +163,17 @@ nv84_fifo_chan_ctor_dma(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, 0x2000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_MPEG) | - (1 << NVDEV_ENGINE_ME) | - (1 << NVDEV_ENGINE_VP) | - (1 << NVDEV_ENGINE_CRYPT) | - (1 << NVDEV_ENGINE_BSP) | - (1 << NVDEV_ENGINE_PPP) | - (1 << NVDEV_ENGINE_COPY0) | - (1 << NVDEV_ENGINE_UNK1C1), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG) | + (1ULL << NVDEV_ENGINE_ME) | + (1ULL << NVDEV_ENGINE_VP) | + (1ULL << NVDEV_ENGINE_CRYPT) | + (1ULL << NVDEV_ENGINE_BSP) | + (1ULL << NVDEV_ENGINE_PPP) | + (1ULL << NVDEV_ENGINE_COPY0) | + (1ULL << NVDEV_ENGINE_UNK1C1), &chan); *pobject = nv_object(chan); if (ret) return ret; @@ -225,17 +225,17 @@ nv84_fifo_chan_ctor_ind(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 0, 0xc00000, 0x2000, args->pushbuf, - (1 << NVDEV_ENGINE_DMAOBJ) | - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_MPEG) | - (1 << NVDEV_ENGINE_ME) | - (1 << NVDEV_ENGINE_VP) | - (1 << NVDEV_ENGINE_CRYPT) | - (1 << NVDEV_ENGINE_BSP) | - (1 << NVDEV_ENGINE_PPP) | - (1 << NVDEV_ENGINE_COPY0) | - (1 << NVDEV_ENGINE_UNK1C1), &chan); + (1ULL << NVDEV_ENGINE_DMAOBJ) | + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_MPEG) | + (1ULL << NVDEV_ENGINE_ME) | + (1ULL << NVDEV_ENGINE_VP) | + (1ULL << NVDEV_ENGINE_CRYPT) | + (1ULL << NVDEV_ENGINE_BSP) | + (1ULL << NVDEV_ENGINE_PPP) | + (1ULL << NVDEV_ENGINE_COPY0) | + (1ULL << NVDEV_ENGINE_UNK1C1), &chan); *pobject = nv_object(chan); if (ret) return ret; diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c index 6f21be60055..b4365dde185 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nvc0.c @@ -103,6 +103,9 @@ nvc0_fifo_context_attach(struct nouveau_object *parent, case NVDEV_ENGINE_GR : addr = 0x0210; break; case NVDEV_ENGINE_COPY0: addr = 0x0230; break; case NVDEV_ENGINE_COPY1: addr = 0x0240; break; + case NVDEV_ENGINE_BSP : addr = 0x0270; break; + case NVDEV_ENGINE_VP : addr = 0x0250; break; + case NVDEV_ENGINE_PPP : addr = 0x0260; break; default: return -EINVAL; } @@ -137,14 +140,13 @@ nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend, case NVDEV_ENGINE_GR : addr = 0x0210; break; case NVDEV_ENGINE_COPY0: addr = 0x0230; break; case NVDEV_ENGINE_COPY1: addr = 0x0240; break; + case NVDEV_ENGINE_BSP : addr = 0x0270; break; + case NVDEV_ENGINE_VP : addr = 0x0250; break; + case NVDEV_ENGINE_PPP : addr = 0x0260; break; default: return -EINVAL; } - nv_wo32(base, addr + 0x00, 0x00000000); - nv_wo32(base, addr + 0x04, 0x00000000); - bar->flush(bar); - nv_wr32(priv, 0x002634, chan->base.chid); if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { nv_error(priv, "channel %d kick timeout\n", chan->base.chid); @@ -152,6 +154,9 @@ nvc0_fifo_context_detach(struct nouveau_object *parent, bool suspend, return -EBUSY; } + nv_wo32(base, addr + 0x00, 0x00000000); + nv_wo32(base, addr + 0x04, 0x00000000); + bar->flush(bar); return 0; } @@ -175,10 +180,13 @@ nvc0_fifo_chan_ctor(struct nouveau_object *parent, ret = nouveau_fifo_channel_create(parent, engine, oclass, 1, priv->user.bar.offset, 0x1000, args->pushbuf, - (1 << NVDEV_ENGINE_SW) | - (1 << NVDEV_ENGINE_GR) | - (1 << NVDEV_ENGINE_COPY0) | - (1 << NVDEV_ENGINE_COPY1), &chan); + (1ULL << NVDEV_ENGINE_SW) | + (1ULL << NVDEV_ENGINE_GR) | + (1ULL << NVDEV_ENGINE_COPY0) | + (1ULL << NVDEV_ENGINE_COPY1) | + (1ULL << NVDEV_ENGINE_BSP) | + (1ULL << NVDEV_ENGINE_VP) | + (1ULL << NVDEV_ENGINE_PPP), &chan); *pobject = nv_object(chan); if (ret) return ret; @@ -494,7 +502,7 @@ nvc0_fifo_intr(struct nouveau_subdev *subdev) u32 stat = nv_rd32(priv, 0x002100) & mask; if (stat & 0x00000100) { - nv_info(priv, "unknown status 0x00000100\n"); + nv_warn(priv, "unknown status 0x00000100\n"); nv_wr32(priv, 0x002100, 0x00000100); stat &= ~0x00000100; } diff --git a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c index 36e81b6fafb..c930da99c2c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/fifo/nve0.c @@ -38,12 +38,12 @@ #include <engine/dmaobj.h> #include <engine/fifo.h> -#define _(a,b) { (a), ((1 << (a)) | (b)) } +#define _(a,b) { (a), ((1ULL << (a)) | (b)) } static const struct { - int subdev; - u32 mask; + u64 subdev; + u64 mask; } fifo_engine[] = { - _(NVDEV_ENGINE_GR , (1 << NVDEV_ENGINE_SW)), + _(NVDEV_ENGINE_GR , (1ULL << NVDEV_ENGINE_SW)), _(NVDEV_ENGINE_VP , 0), _(NVDEV_ENGINE_PPP , 0), _(NVDEV_ENGINE_BSP , 0), @@ -138,6 +138,9 @@ nve0_fifo_context_attach(struct nouveau_object *parent, case NVDEV_ENGINE_GR : case NVDEV_ENGINE_COPY0: case NVDEV_ENGINE_COPY1: addr = 0x0210; break; + case NVDEV_ENGINE_BSP : addr = 0x0270; break; + case NVDEV_ENGINE_VP : addr = 0x0250; break; + case NVDEV_ENGINE_PPP : addr = 0x0260; break; default: return -EINVAL; } @@ -172,14 +175,13 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, case NVDEV_ENGINE_GR : case NVDEV_ENGINE_COPY0: case NVDEV_ENGINE_COPY1: addr = 0x0210; break; + case NVDEV_ENGINE_BSP : addr = 0x0270; break; + case NVDEV_ENGINE_VP : addr = 0x0250; break; + case NVDEV_ENGINE_PPP : addr = 0x0260; break; default: return -EINVAL; } - nv_wo32(base, addr + 0x00, 0x00000000); - nv_wo32(base, addr + 0x04, 0x00000000); - bar->flush(bar); - nv_wr32(priv, 0x002634, chan->base.chid); if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { nv_error(priv, "channel %d kick timeout\n", chan->base.chid); @@ -187,6 +189,9 @@ nve0_fifo_context_detach(struct nouveau_object *parent, bool suspend, return -EBUSY; } + nv_wo32(base, addr + 0x00, 0x00000000); + nv_wo32(base, addr + 0x04, 0x00000000); + bar->flush(bar); return 0; } diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c index 61852824845..e30a9c5ff1f 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv04.c @@ -787,168 +787,168 @@ nv01_graph_mthd_bind_chroma(struct nouveau_object *object, u32 mthd, static struct nouveau_omthds nv03_graph_gdi_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_patt }, - { 0x0188, nv04_graph_mthd_bind_rop }, - { 0x018c, nv04_graph_mthd_bind_beta1 }, - { 0x0190, nv04_graph_mthd_bind_surf_dst }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_patt }, + { 0x0188, 0x0188, nv04_graph_mthd_bind_rop }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_beta1 }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_gdi_omthds[] = { - { 0x0188, nv04_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_beta4 }, - { 0x0198, nv04_graph_mthd_bind_surf2d }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0188, 0x0188, nv04_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv01_graph_blit_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_chroma }, - { 0x0188, nv01_graph_mthd_bind_clip }, - { 0x018c, nv01_graph_mthd_bind_patt }, - { 0x0190, nv04_graph_mthd_bind_rop }, - { 0x0194, nv04_graph_mthd_bind_beta1 }, - { 0x0198, nv04_graph_mthd_bind_surf_dst }, - { 0x019c, nv04_graph_mthd_bind_surf_src }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_clip }, + { 0x018c, 0x018c, nv01_graph_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_surf_dst }, + { 0x019c, 0x019c, nv04_graph_mthd_bind_surf_src }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_blit_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_chroma }, - { 0x0188, nv01_graph_mthd_bind_clip }, - { 0x018c, nv04_graph_mthd_bind_patt }, - { 0x0190, nv04_graph_mthd_bind_rop }, - { 0x0194, nv04_graph_mthd_bind_beta1 }, - { 0x0198, nv04_graph_mthd_bind_beta4 }, - { 0x019c, nv04_graph_mthd_bind_surf2d }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_clip }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_beta4 }, + { 0x019c, 0x019c, nv04_graph_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_iifc_omthds[] = { - { 0x0188, nv01_graph_mthd_bind_chroma }, - { 0x018c, nv01_graph_mthd_bind_clip }, - { 0x0190, nv04_graph_mthd_bind_patt }, - { 0x0194, nv04_graph_mthd_bind_rop }, - { 0x0198, nv04_graph_mthd_bind_beta1 }, - { 0x019c, nv04_graph_mthd_bind_beta4 }, - { 0x01a0, nv04_graph_mthd_bind_surf2d_swzsurf }, - { 0x03e4, nv04_graph_mthd_set_operation }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_chroma }, + { 0x018c, 0x018c, nv01_graph_mthd_bind_clip }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_patt }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_rop }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_beta1 }, + { 0x019c, 0x019c, nv04_graph_mthd_bind_beta4 }, + { 0x01a0, 0x01a0, nv04_graph_mthd_bind_surf2d_swzsurf }, + { 0x03e4, 0x03e4, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv01_graph_ifc_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_chroma }, - { 0x0188, nv01_graph_mthd_bind_clip }, - { 0x018c, nv01_graph_mthd_bind_patt }, - { 0x0190, nv04_graph_mthd_bind_rop }, - { 0x0194, nv04_graph_mthd_bind_beta1 }, - { 0x0198, nv04_graph_mthd_bind_surf_dst }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_clip }, + { 0x018c, 0x018c, nv01_graph_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_ifc_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_chroma }, - { 0x0188, nv01_graph_mthd_bind_clip }, - { 0x018c, nv04_graph_mthd_bind_patt }, - { 0x0190, nv04_graph_mthd_bind_rop }, - { 0x0194, nv04_graph_mthd_bind_beta1 }, - { 0x0198, nv04_graph_mthd_bind_beta4 }, - { 0x019c, nv04_graph_mthd_bind_surf2d }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_clip }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_patt }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_rop }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta1 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_beta4 }, + { 0x019c, 0x019c, nv04_graph_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv03_graph_sifc_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_chroma }, - { 0x0188, nv01_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_surf_dst }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_chroma }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_sifc_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_chroma }, - { 0x0188, nv04_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_beta4 }, - { 0x0198, nv04_graph_mthd_bind_surf2d }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_chroma }, + { 0x0188, 0x0188, nv04_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv03_graph_sifm_omthds[] = { - { 0x0188, nv01_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_surf_dst }, - { 0x0304, nv04_graph_mthd_set_operation }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_surf_dst }, + { 0x0304, 0x0304, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_sifm_omthds[] = { - { 0x0188, nv04_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_beta4 }, - { 0x0198, nv04_graph_mthd_bind_surf2d }, - { 0x0304, nv04_graph_mthd_set_operation }, + { 0x0188, 0x0188, nv04_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_surf2d }, + { 0x0304, 0x0304, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_surf3d_omthds[] = { - { 0x02f8, nv04_graph_mthd_surf3d_clip_h }, - { 0x02fc, nv04_graph_mthd_surf3d_clip_v }, + { 0x02f8, 0x02f8, nv04_graph_mthd_surf3d_clip_h }, + { 0x02fc, 0x02fc, nv04_graph_mthd_surf3d_clip_v }, {} }; static struct nouveau_omthds nv03_graph_ttri_omthds[] = { - { 0x0188, nv01_graph_mthd_bind_clip }, - { 0x018c, nv04_graph_mthd_bind_surf_color }, - { 0x0190, nv04_graph_mthd_bind_surf_zeta }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_clip }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_surf_color }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_surf_zeta }, {} }; static struct nouveau_omthds nv01_graph_prim_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_clip }, - { 0x0188, nv01_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_surf_dst }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_clip }, + { 0x0188, 0x0188, nv01_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_surf_dst }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; static struct nouveau_omthds nv04_graph_prim_omthds[] = { - { 0x0184, nv01_graph_mthd_bind_clip }, - { 0x0188, nv04_graph_mthd_bind_patt }, - { 0x018c, nv04_graph_mthd_bind_rop }, - { 0x0190, nv04_graph_mthd_bind_beta1 }, - { 0x0194, nv04_graph_mthd_bind_beta4 }, - { 0x0198, nv04_graph_mthd_bind_surf2d }, - { 0x02fc, nv04_graph_mthd_set_operation }, + { 0x0184, 0x0184, nv01_graph_mthd_bind_clip }, + { 0x0188, 0x0188, nv04_graph_mthd_bind_patt }, + { 0x018c, 0x018c, nv04_graph_mthd_bind_rop }, + { 0x0190, 0x0190, nv04_graph_mthd_bind_beta1 }, + { 0x0194, 0x0194, nv04_graph_mthd_bind_beta4 }, + { 0x0198, 0x0198, nv04_graph_mthd_bind_surf2d }, + { 0x02fc, 0x02fc, nv04_graph_mthd_set_operation }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c index 92521c89e77..5c0f843ea24 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv10.c @@ -570,11 +570,11 @@ nv17_graph_mthd_lma_enable(struct nouveau_object *object, u32 mthd, static struct nouveau_omthds nv17_celcius_omthds[] = { - { 0x1638, nv17_graph_mthd_lma_window }, - { 0x163c, nv17_graph_mthd_lma_window }, - { 0x1640, nv17_graph_mthd_lma_window }, - { 0x1644, nv17_graph_mthd_lma_window }, - { 0x1658, nv17_graph_mthd_lma_enable }, + { 0x1638, 0x1638, nv17_graph_mthd_lma_window }, + { 0x163c, 0x163c, nv17_graph_mthd_lma_window }, + { 0x1640, 0x1640, nv17_graph_mthd_lma_window }, + { 0x1644, 0x1644, nv17_graph_mthd_lma_window }, + { 0x1658, 0x1658, nv17_graph_mthd_lma_enable }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c index 8f3f619c4a7..5b20401bf91 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv20.c @@ -183,7 +183,7 @@ nv20_graph_tile_prog(struct nouveau_engine *engine, int i) nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + 4 * i); nv_wr32(priv, NV10_PGRAPH_RDI_DATA, tile->addr); - if (nv_device(engine)->card_type == NV_20) { + if (nv_device(engine)->chipset != 0x34) { nv_wr32(priv, NV20_PGRAPH_ZCOMP(i), tile->zcomp); nv_wr32(priv, NV10_PGRAPH_RDI_INDEX, 0x00ea0090 + 4 * i); nv_wr32(priv, NV10_PGRAPH_RDI_DATA, tile->zcomp); @@ -224,14 +224,14 @@ nv20_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); if (show) { - nv_info(priv, ""); + nv_error(priv, ""); nouveau_bitfield_print(nv10_graph_intr_name, show); printk(" nsource:"); nouveau_bitfield_print(nv04_graph_nsource, nsource); printk(" nstatus:"); nouveau_bitfield_print(nv10_graph_nstatus, nstatus); printk("\n"); - nv_info(priv, "ch %d/%d class 0x%04x mthd 0x%04x data 0x%08x\n", + nv_error(priv, "ch %d/%d class 0x%04x mthd 0x%04x data 0x%08x\n", chid, subc, class, mthd, data); } diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c index 8d0021049ec..8fc1221408b 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv40.c @@ -216,10 +216,10 @@ nv40_graph_tile_prog(struct nouveau_engine *engine, int i) switch (nv_device(priv)->chipset) { case 0x40: - case 0x41: /* guess */ + case 0x41: case 0x42: case 0x43: - case 0x45: /* guess */ + case 0x45: case 0x4e: nv_wr32(priv, NV20_PGRAPH_TSIZE(i), tile->pitch); nv_wr32(priv, NV20_PGRAPH_TLIMIT(i), tile->limit); @@ -227,6 +227,21 @@ nv40_graph_tile_prog(struct nouveau_engine *engine, int i) nv_wr32(priv, NV40_PGRAPH_TSIZE1(i), tile->pitch); nv_wr32(priv, NV40_PGRAPH_TLIMIT1(i), tile->limit); nv_wr32(priv, NV40_PGRAPH_TILE1(i), tile->addr); + switch (nv_device(priv)->chipset) { + case 0x40: + case 0x45: + nv_wr32(priv, NV20_PGRAPH_ZCOMP(i), tile->zcomp); + nv_wr32(priv, NV40_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + case 0x41: + case 0x42: + case 0x43: + nv_wr32(priv, NV41_PGRAPH_ZCOMP0(i), tile->zcomp); + nv_wr32(priv, NV41_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + default: + break; + } break; case 0x44: case 0x4a: @@ -235,18 +250,31 @@ nv40_graph_tile_prog(struct nouveau_engine *engine, int i) nv_wr32(priv, NV20_PGRAPH_TILE(i), tile->addr); break; case 0x46: + case 0x4c: case 0x47: case 0x49: case 0x4b: - case 0x4c: + case 0x63: case 0x67: - default: + case 0x68: nv_wr32(priv, NV47_PGRAPH_TSIZE(i), tile->pitch); nv_wr32(priv, NV47_PGRAPH_TLIMIT(i), tile->limit); nv_wr32(priv, NV47_PGRAPH_TILE(i), tile->addr); nv_wr32(priv, NV40_PGRAPH_TSIZE1(i), tile->pitch); nv_wr32(priv, NV40_PGRAPH_TLIMIT1(i), tile->limit); nv_wr32(priv, NV40_PGRAPH_TILE1(i), tile->addr); + switch (nv_device(priv)->chipset) { + case 0x47: + case 0x49: + case 0x4b: + nv_wr32(priv, NV47_PGRAPH_ZCOMP0(i), tile->zcomp); + nv_wr32(priv, NV47_PGRAPH_ZCOMP1(i), tile->zcomp); + break; + default: + break; + } + break; + default: break; } @@ -293,7 +321,7 @@ nv40_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, NV04_PGRAPH_FIFO, 0x00000001); if (show) { - nv_info(priv, ""); + nv_error(priv, ""); nouveau_bitfield_print(nv10_graph_intr_name, show); printk(" nsource:"); nouveau_bitfield_print(nv04_graph_nsource, nsource); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c index ab3b9dcaf47..b1c3d835b4c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nv50.c @@ -184,6 +184,65 @@ nv50_graph_tlb_flush(struct nouveau_engine *engine) return 0; } +static const struct nouveau_bitfield nv50_pgraph_status[] = { + { 0x00000001, "BUSY" }, /* set when any bit is set */ + { 0x00000002, "DISPATCH" }, + { 0x00000004, "UNK2" }, + { 0x00000008, "UNK3" }, + { 0x00000010, "UNK4" }, + { 0x00000020, "UNK5" }, + { 0x00000040, "M2MF" }, + { 0x00000080, "UNK7" }, + { 0x00000100, "CTXPROG" }, + { 0x00000200, "VFETCH" }, + { 0x00000400, "CCACHE_UNK4" }, + { 0x00000800, "STRMOUT_GSCHED_UNK5" }, + { 0x00001000, "UNK14XX" }, + { 0x00002000, "UNK24XX_CSCHED" }, + { 0x00004000, "UNK1CXX" }, + { 0x00008000, "CLIPID" }, + { 0x00010000, "ZCULL" }, + { 0x00020000, "ENG2D" }, + { 0x00040000, "UNK34XX" }, + { 0x00080000, "TPRAST" }, + { 0x00100000, "TPROP" }, + { 0x00200000, "TEX" }, + { 0x00400000, "TPVP" }, + { 0x00800000, "MP" }, + { 0x01000000, "ROP" }, + {} +}; + +static const char *const nv50_pgraph_vstatus_0[] = { + "VFETCH", "CCACHE", "UNK4", "UNK5", "GSCHED", "STRMOUT", "UNK14XX", NULL +}; + +static const char *const nv50_pgraph_vstatus_1[] = { + "TPRAST", "TPROP", "TEXTURE", "TPVP", "MP", NULL +}; + +static const char *const nv50_pgraph_vstatus_2[] = { + "UNK24XX", "CSCHED", "UNK1CXX", "CLIPID", "ZCULL", "ENG2D", "UNK34XX", + "ROP", NULL +}; + +static void nouveau_pgraph_vstatus_print(struct nv50_graph_priv *priv, int r, + const char *const units[], u32 status) +{ + int i; + + nv_error(priv, "PGRAPH_VSTATUS%d: 0x%08x", r, status); + + for (i = 0; units[i] && status; i++) { + if ((status & 7) == 1) + pr_cont(" %s", units[i]); + status >>= 3; + } + if (status) + pr_cont(" (invalid: 0x%x)", status); + pr_cont("\n"); +} + static int nv84_graph_tlb_flush(struct nouveau_engine *engine) { @@ -219,10 +278,19 @@ nv84_graph_tlb_flush(struct nouveau_engine *engine) !(timeout = ptimer->read(ptimer) - start > 2000000000)); if (timeout) { - nv_error(priv, "PGRAPH TLB flush idle timeout fail: " - "0x%08x 0x%08x 0x%08x 0x%08x\n", - nv_rd32(priv, 0x400700), nv_rd32(priv, 0x400380), - nv_rd32(priv, 0x400384), nv_rd32(priv, 0x400388)); + nv_error(priv, "PGRAPH TLB flush idle timeout fail\n"); + + tmp = nv_rd32(priv, 0x400700); + nv_error(priv, "PGRAPH_STATUS : 0x%08x", tmp); + nouveau_bitfield_print(nv50_pgraph_status, tmp); + pr_cont("\n"); + + nouveau_pgraph_vstatus_print(priv, 0, nv50_pgraph_vstatus_0, + nv_rd32(priv, 0x400380)); + nouveau_pgraph_vstatus_print(priv, 1, nv50_pgraph_vstatus_1, + nv_rd32(priv, 0x400384)); + nouveau_pgraph_vstatus_print(priv, 2, nv50_pgraph_vstatus_2, + nv_rd32(priv, 0x400388)); } nv50_vm_flush_engine(&engine->base, 0x00); @@ -453,13 +521,13 @@ nv50_priv_tp_trap(struct nv50_graph_priv *priv, int type, u32 ustatus_old, } if (ustatus) { if (display) - nv_info(priv, "%s - TP%d: Unhandled ustatus 0x%08x\n", name, i, ustatus); + nv_error(priv, "%s - TP%d: Unhandled ustatus 0x%08x\n", name, i, ustatus); } nv_wr32(priv, ustatus_addr, 0xc0000000); } if (!tps && display) - nv_info(priv, "%s - No TPs claiming errors?\n", name); + nv_warn(priv, "%s - No TPs claiming errors?\n", name); } static int @@ -718,13 +786,12 @@ nv50_graph_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x400500, 0x00010001); if (show) { - nv_info(priv, ""); + nv_error(priv, ""); nouveau_bitfield_print(nv50_graph_intr_name, show); printk("\n"); nv_error(priv, "ch %d [0x%010llx] subc %d class 0x%04x " "mthd 0x%04x data 0x%08x\n", chid, (u64)inst << 12, subc, class, mthd, data); - nv50_fb_trap(nouveau_fb(priv), 1); } if (nv_rd32(priv, 0x400824) & (1 << 31)) diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c index c62f2d0f5f0..47a02081d70 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nvc0.c @@ -814,7 +814,7 @@ nvc0_graph_init_ctxctl(struct nvc0_graph_priv *priv) nv_wr32(priv, 0x41a100, 0x00000002); nv_wr32(priv, 0x409100, 0x00000002); if (!nv_wait(priv, 0x409800, 0x00000001, 0x00000001)) - nv_info(priv, "0x409800 wait failed\n"); + nv_warn(priv, "0x409800 wait failed\n"); nv_wr32(priv, 0x409840, 0xffffffff); nv_wr32(priv, 0x409500, 0x7fffffff); diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/regs.h b/drivers/gpu/drm/nouveau/core/engine/graph/regs.h index 9c715a25cec..fde8e24415e 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/regs.h +++ b/drivers/gpu/drm/nouveau/core/engine/graph/regs.h @@ -205,6 +205,7 @@ #define NV20_PGRAPH_TSIZE(i) (0x00400908 + (i*16)) #define NV20_PGRAPH_TSTATUS(i) (0x0040090C + (i*16)) #define NV20_PGRAPH_ZCOMP(i) (0x00400980 + 4*(i)) +#define NV41_PGRAPH_ZCOMP0(i) (0x004009c0 + 4*(i)) #define NV10_PGRAPH_TILE(i) (0x00400B00 + (i*16)) #define NV10_PGRAPH_TLIMIT(i) (0x00400B04 + (i*16)) #define NV10_PGRAPH_TSIZE(i) (0x00400B08 + (i*16)) @@ -216,6 +217,7 @@ #define NV47_PGRAPH_TSTATUS(i) (0x00400D0C + (i*16)) #define NV04_PGRAPH_V_RAM 0x00400D40 #define NV04_PGRAPH_W_RAM 0x00400D80 +#define NV47_PGRAPH_ZCOMP0(i) (0x00400e00 + 4*(i)) #define NV10_PGRAPH_COMBINER0_IN_ALPHA 0x00400E40 #define NV10_PGRAPH_COMBINER1_IN_ALPHA 0x00400E44 #define NV10_PGRAPH_COMBINER0_IN_RGB 0x00400E48 @@ -261,9 +263,12 @@ #define NV04_PGRAPH_DMA_B_OFFSET 0x00401098 #define NV04_PGRAPH_DMA_B_SIZE 0x0040109C #define NV04_PGRAPH_DMA_B_Y_SIZE 0x004010A0 +#define NV47_PGRAPH_ZCOMP1(i) (0x004068c0 + 4*(i)) #define NV40_PGRAPH_TILE1(i) (0x00406900 + (i*16)) #define NV40_PGRAPH_TLIMIT1(i) (0x00406904 + (i*16)) #define NV40_PGRAPH_TSIZE1(i) (0x00406908 + (i*16)) #define NV40_PGRAPH_TSTATUS1(i) (0x0040690C + (i*16)) +#define NV40_PGRAPH_ZCOMP1(i) (0x00406980 + 4*(i)) +#define NV41_PGRAPH_ZCOMP1(i) (0x004069c0 + 4*(i)) #endif diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c index 1f394a2629e..9fd86375f4c 100644 --- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c +++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv31.c @@ -121,9 +121,9 @@ nv31_mpeg_ofuncs = { static struct nouveau_omthds nv31_mpeg_omthds[] = { - { 0x0190, nv31_mpeg_mthd_dma }, - { 0x01a0, nv31_mpeg_mthd_dma }, - { 0x01b0, nv31_mpeg_mthd_dma }, + { 0x0190, 0x0190, nv31_mpeg_mthd_dma }, + { 0x01a0, 0x01a0, nv31_mpeg_mthd_dma }, + { 0x01b0, 0x01b0, nv31_mpeg_mthd_dma }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c index 8678a9996d5..bc7d12b30fc 100644 --- a/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/mpeg/nv50.c @@ -157,7 +157,6 @@ nv50_mpeg_intr(struct nouveau_subdev *subdev) nv_wr32(priv, 0x00b100, stat); nv_wr32(priv, 0x00b230, 0x00000001); - nv50_fb_trap(nouveau_fb(priv), 1); } static void diff --git a/drivers/gpu/drm/nouveau/core/engine/ppp/nv98.c b/drivers/gpu/drm/nouveau/core/engine/ppp/nv98.c index 50e7e0da198..5a5b2a773ed 100644 --- a/drivers/gpu/drm/nouveau/core/engine/ppp/nv98.c +++ b/drivers/gpu/drm/nouveau/core/engine/ppp/nv98.c @@ -22,18 +22,18 @@ * Authors: Ben Skeggs */ -#include <core/os.h> -#include <core/class.h> +#include <core/engine.h> #include <core/engctx.h> +#include <core/class.h> #include <engine/ppp.h> struct nv98_ppp_priv { - struct nouveau_ppp base; + struct nouveau_engine base; }; struct nv98_ppp_chan { - struct nouveau_ppp_chan base; + struct nouveau_engctx base; }; /******************************************************************************* @@ -49,61 +49,16 @@ nv98_ppp_sclass[] = { * PPPP context ******************************************************************************/ -static int -nv98_ppp_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv98_ppp_chan *priv; - int ret; - - ret = nouveau_ppp_context_create(parent, engine, oclass, NULL, - 0, 0, 0, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -static void -nv98_ppp_context_dtor(struct nouveau_object *object) -{ - struct nv98_ppp_chan *priv = (void *)object; - nouveau_ppp_context_destroy(&priv->base); -} - -static int -nv98_ppp_context_init(struct nouveau_object *object) -{ - struct nv98_ppp_chan *priv = (void *)object; - int ret; - - ret = nouveau_ppp_context_init(&priv->base); - if (ret) - return ret; - - return 0; -} - -static int -nv98_ppp_context_fini(struct nouveau_object *object, bool suspend) -{ - struct nv98_ppp_chan *priv = (void *)object; - return nouveau_ppp_context_fini(&priv->base, suspend); -} - static struct nouveau_oclass nv98_ppp_cclass = { .handle = NV_ENGCTX(PPP, 0x98), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv98_ppp_context_ctor, - .dtor = nv98_ppp_context_dtor, - .init = nv98_ppp_context_init, - .fini = nv98_ppp_context_fini, - .rd32 = _nouveau_ppp_context_rd32, - .wr32 = _nouveau_ppp_context_wr32, + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, }, }; @@ -111,11 +66,6 @@ nv98_ppp_cclass = { * PPPP engine/subdev functions ******************************************************************************/ -static void -nv98_ppp_intr(struct nouveau_subdev *subdev) -{ -} - static int nv98_ppp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -124,52 +74,25 @@ nv98_ppp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv98_ppp_priv *priv; int ret; - ret = nouveau_ppp_create(parent, engine, oclass, &priv); + ret = nouveau_engine_create(parent, engine, oclass, true, + "PPPP", "ppp", &priv); *pobject = nv_object(priv); if (ret) return ret; nv_subdev(priv)->unit = 0x00400002; - nv_subdev(priv)->intr = nv98_ppp_intr; nv_engine(priv)->cclass = &nv98_ppp_cclass; nv_engine(priv)->sclass = nv98_ppp_sclass; return 0; } -static void -nv98_ppp_dtor(struct nouveau_object *object) -{ - struct nv98_ppp_priv *priv = (void *)object; - nouveau_ppp_destroy(&priv->base); -} - -static int -nv98_ppp_init(struct nouveau_object *object) -{ - struct nv98_ppp_priv *priv = (void *)object; - int ret; - - ret = nouveau_ppp_init(&priv->base); - if (ret) - return ret; - - return 0; -} - -static int -nv98_ppp_fini(struct nouveau_object *object, bool suspend) -{ - struct nv98_ppp_priv *priv = (void *)object; - return nouveau_ppp_fini(&priv->base, suspend); -} - struct nouveau_oclass nv98_ppp_oclass = { .handle = NV_ENGINE(PPP, 0x98), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv98_ppp_ctor, - .dtor = nv98_ppp_dtor, - .init = nv98_ppp_init, - .fini = nv98_ppp_fini, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c new file mode 100644 index 00000000000..ebf0d860e2d --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/ppp/nvc0.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Maarten Lankhorst + * + * 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: Maarten Lankhorst + */ + +#include <core/falcon.h> + +#include <engine/ppp.h> + +struct nvc0_ppp_priv { + struct nouveau_falcon base; +}; + +/******************************************************************************* + * PPP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nvc0_ppp_sclass[] = { + { 0x90b3, &nouveau_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PPPP context + ******************************************************************************/ + +static struct nouveau_oclass +nvc0_ppp_cclass = { + .handle = NV_ENGCTX(PPP, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PPPP engine/subdev functions + ******************************************************************************/ + +static int +nvc0_ppp_init(struct nouveau_object *object) +{ + struct nvc0_ppp_priv *priv = (void *)object; + int ret; + + ret = nouveau_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x086010, 0x0000fff2); + nv_wr32(priv, 0x08601c, 0x0000fff2); + return 0; +} + +static int +nvc0_ppp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvc0_ppp_priv *priv; + int ret; + + ret = nouveau_falcon_create(parent, engine, oclass, 0x086000, true, + "PPPP", "ppp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00000002; + nv_engine(priv)->cclass = &nvc0_ppp_cclass; + nv_engine(priv)->sclass = nvc0_ppp_sclass; + return 0; +} + +struct nouveau_oclass +nvc0_ppp_oclass = { + .handle = NV_ENGINE(PPP, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_ppp_ctor, + .dtor = _nouveau_falcon_dtor, + .init = nvc0_ppp_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv04.c b/drivers/gpu/drm/nouveau/core/engine/software/nv04.c index 3ca4c3aa90b..2a859a31c30 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv04.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv04.c @@ -63,8 +63,8 @@ nv04_software_flip(struct nouveau_object *object, u32 mthd, static struct nouveau_omthds nv04_software_omthds[] = { - { 0x0150, nv04_software_set_ref }, - { 0x0500, nv04_software_flip }, + { 0x0150, 0x0150, nv04_software_set_ref }, + { 0x0500, 0x0500, nv04_software_flip }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv10.c b/drivers/gpu/drm/nouveau/core/engine/software/nv10.c index 6e699afbfdb..a019364b1e1 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv10.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv10.c @@ -52,7 +52,7 @@ nv10_software_flip(struct nouveau_object *object, u32 mthd, static struct nouveau_omthds nv10_software_omthds[] = { - { 0x0500, nv10_software_flip }, + { 0x0500, 0x0500, nv10_software_flip }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c index a2edcd38544..b0e7e1c01ce 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nv50.c @@ -117,11 +117,11 @@ nv50_software_mthd_flip(struct nouveau_object *object, u32 mthd, static struct nouveau_omthds nv50_software_omthds[] = { - { 0x018c, nv50_software_mthd_dma_vblsem }, - { 0x0400, nv50_software_mthd_vblsem_offset }, - { 0x0404, nv50_software_mthd_vblsem_value }, - { 0x0408, nv50_software_mthd_vblsem_release }, - { 0x0500, nv50_software_mthd_flip }, + { 0x018c, 0x018c, nv50_software_mthd_dma_vblsem }, + { 0x0400, 0x0400, nv50_software_mthd_vblsem_offset }, + { 0x0404, 0x0404, nv50_software_mthd_vblsem_value }, + { 0x0408, 0x0408, nv50_software_mthd_vblsem_release }, + { 0x0500, 0x0500, nv50_software_mthd_flip }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c index b7b0d7e330d..282a1cd1bc2 100644 --- a/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/engine/software/nvc0.c @@ -99,11 +99,11 @@ nvc0_software_mthd_flip(struct nouveau_object *object, u32 mthd, static struct nouveau_omthds nvc0_software_omthds[] = { - { 0x0400, nvc0_software_mthd_vblsem_offset }, - { 0x0404, nvc0_software_mthd_vblsem_offset }, - { 0x0408, nvc0_software_mthd_vblsem_value }, - { 0x040c, nvc0_software_mthd_vblsem_release }, - { 0x0500, nvc0_software_mthd_flip }, + { 0x0400, 0x0400, nvc0_software_mthd_vblsem_offset }, + { 0x0404, 0x0404, nvc0_software_mthd_vblsem_offset }, + { 0x0408, 0x0408, nvc0_software_mthd_vblsem_value }, + { 0x040c, 0x040c, nvc0_software_mthd_vblsem_release }, + { 0x0500, 0x0500, nvc0_software_mthd_flip }, {} }; diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c b/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c index dd23c80e540..261cd96e695 100644 --- a/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nv84.c @@ -22,18 +22,13 @@ * Authors: Ben Skeggs */ -#include <core/os.h> -#include <core/class.h> #include <core/engctx.h> +#include <core/class.h> #include <engine/vp.h> struct nv84_vp_priv { - struct nouveau_vp base; -}; - -struct nv84_vp_chan { - struct nouveau_vp_chan base; + struct nouveau_engine base; }; /******************************************************************************* @@ -49,61 +44,16 @@ nv84_vp_sclass[] = { * PVP context ******************************************************************************/ -static int -nv84_vp_context_ctor(struct nouveau_object *parent, - struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nv84_vp_chan *priv; - int ret; - - ret = nouveau_vp_context_create(parent, engine, oclass, NULL, - 0, 0, 0, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - return 0; -} - -static void -nv84_vp_context_dtor(struct nouveau_object *object) -{ - struct nv84_vp_chan *priv = (void *)object; - nouveau_vp_context_destroy(&priv->base); -} - -static int -nv84_vp_context_init(struct nouveau_object *object) -{ - struct nv84_vp_chan *priv = (void *)object; - int ret; - - ret = nouveau_vp_context_init(&priv->base); - if (ret) - return ret; - - return 0; -} - -static int -nv84_vp_context_fini(struct nouveau_object *object, bool suspend) -{ - struct nv84_vp_chan *priv = (void *)object; - return nouveau_vp_context_fini(&priv->base, suspend); -} - static struct nouveau_oclass nv84_vp_cclass = { .handle = NV_ENGCTX(VP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv84_vp_context_ctor, - .dtor = nv84_vp_context_dtor, - .init = nv84_vp_context_init, - .fini = nv84_vp_context_fini, - .rd32 = _nouveau_vp_context_rd32, - .wr32 = _nouveau_vp_context_wr32, + .ctor = _nouveau_engctx_ctor, + .dtor = _nouveau_engctx_dtor, + .init = _nouveau_engctx_init, + .fini = _nouveau_engctx_fini, + .rd32 = _nouveau_engctx_rd32, + .wr32 = _nouveau_engctx_wr32, }, }; @@ -111,11 +61,6 @@ nv84_vp_cclass = { * PVP engine/subdev functions ******************************************************************************/ -static void -nv84_vp_intr(struct nouveau_subdev *subdev) -{ -} - static int nv84_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, @@ -124,52 +69,25 @@ nv84_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nv84_vp_priv *priv; int ret; - ret = nouveau_vp_create(parent, engine, oclass, &priv); + ret = nouveau_engine_create(parent, engine, oclass, true, + "PVP", "vp", &priv); *pobject = nv_object(priv); if (ret) return ret; nv_subdev(priv)->unit = 0x01020000; - nv_subdev(priv)->intr = nv84_vp_intr; nv_engine(priv)->cclass = &nv84_vp_cclass; nv_engine(priv)->sclass = nv84_vp_sclass; return 0; } -static void -nv84_vp_dtor(struct nouveau_object *object) -{ - struct nv84_vp_priv *priv = (void *)object; - nouveau_vp_destroy(&priv->base); -} - -static int -nv84_vp_init(struct nouveau_object *object) -{ - struct nv84_vp_priv *priv = (void *)object; - int ret; - - ret = nouveau_vp_init(&priv->base); - if (ret) - return ret; - - return 0; -} - -static int -nv84_vp_fini(struct nouveau_object *object, bool suspend) -{ - struct nv84_vp_priv *priv = (void *)object; - return nouveau_vp_fini(&priv->base, suspend); -} - struct nouveau_oclass nv84_vp_oclass = { .handle = NV_ENGINE(VP, 0x84), .ofuncs = &(struct nouveau_ofuncs) { .ctor = nv84_vp_ctor, - .dtor = nv84_vp_dtor, - .init = nv84_vp_init, - .fini = nv84_vp_fini, + .dtor = _nouveau_engine_dtor, + .init = _nouveau_engine_init, + .fini = _nouveau_engine_fini, }, }; diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c b/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c new file mode 100644 index 00000000000..f761949d703 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nvc0.c @@ -0,0 +1,110 @@ +/* + * Copyright 2012 Maarten Lankhorst + * + * 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: Maarten Lankhorst + */ + +#include <core/falcon.h> + +#include <engine/vp.h> + +struct nvc0_vp_priv { + struct nouveau_falcon base; +}; + +/******************************************************************************* + * VP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nvc0_vp_sclass[] = { + { 0x90b2, &nouveau_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PVP context + ******************************************************************************/ + +static struct nouveau_oclass +nvc0_vp_cclass = { + .handle = NV_ENGCTX(VP, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PVP engine/subdev functions + ******************************************************************************/ + +static int +nvc0_vp_init(struct nouveau_object *object) +{ + struct nvc0_vp_priv *priv = (void *)object; + int ret; + + ret = nouveau_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x085010, 0x0000fff2); + nv_wr32(priv, 0x08501c, 0x0000fff2); + return 0; +} + +static int +nvc0_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nvc0_vp_priv *priv; + int ret; + + ret = nouveau_falcon_create(parent, engine, oclass, 0x085000, true, + "PVP", "vp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00020000; + nv_engine(priv)->cclass = &nvc0_vp_cclass; + nv_engine(priv)->sclass = nvc0_vp_sclass; + return 0; +} + +struct nouveau_oclass +nvc0_vp_oclass = { + .handle = NV_ENGINE(VP, 0xc0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nvc0_vp_ctor, + .dtor = _nouveau_falcon_dtor, + .init = nvc0_vp_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c b/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c new file mode 100644 index 00000000000..2384ce5dbe1 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/engine/vp/nve0.c @@ -0,0 +1,110 @@ +/* + * 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/falcon.h> + +#include <engine/vp.h> + +struct nve0_vp_priv { + struct nouveau_falcon base; +}; + +/******************************************************************************* + * VP object classes + ******************************************************************************/ + +static struct nouveau_oclass +nve0_vp_sclass[] = { + { 0x95b2, &nouveau_object_ofuncs }, + {}, +}; + +/******************************************************************************* + * PVP context + ******************************************************************************/ + +static struct nouveau_oclass +nve0_vp_cclass = { + .handle = NV_ENGCTX(VP, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = _nouveau_falcon_context_ctor, + .dtor = _nouveau_falcon_context_dtor, + .init = _nouveau_falcon_context_init, + .fini = _nouveau_falcon_context_fini, + .rd32 = _nouveau_falcon_context_rd32, + .wr32 = _nouveau_falcon_context_wr32, + }, +}; + +/******************************************************************************* + * PVP engine/subdev functions + ******************************************************************************/ + +static int +nve0_vp_init(struct nouveau_object *object) +{ + struct nve0_vp_priv *priv = (void *)object; + int ret; + + ret = nouveau_falcon_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x085010, 0x0000fff2); + nv_wr32(priv, 0x08501c, 0x0000fff2); + return 0; +} + +static int +nve0_vp_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nve0_vp_priv *priv; + int ret; + + ret = nouveau_falcon_create(parent, engine, oclass, 0x085000, true, + "PVP", "vp", &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + nv_subdev(priv)->unit = 0x00020000; + nv_engine(priv)->cclass = &nve0_vp_cclass; + nv_engine(priv)->sclass = nve0_vp_sclass; + return 0; +} + +struct nouveau_oclass +nve0_vp_oclass = { + .handle = NV_ENGINE(VP, 0xe0), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nve0_vp_ctor, + .dtor = _nouveau_falcon_dtor, + .init = nve0_vp_init, + .fini = _nouveau_falcon_fini, + .rd32 = _nouveau_falcon_rd32, + .wr32 = _nouveau_falcon_wr32, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/include/core/class.h b/drivers/gpu/drm/nouveau/core/include/core/class.h index 6180ae9800f..47c4b3a5bd3 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/class.h +++ b/drivers/gpu/drm/nouveau/core/include/core/class.h @@ -23,6 +23,7 @@ #define NV_DEVICE_DISABLE_COPY0 0x0000008000000000ULL #define NV_DEVICE_DISABLE_COPY1 0x0000010000000000ULL #define NV_DEVICE_DISABLE_UNK1C1 0x0000020000000000ULL +#define NV_DEVICE_DISABLE_VENC 0x0000040000000000ULL struct nv_device_class { u64 device; /* device identifier, ~0 for client default */ @@ -52,11 +53,49 @@ struct nv_device_class { #define NV_DMA_ACCESS_WR 0x00000200 #define NV_DMA_ACCESS_RDWR 0x00000300 +/* NV50:NVC0 */ +#define NV50_DMA_CONF0_ENABLE 0x80000000 +#define NV50_DMA_CONF0_PRIV 0x00300000 +#define NV50_DMA_CONF0_PRIV_VM 0x00000000 +#define NV50_DMA_CONF0_PRIV_US 0x00100000 +#define NV50_DMA_CONF0_PRIV__S 0x00200000 +#define NV50_DMA_CONF0_PART 0x00030000 +#define NV50_DMA_CONF0_PART_VM 0x00000000 +#define NV50_DMA_CONF0_PART_256 0x00010000 +#define NV50_DMA_CONF0_PART_1KB 0x00020000 +#define NV50_DMA_CONF0_COMP 0x00000180 +#define NV50_DMA_CONF0_COMP_NONE 0x00000000 +#define NV50_DMA_CONF0_COMP_VM 0x00000180 +#define NV50_DMA_CONF0_TYPE 0x0000007f +#define NV50_DMA_CONF0_TYPE_LINEAR 0x00000000 +#define NV50_DMA_CONF0_TYPE_VM 0x0000007f + +/* NVC0:NVD9 */ +#define NVC0_DMA_CONF0_ENABLE 0x80000000 +#define NVC0_DMA_CONF0_PRIV 0x00300000 +#define NVC0_DMA_CONF0_PRIV_VM 0x00000000 +#define NVC0_DMA_CONF0_PRIV_US 0x00100000 +#define NVC0_DMA_CONF0_PRIV__S 0x00200000 +#define NVC0_DMA_CONF0_UNKN /* PART? */ 0x00030000 +#define NVC0_DMA_CONF0_TYPE 0x000000ff +#define NVC0_DMA_CONF0_TYPE_LINEAR 0x00000000 +#define NVC0_DMA_CONF0_TYPE_VM 0x000000ff + +/* NVD9- */ +#define NVD0_DMA_CONF0_ENABLE 0x80000000 +#define NVD0_DMA_CONF0_PAGE 0x00000400 +#define NVD0_DMA_CONF0_PAGE_LP 0x00000000 +#define NVD0_DMA_CONF0_PAGE_SP 0x00000400 +#define NVD0_DMA_CONF0_TYPE 0x000000ff +#define NVD0_DMA_CONF0_TYPE_LINEAR 0x00000000 +#define NVD0_DMA_CONF0_TYPE_VM 0x000000ff + struct nv_dma_class { u32 flags; u32 pad0; u64 start; u64 limit; + u32 conf0; }; /* DMA FIFO channel classes @@ -115,4 +154,190 @@ struct nve0_channel_ind_class { u32 engine; }; +/* 5070: NV50_DISP + * 8270: NV84_DISP + * 8370: NVA0_DISP + * 8870: NV94_DISP + * 8570: NVA3_DISP + * 9070: NVD0_DISP + * 9170: NVE0_DISP + */ + +#define NV50_DISP_CLASS 0x00005070 +#define NV84_DISP_CLASS 0x00008270 +#define NVA0_DISP_CLASS 0x00008370 +#define NV94_DISP_CLASS 0x00008870 +#define NVA3_DISP_CLASS 0x00008570 +#define NVD0_DISP_CLASS 0x00009070 +#define NVE0_DISP_CLASS 0x00009170 + +#define NV50_DISP_SOR_MTHD 0x00010000 +#define NV50_DISP_SOR_MTHD_TYPE 0x0000f000 +#define NV50_DISP_SOR_MTHD_HEAD 0x00000018 +#define NV50_DISP_SOR_MTHD_LINK 0x00000004 +#define NV50_DISP_SOR_MTHD_OR 0x00000003 + +#define NV50_DISP_SOR_PWR 0x00010000 +#define NV50_DISP_SOR_PWR_STATE 0x00000001 +#define NV50_DISP_SOR_PWR_STATE_ON 0x00000001 +#define NV50_DISP_SOR_PWR_STATE_OFF 0x00000000 +#define NVA3_DISP_SOR_HDA_ELD 0x00010100 +#define NV84_DISP_SOR_HDMI_PWR 0x00012000 +#define NV84_DISP_SOR_HDMI_PWR_STATE 0x40000000 +#define NV84_DISP_SOR_HDMI_PWR_STATE_OFF 0x00000000 +#define NV84_DISP_SOR_HDMI_PWR_STATE_ON 0x40000000 +#define NV84_DISP_SOR_HDMI_PWR_MAX_AC_PACKET 0x001f0000 +#define NV84_DISP_SOR_HDMI_PWR_REKEY 0x0000007f +#define NV50_DISP_SOR_LVDS_SCRIPT 0x00013000 +#define NV50_DISP_SOR_LVDS_SCRIPT_ID 0x0000ffff +#define NV94_DISP_SOR_DP_TRAIN 0x00016000 +#define NV94_DISP_SOR_DP_TRAIN_OP 0xf0000000 +#define NV94_DISP_SOR_DP_TRAIN_OP_PATTERN 0x00000000 +#define NV94_DISP_SOR_DP_TRAIN_OP_INIT 0x10000000 +#define NV94_DISP_SOR_DP_TRAIN_OP_FINI 0x20000000 +#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD 0x00000001 +#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF 0x00000000 +#define NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON 0x00000001 +#define NV94_DISP_SOR_DP_TRAIN_PATTERN 0x00000003 +#define NV94_DISP_SOR_DP_TRAIN_PATTERN_DISABLED 0x00000000 +#define NV94_DISP_SOR_DP_LNKCTL 0x00016040 +#define NV94_DISP_SOR_DP_LNKCTL_FRAME 0x80000000 +#define NV94_DISP_SOR_DP_LNKCTL_FRAME_STD 0x00000000 +#define NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH 0x80000000 +#define NV94_DISP_SOR_DP_LNKCTL_WIDTH 0x00001f00 +#define NV94_DISP_SOR_DP_LNKCTL_COUNT 0x00000007 +#define NV94_DISP_SOR_DP_DRVCTL(l) ((l) * 0x40 + 0x00016100) +#define NV94_DISP_SOR_DP_DRVCTL_VS 0x00000300 +#define NV94_DISP_SOR_DP_DRVCTL_PE 0x00000003 + +#define NV50_DISP_DAC_MTHD 0x00020000 +#define NV50_DISP_DAC_MTHD_TYPE 0x0000f000 +#define NV50_DISP_DAC_MTHD_OR 0x00000003 + +#define NV50_DISP_DAC_PWR 0x00020000 +#define NV50_DISP_DAC_PWR_HSYNC 0x00000001 +#define NV50_DISP_DAC_PWR_HSYNC_ON 0x00000000 +#define NV50_DISP_DAC_PWR_HSYNC_LO 0x00000001 +#define NV50_DISP_DAC_PWR_VSYNC 0x00000004 +#define NV50_DISP_DAC_PWR_VSYNC_ON 0x00000000 +#define NV50_DISP_DAC_PWR_VSYNC_LO 0x00000004 +#define NV50_DISP_DAC_PWR_DATA 0x00000010 +#define NV50_DISP_DAC_PWR_DATA_ON 0x00000000 +#define NV50_DISP_DAC_PWR_DATA_LO 0x00000010 +#define NV50_DISP_DAC_PWR_STATE 0x00000040 +#define NV50_DISP_DAC_PWR_STATE_ON 0x00000000 +#define NV50_DISP_DAC_PWR_STATE_OFF 0x00000040 +#define NV50_DISP_DAC_LOAD 0x0002000c +#define NV50_DISP_DAC_LOAD_VALUE 0x00000007 + +struct nv50_display_class { +}; + +/* 507a: NV50_DISP_CURS + * 827a: NV84_DISP_CURS + * 837a: NVA0_DISP_CURS + * 887a: NV94_DISP_CURS + * 857a: NVA3_DISP_CURS + * 907a: NVD0_DISP_CURS + * 917a: NVE0_DISP_CURS + */ + +#define NV50_DISP_CURS_CLASS 0x0000507a +#define NV84_DISP_CURS_CLASS 0x0000827a +#define NVA0_DISP_CURS_CLASS 0x0000837a +#define NV94_DISP_CURS_CLASS 0x0000887a +#define NVA3_DISP_CURS_CLASS 0x0000857a +#define NVD0_DISP_CURS_CLASS 0x0000907a +#define NVE0_DISP_CURS_CLASS 0x0000917a + +struct nv50_display_curs_class { + u32 head; +}; + +/* 507b: NV50_DISP_OIMM + * 827b: NV84_DISP_OIMM + * 837b: NVA0_DISP_OIMM + * 887b: NV94_DISP_OIMM + * 857b: NVA3_DISP_OIMM + * 907b: NVD0_DISP_OIMM + * 917b: NVE0_DISP_OIMM + */ + +#define NV50_DISP_OIMM_CLASS 0x0000507b +#define NV84_DISP_OIMM_CLASS 0x0000827b +#define NVA0_DISP_OIMM_CLASS 0x0000837b +#define NV94_DISP_OIMM_CLASS 0x0000887b +#define NVA3_DISP_OIMM_CLASS 0x0000857b +#define NVD0_DISP_OIMM_CLASS 0x0000907b +#define NVE0_DISP_OIMM_CLASS 0x0000917b + +struct nv50_display_oimm_class { + u32 head; +}; + +/* 507c: NV50_DISP_SYNC + * 827c: NV84_DISP_SYNC + * 837c: NVA0_DISP_SYNC + * 887c: NV94_DISP_SYNC + * 857c: NVA3_DISP_SYNC + * 907c: NVD0_DISP_SYNC + * 917c: NVE0_DISP_SYNC + */ + +#define NV50_DISP_SYNC_CLASS 0x0000507c +#define NV84_DISP_SYNC_CLASS 0x0000827c +#define NVA0_DISP_SYNC_CLASS 0x0000837c +#define NV94_DISP_SYNC_CLASS 0x0000887c +#define NVA3_DISP_SYNC_CLASS 0x0000857c +#define NVD0_DISP_SYNC_CLASS 0x0000907c +#define NVE0_DISP_SYNC_CLASS 0x0000917c + +struct nv50_display_sync_class { + u32 pushbuf; + u32 head; +}; + +/* 507d: NV50_DISP_MAST + * 827d: NV84_DISP_MAST + * 837d: NVA0_DISP_MAST + * 887d: NV94_DISP_MAST + * 857d: NVA3_DISP_MAST + * 907d: NVD0_DISP_MAST + * 917d: NVE0_DISP_MAST + */ + +#define NV50_DISP_MAST_CLASS 0x0000507d +#define NV84_DISP_MAST_CLASS 0x0000827d +#define NVA0_DISP_MAST_CLASS 0x0000837d +#define NV94_DISP_MAST_CLASS 0x0000887d +#define NVA3_DISP_MAST_CLASS 0x0000857d +#define NVD0_DISP_MAST_CLASS 0x0000907d +#define NVE0_DISP_MAST_CLASS 0x0000917d + +struct nv50_display_mast_class { + u32 pushbuf; +}; + +/* 507e: NV50_DISP_OVLY + * 827e: NV84_DISP_OVLY + * 837e: NVA0_DISP_OVLY + * 887e: NV94_DISP_OVLY + * 857e: NVA3_DISP_OVLY + * 907e: NVD0_DISP_OVLY + * 917e: NVE0_DISP_OVLY + */ + +#define NV50_DISP_OVLY_CLASS 0x0000507e +#define NV84_DISP_OVLY_CLASS 0x0000827e +#define NVA0_DISP_OVLY_CLASS 0x0000837e +#define NV94_DISP_OVLY_CLASS 0x0000887e +#define NVA3_DISP_OVLY_CLASS 0x0000857e +#define NVD0_DISP_OVLY_CLASS 0x0000907e +#define NVE0_DISP_OVLY_CLASS 0x0000917e + +struct nv50_display_ovly_class { + u32 pushbuf; + u32 head; +}; + #endif diff --git a/drivers/gpu/drm/nouveau/core/include/core/engctx.h b/drivers/gpu/drm/nouveau/core/include/core/engctx.h index 8a947b6872e..2fd48b564c7 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/engctx.h +++ b/drivers/gpu/drm/nouveau/core/include/core/engctx.h @@ -39,6 +39,9 @@ void nouveau_engctx_destroy(struct nouveau_engctx *); int nouveau_engctx_init(struct nouveau_engctx *); int nouveau_engctx_fini(struct nouveau_engctx *, bool suspend); +int _nouveau_engctx_ctor(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, void *, u32, + struct nouveau_object **); void _nouveau_engctx_dtor(struct nouveau_object *); int _nouveau_engctx_init(struct nouveau_object *); int _nouveau_engctx_fini(struct nouveau_object *, bool suspend); diff --git a/drivers/gpu/drm/nouveau/core/include/core/falcon.h b/drivers/gpu/drm/nouveau/core/include/core/falcon.h new file mode 100644 index 00000000000..1edec386ab3 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/core/falcon.h @@ -0,0 +1,81 @@ +#ifndef __NOUVEAU_FALCON_H__ +#define __NOUVEAU_FALCON_H__ + +#include <core/engine.h> +#include <core/engctx.h> +#include <core/gpuobj.h> + +struct nouveau_falcon_chan { + struct nouveau_engctx base; +}; + +#define nouveau_falcon_context_create(p,e,c,g,s,a,f,d) \ + nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) +#define nouveau_falcon_context_destroy(d) \ + nouveau_engctx_destroy(&(d)->base) +#define nouveau_falcon_context_init(d) \ + nouveau_engctx_init(&(d)->base) +#define nouveau_falcon_context_fini(d,s) \ + nouveau_engctx_fini(&(d)->base, (s)) + +#define _nouveau_falcon_context_ctor _nouveau_engctx_ctor +#define _nouveau_falcon_context_dtor _nouveau_engctx_dtor +#define _nouveau_falcon_context_init _nouveau_engctx_init +#define _nouveau_falcon_context_fini _nouveau_engctx_fini +#define _nouveau_falcon_context_rd32 _nouveau_engctx_rd32 +#define _nouveau_falcon_context_wr32 _nouveau_engctx_wr32 + +struct nouveau_falcon_data { + bool external; +}; + +struct nouveau_falcon { + struct nouveau_engine base; + + u32 addr; + u8 version; + u8 secret; + + struct nouveau_gpuobj *core; + bool external; + + struct { + u32 limit; + u32 *data; + u32 size; + } code; + + struct { + u32 limit; + u32 *data; + u32 size; + } data; +}; + +#define nv_falcon(priv) (&(priv)->base) + +#define nouveau_falcon_create(p,e,c,b,d,i,f,r) \ + nouveau_falcon_create_((p), (e), (c), (b), (d), (i), (f), \ + sizeof(**r),(void **)r) +#define nouveau_falcon_destroy(p) \ + nouveau_engine_destroy(&(p)->base) +#define nouveau_falcon_init(p) ({ \ + struct nouveau_falcon *falcon = (p); \ + _nouveau_falcon_init(nv_object(falcon)); \ +}) +#define nouveau_falcon_fini(p,s) ({ \ + struct nouveau_falcon *falcon = (p); \ + _nouveau_falcon_fini(nv_object(falcon), (s)); \ +}) + +int nouveau_falcon_create_(struct nouveau_object *, struct nouveau_object *, + struct nouveau_oclass *, u32, bool, const char *, + const char *, int, void **); + +#define _nouveau_falcon_dtor _nouveau_engine_dtor +int _nouveau_falcon_init(struct nouveau_object *); +int _nouveau_falcon_fini(struct nouveau_object *, bool); +u32 _nouveau_falcon_rd32(struct nouveau_object *, u64); +void _nouveau_falcon_wr32(struct nouveau_object *, u64, u32); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h b/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h index 6eaff79377a..b3b9ce4e9d3 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h +++ b/drivers/gpu/drm/nouveau/core/include/core/gpuobj.h @@ -65,7 +65,7 @@ nouveau_gpuobj_ref(struct nouveau_gpuobj *obj, struct nouveau_gpuobj **ref) void _nouveau_gpuobj_dtor(struct nouveau_object *); int _nouveau_gpuobj_init(struct nouveau_object *); int _nouveau_gpuobj_fini(struct nouveau_object *, bool); -u32 _nouveau_gpuobj_rd32(struct nouveau_object *, u32); -void _nouveau_gpuobj_wr32(struct nouveau_object *, u32, u32); +u32 _nouveau_gpuobj_rd32(struct nouveau_object *, u64); +void _nouveau_gpuobj_wr32(struct nouveau_object *, u64, u32); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/core/mm.h b/drivers/gpu/drm/nouveau/core/include/core/mm.h index 975137ba34a..2514e81ade0 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/mm.h +++ b/drivers/gpu/drm/nouveau/core/include/core/mm.h @@ -21,6 +21,12 @@ struct nouveau_mm { int heap_nodes; }; +static inline bool +nouveau_mm_initialised(struct nouveau_mm *mm) +{ + return mm->block_size != 0; +} + int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block); int nouveau_mm_fini(struct nouveau_mm *); int nouveau_mm_head(struct nouveau_mm *, u8 type, u32 size_max, u32 size_min, diff --git a/drivers/gpu/drm/nouveau/core/include/core/object.h b/drivers/gpu/drm/nouveau/core/include/core/object.h index 818feabbf4a..27d17a9852e 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/object.h +++ b/drivers/gpu/drm/nouveau/core/include/core/object.h @@ -70,7 +70,8 @@ nv_pclass(struct nouveau_object *parent, u32 oclass) } struct nouveau_omthds { - u32 method; + u32 start; + u32 limit; int (*call)(struct nouveau_object *, u32, void *, u32); }; @@ -81,12 +82,12 @@ struct nouveau_ofuncs { void (*dtor)(struct nouveau_object *); int (*init)(struct nouveau_object *); int (*fini)(struct nouveau_object *, bool suspend); - u8 (*rd08)(struct nouveau_object *, u32 offset); - u16 (*rd16)(struct nouveau_object *, u32 offset); - u32 (*rd32)(struct nouveau_object *, u32 offset); - void (*wr08)(struct nouveau_object *, u32 offset, u8 data); - void (*wr16)(struct nouveau_object *, u32 offset, u16 data); - void (*wr32)(struct nouveau_object *, u32 offset, u32 data); + u8 (*rd08)(struct nouveau_object *, u64 offset); + u16 (*rd16)(struct nouveau_object *, u64 offset); + u32 (*rd32)(struct nouveau_object *, u64 offset); + void (*wr08)(struct nouveau_object *, u64 offset, u8 data); + void (*wr16)(struct nouveau_object *, u64 offset, u16 data); + void (*wr32)(struct nouveau_object *, u64 offset, u32 data); }; static inline struct nouveau_ofuncs * @@ -109,21 +110,27 @@ int nouveau_object_del(struct nouveau_object *, u32 parent, u32 handle); void nouveau_object_debug(void); static inline int -nv_call(void *obj, u32 mthd, u32 data) +nv_exec(void *obj, u32 mthd, void *data, u32 size) { struct nouveau_omthds *method = nv_oclass(obj)->omthds; while (method && method->call) { - if (method->method == mthd) - return method->call(obj, mthd, &data, sizeof(data)); + if (mthd >= method->start && mthd <= method->limit) + return method->call(obj, mthd, data, size); method++; } return -EINVAL; } +static inline int +nv_call(void *obj, u32 mthd, u32 data) +{ + return nv_exec(obj, mthd, &data, sizeof(data)); +} + static inline u8 -nv_ro08(void *obj, u32 addr) +nv_ro08(void *obj, u64 addr) { u8 data = nv_ofuncs(obj)->rd08(obj, addr); nv_spam(obj, "nv_ro08 0x%08x 0x%02x\n", addr, data); @@ -131,7 +138,7 @@ nv_ro08(void *obj, u32 addr) } static inline u16 -nv_ro16(void *obj, u32 addr) +nv_ro16(void *obj, u64 addr) { u16 data = nv_ofuncs(obj)->rd16(obj, addr); nv_spam(obj, "nv_ro16 0x%08x 0x%04x\n", addr, data); @@ -139,7 +146,7 @@ nv_ro16(void *obj, u32 addr) } static inline u32 -nv_ro32(void *obj, u32 addr) +nv_ro32(void *obj, u64 addr) { u32 data = nv_ofuncs(obj)->rd32(obj, addr); nv_spam(obj, "nv_ro32 0x%08x 0x%08x\n", addr, data); @@ -147,28 +154,28 @@ nv_ro32(void *obj, u32 addr) } static inline void -nv_wo08(void *obj, u32 addr, u8 data) +nv_wo08(void *obj, u64 addr, u8 data) { nv_spam(obj, "nv_wo08 0x%08x 0x%02x\n", addr, data); nv_ofuncs(obj)->wr08(obj, addr, data); } static inline void -nv_wo16(void *obj, u32 addr, u16 data) +nv_wo16(void *obj, u64 addr, u16 data) { nv_spam(obj, "nv_wo16 0x%08x 0x%04x\n", addr, data); nv_ofuncs(obj)->wr16(obj, addr, data); } static inline void -nv_wo32(void *obj, u32 addr, u32 data) +nv_wo32(void *obj, u64 addr, u32 data) { nv_spam(obj, "nv_wo32 0x%08x 0x%08x\n", addr, data); nv_ofuncs(obj)->wr32(obj, addr, data); } static inline u32 -nv_mo32(void *obj, u32 addr, u32 mask, u32 data) +nv_mo32(void *obj, u64 addr, u32 mask, u32 data) { u32 temp = nv_ro32(obj, addr); nv_wo32(obj, addr, (temp & ~mask) | data); diff --git a/drivers/gpu/drm/nouveau/core/include/core/parent.h b/drivers/gpu/drm/nouveau/core/include/core/parent.h index 3c2e940eb0f..31cd852c96d 100644 --- a/drivers/gpu/drm/nouveau/core/include/core/parent.h +++ b/drivers/gpu/drm/nouveau/core/include/core/parent.h @@ -14,7 +14,7 @@ struct nouveau_parent { struct nouveau_object base; struct nouveau_sclass *sclass; - u32 engine; + u64 engine; int (*context_attach)(struct nouveau_object *, struct nouveau_object *); diff --git a/drivers/gpu/drm/nouveau/core/include/engine/bsp.h b/drivers/gpu/drm/nouveau/core/include/engine/bsp.h index 75d1ed5f85f..13ccdf54dfa 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/bsp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/bsp.h @@ -1,45 +1,8 @@ #ifndef __NOUVEAU_BSP_H__ #define __NOUVEAU_BSP_H__ -#include <core/engine.h> -#include <core/engctx.h> - -struct nouveau_bsp_chan { - struct nouveau_engctx base; -}; - -#define nouveau_bsp_context_create(p,e,c,g,s,a,f,d) \ - nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) -#define nouveau_bsp_context_destroy(d) \ - nouveau_engctx_destroy(&(d)->base) -#define nouveau_bsp_context_init(d) \ - nouveau_engctx_init(&(d)->base) -#define nouveau_bsp_context_fini(d,s) \ - nouveau_engctx_fini(&(d)->base, (s)) - -#define _nouveau_bsp_context_dtor _nouveau_engctx_dtor -#define _nouveau_bsp_context_init _nouveau_engctx_init -#define _nouveau_bsp_context_fini _nouveau_engctx_fini -#define _nouveau_bsp_context_rd32 _nouveau_engctx_rd32 -#define _nouveau_bsp_context_wr32 _nouveau_engctx_wr32 - -struct nouveau_bsp { - struct nouveau_engine base; -}; - -#define nouveau_bsp_create(p,e,c,d) \ - nouveau_engine_create((p), (e), (c), true, "PBSP", "bsp", (d)) -#define nouveau_bsp_destroy(d) \ - nouveau_engine_destroy(&(d)->base) -#define nouveau_bsp_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_bsp_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -#define _nouveau_bsp_dtor _nouveau_engine_dtor -#define _nouveau_bsp_init _nouveau_engine_init -#define _nouveau_bsp_fini _nouveau_engine_fini - extern struct nouveau_oclass nv84_bsp_oclass; +extern struct nouveau_oclass nvc0_bsp_oclass; +extern struct nouveau_oclass nve0_bsp_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/engine/copy.h b/drivers/gpu/drm/nouveau/core/include/engine/copy.h index 70b9d8c5fcf..8cad2cf28ce 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/copy.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/copy.h @@ -1,44 +1,7 @@ #ifndef __NOUVEAU_COPY_H__ #define __NOUVEAU_COPY_H__ -#include <core/engine.h> -#include <core/engctx.h> - -struct nouveau_copy_chan { - struct nouveau_engctx base; -}; - -#define nouveau_copy_context_create(p,e,c,g,s,a,f,d) \ - nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) -#define nouveau_copy_context_destroy(d) \ - nouveau_engctx_destroy(&(d)->base) -#define nouveau_copy_context_init(d) \ - nouveau_engctx_init(&(d)->base) -#define nouveau_copy_context_fini(d,s) \ - nouveau_engctx_fini(&(d)->base, (s)) - -#define _nouveau_copy_context_dtor _nouveau_engctx_dtor -#define _nouveau_copy_context_init _nouveau_engctx_init -#define _nouveau_copy_context_fini _nouveau_engctx_fini -#define _nouveau_copy_context_rd32 _nouveau_engctx_rd32 -#define _nouveau_copy_context_wr32 _nouveau_engctx_wr32 - -struct nouveau_copy { - struct nouveau_engine base; -}; - -#define nouveau_copy_create(p,e,c,y,i,d) \ - nouveau_engine_create((p), (e), (c), (y), "PCE"#i, "copy"#i, (d)) -#define nouveau_copy_destroy(d) \ - nouveau_engine_destroy(&(d)->base) -#define nouveau_copy_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_copy_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -#define _nouveau_copy_dtor _nouveau_engine_dtor -#define _nouveau_copy_init _nouveau_engine_init -#define _nouveau_copy_fini _nouveau_engine_fini +void nva3_copy_intr(struct nouveau_subdev *); extern struct nouveau_oclass nva3_copy_oclass; extern struct nouveau_oclass nvc0_copy0_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/crypt.h b/drivers/gpu/drm/nouveau/core/include/engine/crypt.h index e3674743baa..db975618e93 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/crypt.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/crypt.h @@ -1,45 +1,6 @@ #ifndef __NOUVEAU_CRYPT_H__ #define __NOUVEAU_CRYPT_H__ -#include <core/engine.h> -#include <core/engctx.h> - -struct nouveau_crypt_chan { - struct nouveau_engctx base; -}; - -#define nouveau_crypt_context_create(p,e,c,g,s,a,f,d) \ - nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) -#define nouveau_crypt_context_destroy(d) \ - nouveau_engctx_destroy(&(d)->base) -#define nouveau_crypt_context_init(d) \ - nouveau_engctx_init(&(d)->base) -#define nouveau_crypt_context_fini(d,s) \ - nouveau_engctx_fini(&(d)->base, (s)) - -#define _nouveau_crypt_context_dtor _nouveau_engctx_dtor -#define _nouveau_crypt_context_init _nouveau_engctx_init -#define _nouveau_crypt_context_fini _nouveau_engctx_fini -#define _nouveau_crypt_context_rd32 _nouveau_engctx_rd32 -#define _nouveau_crypt_context_wr32 _nouveau_engctx_wr32 - -struct nouveau_crypt { - struct nouveau_engine base; -}; - -#define nouveau_crypt_create(p,e,c,d) \ - nouveau_engine_create((p), (e), (c), true, "PCRYPT", "crypt", (d)) -#define nouveau_crypt_destroy(d) \ - nouveau_engine_destroy(&(d)->base) -#define nouveau_crypt_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_crypt_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -#define _nouveau_crypt_dtor _nouveau_engine_dtor -#define _nouveau_crypt_init _nouveau_engine_init -#define _nouveau_crypt_fini _nouveau_engine_fini - extern struct nouveau_oclass nv84_crypt_oclass; extern struct nouveau_oclass nv98_crypt_oclass; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/disp.h b/drivers/gpu/drm/nouveau/core/include/engine/disp.h index 38ec1252cba..46948285f3e 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/disp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/disp.h @@ -39,6 +39,11 @@ nouveau_disp(void *obj) extern struct nouveau_oclass nv04_disp_oclass; extern struct nouveau_oclass nv50_disp_oclass; +extern struct nouveau_oclass nv84_disp_oclass; +extern struct nouveau_oclass nva0_disp_oclass; +extern struct nouveau_oclass nv94_disp_oclass; +extern struct nouveau_oclass nva3_disp_oclass; extern struct nouveau_oclass nvd0_disp_oclass; +extern struct nouveau_oclass nve0_disp_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h b/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h index 700ccbb1941..b28914ed175 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/dmaobj.h @@ -12,29 +12,17 @@ struct nouveau_dmaobj { u32 access; u64 start; u64 limit; + u32 conf0; }; -#define nouveau_dmaobj_create(p,e,c,a,s,d) \ - nouveau_dmaobj_create_((p), (e), (c), (a), (s), sizeof(**d), (void **)d) -#define nouveau_dmaobj_destroy(p) \ - nouveau_object_destroy(&(p)->base) -#define nouveau_dmaobj_init(p) \ - nouveau_object_init(&(p)->base) -#define nouveau_dmaobj_fini(p,s) \ - nouveau_object_fini(&(p)->base, (s)) - -int nouveau_dmaobj_create_(struct nouveau_object *, struct nouveau_object *, - struct nouveau_oclass *, void *data, u32 size, - int length, void **); - -#define _nouveau_dmaobj_dtor nouveau_object_destroy -#define _nouveau_dmaobj_init nouveau_object_init -#define _nouveau_dmaobj_fini nouveau_object_fini - struct nouveau_dmaeng { struct nouveau_engine base; - int (*bind)(struct nouveau_dmaeng *, struct nouveau_object *parent, - struct nouveau_dmaobj *, struct nouveau_gpuobj **); + + /* creates a "physical" dma object from a struct nouveau_dmaobj */ + int (*bind)(struct nouveau_dmaeng *dmaeng, + struct nouveau_object *parent, + struct nouveau_dmaobj *dmaobj, + struct nouveau_gpuobj **); }; #define nouveau_dmaeng_create(p,e,c,d) \ @@ -53,5 +41,8 @@ struct nouveau_dmaeng { extern struct nouveau_oclass nv04_dmaeng_oclass; extern struct nouveau_oclass nv50_dmaeng_oclass; extern struct nouveau_oclass nvc0_dmaeng_oclass; +extern struct nouveau_oclass nvd0_dmaeng_oclass; + +extern struct nouveau_oclass nouveau_dmaobj_sclass[]; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h index d67fed1e397..f18846c8c6f 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/fifo.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/fifo.h @@ -33,15 +33,15 @@ int nouveau_fifo_channel_create_(struct nouveau_object *, struct nouveau_object *, struct nouveau_oclass *, int bar, u32 addr, u32 size, u32 push, - u32 engmask, int len, void **); + u64 engmask, int len, void **); void nouveau_fifo_channel_destroy(struct nouveau_fifo_chan *); #define _nouveau_fifo_channel_init _nouveau_namedb_init #define _nouveau_fifo_channel_fini _nouveau_namedb_fini void _nouveau_fifo_channel_dtor(struct nouveau_object *); -u32 _nouveau_fifo_channel_rd32(struct nouveau_object *, u32); -void _nouveau_fifo_channel_wr32(struct nouveau_object *, u32, u32); +u32 _nouveau_fifo_channel_rd32(struct nouveau_object *, u64); +void _nouveau_fifo_channel_wr32(struct nouveau_object *, u64, u32); struct nouveau_fifo_base { struct nouveau_gpuobj base; diff --git a/drivers/gpu/drm/nouveau/core/include/engine/ppp.h b/drivers/gpu/drm/nouveau/core/include/engine/ppp.h index 74d554fb328..0a66781e8cf 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/ppp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/ppp.h @@ -1,45 +1,7 @@ #ifndef __NOUVEAU_PPP_H__ #define __NOUVEAU_PPP_H__ -#include <core/engine.h> -#include <core/engctx.h> - -struct nouveau_ppp_chan { - struct nouveau_engctx base; -}; - -#define nouveau_ppp_context_create(p,e,c,g,s,a,f,d) \ - nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) -#define nouveau_ppp_context_destroy(d) \ - nouveau_engctx_destroy(&(d)->base) -#define nouveau_ppp_context_init(d) \ - nouveau_engctx_init(&(d)->base) -#define nouveau_ppp_context_fini(d,s) \ - nouveau_engctx_fini(&(d)->base, (s)) - -#define _nouveau_ppp_context_dtor _nouveau_engctx_dtor -#define _nouveau_ppp_context_init _nouveau_engctx_init -#define _nouveau_ppp_context_fini _nouveau_engctx_fini -#define _nouveau_ppp_context_rd32 _nouveau_engctx_rd32 -#define _nouveau_ppp_context_wr32 _nouveau_engctx_wr32 - -struct nouveau_ppp { - struct nouveau_engine base; -}; - -#define nouveau_ppp_create(p,e,c,d) \ - nouveau_engine_create((p), (e), (c), true, "PPPP", "ppp", (d)) -#define nouveau_ppp_destroy(d) \ - nouveau_engine_destroy(&(d)->base) -#define nouveau_ppp_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_ppp_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -#define _nouveau_ppp_dtor _nouveau_engine_dtor -#define _nouveau_ppp_init _nouveau_engine_init -#define _nouveau_ppp_fini _nouveau_engine_fini - extern struct nouveau_oclass nv98_ppp_oclass; +extern struct nouveau_oclass nvc0_ppp_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/engine/vp.h b/drivers/gpu/drm/nouveau/core/include/engine/vp.h index 05cd08fba37..d7b287b115b 100644 --- a/drivers/gpu/drm/nouveau/core/include/engine/vp.h +++ b/drivers/gpu/drm/nouveau/core/include/engine/vp.h @@ -1,45 +1,8 @@ #ifndef __NOUVEAU_VP_H__ #define __NOUVEAU_VP_H__ -#include <core/engine.h> -#include <core/engctx.h> - -struct nouveau_vp_chan { - struct nouveau_engctx base; -}; - -#define nouveau_vp_context_create(p,e,c,g,s,a,f,d) \ - nouveau_engctx_create((p), (e), (c), (g), (s), (a), (f), (d)) -#define nouveau_vp_context_destroy(d) \ - nouveau_engctx_destroy(&(d)->base) -#define nouveau_vp_context_init(d) \ - nouveau_engctx_init(&(d)->base) -#define nouveau_vp_context_fini(d,s) \ - nouveau_engctx_fini(&(d)->base, (s)) - -#define _nouveau_vp_context_dtor _nouveau_engctx_dtor -#define _nouveau_vp_context_init _nouveau_engctx_init -#define _nouveau_vp_context_fini _nouveau_engctx_fini -#define _nouveau_vp_context_rd32 _nouveau_engctx_rd32 -#define _nouveau_vp_context_wr32 _nouveau_engctx_wr32 - -struct nouveau_vp { - struct nouveau_engine base; -}; - -#define nouveau_vp_create(p,e,c,d) \ - nouveau_engine_create((p), (e), (c), true, "PVP", "vp", (d)) -#define nouveau_vp_destroy(d) \ - nouveau_engine_destroy(&(d)->base) -#define nouveau_vp_init(d) \ - nouveau_engine_init(&(d)->base) -#define nouveau_vp_fini(d,s) \ - nouveau_engine_fini(&(d)->base, (s)) - -#define _nouveau_vp_dtor _nouveau_engine_dtor -#define _nouveau_vp_init _nouveau_engine_init -#define _nouveau_vp_fini _nouveau_engine_fini - extern struct nouveau_oclass nv84_vp_oclass; +extern struct nouveau_oclass nvc0_vp_oclass; +extern struct nouveau_oclass nve0_vp_oclass; #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h index d682fb62583..b79025da581 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dcb.h @@ -23,6 +23,7 @@ struct dcb_output { uint8_t bus; uint8_t location; uint8_t or; + uint8_t link; bool duallink_possible; union { struct sor_conf { @@ -55,36 +56,11 @@ struct dcb_output { u16 dcb_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *ent, u8 *len); u16 dcb_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len); +u16 dcb_outp_parse(struct nouveau_bios *, u8 idx, u8 *, u8 *, + struct dcb_output *); +u16 dcb_outp_match(struct nouveau_bios *, u16 type, u16 mask, u8 *, u8 *, + struct dcb_output *); int dcb_outp_foreach(struct nouveau_bios *, void *data, int (*exec) (struct nouveau_bios *, void *, int index, u16 entry)); - -/* BIT 'U'/'d' table encoder subtables have hashes matching them to - * a particular set of encoders. - * - * This function returns true if a particular DCB entry matches. - */ -static inline bool -dcb_hash_match(struct dcb_output *dcb, u32 hash) -{ - if ((hash & 0x000000f0) != (dcb->location << 4)) - return false; - if ((hash & 0x0000000f) != dcb->type) - return false; - if (!(hash & (dcb->or << 16))) - return false; - - switch (dcb->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - if (hash & 0x00c00000) { - if (!(hash & (dcb->sorconf.link << 22))) - return false; - } - default: - return true; - } -} - #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/disp.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/disp.h new file mode 100644 index 00000000000..c35937e2f6a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/disp.h @@ -0,0 +1,48 @@ +#ifndef __NVBIOS_DISP_H__ +#define __NVBIOS_DISP_H__ + +u16 nvbios_disp_table(struct nouveau_bios *, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub); + +struct nvbios_disp { + u16 data; +}; + +u16 nvbios_disp_entry(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr__, u8 *sub); +u16 nvbios_disp_parse(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr__, u8 *sub, + struct nvbios_disp *); + +struct nvbios_outp { + u16 type; + u16 mask; + u16 script[3]; +}; + +u16 nvbios_outp_entry(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_outp_parse(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *); +u16 nvbios_outp_match(struct nouveau_bios *, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *); + + +struct nvbios_ocfg { + u16 match; + u16 clkcmp[2]; +}; + +u16 nvbios_ocfg_entry(struct nouveau_bios *, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len); +u16 nvbios_ocfg_parse(struct nouveau_bios *, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_ocfg *); +u16 nvbios_ocfg_match(struct nouveau_bios *, u16 outp, u16 type, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_ocfg *); +u16 nvbios_oclk_match(struct nouveau_bios *, u16 cmp, u32 khz); + +#endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h index 73b5e5d3e75..6e54218b55f 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/bios/dp.h @@ -1,8 +1,34 @@ #ifndef __NVBIOS_DP_H__ #define __NVBIOS_DP_H__ -u16 dp_table(struct nouveau_bios *, u8 *ver, u8 *hdr, u8 *cnt, u8 *len); -u16 dp_outp(struct nouveau_bios *, u8 idx, u8 *ver, u8 *len); -u16 dp_outp_match(struct nouveau_bios *, struct dcb_output *, u8 *ver, u8 *len); +struct nvbios_dpout { + u16 type; + u16 mask; + u8 flags; + u32 script[5]; + u32 lnkcmp; +}; + +u16 nvbios_dpout_parse(struct nouveau_bios *, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *); +u16 nvbios_dpout_match(struct nouveau_bios *, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *); + +struct nvbios_dpcfg { + u8 drv; + u8 pre; + u8 unk; +}; + +u16 +nvbios_dpcfg_parse(struct nouveau_bios *, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpcfg *); +u16 +nvbios_dpcfg_match(struct nouveau_bios *, u16 outp, u8 un, u8 vs, u8 pe, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpcfg *); #endif diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h index 5c1b5e1904f..da470e6851b 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/fb.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/fb.h @@ -69,8 +69,11 @@ struct nouveau_fb { } type; u64 stolen; u64 size; + int ranks; + int parts; + int (*init)(struct nouveau_fb *); int (*get)(struct nouveau_fb *, u64 size, u32 align, u32 size_nc, u32 type, struct nouveau_mem **); void (*put)(struct nouveau_fb *, struct nouveau_mem **); @@ -84,6 +87,8 @@ struct nouveau_fb { int regions; void (*init)(struct nouveau_fb *, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *); + void (*comp)(struct nouveau_fb *, int i, u32 size, u32 flags, + struct nouveau_fb_tile *); void (*fini)(struct nouveau_fb *, int i, struct nouveau_fb_tile *); void (*prog)(struct nouveau_fb *, int i, @@ -99,7 +104,7 @@ nouveau_fb(void *obj) #define nouveau_fb_create(p,e,c,d) \ nouveau_subdev_create((p), (e), (c), 0, "PFB", "fb", (d)) -int nouveau_fb_created(struct nouveau_fb *); +int nouveau_fb_preinit(struct nouveau_fb *); void nouveau_fb_destroy(struct nouveau_fb *); int nouveau_fb_init(struct nouveau_fb *); #define nouveau_fb_fini(p,s) \ @@ -111,9 +116,19 @@ int _nouveau_fb_init(struct nouveau_object *); extern struct nouveau_oclass nv04_fb_oclass; extern struct nouveau_oclass nv10_fb_oclass; +extern struct nouveau_oclass nv1a_fb_oclass; extern struct nouveau_oclass nv20_fb_oclass; +extern struct nouveau_oclass nv25_fb_oclass; extern struct nouveau_oclass nv30_fb_oclass; +extern struct nouveau_oclass nv35_fb_oclass; +extern struct nouveau_oclass nv36_fb_oclass; extern struct nouveau_oclass nv40_fb_oclass; +extern struct nouveau_oclass nv41_fb_oclass; +extern struct nouveau_oclass nv44_fb_oclass; +extern struct nouveau_oclass nv46_fb_oclass; +extern struct nouveau_oclass nv47_fb_oclass; +extern struct nouveau_oclass nv49_fb_oclass; +extern struct nouveau_oclass nv4e_fb_oclass; extern struct nouveau_oclass nv50_fb_oclass; extern struct nouveau_oclass nvc0_fb_oclass; @@ -122,13 +137,35 @@ int nouveau_fb_bios_memtype(struct nouveau_bios *); bool nv04_fb_memtype_valid(struct nouveau_fb *, u32 memtype); +void nv10_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); +void nv10_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); void nv10_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); +int nv20_fb_vram_init(struct nouveau_fb *); +void nv20_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); +void nv20_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); +void nv20_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +int nv30_fb_init(struct nouveau_object *); void nv30_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *); -void nv30_fb_tile_fini(struct nouveau_fb *, int i, struct nouveau_fb_tile *); + +void nv40_fb_tile_comp(struct nouveau_fb *, int i, u32 size, u32 flags, + struct nouveau_fb_tile *); + +int nv41_fb_vram_init(struct nouveau_fb *); +int nv41_fb_init(struct nouveau_object *); +void nv41_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +int nv44_fb_vram_init(struct nouveau_fb *); +int nv44_fb_init(struct nouveau_object *); +void nv44_fb_tile_prog(struct nouveau_fb *, int, struct nouveau_fb_tile *); + +void nv46_fb_tile_init(struct nouveau_fb *, int i, u32 addr, u32 size, + u32 pitch, u32 flags, struct nouveau_fb_tile *); void nv50_fb_vram_del(struct nouveau_fb *, struct nouveau_mem **); -void nv50_fb_trap(struct nouveau_fb *, int display); #endif diff --git a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c index cd01c533007..d70ba342aa2 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bar/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bar/base.c @@ -65,14 +65,14 @@ nouveau_barobj_dtor(struct nouveau_object *object) } static u32 -nouveau_barobj_rd32(struct nouveau_object *object, u32 addr) +nouveau_barobj_rd32(struct nouveau_object *object, u64 addr) { struct nouveau_barobj *barobj = (void *)object; return ioread32_native(barobj->iomem + addr); } static void -nouveau_barobj_wr32(struct nouveau_object *object, u32 addr, u32 data) +nouveau_barobj_wr32(struct nouveau_object *object, u64 addr, u32 data) { struct nouveau_barobj *barobj = (void *)object; iowrite32_native(data, barobj->iomem + addr); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index 70ca7d5a1aa..dd111947eb8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -63,7 +63,7 @@ nouveau_bios_shadow_of(struct nouveau_bios *bios) struct pci_dev *pdev = nv_device(bios)->pdev; struct device_node *dn; const u32 *data; - int size, i; + int size; dn = pci_device_to_OF_node(pdev); if (!dn) { @@ -210,11 +210,19 @@ nouveau_bios_shadow_acpi(struct nouveau_bios *bios) return; bios->data = kmalloc(bios->size, GFP_KERNEL); - for (i = 0; bios->data && i < bios->size; i += cnt) { - cnt = min((bios->size - i), (u32)4096); - ret = nouveau_acpi_get_bios_chunk(bios->data, i, cnt); - if (ret != cnt) - break; + if (bios->data) { + /* disobey the acpi spec - much faster on at least w530 ... */ + ret = nouveau_acpi_get_bios_chunk(bios->data, 0, bios->size); + if (ret != bios->size || + nvbios_checksum(bios->data, bios->size)) { + /* ... that didn't work, ok, i'll be good now */ + for (i = 0; i < bios->size; i += cnt) { + cnt = min((bios->size - i), (u32)4096); + ret = nouveau_acpi_get_bios_chunk(bios->data, i, cnt); + if (ret != cnt) + break; + } + } } } @@ -358,42 +366,42 @@ nouveau_bios_shadow(struct nouveau_bios *bios) } static u8 -nouveau_bios_rd08(struct nouveau_object *object, u32 addr) +nouveau_bios_rd08(struct nouveau_object *object, u64 addr) { struct nouveau_bios *bios = (void *)object; return bios->data[addr]; } static u16 -nouveau_bios_rd16(struct nouveau_object *object, u32 addr) +nouveau_bios_rd16(struct nouveau_object *object, u64 addr) { struct nouveau_bios *bios = (void *)object; return get_unaligned_le16(&bios->data[addr]); } static u32 -nouveau_bios_rd32(struct nouveau_object *object, u32 addr) +nouveau_bios_rd32(struct nouveau_object *object, u64 addr) { struct nouveau_bios *bios = (void *)object; return get_unaligned_le32(&bios->data[addr]); } static void -nouveau_bios_wr08(struct nouveau_object *object, u32 addr, u8 data) +nouveau_bios_wr08(struct nouveau_object *object, u64 addr, u8 data) { struct nouveau_bios *bios = (void *)object; bios->data[addr] = data; } static void -nouveau_bios_wr16(struct nouveau_object *object, u32 addr, u16 data) +nouveau_bios_wr16(struct nouveau_object *object, u64 addr, u16 data) { struct nouveau_bios *bios = (void *)object; put_unaligned_le16(data, &bios->data[addr]); } static void -nouveau_bios_wr32(struct nouveau_object *object, u32 addr, u32 data) +nouveau_bios_wr32(struct nouveau_object *object, u64 addr, u32 data) { struct nouveau_bios *bios = (void *)object; put_unaligned_le32(data, &bios->data[addr]); diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c index 7d750382a83..bbd709fba0a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dcb.c @@ -107,6 +107,69 @@ dcb_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) return 0x0000; } +u16 +dcb_outp_parse(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len, + struct dcb_output *outp) +{ + u16 dcb = dcb_outp(bios, idx, ver, len); + if (dcb) { + if (*ver >= 0x20) { + u32 conn = nv_ro32(bios, dcb + 0x00); + outp->or = (conn & 0x0f000000) >> 24; + outp->location = (conn & 0x00300000) >> 20; + outp->bus = (conn & 0x000f0000) >> 16; + outp->connector = (conn & 0x0000f000) >> 12; + outp->heads = (conn & 0x00000f00) >> 8; + outp->i2c_index = (conn & 0x000000f0) >> 4; + outp->type = (conn & 0x0000000f); + outp->link = 0; + } else { + dcb = 0x0000; + } + + if (*ver >= 0x40) { + u32 conf = nv_ro32(bios, dcb + 0x04); + switch (outp->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + case DCB_OUTPUT_DP: + outp->link = (conf & 0x00000030) >> 4; + outp->sorconf.link = outp->link; /*XXX*/ + break; + default: + break; + } + } + } + return dcb; +} + +static inline u16 +dcb_outp_hasht(struct dcb_output *outp) +{ + return outp->type; +} + +static inline u16 +dcb_outp_hashm(struct dcb_output *outp) +{ + return (outp->heads << 8) | (outp->link << 6) | outp->or; +} + +u16 +dcb_outp_match(struct nouveau_bios *bios, u16 type, u16 mask, + u8 *ver, u8 *len, struct dcb_output *outp) +{ + u16 dcb, idx = 0; + while ((dcb = dcb_outp_parse(bios, idx++, ver, len, outp))) { + if (dcb_outp_hasht(outp) == type) { + if ((dcb_outp_hashm(outp) & mask) == mask) + break; + } + } + return dcb; +} + int dcb_outp_foreach(struct nouveau_bios *bios, void *data, int (*exec)(struct nouveau_bios *, void *, int, u16)) diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/disp.c b/drivers/gpu/drm/nouveau/core/subdev/bios/disp.c new file mode 100644 index 00000000000..7f16e52d9be --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/disp.c @@ -0,0 +1,178 @@ +/* + * 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 <subdev/bios.h> +#include <subdev/bios/bit.h> +#include <subdev/bios/disp.h> + +u16 +nvbios_disp_table(struct nouveau_bios *bios, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, u8 *sub) +{ + struct bit_entry U; + + if (!bit_entry(bios, 'U', &U)) { + if (U.version == 1) { + u16 data = nv_ro16(bios, U.offset); + if (data) { + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x20: + case 0x21: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *cnt = nv_ro08(bios, data + 0x03); + *sub = nv_ro08(bios, data + 0x04); + return data; + default: + break; + } + } + } + } + + return 0x0000; +} + +u16 +nvbios_disp_entry(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *len, u8 *sub) +{ + u8 hdr, cnt; + u16 data = nvbios_disp_table(bios, ver, &hdr, &cnt, len, sub); + if (data && idx < cnt) + return data + hdr + (idx * *len); + *ver = 0x00; + return 0x0000; +} + +u16 +nvbios_disp_parse(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *len, u8 *sub, + struct nvbios_disp *info) +{ + u16 data = nvbios_disp_entry(bios, idx, ver, len, sub); + if (data && *len >= 2) { + info->data = nv_ro16(bios, data + 0); + return data; + } + return 0x0000; +} + +u16 +nvbios_outp_entry(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + struct nvbios_disp info; + u16 data = nvbios_disp_parse(bios, idx, ver, len, hdr, &info); + if (data) { + *cnt = nv_ro08(bios, info.data + 0x05); + *len = 0x06; + data = info.data; + } + return data; +} + +u16 +nvbios_outp_parse(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *info) +{ + u16 data = nvbios_outp_entry(bios, idx, ver, hdr, cnt, len); + if (data && *hdr >= 0x0a) { + info->type = nv_ro16(bios, data + 0x00); + info->mask = nv_ro32(bios, data + 0x02); + if (*ver <= 0x20) /* match any link */ + info->mask |= 0x00c0; + info->script[0] = nv_ro16(bios, data + 0x06); + info->script[1] = nv_ro16(bios, data + 0x08); + info->script[2] = 0x0000; + if (*hdr >= 0x0c) + info->script[2] = nv_ro16(bios, data + 0x0a); + return data; + } + return 0x0000; +} + +u16 +nvbios_outp_match(struct nouveau_bios *bios, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_outp *info) +{ + u16 data, idx = 0; + while ((data = nvbios_outp_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) { + if (data && info->type == type) { + if ((info->mask & mask) == mask) + break; + } + } + return data; +} + +u16 +nvbios_ocfg_entry(struct nouveau_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + if (idx < *cnt) + return outp + *hdr + (idx * *len); + return 0x0000; +} + +u16 +nvbios_ocfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_ocfg *info) +{ + u16 data = nvbios_ocfg_entry(bios, outp, idx, ver, hdr, cnt, len); + if (data) { + info->match = nv_ro16(bios, data + 0x00); + info->clkcmp[0] = nv_ro16(bios, data + 0x02); + info->clkcmp[1] = nv_ro16(bios, data + 0x04); + } + return data; +} + +u16 +nvbios_ocfg_match(struct nouveau_bios *bios, u16 outp, u16 type, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_ocfg *info) +{ + u16 data, idx = 0; + while ((data = nvbios_ocfg_parse(bios, outp, idx++, ver, hdr, cnt, len, info))) { + if (info->match == type) + break; + } + return data; +} + +u16 +nvbios_oclk_match(struct nouveau_bios *bios, u16 cmp, u32 khz) +{ + while (cmp) { + if (khz / 10 >= nv_ro16(bios, cmp + 0x00)) + return nv_ro16(bios, cmp + 0x02); + cmp += 0x04; + } + return 0x0000; +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c index 3cbc0f3e8d5..663853bcca8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/dp.c @@ -25,23 +25,29 @@ #include "subdev/bios.h" #include "subdev/bios/bit.h" -#include "subdev/bios/dcb.h" #include "subdev/bios/dp.h" -u16 -dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +static u16 +nvbios_dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) { - struct bit_entry bit_d; + struct bit_entry d; - if (!bit_entry(bios, 'd', &bit_d)) { - if (bit_d.version == 1) { - u16 data = nv_ro16(bios, bit_d.offset); + if (!bit_entry(bios, 'd', &d)) { + if (d.version == 1 && d.length >= 2) { + u16 data = nv_ro16(bios, d.offset); if (data) { - *ver = nv_ro08(bios, data + 0); - *hdr = nv_ro08(bios, data + 1); - *len = nv_ro08(bios, data + 2); - *cnt = nv_ro08(bios, data + 3); - return data; + *ver = nv_ro08(bios, data + 0x00); + switch (*ver) { + case 0x21: + case 0x30: + case 0x40: + *hdr = nv_ro08(bios, data + 0x01); + *len = nv_ro08(bios, data + 0x02); + *cnt = nv_ro08(bios, data + 0x03); + return data; + default: + break; + } } } } @@ -49,28 +55,150 @@ dp_table(struct nouveau_bios *bios, u8 *ver, u8 *hdr, u8 *cnt, u8 *len) return 0x0000; } +static u16 +nvbios_dpout_entry(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + u16 data = nvbios_dp_table(bios, ver, hdr, cnt, len); + if (data && idx < *cnt) { + u16 outp = nv_ro16(bios, data + *hdr + idx * *len); + switch (*ver * !!outp) { + case 0x21: + case 0x30: + *hdr = nv_ro08(bios, data + 0x04); + *len = nv_ro08(bios, data + 0x05); + *cnt = nv_ro08(bios, outp + 0x04); + break; + case 0x40: + *hdr = nv_ro08(bios, data + 0x04); + *cnt = 0; + *len = 0; + break; + default: + break; + } + return outp; + } + *ver = 0x00; + return 0x0000; +} + u16 -dp_outp(struct nouveau_bios *bios, u8 idx, u8 *ver, u8 *len) +nvbios_dpout_parse(struct nouveau_bios *bios, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *info) { - u8 hdr, cnt; - u16 table = dp_table(bios, ver, &hdr, &cnt, len); - if (table && idx < cnt) - return nv_ro16(bios, table + hdr + (idx * *len)); - return 0xffff; + u16 data = nvbios_dpout_entry(bios, idx, ver, hdr, cnt, len); + if (data && *ver) { + info->type = nv_ro16(bios, data + 0x00); + info->mask = nv_ro16(bios, data + 0x02); + switch (*ver) { + case 0x21: + case 0x30: + info->flags = nv_ro08(bios, data + 0x05); + info->script[0] = nv_ro16(bios, data + 0x06); + info->script[1] = nv_ro16(bios, data + 0x08); + info->lnkcmp = nv_ro16(bios, data + 0x0a); + info->script[2] = nv_ro16(bios, data + 0x0c); + info->script[3] = nv_ro16(bios, data + 0x0e); + info->script[4] = nv_ro16(bios, data + 0x10); + break; + case 0x40: + info->flags = nv_ro08(bios, data + 0x04); + info->script[0] = nv_ro16(bios, data + 0x05); + info->script[1] = nv_ro16(bios, data + 0x07); + info->lnkcmp = nv_ro16(bios, data + 0x09); + info->script[2] = nv_ro16(bios, data + 0x0b); + info->script[3] = nv_ro16(bios, data + 0x0d); + info->script[4] = nv_ro16(bios, data + 0x0f); + break; + default: + data = 0x0000; + break; + } + } + return data; } u16 -dp_outp_match(struct nouveau_bios *bios, struct dcb_output *outp, - u8 *ver, u8 *len) +nvbios_dpout_match(struct nouveau_bios *bios, u16 type, u16 mask, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpout *info) { - u8 idx = 0; - u16 data; - while ((data = dp_outp(bios, idx++, ver, len)) != 0xffff) { - if (data) { - u32 hash = nv_ro32(bios, data); - if (dcb_hash_match(outp, hash)) - return data; + u16 data, idx = 0; + while ((data = nvbios_dpout_parse(bios, idx++, ver, hdr, cnt, len, info)) || *ver) { + if (data && info->type == type) { + if ((info->mask & mask) == mask) + break; } } + return data; +} + +static u16 +nvbios_dpcfg_entry(struct nouveau_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len) +{ + if (*ver >= 0x40) { + outp = nvbios_dp_table(bios, ver, hdr, cnt, len); + *hdr = *hdr + (*len * * cnt); + *len = nv_ro08(bios, outp + 0x06); + *cnt = nv_ro08(bios, outp + 0x07); + } + + if (idx < *cnt) + return outp + *hdr + (idx * *len); + return 0x0000; } + +u16 +nvbios_dpcfg_parse(struct nouveau_bios *bios, u16 outp, u8 idx, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpcfg *info) +{ + u16 data = nvbios_dpcfg_entry(bios, outp, idx, ver, hdr, cnt, len); + if (data) { + switch (*ver) { + case 0x21: + info->drv = nv_ro08(bios, data + 0x02); + info->pre = nv_ro08(bios, data + 0x03); + info->unk = nv_ro08(bios, data + 0x04); + break; + case 0x30: + case 0x40: + info->drv = nv_ro08(bios, data + 0x01); + info->pre = nv_ro08(bios, data + 0x02); + info->unk = nv_ro08(bios, data + 0x03); + break; + default: + data = 0x0000; + break; + } + } + return data; +} + +u16 +nvbios_dpcfg_match(struct nouveau_bios *bios, u16 outp, u8 un, u8 vs, u8 pe, + u8 *ver, u8 *hdr, u8 *cnt, u8 *len, + struct nvbios_dpcfg *info) +{ + u8 idx = 0xff; + u16 data; + + if (*ver >= 0x30) { + const u8 vsoff[] = { 0, 4, 7, 9 }; + idx = (un * 10) + vsoff[vs] + pe; + } else { + while ((data = nvbios_dpcfg_entry(bios, outp, idx, + ver, hdr, cnt, len))) { + if (nv_ro08(bios, data + 0x00) == vs && + nv_ro08(bios, data + 0x01) == pe) + break; + idx++; + } + } + + return nvbios_dpcfg_parse(bios, outp, pe, ver, hdr, cnt, len, info); +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c b/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c index 4c9f1e50816..c90d4aa3ae4 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/gpio.c @@ -101,8 +101,8 @@ dcb_gpio_parse(struct nouveau_bios *bios, int idx, u8 func, u8 line, } /* DCB 2.2, fixed TVDAC GPIO data */ - if ((entry = dcb_table(bios, &ver, &hdr, &cnt, &len)) && ver >= 0x22) { - if (func == DCB_GPIO_TVDAC0) { + if ((entry = dcb_table(bios, &ver, &hdr, &cnt, &len))) { + if (ver >= 0x22 && ver < 0x30 && func == DCB_GPIO_TVDAC0) { u8 conf = nv_ro08(bios, entry - 5); u8 addr = nv_ro08(bios, entry - 4); if (conf & 0x01) { diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 6be8c32f6e4..ae168bbb86d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -743,9 +743,10 @@ static void init_dp_condition(struct nvbios_init *init) { struct nouveau_bios *bios = init->bios; + struct nvbios_dpout info; u8 cond = nv_ro08(bios, init->offset + 1); u8 unkn = nv_ro08(bios, init->offset + 2); - u8 ver, len; + u8 ver, hdr, cnt, len; u16 data; trace("DP_CONDITION\t0x%02x 0x%02x\n", cond, unkn); @@ -759,10 +760,12 @@ init_dp_condition(struct nvbios_init *init) case 1: case 2: if ( init->outp && - (data = dp_outp_match(bios, init->outp, &ver, &len))) { - if (ver <= 0x40 && !(nv_ro08(bios, data + 5) & cond)) - init_exec_set(init, false); - if (ver == 0x40 && !(nv_ro08(bios, data + 4) & cond)) + (data = nvbios_dpout_match(bios, DCB_OUTPUT_DP, + (init->outp->or << 0) | + (init->outp->sorconf.link << 6), + &ver, &hdr, &cnt, &len, &info))) + { + if (!(info.flags & cond)) init_exec_set(init, false); break; } diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/base.c b/drivers/gpu/drm/nouveau/core/subdev/device/base.c index ca9a4648bd8..f8a7ed4166c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/base.c @@ -25,7 +25,6 @@ #include <core/object.h> #include <core/device.h> #include <core/client.h> -#include <core/device.h> #include <core/option.h> #include <core/class.h> @@ -61,19 +60,24 @@ struct nouveau_devobj { static const u64 disable_map[] = { [NVDEV_SUBDEV_VBIOS] = NV_DEVICE_DISABLE_VBIOS, + [NVDEV_SUBDEV_DEVINIT] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_GPIO] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_I2C] = NV_DEVICE_DISABLE_CORE, - [NVDEV_SUBDEV_DEVINIT] = NV_DEVICE_DISABLE_CORE, + [NVDEV_SUBDEV_CLOCK] = NV_DEVICE_DISABLE_CORE, + [NVDEV_SUBDEV_MXM] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_MC] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_TIMER] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_FB] = NV_DEVICE_DISABLE_CORE, - [NVDEV_SUBDEV_VM] = NV_DEVICE_DISABLE_CORE, + [NVDEV_SUBDEV_LTCG] = NV_DEVICE_DISABLE_CORE, + [NVDEV_SUBDEV_IBUS] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_INSTMEM] = NV_DEVICE_DISABLE_CORE, + [NVDEV_SUBDEV_VM] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_BAR] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_VOLT] = NV_DEVICE_DISABLE_CORE, - [NVDEV_SUBDEV_CLOCK] = NV_DEVICE_DISABLE_CORE, [NVDEV_SUBDEV_THERM] = NV_DEVICE_DISABLE_CORE, [NVDEV_ENGINE_DMAOBJ] = NV_DEVICE_DISABLE_CORE, + [NVDEV_ENGINE_FIFO] = NV_DEVICE_DISABLE_FIFO, + [NVDEV_ENGINE_SW] = NV_DEVICE_DISABLE_FIFO, [NVDEV_ENGINE_GR] = NV_DEVICE_DISABLE_GRAPH, [NVDEV_ENGINE_MPEG] = NV_DEVICE_DISABLE_MPEG, [NVDEV_ENGINE_ME] = NV_DEVICE_DISABLE_ME, @@ -84,7 +88,7 @@ static const u64 disable_map[] = { [NVDEV_ENGINE_COPY0] = NV_DEVICE_DISABLE_COPY0, [NVDEV_ENGINE_COPY1] = NV_DEVICE_DISABLE_COPY1, [NVDEV_ENGINE_UNK1C1] = NV_DEVICE_DISABLE_UNK1C1, - [NVDEV_ENGINE_FIFO] = NV_DEVICE_DISABLE_FIFO, + [NVDEV_ENGINE_VENC] = NV_DEVICE_DISABLE_VENC, [NVDEV_ENGINE_DISP] = NV_DEVICE_DISABLE_DISP, [NVDEV_SUBDEV_NR] = 0, }; @@ -208,7 +212,7 @@ nouveau_devobj_ctor(struct nouveau_object *parent, /* determine frequency of timing crystal */ if ( device->chipset < 0x17 || - (device->chipset >= 0x20 && device->chipset <= 0x25)) + (device->chipset >= 0x20 && device->chipset < 0x25)) strap &= 0x00000040; else strap &= 0x00400040; @@ -356,37 +360,37 @@ fail: } static u8 -nouveau_devobj_rd08(struct nouveau_object *object, u32 addr) +nouveau_devobj_rd08(struct nouveau_object *object, u64 addr) { return nv_rd08(object->engine, addr); } static u16 -nouveau_devobj_rd16(struct nouveau_object *object, u32 addr) +nouveau_devobj_rd16(struct nouveau_object *object, u64 addr) { return nv_rd16(object->engine, addr); } static u32 -nouveau_devobj_rd32(struct nouveau_object *object, u32 addr) +nouveau_devobj_rd32(struct nouveau_object *object, u64 addr) { return nv_rd32(object->engine, addr); } static void -nouveau_devobj_wr08(struct nouveau_object *object, u32 addr, u8 data) +nouveau_devobj_wr08(struct nouveau_object *object, u64 addr, u8 data) { nv_wr08(object->engine, addr, data); } static void -nouveau_devobj_wr16(struct nouveau_object *object, u32 addr, u16 data) +nouveau_devobj_wr16(struct nouveau_object *object, u64 addr, u16 data) { nv_wr16(object->engine, addr, data); } static void -nouveau_devobj_wr32(struct nouveau_object *object, u32 addr, u32 data) +nouveau_devobj_wr32(struct nouveau_object *object, u64 addr, u32 data) { nv_wr32(object->engine, addr, data); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c index f09accfd0e3..9c40b0fb23f 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv10.c @@ -105,7 +105,7 @@ nv10_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv1a_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -159,7 +159,7 @@ nv10_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv1a_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c index 5fa58b7369b..74f88f48e1c 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv20.c @@ -72,7 +72,7 @@ nv20_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv20_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv25_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -90,7 +90,7 @@ nv20_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv20_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv25_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -108,7 +108,7 @@ nv20_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv20_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv25_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c index 7f4b8fe6ccc..0ac1b2c4f61 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv30.c @@ -72,7 +72,7 @@ nv30_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv30_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv35_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -109,7 +109,7 @@ nv30_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv20_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv30_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv36_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -128,7 +128,7 @@ nv30_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv10_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv30_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv10_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv04_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv04_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c index 42deadca0f0..41d59689a02 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv40.c @@ -76,7 +76,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv41_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -96,7 +96,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv41_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -116,7 +116,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv41_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -156,7 +156,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv47_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -176,7 +176,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv49_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -196,7 +196,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv04_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv49_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv41_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -216,7 +216,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv44_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -236,7 +236,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -256,7 +256,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv44_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -276,7 +276,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -296,7 +296,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv4e_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -316,7 +316,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -336,7 +336,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; @@ -356,7 +356,7 @@ nv40_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_DEVINIT] = &nv1a_devinit_oclass; device->oclass[NVDEV_SUBDEV_MC ] = &nv44_mc_oclass; device->oclass[NVDEV_SUBDEV_TIMER ] = &nv04_timer_oclass; - device->oclass[NVDEV_SUBDEV_FB ] = &nv40_fb_oclass; + device->oclass[NVDEV_SUBDEV_FB ] = &nv46_fb_oclass; device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv40_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nv44_vmmgr_oclass; device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nv04_dmaeng_oclass; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c index fec3bcc9a6f..6ccfd8585ba 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nv50.c @@ -98,7 +98,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv84_disp_oclass; break; case 0x86: device->cname = "G86"; @@ -123,7 +123,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv84_disp_oclass; break; case 0x92: device->cname = "G92"; @@ -148,7 +148,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv84_disp_oclass; break; case 0x94: device->cname = "G94"; @@ -173,7 +173,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; case 0x96: device->cname = "G96"; @@ -198,7 +198,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; case 0x98: device->cname = "G98"; @@ -223,7 +223,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; case 0xa0: device->cname = "G200"; @@ -248,7 +248,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; device->oclass[NVDEV_ENGINE_CRYPT ] = &nv84_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva0_disp_oclass; break; case 0xaa: device->cname = "MCP77/MCP78"; @@ -273,7 +273,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; case 0xac: device->cname = "MCP79/MCP7A"; @@ -298,7 +298,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_CRYPT ] = &nv98_crypt_oclass; device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nv94_disp_oclass; break; case 0xa3: device->cname = "GT215"; @@ -324,7 +324,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xa5: device->cname = "GT216"; @@ -349,7 +349,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xa8: device->cname = "GT218"; @@ -374,7 +374,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xaf: device->cname = "MCP89"; @@ -399,7 +399,7 @@ nv50_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nva3_copy_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; default: nv_fatal(device, "unknown Tesla chipset\n"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c index 6697f0f9c29..f0461685a42 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nvc0.c @@ -74,12 +74,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xc4: device->cname = "GF104"; @@ -102,12 +102,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xc3: device->cname = "GF106"; @@ -130,12 +130,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xce: device->cname = "GF114"; @@ -158,12 +158,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xcf: device->cname = "GF116"; @@ -186,12 +186,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xc1: device->cname = "GF108"; @@ -214,12 +214,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xc8: device->cname = "GF110"; @@ -242,12 +242,12 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nvc0_copy1_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nv50_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nva3_disp_oclass; break; case 0xd9: device->cname = "GF119"; @@ -266,13 +266,13 @@ nvc0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; - device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nvc0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nvc0_graph_oclass; - device->oclass[NVDEV_ENGINE_VP ] = &nv84_vp_oclass; - device->oclass[NVDEV_ENGINE_BSP ] = &nv84_bsp_oclass; - device->oclass[NVDEV_ENGINE_PPP ] = &nv98_ppp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nvc0_vp_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nvc0_bsp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nvc0_copy0_oclass; device->oclass[NVDEV_ENGINE_DISP ] = &nvd0_disp_oclass; break; diff --git a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c index 4a280b7ab85..9b7881e7663 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/device/nve0.c @@ -45,6 +45,9 @@ #include <engine/graph.h> #include <engine/disp.h> #include <engine/copy.h> +#include <engine/bsp.h> +#include <engine/vp.h> +#include <engine/ppp.h> int nve0_identify(struct nouveau_device *device) @@ -67,13 +70,16 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; - device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nve0_graph_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nvd0_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; break; case 0xe7: device->cname = "GK107"; @@ -92,13 +98,16 @@ nve0_identify(struct nouveau_device *device) device->oclass[NVDEV_SUBDEV_INSTMEM] = &nv50_instmem_oclass; device->oclass[NVDEV_SUBDEV_VM ] = &nvc0_vmmgr_oclass; device->oclass[NVDEV_SUBDEV_BAR ] = &nvc0_bar_oclass; - device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvc0_dmaeng_oclass; + device->oclass[NVDEV_ENGINE_DMAOBJ ] = &nvd0_dmaeng_oclass; device->oclass[NVDEV_ENGINE_FIFO ] = &nve0_fifo_oclass; device->oclass[NVDEV_ENGINE_SW ] = &nvc0_software_oclass; device->oclass[NVDEV_ENGINE_GR ] = &nve0_graph_oclass; - device->oclass[NVDEV_ENGINE_DISP ] = &nvd0_disp_oclass; + device->oclass[NVDEV_ENGINE_DISP ] = &nve0_disp_oclass; device->oclass[NVDEV_ENGINE_COPY0 ] = &nve0_copy0_oclass; device->oclass[NVDEV_ENGINE_COPY1 ] = &nve0_copy1_oclass; + device->oclass[NVDEV_ENGINE_BSP ] = &nve0_bsp_oclass; + device->oclass[NVDEV_ENGINE_VP ] = &nve0_vp_oclass; + device->oclass[NVDEV_ENGINE_PPP ] = &nvc0_ppp_oclass; break; default: nv_fatal(device, "unknown Kepler chipset\n"); diff --git a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c index 61becfa732e..ae7249b0979 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/devinit/nv50.c @@ -22,6 +22,10 @@ * Authors: Ben Skeggs */ +#include <subdev/bios.h> +#include <subdev/bios/dcb.h> +#include <subdev/bios/disp.h> +#include <subdev/bios/init.h> #include <subdev/devinit.h> #include <subdev/vga.h> @@ -55,7 +59,12 @@ nv50_devinit_dtor(struct nouveau_object *object) static int nv50_devinit_init(struct nouveau_object *object) { + struct nouveau_bios *bios = nouveau_bios(object); struct nv50_devinit_priv *priv = (void *)object; + struct nvbios_outp info; + struct dcb_output outp; + u8 ver = 0xff, hdr, cnt, len; + int ret, i = 0; if (!priv->base.post) { if (!nv_rdvgac(priv, 0, 0x00) && @@ -65,7 +74,30 @@ nv50_devinit_init(struct nouveau_object *object) } } - return nouveau_devinit_init(&priv->base); + ret = nouveau_devinit_init(&priv->base); + if (ret) + return ret; + + /* if we ran the init tables, execute first script pointer for each + * display table output entry that has a matching dcb entry. + */ + while (priv->base.post && ver) { + u16 data = nvbios_outp_parse(bios, i++, &ver, &hdr, &cnt, &len, &info); + if (data && dcb_outp_match(bios, info.type, info.mask, &ver, &len, &outp)) { + struct nvbios_init init = { + .subdev = nv_subdev(priv), + .bios = bios, + .offset = info.script[0], + .outp = &outp, + .crtc = -1, + .execute = 1, + }; + + nvbios_exec(&init); + } + }; + + return 0; } static int diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c index f0086de8af3..d6d16007ec1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/base.c @@ -57,25 +57,45 @@ nouveau_fb_bios_memtype(struct nouveau_bios *bios) } int -nouveau_fb_init(struct nouveau_fb *pfb) +nouveau_fb_preinit(struct nouveau_fb *pfb) { - int ret, i; + static const char *name[] = { + [NV_MEM_TYPE_UNKNOWN] = "unknown", + [NV_MEM_TYPE_STOLEN ] = "stolen system memory", + [NV_MEM_TYPE_SGRAM ] = "SGRAM", + [NV_MEM_TYPE_SDRAM ] = "SDRAM", + [NV_MEM_TYPE_DDR1 ] = "DDR1", + [NV_MEM_TYPE_DDR2 ] = "DDR2", + [NV_MEM_TYPE_DDR3 ] = "DDR3", + [NV_MEM_TYPE_GDDR2 ] = "GDDR2", + [NV_MEM_TYPE_GDDR3 ] = "GDDR3", + [NV_MEM_TYPE_GDDR4 ] = "GDDR4", + [NV_MEM_TYPE_GDDR5 ] = "GDDR5", + }; + int ret, tags; - ret = nouveau_subdev_init(&pfb->base); - if (ret) - return ret; + tags = pfb->ram.init(pfb); + if (tags < 0 || !pfb->ram.size) { + nv_fatal(pfb, "error detecting memory configuration!!\n"); + return (tags < 0) ? tags : -ERANGE; + } - for (i = 0; i < pfb->tile.regions; i++) - pfb->tile.prog(pfb, i, &pfb->tile.region[i]); + if (!nouveau_mm_initialised(&pfb->vram)) { + ret = nouveau_mm_init(&pfb->vram, 0, pfb->ram.size >> 12, 1); + if (ret) + return ret; + } - return 0; -} + if (!nouveau_mm_initialised(&pfb->tags) && tags) { + ret = nouveau_mm_init(&pfb->tags, 0, ++tags, 1); + if (ret) + return ret; + } -int -_nouveau_fb_init(struct nouveau_object *object) -{ - struct nouveau_fb *pfb = (void *)object; - return nouveau_fb_init(pfb); + nv_info(pfb, "RAM type: %s\n", name[pfb->ram.type]); + nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram.size >> 20)); + nv_info(pfb, " ZCOMP: %d tags\n", tags); + return 0; } void @@ -85,12 +105,8 @@ nouveau_fb_destroy(struct nouveau_fb *pfb) for (i = 0; i < pfb->tile.regions; i++) pfb->tile.fini(pfb, i, &pfb->tile.region[i]); - - if (pfb->tags.block_size) - nouveau_mm_fini(&pfb->tags); - - if (pfb->vram.block_size) - nouveau_mm_fini(&pfb->vram); + nouveau_mm_fini(&pfb->tags); + nouveau_mm_fini(&pfb->vram); nouveau_subdev_destroy(&pfb->base); } @@ -101,30 +117,24 @@ _nouveau_fb_dtor(struct nouveau_object *object) struct nouveau_fb *pfb = (void *)object; nouveau_fb_destroy(pfb); } - int -nouveau_fb_created(struct nouveau_fb *pfb) +nouveau_fb_init(struct nouveau_fb *pfb) { - static const char *name[] = { - [NV_MEM_TYPE_UNKNOWN] = "unknown", - [NV_MEM_TYPE_STOLEN ] = "stolen system memory", - [NV_MEM_TYPE_SGRAM ] = "SGRAM", - [NV_MEM_TYPE_SDRAM ] = "SDRAM", - [NV_MEM_TYPE_DDR1 ] = "DDR1", - [NV_MEM_TYPE_DDR2 ] = "DDR2", - [NV_MEM_TYPE_DDR3 ] = "DDR3", - [NV_MEM_TYPE_GDDR2 ] = "GDDR2", - [NV_MEM_TYPE_GDDR3 ] = "GDDR3", - [NV_MEM_TYPE_GDDR4 ] = "GDDR4", - [NV_MEM_TYPE_GDDR5 ] = "GDDR5", - }; + int ret, i; - if (pfb->ram.size == 0) { - nv_fatal(pfb, "no vram detected!!\n"); - return -ERANGE; - } + ret = nouveau_subdev_init(&pfb->base); + if (ret) + return ret; + + for (i = 0; i < pfb->tile.regions; i++) + pfb->tile.prog(pfb, i, &pfb->tile.region[i]); - nv_info(pfb, "RAM type: %s\n", name[pfb->ram.type]); - nv_info(pfb, "RAM size: %d MiB\n", (int)(pfb->ram.size >> 20)); return 0; } + +int +_nouveau_fb_init(struct nouveau_object *object) +{ + struct nouveau_fb *pfb = (void *)object; + return nouveau_fb_init(pfb); +} diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c index eb06836b69f..6e369f85361 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv04.c @@ -56,6 +56,37 @@ nv04_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) } static int +nv04_fb_vram_init(struct nouveau_fb *pfb) +{ + u32 boot0 = nv_rd32(pfb, NV04_PFB_BOOT_0); + if (boot0 & 0x00000100) { + pfb->ram.size = ((boot0 >> 12) & 0xf) * 2 + 2; + pfb->ram.size *= 1024 * 1024; + } else { + switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { + case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: + pfb->ram.size = 32 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: + pfb->ram.size = 16 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: + pfb->ram.size = 8 * 1024 * 1024; + break; + case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: + pfb->ram.size = 4 * 1024 * 1024; + break; + } + } + + if ((boot0 & 0x00000038) <= 0x10) + pfb->ram.type = NV_MEM_TYPE_SGRAM; + else + pfb->ram.type = NV_MEM_TYPE_SDRAM; + return 0; +} + +static int nv04_fb_init(struct nouveau_object *object) { struct nv04_fb_priv *priv = (void *)object; @@ -79,7 +110,6 @@ nv04_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nv04_fb_priv *priv; - u32 boot0; int ret; ret = nouveau_fb_create(parent, engine, oclass, &priv); @@ -87,35 +117,9 @@ nv04_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - boot0 = nv_rd32(priv, NV04_PFB_BOOT_0); - if (boot0 & 0x00000100) { - priv->base.ram.size = ((boot0 >> 12) & 0xf) * 2 + 2; - priv->base.ram.size *= 1024 * 1024; - } else { - switch (boot0 & NV04_PFB_BOOT_0_RAM_AMOUNT) { - case NV04_PFB_BOOT_0_RAM_AMOUNT_32MB: - priv->base.ram.size = 32 * 1024 * 1024; - break; - case NV04_PFB_BOOT_0_RAM_AMOUNT_16MB: - priv->base.ram.size = 16 * 1024 * 1024; - break; - case NV04_PFB_BOOT_0_RAM_AMOUNT_8MB: - priv->base.ram.size = 8 * 1024 * 1024; - break; - case NV04_PFB_BOOT_0_RAM_AMOUNT_4MB: - priv->base.ram.size = 4 * 1024 * 1024; - break; - } - } - - if ((boot0 & 0x00000038) <= 0x10) - priv->base.ram.type = NV_MEM_TYPE_SGRAM; - else - priv->base.ram.type = NV_MEM_TYPE_SDRAM; - - priv->base.memtype_valid = nv04_fb_memtype_valid; - return nouveau_fb_created(&priv->base); + priv->base.ram.init = nv04_fb_vram_init; + return nouveau_fb_preinit(&priv->base); } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c index f037a422d2f..edbbe26e858 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv10.c @@ -30,7 +30,20 @@ struct nv10_fb_priv { struct nouveau_fb base; }; -static void +static int +nv10_fb_vram_init(struct nouveau_fb *pfb) +{ + u32 cfg0 = nv_rd32(pfb, 0x100200); + if (cfg0 & 0x00000001) + pfb->ram.type = NV_MEM_TYPE_DDR1; + else + pfb->ram.type = NV_MEM_TYPE_SDRAM; + + pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + return 0; +} + +void nv10_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *tile) { @@ -39,7 +52,7 @@ nv10_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, tile->pitch = pitch; } -static void +void nv10_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) { tile->addr = 0; @@ -54,6 +67,7 @@ nv10_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit); nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch); nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100240 + (i * 0x10)); } static int @@ -61,7 +75,6 @@ nv10_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { - struct nouveau_device *device = nv_device(parent); struct nv10_fb_priv *priv; int ret; @@ -70,42 +83,13 @@ nv10_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - if (device->chipset == 0x1a || device->chipset == 0x1f) { - struct pci_dev *bridge; - u32 mem, mib; - - bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); - if (!bridge) { - nv_fatal(device, "no bridge device\n"); - return 0; - } - - if (device->chipset == 0x1a) { - pci_read_config_dword(bridge, 0x7c, &mem); - mib = ((mem >> 6) & 31) + 1; - } else { - pci_read_config_dword(bridge, 0x84, &mem); - mib = ((mem >> 4) & 127) + 1; - } - - priv->base.ram.type = NV_MEM_TYPE_STOLEN; - priv->base.ram.size = mib * 1024 * 1024; - } else { - u32 cfg0 = nv_rd32(priv, 0x100200); - if (cfg0 & 0x00000001) - priv->base.ram.type = NV_MEM_TYPE_DDR1; - else - priv->base.ram.type = NV_MEM_TYPE_SDRAM; - - priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000; - } - priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv10_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv10_fb_tile_init; priv->base.tile.fini = nv10_fb_tile_fini; priv->base.tile.prog = nv10_fb_tile_prog; - return nouveau_fb_created(&priv->base); + return nouveau_fb_preinit(&priv->base); } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c new file mode 100644 index 00000000000..48366841db4 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv1a.c @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv1a_fb_priv { + struct nouveau_fb base; +}; + +static int +nv1a_fb_vram_init(struct nouveau_fb *pfb) +{ + struct pci_dev *bridge; + u32 mem, mib; + + bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1)); + if (!bridge) { + nv_fatal(pfb, "no bridge device\n"); + return -ENODEV; + } + + if (nv_device(pfb)->chipset == 0x1a) { + pci_read_config_dword(bridge, 0x7c, &mem); + mib = ((mem >> 6) & 31) + 1; + } else { + pci_read_config_dword(bridge, 0x84, &mem); + mib = ((mem >> 4) & 127) + 1; + } + + pfb->ram.type = NV_MEM_TYPE_STOLEN; + pfb->ram.size = mib * 1024 * 1024; + return 0; +} + +static int +nv1a_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv1a_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv1a_fb_vram_init; + priv->base.tile.regions = 8; + priv->base.tile.init = nv10_fb_tile_init; + priv->base.tile.fini = nv10_fb_tile_fini; + priv->base.tile.prog = nv10_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + +struct nouveau_oclass +nv1a_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x1a), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv1a_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = _nouveau_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c index 4b3578fcb7f..5d14612a2c8 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv20.c @@ -30,43 +30,54 @@ struct nv20_fb_priv { struct nouveau_fb base; }; -static void +int +nv20_fb_vram_init(struct nouveau_fb *pfb) +{ + u32 pbus1218 = nv_rd32(pfb, 0x001218); + + switch (pbus1218 & 0x00000300) { + case 0x00000000: pfb->ram.type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: pfb->ram.type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: pfb->ram.type = NV_MEM_TYPE_GDDR2; break; + } + pfb->ram.size = (nv_rd32(pfb, 0x10020c) & 0xff000000); + pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + + return nv_rd32(pfb, 0x100320); +} + +void nv20_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *tile) { - struct nouveau_device *device = nv_device(pfb); - int bpp = (flags & 2) ? 32 : 16; - tile->addr = 0x00000001 | addr; tile->limit = max(1u, addr + size) - 1; tile->pitch = pitch; - - /* Allocate some of the on-die tag memory, used to store Z - * compression meta-data (most likely just a bitmap determining - * if a given tile is compressed or not). - */ - size /= 256; if (flags & 4) { - if (!nouveau_mm_head(&pfb->tags, 1, size, size, 1, &tile->tag)) { - /* Enable Z compression */ - tile->zcomp = tile->tag->offset; - if (device->chipset >= 0x25) { - if (bpp == 16) - tile->zcomp |= 0x00100000; - else - tile->zcomp |= 0x00200000; - } else { - tile->zcomp |= 0x80000000; - if (bpp != 16) - tile->zcomp |= 0x04000000; - } - } - + pfb->tile.comp(pfb, i, size, flags, tile); tile->addr |= 2; } } static void +nv20_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, + struct nouveau_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00000000; /* Z16 */ + else tile->zcomp = 0x04000000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; + tile->zcomp |= 0x80000000; /* enable */ +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x08000000; +#endif + } +} + +void nv20_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) { tile->addr = 0; @@ -76,12 +87,13 @@ nv20_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) nouveau_mm_free(&pfb->tags, &tile->tag); } -static void +void nv20_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) { nv_wr32(pfb, 0x100244 + (i * 0x10), tile->limit); nv_wr32(pfb, 0x100248 + (i * 0x10), tile->pitch); nv_wr32(pfb, 0x100240 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100240 + (i * 0x10)); nv_wr32(pfb, 0x100300 + (i * 0x04), tile->zcomp); } @@ -90,9 +102,7 @@ nv20_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { - struct nouveau_device *device = nv_device(parent); struct nv20_fb_priv *priv; - u32 pbus1218; int ret; ret = nouveau_fb_create(parent, engine, oclass, &priv); @@ -100,28 +110,14 @@ nv20_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - pbus1218 = nv_rd32(priv, 0x001218); - switch (pbus1218 & 0x00000300) { - case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_SDRAM; break; - case 0x00000100: priv->base.ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000200: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000300: priv->base.ram.type = NV_MEM_TYPE_GDDR2; break; - } - priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000; - - if (device->chipset >= 0x25) - ret = nouveau_mm_init(&priv->base.tags, 0, 64 * 1024, 1); - else - ret = nouveau_mm_init(&priv->base.tags, 0, 32 * 1024, 1); - if (ret) - return ret; - priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv20_fb_tile_init; + priv->base.tile.comp = nv20_fb_tile_comp; priv->base.tile.fini = nv20_fb_tile_fini; priv->base.tile.prog = nv20_fb_tile_prog; - return nouveau_fb_created(&priv->base); + return nouveau_fb_preinit(&priv->base); } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c new file mode 100644 index 00000000000..0042ace6bef --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv25.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv25_fb_priv { + struct nouveau_fb base; +}; + +static void +nv25_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, + struct nouveau_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (!(flags & 2)) tile->zcomp = 0x00100000; /* Z16 */ + else tile->zcomp = 0x00200000; /* Z24S8 */ + tile->zcomp |= tile->tag->offset; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x01000000; +#endif + } +} + +static int +nv25_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv25_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv20_fb_vram_init; + priv->base.tile.regions = 8; + priv->base.tile.init = nv20_fb_tile_init; + priv->base.tile.comp = nv25_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv20_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + +struct nouveau_oclass +nv25_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x25), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv25_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = _nouveau_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c index cba67bc9139..a7ba0d048ae 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv30.c @@ -34,17 +34,36 @@ void nv30_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, u32 flags, struct nouveau_fb_tile *tile) { - tile->addr = addr | 1; + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) { + tile->addr = (0 << 4); + } else { + if (pfb->tile.comp) /* z compression */ + pfb->tile.comp(pfb, i, size, flags, tile); + tile->addr = (1 << 4); + } + + tile->addr |= 0x00000001; /* enable */ + tile->addr |= addr; tile->limit = max(1u, addr + size) - 1; tile->pitch = pitch; } -void -nv30_fb_tile_fini(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) +static void +nv30_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, + struct nouveau_fb_tile *tile) { - tile->addr = 0; - tile->limit = 0; - tile->pitch = 0; + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x01000000; /* Z16 */ + else tile->zcomp |= 0x02000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 12; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x10000000; +#endif + } } static int @@ -72,7 +91,7 @@ calc_ref(struct nv30_fb_priv *priv, int l, int k, int i) return x; } -static int +int nv30_fb_init(struct nouveau_object *object) { struct nouveau_device *device = nv_device(object); @@ -111,7 +130,6 @@ nv30_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_object **pobject) { struct nv30_fb_priv *priv; - u32 pbus1218; int ret; ret = nouveau_fb_create(parent, engine, oclass, &priv); @@ -119,21 +137,14 @@ nv30_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - pbus1218 = nv_rd32(priv, 0x001218); - switch (pbus1218 & 0x00000300) { - case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_SDRAM; break; - case 0x00000100: priv->base.ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000200: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000300: priv->base.ram.type = NV_MEM_TYPE_GDDR2; break; - } - priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000; - priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv20_fb_vram_init; priv->base.tile.regions = 8; priv->base.tile.init = nv30_fb_tile_init; - priv->base.tile.fini = nv30_fb_tile_fini; - priv->base.tile.prog = nv10_fb_tile_prog; - return nouveau_fb_created(&priv->base); + priv->base.tile.comp = nv30_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv20_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); } struct nouveau_oclass diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c new file mode 100644 index 00000000000..092f6f4f352 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv35.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv35_fb_priv { + struct nouveau_fb base; +}; + +static void +nv35_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, + struct nouveau_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x04000000; /* Z16 */ + else tile->zcomp |= 0x08000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } +} + +static int +nv35_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv35_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv20_fb_vram_init; + priv->base.tile.regions = 8; + priv->base.tile.init = nv30_fb_tile_init; + priv->base.tile.comp = nv35_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv20_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + +struct nouveau_oclass +nv35_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x35), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv35_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv30_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c new file mode 100644 index 00000000000..797ab3b821b --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv36.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv36_fb_priv { + struct nouveau_fb base; +}; + +static void +nv36_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, + struct nouveau_fb_tile *tile) +{ + u32 tiles = DIV_ROUND_UP(size, 0x40); + u32 tags = round_up(tiles / pfb->ram.parts, 0x40); + if (!nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + if (flags & 2) tile->zcomp |= 0x10000000; /* Z16 */ + else tile->zcomp |= 0x20000000; /* Z24S8 */ + tile->zcomp |= ((tile->tag->offset ) >> 6); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 6) << 14; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x80000000; +#endif + } +} + +static int +nv36_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv36_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv20_fb_vram_init; + priv->base.tile.regions = 8; + priv->base.tile.init = nv30_fb_tile_init; + priv->base.tile.comp = nv36_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv20_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + +struct nouveau_oclass +nv36_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x36), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv36_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv30_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c index 347a496fcad..65e131b90f3 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv40.c @@ -30,34 +30,37 @@ struct nv40_fb_priv { struct nouveau_fb base; }; -static inline int -nv44_graph_class(struct nouveau_device *device) -{ - if ((device->chipset & 0xf0) == 0x60) - return 1; - - return !(0x0baf & (1 << (device->chipset & 0x0f))); -} - -static void -nv40_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) +static int +nv40_fb_vram_init(struct nouveau_fb *pfb) { - nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); - nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); - nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); -} + u32 pbus1218 = nv_rd32(pfb, 0x001218); + switch (pbus1218 & 0x00000300) { + case 0x00000000: pfb->ram.type = NV_MEM_TYPE_SDRAM; break; + case 0x00000100: pfb->ram.type = NV_MEM_TYPE_DDR1; break; + case 0x00000200: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000300: pfb->ram.type = NV_MEM_TYPE_DDR2; break; + } -static void -nv40_fb_init_gart(struct nv40_fb_priv *priv) -{ - nv_wr32(priv, 0x100800, 0x00000001); + pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + return nv_rd32(pfb, 0x100320); } -static void -nv44_fb_init_gart(struct nv40_fb_priv *priv) +void +nv40_fb_tile_comp(struct nouveau_fb *pfb, int i, u32 size, u32 flags, + struct nouveau_fb_tile *tile) { - nv_wr32(priv, 0x100850, 0x80000000); - nv_wr32(priv, 0x100800, 0x00000001); + u32 tiles = DIV_ROUND_UP(size, 0x80); + u32 tags = round_up(tiles / pfb->ram.parts, 0x100); + if ( (flags & 2) && + !nouveau_mm_head(&pfb->tags, 1, tags, tags, 1, &tile->tag)) { + tile->zcomp = 0x28000000; /* Z24S8_SPLIT_GRAD */ + tile->zcomp |= ((tile->tag->offset ) >> 8); + tile->zcomp |= ((tile->tag->offset + tags - 1) >> 8) << 13; +#ifdef __BIG_ENDIAN + tile->zcomp |= 0x40000000; +#endif + } } static int @@ -70,19 +73,7 @@ nv40_fb_init(struct nouveau_object *object) if (ret) return ret; - switch (nv_device(priv)->chipset) { - case 0x40: - case 0x45: - nv_mask(priv, 0x10033c, 0x00008000, 0x00000000); - break; - default: - if (nv44_graph_class(nv_device(priv))) - nv44_fb_init_gart(priv); - else - nv40_fb_init_gart(priv); - break; - } - + nv_mask(priv, 0x10033c, 0x00008000, 0x00000000); return 0; } @@ -91,7 +82,6 @@ nv40_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) { - struct nouveau_device *device = nv_device(parent); struct nv40_fb_priv *priv; int ret; @@ -100,69 +90,14 @@ nv40_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (ret) return ret; - /* 0x001218 is actually present on a few other NV4X I looked at, - * and even contains sane values matching 0x100474. From looking - * at various vbios images however, this isn't the case everywhere. - * So, I chose to use the same regs I've seen NVIDIA reading around - * the memory detection, hopefully that'll get us the right numbers - */ - if (device->chipset == 0x40) { - u32 pbus1218 = nv_rd32(priv, 0x001218); - switch (pbus1218 & 0x00000300) { - case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_SDRAM; break; - case 0x00000100: priv->base.ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000200: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000300: priv->base.ram.type = NV_MEM_TYPE_DDR2; break; - } - } else - if (device->chipset == 0x49 || device->chipset == 0x4b) { - u32 pfb914 = nv_rd32(priv, 0x100914); - switch (pfb914 & 0x00000003) { - case 0x00000000: priv->base.ram.type = NV_MEM_TYPE_DDR1; break; - case 0x00000001: priv->base.ram.type = NV_MEM_TYPE_DDR2; break; - case 0x00000002: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break; - case 0x00000003: break; - } - } else - if (device->chipset != 0x4e) { - u32 pfb474 = nv_rd32(priv, 0x100474); - if (pfb474 & 0x00000004) - priv->base.ram.type = NV_MEM_TYPE_GDDR3; - if (pfb474 & 0x00000002) - priv->base.ram.type = NV_MEM_TYPE_DDR2; - if (pfb474 & 0x00000001) - priv->base.ram.type = NV_MEM_TYPE_DDR1; - } else { - priv->base.ram.type = NV_MEM_TYPE_STOLEN; - } - - priv->base.ram.size = nv_rd32(priv, 0x10020c) & 0xff000000; - priv->base.memtype_valid = nv04_fb_memtype_valid; - switch (device->chipset) { - case 0x40: - case 0x45: - priv->base.tile.regions = 8; - break; - case 0x46: - case 0x47: - case 0x49: - case 0x4b: - case 0x4c: - priv->base.tile.regions = 15; - break; - default: - priv->base.tile.regions = 12; - break; - } + priv->base.ram.init = nv40_fb_vram_init; + priv->base.tile.regions = 8; priv->base.tile.init = nv30_fb_tile_init; - priv->base.tile.fini = nv30_fb_tile_fini; - if (device->chipset == 0x40) - priv->base.tile.prog = nv10_fb_tile_prog; - else - priv->base.tile.prog = nv40_fb_tile_prog; - - return nouveau_fb_created(&priv->base); + priv->base.tile.comp = nv40_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv20_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c new file mode 100644 index 00000000000..e9e5a08c41a --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv41.c @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv41_fb_priv { + struct nouveau_fb base; +}; + +int +nv41_fb_vram_init(struct nouveau_fb *pfb) +{ + u32 pfb474 = nv_rd32(pfb, 0x100474); + if (pfb474 & 0x00000004) + pfb->ram.type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + pfb->ram.type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + pfb->ram.type = NV_MEM_TYPE_DDR1; + + pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + return nv_rd32(pfb, 0x100320); +} + +void +nv41_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) +{ + nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100600 + (i * 0x10)); + nv_wr32(pfb, 0x100700 + (i * 0x04), tile->zcomp); +} + +int +nv41_fb_init(struct nouveau_object *object) +{ + struct nv41_fb_priv *priv = (void *)object; + int ret; + + ret = nouveau_fb_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100800, 0x00000001); + return 0; +} + +static int +nv41_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv41_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv41_fb_vram_init; + priv->base.tile.regions = 12; + priv->base.tile.init = nv30_fb_tile_init; + priv->base.tile.comp = nv40_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv41_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + + +struct nouveau_oclass +nv41_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x41), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv41_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv41_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c new file mode 100644 index 00000000000..ae89b5006f7 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv44.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv44_fb_priv { + struct nouveau_fb base; +}; + +int +nv44_fb_vram_init(struct nouveau_fb *pfb) +{ + u32 pfb474 = nv_rd32(pfb, 0x100474); + if (pfb474 & 0x00000004) + pfb->ram.type = NV_MEM_TYPE_GDDR3; + if (pfb474 & 0x00000002) + pfb->ram.type = NV_MEM_TYPE_DDR2; + if (pfb474 & 0x00000001) + pfb->ram.type = NV_MEM_TYPE_DDR1; + + pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + return 0; +} + +static void +nv44_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nouveau_fb_tile *tile) +{ + tile->addr = 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +void +nv44_fb_tile_prog(struct nouveau_fb *pfb, int i, struct nouveau_fb_tile *tile) +{ + nv_wr32(pfb, 0x100604 + (i * 0x10), tile->limit); + nv_wr32(pfb, 0x100608 + (i * 0x10), tile->pitch); + nv_wr32(pfb, 0x100600 + (i * 0x10), tile->addr); + nv_rd32(pfb, 0x100600 + (i * 0x10)); +} + +int +nv44_fb_init(struct nouveau_object *object) +{ + struct nv44_fb_priv *priv = (void *)object; + int ret; + + ret = nouveau_fb_init(&priv->base); + if (ret) + return ret; + + nv_wr32(priv, 0x100850, 0x80000000); + nv_wr32(priv, 0x100800, 0x00000001); + return 0; +} + +static int +nv44_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv44_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv44_fb_vram_init; + priv->base.tile.regions = 12; + priv->base.tile.init = nv44_fb_tile_init; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv44_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + + +struct nouveau_oclass +nv44_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x44), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv44_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv44_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c new file mode 100644 index 00000000000..589b93ea299 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv46.c @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv46_fb_priv { + struct nouveau_fb base; +}; + +void +nv46_fb_tile_init(struct nouveau_fb *pfb, int i, u32 addr, u32 size, u32 pitch, + u32 flags, struct nouveau_fb_tile *tile) +{ + /* for performance, select alternate bank offset for zeta */ + if (!(flags & 4)) tile->addr = (0 << 3); + else tile->addr = (1 << 3); + + tile->addr |= 0x00000001; /* mode = vram */ + tile->addr |= addr; + tile->limit = max(1u, addr + size) - 1; + tile->pitch = pitch; +} + +static int +nv46_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv46_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv44_fb_vram_init; + priv->base.tile.regions = 15; + priv->base.tile.init = nv46_fb_tile_init; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv44_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + + +struct nouveau_oclass +nv46_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x46), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv46_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv44_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c new file mode 100644 index 00000000000..818bba35b36 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv47.c @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv47_fb_priv { + struct nouveau_fb base; +}; + +static int +nv47_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv47_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv41_fb_vram_init; + priv->base.tile.regions = 15; + priv->base.tile.init = nv30_fb_tile_init; + priv->base.tile.comp = nv40_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv41_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + + +struct nouveau_oclass +nv47_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x47), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv47_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv41_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c new file mode 100644 index 00000000000..84a31af16ab --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv49.c @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv49_fb_priv { + struct nouveau_fb base; +}; + +static int +nv49_fb_vram_init(struct nouveau_fb *pfb) +{ + u32 pfb914 = nv_rd32(pfb, 0x100914); + + switch (pfb914 & 0x00000003) { + case 0x00000000: pfb->ram.type = NV_MEM_TYPE_DDR1; break; + case 0x00000001: pfb->ram.type = NV_MEM_TYPE_DDR2; break; + case 0x00000002: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; + case 0x00000003: break; + } + + pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + pfb->ram.parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1; + return nv_rd32(pfb, 0x100320); +} + +static int +nv49_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv49_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv49_fb_vram_init; + priv->base.tile.regions = 15; + priv->base.tile.init = nv30_fb_tile_init; + priv->base.tile.comp = nv40_fb_tile_comp; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv41_fb_tile_prog; + + return nouveau_fb_preinit(&priv->base); +} + + +struct nouveau_oclass +nv49_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x49), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv49_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv41_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c new file mode 100644 index 00000000000..797fd558170 --- /dev/null +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv4e.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2010 Francisco Jerez. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include <subdev/fb.h> + +struct nv4e_fb_priv { + struct nouveau_fb base; +}; + +static int +nv4e_fb_vram_init(struct nouveau_fb *pfb) +{ + pfb->ram.size = nv_rd32(pfb, 0x10020c) & 0xff000000; + pfb->ram.type = NV_MEM_TYPE_STOLEN; + return 0; +} + +static int +nv4e_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nv4e_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->base.memtype_valid = nv04_fb_memtype_valid; + priv->base.ram.init = nv4e_fb_vram_init; + priv->base.tile.regions = 12; + priv->base.tile.init = nv46_fb_tile_init; + priv->base.tile.fini = nv20_fb_tile_fini; + priv->base.tile.prog = nv44_fb_tile_prog; + return nouveau_fb_preinit(&priv->base); +} + +struct nouveau_oclass +nv4e_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x4e), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv4e_fb_ctor, + .dtor = _nouveau_fb_dtor, + .init = nv44_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c index 5f570806143..487cb8c6c20 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nv50.c @@ -51,6 +51,101 @@ nv50_fb_memtype_valid(struct nouveau_fb *pfb, u32 memtype) return types[(memtype & 0xff00) >> 8] != 0; } +static u32 +nv50_fb_vram_rblock(struct nouveau_fb *pfb) +{ + int i, parts, colbits, rowbitsa, rowbitsb, banks; + u64 rowsize, predicted; + u32 r0, r4, rt, ru, rblock_size; + + r0 = nv_rd32(pfb, 0x100200); + r4 = nv_rd32(pfb, 0x100204); + rt = nv_rd32(pfb, 0x100250); + ru = nv_rd32(pfb, 0x001540); + nv_debug(pfb, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru); + + for (i = 0, parts = 0; i < 8; i++) { + if (ru & (0x00010000 << i)) + parts++; + } + + colbits = (r4 & 0x0000f000) >> 12; + rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; + rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; + banks = 1 << (((r4 & 0x03000000) >> 24) + 2); + + rowsize = parts * banks * (1 << colbits) * 8; + predicted = rowsize << rowbitsa; + if (r0 & 0x00000004) + predicted += rowsize << rowbitsb; + + if (predicted != pfb->ram.size) { + nv_warn(pfb, "memory controller reports %d MiB VRAM\n", + (u32)(pfb->ram.size >> 20)); + } + + rblock_size = rowsize; + if (rt & 1) + rblock_size *= 3; + + nv_debug(pfb, "rblock %d bytes\n", rblock_size); + return rblock_size; +} + +static int +nv50_fb_vram_init(struct nouveau_fb *pfb) +{ + struct nouveau_device *device = nv_device(pfb); + struct nouveau_bios *bios = nouveau_bios(device); + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 size; + int ret; + + pfb->ram.size = nv_rd32(pfb, 0x10020c); + pfb->ram.size = (pfb->ram.size & 0xffffff00) | + ((pfb->ram.size & 0x000000ff) << 32); + + size = (pfb->ram.size >> 12) - rsvd_head - rsvd_tail; + switch (device->chipset) { + case 0xaa: + case 0xac: + case 0xaf: /* IGPs, no reordering, no real VRAM */ + ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, 1); + if (ret) + return ret; + + pfb->ram.type = NV_MEM_TYPE_STOLEN; + pfb->ram.stolen = (u64)nv_rd32(pfb, 0x100e10) << 12; + break; + default: + switch (nv_rd32(pfb, 0x100714) & 0x00000007) { + case 0: pfb->ram.type = NV_MEM_TYPE_DDR1; break; + case 1: + if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) + pfb->ram.type = NV_MEM_TYPE_DDR3; + else + pfb->ram.type = NV_MEM_TYPE_DDR2; + break; + case 2: pfb->ram.type = NV_MEM_TYPE_GDDR3; break; + case 3: pfb->ram.type = NV_MEM_TYPE_GDDR4; break; + case 4: pfb->ram.type = NV_MEM_TYPE_GDDR5; break; + default: + break; + } + + ret = nouveau_mm_init(&pfb->vram, rsvd_head, size, + nv50_fb_vram_rblock(pfb) >> 12); + if (ret) + return ret; + + pfb->ram.ranks = (nv_rd32(pfb, 0x100200) & 0x4) ? 2 : 1; + break; + } + + return nv_rd32(pfb, 0x100320); +} + static int nv50_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, u32 memtype, struct nouveau_mem **pmem) @@ -140,195 +235,6 @@ nv50_fb_vram_del(struct nouveau_fb *pfb, struct nouveau_mem **pmem) kfree(mem); } -static u32 -nv50_vram_rblock(struct nv50_fb_priv *priv) -{ - int i, parts, colbits, rowbitsa, rowbitsb, banks; - u64 rowsize, predicted; - u32 r0, r4, rt, ru, rblock_size; - - r0 = nv_rd32(priv, 0x100200); - r4 = nv_rd32(priv, 0x100204); - rt = nv_rd32(priv, 0x100250); - ru = nv_rd32(priv, 0x001540); - nv_debug(priv, "memcfg 0x%08x 0x%08x 0x%08x 0x%08x\n", r0, r4, rt, ru); - - for (i = 0, parts = 0; i < 8; i++) { - if (ru & (0x00010000 << i)) - parts++; - } - - colbits = (r4 & 0x0000f000) >> 12; - rowbitsa = ((r4 & 0x000f0000) >> 16) + 8; - rowbitsb = ((r4 & 0x00f00000) >> 20) + 8; - banks = 1 << (((r4 & 0x03000000) >> 24) + 2); - - rowsize = parts * banks * (1 << colbits) * 8; - predicted = rowsize << rowbitsa; - if (r0 & 0x00000004) - predicted += rowsize << rowbitsb; - - if (predicted != priv->base.ram.size) { - nv_warn(priv, "memory controller reports %d MiB VRAM\n", - (u32)(priv->base.ram.size >> 20)); - } - - rblock_size = rowsize; - if (rt & 1) - rblock_size *= 3; - - nv_debug(priv, "rblock %d bytes\n", rblock_size); - return rblock_size; -} - -static int -nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, - struct nouveau_oclass *oclass, void *data, u32 size, - struct nouveau_object **pobject) -{ - struct nouveau_device *device = nv_device(parent); - struct nouveau_bios *bios = nouveau_bios(device); - const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ - const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ - struct nv50_fb_priv *priv; - u32 tags; - int ret; - - ret = nouveau_fb_create(parent, engine, oclass, &priv); - *pobject = nv_object(priv); - if (ret) - return ret; - - switch (nv_rd32(priv, 0x100714) & 0x00000007) { - case 0: priv->base.ram.type = NV_MEM_TYPE_DDR1; break; - case 1: - if (nouveau_fb_bios_memtype(bios) == NV_MEM_TYPE_DDR3) - priv->base.ram.type = NV_MEM_TYPE_DDR3; - else - priv->base.ram.type = NV_MEM_TYPE_DDR2; - break; - case 2: priv->base.ram.type = NV_MEM_TYPE_GDDR3; break; - case 3: priv->base.ram.type = NV_MEM_TYPE_GDDR4; break; - case 4: priv->base.ram.type = NV_MEM_TYPE_GDDR5; break; - default: - break; - } - - priv->base.ram.size = nv_rd32(priv, 0x10020c); - priv->base.ram.size = (priv->base.ram.size & 0xffffff00) | - ((priv->base.ram.size & 0x000000ff) << 32); - - tags = nv_rd32(priv, 0x100320); - ret = nouveau_mm_init(&priv->base.tags, 0, tags, 1); - if (ret) - return ret; - - nv_debug(priv, "%d compression tags\n", tags); - - size = (priv->base.ram.size >> 12) - rsvd_head - rsvd_tail; - switch (device->chipset) { - case 0xaa: - case 0xac: - case 0xaf: /* IGPs, no reordering, no real VRAM */ - ret = nouveau_mm_init(&priv->base.vram, rsvd_head, size, 1); - if (ret) - return ret; - - priv->base.ram.stolen = (u64)nv_rd32(priv, 0x100e10) << 12; - priv->base.ram.type = NV_MEM_TYPE_STOLEN; - break; - default: - ret = nouveau_mm_init(&priv->base.vram, rsvd_head, size, - nv50_vram_rblock(priv) >> 12); - if (ret) - return ret; - - priv->base.ram.ranks = (nv_rd32(priv, 0x100200) & 0x4) ? 2 : 1; - break; - } - - priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); - if (priv->r100c08_page) { - priv->r100c08 = pci_map_page(device->pdev, priv->r100c08_page, - 0, PAGE_SIZE, - PCI_DMA_BIDIRECTIONAL); - if (pci_dma_mapping_error(device->pdev, priv->r100c08)) - nv_warn(priv, "failed 0x100c08 page map\n"); - } else { - nv_warn(priv, "failed 0x100c08 page alloc\n"); - } - - priv->base.memtype_valid = nv50_fb_memtype_valid; - priv->base.ram.get = nv50_fb_vram_new; - priv->base.ram.put = nv50_fb_vram_del; - return nouveau_fb_created(&priv->base); -} - -static void -nv50_fb_dtor(struct nouveau_object *object) -{ - struct nouveau_device *device = nv_device(object); - struct nv50_fb_priv *priv = (void *)object; - - if (priv->r100c08_page) { - pci_unmap_page(device->pdev, priv->r100c08, PAGE_SIZE, - PCI_DMA_BIDIRECTIONAL); - __free_page(priv->r100c08_page); - } - - nouveau_fb_destroy(&priv->base); -} - -static int -nv50_fb_init(struct nouveau_object *object) -{ - struct nouveau_device *device = nv_device(object); - struct nv50_fb_priv *priv = (void *)object; - int ret; - - ret = nouveau_fb_init(&priv->base); - if (ret) - return ret; - - /* Not a clue what this is exactly. Without pointing it at a - * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) - * cause IOMMU "read from address 0" errors (rh#561267) - */ - nv_wr32(priv, 0x100c08, priv->r100c08 >> 8); - - /* This is needed to get meaningful information from 100c90 - * on traps. No idea what these values mean exactly. */ - switch (device->chipset) { - case 0x50: - nv_wr32(priv, 0x100c90, 0x000707ff); - break; - case 0xa3: - case 0xa5: - case 0xa8: - nv_wr32(priv, 0x100c90, 0x000d0fff); - break; - case 0xaf: - nv_wr32(priv, 0x100c90, 0x089d1fff); - break; - default: - nv_wr32(priv, 0x100c90, 0x001d07ff); - break; - } - - return 0; -} - -struct nouveau_oclass -nv50_fb_oclass = { - .handle = NV_SUBDEV(FB, 0x50), - .ofuncs = &(struct nouveau_ofuncs) { - .ctor = nv50_fb_ctor, - .dtor = nv50_fb_dtor, - .init = nv50_fb_init, - .fini = _nouveau_fb_fini, - }, -}; - static const struct nouveau_enum vm_dispatch_subclients[] = { { 0x00000000, "GRCTX", NULL }, { 0x00000001, "NOTIFY", NULL }, @@ -424,11 +330,11 @@ static const struct nouveau_enum vm_fault[] = { {} }; -void -nv50_fb_trap(struct nouveau_fb *pfb, int display) +static void +nv50_fb_intr(struct nouveau_subdev *subdev) { - struct nouveau_device *device = nv_device(pfb); - struct nv50_fb_priv *priv = (void *)pfb; + struct nouveau_device *device = nv_device(subdev); + struct nv50_fb_priv *priv = (void *)subdev; const struct nouveau_enum *en, *cl; u32 trap[6], idx, chan; u8 st0, st1, st2, st3; @@ -445,9 +351,6 @@ nv50_fb_trap(struct nouveau_fb *pfb, int display) } nv_wr32(priv, 0x100c90, idx | 0x80000000); - if (!display) - return; - /* decode status bits into something more useful */ if (device->chipset < 0xa3 || device->chipset == 0xaa || device->chipset == 0xac) { @@ -494,3 +397,101 @@ nv50_fb_trap(struct nouveau_fb *pfb, int display) else printk("0x%08x\n", st1); } + +static int +nv50_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, + struct nouveau_oclass *oclass, void *data, u32 size, + struct nouveau_object **pobject) +{ + struct nouveau_device *device = nv_device(parent); + struct nv50_fb_priv *priv; + int ret; + + ret = nouveau_fb_create(parent, engine, oclass, &priv); + *pobject = nv_object(priv); + if (ret) + return ret; + + priv->r100c08_page = alloc_page(GFP_KERNEL | __GFP_ZERO); + if (priv->r100c08_page) { + priv->r100c08 = pci_map_page(device->pdev, priv->r100c08_page, + 0, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(device->pdev, priv->r100c08)) + nv_warn(priv, "failed 0x100c08 page map\n"); + } else { + nv_warn(priv, "failed 0x100c08 page alloc\n"); + } + + priv->base.memtype_valid = nv50_fb_memtype_valid; + priv->base.ram.init = nv50_fb_vram_init; + priv->base.ram.get = nv50_fb_vram_new; + priv->base.ram.put = nv50_fb_vram_del; + nv_subdev(priv)->intr = nv50_fb_intr; + return nouveau_fb_preinit(&priv->base); +} + +static void +nv50_fb_dtor(struct nouveau_object *object) +{ + struct nouveau_device *device = nv_device(object); + struct nv50_fb_priv *priv = (void *)object; + + if (priv->r100c08_page) { + pci_unmap_page(device->pdev, priv->r100c08, PAGE_SIZE, + PCI_DMA_BIDIRECTIONAL); + __free_page(priv->r100c08_page); + } + + nouveau_fb_destroy(&priv->base); +} + +static int +nv50_fb_init(struct nouveau_object *object) +{ + struct nouveau_device *device = nv_device(object); + struct nv50_fb_priv *priv = (void *)object; + int ret; + + ret = nouveau_fb_init(&priv->base); + if (ret) + return ret; + + /* Not a clue what this is exactly. Without pointing it at a + * scratch page, VRAM->GART blits with M2MF (as in DDX DFS) + * cause IOMMU "read from address 0" errors (rh#561267) + */ + nv_wr32(priv, 0x100c08, priv->r100c08 >> 8); + + /* This is needed to get meaningful information from 100c90 + * on traps. No idea what these values mean exactly. */ + switch (device->chipset) { + case 0x50: + nv_wr32(priv, 0x100c90, 0x000707ff); + break; + case 0xa3: + case 0xa5: + case 0xa8: + nv_wr32(priv, 0x100c90, 0x000d0fff); + break; + case 0xaf: + nv_wr32(priv, 0x100c90, 0x089d1fff); + break; + default: + nv_wr32(priv, 0x100c90, 0x001d07ff); + break; + } + + return 0; +} + +struct nouveau_oclass +nv50_fb_oclass = { + .handle = NV_SUBDEV(FB, 0x50), + .ofuncs = &(struct nouveau_ofuncs) { + .ctor = nv50_fb_ctor, + .dtor = nv50_fb_dtor, + .init = nv50_fb_init, + .fini = _nouveau_fb_fini, + }, +}; diff --git a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c index 9f59f2bf007..306bdf12145 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/fb/nvc0.c @@ -62,6 +62,65 @@ nvc0_fb_memtype_valid(struct nouveau_fb *pfb, u32 tile_flags) } static int +nvc0_fb_vram_init(struct nouveau_fb *pfb) +{ + struct nouveau_bios *bios = nouveau_bios(pfb); + const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ + const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ + u32 parts = nv_rd32(pfb, 0x022438); + u32 pmask = nv_rd32(pfb, 0x022554); + u32 bsize = nv_rd32(pfb, 0x10f20c); + u32 offset, length; + bool uniform = true; + int ret, part; + + nv_debug(pfb, "0x100800: 0x%08x\n", nv_rd32(pfb, 0x100800)); + nv_debug(pfb, "parts 0x%08x mask 0x%08x\n", parts, pmask); + + pfb->ram.type = nouveau_fb_bios_memtype(bios); + pfb->ram.ranks = (nv_rd32(pfb, 0x10f200) & 0x00000004) ? 2 : 1; + + /* read amount of vram attached to each memory controller */ + for (part = 0; part < parts; part++) { + if (!(pmask & (1 << part))) { + u32 psize = nv_rd32(pfb, 0x11020c + (part * 0x1000)); + if (psize != bsize) { + if (psize < bsize) + bsize = psize; + uniform = false; + } + + nv_debug(pfb, "%d: mem_amount 0x%08x\n", part, psize); + pfb->ram.size += (u64)psize << 20; + } + } + + /* if all controllers have the same amount attached, there's no holes */ + if (uniform) { + offset = rsvd_head; + length = (pfb->ram.size >> 12) - rsvd_head - rsvd_tail; + return nouveau_mm_init(&pfb->vram, offset, length, 1); + } + + /* otherwise, address lowest common amount from 0GiB */ + ret = nouveau_mm_init(&pfb->vram, rsvd_head, (bsize << 8) * parts, 1); + if (ret) + return ret; + + /* and the rest starting from (8GiB + common_size) */ + offset = (0x0200000000ULL >> 12) + (bsize << 8); + length = (pfb->ram.size >> 12) - (bsize << 8) - rsvd_tail; + + ret = nouveau_mm_init(&pfb->vram, offset, length, 0); + if (ret) { + nouveau_mm_fini(&pfb->vram); + return ret; + } + + return 0; +} + +static int nvc0_fb_vram_new(struct nouveau_fb *pfb, u64 size, u32 align, u32 ncmin, u32 memtype, struct nouveau_mem **pmem) { @@ -139,66 +198,6 @@ nvc0_fb_dtor(struct nouveau_object *object) } static int -nvc0_vram_detect(struct nvc0_fb_priv *priv) -{ - struct nouveau_bios *bios = nouveau_bios(priv); - struct nouveau_fb *pfb = &priv->base; - const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */ - const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */ - u32 parts = nv_rd32(priv, 0x022438); - u32 pmask = nv_rd32(priv, 0x022554); - u32 bsize = nv_rd32(priv, 0x10f20c); - u32 offset, length; - bool uniform = true; - int ret, part; - - nv_debug(priv, "0x100800: 0x%08x\n", nv_rd32(priv, 0x100800)); - nv_debug(priv, "parts 0x%08x mask 0x%08x\n", parts, pmask); - - priv->base.ram.type = nouveau_fb_bios_memtype(bios); - priv->base.ram.ranks = (nv_rd32(priv, 0x10f200) & 0x00000004) ? 2 : 1; - - /* read amount of vram attached to each memory controller */ - for (part = 0; part < parts; part++) { - if (!(pmask & (1 << part))) { - u32 psize = nv_rd32(priv, 0x11020c + (part * 0x1000)); - if (psize != bsize) { - if (psize < bsize) - bsize = psize; - uniform = false; - } - - nv_debug(priv, "%d: mem_amount 0x%08x\n", part, psize); - priv->base.ram.size += (u64)psize << 20; - } - } - - /* if all controllers have the same amount attached, there's no holes */ - if (uniform) { - offset = rsvd_head; - length = (priv->base.ram.size >> 12) - rsvd_head - rsvd_tail; - return nouveau_mm_init(&pfb->vram, offset, length, 1); - } - - /* otherwise, address lowest common amount from 0GiB */ - ret = nouveau_mm_init(&pfb->vram, rsvd_head, (bsize << 8) * parts, 1); - if (ret) - return ret; - - /* and the rest starting from (8GiB + common_size) */ - offset = (0x0200000000ULL >> 12) + (bsize << 8); - length = (priv->base.ram.size >> 12) - (bsize << 8) - rsvd_tail; - - ret = nouveau_mm_init(&pfb->vram, offset, length, 0); - if (ret) { - nouveau_mm_fini(&pfb->vram); - return ret; - } - - return 0; -} - -static int nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, struct nouveau_oclass *oclass, void *data, u32 size, struct nouveau_object **pobject) @@ -213,13 +212,10 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, return ret; priv->base.memtype_valid = nvc0_fb_memtype_valid; + priv->base.ram.init = nvc0_fb_vram_init; priv->base.ram.get = nvc0_fb_vram_new; priv->base.ram.put = nv50_fb_vram_del; - ret = nvc0_vram_detect(priv); - if (ret) - return ret; - priv->r100c10_page = alloc_page(GFP_KERNEL | __GFP_ZERO); if (!priv->r100c10_page) return -ENOMEM; @@ -229,7 +225,7 @@ nvc0_fb_ctor(struct nouveau_object *parent, struct nouveau_object *engine, if (pci_dma_mapping_error(device->pdev, priv->r100c10)) return -EFAULT; - return nouveau_fb_created(&priv->base); + return nouveau_fb_preinit(&priv->base); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c index fe1ebf199ba..dc27e794a85 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/aux.c @@ -50,7 +50,7 @@ auxch_init(struct nouveau_i2c *aux, int ch) ctrl = nv_rd32(aux, 0x00e4e4 + (ch * 0x50)); udelay(1); if (!timeout--) { - AUX_ERR("begin idle timeout 0x%08x", ctrl); + AUX_ERR("begin idle timeout 0x%08x\n", ctrl); return -EBUSY; } } while (ctrl & 0x03010000); diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c index ba4d28b5036..f5bbd383411 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c +++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv04.c @@ -63,14 +63,14 @@ nv04_instobj_dtor(struct nouveau_object *object) } static u32 -nv04_instobj_rd32(struct nouveau_object *object, u32 addr) +nv04_instobj_rd32(struct nouveau_object *object, u64 addr) { struct nv04_instobj_priv *node = (void *)object; return nv_ro32(object->engine, node->mem->offset + addr); } static void -nv04_instobj_wr32(struct nouveau_object *object, u32 addr, u32 data) +nv04_instobj_wr32(struct nouveau_object *object, u64 addr, u32 data) { struct nv04_instobj_priv *node = (void *)object; nv_wo32(object->engine, node->mem->offset + addr, data); @@ -173,13 +173,13 @@ nv04_instmem_dtor(struct nouveau_object *object) } static u32 -nv04_instmem_rd32(struct nouveau_object *object, u32 addr) +nv04_instmem_rd32(struct nouveau_object *object, u64 addr) { return nv_rd32(object, 0x700000 + addr); } static void -nv04_instmem_wr32(struct nouveau_object *object, u32 addr, u32 data) +nv04_instmem_wr32(struct nouveau_object *object, u64 addr, u32 data) { return nv_wr32(object, 0x700000 + addr, data); } diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c index 73c52ebd593..da64253201e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv40.c @@ -111,14 +111,14 @@ nv40_instmem_ctor(struct nouveau_object *parent, struct nouveau_object *engine, } static u32 -nv40_instmem_rd32(struct nouveau_object *object, u32 addr) +nv40_instmem_rd32(struct nouveau_object *object, u64 addr) { struct nv04_instmem_priv *priv = (void *)object; return ioread32_native(priv->iomem + addr); } static void -nv40_instmem_wr32(struct nouveau_object *object, u32 addr, u32 data) +nv40_instmem_wr32(struct nouveau_object *object, u64 addr, u32 data) { struct nv04_instmem_priv *priv = (void *)object; iowrite32_native(data, priv->iomem + addr); diff --git a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c index 27ef0891d10..cfc7e31461d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/instmem/nv50.c @@ -76,7 +76,7 @@ nv50_instobj_dtor(struct nouveau_object *object) } static u32 -nv50_instobj_rd32(struct nouveau_object *object, u32 offset) +nv50_instobj_rd32(struct nouveau_object *object, u64 offset) { struct nv50_instmem_priv *priv = (void *)object->engine; struct nv50_instobj_priv *node = (void *)object; @@ -96,7 +96,7 @@ nv50_instobj_rd32(struct nouveau_object *object, u32 offset) } static void -nv50_instobj_wr32(struct nouveau_object *object, u32 offset, u32 data) +nv50_instobj_wr32(struct nouveau_object *object, u64 offset, u32 data) { struct nv50_instmem_priv *priv = (void *)object->engine; struct nv50_instobj_priv *node = (void *)object; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c index de5721cfc4c..8379aafa6e1 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/base.c @@ -30,20 +30,20 @@ nouveau_mc_intr(struct nouveau_subdev *subdev) struct nouveau_mc *pmc = nouveau_mc(subdev); const struct nouveau_mc_intr *map = pmc->intr_map; struct nouveau_subdev *unit; - u32 stat; + u32 stat, intr; - stat = nv_rd32(pmc, 0x000100); + intr = stat = nv_rd32(pmc, 0x000100); while (stat && map->stat) { if (stat & map->stat) { unit = nouveau_subdev(subdev, map->unit); if (unit && unit->intr) unit->intr(unit); - stat &= ~map->stat; + intr &= ~map->stat; } map++; } - if (stat) { + if (intr) { nv_error(pmc, "unknown intr 0x%08x\n", stat); } } diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c index cedf33b0297..8d759f83032 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c @@ -39,6 +39,7 @@ nv50_mc_intr[] = { { 0x00200000, NVDEV_SUBDEV_GPIO }, { 0x04000000, NVDEV_ENGINE_DISP }, { 0x80000000, NVDEV_ENGINE_SW }, + { 0x0000d101, NVDEV_SUBDEV_FB }, {}, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c index a001e4c4d38..ceb5c83f945 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c @@ -40,6 +40,7 @@ nv98_mc_intr[] = { { 0x00400000, NVDEV_ENGINE_COPY0 }, /* NVA3- */ { 0x04000000, NVDEV_ENGINE_DISP }, { 0x80000000, NVDEV_ENGINE_SW }, + { 0x0040d101, NVDEV_SUBDEV_FB }, {}, }; diff --git a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c index c2b81e30a17..92796682722 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c +++ b/drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c @@ -36,6 +36,7 @@ nvc0_mc_intr[] = { { 0x00000100, NVDEV_ENGINE_FIFO }, { 0x00001000, NVDEV_ENGINE_GR }, { 0x00008000, NVDEV_ENGINE_BSP }, + { 0x00020000, NVDEV_ENGINE_VP }, { 0x00100000, NVDEV_SUBDEV_TIMER }, { 0x00200000, NVDEV_SUBDEV_GPIO }, { 0x02000000, NVDEV_SUBDEV_LTCG }, diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index cc79c796afe..d48c02a3652 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -242,14 +242,26 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) if (unlikely(!abi16)) return -ENOMEM; client = nv_client(abi16->client); - - if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0) - return nouveau_abi16_put(abi16, -EINVAL); - device = nv_device(abi16->device); imem = nouveau_instmem(device); pfb = nouveau_fb(device); + /* hack to allow channel engine type specification on kepler */ + if (device->card_type >= NV_E0) { + if (init->fb_ctxdma_handle != ~0) + init->fb_ctxdma_handle = NVE0_CHANNEL_IND_ENGINE_GR; + else + init->fb_ctxdma_handle = init->tt_ctxdma_handle; + + /* allow flips to be executed if this is a graphics channel */ + init->tt_ctxdma_handle = 0; + if (init->fb_ctxdma_handle == NVE0_CHANNEL_IND_ENGINE_GR) + init->tt_ctxdma_handle = 1; + } + + if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0) + return nouveau_abi16_put(abi16, -EINVAL); + /* allocate "abi16 channel" data and make up a handle for it */ init->channel = ffsll(~abi16->handles); if (!init->channel--) @@ -264,11 +276,6 @@ nouveau_abi16_ioctl_channel_alloc(ABI16_IOCTL_ARGS) abi16->handles |= (1 << init->channel); /* create channel object and initialise dma and fence management */ - if (device->card_type >= NV_E0) { - init->fb_ctxdma_handle = NVE0_CHANNEL_IND_ENGINE_GR; - init->tt_ctxdma_handle = 0; - } - ret = nouveau_channel_new(drm, cli, NVDRM_DEVICE, NVDRM_CHAN | init->channel, init->fb_ctxdma_handle, init->tt_ctxdma_handle, &chan->chan); @@ -378,7 +385,7 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS) struct nouveau_abi16_chan *chan, *temp; struct nouveau_abi16_ntfy *ntfy; struct nouveau_object *object; - struct nv_dma_class args; + struct nv_dma_class args = {}; int ret; if (unlikely(!abi16)) diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 48783e14114..d97f20069d3 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c @@ -35,6 +35,14 @@ static struct nouveau_dsm_priv { acpi_handle rom_handle; } nouveau_dsm_priv; +bool nouveau_is_optimus(void) { + return nouveau_dsm_priv.optimus_detected; +} + +bool nouveau_is_v1_dsm(void) { + return nouveau_dsm_priv.dsm_detected; +} + #define NOUVEAU_DSM_HAS_MUX 0x1 #define NOUVEAU_DSM_HAS_OPT 0x2 @@ -183,9 +191,7 @@ static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switchero static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) { - /* perhaps the _DSM functions are mutually exclusive, but prepare for - * the future */ - if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) + if (!nouveau_dsm_priv.dsm_detected) return 0; if (id == VGA_SWITCHEROO_IGD) return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); @@ -201,7 +207,7 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, /* Optimus laptops have the card already disabled in * nouveau_switcheroo_set_state */ - if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) + if (!nouveau_dsm_priv.dsm_detected) return 0; return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); @@ -283,24 +289,24 @@ static bool nouveau_dsm_detect(void) has_optimus = 1; } - if (vga_count == 2 && has_dsm && guid_valid) { + /* find the optimus DSM or the old v1 DSM */ + if (has_optimus == 1) { acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); - printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", + printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", acpi_method_name); - nouveau_dsm_priv.dsm_detected = true; + nouveau_dsm_priv.optimus_detected = true; ret = true; - } - - if (has_optimus == 1) { + } else if (vga_count == 2 && has_dsm && guid_valid) { acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); - printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", + printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", acpi_method_name); - nouveau_dsm_priv.optimus_detected = true; + nouveau_dsm_priv.dsm_detected = true; ret = true; } + return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.h b/drivers/gpu/drm/nouveau/nouveau_acpi.h index 08af67722b5..d0da230d770 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.h +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.h @@ -4,6 +4,8 @@ #define ROM_BIOS_PAGE 4096 #if defined(CONFIG_ACPI) +bool nouveau_is_optimus(void); +bool nouveau_is_v1_dsm(void); void nouveau_register_dsm_handler(void); void nouveau_unregister_dsm_handler(void); void nouveau_switcheroo_optimus_dsm(void); @@ -11,6 +13,8 @@ int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); bool nouveau_acpi_rom_supported(struct pci_dev *pdev); void *nouveau_acpi_edid(struct drm_device *, struct drm_connector *); #else +static inline bool nouveau_is_optimus(void) { return false; }; +static inline bool nouveau_is_v1_dsm(void) { return false; }; static inline void nouveau_register_dsm_handler(void) {} static inline void nouveau_unregister_dsm_handler(void) {} static inline void nouveau_switcheroo_optimus_dsm(void) {} diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c index 09fdef23588..865eddfa30a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.c +++ b/drivers/gpu/drm/nouveau/nouveau_bios.c @@ -624,206 +624,6 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b return 0; } -/* BIT 'U'/'d' table encoder subtables have hashes matching them to - * a particular set of encoders. - * - * This function returns true if a particular DCB entry matches. - */ -bool -bios_encoder_match(struct dcb_output *dcb, u32 hash) -{ - if ((hash & 0x000000f0) != (dcb->location << 4)) - return false; - if ((hash & 0x0000000f) != dcb->type) - return false; - if (!(hash & (dcb->or << 16))) - return false; - - switch (dcb->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - if (hash & 0x00c00000) { - if (!(hash & (dcb->sorconf.link << 22))) - return false; - } - default: - return true; - } -} - -int -nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk, - struct dcb_output *dcbent, int crtc) -{ - /* - * The display script table is located by the BIT 'U' table. - * - * It contains an array of pointers to various tables describing - * a particular output type. The first 32-bits of the output - * tables contains similar information to a DCB entry, and is - * used to decide whether that particular table is suitable for - * the output you want to access. - * - * The "record header length" field here seems to indicate the - * offset of the first configuration entry in the output tables. - * This is 10 on most cards I've seen, but 12 has been witnessed - * on DP cards, and there's another script pointer within the - * header. - * - * offset + 0 ( 8 bits): version - * offset + 1 ( 8 bits): header length - * offset + 2 ( 8 bits): record length - * offset + 3 ( 8 bits): number of records - * offset + 4 ( 8 bits): record header length - * offset + 5 (16 bits): pointer to first output script table - */ - - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvbios *bios = &drm->vbios; - uint8_t *table = &bios->data[bios->display.script_table_ptr]; - uint8_t *otable = NULL; - uint16_t script; - int i; - - if (!bios->display.script_table_ptr) { - NV_ERROR(drm, "No pointer to output script table\n"); - return 1; - } - - /* - * Nothing useful has been in any of the pre-2.0 tables I've seen, - * so until they are, we really don't need to care. - */ - if (table[0] < 0x20) - return 1; - - if (table[0] != 0x20 && table[0] != 0x21) { - NV_ERROR(drm, "Output script table version 0x%02x unknown\n", - table[0]); - return 1; - } - - /* - * The output script tables describing a particular output type - * look as follows: - * - * offset + 0 (32 bits): output this table matches (hash of DCB) - * offset + 4 ( 8 bits): unknown - * offset + 5 ( 8 bits): number of configurations - * offset + 6 (16 bits): pointer to some script - * offset + 8 (16 bits): pointer to some script - * - * headerlen == 10 - * offset + 10 : configuration 0 - * - * headerlen == 12 - * offset + 10 : pointer to some script - * offset + 12 : configuration 0 - * - * Each config entry is as follows: - * - * offset + 0 (16 bits): unknown, assumed to be a match value - * offset + 2 (16 bits): pointer to script table (clock set?) - * offset + 4 (16 bits): pointer to script table (reset?) - * - * There doesn't appear to be a count value to say how many - * entries exist in each script table, instead, a 0 value in - * the first 16-bit word seems to indicate both the end of the - * list and the default entry. The second 16-bit word in the - * script tables is a pointer to the script to execute. - */ - - NV_DEBUG(drm, "Searching for output entry for %d %d %d\n", - dcbent->type, dcbent->location, dcbent->or); - for (i = 0; i < table[3]; i++) { - otable = ROMPTR(dev, table[table[1] + (i * table[2])]); - if (otable && bios_encoder_match(dcbent, ROM32(otable[0]))) - break; - } - - if (!otable) { - NV_DEBUG(drm, "failed to match any output table\n"); - return 1; - } - - if (pclk < -2 || pclk > 0) { - /* Try to find matching script table entry */ - for (i = 0; i < otable[5]; i++) { - if (ROM16(otable[table[4] + i*6]) == type) - break; - } - - if (i == otable[5]) { - NV_ERROR(drm, "Table 0x%04x not found for %d/%d, " - "using first\n", - type, dcbent->type, dcbent->or); - i = 0; - } - } - - if (pclk == 0) { - script = ROM16(otable[6]); - if (!script) { - NV_DEBUG(drm, "output script 0 not found\n"); - return 1; - } - - NV_DEBUG(drm, "0x%04X: parsing output script 0\n", script); - nouveau_bios_run_init_table(dev, script, dcbent, crtc); - } else - if (pclk == -1) { - script = ROM16(otable[8]); - if (!script) { - NV_DEBUG(drm, "output script 1 not found\n"); - return 1; - } - - NV_DEBUG(drm, "0x%04X: parsing output script 1\n", script); - nouveau_bios_run_init_table(dev, script, dcbent, crtc); - } else - if (pclk == -2) { - if (table[4] >= 12) - script = ROM16(otable[10]); - else - script = 0; - if (!script) { - NV_DEBUG(drm, "output script 2 not found\n"); - return 1; - } - - NV_DEBUG(drm, "0x%04X: parsing output script 2\n", script); - nouveau_bios_run_init_table(dev, script, dcbent, crtc); - } else - if (pclk > 0) { - script = ROM16(otable[table[4] + i*6 + 2]); - if (script) - script = clkcmptable(bios, script, pclk); - if (!script) { - NV_DEBUG(drm, "clock script 0 not found\n"); - return 1; - } - - NV_DEBUG(drm, "0x%04X: parsing clock script 0\n", script); - nouveau_bios_run_init_table(dev, script, dcbent, crtc); - } else - if (pclk < 0) { - script = ROM16(otable[table[4] + i*6 + 4]); - if (script) - script = clkcmptable(bios, script, -pclk); - if (!script) { - NV_DEBUG(drm, "clock script 1 not found\n"); - return 1; - } - - NV_DEBUG(drm, "0x%04X: parsing clock script 1\n", script); - nouveau_bios_run_init_table(dev, script, dcbent, crtc); - } - - return 0; -} - - int run_tmds_table(struct drm_device *dev, struct dcb_output *dcbent, int head, int pxclk) { /* @@ -1212,31 +1012,6 @@ static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, return 0; } -static int -parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios, - struct bit_entry *bitentry) -{ - /* - * Parses the pointer to the G80 output script tables - * - * Starting at bitentry->offset: - * - * offset + 0 (16 bits): output script table pointer - */ - - struct nouveau_drm *drm = nouveau_drm(dev); - uint16_t outputscripttableptr; - - if (bitentry->length != 3) { - NV_ERROR(drm, "Do not understand BIT U table\n"); - return -EINVAL; - } - - outputscripttableptr = ROM16(bios->data[bitentry->offset]); - bios->display.script_table_ptr = outputscripttableptr; - return 0; -} - struct bit_table { const char id; int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *); @@ -1313,7 +1088,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset) parse_bit_table(bios, bitoffset, &BIT_TABLE('M', M)); /* memory? */ parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds)); parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds)); - parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U)); return 0; } @@ -2324,7 +2098,7 @@ nouveau_run_vbios_init(struct drm_device *dev) { struct nouveau_drm *drm = nouveau_drm(dev); struct nvbios *bios = &drm->vbios; - int i, ret = 0; + int ret = 0; /* Reset the BIOS head to 0. */ bios->state.crtchead = 0; @@ -2337,13 +2111,6 @@ nouveau_run_vbios_init(struct drm_device *dev) bios->fp.lvds_init_run = false; } - if (nv_device(drm->device)->card_type >= NV_50) { - for (i = 0; bios->execute && i < bios->dcb.entries; i++) { - nouveau_bios_run_display_table(dev, 0, 0, - &bios->dcb.entry[i], -1); - } - } - return ret; } diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h index 3befbb821a5..f68c54ca422 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bios.h +++ b/drivers/gpu/drm/nouveau/nouveau_bios.h @@ -128,12 +128,6 @@ struct nvbios { } state; struct { - struct dcb_output *output; - int crtc; - uint16_t script_table_ptr; - } display; - - struct { uint16_t fptablepointer; /* also used by tmds */ uint16_t fpxlatetableptr; int xlatwidth; @@ -185,8 +179,6 @@ void nouveau_bios_takedown(struct drm_device *dev); int nouveau_run_vbios_init(struct drm_device *); struct dcb_connector_table_entry * nouveau_bios_connector_entry(struct drm_device *, int index); -int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk, - struct dcb_output *, int crtc); bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *); uint8_t *nouveau_bios_embedded_edid(struct drm_device *); int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk, @@ -195,6 +187,5 @@ int run_tmds_table(struct drm_device *, struct dcb_output *, int head, int pxclk); int call_lvds_script(struct drm_device *, struct dcb_output *, int head, enum LVDS_script, int pxclk); -bool bios_encoder_match(struct dcb_output *, u32 hash); #endif diff --git a/drivers/gpu/drm/nouveau/nouveau_chan.c b/drivers/gpu/drm/nouveau/nouveau_chan.c index c1d7301c0e9..174300b6a02 100644 --- a/drivers/gpu/drm/nouveau/nouveau_chan.c +++ b/drivers/gpu/drm/nouveau/nouveau_chan.c @@ -76,6 +76,8 @@ nouveau_channel_del(struct nouveau_channel **pchan) nouveau_object_del(client, NVDRM_DEVICE, chan->push.handle); nouveau_bo_vma_del(chan->push.buffer, &chan->push.vma); nouveau_bo_unmap(chan->push.buffer); + if (chan->push.buffer && chan->push.buffer->pin_refcnt) + nouveau_bo_unpin(chan->push.buffer); nouveau_bo_ref(NULL, &chan->push.buffer); kfree(chan); } @@ -267,7 +269,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) struct nouveau_fb *pfb = nouveau_fb(device); struct nouveau_software_chan *swch; struct nouveau_object *object; - struct nv_dma_class args; + struct nv_dma_class args = {}; int ret, i; /* allocate dma objects to cover all allowed vram, and gart */ @@ -346,7 +348,7 @@ nouveau_channel_init(struct nouveau_channel *chan, u32 vram, u32 gart) /* allocate software object class (used for fences on <= nv05, and * to signal flip completion), bind it to a subchannel. */ - if (chan != chan->drm->cechan) { + if ((device->card_type < NV_E0) || gart /* nve0: want_nvsw */) { ret = nouveau_object_new(nv_object(client), chan->handle, NvSw, nouveau_abi16_swclass(chan->drm), NULL, 0, &object); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 9a6e2cb282d..98d7e9238f6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -110,7 +110,6 @@ nouveau_connector_destroy(struct drm_connector *connector) dev = nv_connector->base.dev; drm = nouveau_drm(dev); gpio = nouveau_gpio(drm->device); - NV_DEBUG(drm, "\n"); if (gpio && nv_connector->hpd != DCB_GPIO_UNUSED) { gpio->isr_del(gpio, 0, nv_connector->hpd, 0xff, @@ -929,8 +928,6 @@ nouveau_connector_create(struct drm_device *dev, int index) int type, ret = 0; bool dummy; - NV_DEBUG(drm, "\n"); - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { nv_connector = nouveau_connector(connector); if (nv_connector->index == index) diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index ebdb87670a8..20eb84cce9e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -28,6 +28,7 @@ #define __NOUVEAU_CONNECTOR_H__ #include <drm/drm_edid.h> +#include "nouveau_crtc.h" struct nouveau_i2c_port; @@ -80,6 +81,21 @@ static inline struct nouveau_connector *nouveau_connector( return container_of(con, struct nouveau_connector, base); } +static inline struct nouveau_connector * +nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) +{ + struct drm_device *dev = nv_crtc->base.dev; + struct drm_connector *connector; + struct drm_crtc *crtc = to_drm_crtc(nv_crtc); + + list_for_each_entry(connector, &dev->mode_config.connector_list, head) { + if (connector->encoder && connector->encoder->crtc == crtc) + return nouveau_connector(connector); + } + + return NULL; +} + struct drm_connector * nouveau_connector_create(struct drm_device *, int index); diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h index e6d0d1eb013..d1e5890784d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_crtc.h +++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h @@ -82,16 +82,6 @@ static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc) return &crtc->base; } -int nv50_crtc_create(struct drm_device *dev, int index); -int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv, - uint32_t buffer_handle, uint32_t width, - uint32_t height); -int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y); - int nv04_cursor_init(struct nouveau_crtc *); -int nv50_cursor_init(struct nouveau_crtc *); - -struct nouveau_connector * -nouveau_crtc_connector_get(struct nouveau_crtc *crtc); #endif /* __NOUVEAU_CRTC_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c index 86124b131f4..e4188f24fc7 100644 --- a/drivers/gpu/drm/nouveau/nouveau_display.c +++ b/drivers/gpu/drm/nouveau/nouveau_display.c @@ -98,12 +98,12 @@ nouveau_framebuffer_init(struct drm_device *dev, nv_fb->r_dma = NvEvoVRAM_LP; switch (fb->depth) { - case 8: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_8; break; - case 15: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_15; break; - case 16: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_16; break; + case 8: nv_fb->r_format = 0x1e00; break; + case 15: nv_fb->r_format = 0xe900; break; + case 16: nv_fb->r_format = 0xe800; break; case 24: - case 32: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_24; break; - case 30: nv_fb->r_format = NV50_EVO_CRTC_FB_DEPTH_30; break; + case 32: nv_fb->r_format = 0xcf00; break; + case 30: nv_fb->r_format = 0xd100; break; default: NV_ERROR(drm, "unknown depth %d\n", fb->depth); return -EINVAL; @@ -324,7 +324,7 @@ nouveau_display_create(struct drm_device *dev) disp->underscan_vborder_property = drm_property_create_range(dev, 0, "underscan vborder", 0, 128); - if (gen == 1) { + if (gen >= 1) { disp->vibrant_hue_property = drm_property_create(dev, DRM_MODE_PROP_RANGE, "vibrant hue", 2); @@ -366,10 +366,7 @@ nouveau_display_create(struct drm_device *dev) if (nv_device(drm->device)->card_type < NV_50) ret = nv04_display_create(dev); else - if (nv_device(drm->device)->card_type < NV_D0) ret = nv50_display_create(dev); - else - ret = nvd0_display_create(dev); if (ret) goto disp_create_err; @@ -400,11 +397,12 @@ nouveau_display_destroy(struct drm_device *dev) nouveau_backlight_exit(dev); drm_vblank_cleanup(dev); + drm_kms_helper_poll_fini(dev); + drm_mode_config_cleanup(dev); + if (disp->dtor) disp->dtor(dev); - drm_kms_helper_poll_fini(dev); - drm_mode_config_cleanup(dev); nouveau_drm(dev)->display = NULL; kfree(disp); } @@ -659,10 +657,7 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, /* Emit a page flip */ if (nv_device(drm->device)->card_type >= NV_50) { - if (nv_device(drm->device)->card_type >= NV_D0) - ret = nvd0_display_flip_next(crtc, fb, chan, 0); - else - ret = nv50_display_flip_next(crtc, fb, chan); + ret = nv50_display_flip_next(crtc, fb, chan, 0); if (ret) { mutex_unlock(&chan->cli->mutex); goto fail_unreserve; diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 978a108ba7a..59838651ee8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -30,60 +30,17 @@ #include "nouveau_encoder.h" #include "nouveau_crtc.h" +#include <core/class.h> + #include <subdev/gpio.h> #include <subdev/i2c.h> -u8 * -nouveau_dp_bios_data(struct drm_device *dev, struct dcb_output *dcb, u8 **entry) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct bit_entry d; - u8 *table; - int i; - - if (bit_table(dev, 'd', &d)) { - NV_ERROR(drm, "BIT 'd' table not found\n"); - return NULL; - } - - if (d.version != 1) { - NV_ERROR(drm, "BIT 'd' table version %d unknown\n", d.version); - return NULL; - } - - table = ROMPTR(dev, d.data[0]); - if (!table) { - NV_ERROR(drm, "displayport table pointer invalid\n"); - return NULL; - } - - switch (table[0]) { - case 0x20: - case 0x21: - case 0x30: - case 0x40: - break; - default: - NV_ERROR(drm, "displayport table 0x%02x unknown\n", table[0]); - return NULL; - } - - for (i = 0; i < table[3]; i++) { - *entry = ROMPTR(dev, table[table[1] + (i * table[2])]); - if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0]))) - return table; - } - - NV_ERROR(drm, "displayport encoder table not found\n"); - return NULL; -} - /****************************************************************************** * link training *****************************************************************************/ struct dp_state { struct nouveau_i2c_port *auxch; - struct dp_train_func *func; + struct nouveau_object *core; struct dcb_output *dcb; int crtc; u8 *dpcd; @@ -97,13 +54,20 @@ static void dp_set_link_config(struct drm_device *dev, struct dp_state *dp) { struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_output *dcb = dp->dcb; + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 moff = (dp->crtc << 3) | (link << 2) | or; u8 sink[2]; + u32 data; NV_DEBUG(drm, "%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw); /* set desired link configuration on the source */ - dp->func->link_set(dev, dp->dcb, dp->crtc, dp->link_nr, dp->link_bw, - dp->dpcd[2] & DP_ENHANCED_FRAME_CAP); + data = ((dp->link_bw / 27000) << 8) | dp->link_nr; + if (dp->dpcd[2] & DP_ENHANCED_FRAME_CAP) + data |= NV94_DISP_SOR_DP_LNKCTL_FRAME_ENH; + + nv_call(dp->core, NV94_DISP_SOR_DP_LNKCTL + moff, data); /* inform the sink of the new configuration */ sink[0] = dp->link_bw / 27000; @@ -118,11 +82,14 @@ static void dp_set_training_pattern(struct drm_device *dev, struct dp_state *dp, u8 pattern) { struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_output *dcb = dp->dcb; + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 moff = (dp->crtc << 3) | (link << 2) | or; u8 sink_tp; NV_DEBUG(drm, "training pattern %d\n", pattern); - dp->func->train_set(dev, dp->dcb, pattern); + nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, pattern); nv_rdaux(dp->auxch, DP_TRAINING_PATTERN_SET, &sink_tp, 1); sink_tp &= ~DP_TRAINING_PATTERN_MASK; @@ -134,6 +101,9 @@ static int dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) { struct nouveau_drm *drm = nouveau_drm(dev); + struct dcb_output *dcb = dp->dcb; + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 moff = (dp->crtc << 3) | (link << 2) | or; int i; for (i = 0; i < dp->link_nr; i++) { @@ -148,7 +118,8 @@ dp_link_train_commit(struct drm_device *dev, struct dp_state *dp) dp->conf[i] |= DP_TRAIN_MAX_PRE_EMPHASIS_REACHED; NV_DEBUG(drm, "config lane %d %02x\n", i, dp->conf[i]); - dp->func->train_adj(dev, dp->dcb, i, lvsw, lpre); + + nv_call(dp->core, NV94_DISP_SOR_DP_DRVCTL(i) + moff, (lvsw << 8) | lpre); } return nv_wraux(dp->auxch, DP_TRAINING_LANE0_SET, dp->conf, 4); @@ -234,59 +205,32 @@ dp_link_train_eq(struct drm_device *dev, struct dp_state *dp) } static void -dp_set_downspread(struct drm_device *dev, struct dp_state *dp, bool enable) +dp_link_train_init(struct drm_device *dev, struct dp_state *dp, bool spread) { - u16 script = 0x0000; - u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); - if (table) { - if (table[0] >= 0x20 && table[0] <= 0x30) { - if (enable) script = ROM16(entry[12]); - else script = ROM16(entry[14]); - } else - if (table[0] == 0x40) { - if (enable) script = ROM16(entry[11]); - else script = ROM16(entry[13]); - } - } - - nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); -} - -static void -dp_link_train_init(struct drm_device *dev, struct dp_state *dp) -{ - u16 script = 0x0000; - u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); - if (table) { - if (table[0] >= 0x20 && table[0] <= 0x30) - script = ROM16(entry[6]); - else - if (table[0] == 0x40) - script = ROM16(entry[5]); - } - - nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); + struct dcb_output *dcb = dp->dcb; + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 moff = (dp->crtc << 3) | (link << 2) | or; + + nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, (spread ? + NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_ON : + NV94_DISP_SOR_DP_TRAIN_INIT_SPREAD_OFF) | + NV94_DISP_SOR_DP_TRAIN_OP_INIT); } static void dp_link_train_fini(struct drm_device *dev, struct dp_state *dp) { - u16 script = 0x0000; - u8 *entry, *table = nouveau_dp_bios_data(dev, dp->dcb, &entry); - if (table) { - if (table[0] >= 0x20 && table[0] <= 0x30) - script = ROM16(entry[8]); - else - if (table[0] == 0x40) - script = ROM16(entry[7]); - } + struct dcb_output *dcb = dp->dcb; + const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); + const u32 moff = (dp->crtc << 3) | (link << 2) | or; - nouveau_bios_run_init_table(dev, script, dp->dcb, dp->crtc); + nv_call(dp->core, NV94_DISP_SOR_DP_TRAIN + moff, + NV94_DISP_SOR_DP_TRAIN_OP_FINI); } static bool nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, - struct dp_train_func *func) + struct nouveau_object *core) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); @@ -304,7 +248,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, if (!dp.auxch) return false; - dp.func = func; + dp.core = core; dp.dcb = nv_encoder->dcb; dp.crtc = nv_crtc->index; dp.dpcd = nv_encoder->dp.dpcd; @@ -318,11 +262,8 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, */ gpio->irq(gpio, 0, nv_connector->hpd, 0xff, false); - /* enable down-spreading, if possible */ - dp_set_downspread(dev, &dp, nv_encoder->dp.dpcd[3] & 1); - - /* execute pre-train script from vbios */ - dp_link_train_init(dev, &dp); + /* enable down-spreading and execute pre-train script from vbios */ + dp_link_train_init(dev, &dp, nv_encoder->dp.dpcd[3] & 1); /* start off at highest link rate supported by encoder and display */ while (*link_bw > nv_encoder->dp.link_bw) @@ -365,7 +306,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate, void nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, - struct dp_train_func *func) + struct nouveau_object *core) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); struct nouveau_drm *drm = nouveau_drm(encoder->dev); @@ -385,7 +326,7 @@ nouveau_dp_dpms(struct drm_encoder *encoder, int mode, u32 datarate, nv_wraux(auxch, DP_SET_POWER, &status, 1); if (mode == DRM_MODE_DPMS_ON) - nouveau_dp_link_train(encoder, datarate, func); + nouveau_dp_link_train(encoder, datarate, core); } static void diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 8244863cc04..a7529b37238 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -49,8 +49,6 @@ #include "nouveau_fbcon.h" #include "nouveau_fence.h" -#include "nouveau_ttm.h" - MODULE_PARM_DESC(config, "option string to pass to driver core"); static char *nouveau_config; module_param_named(config, nouveau_config, charp, 0400); @@ -148,7 +146,7 @@ nouveau_accel_init(struct nouveau_drm *drm) NV_ERROR(drm, "failed to create ce channel, %d\n", ret); arg0 = NVE0_CHANNEL_IND_ENGINE_GR; - arg1 = 0; + arg1 = 1; } else { arg0 = NvDmaFB; arg1 = NvDmaTT; @@ -395,17 +393,12 @@ nouveau_drm_remove(struct pci_dev *pdev) } int -nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state) +nouveau_do_suspend(struct drm_device *dev) { - struct drm_device *dev = pci_get_drvdata(pdev); struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_cli *cli; int ret; - if (dev->switch_power_state == DRM_SWITCH_POWER_OFF || - pm_state.event == PM_EVENT_PRETHAW) - return 0; - if (dev->mode_config.num_crtc) { NV_INFO(drm, "suspending fbcon...\n"); nouveau_fbcon_set_suspend(dev, 1); @@ -436,13 +429,6 @@ nouveau_drm_suspend(struct pci_dev *pdev, pm_message_t pm_state) goto fail_client; nouveau_agp_fini(drm); - - pci_save_state(pdev); - if (pm_state.event == PM_EVENT_SUSPEND) { - pci_disable_device(pdev); - pci_set_power_state(pdev, PCI_D3hot); - } - return 0; fail_client: @@ -457,24 +443,33 @@ fail_client: return ret; } -int -nouveau_drm_resume(struct pci_dev *pdev) +int nouveau_pmops_suspend(struct device *dev) { - struct drm_device *dev = pci_get_drvdata(pdev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_cli *cli; + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); int ret; - if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; - NV_INFO(drm, "re-enabling device...\n"); - pci_set_power_state(pdev, PCI_D0); - pci_restore_state(pdev); - ret = pci_enable_device(pdev); + ret = nouveau_do_suspend(drm_dev); if (ret) return ret; - pci_set_master(pdev); + + pci_save_state(pdev); + pci_disable_device(pdev); + pci_set_power_state(pdev, PCI_D3hot); + + return 0; +} + +int +nouveau_do_resume(struct drm_device *dev) +{ + struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_cli *cli; + + NV_INFO(drm, "re-enabling device...\n"); nouveau_agp_reset(drm); @@ -500,6 +495,42 @@ nouveau_drm_resume(struct pci_dev *pdev) return 0; } +int nouveau_pmops_resume(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + int ret; + + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + + pci_set_power_state(pdev, PCI_D0); + pci_restore_state(pdev); + ret = pci_enable_device(pdev); + if (ret) + return ret; + pci_set_master(pdev); + + return nouveau_do_resume(drm_dev); +} + +static int nouveau_pmops_freeze(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + + return nouveau_do_suspend(drm_dev); +} + +static int nouveau_pmops_thaw(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + struct drm_device *drm_dev = pci_get_drvdata(pdev); + + return nouveau_do_resume(drm_dev); +} + + static int nouveau_drm_open(struct drm_device *dev, struct drm_file *fpriv) { @@ -652,14 +683,22 @@ nouveau_drm_pci_table[] = { {} }; +static const struct dev_pm_ops nouveau_pm_ops = { + .suspend = nouveau_pmops_suspend, + .resume = nouveau_pmops_resume, + .freeze = nouveau_pmops_freeze, + .thaw = nouveau_pmops_thaw, + .poweroff = nouveau_pmops_freeze, + .restore = nouveau_pmops_resume, +}; + static struct pci_driver nouveau_drm_pci_driver = { .name = "nouveau", .id_table = nouveau_drm_pci_table, .probe = nouveau_drm_probe, .remove = nouveau_drm_remove, - .suspend = nouveau_drm_suspend, - .resume = nouveau_drm_resume, + .driver.pm = &nouveau_pm_ops, }; static int __init diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index a1016992708..aa89eb938b4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -129,8 +129,8 @@ nouveau_dev(struct drm_device *dev) return nv_device(nouveau_drm(dev)->device); } -int nouveau_drm_suspend(struct pci_dev *, pm_message_t); -int nouveau_drm_resume(struct pci_dev *); +int nouveau_pmops_suspend(struct device *); +int nouveau_pmops_resume(struct device *); #define NV_FATAL(cli, fmt, args...) nv_fatal((cli), fmt, ##args) #define NV_ERROR(cli, fmt, args...) nv_error((cli), fmt, ##args) diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 6a17bf2ba9a..d0d95bd511a 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -93,14 +93,9 @@ get_slave_funcs(struct drm_encoder *enc) /* nouveau_dp.c */ bool nouveau_dp_detect(struct drm_encoder *); void nouveau_dp_dpms(struct drm_encoder *, int mode, u32 datarate, - struct dp_train_func *); -u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_output *, u8 **); + struct nouveau_object *); struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); -int nv50_sor_create(struct drm_connector *, struct dcb_output *); -void nv50_sor_dp_calc_tu(struct drm_device *, int, int, u32, u32); -int nv50_dac_create(struct drm_connector *, struct dcb_output *); - #endif /* __NOUVEAU_ENCODER_H__ */ diff --git a/drivers/gpu/drm/nouveau/nouveau_hdmi.c b/drivers/gpu/drm/nouveau/nouveau_hdmi.c deleted file mode 100644 index 2c672cebc88..00000000000 --- a/drivers/gpu/drm/nouveau/nouveau_hdmi.c +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2011 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: Ben Skeggs - */ - -#include <drm/drmP.h> -#include "nouveau_drm.h" -#include "nouveau_connector.h" -#include "nouveau_encoder.h" -#include "nouveau_crtc.h" - -static bool -hdmi_sor(struct drm_encoder *encoder) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - if (nv_device(drm->device)->chipset < 0xa3 || - nv_device(drm->device)->chipset == 0xaa || - nv_device(drm->device)->chipset == 0xac) - return false; - return true; -} - -static inline u32 -hdmi_base(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); - if (!hdmi_sor(encoder)) - return 0x616500 + (nv_crtc->index * 0x800); - return 0x61c500 + (nv_encoder->or * 0x800); -} - -static void -hdmi_wr32(struct drm_encoder *encoder, u32 reg, u32 val) -{ - struct nouveau_device *device = nouveau_dev(encoder->dev); - nv_wr32(device, hdmi_base(encoder) + reg, val); -} - -static u32 -hdmi_rd32(struct drm_encoder *encoder, u32 reg) -{ - struct nouveau_device *device = nouveau_dev(encoder->dev); - return nv_rd32(device, hdmi_base(encoder) + reg); -} - -static u32 -hdmi_mask(struct drm_encoder *encoder, u32 reg, u32 mask, u32 val) -{ - u32 tmp = hdmi_rd32(encoder, reg); - hdmi_wr32(encoder, reg, (tmp & ~mask) | val); - return tmp; -} - -static void -nouveau_audio_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_device *device = nouveau_dev(encoder->dev); - u32 or = nv_encoder->or * 0x800; - - if (hdmi_sor(encoder)) - nv_mask(device, 0x61c448 + or, 0x00000003, 0x00000000); -} - -static void -nouveau_audio_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_device *device = nouveau_dev(encoder->dev); - struct nouveau_connector *nv_connector; - u32 or = nv_encoder->or * 0x800; - int i; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (!drm_detect_monitor_audio(nv_connector->edid)) { - nouveau_audio_disconnect(encoder); - return; - } - - if (hdmi_sor(encoder)) { - nv_mask(device, 0x61c448 + or, 0x00000001, 0x00000001); - - drm_edid_to_eld(&nv_connector->base, nv_connector->edid); - if (nv_connector->base.eld[0]) { - u8 *eld = nv_connector->base.eld; - for (i = 0; i < eld[2] * 4; i++) - nv_wr32(device, 0x61c440 + or, (i << 8) | eld[i]); - for (i = eld[2] * 4; i < 0x60; i++) - nv_wr32(device, 0x61c440 + or, (i << 8) | 0x00); - nv_mask(device, 0x61c448 + or, 0x00000002, 0x00000002); - } - } -} - -static void -nouveau_hdmi_infoframe(struct drm_encoder *encoder, u32 ctrl, u8 *frame) -{ - /* calculate checksum for the infoframe */ - u8 sum = 0, i; - for (i = 0; i < frame[2]; i++) - sum += frame[i]; - frame[3] = 256 - sum; - - /* disable infoframe, and write header */ - hdmi_mask(encoder, ctrl + 0x00, 0x00000001, 0x00000000); - hdmi_wr32(encoder, ctrl + 0x08, *(u32 *)frame & 0xffffff); - - /* register scans tell me the audio infoframe has only one set of - * subpack regs, according to tegra (gee nvidia, it'd be nice if we - * could get those docs too!), the hdmi block pads out the rest of - * the packet on its own. - */ - if (ctrl == 0x020) - frame[2] = 6; - - /* write out checksum and data, weird weird 7 byte register pairs */ - for (i = 0; i < frame[2] + 1; i += 7) { - u32 rsubpack = ctrl + 0x0c + ((i / 7) * 8); - u32 *subpack = (u32 *)&frame[3 + i]; - hdmi_wr32(encoder, rsubpack + 0, subpack[0]); - hdmi_wr32(encoder, rsubpack + 4, subpack[1] & 0xffffff); - } - - /* enable the infoframe */ - hdmi_mask(encoder, ctrl, 0x00000001, 0x00000001); -} - -static void -nouveau_hdmi_video_infoframe(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - const u8 Y = 0, A = 0, B = 0, S = 0, C = 0, M = 0, R = 0; - const u8 ITC = 0, EC = 0, Q = 0, SC = 0, VIC = 0, PR = 0; - const u8 bar_top = 0, bar_bottom = 0, bar_left = 0, bar_right = 0; - u8 frame[20]; - - frame[0x00] = 0x82; /* AVI infoframe */ - frame[0x01] = 0x02; /* version */ - frame[0x02] = 0x0d; /* length */ - frame[0x03] = 0x00; - frame[0x04] = (Y << 5) | (A << 4) | (B << 2) | S; - frame[0x05] = (C << 6) | (M << 4) | R; - frame[0x06] = (ITC << 7) | (EC << 4) | (Q << 2) | SC; - frame[0x07] = VIC; - frame[0x08] = PR; - frame[0x09] = bar_top & 0xff; - frame[0x0a] = bar_top >> 8; - frame[0x0b] = bar_bottom & 0xff; - frame[0x0c] = bar_bottom >> 8; - frame[0x0d] = bar_left & 0xff; - frame[0x0e] = bar_left >> 8; - frame[0x0f] = bar_right & 0xff; - frame[0x10] = bar_right >> 8; - frame[0x11] = 0x00; - frame[0x12] = 0x00; - frame[0x13] = 0x00; - - nouveau_hdmi_infoframe(encoder, 0x020, frame); -} - -static void -nouveau_hdmi_audio_infoframe(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - const u8 CT = 0x00, CC = 0x01, ceaSS = 0x00, SF = 0x00, FMT = 0x00; - const u8 CA = 0x00, DM_INH = 0, LSV = 0x00; - u8 frame[12]; - - frame[0x00] = 0x84; /* Audio infoframe */ - frame[0x01] = 0x01; /* version */ - frame[0x02] = 0x0a; /* length */ - frame[0x03] = 0x00; - frame[0x04] = (CT << 4) | CC; - frame[0x05] = (SF << 2) | ceaSS; - frame[0x06] = FMT; - frame[0x07] = CA; - frame[0x08] = (DM_INH << 7) | (LSV << 3); - frame[0x09] = 0x00; - frame[0x0a] = 0x00; - frame[0x0b] = 0x00; - - nouveau_hdmi_infoframe(encoder, 0x000, frame); -} - -static void -nouveau_hdmi_disconnect(struct drm_encoder *encoder) -{ - nouveau_audio_disconnect(encoder); - - /* disable audio and avi infoframes */ - hdmi_mask(encoder, 0x000, 0x00000001, 0x00000000); - hdmi_mask(encoder, 0x020, 0x00000001, 0x00000000); - - /* disable hdmi */ - hdmi_mask(encoder, 0x0a4, 0x40000000, 0x00000000); -} - -void -nouveau_hdmi_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode) -{ - struct nouveau_device *device = nouveau_dev(encoder->dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector; - u32 max_ac_packet, rekey; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (!mode || !nv_connector || !nv_connector->edid || - !drm_detect_hdmi_monitor(nv_connector->edid)) { - nouveau_hdmi_disconnect(encoder); - return; - } - - nouveau_hdmi_video_infoframe(encoder, mode); - nouveau_hdmi_audio_infoframe(encoder, mode); - - hdmi_mask(encoder, 0x0d0, 0x00070001, 0x00010001); /* SPARE, HW_CTS */ - hdmi_mask(encoder, 0x068, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */ - hdmi_mask(encoder, 0x078, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */ - - nv_mask(device, 0x61733c, 0x00100000, 0x00100000); /* RESETF */ - nv_mask(device, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */ - nv_mask(device, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */ - - /* value matches nvidia binary driver, and tegra constant */ - rekey = 56; - - max_ac_packet = mode->htotal - mode->hdisplay; - max_ac_packet -= rekey; - max_ac_packet -= 18; /* constant from tegra */ - max_ac_packet /= 32; - - /* enable hdmi */ - hdmi_mask(encoder, 0x0a4, 0x5f1f003f, 0x40000000 | /* enable */ - 0x1f000000 | /* unknown */ - max_ac_packet << 16 | - rekey); - - nouveau_audio_mode_set(encoder, mode); -} diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c index 1d8cb506a28..1303680affd 100644 --- a/drivers/gpu/drm/nouveau/nouveau_irq.c +++ b/drivers/gpu/drm/nouveau/nouveau_irq.c @@ -60,18 +60,6 @@ nouveau_irq_handler(DRM_IRQ_ARGS) return IRQ_NONE; nv_subdev(pmc)->intr(nv_subdev(pmc)); - - if (dev->mode_config.num_crtc) { - if (device->card_type >= NV_D0) { - if (nv_rd32(device, 0x000100) & 0x04000000) - nvd0_display_intr(dev); - } else - if (device->card_type >= NV_50) { - if (nv_rd32(device, 0x000100) & 0x04000000) - nv50_display_intr(dev); - } - } - return IRQ_HANDLED; } diff --git a/drivers/gpu/drm/nouveau/nouveau_prime.c b/drivers/gpu/drm/nouveau/nouveau_prime.c index 366462cf8a2..3543fec2355 100644 --- a/drivers/gpu/drm/nouveau/nouveau_prime.c +++ b/drivers/gpu/drm/nouveau/nouveau_prime.c @@ -155,10 +155,6 @@ nouveau_prime_new(struct drm_device *dev, return ret; nvbo = *pnvbo; - /* we restrict allowed domains on nv50+ to only the types - * that were requested at creation time. not possibly on - * earlier chips without busting the ABI. - */ nvbo->valid_domains = NOUVEAU_GEM_DOMAIN_GART; nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size); if (!nvbo->gem) { diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c index 6f0ac64873d..25d3495725e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_vga.c +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c @@ -31,12 +31,11 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) { struct drm_device *dev = pci_get_drvdata(pdev); - pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; if (state == VGA_SWITCHEROO_ON) { printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; - nouveau_drm_resume(pdev); + nouveau_pmops_resume(&pdev->dev); drm_kms_helper_poll_enable(dev); dev->switch_power_state = DRM_SWITCH_POWER_ON; } else { @@ -44,7 +43,7 @@ nouveau_switcheroo_set_state(struct pci_dev *pdev, dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; drm_kms_helper_poll_disable(dev); nouveau_switcheroo_optimus_dsm(); - nouveau_drm_suspend(pdev, pmm); + nouveau_pmops_suspend(&pdev->dev); dev->switch_power_state = DRM_SWITCH_POWER_OFF; } } diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c index 82a0d9c6cda..6578cd28c55 100644 --- a/drivers/gpu/drm/nouveau/nv04_crtc.c +++ b/drivers/gpu/drm/nouveau/nv04_crtc.c @@ -730,6 +730,7 @@ static void nv_crtc_destroy(struct drm_crtc *crtc) drm_crtc_cleanup(crtc); nouveau_bo_unmap(nv_crtc->cursor.nvbo); + nouveau_bo_unpin(nv_crtc->cursor.nvbo); nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); kfree(nv_crtc); } @@ -1056,8 +1057,11 @@ nv04_crtc_create(struct drm_device *dev, int crtc_num) 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); if (!ret) { ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) + if (!ret) { ret = nouveau_bo_map(nv_crtc->cursor.nvbo); + if (ret) + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + } if (ret) nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); } diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c index 846050f04c2..2cd6fb8c548 100644 --- a/drivers/gpu/drm/nouveau/nv04_display.c +++ b/drivers/gpu/drm/nouveau/nv04_display.c @@ -60,8 +60,6 @@ nv04_display_create(struct drm_device *dev) struct nv04_display *disp; int i, ret; - NV_DEBUG(drm, "\n"); - disp = kzalloc(sizeof(*disp), GFP_KERNEL); if (!disp) return -ENOMEM; @@ -132,13 +130,10 @@ nv04_display_create(struct drm_device *dev) void nv04_display_destroy(struct drm_device *dev) { - struct nouveau_drm *drm = nouveau_drm(dev); struct nv04_display *disp = nv04_display(dev); struct drm_encoder *encoder; struct drm_crtc *crtc; - NV_DEBUG(drm, "\n"); - /* Turn every CRTC off. */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { struct drm_mode_set modeset = { diff --git a/drivers/gpu/drm/nouveau/nv10_fence.c b/drivers/gpu/drm/nouveau/nv10_fence.c index ce752bf5cc4..7ae7f97a6d4 100644 --- a/drivers/gpu/drm/nouveau/nv10_fence.c +++ b/drivers/gpu/drm/nouveau/nv10_fence.c @@ -155,6 +155,8 @@ nv10_fence_destroy(struct nouveau_drm *drm) { struct nv10_fence_priv *priv = drm->fence; nouveau_bo_unmap(priv->bo); + if (priv->bo) + nouveau_bo_unpin(priv->bo); nouveau_bo_ref(NULL, &priv->bo); drm->fence = NULL; kfree(priv); @@ -183,8 +185,11 @@ nv10_fence_create(struct nouveau_drm *drm) 0, 0x0000, NULL, &priv->bo); if (!ret) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); - if (!ret) + if (!ret) { ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } if (ret) nouveau_bo_ref(NULL, &priv->bo); } diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c deleted file mode 100644 index 222de77d626..00000000000 --- a/drivers/gpu/drm/nouveau/nv50_crtc.c +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright (C) 2008 Maarten Maathuis. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> - -#include "nouveau_reg.h" -#include "nouveau_drm.h" -#include "nouveau_dma.h" -#include "nouveau_gem.h" -#include "nouveau_hw.h" -#include "nouveau_encoder.h" -#include "nouveau_crtc.h" -#include "nouveau_connector.h" -#include "nv50_display.h" - -#include <subdev/clock.h> - -static void -nv50_crtc_lut_load(struct drm_crtc *crtc) -{ - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo); - int i; - - NV_DEBUG(drm, "\n"); - - for (i = 0; i < 256; i++) { - writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0); - writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2); - writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4); - } - - if (nv_crtc->lut.depth == 30) { - writew(nv_crtc->lut.r[i - 1] >> 2, lut + 8*i + 0); - writew(nv_crtc->lut.g[i - 1] >> 2, lut + 8*i + 2); - writew(nv_crtc->lut.b[i - 1] >> 2, lut + 8*i + 4); - } -} - -int -nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked) -{ - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - int index = nv_crtc->index, ret; - - NV_DEBUG(drm, "index %d\n", nv_crtc->index); - NV_DEBUG(drm, "%s\n", blanked ? "blanked" : "unblanked"); - - if (blanked) { - nv_crtc->cursor.hide(nv_crtc, false); - - ret = RING_SPACE(evo, nv_device(drm->device)->chipset != 0x50 ? 7 : 5); - if (ret) { - NV_ERROR(drm, "no space while blanking crtc\n"); - return ret; - } - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2); - OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK); - OUT_RING(evo, 0); - if (nv_device(drm->device)->chipset != 0x50) { - BEGIN_NV04(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); - OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE); - } - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1); - OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE); - } else { - if (nv_crtc->cursor.visible) - nv_crtc->cursor.show(nv_crtc, false); - else - nv_crtc->cursor.hide(nv_crtc, false); - - ret = RING_SPACE(evo, nv_device(drm->device)->chipset != 0x50 ? 10 : 8); - if (ret) { - NV_ERROR(drm, "no space while unblanking crtc\n"); - return ret; - } - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2); - OUT_RING(evo, nv_crtc->lut.depth == 8 ? - NV50_EVO_CRTC_CLUT_MODE_OFF : - NV50_EVO_CRTC_CLUT_MODE_ON); - OUT_RING(evo, nv_crtc->lut.nvbo->bo.offset >> 8); - if (nv_device(drm->device)->chipset != 0x50) { - BEGIN_NV04(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1); - OUT_RING(evo, NvEvoVRAM); - } - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2); - OUT_RING(evo, nv_crtc->fb.offset >> 8); - OUT_RING(evo, 0); - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1); - if (nv_device(drm->device)->chipset != 0x50) - if (nv_crtc->fb.tile_flags == 0x7a00 || - nv_crtc->fb.tile_flags == 0xfe00) - OUT_RING(evo, NvEvoFB32); - else - if (nv_crtc->fb.tile_flags == 0x7000) - OUT_RING(evo, NvEvoFB16); - else - OUT_RING(evo, NvEvoVRAM_LP); - else - OUT_RING(evo, NvEvoVRAM_LP); - } - - nv_crtc->fb.blanked = blanked; - return 0; -} - -static int -nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) -{ - struct nouveau_channel *evo = nv50_display(nv_crtc->base.dev)->master; - struct nouveau_connector *nv_connector; - struct drm_connector *connector; - int head = nv_crtc->index, ret; - u32 mode = 0x00; - - nv_connector = nouveau_crtc_connector_get(nv_crtc); - connector = &nv_connector->base; - if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { - if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) - mode = DITHERING_MODE_DYNAMIC2X2; - } else { - mode = nv_connector->dithering_mode; - } - - if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { - if (connector->display_info.bpc >= 8) - mode |= DITHERING_DEPTH_8BPC; - } else { - mode |= nv_connector->dithering_depth; - } - - ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); - if (ret == 0) { - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1); - OUT_RING (evo, mode); - if (update) { - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING (evo, 0); - FIRE_RING (evo); - } - } - - return ret; -} - -static int -nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update) -{ - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - int ret; - int adj; - u32 hue, vib; - - NV_DEBUG(drm, "vibrance = %i, hue = %i\n", - nv_crtc->color_vibrance, nv_crtc->vibrant_hue); - - ret = RING_SPACE(evo, 2 + (update ? 2 : 0)); - if (ret) { - NV_ERROR(drm, "no space while setting color vibrance\n"); - return ret; - } - - adj = (nv_crtc->color_vibrance > 0) ? 50 : 0; - vib = ((nv_crtc->color_vibrance * 2047 + adj) / 100) & 0xfff; - - hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff; - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1); - OUT_RING (evo, (hue << 20) | (vib << 8)); - - if (update) { - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING (evo, 0); - FIRE_RING (evo); - } - - return 0; -} - -struct nouveau_connector * -nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc) -{ - struct drm_device *dev = nv_crtc->base.dev; - struct drm_connector *connector; - struct drm_crtc *crtc = to_drm_crtc(nv_crtc); - - /* The safest approach is to find an encoder with the right crtc, that - * is also linked to a connector. */ - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { - if (connector->encoder) - if (connector->encoder->crtc == crtc) - return nouveau_connector(connector); - } - - return NULL; -} - -static int -nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) -{ - struct nouveau_connector *nv_connector; - struct drm_crtc *crtc = &nv_crtc->base; - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_display_mode *umode = &crtc->mode; - struct drm_display_mode *omode; - int scaling_mode, ret; - u32 ctrl = 0, oX, oY; - - NV_DEBUG(drm, "\n"); - - nv_connector = nouveau_crtc_connector_get(nv_crtc); - if (!nv_connector || !nv_connector->native_mode) { - NV_ERROR(drm, "no native mode, forcing panel scaling\n"); - scaling_mode = DRM_MODE_SCALE_NONE; - } else { - scaling_mode = nv_connector->scaling_mode; - } - - /* start off at the resolution we programmed the crtc for, this - * effectively handles NONE/FULL scaling - */ - if (scaling_mode != DRM_MODE_SCALE_NONE) - omode = nv_connector->native_mode; - else - omode = umode; - - oX = omode->hdisplay; - oY = omode->vdisplay; - if (omode->flags & DRM_MODE_FLAG_DBLSCAN) - oY *= 2; - - /* add overscan compensation if necessary, will keep the aspect - * ratio the same as the backend mode unless overridden by the - * user setting both hborder and vborder properties. - */ - if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || - (nv_connector->underscan == UNDERSCAN_AUTO && - nv_connector->edid && - drm_detect_hdmi_monitor(nv_connector->edid)))) { - u32 bX = nv_connector->underscan_hborder; - u32 bY = nv_connector->underscan_vborder; - u32 aspect = (oY << 19) / oX; - - if (bX) { - oX -= (bX * 2); - if (bY) oY -= (bY * 2); - else oY = ((oX * aspect) + (aspect / 2)) >> 19; - } else { - oX -= (oX >> 4) + 32; - if (bY) oY -= (bY * 2); - else oY = ((oX * aspect) + (aspect / 2)) >> 19; - } - } - - /* handle CENTER/ASPECT scaling, taking into account the areas - * removed already for overscan compensation - */ - switch (scaling_mode) { - case DRM_MODE_SCALE_CENTER: - oX = min((u32)umode->hdisplay, oX); - oY = min((u32)umode->vdisplay, oY); - /* fall-through */ - case DRM_MODE_SCALE_ASPECT: - if (oY < oX) { - u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; - oX = ((oY * aspect) + (aspect / 2)) >> 19; - } else { - u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; - oY = ((oX * aspect) + (aspect / 2)) >> 19; - } - break; - default: - break; - } - - if (umode->hdisplay != oX || umode->vdisplay != oY || - umode->flags & DRM_MODE_FLAG_INTERLACE || - umode->flags & DRM_MODE_FLAG_DBLSCAN) - ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE; - - ret = RING_SPACE(evo, 5); - if (ret) - return ret; - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1); - OUT_RING (evo, ctrl); - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2); - OUT_RING (evo, oY << 16 | oX); - OUT_RING (evo, oY << 16 | oX); - - if (update) { - nv50_display_flip_stop(crtc); - nv50_display_sync(dev); - nv50_display_flip_next(crtc, crtc->fb, NULL); - } - - return 0; -} - -int -nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_clock *clk = nouveau_clock(device); - - return clk->pll_set(clk, PLL_VPLL0 + head, pclk); -} - -static void -nv50_crtc_destroy(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - - NV_DEBUG(drm, "\n"); - - nouveau_bo_unmap(nv_crtc->lut.nvbo); - nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); - nouveau_bo_unmap(nv_crtc->cursor.nvbo); - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - drm_crtc_cleanup(&nv_crtc->base); - kfree(nv_crtc); -} - -int -nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, - uint32_t buffer_handle, uint32_t width, uint32_t height) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_bo *cursor = NULL; - struct drm_gem_object *gem; - int ret = 0, i; - - if (!buffer_handle) { - nv_crtc->cursor.hide(nv_crtc, true); - return 0; - } - - if (width != 64 || height != 64) - return -EINVAL; - - gem = drm_gem_object_lookup(dev, file_priv, buffer_handle); - if (!gem) - return -ENOENT; - cursor = nouveau_gem_object(gem); - - ret = nouveau_bo_map(cursor); - if (ret) - goto out; - - /* The simple will do for now. */ - for (i = 0; i < 64 * 64; i++) - nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, nouveau_bo_rd32(cursor, i)); - - nouveau_bo_unmap(cursor); - - nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset); - nv_crtc->cursor.show(nv_crtc, true); - -out: - drm_gem_object_unreference_unlocked(gem); - return ret; -} - -int -nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - nv_crtc->cursor.set_pos(nv_crtc, x, y); - return 0; -} - -static void -nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t start, uint32_t size) -{ - int end = (start + size > 256) ? 256 : start + size, i; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - for (i = start; i < end; i++) { - nv_crtc->lut.r[i] = r[i]; - nv_crtc->lut.g[i] = g[i]; - nv_crtc->lut.b[i] = b[i]; - } - - /* We need to know the depth before we upload, but it's possible to - * get called before a framebuffer is bound. If this is the case, - * mark the lut values as dirty by setting depth==0, and it'll be - * uploaded on the first mode_set_base() - */ - if (!nv_crtc->base.fb) { - nv_crtc->lut.depth = 0; - return; - } - - nv50_crtc_lut_load(crtc); -} - -static void -nv50_crtc_save(struct drm_crtc *crtc) -{ - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - NV_ERROR(drm, "!!\n"); -} - -static void -nv50_crtc_restore(struct drm_crtc *crtc) -{ - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - NV_ERROR(drm, "!!\n"); -} - -static const struct drm_crtc_funcs nv50_crtc_funcs = { - .save = nv50_crtc_save, - .restore = nv50_crtc_restore, - .cursor_set = nv50_crtc_cursor_set, - .cursor_move = nv50_crtc_cursor_move, - .gamma_set = nv50_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, - .page_flip = nouveau_crtc_page_flip, - .destroy = nv50_crtc_destroy, -}; - -static void -nv50_crtc_dpms(struct drm_crtc *crtc, int mode) -{ -} - -static void -nv50_crtc_prepare(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - - NV_DEBUG(drm, "index %d\n", nv_crtc->index); - - nv50_display_flip_stop(crtc); - drm_vblank_pre_modeset(dev, nv_crtc->index); - nv50_crtc_blank(nv_crtc, true); -} - -static void -nv50_crtc_commit(struct drm_crtc *crtc) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - - NV_DEBUG(drm, "index %d\n", nv_crtc->index); - - nv50_crtc_blank(nv_crtc, false); - drm_vblank_post_modeset(dev, nv_crtc->index); - nv50_display_sync(dev); - nv50_display_flip_next(crtc, crtc->fb, NULL); -} - -static bool -nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static int -nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, - struct drm_framebuffer *passed_fb, - int x, int y, bool atomic) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - struct drm_framebuffer *drm_fb; - struct nouveau_framebuffer *fb; - int ret; - - NV_DEBUG(drm, "index %d\n", nv_crtc->index); - - /* no fb bound */ - if (!atomic && !crtc->fb) { - NV_DEBUG(drm, "No FB bound\n"); - return 0; - } - - /* If atomic, we want to switch to the fb we were passed, so - * now we update pointers to do that. (We don't pin; just - * assume we're already pinned and update the base address.) - */ - if (atomic) { - drm_fb = passed_fb; - fb = nouveau_framebuffer(passed_fb); - } else { - drm_fb = crtc->fb; - fb = nouveau_framebuffer(crtc->fb); - /* If not atomic, we can go ahead and pin, and unpin the - * old fb we were passed. - */ - ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM); - if (ret) - return ret; - - if (passed_fb) { - struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb); - nouveau_bo_unpin(ofb->nvbo); - } - } - - nv_crtc->fb.offset = fb->nvbo->bo.offset; - nv_crtc->fb.tile_flags = nouveau_bo_tile_layout(fb->nvbo); - nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8; - if (!nv_crtc->fb.blanked && nv_device(drm->device)->chipset != 0x50) { - ret = RING_SPACE(evo, 2); - if (ret) - return ret; - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1); - OUT_RING (evo, fb->r_dma); - } - - ret = RING_SPACE(evo, 12); - if (ret) - return ret; - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5); - OUT_RING (evo, nv_crtc->fb.offset >> 8); - OUT_RING (evo, 0); - OUT_RING (evo, (drm_fb->height << 16) | drm_fb->width); - OUT_RING (evo, fb->r_pitch); - OUT_RING (evo, fb->r_format); - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1); - OUT_RING (evo, fb->base.depth == 8 ? - NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON); - - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1); - OUT_RING (evo, (y << 16) | x); - - if (nv_crtc->lut.depth != fb->base.depth) { - nv_crtc->lut.depth = fb->base.depth; - nv50_crtc_lut_load(crtc); - } - - return 0; -} - -static int -nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, - struct drm_display_mode *mode, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct drm_device *dev = crtc->dev; - struct nouveau_channel *evo = nv50_display(dev)->master; - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 head = nv_crtc->index * 0x400; - u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; - u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; - u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; - u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; - u32 vblan2e = 0, vblan2s = 1; - int ret; - - /* hw timing description looks like this: - * - * <sync> <back porch> <---------display---------> <front porch> - * ______ - * |____________|---------------------------|____________| - * - * ^ synce ^ blanke ^ blanks ^ active - * - * interlaced modes also have 2 additional values pointing at the end - * and start of the next field's blanking period. - */ - - hactive = mode->htotal; - hsynce = mode->hsync_end - mode->hsync_start - 1; - hbackp = mode->htotal - mode->hsync_end; - hblanke = hsynce + hbackp; - hfrontp = mode->hsync_start - mode->hdisplay; - hblanks = mode->htotal - hfrontp - 1; - - vactive = mode->vtotal * vscan / ilace; - vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; - vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; - vblanke = vsynce + vbackp; - vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; - vblanks = vactive - vfrontp - 1; - if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - vblan2e = vactive + vsynce + vbackp; - vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); - vactive = (vactive * 2) + 1; - } - - ret = RING_SPACE(evo, 18); - if (ret == 0) { - BEGIN_NV04(evo, 0, 0x0804 + head, 2); - OUT_RING (evo, 0x00800000 | mode->clock); - OUT_RING (evo, (ilace == 2) ? 2 : 0); - BEGIN_NV04(evo, 0, 0x0810 + head, 6); - OUT_RING (evo, 0x00000000); /* border colour */ - OUT_RING (evo, (vactive << 16) | hactive); - OUT_RING (evo, ( vsynce << 16) | hsynce); - OUT_RING (evo, (vblanke << 16) | hblanke); - OUT_RING (evo, (vblanks << 16) | hblanks); - OUT_RING (evo, (vblan2e << 16) | vblan2s); - BEGIN_NV04(evo, 0, 0x082c + head, 1); - OUT_RING (evo, 0x00000000); - BEGIN_NV04(evo, 0, 0x0900 + head, 1); - OUT_RING (evo, 0x00000311); /* makes sync channel work */ - BEGIN_NV04(evo, 0, 0x08c8 + head, 1); - OUT_RING (evo, (umode->vdisplay << 16) | umode->hdisplay); - BEGIN_NV04(evo, 0, 0x08d4 + head, 1); - OUT_RING (evo, 0x00000000); /* screen position */ - } - - nv_crtc->set_dither(nv_crtc, false); - nv_crtc->set_scale(nv_crtc, false); - nv_crtc->set_color_vibrance(nv_crtc, false); - - return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); -} - -static int -nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - int ret; - - nv50_display_flip_stop(crtc); - ret = nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false); - if (ret) - return ret; - - ret = nv50_display_sync(crtc->dev); - if (ret) - return ret; - - return nv50_display_flip_next(crtc, crtc->fb, NULL); -} - -static int -nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc, - struct drm_framebuffer *fb, - int x, int y, enum mode_set_atomic state) -{ - int ret; - - nv50_display_flip_stop(crtc); - ret = nv50_crtc_do_mode_set_base(crtc, fb, x, y, true); - if (ret) - return ret; - - return nv50_display_sync(crtc->dev); -} - -static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = { - .dpms = nv50_crtc_dpms, - .prepare = nv50_crtc_prepare, - .commit = nv50_crtc_commit, - .mode_fixup = nv50_crtc_mode_fixup, - .mode_set = nv50_crtc_mode_set, - .mode_set_base = nv50_crtc_mode_set_base, - .mode_set_base_atomic = nv50_crtc_mode_set_base_atomic, - .load_lut = nv50_crtc_lut_load, -}; - -int -nv50_crtc_create(struct drm_device *dev, int index) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_crtc *nv_crtc = NULL; - int ret, i; - - NV_DEBUG(drm, "\n"); - - nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); - if (!nv_crtc) - return -ENOMEM; - - nv_crtc->index = index; - nv_crtc->set_dither = nv50_crtc_set_dither; - nv_crtc->set_scale = nv50_crtc_set_scale; - nv_crtc->set_color_vibrance = nv50_crtc_set_color_vibrance; - nv_crtc->color_vibrance = 50; - nv_crtc->vibrant_hue = 0; - nv_crtc->lut.depth = 0; - for (i = 0; i < 256; i++) { - nv_crtc->lut.r[i] = i << 8; - nv_crtc->lut.g[i] = i << 8; - nv_crtc->lut.b[i] = i << 8; - } - - drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs); - drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs); - drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256); - - ret = nouveau_bo_new(dev, 4096, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &nv_crtc->lut.nvbo); - if (!ret) { - ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(nv_crtc->lut.nvbo); - if (ret) - nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); - } - - if (ret) - goto out; - - - ret = nouveau_bo_new(dev, 64*64*4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); - if (!ret) { - ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(nv_crtc->cursor.nvbo); - if (ret) - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - } - - if (ret) - goto out; - - nv50_cursor_init(nv_crtc); -out: - if (ret) - nv50_crtc_destroy(&nv_crtc->base); - return ret; -} diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c deleted file mode 100644 index 223da113cee..00000000000 --- a/drivers/gpu/drm/nouveau/nv50_cursor.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2008 Maarten Maathuis. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <drm/drmP.h> - -#include "nouveau_drm.h" -#include "nouveau_dma.h" -#include "nouveau_crtc.h" -#include "nv50_display.h" - -static void -nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update) -{ - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - int ret; - - NV_DEBUG(drm, "\n"); - - if (update && nv_crtc->cursor.visible) - return; - - ret = RING_SPACE(evo, (nv_device(drm->device)->chipset != 0x50 ? 5 : 3) + update * 2); - if (ret) { - NV_ERROR(drm, "no space while unhiding cursor\n"); - return; - } - - if (nv_device(drm->device)->chipset != 0x50) { - BEGIN_NV04(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1); - OUT_RING(evo, NvEvoVRAM); - } - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2); - OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_SHOW); - OUT_RING(evo, nv_crtc->cursor.offset >> 8); - - if (update) { - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - FIRE_RING(evo); - nv_crtc->cursor.visible = true; - } -} - -static void -nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update) -{ - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - int ret; - - NV_DEBUG(drm, "\n"); - - if (update && !nv_crtc->cursor.visible) - return; - - ret = RING_SPACE(evo, (nv_device(drm->device)->chipset != 0x50 ? 5 : 3) + update * 2); - if (ret) { - NV_ERROR(drm, "no space while hiding cursor\n"); - return; - } - BEGIN_NV04(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2); - OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_HIDE); - OUT_RING(evo, 0); - if (nv_device(drm->device)->chipset != 0x50) { - BEGIN_NV04(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1); - OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE); - } - - if (update) { - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - FIRE_RING(evo); - nv_crtc->cursor.visible = false; - } -} - -static void -nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) -{ - struct nouveau_device *device = nouveau_dev(nv_crtc->base.dev); - - nv_crtc->cursor_saved_x = x; nv_crtc->cursor_saved_y = y; - nv_wr32(device, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index), - ((y & 0xFFFF) << 16) | (x & 0xFFFF)); - /* Needed to make the cursor move. */ - nv_wr32(device, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0); -} - -static void -nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) -{ - if (offset == nv_crtc->cursor.offset) - return; - - nv_crtc->cursor.offset = offset; - if (nv_crtc->cursor.visible) { - nv_crtc->cursor.visible = false; - nv_crtc->cursor.show(nv_crtc, true); - } -} - -int -nv50_cursor_init(struct nouveau_crtc *nv_crtc) -{ - nv_crtc->cursor.set_offset = nv50_cursor_set_offset; - nv_crtc->cursor.set_pos = nv50_cursor_set_pos; - nv_crtc->cursor.hide = nv50_cursor_hide; - nv_crtc->cursor.show = nv50_cursor_show; - return 0; -} diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c deleted file mode 100644 index 6a30a174857..00000000000 --- a/drivers/gpu/drm/nouveau/nv50_dac.c +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright (C) 2008 Maarten Maathuis. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> - -#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) -#include "nouveau_reg.h" -#include "nouveau_drm.h" -#include "nouveau_dma.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nv50_display.h" - -#include <subdev/timer.h> - -static void -nv50_dac_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_channel *evo = nv50_display(dev)->master; - int ret; - - if (!nv_encoder->crtc) - return; - nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true); - - NV_DEBUG(drm, "Disconnecting DAC %d\n", nv_encoder->or); - - ret = RING_SPACE(evo, 4); - if (ret) { - NV_ERROR(drm, "no space while disconnecting DAC\n"); - return; - } - BEGIN_NV04(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1); - OUT_RING (evo, 0); - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING (evo, 0); - - nv_encoder->crtc = NULL; -} - -static enum drm_connector_status -nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_device *device = nouveau_dev(encoder->dev); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - enum drm_connector_status status = connector_status_disconnected; - uint32_t dpms_state, load_pattern, load_state; - int or = nv_encoder->or; - - nv_wr32(device, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001); - dpms_state = nv_rd32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or)); - - nv_wr32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or), - 0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); - if (!nv_wait(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or), - NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) { - NV_ERROR(drm, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or); - NV_ERROR(drm, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or, - nv_rd32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or))); - return status; - } - - /* Use bios provided value if possible. */ - if (drm->vbios.dactestval) { - load_pattern = drm->vbios.dactestval; - NV_DEBUG(drm, "Using bios provided load_pattern of %d\n", - load_pattern); - } else { - load_pattern = 340; - NV_DEBUG(drm, "Using default load_pattern of %d\n", - load_pattern); - } - - nv_wr32(device, NV50_PDISPLAY_DAC_LOAD_CTRL(or), - NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE | load_pattern); - mdelay(45); /* give it some time to process */ - load_state = nv_rd32(device, NV50_PDISPLAY_DAC_LOAD_CTRL(or)); - - nv_wr32(device, NV50_PDISPLAY_DAC_LOAD_CTRL(or), 0); - nv_wr32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or), dpms_state | - NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); - - if ((load_state & NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) == - NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) - status = connector_status_connected; - - if (status == connector_status_connected) - NV_DEBUG(drm, "Load was detected on output with or %d\n", or); - else - NV_DEBUG(drm, "Load was not detected on output with or %d\n", or); - - return status; -} - -static void -nv50_dac_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_device *device = nouveau_dev(encoder->dev); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - uint32_t val; - int or = nv_encoder->or; - - NV_DEBUG(drm, "or %d mode %d\n", or, mode); - - /* wait for it to be done */ - if (!nv_wait(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or), - NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) { - NV_ERROR(drm, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or); - NV_ERROR(drm, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or, - nv_rd32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or))); - return; - } - - val = nv_rd32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F; - - if (mode != DRM_MODE_DPMS_ON) - val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED; - - switch (mode) { - case DRM_MODE_DPMS_STANDBY: - val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF; - break; - case DRM_MODE_DPMS_SUSPEND: - val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF; - break; - case DRM_MODE_DPMS_OFF: - val |= NV50_PDISPLAY_DAC_DPMS_CTRL_OFF; - val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF; - val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF; - break; - default: - break; - } - - nv_wr32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val | - NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); -} - -static void -nv50_dac_save(struct drm_encoder *encoder) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - NV_ERROR(drm, "!!\n"); -} - -static void -nv50_dac_restore(struct drm_encoder *encoder) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - NV_ERROR(drm, "!!\n"); -} - -static bool -nv50_dac_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *connector; - - NV_DEBUG(drm, "or %d\n", nv_encoder->or); - - connector = nouveau_encoder_connector_get(nv_encoder); - if (!connector) { - NV_ERROR(drm, "Encoder has no connector\n"); - return false; - } - - if (connector->scaling_mode != DRM_MODE_SCALE_NONE && - connector->native_mode) - drm_mode_copy(adjusted_mode, connector->native_mode); - - return true; -} - -static void -nv50_dac_commit(struct drm_encoder *encoder) -{ -} - -static void -nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct drm_device *dev = encoder->dev; - struct nouveau_channel *evo = nv50_display(dev)->master; - struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); - uint32_t mode_ctl = 0, mode_ctl2 = 0; - int ret; - - NV_DEBUG(drm, "or %d type %d crtc %d\n", - nv_encoder->or, nv_encoder->dcb->type, crtc->index); - - nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON); - - if (crtc->index == 1) - mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC1; - else - mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0; - - /* Lacking a working tv-out, this is not a 100% sure. */ - if (nv_encoder->dcb->type == DCB_OUTPUT_ANALOG) - mode_ctl |= 0x40; - else - if (nv_encoder->dcb->type == DCB_OUTPUT_TV) - mode_ctl |= 0x100; - - if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC) - mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NHSYNC; - - if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC) - mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NVSYNC; - - ret = RING_SPACE(evo, 3); - if (ret) { - NV_ERROR(drm, "no space while connecting DAC\n"); - return; - } - BEGIN_NV04(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2); - OUT_RING(evo, mode_ctl); - OUT_RING(evo, mode_ctl2); - - nv_encoder->crtc = encoder->crtc; -} - -static struct drm_crtc * -nv50_dac_crtc_get(struct drm_encoder *encoder) -{ - return nouveau_encoder(encoder)->crtc; -} - -static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = { - .dpms = nv50_dac_dpms, - .save = nv50_dac_save, - .restore = nv50_dac_restore, - .mode_fixup = nv50_dac_mode_fixup, - .prepare = nv50_dac_disconnect, - .commit = nv50_dac_commit, - .mode_set = nv50_dac_mode_set, - .get_crtc = nv50_dac_crtc_get, - .detect = nv50_dac_detect, - .disable = nv50_dac_disconnect -}; - -static void -nv50_dac_destroy(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - - if (!encoder) - return; - - NV_DEBUG(drm, "\n"); - - drm_encoder_cleanup(encoder); - kfree(nv_encoder); -} - -static const struct drm_encoder_funcs nv50_dac_encoder_funcs = { - .destroy = nv50_dac_destroy, -}; - -int -nv50_dac_create(struct drm_connector *connector, struct dcb_output *entry) -{ - struct nouveau_encoder *nv_encoder; - struct drm_encoder *encoder; - - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - encoder = to_drm_encoder(nv_encoder); - - nv_encoder->dcb = entry; - nv_encoder->or = ffs(entry->or) - 1; - - drm_encoder_init(connector->dev, encoder, &nv50_dac_encoder_funcs, - DRM_MODE_ENCODER_DAC); - drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs); - - encoder->possible_crtcs = entry->heads; - encoder->possible_clones = 0; - - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} - diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index f97b42cbb6b..35874085a61 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -1,969 +1,2058 @@ -/* - * Copyright (C) 2008 Maarten Maathuis. - * All Rights Reserved. + /* + * Copyright 2011 Red Hat Inc. * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. + * The 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 OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * THE 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 <linux/dma-mapping.h> + +#include <drm/drmP.h> +#include <drm/drm_crtc_helper.h> + #include "nouveau_drm.h" #include "nouveau_dma.h" - -#include "nv50_display.h" -#include "nouveau_crtc.h" -#include "nouveau_encoder.h" +#include "nouveau_gem.h" #include "nouveau_connector.h" -#include "nouveau_fbcon.h" -#include <drm/drm_crtc_helper.h> +#include "nouveau_encoder.h" +#include "nouveau_crtc.h" #include "nouveau_fence.h" +#include "nv50_display.h" +#include <core/client.h> #include <core/gpuobj.h> -#include <subdev/timer.h> +#include <core/class.h> -static void nv50_display_bh(unsigned long); - -static inline int -nv50_sor_nr(struct drm_device *dev) +#include <subdev/timer.h> +#include <subdev/bar.h> +#include <subdev/fb.h> + +#define EVO_DMA_NR 9 + +#define EVO_MASTER (0x00) +#define EVO_FLIP(c) (0x01 + (c)) +#define EVO_OVLY(c) (0x05 + (c)) +#define EVO_OIMM(c) (0x09 + (c)) +#define EVO_CURS(c) (0x0d + (c)) + +/* offsets in shared sync bo of various structures */ +#define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) +#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) +#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00) +#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10) + +#define EVO_CORE_HANDLE (0xd1500000) +#define EVO_CHAN_HANDLE(t,i) (0xd15c0000 | (((t) & 0x00ff) << 8) | (i)) +#define EVO_CHAN_OCLASS(t,c) ((nv_hclass(c) & 0xff00) | ((t) & 0x00ff)) +#define EVO_PUSH_HANDLE(t,i) (0xd15b0000 | (i) | \ + (((NV50_DISP_##t##_CLASS) & 0x00ff) << 8)) + +/****************************************************************************** + * EVO channel + *****************************************************************************/ + +struct nv50_chan { + struct nouveau_object *user; + u32 handle; +}; + +static int +nv50_chan_create(struct nouveau_object *core, u32 bclass, u8 head, + void *data, u32 size, struct nv50_chan *chan) { - struct nouveau_device *device = nouveau_dev(dev); + struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS); + const u32 oclass = EVO_CHAN_OCLASS(bclass, core); + const u32 handle = EVO_CHAN_HANDLE(bclass, head); + int ret; - if (device->chipset < 0x90 || - device->chipset == 0x92 || - device->chipset == 0xa0) - return 2; + ret = nouveau_object_new(client, EVO_CORE_HANDLE, handle, + oclass, data, size, &chan->user); + if (ret) + return ret; - return 4; + chan->handle = handle; + return 0; } -u32 -nv50_display_active_crtcs(struct drm_device *dev) +static void +nv50_chan_destroy(struct nouveau_object *core, struct nv50_chan *chan) { - struct nouveau_device *device = nouveau_dev(dev); - u32 mask = 0; - int i; - - if (device->chipset < 0x90 || - device->chipset == 0x92 || - device->chipset == 0xa0) { - for (i = 0; i < 2; i++) - mask |= nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); - } else { - for (i = 0; i < 4; i++) - mask |= nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); - } + struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS); + if (chan->handle) + nouveau_object_del(client, EVO_CORE_HANDLE, chan->handle); +} - for (i = 0; i < 3; i++) - mask |= nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); +/****************************************************************************** + * PIO EVO channel + *****************************************************************************/ - return mask & 3; -} +struct nv50_pioc { + struct nv50_chan base; +}; -int -nv50_display_early_init(struct drm_device *dev) +static void +nv50_pioc_destroy(struct nouveau_object *core, struct nv50_pioc *pioc) { - return 0; + nv50_chan_destroy(core, &pioc->base); } -void -nv50_display_late_takedown(struct drm_device *dev) +static int +nv50_pioc_create(struct nouveau_object *core, u32 bclass, u8 head, + void *data, u32 size, struct nv50_pioc *pioc) { + return nv50_chan_create(core, bclass, head, data, size, &pioc->base); } -int -nv50_display_sync(struct drm_device *dev) -{ - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo = disp->master; - int ret; +/****************************************************************************** + * DMA EVO channel + *****************************************************************************/ - ret = RING_SPACE(evo, 6); - if (ret == 0) { - BEGIN_NV04(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x80000000); - BEGIN_NV04(evo, 0, 0x0080, 1); - OUT_RING (evo, 0); - BEGIN_NV04(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000000); +struct nv50_dmac { + struct nv50_chan base; + dma_addr_t handle; + u32 *ptr; +}; - nv_wo32(disp->ramin, 0x2000, 0x00000000); - FIRE_RING (evo); - - if (nv_wait_ne(disp->ramin, 0x2000, 0xffffffff, 0x00000000)) - return 0; +static void +nv50_dmac_destroy(struct nouveau_object *core, struct nv50_dmac *dmac) +{ + if (dmac->ptr) { + struct pci_dev *pdev = nv_device(core)->pdev; + pci_free_consistent(pdev, PAGE_SIZE, dmac->ptr, dmac->handle); } - return 0; + nv50_chan_destroy(core, &dmac->base); } -int -nv50_display_init(struct drm_device *dev) +static int +nv50_dmac_create_fbdma(struct nouveau_object *core, u32 parent) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_channel *evo; - int ret, i; - u32 val; - - NV_DEBUG(drm, "\n"); - - nv_wr32(device, 0x00610184, nv_rd32(device, 0x00614004)); - - /* - * I think the 0x006101XX range is some kind of main control area - * that enables things. - */ - /* CRTC? */ - for (i = 0; i < 2; i++) { - val = nv_rd32(device, 0x00616100 + (i * 0x800)); - nv_wr32(device, 0x00610190 + (i * 0x10), val); - val = nv_rd32(device, 0x00616104 + (i * 0x800)); - nv_wr32(device, 0x00610194 + (i * 0x10), val); - val = nv_rd32(device, 0x00616108 + (i * 0x800)); - nv_wr32(device, 0x00610198 + (i * 0x10), val); - val = nv_rd32(device, 0x0061610c + (i * 0x800)); - nv_wr32(device, 0x0061019c + (i * 0x10), val); - } - - /* DAC */ - for (i = 0; i < 3; i++) { - val = nv_rd32(device, 0x0061a000 + (i * 0x800)); - nv_wr32(device, 0x006101d0 + (i * 0x04), val); - } - - /* SOR */ - for (i = 0; i < nv50_sor_nr(dev); i++) { - val = nv_rd32(device, 0x0061c000 + (i * 0x800)); - nv_wr32(device, 0x006101e0 + (i * 0x04), val); - } - - /* EXT */ - for (i = 0; i < 3; i++) { - val = nv_rd32(device, 0x0061e000 + (i * 0x800)); - nv_wr32(device, 0x006101f0 + (i * 0x04), val); - } - - for (i = 0; i < 3; i++) { - nv_wr32(device, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 | - NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING); - nv_wr32(device, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001); - } - - /* The precise purpose is unknown, i suspect it has something to do - * with text mode. - */ - if (nv_rd32(device, NV50_PDISPLAY_INTR_1) & 0x100) { - nv_wr32(device, NV50_PDISPLAY_INTR_1, 0x100); - nv_wr32(device, 0x006194e8, nv_rd32(device, 0x006194e8) & ~1); - if (!nv_wait(device, 0x006194e8, 2, 0)) { - NV_ERROR(drm, "timeout: (0x6194e8 & 2) != 0\n"); - NV_ERROR(drm, "0x6194e8 = 0x%08x\n", - nv_rd32(device, 0x6194e8)); - return -EBUSY; - } - } - - for (i = 0; i < 2; i++) { - nv_wr32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000); - if (!nv_wait(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { - NV_ERROR(drm, "timeout: CURSOR_CTRL2_STATUS == 0\n"); - NV_ERROR(drm, "CURSOR_CTRL2 = 0x%08x\n", - nv_rd32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); - return -EBUSY; - } - - nv_wr32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON); - if (!nv_wait(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) { - NV_ERROR(drm, "timeout: " - "CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i); - NV_ERROR(drm, "CURSOR_CTRL2(%d) = 0x%08x\n", i, - nv_rd32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); - return -EBUSY; - } - } - - nv_wr32(device, NV50_PDISPLAY_PIO_CTRL, 0x00000000); - nv_mask(device, NV50_PDISPLAY_INTR_0, 0x00000000, 0x00000000); - nv_wr32(device, NV50_PDISPLAY_INTR_EN_0, 0x00000000); - nv_mask(device, NV50_PDISPLAY_INTR_1, 0x00000000, 0x00000000); - nv_wr32(device, NV50_PDISPLAY_INTR_EN_1, - NV50_PDISPLAY_INTR_EN_1_CLK_UNK10 | - NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 | - NV50_PDISPLAY_INTR_EN_1_CLK_UNK40); - - ret = nv50_evo_init(dev); + struct nouveau_fb *pfb = nouveau_fb(core); + struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS); + struct nouveau_object *object; + int ret = nouveau_object_new(client, parent, NvEvoVRAM_LP, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NV50_DMA_CONF0_ENABLE | + NV50_DMA_CONF0_PART_256, + }, sizeof(struct nv_dma_class), &object); if (ret) return ret; - evo = nv50_display(dev)->master; - - nv_wr32(device, NV50_PDISPLAY_OBJECTS, (nv50_display(dev)->ramin->addr >> 8) | 9); - ret = RING_SPACE(evo, 3); + ret = nouveau_object_new(client, parent, NvEvoFB16, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NV50_DMA_CONF0_ENABLE | 0x70 | + NV50_DMA_CONF0_PART_256, + }, sizeof(struct nv_dma_class), &object); if (ret) return ret; - BEGIN_NV04(evo, 0, NV50_EVO_UNK84, 2); - OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED); - OUT_RING (evo, NvEvoSync); - return nv50_display_sync(dev); + ret = nouveau_object_new(client, parent, NvEvoFB32, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NV50_DMA_CONF0_ENABLE | 0x7a | + NV50_DMA_CONF0_PART_256, + }, sizeof(struct nv_dma_class), &object); + return ret; } -void -nv50_display_fini(struct drm_device *dev) +static int +nvc0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_device *device = nouveau_dev(dev); - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo = disp->master; - struct drm_crtc *drm_crtc; - int ret, i; + struct nouveau_fb *pfb = nouveau_fb(core); + struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS); + struct nouveau_object *object; + int ret = nouveau_object_new(client, parent, NvEvoVRAM_LP, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NVC0_DMA_CONF0_ENABLE, + }, sizeof(struct nv_dma_class), &object); + if (ret) + return ret; - NV_DEBUG(drm, "\n"); + ret = nouveau_object_new(client, parent, NvEvoFB16, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe, + }, sizeof(struct nv_dma_class), &object); + if (ret) + return ret; - list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); + ret = nouveau_object_new(client, parent, NvEvoFB32, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NVC0_DMA_CONF0_ENABLE | 0xfe, + }, sizeof(struct nv_dma_class), &object); + return ret; +} - nv50_crtc_blank(crtc, true); - } +static int +nvd0_dmac_create_fbdma(struct nouveau_object *core, u32 parent) +{ + struct nouveau_fb *pfb = nouveau_fb(core); + struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS); + struct nouveau_object *object; + int ret = nouveau_object_new(client, parent, NvEvoVRAM_LP, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NVD0_DMA_CONF0_ENABLE | + NVD0_DMA_CONF0_PAGE_LP, + }, sizeof(struct nv_dma_class), &object); + if (ret) + return ret; - ret = RING_SPACE(evo, 2); - if (ret == 0) { - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING(evo, 0); - } - FIRE_RING(evo); + ret = nouveau_object_new(client, parent, NvEvoFB32, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + .conf0 = NVD0_DMA_CONF0_ENABLE | 0xfe | + NVD0_DMA_CONF0_PAGE_LP, + }, sizeof(struct nv_dma_class), &object); + return ret; +} - /* Almost like ack'ing a vblank interrupt, maybe in the spirit of - * cleaning up? - */ - list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) { - struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc); - uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index); +static int +nv50_dmac_create(struct nouveau_object *core, u32 bclass, u8 head, + void *data, u32 size, u64 syncbuf, + struct nv50_dmac *dmac) +{ + struct nouveau_fb *pfb = nouveau_fb(core); + struct nouveau_object *client = nv_pclass(core, NV_CLIENT_CLASS); + struct nouveau_object *object; + u32 pushbuf = *(u32 *)data; + int ret; - if (!crtc->base.enabled) - continue; + dmac->ptr = pci_alloc_consistent(nv_device(core)->pdev, PAGE_SIZE, + &dmac->handle); + if (!dmac->ptr) + return -ENOMEM; - nv_wr32(device, NV50_PDISPLAY_INTR_1, mask); - if (!nv_wait(device, NV50_PDISPLAY_INTR_1, mask, mask)) { - NV_ERROR(drm, "timeout: (0x610024 & 0x%08x) == " - "0x%08x\n", mask, mask); - NV_ERROR(drm, "0x610024 = 0x%08x\n", - nv_rd32(device, NV50_PDISPLAY_INTR_1)); - } - } + ret = nouveau_object_new(client, NVDRM_DEVICE, pushbuf, + NV_DMA_FROM_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_PCI_US | + NV_DMA_ACCESS_RD, + .start = dmac->handle + 0x0000, + .limit = dmac->handle + 0x0fff, + }, sizeof(struct nv_dma_class), &object); + if (ret) + return ret; - for (i = 0; i < 2; i++) { - nv_wr32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0); - if (!nv_wait(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), - NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) { - NV_ERROR(drm, "timeout: CURSOR_CTRL2_STATUS == 0\n"); - NV_ERROR(drm, "CURSOR_CTRL2 = 0x%08x\n", - nv_rd32(device, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i))); - } - } + ret = nv50_chan_create(core, bclass, head, data, size, &dmac->base); + if (ret) + return ret; - nv50_evo_fini(dev); + ret = nouveau_object_new(client, dmac->base.handle, NvEvoSync, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = syncbuf + 0x0000, + .limit = syncbuf + 0x0fff, + }, sizeof(struct nv_dma_class), &object); + if (ret) + return ret; - for (i = 0; i < 3; i++) { - if (!nv_wait(device, NV50_PDISPLAY_SOR_DPMS_STATE(i), - NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { - NV_ERROR(drm, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i); - NV_ERROR(drm, "SOR_DPMS_STATE(%d) = 0x%08x\n", i, - nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_STATE(i))); - } - } + ret = nouveau_object_new(client, dmac->base.handle, NvEvoVRAM, + NV_DMA_IN_MEMORY_CLASS, + &(struct nv_dma_class) { + .flags = NV_DMA_TARGET_VRAM | + NV_DMA_ACCESS_RDWR, + .start = 0, + .limit = pfb->ram.size - 1, + }, sizeof(struct nv_dma_class), &object); + if (ret) + return ret; - /* disable interrupts. */ - nv_wr32(device, NV50_PDISPLAY_INTR_EN_1, 0x00000000); + if (nv_device(core)->card_type < NV_C0) + ret = nv50_dmac_create_fbdma(core, dmac->base.handle); + else + if (nv_device(core)->card_type < NV_D0) + ret = nvc0_dmac_create_fbdma(core, dmac->base.handle); + else + ret = nvd0_dmac_create_fbdma(core, dmac->base.handle); + return ret; } -int -nv50_display_create(struct drm_device *dev) +struct nv50_mast { + struct nv50_dmac base; +}; + +struct nv50_curs { + struct nv50_pioc base; +}; + +struct nv50_sync { + struct nv50_dmac base; + struct { + u32 offset; + u16 value; + } sem; +}; + +struct nv50_ovly { + struct nv50_dmac base; +}; + +struct nv50_oimm { + struct nv50_pioc base; +}; + +struct nv50_head { + struct nouveau_crtc base; + struct nv50_curs curs; + struct nv50_sync sync; + struct nv50_ovly ovly; + struct nv50_oimm oimm; +}; + +#define nv50_head(c) ((struct nv50_head *)nouveau_crtc(c)) +#define nv50_curs(c) (&nv50_head(c)->curs) +#define nv50_sync(c) (&nv50_head(c)->sync) +#define nv50_ovly(c) (&nv50_head(c)->ovly) +#define nv50_oimm(c) (&nv50_head(c)->oimm) +#define nv50_chan(c) (&(c)->base.base) +#define nv50_vers(c) nv_mclass(nv50_chan(c)->user) + +struct nv50_disp { + struct nouveau_object *core; + struct nv50_mast mast; + + u32 modeset; + + struct nouveau_bo *sync; +}; + +static struct nv50_disp * +nv50_disp(struct drm_device *dev) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_table *dcb = &drm->vbios.dcb; - struct drm_connector *connector, *ct; - struct nv50_display *priv; - int ret, i; + return nouveau_display(dev)->priv; +} - NV_DEBUG(drm, "\n"); +#define nv50_mast(d) (&nv50_disp(d)->mast) - priv = kzalloc(sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - nouveau_display(dev)->priv = priv; - nouveau_display(dev)->dtor = nv50_display_destroy; - nouveau_display(dev)->init = nv50_display_init; - nouveau_display(dev)->fini = nv50_display_fini; +static struct drm_crtc * +nv50_display_crtc_get(struct drm_encoder *encoder) +{ + return nouveau_encoder(encoder)->crtc; +} - /* Create CRTC objects */ - for (i = 0; i < 2; i++) { - ret = nv50_crtc_create(dev, i); - if (ret) - return ret; - } +/****************************************************************************** + * EVO channel helpers + *****************************************************************************/ +static u32 * +evo_wait(void *evoc, int nr) +{ + struct nv50_dmac *dmac = evoc; + u32 put = nv_ro32(dmac->base.user, 0x0000) / 4; - /* We setup the encoders from the BIOS table */ - for (i = 0 ; i < dcb->entries; i++) { - struct dcb_output *entry = &dcb->entry[i]; + if (put + nr >= (PAGE_SIZE / 4) - 8) { + dmac->ptr[put] = 0x20000000; - if (entry->location != DCB_LOC_ON_CHIP) { - NV_WARN(drm, "Off-chip encoder %d/%d unsupported\n", - entry->type, ffs(entry->or) - 1); - continue; + nv_wo32(dmac->base.user, 0x0000, 0x00000000); + if (!nv_wait(dmac->base.user, 0x0004, ~0, 0x00000000)) { + NV_ERROR(dmac->base.user, "channel stalled\n"); + return NULL; } - connector = nouveau_connector_create(dev, entry->connector); - if (IS_ERR(connector)) - continue; - - switch (entry->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - nv50_sor_create(connector, entry); - break; - case DCB_OUTPUT_ANALOG: - nv50_dac_create(connector, entry); - break; - default: - NV_WARN(drm, "DCB encoder %d unknown\n", entry->type); - continue; - } + put = 0; } - list_for_each_entry_safe(connector, ct, - &dev->mode_config.connector_list, head) { - if (!connector->encoder_ids[0]) { - NV_WARN(drm, "%s has no encoders, removing\n", - drm_get_connector_name(connector)); - connector->funcs->destroy(connector); - } - } + return dmac->ptr + put; +} - tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev); +static void +evo_kick(u32 *push, void *evoc) +{ + struct nv50_dmac *dmac = evoc; + nv_wo32(dmac->base.user, 0x0000, (push - dmac->ptr) << 2); +} - ret = nv50_evo_create(dev); - if (ret) { - nv50_display_destroy(dev); - return ret; - } +#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) +#define evo_data(p,d) *((p)++) = (d) - return 0; +static bool +evo_sync_wait(void *data) +{ + return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000; } -void -nv50_display_destroy(struct drm_device *dev) +static int +evo_sync(struct drm_device *dev) { - struct nv50_display *disp = nv50_display(dev); + struct nouveau_device *device = nouveau_dev(dev); + struct nv50_disp *disp = nv50_disp(dev); + struct nv50_mast *mast = nv50_mast(dev); + u32 *push = evo_wait(mast, 8); + if (push) { + nouveau_bo_wr32(disp->sync, EVO_MAST_NTFY, 0x00000000); + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x80000000 | EVO_MAST_NTFY); + evo_mthd(push, 0x0080, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_kick(push, mast); + if (nv_wait_cb(device, evo_sync_wait, disp->sync)) + return 0; + } - nv50_evo_destroy(dev); - kfree(disp); + return -EBUSY; } +/****************************************************************************** + * Page flipping channel + *****************************************************************************/ struct nouveau_bo * nv50_display_crtc_sema(struct drm_device *dev, int crtc) { - return nv50_display(dev)->crtc[crtc].sem.bo; + return nv50_disp(dev)->sync; } void nv50_display_flip_stop(struct drm_crtc *crtc) { - struct nv50_display *disp = nv50_display(crtc->dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; - struct nouveau_channel *evo = dispc->sync; - int ret; - - ret = RING_SPACE(evo, 8); - if (ret) { - WARN_ON(1); - return; + struct nv50_sync *sync = nv50_sync(crtc); + u32 *push; + + push = evo_wait(sync, 8); + if (push) { + evo_mthd(push, 0x0084, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0094, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, sync); } - - BEGIN_NV04(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000000); - BEGIN_NV04(evo, 0, 0x0094, 1); - OUT_RING (evo, 0x00000000); - BEGIN_NV04(evo, 0, 0x00c0, 1); - OUT_RING (evo, 0x00000000); - BEGIN_NV04(evo, 0, 0x0080, 1); - OUT_RING (evo, 0x00000000); - FIRE_RING (evo); } int nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, - struct nouveau_channel *chan) + struct nouveau_channel *chan, u32 swap_interval) { - struct nouveau_drm *drm = nouveau_drm(crtc->dev); struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); - struct nv50_display *disp = nv50_display(crtc->dev); + struct nv50_disp *disp = nv50_disp(crtc->dev); struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nv50_display_crtc *dispc = &disp->crtc[nv_crtc->index]; - struct nouveau_channel *evo = dispc->sync; + struct nv50_sync *sync = nv50_sync(crtc); + u32 *push; int ret; - ret = RING_SPACE(evo, chan ? 25 : 27); - if (unlikely(ret)) - return ret; + swap_interval <<= 4; + if (swap_interval == 0) + swap_interval |= 0x100; + + push = evo_wait(sync, 128); + if (unlikely(push == NULL)) + return -EBUSY; /* synchronise with the rendering channel, if necessary */ if (likely(chan)) { ret = RING_SPACE(chan, 10); - if (ret) { - WIND_RING(evo); + if (ret) return ret; - } - if (nv_device(drm->device)->chipset < 0xc0) { - BEGIN_NV04(chan, 0, 0x0060, 2); + if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); OUT_RING (chan, NvEvoSema0 + nv_crtc->index); - OUT_RING (chan, dispc->sem.offset); - BEGIN_NV04(chan, 0, 0x006c, 1); - OUT_RING (chan, 0xf00d0000 | dispc->sem.value); - BEGIN_NV04(chan, 0, 0x0064, 2); - OUT_RING (chan, dispc->sem.offset ^ 0x10); + OUT_RING (chan, sync->sem.offset); + BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); + OUT_RING (chan, 0xf00d0000 | sync->sem.value); + BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); + OUT_RING (chan, sync->sem.offset ^ 0x10); OUT_RING (chan, 0x74b1e000); - BEGIN_NV04(chan, 0, 0x0060, 1); - if (nv_device(drm->device)->chipset < 0x84) + BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); + if (nv_mclass(chan->object) < NV84_CHANNEL_DMA_CLASS) OUT_RING (chan, NvSema); else OUT_RING (chan, chan->vram); } else { u64 offset = nvc0_fence_crtc(chan, nv_crtc->index); - offset += dispc->sem.offset; - BEGIN_NVC0(chan, 0, 0x0010, 4); + offset += sync->sem.offset; + + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 0xf00d0000 | dispc->sem.value); + OUT_RING (chan, 0xf00d0000 | sync->sem.value); OUT_RING (chan, 0x1002); - BEGIN_NVC0(chan, 0, 0x0010, 4); + BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); OUT_RING (chan, upper_32_bits(offset)); OUT_RING (chan, lower_32_bits(offset ^ 0x10)); OUT_RING (chan, 0x74b1e000); OUT_RING (chan, 0x1001); } + FIRE_RING (chan); } else { - nouveau_bo_wr32(dispc->sem.bo, dispc->sem.offset / 4, - 0xf00d0000 | dispc->sem.value); + nouveau_bo_wr32(disp->sync, sync->sem.offset / 4, + 0xf00d0000 | sync->sem.value); + evo_sync(crtc->dev); } - /* queue the flip on the crtc's "display sync" channel */ - BEGIN_NV04(evo, 0, 0x0100, 1); - OUT_RING (evo, 0xfffe0000); - if (chan) { - BEGIN_NV04(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000100); + /* queue the flip */ + evo_mthd(push, 0x0100, 1); + evo_data(push, 0xfffe0000); + evo_mthd(push, 0x0084, 1); + evo_data(push, swap_interval); + if (!(swap_interval & 0x00000100)) { + evo_mthd(push, 0x00e0, 1); + evo_data(push, 0x40000000); + } + evo_mthd(push, 0x0088, 4); + evo_data(push, sync->sem.offset); + evo_data(push, 0xf00d0000 | sync->sem.value); + evo_data(push, 0x74b1e000); + evo_data(push, NvEvoSync); + evo_mthd(push, 0x00a0, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x00c0, 1); + evo_data(push, nv_fb->r_dma); + evo_mthd(push, 0x0110, 2); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + if (nv50_vers(sync) < NVD0_DISP_SYNC_CLASS) { + evo_mthd(push, 0x0800, 5); + evo_data(push, nv_fb->nvbo->bo.offset >> 8); + evo_data(push, 0); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nv_fb->r_pitch); + evo_data(push, nv_fb->r_format); } else { - BEGIN_NV04(evo, 0, 0x0084, 1); - OUT_RING (evo, 0x00000010); - /* allows gamma somehow, PDISP will bitch at you if - * you don't wait for vblank before changing this.. - */ - BEGIN_NV04(evo, 0, 0x00e0, 1); - OUT_RING (evo, 0x40000000); - } - BEGIN_NV04(evo, 0, 0x0088, 4); - OUT_RING (evo, dispc->sem.offset); - OUT_RING (evo, 0xf00d0000 | dispc->sem.value); - OUT_RING (evo, 0x74b1e000); - OUT_RING (evo, NvEvoSync); - BEGIN_NV04(evo, 0, 0x00a0, 2); - OUT_RING (evo, 0x00000000); - OUT_RING (evo, 0x00000000); - BEGIN_NV04(evo, 0, 0x00c0, 1); - OUT_RING (evo, nv_fb->r_dma); - BEGIN_NV04(evo, 0, 0x0110, 2); - OUT_RING (evo, 0x00000000); - OUT_RING (evo, 0x00000000); - BEGIN_NV04(evo, 0, 0x0800, 5); - OUT_RING (evo, nv_fb->nvbo->bo.offset >> 8); - OUT_RING (evo, 0); - OUT_RING (evo, (fb->height << 16) | fb->width); - OUT_RING (evo, nv_fb->r_pitch); - OUT_RING (evo, nv_fb->r_format); - BEGIN_NV04(evo, 0, 0x0080, 1); - OUT_RING (evo, 0x00000000); - FIRE_RING (evo); - - dispc->sem.offset ^= 0x10; - dispc->sem.value++; + evo_mthd(push, 0x0400, 5); + evo_data(push, nv_fb->nvbo->bo.offset >> 8); + evo_data(push, 0); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nv_fb->r_pitch); + evo_data(push, nv_fb->r_format); + } + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, sync); + + sync->sem.offset ^= 0x10; + sync->sem.value++; return 0; } -static u16 -nv50_display_script_select(struct drm_device *dev, struct dcb_output *dcb, - u32 mc, int pxclk) +/****************************************************************************** + * CRTC + *****************************************************************************/ +static int +nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) { - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_connector *nv_connector = NULL; - struct drm_encoder *encoder; - struct nvbios *bios = &drm->vbios; - u32 script = 0, or; + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + struct nouveau_connector *nv_connector; + struct drm_connector *connector; + u32 *push, mode = 0x00; + + nv_connector = nouveau_crtc_connector_get(nv_crtc); + connector = &nv_connector->base; + if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { + if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) + mode = DITHERING_MODE_DYNAMIC2X2; + } else { + mode = nv_connector->dithering_mode; + } - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { + if (connector->display_info.bpc >= 8) + mode |= DITHERING_DEPTH_8BPC; + } else { + mode |= nv_connector->dithering_depth; + } - if (nv_encoder->dcb != dcb) - continue; + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x08a0 + (nv_crtc->index * 0x0400), 1); + evo_data(push, mode); + } else + if (nv50_vers(mast) < NVE0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0490 + (nv_crtc->index * 0x0300), 1); + evo_data(push, mode); + } else { + evo_mthd(push, 0x04a0 + (nv_crtc->index * 0x0300), 1); + evo_data(push, mode); + } - nv_connector = nouveau_encoder_connector_get(nv_encoder); - break; + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); } - or = ffs(dcb->or) - 1; - switch (dcb->type) { - case DCB_OUTPUT_LVDS: - script = (mc >> 8) & 0xf; - if (bios->fp_no_ddc) { - if (bios->fp.dual_link) - script |= 0x0100; - if (bios->fp.if_is_24bit) - script |= 0x0200; - } else { - /* determine number of lvds links */ - if (nv_connector && nv_connector->edid && - nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { - /* http://www.spwg.org */ - if (((u8 *)nv_connector->edid)[121] == 2) - script |= 0x0100; - } else - if (pxclk >= bios->fp.duallink_transition_clk) { - script |= 0x0100; - } + return 0; +} - /* determine panel depth */ - if (script & 0x0100) { - if (bios->fp.strapless_is_24bit & 2) - script |= 0x0200; - } else { - if (bios->fp.strapless_is_24bit & 1) - script |= 0x0200; - } +static int +nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; + struct drm_crtc *crtc = &nv_crtc->base; + struct nouveau_connector *nv_connector; + int mode = DRM_MODE_SCALE_NONE; + u32 oX, oY, *push; + + /* start off at the resolution we programmed the crtc for, this + * effectively handles NONE/FULL scaling + */ + nv_connector = nouveau_crtc_connector_get(nv_crtc); + if (nv_connector && nv_connector->native_mode) + mode = nv_connector->scaling_mode; + + if (mode != DRM_MODE_SCALE_NONE) + omode = nv_connector->native_mode; + else + omode = umode; + + oX = omode->hdisplay; + oY = omode->vdisplay; + if (omode->flags & DRM_MODE_FLAG_DBLSCAN) + oY *= 2; + + /* add overscan compensation if necessary, will keep the aspect + * ratio the same as the backend mode unless overridden by the + * user setting both hborder and vborder properties. + */ + if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || + (nv_connector->underscan == UNDERSCAN_AUTO && + nv_connector->edid && + drm_detect_hdmi_monitor(nv_connector->edid)))) { + u32 bX = nv_connector->underscan_hborder; + u32 bY = nv_connector->underscan_vborder; + u32 aspect = (oY << 19) / oX; + + if (bX) { + oX -= (bX * 2); + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } else { + oX -= (oX >> 4) + 32; + if (bY) oY -= (bY * 2); + else oY = ((oX * aspect) + (aspect / 2)) >> 19; + } + } - if (nv_connector && nv_connector->edid && - (nv_connector->edid->revision >= 4) && - (nv_connector->edid->input & 0x70) >= 0x20) - script |= 0x0200; + /* handle CENTER/ASPECT scaling, taking into account the areas + * removed already for overscan compensation + */ + switch (mode) { + case DRM_MODE_SCALE_CENTER: + oX = min((u32)umode->hdisplay, oX); + oY = min((u32)umode->vdisplay, oY); + /* fall-through */ + case DRM_MODE_SCALE_ASPECT: + if (oY < oX) { + u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; + oX = ((oY * aspect) + (aspect / 2)) >> 19; + } else { + u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; + oY = ((oX * aspect) + (aspect / 2)) >> 19; } break; - case DCB_OUTPUT_TMDS: - script = (mc >> 8) & 0xf; - if (pxclk >= 165000) - script |= 0x0100; - break; - case DCB_OUTPUT_DP: - script = (mc >> 8) & 0xf; - break; - case DCB_OUTPUT_ANALOG: - script = 0xff; - break; default: - NV_ERROR(drm, "modeset on unsupported output type!\n"); break; } - return script; + push = evo_wait(mast, 8); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + /*XXX: SCALE_CTRL_ACTIVE??? */ + evo_mthd(push, 0x08d8 + (nv_crtc->index * 0x400), 2); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_mthd(push, 0x08a4 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x08c8 + (nv_crtc->index * 0x400), 1); + evo_data(push, umode->vdisplay << 16 | umode->hdisplay); + } else { + evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_data(push, (oY << 16) | oX); + evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); + evo_data(push, umode->vdisplay << 16 | umode->hdisplay); + } + + evo_kick(push, mast); + + if (update) { + nv50_display_flip_stop(crtc); + nv50_display_flip_next(crtc, crtc->fb, NULL, 1); + } + } + + return 0; } -static void -nv50_display_unk10_handler(struct drm_device *dev) +static int +nv50_crtc_set_color_vibrance(struct nouveau_crtc *nv_crtc, bool update) { - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv50_display *disp = nv50_display(dev); - u32 unk30 = nv_rd32(device, 0x610030), mc; - int i, crtc, or = 0, type = DCB_OUTPUT_ANY; + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push, hue, vib; + int adj; + + adj = (nv_crtc->color_vibrance > 0) ? 50 : 0; + vib = ((nv_crtc->color_vibrance * 2047 + adj) / 100) & 0xfff; + hue = ((nv_crtc->vibrant_hue * 2047) / 100) & 0xfff; + + push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x08a8 + (nv_crtc->index * 0x400), 1); + evo_data(push, (hue << 20) | (vib << 8)); + } else { + evo_mthd(push, 0x0498 + (nv_crtc->index * 0x300), 1); + evo_data(push, (hue << 20) | (vib << 8)); + } - NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30); - disp->irq.dcb = NULL; + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } - nv_wr32(device, 0x619494, nv_rd32(device, 0x619494) & ~8); + return 0; +} - /* Determine which CRTC we're dealing with, only 1 ever will be - * signalled at the same time with the current nouveau code. - */ - crtc = ffs((unk30 & 0x00000060) >> 5) - 1; - if (crtc < 0) - goto ack; - - /* Nothing needs to be done for the encoder */ - crtc = ffs((unk30 & 0x00000180) >> 7) - 1; - if (crtc < 0) - goto ack; - - /* Find which encoder was connected to the CRTC */ - for (i = 0; type == DCB_OUTPUT_ANY && i < 3; i++) { - mc = nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_C(i)); - NV_DEBUG(drm, "DAC-%d mc: 0x%08x\n", i, mc); - if (!(mc & (1 << crtc))) - continue; +static int +nv50_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, + int x, int y, bool update) +{ + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push; + + push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0860 + (nv_crtc->index * 0x400), 1); + evo_data(push, nvfb->nvbo->bo.offset >> 8); + evo_mthd(push, 0x0868 + (nv_crtc->index * 0x400), 3); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nvfb->r_pitch); + evo_data(push, nvfb->r_format); + evo_mthd(push, 0x08c0 + (nv_crtc->index * 0x400), 1); + evo_data(push, (y << 16) | x); + if (nv50_vers(mast) > NV50_DISP_MAST_CLASS) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, nvfb->r_dma); + } + } else { + evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); + evo_data(push, nvfb->nvbo->bo.offset >> 8); + evo_mthd(push, 0x0468 + (nv_crtc->index * 0x300), 4); + evo_data(push, (fb->height << 16) | fb->width); + evo_data(push, nvfb->r_pitch); + evo_data(push, nvfb->r_format); + evo_data(push, nvfb->r_dma); + evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); + evo_data(push, (y << 16) | x); + } - switch ((mc & 0x00000f00) >> 8) { - case 0: type = DCB_OUTPUT_ANALOG; break; - case 1: type = DCB_OUTPUT_TV; break; - default: - NV_ERROR(drm, "invalid mc, DAC-%d: 0x%08x\n", i, mc); - goto ack; + if (update) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); } + evo_kick(push, mast); + } - or = i; + nv_crtc->fb.tile_flags = nvfb->r_dma; + return 0; +} + +static void +nv50_crtc_cursor_show(struct nouveau_crtc *nv_crtc) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + } else + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1); + evo_data(push, NvEvoVRAM); + } else { + evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x85000000); + evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); + evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); + evo_data(push, NvEvoVRAM); + } + evo_kick(push, mast); } +} - for (i = 0; type == DCB_OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { - if (nv_device(drm->device)->chipset < 0x90 || - nv_device(drm->device)->chipset == 0x92 || - nv_device(drm->device)->chipset == 0xa0) - mc = nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_C(i)); - else - mc = nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_C(i)); +static void +nv50_crtc_cursor_hide(struct nouveau_crtc *nv_crtc) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + u32 *push = evo_wait(mast, 16); + if (push) { + if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x05000000); + } else + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0880 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x05000000); + evo_mthd(push, 0x089c + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x05000000); + evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + } + evo_kick(push, mast); + } +} - NV_DEBUG(drm, "SOR-%d mc: 0x%08x\n", i, mc); - if (!(mc & (1 << crtc))) - continue; +static void +nv50_crtc_cursor_show_hide(struct nouveau_crtc *nv_crtc, bool show, bool update) +{ + struct nv50_mast *mast = nv50_mast(nv_crtc->base.dev); + + if (show) + nv50_crtc_cursor_show(nv_crtc); + else + nv50_crtc_cursor_hide(nv_crtc); + + if (update) { + u32 *push = evo_wait(mast, 2); + if (push) { + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, mast); + } + } +} - switch ((mc & 0x00000f00) >> 8) { - case 0: type = DCB_OUTPUT_LVDS; break; - case 1: type = DCB_OUTPUT_TMDS; break; - case 2: type = DCB_OUTPUT_TMDS; break; - case 5: type = DCB_OUTPUT_TMDS; break; - case 8: type = DCB_OUTPUT_DP; break; - case 9: type = DCB_OUTPUT_DP; break; - default: - NV_ERROR(drm, "invalid mc, SOR-%d: 0x%08x\n", i, mc); - goto ack; +static void +nv50_crtc_dpms(struct drm_crtc *crtc, int mode) +{ +} + +static void +nv50_crtc_prepare(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_mast *mast = nv50_mast(crtc->dev); + u32 *push; + + nv50_display_flip_stop(crtc); + + push = evo_wait(mast, 2); + if (push) { + if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x40000000); + } else + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x40000000); + evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x03000000); + evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); + } + + evo_kick(push, mast); + } + + nv50_crtc_cursor_show_hide(nv_crtc, false, false); +} + +static void +nv50_crtc_commit(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_mast *mast = nv50_mast(crtc->dev); + u32 *push; + + push = evo_wait(mast, 32); + if (push) { + if (nv50_vers(mast) < NV84_DISP_MAST_CLASS) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, NvEvoVRAM_LP); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0xc0000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + } else + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0874 + (nv_crtc->index * 0x400), 1); + evo_data(push, nv_crtc->fb.tile_flags); + evo_mthd(push, 0x0840 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0xc0000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + evo_mthd(push, 0x085c + (nv_crtc->index * 0x400), 1); + evo_data(push, NvEvoVRAM); + } else { + evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); + evo_data(push, nv_crtc->fb.tile_flags); + evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4); + evo_data(push, 0x83000000); + evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); + evo_data(push, 0x00000000); + evo_data(push, 0x00000000); + evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); + evo_data(push, NvEvoVRAM); + evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); + evo_data(push, 0xffffff00); } - or = i; + evo_kick(push, mast); + } + + nv50_crtc_cursor_show_hide(nv_crtc, nv_crtc->cursor.visible, true); + nv50_display_flip_next(crtc, crtc->fb, NULL, 1); +} + +static bool +nv50_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + return true; +} + +static int +nv50_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) +{ + struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb); + int ret; + + ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM); + if (ret) + return ret; + + if (old_fb) { + nvfb = nouveau_framebuffer(old_fb); + nouveau_bo_unpin(nvfb->nvbo); } - /* There was no encoder to disable */ - if (type == DCB_OUTPUT_ANY) - goto ack; + return 0; +} + +static int +nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, + struct drm_display_mode *mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct nv50_mast *mast = nv50_mast(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nouveau_connector *nv_connector; + u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; + u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; + u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; + u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; + u32 vblan2e = 0, vblan2s = 1; + u32 *push; + int ret; + + hactive = mode->htotal; + hsynce = mode->hsync_end - mode->hsync_start - 1; + hbackp = mode->htotal - mode->hsync_end; + hblanke = hsynce + hbackp; + hfrontp = mode->hsync_start - mode->hdisplay; + hblanks = mode->htotal - hfrontp - 1; + + vactive = mode->vtotal * vscan / ilace; + vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; + vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; + vblanke = vsynce + vbackp; + vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; + vblanks = vactive - vfrontp - 1; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) { + vblan2e = vactive + vsynce + vbackp; + vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); + vactive = (vactive * 2) + 1; + } - /* Disable the encoder */ - for (i = 0; i < drm->vbios.dcb.entries; i++) { - struct dcb_output *dcb = &drm->vbios.dcb.entry[i]; + ret = nv50_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; - if (dcb->type == type && (dcb->or & (1 << or))) { - nouveau_bios_run_display_table(dev, 0, -1, dcb, -1); - disp->irq.dcb = dcb; - goto ack; + push = evo_wait(mast, 64); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0804 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x00800000 | mode->clock); + evo_data(push, (ilace == 2) ? 2 : 0); + evo_mthd(push, 0x0810 + (nv_crtc->index * 0x400), 6); + evo_data(push, 0x00000000); + evo_data(push, (vactive << 16) | hactive); + evo_data(push, ( vsynce << 16) | hsynce); + evo_data(push, (vblanke << 16) | hblanke); + evo_data(push, (vblanks << 16) | hblanks); + evo_data(push, (vblan2e << 16) | vblan2s); + evo_mthd(push, 0x082c + (nv_crtc->index * 0x400), 1); + evo_data(push, 0x00000000); + evo_mthd(push, 0x0900 + (nv_crtc->index * 0x400), 2); + evo_data(push, 0x00000311); + evo_data(push, 0x00000100); + } else { + evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6); + evo_data(push, 0x00000000); + evo_data(push, (vactive << 16) | hactive); + evo_data(push, ( vsynce << 16) | hsynce); + evo_data(push, (vblanke << 16) | hblanke); + evo_data(push, (vblanks << 16) | hblanks); + evo_data(push, (vblan2e << 16) | vblan2s); + evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1); + evo_data(push, 0x00000000); /* ??? */ + evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3); + evo_data(push, mode->clock * 1000); + evo_data(push, 0x00200000); /* ??? */ + evo_data(push, mode->clock * 1000); + evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2); + evo_data(push, 0x00000311); + evo_data(push, 0x00000100); } + + evo_kick(push, mast); } - NV_ERROR(drm, "no dcb for %d %d 0x%08x\n", or, type, mc); -ack: - nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10); - nv_wr32(device, 0x610030, 0x80000000); + nv_connector = nouveau_crtc_connector_get(nv_crtc); + nv50_crtc_set_dither(nv_crtc, false); + nv50_crtc_set_scale(nv_crtc, false); + nv50_crtc_set_color_vibrance(nv_crtc, false); + nv50_crtc_set_image(nv_crtc, crtc->fb, x, y, false); + return 0; +} + +static int +nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + struct nouveau_drm *drm = nouveau_drm(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + int ret; + + if (!crtc->fb) { + NV_DEBUG(drm, "No FB bound\n"); + return 0; + } + + ret = nv50_crtc_swap_fbs(crtc, old_fb); + if (ret) + return ret; + + nv50_display_flip_stop(crtc); + nv50_crtc_set_image(nv_crtc, crtc->fb, x, y, true); + nv50_display_flip_next(crtc, crtc->fb, NULL, 1); + return 0; +} + +static int +nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc, + struct drm_framebuffer *fb, int x, int y, + enum mode_set_atomic state) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + nv50_display_flip_stop(crtc); + nv50_crtc_set_image(nv_crtc, fb, x, y, true); + return 0; } static void -nv50_display_unk20_handler(struct drm_device *dev) +nv50_crtc_lut_load(struct drm_crtc *crtc) { - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv50_display *disp = nv50_display(dev); - u32 unk30 = nv_rd32(device, 0x610030), tmp, pclk, script, mc = 0; - struct dcb_output *dcb; - int i, crtc, or = 0, type = DCB_OUTPUT_ANY; - - NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30); - dcb = disp->irq.dcb; - if (dcb) { - nouveau_bios_run_display_table(dev, 0, -2, dcb, -1); - disp->irq.dcb = NULL; - } - - /* CRTC clock change requested? */ - crtc = ffs((unk30 & 0x00000600) >> 9) - 1; - if (crtc >= 0) { - pclk = nv_rd32(device, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)); - pclk &= 0x003fffff; - if (pclk) - nv50_crtc_set_clock(dev, crtc, pclk); - - tmp = nv_rd32(device, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc)); - tmp &= ~0x000000f; - nv_wr32(device, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc), tmp); - } - - /* Nothing needs to be done for the encoder */ - crtc = ffs((unk30 & 0x00000180) >> 7) - 1; - if (crtc < 0) - goto ack; - pclk = nv_rd32(device, NV50_PDISPLAY_CRTC_P(crtc, CLOCK)) & 0x003fffff; - - /* Find which encoder is connected to the CRTC */ - for (i = 0; type == DCB_OUTPUT_ANY && i < 3; i++) { - mc = nv_rd32(device, NV50_PDISPLAY_DAC_MODE_CTRL_P(i)); - NV_DEBUG(drm, "DAC-%d mc: 0x%08x\n", i, mc); - if (!(mc & (1 << crtc))) - continue; + struct nv50_disp *disp = nv50_disp(crtc->dev); + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo); + int i; - switch ((mc & 0x00000f00) >> 8) { - case 0: type = DCB_OUTPUT_ANALOG; break; - case 1: type = DCB_OUTPUT_TV; break; - default: - NV_ERROR(drm, "invalid mc, DAC-%d: 0x%08x\n", i, mc); - goto ack; + for (i = 0; i < 256; i++) { + u16 r = nv_crtc->lut.r[i] >> 2; + u16 g = nv_crtc->lut.g[i] >> 2; + u16 b = nv_crtc->lut.b[i] >> 2; + + if (nv_mclass(disp->core) < NVD0_DISP_CLASS) { + writew(r + 0x0000, lut + (i * 0x08) + 0); + writew(g + 0x0000, lut + (i * 0x08) + 2); + writew(b + 0x0000, lut + (i * 0x08) + 4); + } else { + writew(r + 0x6000, lut + (i * 0x20) + 0); + writew(g + 0x6000, lut + (i * 0x20) + 2); + writew(b + 0x6000, lut + (i * 0x20) + 4); + } + } +} + +static int +nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, + uint32_t handle, uint32_t width, uint32_t height) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct drm_device *dev = crtc->dev; + struct drm_gem_object *gem; + struct nouveau_bo *nvbo; + bool visible = (handle != 0); + int i, ret = 0; + + if (visible) { + if (width != 64 || height != 64) + return -EINVAL; + + gem = drm_gem_object_lookup(dev, file_priv, handle); + if (unlikely(!gem)) + return -ENOENT; + nvbo = nouveau_gem_object(gem); + + ret = nouveau_bo_map(nvbo); + if (ret == 0) { + for (i = 0; i < 64 * 64; i++) { + u32 v = nouveau_bo_rd32(nvbo, i); + nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, v); + } + nouveau_bo_unmap(nvbo); } - or = i; + drm_gem_object_unreference_unlocked(gem); } - for (i = 0; type == DCB_OUTPUT_ANY && i < nv50_sor_nr(dev); i++) { - if (nv_device(drm->device)->chipset < 0x90 || - nv_device(drm->device)->chipset == 0x92 || - nv_device(drm->device)->chipset == 0xa0) - mc = nv_rd32(device, NV50_PDISPLAY_SOR_MODE_CTRL_P(i)); - else - mc = nv_rd32(device, NV90_PDISPLAY_SOR_MODE_CTRL_P(i)); + if (visible != nv_crtc->cursor.visible) { + nv50_crtc_cursor_show_hide(nv_crtc, visible, true); + nv_crtc->cursor.visible = visible; + } - NV_DEBUG(drm, "SOR-%d mc: 0x%08x\n", i, mc); - if (!(mc & (1 << crtc))) - continue; + return ret; +} - switch ((mc & 0x00000f00) >> 8) { - case 0: type = DCB_OUTPUT_LVDS; break; - case 1: type = DCB_OUTPUT_TMDS; break; - case 2: type = DCB_OUTPUT_TMDS; break; - case 5: type = DCB_OUTPUT_TMDS; break; - case 8: type = DCB_OUTPUT_DP; break; - case 9: type = DCB_OUTPUT_DP; break; - default: - NV_ERROR(drm, "invalid mc, SOR-%d: 0x%08x\n", i, mc); - goto ack; - } +static int +nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + struct nv50_curs *curs = nv50_curs(crtc); + struct nv50_chan *chan = nv50_chan(curs); + nv_wo32(chan->user, 0x0084, (y << 16) | (x & 0xffff)); + nv_wo32(chan->user, 0x0080, 0x00000000); + return 0; +} - or = i; +static void +nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, + uint32_t start, uint32_t size) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + u32 end = max(start + size, (u32)256); + u32 i; + + for (i = start; i < end; i++) { + nv_crtc->lut.r[i] = r[i]; + nv_crtc->lut.g[i] = g[i]; + nv_crtc->lut.b[i] = b[i]; } - if (type == DCB_OUTPUT_ANY) - goto ack; + nv50_crtc_lut_load(crtc); +} - /* Enable the encoder */ - for (i = 0; i < drm->vbios.dcb.entries; i++) { - dcb = &drm->vbios.dcb.entry[i]; - if (dcb->type == type && (dcb->or & (1 << or))) - break; +static void +nv50_crtc_destroy(struct drm_crtc *crtc) +{ + struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); + struct nv50_disp *disp = nv50_disp(crtc->dev); + struct nv50_head *head = nv50_head(crtc); + nv50_dmac_destroy(disp->core, &head->ovly.base); + nv50_pioc_destroy(disp->core, &head->oimm.base); + nv50_dmac_destroy(disp->core, &head->sync.base); + nv50_pioc_destroy(disp->core, &head->curs.base); + nouveau_bo_unmap(nv_crtc->cursor.nvbo); + if (nv_crtc->cursor.nvbo) + nouveau_bo_unpin(nv_crtc->cursor.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); + nouveau_bo_unmap(nv_crtc->lut.nvbo); + if (nv_crtc->lut.nvbo) + nouveau_bo_unpin(nv_crtc->lut.nvbo); + nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); + drm_crtc_cleanup(crtc); + kfree(crtc); +} + +static const struct drm_crtc_helper_funcs nv50_crtc_hfunc = { + .dpms = nv50_crtc_dpms, + .prepare = nv50_crtc_prepare, + .commit = nv50_crtc_commit, + .mode_fixup = nv50_crtc_mode_fixup, + .mode_set = nv50_crtc_mode_set, + .mode_set_base = nv50_crtc_mode_set_base, + .mode_set_base_atomic = nv50_crtc_mode_set_base_atomic, + .load_lut = nv50_crtc_lut_load, +}; + +static const struct drm_crtc_funcs nv50_crtc_func = { + .cursor_set = nv50_crtc_cursor_set, + .cursor_move = nv50_crtc_cursor_move, + .gamma_set = nv50_crtc_gamma_set, + .set_config = drm_crtc_helper_set_config, + .destroy = nv50_crtc_destroy, + .page_flip = nouveau_crtc_page_flip, +}; + +static void +nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) +{ +} + +static void +nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) +{ +} + +static int +nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index) +{ + struct nv50_disp *disp = nv50_disp(dev); + struct nv50_head *head; + struct drm_crtc *crtc; + int ret, i; + + head = kzalloc(sizeof(*head), GFP_KERNEL); + if (!head) + return -ENOMEM; + + head->base.index = index; + head->base.set_dither = nv50_crtc_set_dither; + head->base.set_scale = nv50_crtc_set_scale; + head->base.set_color_vibrance = nv50_crtc_set_color_vibrance; + head->base.color_vibrance = 50; + head->base.vibrant_hue = 0; + head->base.cursor.set_offset = nv50_cursor_set_offset; + head->base.cursor.set_pos = nv50_cursor_set_pos; + for (i = 0; i < 256; i++) { + head->base.lut.r[i] = i << 8; + head->base.lut.g[i] = i << 8; + head->base.lut.b[i] = i << 8; } - if (i == drm->vbios.dcb.entries) { - NV_ERROR(drm, "no dcb for %d %d 0x%08x\n", or, type, mc); - goto ack; + crtc = &head->base.base; + drm_crtc_init(dev, crtc, &nv50_crtc_func); + drm_crtc_helper_add(crtc, &nv50_crtc_hfunc); + drm_mode_crtc_set_gamma_size(crtc, 256); + + ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, &head->base.lut.nvbo); + if (!ret) { + ret = nouveau_bo_pin(head->base.lut.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) { + ret = nouveau_bo_map(head->base.lut.nvbo); + if (ret) + nouveau_bo_unpin(head->base.lut.nvbo); + } + if (ret) + nouveau_bo_ref(NULL, &head->base.lut.nvbo); } - script = nv50_display_script_select(dev, dcb, mc, pclk); - nouveau_bios_run_display_table(dev, script, pclk, dcb, -1); + if (ret) + goto out; - if (type == DCB_OUTPUT_DP) { - int link = !(dcb->dpconf.sor.link & 1); - if ((mc & 0x000f0000) == 0x00020000) - nv50_sor_dp_calc_tu(dev, or, link, pclk, 18); - else - nv50_sor_dp_calc_tu(dev, or, link, pclk, 24); + nv50_crtc_lut_load(crtc); + + /* allocate cursor resources */ + ret = nv50_pioc_create(disp->core, NV50_DISP_CURS_CLASS, index, + &(struct nv50_display_curs_class) { + .head = index, + }, sizeof(struct nv50_display_curs_class), + &head->curs.base); + if (ret) + goto out; + + ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, &head->base.cursor.nvbo); + if (!ret) { + ret = nouveau_bo_pin(head->base.cursor.nvbo, TTM_PL_FLAG_VRAM); + if (!ret) { + ret = nouveau_bo_map(head->base.cursor.nvbo); + if (ret) + nouveau_bo_unpin(head->base.lut.nvbo); + } + if (ret) + nouveau_bo_ref(NULL, &head->base.cursor.nvbo); } - if (dcb->type != DCB_OUTPUT_ANALOG) { - tmp = nv_rd32(device, NV50_PDISPLAY_SOR_CLK_CTRL2(or)); - tmp &= ~0x00000f0f; - if (script & 0x0100) - tmp |= 0x00000101; - nv_wr32(device, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp); - } else { - nv_wr32(device, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0); + if (ret) + goto out; + + /* allocate page flip / sync resources */ + ret = nv50_dmac_create(disp->core, NV50_DISP_SYNC_CLASS, index, + &(struct nv50_display_sync_class) { + .pushbuf = EVO_PUSH_HANDLE(SYNC, index), + .head = index, + }, sizeof(struct nv50_display_sync_class), + disp->sync->bo.offset, &head->sync.base); + if (ret) + goto out; + + head->sync.sem.offset = EVO_SYNC(1 + index, 0x00); + + /* allocate overlay resources */ + ret = nv50_pioc_create(disp->core, NV50_DISP_OIMM_CLASS, index, + &(struct nv50_display_oimm_class) { + .head = index, + }, sizeof(struct nv50_display_oimm_class), + &head->oimm.base); + if (ret) + goto out; + + ret = nv50_dmac_create(disp->core, NV50_DISP_OVLY_CLASS, index, + &(struct nv50_display_ovly_class) { + .pushbuf = EVO_PUSH_HANDLE(OVLY, index), + .head = index, + }, sizeof(struct nv50_display_ovly_class), + disp->sync->bo.offset, &head->ovly.base); + if (ret) + goto out; + +out: + if (ret) + nv50_crtc_destroy(crtc); + return ret; +} + +/****************************************************************************** + * DAC + *****************************************************************************/ +static void +nv50_dac_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + int or = nv_encoder->or; + u32 dpms_ctrl; + + dpms_ctrl = 0x00000000; + if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF) + dpms_ctrl |= 0x00000001; + if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF) + dpms_ctrl |= 0x00000004; + + nv_call(disp->core, NV50_DISP_DAC_PWR + or, dpms_ctrl); +} + +static bool +nv50_dac_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->native_mode) { + if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { + int id = adjusted_mode->base.id; + *adjusted_mode = *nv_connector->native_mode; + adjusted_mode->base.id = id; + } } - disp->irq.dcb = dcb; - disp->irq.pclk = pclk; - disp->irq.script = script; + return true; +} + +static void +nv50_dac_commit(struct drm_encoder *encoder) +{ +} + +static void +nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nv50_mast *mast = nv50_mast(encoder->dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + u32 *push; + + nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(mast, 8); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + u32 syncs = 0x00000000; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000001; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000002; + + evo_mthd(push, 0x0400 + (nv_encoder->or * 0x080), 2); + evo_data(push, 1 << nv_crtc->index); + evo_data(push, syncs); + } else { + u32 magic = 0x31ec6000 | (nv_crtc->index << 25); + u32 syncs = 0x00000001; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000008; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000010; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + magic |= 0x00000001; + + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); + evo_data(push, syncs); + evo_data(push, magic); + evo_mthd(push, 0x0180 + (nv_encoder->or * 0x020), 1); + evo_data(push, 1 << nv_crtc->index); + } + + evo_kick(push, mast); + } -ack: - nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20); - nv_wr32(device, 0x610030, 0x80000000); + nv_encoder->crtc = encoder->crtc; } -/* If programming a TMDS output on a SOR that can also be configured for - * DisplayPort, make sure NV50_SOR_DP_CTRL_ENABLE is forced off. - * - * It looks like the VBIOS TMDS scripts make an attempt at this, however, - * the VBIOS scripts on at least one board I have only switch it off on - * link 0, causing a blank display if the output has previously been - * programmed for DisplayPort. - */ static void -nv50_display_unk40_dp_set_tmds(struct drm_device *dev, struct dcb_output *dcb) +nv50_dac_disconnect(struct drm_encoder *encoder) { - struct nouveau_device *device = nouveau_dev(dev); - int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_mast *mast = nv50_mast(encoder->dev); + const int or = nv_encoder->or; + u32 *push; + + if (nv_encoder->crtc) { + nv50_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0400 + (or * 0x080), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0180 + (or * 0x020), 1); + evo_data(push, 0x00000000); + } + + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, mast); + } + } + + nv_encoder->crtc = NULL; +} + +static enum drm_connector_status +nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) +{ + struct nv50_disp *disp = nv50_disp(encoder->dev); + int ret, or = nouveau_encoder(encoder)->or; + u32 load = 0; + + ret = nv_exec(disp->core, NV50_DISP_DAC_LOAD + or, &load, sizeof(load)); + if (ret || load != 7) + return connector_status_disconnected; + + return connector_status_connected; +} + +static void +nv50_dac_destroy(struct drm_encoder *encoder) +{ + drm_encoder_cleanup(encoder); + kfree(encoder); +} + +static const struct drm_encoder_helper_funcs nv50_dac_hfunc = { + .dpms = nv50_dac_dpms, + .mode_fixup = nv50_dac_mode_fixup, + .prepare = nv50_dac_disconnect, + .commit = nv50_dac_commit, + .mode_set = nv50_dac_mode_set, + .disable = nv50_dac_disconnect, + .get_crtc = nv50_display_crtc_get, + .detect = nv50_dac_detect +}; + +static const struct drm_encoder_funcs nv50_dac_func = { + .destroy = nv50_dac_destroy, +}; + +static int +nv50_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) +{ + struct drm_device *dev = connector->dev; + struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; - u32 tmp; - if (dcb->type != DCB_OUTPUT_TMDS) + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &nv50_dac_func, DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(encoder, &nv50_dac_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} + +/****************************************************************************** + * Audio + *****************************************************************************/ +static void +nv50_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + struct nv50_disp *disp = nv50_disp(encoder->dev); + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_monitor_audio(nv_connector->edid)) + return; + + drm_edid_to_eld(&nv_connector->base, nv_connector->edid); + + nv_exec(disp->core, NVA3_DISP_SOR_HDA_ELD + nv_encoder->or, + nv_connector->base.eld, + nv_connector->base.eld[2] * 4); +} + +static void +nv50_audio_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_disp *disp = nv50_disp(encoder->dev); + + nv_exec(disp->core, NVA3_DISP_SOR_HDA_ELD + nv_encoder->or, NULL, 0); +} + +/****************************************************************************** + * HDMI + *****************************************************************************/ +static void +nv50_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + struct nv50_disp *disp = nv50_disp(encoder->dev); + const u32 moff = (nv_crtc->index << 3) | nv_encoder->or; + u32 rekey = 56; /* binary driver, and tegra constant */ + u32 max_ac_packet; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (!drm_detect_hdmi_monitor(nv_connector->edid)) return; - list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + max_ac_packet = mode->htotal - mode->hdisplay; + max_ac_packet -= rekey; + max_ac_packet -= 18; /* constant from tegra */ + max_ac_packet /= 32; + + nv_call(disp->core, NV84_DISP_SOR_HDMI_PWR + moff, + NV84_DISP_SOR_HDMI_PWR_STATE_ON | + (max_ac_packet << 16) | rekey); - if (nv_encoder->dcb->type == DCB_OUTPUT_DP && - nv_encoder->dcb->or & (1 << or)) { - tmp = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)); - tmp &= ~NV50_SOR_DP_CTRL_ENABLED; - nv_wr32(device, NV50_SOR_DP_CTRL(or, link), tmp); + nv50_audio_mode_set(encoder, mode); +} + +static void +nv50_hdmi_disconnect(struct drm_encoder *encoder) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); + struct nv50_disp *disp = nv50_disp(encoder->dev); + const u32 moff = (nv_crtc->index << 3) | nv_encoder->or; + + nv50_audio_disconnect(encoder); + + nv_call(disp->core, NV84_DISP_SOR_HDMI_PWR + moff, 0x00000000); +} + +/****************************************************************************** + * SOR + *****************************************************************************/ +static void +nv50_sor_dpms(struct drm_encoder *encoder, int mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct drm_device *dev = encoder->dev; + struct nv50_disp *disp = nv50_disp(dev); + struct drm_encoder *partner; + int or = nv_encoder->or; + + nv_encoder->last_dpms = mode; + + list_for_each_entry(partner, &dev->mode_config.encoder_list, head) { + struct nouveau_encoder *nv_partner = nouveau_encoder(partner); + + if (partner->encoder_type != DRM_MODE_ENCODER_TMDS) + continue; + + if (nv_partner != nv_encoder && + nv_partner->dcb->or == nv_encoder->dcb->or) { + if (nv_partner->last_dpms == DRM_MODE_DPMS_ON) + return; break; } } + + nv_call(disp->core, NV50_DISP_SOR_PWR + or, (mode == DRM_MODE_DPMS_ON)); + + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) + nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, disp->core); +} + +static bool +nv50_sor_mode_fixup(struct drm_encoder *encoder, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_connector *nv_connector; + + nv_connector = nouveau_encoder_connector_get(nv_encoder); + if (nv_connector && nv_connector->native_mode) { + if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { + int id = adjusted_mode->base.id; + *adjusted_mode = *nv_connector->native_mode; + adjusted_mode->base.id = id; + } + } + + return true; } static void -nv50_display_unk40_handler(struct drm_device *dev) +nv50_sor_disconnect(struct drm_encoder *encoder) { - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv50_display *disp = nv50_display(dev); - struct dcb_output *dcb = disp->irq.dcb; - u16 script = disp->irq.script; - u32 unk30 = nv_rd32(device, 0x610030), pclk = disp->irq.pclk; + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nv50_mast *mast = nv50_mast(encoder->dev); + const int or = nv_encoder->or; + u32 *push; + + if (nv_encoder->crtc) { + nv50_crtc_prepare(nv_encoder->crtc); + + push = evo_wait(mast, 4); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_MAST_CLASS) { + evo_mthd(push, 0x0600 + (or * 0x40), 1); + evo_data(push, 0x00000000); + } else { + evo_mthd(push, 0x0200 + (or * 0x20), 1); + evo_data(push, 0x00000000); + } + + evo_mthd(push, 0x0080, 1); + evo_data(push, 0x00000000); + evo_kick(push, mast); + } - NV_DEBUG(drm, "0x610030: 0x%08x\n", unk30); - disp->irq.dcb = NULL; - if (!dcb) - goto ack; + nv50_hdmi_disconnect(encoder); + } - nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1); - nv50_display_unk40_dp_set_tmds(dev, dcb); + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; + nv_encoder->crtc = NULL; +} -ack: - nv_wr32(device, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40); - nv_wr32(device, 0x610030, 0x80000000); - nv_wr32(device, 0x619494, nv_rd32(device, 0x619494) | 8); +static void +nv50_sor_prepare(struct drm_encoder *encoder) +{ + nv50_sor_disconnect(encoder); + if (nouveau_encoder(encoder)->dcb->type == DCB_OUTPUT_DP) + evo_sync(encoder->dev); } static void -nv50_display_bh(unsigned long data) +nv50_sor_commit(struct drm_encoder *encoder) { - struct drm_device *dev = (struct drm_device *)data; - struct nouveau_device *device = nouveau_dev(dev); +} + +static void +nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, + struct drm_display_mode *mode) +{ + struct nv50_disp *disp = nv50_disp(encoder->dev); + struct nv50_mast *mast = nv50_mast(encoder->dev); + struct drm_device *dev = encoder->dev; struct nouveau_drm *drm = nouveau_drm(dev); + struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); + struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); + struct nouveau_connector *nv_connector; + struct nvbios *bios = &drm->vbios; + u32 *push, lvds = 0; + u8 owner = 1 << nv_crtc->index; + u8 proto = 0xf; + u8 depth = 0x0; - for (;;) { - uint32_t intr0 = nv_rd32(device, NV50_PDISPLAY_INTR_0); - uint32_t intr1 = nv_rd32(device, NV50_PDISPLAY_INTR_1); + nv_connector = nouveau_encoder_connector_get(nv_encoder); + switch (nv_encoder->dcb->type) { + case DCB_OUTPUT_TMDS: + if (nv_encoder->dcb->sorconf.link & 1) { + if (mode->clock < 165000) + proto = 0x1; + else + proto = 0x5; + } else { + proto = 0x2; + } - NV_DEBUG(drm, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1); + nv50_hdmi_mode_set(encoder, mode); + break; + case DCB_OUTPUT_LVDS: + proto = 0x0; - if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10) - nv50_display_unk10_handler(dev); - else - if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20) - nv50_display_unk20_handler(dev); - else - if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40) - nv50_display_unk40_handler(dev); + if (bios->fp_no_ddc) { + if (bios->fp.dual_link) + lvds |= 0x0100; + if (bios->fp.if_is_24bit) + lvds |= 0x0200; + } else { + if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { + if (((u8 *)nv_connector->edid)[121] == 2) + lvds |= 0x0100; + } else + if (mode->clock >= bios->fp.duallink_transition_clk) { + lvds |= 0x0100; + } + + if (lvds & 0x0100) { + if (bios->fp.strapless_is_24bit & 2) + lvds |= 0x0200; + } else { + if (bios->fp.strapless_is_24bit & 1) + lvds |= 0x0200; + } + + if (nv_connector->base.display_info.bpc == 8) + lvds |= 0x0200; + } + + nv_call(disp->core, NV50_DISP_SOR_LVDS_SCRIPT + nv_encoder->or, lvds); + break; + case DCB_OUTPUT_DP: + if (nv_connector->base.display_info.bpc == 6) { + nv_encoder->dp.datarate = mode->clock * 18 / 8; + depth = 0x2; + } else + if (nv_connector->base.display_info.bpc == 8) { + nv_encoder->dp.datarate = mode->clock * 24 / 8; + depth = 0x5; + } else { + nv_encoder->dp.datarate = mode->clock * 30 / 8; + depth = 0x6; + } + + if (nv_encoder->dcb->sorconf.link & 1) + proto = 0x8; else - break; + proto = 0x9; + break; + default: + BUG_ON(1); + break; } - nv_wr32(device, NV03_PMC_INTR_EN_0, 1); + nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); + + push = evo_wait(nv50_mast(dev), 8); + if (push) { + if (nv50_vers(mast) < NVD0_DISP_CLASS) { + evo_mthd(push, 0x0600 + (nv_encoder->or * 0x040), 1); + evo_data(push, (depth << 16) | (proto << 8) | owner); + } else { + u32 magic = 0x31ec6000 | (nv_crtc->index << 25); + u32 syncs = 0x00000001; + + if (mode->flags & DRM_MODE_FLAG_NHSYNC) + syncs |= 0x00000008; + if (mode->flags & DRM_MODE_FLAG_NVSYNC) + syncs |= 0x00000010; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + magic |= 0x00000001; + + evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); + evo_data(push, syncs | (depth << 6)); + evo_data(push, magic); + evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 1); + evo_data(push, owner | (proto << 8)); + } + + evo_kick(push, mast); + } + + nv_encoder->crtc = encoder->crtc; } static void -nv50_display_error_handler(struct drm_device *dev) +nv50_sor_destroy(struct drm_encoder *encoder) { - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - u32 channels = (nv_rd32(device, NV50_PDISPLAY_INTR_0) & 0x001f0000) >> 16; - u32 addr, data; - int chid; + drm_encoder_cleanup(encoder); + kfree(encoder); +} - for (chid = 0; chid < 5; chid++) { - if (!(channels & (1 << chid))) - continue; +static const struct drm_encoder_helper_funcs nv50_sor_hfunc = { + .dpms = nv50_sor_dpms, + .mode_fixup = nv50_sor_mode_fixup, + .prepare = nv50_sor_prepare, + .commit = nv50_sor_commit, + .mode_set = nv50_sor_mode_set, + .disable = nv50_sor_disconnect, + .get_crtc = nv50_display_crtc_get, +}; + +static const struct drm_encoder_funcs nv50_sor_func = { + .destroy = nv50_sor_destroy, +}; + +static int +nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) +{ + struct drm_device *dev = connector->dev; + struct nouveau_encoder *nv_encoder; + struct drm_encoder *encoder; + + nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); + if (!nv_encoder) + return -ENOMEM; + nv_encoder->dcb = dcbe; + nv_encoder->or = ffs(dcbe->or) - 1; + nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; - nv_wr32(device, NV50_PDISPLAY_INTR_0, 0x00010000 << chid); - addr = nv_rd32(device, NV50_PDISPLAY_TRAPPED_ADDR(chid)); - data = nv_rd32(device, NV50_PDISPLAY_TRAPPED_DATA(chid)); - NV_ERROR(drm, "EvoCh %d Mthd 0x%04x Data 0x%08x " - "(0x%04x 0x%02x)\n", chid, - addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf); + encoder = to_drm_encoder(nv_encoder); + encoder->possible_crtcs = dcbe->heads; + encoder->possible_clones = 0; + drm_encoder_init(dev, encoder, &nv50_sor_func, DRM_MODE_ENCODER_TMDS); + drm_encoder_helper_add(encoder, &nv50_sor_hfunc); + + drm_mode_connector_attach_encoder(connector, encoder); + return 0; +} + +/****************************************************************************** + * Init + *****************************************************************************/ +void +nv50_display_fini(struct drm_device *dev) +{ +} - nv_wr32(device, NV50_PDISPLAY_TRAPPED_ADDR(chid), 0x90000000); +int +nv50_display_init(struct drm_device *dev) +{ + u32 *push = evo_wait(nv50_mast(dev), 32); + if (push) { + evo_mthd(push, 0x0088, 1); + evo_data(push, NvEvoSync); + evo_kick(push, nv50_mast(dev)); + return evo_sync(dev); } + + return -EBUSY; } void -nv50_display_intr(struct drm_device *dev) +nv50_display_destroy(struct drm_device *dev) +{ + struct nv50_disp *disp = nv50_disp(dev); + + nv50_dmac_destroy(disp->core, &disp->mast.base); + + nouveau_bo_unmap(disp->sync); + if (disp->sync) + nouveau_bo_unpin(disp->sync); + nouveau_bo_ref(NULL, &disp->sync); + + nouveau_display(dev)->priv = NULL; + kfree(disp); +} + +int +nv50_display_create(struct drm_device *dev) { + static const u16 oclass[] = { + NVE0_DISP_CLASS, + NVD0_DISP_CLASS, + NVA3_DISP_CLASS, + NV94_DISP_CLASS, + NVA0_DISP_CLASS, + NV84_DISP_CLASS, + NV50_DISP_CLASS, + }; struct nouveau_device *device = nouveau_dev(dev); struct nouveau_drm *drm = nouveau_drm(dev); - struct nv50_display *disp = nv50_display(dev); - uint32_t delayed = 0; - - while (nv_rd32(device, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) { - uint32_t intr0 = nv_rd32(device, NV50_PDISPLAY_INTR_0); - uint32_t intr1 = nv_rd32(device, NV50_PDISPLAY_INTR_1); - uint32_t clock; + struct dcb_table *dcb = &drm->vbios.dcb; + struct drm_connector *connector, *tmp; + struct nv50_disp *disp; + struct dcb_output *dcbe; + int crtcs, ret, i; - NV_DEBUG(drm, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1); + disp = kzalloc(sizeof(*disp), GFP_KERNEL); + if (!disp) + return -ENOMEM; - if (!intr0 && !(intr1 & ~delayed)) - break; + nouveau_display(dev)->priv = disp; + nouveau_display(dev)->dtor = nv50_display_destroy; + nouveau_display(dev)->init = nv50_display_init; + nouveau_display(dev)->fini = nv50_display_fini; - if (intr0 & 0x001f0000) { - nv50_display_error_handler(dev); - intr0 &= ~0x001f0000; + /* small shared memory area we use for notifiers and semaphores */ + ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, + 0, 0x0000, NULL, &disp->sync); + if (!ret) { + ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); + if (!ret) { + ret = nouveau_bo_map(disp->sync); + if (ret) + nouveau_bo_unpin(disp->sync); } + if (ret) + nouveau_bo_ref(NULL, &disp->sync); + } - if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) { - intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC; - delayed |= NV50_PDISPLAY_INTR_1_VBLANK_CRTC; - } + if (ret) + goto out; + + /* attempt to allocate a supported evo display class */ + ret = -ENODEV; + for (i = 0; ret && i < ARRAY_SIZE(oclass); i++) { + ret = nouveau_object_new(nv_object(drm), NVDRM_DEVICE, + 0xd1500000, oclass[i], NULL, 0, + &disp->core); + } - clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 | - NV50_PDISPLAY_INTR_1_CLK_UNK20 | - NV50_PDISPLAY_INTR_1_CLK_UNK40)); - if (clock) { - nv_wr32(device, NV03_PMC_INTR_EN_0, 0); - tasklet_schedule(&disp->tasklet); - delayed |= clock; - intr1 &= ~clock; - } + if (ret) + goto out; + + /* allocate master evo channel */ + ret = nv50_dmac_create(disp->core, NV50_DISP_MAST_CLASS, 0, + &(struct nv50_display_mast_class) { + .pushbuf = EVO_PUSH_HANDLE(MAST, 0), + }, sizeof(struct nv50_display_mast_class), + disp->sync->bo.offset, &disp->mast.base); + if (ret) + goto out; + + /* create crtc objects to represent the hw heads */ + if (nv_mclass(disp->core) >= NVD0_DISP_CLASS) + crtcs = nv_rd32(device, 0x022448); + else + crtcs = 2; + + for (i = 0; i < crtcs; i++) { + ret = nv50_crtc_create(dev, disp->core, i); + if (ret) + goto out; + } + + /* create encoder/connector objects based on VBIOS DCB table */ + for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { + connector = nouveau_connector_create(dev, dcbe->connector); + if (IS_ERR(connector)) + continue; - if (intr0) { - NV_ERROR(drm, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0); - nv_wr32(device, NV50_PDISPLAY_INTR_0, intr0); + if (dcbe->location != DCB_LOC_ON_CHIP) { + NV_WARN(drm, "skipping off-chip encoder %d/%d\n", + dcbe->type, ffs(dcbe->or) - 1); + continue; } - if (intr1) { - NV_ERROR(drm, - "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1); - nv_wr32(device, NV50_PDISPLAY_INTR_1, intr1); + switch (dcbe->type) { + case DCB_OUTPUT_TMDS: + case DCB_OUTPUT_LVDS: + case DCB_OUTPUT_DP: + nv50_sor_create(connector, dcbe); + break; + case DCB_OUTPUT_ANALOG: + nv50_dac_create(connector, dcbe); + break; + default: + NV_WARN(drm, "skipping unsupported encoder %d/%d\n", + dcbe->type, ffs(dcbe->or) - 1); + continue; } } + + /* cull any connectors we created that don't have an encoder */ + list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { + if (connector->encoder_ids[0]) + continue; + + NV_WARN(drm, "%s has no encoders, removing\n", + drm_get_connector_name(connector)); + connector->funcs->destroy(connector); + } + +out: + if (ret) + nv50_display_destroy(dev); + return ret; } diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h index 973554d8a7a..70da347aa8c 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.h +++ b/drivers/gpu/drm/nouveau/nv50_display.h @@ -30,77 +30,16 @@ #include "nouveau_display.h" #include "nouveau_crtc.h" #include "nouveau_reg.h" -#include "nv50_evo.h" -struct nv50_display_crtc { - struct nouveau_channel *sync; - struct { - struct nouveau_bo *bo; - u32 offset; - u16 value; - } sem; -}; +int nv50_display_create(struct drm_device *); +void nv50_display_destroy(struct drm_device *); +int nv50_display_init(struct drm_device *); +void nv50_display_fini(struct drm_device *); -struct nv50_display { - struct nouveau_channel *master; - - struct nouveau_gpuobj *ramin; - u32 dmao; - u32 hash; - - struct nv50_display_crtc crtc[2]; - - struct tasklet_struct tasklet; - struct { - struct dcb_output *dcb; - u16 script; - u32 pclk; - } irq; -}; - -static inline struct nv50_display * -nv50_display(struct drm_device *dev) -{ - return nouveau_display(dev)->priv; -} - -int nv50_display_early_init(struct drm_device *dev); -void nv50_display_late_takedown(struct drm_device *dev); -int nv50_display_create(struct drm_device *dev); -int nv50_display_init(struct drm_device *dev); -void nv50_display_fini(struct drm_device *dev); -void nv50_display_destroy(struct drm_device *dev); -void nv50_display_intr(struct drm_device *); -int nv50_crtc_blank(struct nouveau_crtc *, bool blank); -int nv50_crtc_set_clock(struct drm_device *, int head, int pclk); - -u32 nv50_display_active_crtcs(struct drm_device *); - -int nv50_display_sync(struct drm_device *); -int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, - struct nouveau_channel *chan); void nv50_display_flip_stop(struct drm_crtc *); - -int nv50_evo_create(struct drm_device *dev); -void nv50_evo_destroy(struct drm_device *dev); -int nv50_evo_init(struct drm_device *dev); -void nv50_evo_fini(struct drm_device *dev); -void nv50_evo_dmaobj_init(struct nouveau_gpuobj *, u32 memtype, u64 base, - u64 size); -int nv50_evo_dmaobj_new(struct nouveau_channel *, u32 handle, u32 memtype, - u64 base, u64 size, struct nouveau_gpuobj **); - -int nvd0_display_create(struct drm_device *); -void nvd0_display_destroy(struct drm_device *); -int nvd0_display_init(struct drm_device *); -void nvd0_display_fini(struct drm_device *); -void nvd0_display_intr(struct drm_device *); - -void nvd0_display_flip_stop(struct drm_crtc *); -int nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, +int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *, struct nouveau_channel *, u32 swap_interval); struct nouveau_bo *nv50_display_crtc_sema(struct drm_device *, int head); -struct nouveau_bo *nvd0_display_crtc_sema(struct drm_device *, int head); #endif /* __NV50_DISPLAY_H__ */ diff --git a/drivers/gpu/drm/nouveau/nv50_evo.c b/drivers/gpu/drm/nouveau/nv50_evo.c deleted file mode 100644 index 9f6f55cdfa7..00000000000 --- a/drivers/gpu/drm/nouveau/nv50_evo.c +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2010 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 <drm/drmP.h> - -#include "nouveau_drm.h" -#include "nouveau_dma.h" -#include "nv50_display.h" - -#include <core/gpuobj.h> - -#include <subdev/timer.h> -#include <subdev/fb.h> - -static u32 -nv50_evo_rd32(struct nouveau_object *object, u32 addr) -{ - void __iomem *iomem = object->oclass->ofuncs->rd08; - return ioread32_native(iomem + addr); -} - -static void -nv50_evo_wr32(struct nouveau_object *object, u32 addr, u32 data) -{ - void __iomem *iomem = object->oclass->ofuncs->rd08; - iowrite32_native(data, iomem + addr); -} - -static void -nv50_evo_channel_del(struct nouveau_channel **pevo) -{ - struct nouveau_channel *evo = *pevo; - - if (!evo) - return; - *pevo = NULL; - - nouveau_bo_unmap(evo->push.buffer); - nouveau_bo_ref(NULL, &evo->push.buffer); - - if (evo->object) - iounmap(evo->object->oclass->ofuncs); - - kfree(evo); -} - -int -nv50_evo_dmaobj_new(struct nouveau_channel *evo, u32 handle, u32 memtype, - u64 base, u64 size, struct nouveau_gpuobj **pobj) -{ - struct drm_device *dev = evo->fence; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv50_display *disp = nv50_display(dev); - u32 dmao = disp->dmao; - u32 hash = disp->hash; - u32 flags5; - - if (nv_device(drm->device)->chipset < 0xc0) { - /* not supported on 0x50, specified in format mthd */ - if (nv_device(drm->device)->chipset == 0x50) - memtype = 0; - flags5 = 0x00010000; - } else { - if (memtype & 0x80000000) - flags5 = 0x00000000; /* large pages */ - else - flags5 = 0x00020000; - } - - nv_wo32(disp->ramin, dmao + 0x00, 0x0019003d | (memtype << 22)); - nv_wo32(disp->ramin, dmao + 0x04, lower_32_bits(base + size - 1)); - nv_wo32(disp->ramin, dmao + 0x08, lower_32_bits(base)); - nv_wo32(disp->ramin, dmao + 0x0c, upper_32_bits(base + size - 1) << 24 | - upper_32_bits(base)); - nv_wo32(disp->ramin, dmao + 0x10, 0x00000000); - nv_wo32(disp->ramin, dmao + 0x14, flags5); - - nv_wo32(disp->ramin, hash + 0x00, handle); - nv_wo32(disp->ramin, hash + 0x04, (evo->handle << 28) | (dmao << 10) | - evo->handle); - - disp->dmao += 0x20; - disp->hash += 0x08; - return 0; -} - -static int -nv50_evo_channel_new(struct drm_device *dev, int chid, - struct nouveau_channel **pevo) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo; - int ret; - - evo = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL); - if (!evo) - return -ENOMEM; - *pevo = evo; - - evo->drm = drm; - evo->handle = chid; - evo->fence = dev; - evo->user_get = 4; - evo->user_put = 0; - - ret = nouveau_bo_new(dev, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0, NULL, - &evo->push.buffer); - if (ret == 0) - ret = nouveau_bo_pin(evo->push.buffer, TTM_PL_FLAG_VRAM); - if (ret) { - NV_ERROR(drm, "Error creating EVO DMA push buffer: %d\n", ret); - nv50_evo_channel_del(pevo); - return ret; - } - - ret = nouveau_bo_map(evo->push.buffer); - if (ret) { - NV_ERROR(drm, "Error mapping EVO DMA push buffer: %d\n", ret); - nv50_evo_channel_del(pevo); - return ret; - } - - evo->object = kzalloc(sizeof(*evo->object), GFP_KERNEL); -#ifdef NOUVEAU_OBJECT_MAGIC - evo->object->_magic = NOUVEAU_OBJECT_MAGIC; -#endif - evo->object->parent = nv_object(disp->ramin)->parent; - evo->object->engine = nv_object(disp->ramin)->engine; - evo->object->oclass = - kzalloc(sizeof(*evo->object->oclass), GFP_KERNEL); - evo->object->oclass->ofuncs = - kzalloc(sizeof(*evo->object->oclass->ofuncs), GFP_KERNEL); - evo->object->oclass->ofuncs->rd32 = nv50_evo_rd32; - evo->object->oclass->ofuncs->wr32 = nv50_evo_wr32; - evo->object->oclass->ofuncs->rd08 = - ioremap(pci_resource_start(dev->pdev, 0) + - NV50_PDISPLAY_USER(evo->handle), PAGE_SIZE); - return 0; -} - -static int -nv50_evo_channel_init(struct nouveau_channel *evo) -{ - struct nouveau_drm *drm = evo->drm; - struct nouveau_device *device = nv_device(drm->device); - int id = evo->handle, ret, i; - u64 pushbuf = evo->push.buffer->bo.offset; - u32 tmp; - - tmp = nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id)); - if ((tmp & 0x009f0000) == 0x00020000) - nv_wr32(device, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00800000); - - tmp = nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id)); - if ((tmp & 0x003f0000) == 0x00030000) - nv_wr32(device, NV50_PDISPLAY_EVO_CTRL(id), tmp | 0x00600000); - - /* initialise fifo */ - nv_wr32(device, NV50_PDISPLAY_EVO_DMA_CB(id), pushbuf >> 8 | - NV50_PDISPLAY_EVO_DMA_CB_LOCATION_VRAM | - NV50_PDISPLAY_EVO_DMA_CB_VALID); - nv_wr32(device, NV50_PDISPLAY_EVO_UNK2(id), 0x00010000); - nv_wr32(device, NV50_PDISPLAY_EVO_HASH_TAG(id), id); - nv_mask(device, NV50_PDISPLAY_EVO_CTRL(id), NV50_PDISPLAY_EVO_CTRL_DMA, - NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED); - - nv_wr32(device, NV50_PDISPLAY_USER_PUT(id), 0x00000000); - nv_wr32(device, NV50_PDISPLAY_EVO_CTRL(id), 0x01000003 | - NV50_PDISPLAY_EVO_CTRL_DMA_ENABLED); - if (!nv_wait(device, NV50_PDISPLAY_EVO_CTRL(id), 0x80000000, 0x00000000)) { - NV_ERROR(drm, "EvoCh %d init timeout: 0x%08x\n", id, - nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id))); - return -EBUSY; - } - - /* enable error reporting on the channel */ - nv_mask(device, 0x610028, 0x00000000, 0x00010001 << id); - - evo->dma.max = (4096/4) - 2; - evo->dma.max &= ~7; - evo->dma.put = 0; - evo->dma.cur = evo->dma.put; - evo->dma.free = evo->dma.max - evo->dma.cur; - - ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS); - if (ret) - return ret; - - for (i = 0; i < NOUVEAU_DMA_SKIPS; i++) - OUT_RING(evo, 0); - - return 0; -} - -static void -nv50_evo_channel_fini(struct nouveau_channel *evo) -{ - struct nouveau_drm *drm = evo->drm; - struct nouveau_device *device = nv_device(drm->device); - int id = evo->handle; - - nv_mask(device, 0x610028, 0x00010001 << id, 0x00000000); - nv_mask(device, NV50_PDISPLAY_EVO_CTRL(id), 0x00001010, 0x00001000); - nv_wr32(device, NV50_PDISPLAY_INTR_0, (1 << id)); - nv_mask(device, NV50_PDISPLAY_EVO_CTRL(id), 0x00000003, 0x00000000); - if (!nv_wait(device, NV50_PDISPLAY_EVO_CTRL(id), 0x001e0000, 0x00000000)) { - NV_ERROR(drm, "EvoCh %d takedown timeout: 0x%08x\n", id, - nv_rd32(device, NV50_PDISPLAY_EVO_CTRL(id))); - } -} - -void -nv50_evo_destroy(struct drm_device *dev) -{ - struct nv50_display *disp = nv50_display(dev); - int i; - - for (i = 0; i < 2; i++) { - if (disp->crtc[i].sem.bo) { - nouveau_bo_unmap(disp->crtc[i].sem.bo); - nouveau_bo_ref(NULL, &disp->crtc[i].sem.bo); - } - nv50_evo_channel_del(&disp->crtc[i].sync); - } - nv50_evo_channel_del(&disp->master); - nouveau_gpuobj_ref(NULL, &disp->ramin); -} - -int -nv50_evo_create(struct drm_device *dev) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_fb *pfb = nouveau_fb(drm->device); - struct nv50_display *disp = nv50_display(dev); - struct nouveau_channel *evo; - int ret, i, j; - - /* setup object management on it, any other evo channel will - * use this also as there's no per-channel support on the - * hardware - */ - ret = nouveau_gpuobj_new(drm->device, NULL, 32768, 65536, - NVOBJ_FLAG_ZERO_ALLOC, &disp->ramin); - if (ret) { - NV_ERROR(drm, "Error allocating EVO channel memory: %d\n", ret); - goto err; - } - - disp->hash = 0x0000; - disp->dmao = 0x1000; - - /* create primary evo channel, the one we use for modesetting - * purporses - */ - ret = nv50_evo_channel_new(dev, 0, &disp->master); - if (ret) - return ret; - evo = disp->master; - - ret = nv50_evo_dmaobj_new(disp->master, NvEvoSync, 0x0000, - disp->ramin->addr + 0x2000, 0x1000, NULL); - if (ret) - goto err; - - /* create some default objects for the scanout memtypes we support */ - ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM, 0x0000, - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(disp->master, NvEvoVRAM_LP, 0x80000000, - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB32, 0x80000000 | - (nv_device(drm->device)->chipset < 0xc0 ? 0x7a : 0xfe), - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(disp->master, NvEvoFB16, 0x80000000 | - (nv_device(drm->device)->chipset < 0xc0 ? 0x70 : 0xfe), - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - /* create "display sync" channels and other structures we need - * to implement page flipping - */ - for (i = 0; i < 2; i++) { - struct nv50_display_crtc *dispc = &disp->crtc[i]; - u64 offset; - - ret = nv50_evo_channel_new(dev, 1 + i, &dispc->sync); - if (ret) - goto err; - - ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &dispc->sem.bo); - if (!ret) { - ret = nouveau_bo_pin(dispc->sem.bo, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(dispc->sem.bo); - if (ret) - nouveau_bo_ref(NULL, &dispc->sem.bo); - offset = dispc->sem.bo->bo.offset; - } - - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoSync, 0x0000, - offset, 4096, NULL); - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoVRAM_LP, 0x80000000, - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB32, 0x80000000 | - (nv_device(drm->device)->chipset < 0xc0 ? - 0x7a : 0xfe), - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - ret = nv50_evo_dmaobj_new(dispc->sync, NvEvoFB16, 0x80000000 | - (nv_device(drm->device)->chipset < 0xc0 ? - 0x70 : 0xfe), - 0, pfb->ram.size, NULL); - if (ret) - goto err; - - for (j = 0; j < 4096; j += 4) - nouveau_bo_wr32(dispc->sem.bo, j / 4, 0x74b1e000); - dispc->sem.offset = 0; - } - - return 0; - -err: - nv50_evo_destroy(dev); - return ret; -} - -int -nv50_evo_init(struct drm_device *dev) -{ - struct nv50_display *disp = nv50_display(dev); - int ret, i; - - ret = nv50_evo_channel_init(disp->master); - if (ret) - return ret; - - for (i = 0; i < 2; i++) { - ret = nv50_evo_channel_init(disp->crtc[i].sync); - if (ret) - return ret; - } - - return 0; -} - -void -nv50_evo_fini(struct drm_device *dev) -{ - struct nv50_display *disp = nv50_display(dev); - int i; - - for (i = 0; i < 2; i++) { - if (disp->crtc[i].sync) - nv50_evo_channel_fini(disp->crtc[i].sync); - } - - if (disp->master) - nv50_evo_channel_fini(disp->master); -} diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h deleted file mode 100644 index 771d879bc83..00000000000 --- a/drivers/gpu/drm/nouveau/nv50_evo.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2008 Maarten Maathuis. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __NV50_EVO_H__ -#define __NV50_EVO_H__ - -#define NV50_EVO_UPDATE 0x00000080 -#define NV50_EVO_UNK84 0x00000084 -#define NV50_EVO_UNK84_NOTIFY 0x40000000 -#define NV50_EVO_UNK84_NOTIFY_DISABLED 0x00000000 -#define NV50_EVO_UNK84_NOTIFY_ENABLED 0x40000000 -#define NV50_EVO_DMA_NOTIFY 0x00000088 -#define NV50_EVO_DMA_NOTIFY_HANDLE 0xffffffff -#define NV50_EVO_DMA_NOTIFY_HANDLE_NONE 0x00000000 -#define NV50_EVO_UNK8C 0x0000008C - -#define NV50_EVO_DAC(n, r) ((n) * 0x80 + NV50_EVO_DAC_##r) -#define NV50_EVO_DAC_MODE_CTRL 0x00000400 -#define NV50_EVO_DAC_MODE_CTRL_CRTC0 0x00000001 -#define NV50_EVO_DAC_MODE_CTRL_CRTC1 0x00000002 -#define NV50_EVO_DAC_MODE_CTRL2 0x00000404 -#define NV50_EVO_DAC_MODE_CTRL2_NHSYNC 0x00000001 -#define NV50_EVO_DAC_MODE_CTRL2_NVSYNC 0x00000002 - -#define NV50_EVO_SOR(n, r) ((n) * 0x40 + NV50_EVO_SOR_##r) -#define NV50_EVO_SOR_MODE_CTRL 0x00000600 -#define NV50_EVO_SOR_MODE_CTRL_CRTC0 0x00000001 -#define NV50_EVO_SOR_MODE_CTRL_CRTC1 0x00000002 -#define NV50_EVO_SOR_MODE_CTRL_TMDS 0x00000100 -#define NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK 0x00000400 -#define NV50_EVO_SOR_MODE_CTRL_NHSYNC 0x00001000 -#define NV50_EVO_SOR_MODE_CTRL_NVSYNC 0x00002000 - -#define NV50_EVO_CRTC(n, r) ((n) * 0x400 + NV50_EVO_CRTC_##r) -#define NV84_EVO_CRTC(n, r) ((n) * 0x400 + NV84_EVO_CRTC_##r) -#define NV50_EVO_CRTC_UNK0800 0x00000800 -#define NV50_EVO_CRTC_CLOCK 0x00000804 -#define NV50_EVO_CRTC_INTERLACE 0x00000808 -#define NV50_EVO_CRTC_DISPLAY_START 0x00000810 -#define NV50_EVO_CRTC_DISPLAY_TOTAL 0x00000814 -#define NV50_EVO_CRTC_SYNC_DURATION 0x00000818 -#define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END 0x0000081c -#define NV50_EVO_CRTC_UNK0820 0x00000820 -#define NV50_EVO_CRTC_UNK0824 0x00000824 -#define NV50_EVO_CRTC_UNK082C 0x0000082c -#define NV50_EVO_CRTC_CLUT_MODE 0x00000840 -/* You can't have a palette in 8 bit mode (=OFF) */ -#define NV50_EVO_CRTC_CLUT_MODE_BLANK 0x00000000 -#define NV50_EVO_CRTC_CLUT_MODE_OFF 0x80000000 -#define NV50_EVO_CRTC_CLUT_MODE_ON 0xC0000000 -#define NV50_EVO_CRTC_CLUT_OFFSET 0x00000844 -#define NV84_EVO_CRTC_CLUT_DMA 0x0000085C -#define NV84_EVO_CRTC_CLUT_DMA_HANDLE 0xffffffff -#define NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE 0x00000000 -#define NV50_EVO_CRTC_FB_OFFSET 0x00000860 -#define NV50_EVO_CRTC_FB_SIZE 0x00000868 -#define NV50_EVO_CRTC_FB_CONFIG 0x0000086c -#define NV50_EVO_CRTC_FB_CONFIG_MODE 0x00100000 -#define NV50_EVO_CRTC_FB_CONFIG_MODE_TILE 0x00000000 -#define NV50_EVO_CRTC_FB_CONFIG_MODE_PITCH 0x00100000 -#define NV50_EVO_CRTC_FB_DEPTH 0x00000870 -#define NV50_EVO_CRTC_FB_DEPTH_8 0x00001e00 -#define NV50_EVO_CRTC_FB_DEPTH_15 0x0000e900 -#define NV50_EVO_CRTC_FB_DEPTH_16 0x0000e800 -#define NV50_EVO_CRTC_FB_DEPTH_24 0x0000cf00 -#define NV50_EVO_CRTC_FB_DEPTH_30 0x0000d100 -#define NV50_EVO_CRTC_FB_DMA 0x00000874 -#define NV50_EVO_CRTC_FB_DMA_HANDLE 0xffffffff -#define NV50_EVO_CRTC_FB_DMA_HANDLE_NONE 0x00000000 -#define NV50_EVO_CRTC_CURSOR_CTRL 0x00000880 -#define NV50_EVO_CRTC_CURSOR_CTRL_HIDE 0x05000000 -#define NV50_EVO_CRTC_CURSOR_CTRL_SHOW 0x85000000 -#define NV50_EVO_CRTC_CURSOR_OFFSET 0x00000884 -#define NV84_EVO_CRTC_CURSOR_DMA 0x0000089c -#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE 0xffffffff -#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE 0x00000000 -#define NV50_EVO_CRTC_DITHER_CTRL 0x000008a0 -#define NV50_EVO_CRTC_DITHER_CTRL_OFF 0x00000000 -#define NV50_EVO_CRTC_DITHER_CTRL_ON 0x00000011 -#define NV50_EVO_CRTC_SCALE_CTRL 0x000008a4 -#define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000 -#define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009 -#define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8 -#define NV50_EVO_CRTC_COLOR_CTRL_VIBRANCE 0x000fff00 -#define NV50_EVO_CRTC_COLOR_CTRL_HUE 0xfff00000 -#define NV50_EVO_CRTC_FB_POS 0x000008c0 -#define NV50_EVO_CRTC_REAL_RES 0x000008c8 -#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4 -#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) \ - ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF)) -/* Both of these are needed, otherwise nothing happens. */ -#define NV50_EVO_CRTC_SCALE_RES1 0x000008d8 -#define NV50_EVO_CRTC_SCALE_RES2 0x000008dc -#define NV50_EVO_CRTC_UNK900 0x00000900 -#define NV50_EVO_CRTC_UNK904 0x00000904 - -#endif diff --git a/drivers/gpu/drm/nouveau/nv50_fence.c b/drivers/gpu/drm/nouveau/nv50_fence.c index e0763ea88ee..c20f2727ea0 100644 --- a/drivers/gpu/drm/nouveau/nv50_fence.c +++ b/drivers/gpu/drm/nouveau/nv50_fence.c @@ -110,8 +110,11 @@ nv50_fence_create(struct nouveau_drm *drm) 0, 0x0000, NULL, &priv->bo); if (!ret) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); - if (!ret) + if (!ret) { ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } if (ret) nouveau_bo_ref(NULL, &priv->bo); } diff --git a/drivers/gpu/drm/nouveau/nv50_pm.c b/drivers/gpu/drm/nouveau/nv50_pm.c index c4a65039b1c..8bd5d2781ba 100644 --- a/drivers/gpu/drm/nouveau/nv50_pm.c +++ b/drivers/gpu/drm/nouveau/nv50_pm.c @@ -546,7 +546,7 @@ calc_mclk(struct drm_device *dev, struct nouveau_pm_level *perflvl, { struct nouveau_drm *drm = nouveau_drm(dev); struct nouveau_device *device = nouveau_dev(dev); - u32 crtc_mask = nv50_display_active_crtcs(dev); + u32 crtc_mask = 0; /*XXX: nv50_display_active_crtcs(dev); */ struct nouveau_mem_exec_func exec = { .dev = dev, .precharge = mclk_precharge, diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c deleted file mode 100644 index b562b59e132..00000000000 --- a/drivers/gpu/drm/nouveau/nv50_sor.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2008 Maarten Maathuis. - * All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice (including the - * next paragraph) shall be included in all copies or substantial - * portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> - -#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO) -#include "nouveau_reg.h" -#include "nouveau_drm.h" -#include "nouveau_dma.h" -#include "nouveau_encoder.h" -#include "nouveau_connector.h" -#include "nouveau_crtc.h" -#include "nv50_display.h" - -#include <subdev/timer.h> - -static u32 -nv50_sor_dp_lane_map(struct drm_device *dev, struct dcb_output *dcb, u8 lane) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - static const u8 nvaf[] = { 24, 16, 8, 0 }; /* thanks, apple.. */ - static const u8 nv50[] = { 16, 8, 0, 24 }; - if (nv_device(drm->device)->chipset == 0xaf) - return nvaf[lane]; - return nv50[lane]; -} - -static void -nv50_sor_dp_train_set(struct drm_device *dev, struct dcb_output *dcb, u8 pattern) -{ - struct nouveau_device *device = nouveau_dev(dev); - u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - nv_mask(device, NV50_SOR_DP_CTRL(or, link), 0x0f000000, pattern << 24); -} - -static void -nv50_sor_dp_train_adj(struct drm_device *dev, struct dcb_output *dcb, - u8 lane, u8 swing, u8 preem) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - u32 shift = nv50_sor_dp_lane_map(dev, dcb, lane); - u32 mask = 0x000000ff << shift; - u8 *table, *entry, *config; - - table = nouveau_dp_bios_data(dev, dcb, &entry); - if (!table || (table[0] != 0x20 && table[0] != 0x21)) { - NV_ERROR(drm, "PDISP: unsupported DP table for chipset\n"); - return; - } - - config = entry + table[4]; - while (config[0] != swing || config[1] != preem) { - config += table[5]; - if (config >= entry + table[4] + entry[4] * table[5]) - return; - } - - nv_mask(device, NV50_SOR_DP_UNK118(or, link), mask, config[2] << shift); - nv_mask(device, NV50_SOR_DP_UNK120(or, link), mask, config[3] << shift); - nv_mask(device, NV50_SOR_DP_UNK130(or, link), 0x0000ff00, config[4] << 8); -} - -static void -nv50_sor_dp_link_set(struct drm_device *dev, struct dcb_output *dcb, int crtc, - int link_nr, u32 link_bw, bool enhframe) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - u32 dpctrl = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)) & ~0x001f4000; - u32 clksor = nv_rd32(device, 0x614300 + (or * 0x800)) & ~0x000c0000; - u8 *table, *entry, mask; - int i; - - table = nouveau_dp_bios_data(dev, dcb, &entry); - if (!table || (table[0] != 0x20 && table[0] != 0x21)) { - NV_ERROR(drm, "PDISP: unsupported DP table for chipset\n"); - return; - } - - entry = ROMPTR(dev, entry[10]); - if (entry) { - while (link_bw < ROM16(entry[0]) * 10) - entry += 4; - - nouveau_bios_run_init_table(dev, ROM16(entry[2]), dcb, crtc); - } - - dpctrl |= ((1 << link_nr) - 1) << 16; - if (enhframe) - dpctrl |= 0x00004000; - - if (link_bw > 162000) - clksor |= 0x00040000; - - nv_wr32(device, 0x614300 + (or * 0x800), clksor); - nv_wr32(device, NV50_SOR_DP_CTRL(or, link), dpctrl); - - mask = 0; - for (i = 0; i < link_nr; i++) - mask |= 1 << (nv50_sor_dp_lane_map(dev, dcb, i) >> 3); - nv_mask(device, NV50_SOR_DP_UNK130(or, link), 0x0000000f, mask); -} - -static void -nv50_sor_dp_link_get(struct drm_device *dev, u32 or, u32 link, u32 *nr, u32 *bw) -{ - struct nouveau_device *device = nouveau_dev(dev); - u32 dpctrl = nv_rd32(device, NV50_SOR_DP_CTRL(or, link)) & 0x000f0000; - u32 clksor = nv_rd32(device, 0x614300 + (or * 0x800)); - if (clksor & 0x000c0000) - *bw = 270000; - else - *bw = 162000; - - if (dpctrl > 0x00030000) *nr = 4; - else if (dpctrl > 0x00010000) *nr = 2; - else *nr = 1; -} - -void -nv50_sor_dp_calc_tu(struct drm_device *dev, int or, int link, u32 clk, u32 bpp) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - const u32 symbol = 100000; - int bestTU = 0, bestVTUi = 0, bestVTUf = 0, bestVTUa = 0; - int TU, VTUi, VTUf, VTUa; - u64 link_data_rate, link_ratio, unk; - u32 best_diff = 64 * symbol; - u32 link_nr, link_bw, r; - - /* calculate packed data rate for each lane */ - nv50_sor_dp_link_get(dev, or, link, &link_nr, &link_bw); - link_data_rate = (clk * bpp / 8) / link_nr; - - /* calculate ratio of packed data rate to link symbol rate */ - link_ratio = link_data_rate * symbol; - r = do_div(link_ratio, link_bw); - - for (TU = 64; TU >= 32; TU--) { - /* calculate average number of valid symbols in each TU */ - u32 tu_valid = link_ratio * TU; - u32 calc, diff; - - /* find a hw representation for the fraction.. */ - VTUi = tu_valid / symbol; - calc = VTUi * symbol; - diff = tu_valid - calc; - if (diff) { - if (diff >= (symbol / 2)) { - VTUf = symbol / (symbol - diff); - if (symbol - (VTUf * diff)) - VTUf++; - - if (VTUf <= 15) { - VTUa = 1; - calc += symbol - (symbol / VTUf); - } else { - VTUa = 0; - VTUf = 1; - calc += symbol; - } - } else { - VTUa = 0; - VTUf = min((int)(symbol / diff), 15); - calc += symbol / VTUf; - } - - diff = calc - tu_valid; - } else { - /* no remainder, but the hw doesn't like the fractional - * part to be zero. decrement the integer part and - * have the fraction add a whole symbol back - */ - VTUa = 0; - VTUf = 1; - VTUi--; - } - - if (diff < best_diff) { - best_diff = diff; - bestTU = TU; - bestVTUa = VTUa; - bestVTUf = VTUf; - bestVTUi = VTUi; - if (diff == 0) - break; - } - } - - if (!bestTU) { - NV_ERROR(drm, "DP: unable to find suitable config\n"); - return; - } - - /* XXX close to vbios numbers, but not right */ - unk = (symbol - link_ratio) * bestTU; - unk *= link_ratio; - r = do_div(unk, symbol); - r = do_div(unk, symbol); - unk += 6; - - nv_mask(device, NV50_SOR_DP_CTRL(or, link), 0x000001fc, bestTU << 2); - nv_mask(device, NV50_SOR_DP_SCFG(or, link), 0x010f7f3f, bestVTUa << 24 | - bestVTUf << 16 | - bestVTUi << 8 | - unk); -} -static void -nv50_sor_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct drm_device *dev = encoder->dev; - struct nouveau_channel *evo = nv50_display(dev)->master; - int ret; - - if (!nv_encoder->crtc) - return; - nv50_crtc_blank(nouveau_crtc(nv_encoder->crtc), true); - - NV_DEBUG(drm, "Disconnecting SOR %d\n", nv_encoder->or); - - ret = RING_SPACE(evo, 4); - if (ret) { - NV_ERROR(drm, "no space while disconnecting SOR\n"); - return; - } - BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); - OUT_RING (evo, 0); - BEGIN_NV04(evo, 0, NV50_EVO_UPDATE, 1); - OUT_RING (evo, 0); - - nouveau_hdmi_mode_set(encoder, NULL); - - nv_encoder->crtc = NULL; - nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; -} - -static void -nv50_sor_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_device *device = nouveau_dev(encoder->dev); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct drm_device *dev = encoder->dev; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_encoder *enc; - uint32_t val; - int or = nv_encoder->or; - - NV_DEBUG(drm, "or %d type %d mode %d\n", or, nv_encoder->dcb->type, mode); - - nv_encoder->last_dpms = mode; - list_for_each_entry(enc, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nvenc = nouveau_encoder(enc); - - if (nvenc == nv_encoder || - (nvenc->dcb->type != DCB_OUTPUT_TMDS && - nvenc->dcb->type != DCB_OUTPUT_LVDS && - nvenc->dcb->type != DCB_OUTPUT_DP) || - nvenc->dcb->or != nv_encoder->dcb->or) - continue; - - if (nvenc->last_dpms == DRM_MODE_DPMS_ON) - return; - } - - /* wait for it to be done */ - if (!nv_wait(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or), - NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) { - NV_ERROR(drm, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or); - NV_ERROR(drm, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or, - nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or))); - } - - val = nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or)); - - if (mode == DRM_MODE_DPMS_ON) - val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON; - else - val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON; - - nv_wr32(device, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val | - NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING); - if (!nv_wait(device, NV50_PDISPLAY_SOR_DPMS_STATE(or), - NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) { - NV_ERROR(drm, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or); - NV_ERROR(drm, "SOR_DPMS_STATE(%d) = 0x%08x\n", or, - nv_rd32(device, NV50_PDISPLAY_SOR_DPMS_STATE(or))); - } - - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - struct dp_train_func func = { - .link_set = nv50_sor_dp_link_set, - .train_set = nv50_sor_dp_train_set, - .train_adj = nv50_sor_dp_train_adj - }; - - nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); - } -} - -static void -nv50_sor_save(struct drm_encoder *encoder) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - NV_ERROR(drm, "!!\n"); -} - -static void -nv50_sor_restore(struct drm_encoder *encoder) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - NV_ERROR(drm, "!!\n"); -} - -static bool -nv50_sor_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *connector; - - NV_DEBUG(drm, "or %d\n", nv_encoder->or); - - connector = nouveau_encoder_connector_get(nv_encoder); - if (!connector) { - NV_ERROR(drm, "Encoder has no connector\n"); - return false; - } - - if (connector->scaling_mode != DRM_MODE_SCALE_NONE && - connector->native_mode) - drm_mode_copy(adjusted_mode, connector->native_mode); - - return true; -} - -static void -nv50_sor_prepare(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - nv50_sor_disconnect(encoder); - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - /* avoid race between link training and supervisor intr */ - nv50_display_sync(encoder->dev); - } -} - -static void -nv50_sor_commit(struct drm_encoder *encoder) -{ -} - -static void -nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, - struct drm_display_mode *mode) -{ - struct nouveau_channel *evo = nv50_display(encoder->dev)->master; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc); - struct nouveau_connector *nv_connector; - uint32_t mode_ctl = 0; - int ret; - - NV_DEBUG(drm, "or %d type %d -> crtc %d\n", - nv_encoder->or, nv_encoder->dcb->type, crtc->index); - nv_encoder->crtc = encoder->crtc; - - switch (nv_encoder->dcb->type) { - case DCB_OUTPUT_TMDS: - if (nv_encoder->dcb->sorconf.link & 1) { - if (mode->clock < 165000) - mode_ctl = 0x0100; - else - mode_ctl = 0x0500; - } else - mode_ctl = 0x0200; - - nouveau_hdmi_mode_set(encoder, mode); - break; - case DCB_OUTPUT_DP: - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (nv_connector && nv_connector->base.display_info.bpc == 6) { - nv_encoder->dp.datarate = mode->clock * 18 / 8; - mode_ctl |= 0x00020000; - } else { - nv_encoder->dp.datarate = mode->clock * 24 / 8; - mode_ctl |= 0x00050000; - } - - if (nv_encoder->dcb->sorconf.link & 1) - mode_ctl |= 0x00000800; - else - mode_ctl |= 0x00000900; - break; - default: - break; - } - - if (crtc->index == 1) - mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1; - else - mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0; - - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC; - - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC; - - nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON); - - ret = RING_SPACE(evo, 2); - if (ret) { - NV_ERROR(drm, "no space while connecting SOR\n"); - nv_encoder->crtc = NULL; - return; - } - BEGIN_NV04(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1); - OUT_RING(evo, mode_ctl); -} - -static struct drm_crtc * -nv50_sor_crtc_get(struct drm_encoder *encoder) -{ - return nouveau_encoder(encoder)->crtc; -} - -static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = { - .dpms = nv50_sor_dpms, - .save = nv50_sor_save, - .restore = nv50_sor_restore, - .mode_fixup = nv50_sor_mode_fixup, - .prepare = nv50_sor_prepare, - .commit = nv50_sor_commit, - .mode_set = nv50_sor_mode_set, - .get_crtc = nv50_sor_crtc_get, - .detect = NULL, - .disable = nv50_sor_disconnect -}; - -static void -nv50_sor_destroy(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_drm *drm = nouveau_drm(encoder->dev); - - NV_DEBUG(drm, "\n"); - - drm_encoder_cleanup(encoder); - - kfree(nv_encoder); -} - -static const struct drm_encoder_funcs nv50_sor_encoder_funcs = { - .destroy = nv50_sor_destroy, -}; - -int -nv50_sor_create(struct drm_connector *connector, struct dcb_output *entry) -{ - struct nouveau_encoder *nv_encoder = NULL; - struct drm_device *dev = connector->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct drm_encoder *encoder; - int type; - - NV_DEBUG(drm, "\n"); - - switch (entry->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_DP: - type = DRM_MODE_ENCODER_TMDS; - break; - case DCB_OUTPUT_LVDS: - type = DRM_MODE_ENCODER_LVDS; - break; - default: - return -EINVAL; - } - - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - encoder = to_drm_encoder(nv_encoder); - - nv_encoder->dcb = entry; - nv_encoder->or = ffs(entry->or) - 1; - nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; - - drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type); - drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs); - - encoder->possible_crtcs = entry->heads; - encoder->possible_clones = 0; - - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} diff --git a/drivers/gpu/drm/nouveau/nvc0_fence.c b/drivers/gpu/drm/nouveau/nvc0_fence.c index 53299eac967..2a56b1b551c 100644 --- a/drivers/gpu/drm/nouveau/nvc0_fence.c +++ b/drivers/gpu/drm/nouveau/nvc0_fence.c @@ -114,17 +114,9 @@ nvc0_fence_context_del(struct nouveau_channel *chan) struct nvc0_fence_chan *fctx = chan->fence; int i; - if (nv_device(chan->drm->device)->card_type >= NV_D0) { - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i); - nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); - } - } else - if (nv_device(chan->drm->device)->card_type >= NV_50) { - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); - nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); - } + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct nouveau_bo *bo = nv50_display_crtc_sema(dev, i); + nouveau_bo_vma_del(bo, &fctx->dispc_vma[i]); } nouveau_bo_vma_del(priv->bo, &fctx->vma); @@ -154,12 +146,7 @@ nvc0_fence_context_new(struct nouveau_channel *chan) /* map display semaphore buffers into channel's vm */ for (i = 0; !ret && i < chan->drm->dev->mode_config.num_crtc; i++) { - struct nouveau_bo *bo; - if (nv_device(chan->drm->device)->card_type >= NV_D0) - bo = nvd0_display_crtc_sema(chan->drm->dev, i); - else - bo = nv50_display_crtc_sema(chan->drm->dev, i); - + struct nouveau_bo *bo = nv50_display_crtc_sema(chan->drm->dev, i); ret = nouveau_bo_vma_add(bo, client->vm, &fctx->dispc_vma[i]); } @@ -203,6 +190,8 @@ nvc0_fence_destroy(struct nouveau_drm *drm) { struct nvc0_fence_priv *priv = drm->fence; nouveau_bo_unmap(priv->bo); + if (priv->bo) + nouveau_bo_unpin(priv->bo); nouveau_bo_ref(NULL, &priv->bo); drm->fence = NULL; kfree(priv); @@ -232,8 +221,11 @@ nvc0_fence_create(struct nouveau_drm *drm) TTM_PL_FLAG_VRAM, 0, 0, NULL, &priv->bo); if (ret == 0) { ret = nouveau_bo_pin(priv->bo, TTM_PL_FLAG_VRAM); - if (ret == 0) + if (ret == 0) { ret = nouveau_bo_map(priv->bo); + if (ret) + nouveau_bo_unpin(priv->bo); + } if (ret) nouveau_bo_ref(NULL, &priv->bo); } diff --git a/drivers/gpu/drm/nouveau/nvd0_display.c b/drivers/gpu/drm/nouveau/nvd0_display.c deleted file mode 100644 index c402fca2b2b..00000000000 --- a/drivers/gpu/drm/nouveau/nvd0_display.c +++ /dev/null @@ -1,2141 +0,0 @@ -/* - * Copyright 2011 Red Hat Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Authors: Ben Skeggs - */ - -#include <linux/dma-mapping.h> - -#include <drm/drmP.h> -#include <drm/drm_crtc_helper.h> - -#include "nouveau_drm.h" -#include "nouveau_dma.h" -#include "nouveau_gem.h" -#include "nouveau_connector.h" -#include "nouveau_encoder.h" -#include "nouveau_crtc.h" -#include "nouveau_fence.h" -#include "nv50_display.h" - -#include <core/gpuobj.h> - -#include <subdev/timer.h> -#include <subdev/bar.h> -#include <subdev/fb.h> - -#define EVO_DMA_NR 9 - -#define EVO_MASTER (0x00) -#define EVO_FLIP(c) (0x01 + (c)) -#define EVO_OVLY(c) (0x05 + (c)) -#define EVO_OIMM(c) (0x09 + (c)) -#define EVO_CURS(c) (0x0d + (c)) - -/* offsets in shared sync bo of various structures */ -#define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) -#define EVO_MAST_NTFY EVO_SYNC( 0, 0x00) -#define EVO_FLIP_SEM0(c) EVO_SYNC((c), 0x00) -#define EVO_FLIP_SEM1(c) EVO_SYNC((c), 0x10) - -struct evo { - int idx; - dma_addr_t handle; - u32 *ptr; - struct { - u32 offset; - u16 value; - } sem; -}; - -struct nvd0_display { - struct nouveau_gpuobj *mem; - struct nouveau_bo *sync; - struct evo evo[9]; - - struct tasklet_struct tasklet; - u32 modeset; -}; - -static struct nvd0_display * -nvd0_display(struct drm_device *dev) -{ - return nouveau_display(dev)->priv; -} - -static struct drm_crtc * -nvd0_display_crtc_get(struct drm_encoder *encoder) -{ - return nouveau_encoder(encoder)->crtc; -} - -/****************************************************************************** - * EVO channel helpers - *****************************************************************************/ -static inline int -evo_icmd(struct drm_device *dev, int id, u32 mthd, u32 data) -{ - struct nouveau_device *device = nouveau_dev(dev); - int ret = 0; - nv_mask(device, 0x610700 + (id * 0x10), 0x00000001, 0x00000001); - nv_wr32(device, 0x610704 + (id * 0x10), data); - nv_mask(device, 0x610704 + (id * 0x10), 0x80000ffc, 0x80000000 | mthd); - if (!nv_wait(device, 0x610704 + (id * 0x10), 0x80000000, 0x00000000)) - ret = -EBUSY; - nv_mask(device, 0x610700 + (id * 0x10), 0x00000001, 0x00000000); - return ret; -} - -static u32 * -evo_wait(struct drm_device *dev, int id, int nr) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvd0_display *disp = nvd0_display(dev); - u32 put = nv_rd32(device, 0x640000 + (id * 0x1000)) / 4; - - if (put + nr >= (PAGE_SIZE / 4)) { - disp->evo[id].ptr[put] = 0x20000000; - - nv_wr32(device, 0x640000 + (id * 0x1000), 0x00000000); - if (!nv_wait(device, 0x640004 + (id * 0x1000), ~0, 0x00000000)) { - NV_ERROR(drm, "evo %d dma stalled\n", id); - return NULL; - } - - put = 0; - } - - return disp->evo[id].ptr + put; -} - -static void -evo_kick(u32 *push, struct drm_device *dev, int id) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nvd0_display *disp = nvd0_display(dev); - - nv_wr32(device, 0x640000 + (id * 0x1000), (push - disp->evo[id].ptr) << 2); -} - -#define evo_mthd(p,m,s) *((p)++) = (((s) << 18) | (m)) -#define evo_data(p,d) *((p)++) = (d) - -static int -evo_init_dma(struct drm_device *dev, int ch) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvd0_display *disp = nvd0_display(dev); - u32 flags; - - flags = 0x00000000; - if (ch == EVO_MASTER) - flags |= 0x01000000; - - nv_wr32(device, 0x610494 + (ch * 0x0010), (disp->evo[ch].handle >> 8) | 3); - nv_wr32(device, 0x610498 + (ch * 0x0010), 0x00010000); - nv_wr32(device, 0x61049c + (ch * 0x0010), 0x00000001); - nv_mask(device, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010); - nv_wr32(device, 0x640000 + (ch * 0x1000), 0x00000000); - nv_wr32(device, 0x610490 + (ch * 0x0010), 0x00000013 | flags); - if (!nv_wait(device, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000)) { - NV_ERROR(drm, "PDISP: ch%d 0x%08x\n", ch, - nv_rd32(device, 0x610490 + (ch * 0x0010))); - return -EBUSY; - } - - nv_mask(device, 0x610090, (1 << ch), (1 << ch)); - nv_mask(device, 0x6100a0, (1 << ch), (1 << ch)); - return 0; -} - -static void -evo_fini_dma(struct drm_device *dev, int ch) -{ - struct nouveau_device *device = nouveau_dev(dev); - - if (!(nv_rd32(device, 0x610490 + (ch * 0x0010)) & 0x00000010)) - return; - - nv_mask(device, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000000); - nv_mask(device, 0x610490 + (ch * 0x0010), 0x00000003, 0x00000000); - nv_wait(device, 0x610490 + (ch * 0x0010), 0x80000000, 0x00000000); - nv_mask(device, 0x610090, (1 << ch), 0x00000000); - nv_mask(device, 0x6100a0, (1 << ch), 0x00000000); -} - -static inline void -evo_piow(struct drm_device *dev, int ch, u16 mthd, u32 data) -{ - struct nouveau_device *device = nouveau_dev(dev); - nv_wr32(device, 0x640000 + (ch * 0x1000) + mthd, data); -} - -static int -evo_init_pio(struct drm_device *dev, int ch) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - - nv_wr32(device, 0x610490 + (ch * 0x0010), 0x00000001); - if (!nv_wait(device, 0x610490 + (ch * 0x0010), 0x00010000, 0x00010000)) { - NV_ERROR(drm, "PDISP: ch%d 0x%08x\n", ch, - nv_rd32(device, 0x610490 + (ch * 0x0010))); - return -EBUSY; - } - - nv_mask(device, 0x610090, (1 << ch), (1 << ch)); - nv_mask(device, 0x6100a0, (1 << ch), (1 << ch)); - return 0; -} - -static void -evo_fini_pio(struct drm_device *dev, int ch) -{ - struct nouveau_device *device = nouveau_dev(dev); - - if (!(nv_rd32(device, 0x610490 + (ch * 0x0010)) & 0x00000001)) - return; - - nv_mask(device, 0x610490 + (ch * 0x0010), 0x00000010, 0x00000010); - nv_mask(device, 0x610490 + (ch * 0x0010), 0x00000001, 0x00000000); - nv_wait(device, 0x610490 + (ch * 0x0010), 0x00010000, 0x00000000); - nv_mask(device, 0x610090, (1 << ch), 0x00000000); - nv_mask(device, 0x6100a0, (1 << ch), 0x00000000); -} - -static bool -evo_sync_wait(void *data) -{ - return nouveau_bo_rd32(data, EVO_MAST_NTFY) != 0x00000000; -} - -static int -evo_sync(struct drm_device *dev, int ch) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nvd0_display *disp = nvd0_display(dev); - u32 *push = evo_wait(dev, ch, 8); - if (push) { - nouveau_bo_wr32(disp->sync, EVO_MAST_NTFY, 0x00000000); - evo_mthd(push, 0x0084, 1); - evo_data(push, 0x80000000 | EVO_MAST_NTFY); - evo_mthd(push, 0x0080, 2); - evo_data(push, 0x00000000); - evo_data(push, 0x00000000); - evo_kick(push, dev, ch); - if (nv_wait_cb(device, evo_sync_wait, disp->sync)) - return 0; - } - - return -EBUSY; -} - -/****************************************************************************** - * Page flipping channel - *****************************************************************************/ -struct nouveau_bo * -nvd0_display_crtc_sema(struct drm_device *dev, int crtc) -{ - return nvd0_display(dev)->sync; -} - -void -nvd0_display_flip_stop(struct drm_crtc *crtc) -{ - struct nvd0_display *disp = nvd0_display(crtc->dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)]; - u32 *push; - - push = evo_wait(crtc->dev, evo->idx, 8); - if (push) { - evo_mthd(push, 0x0084, 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0094, 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x00c0, 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - evo_kick(push, crtc->dev, evo->idx); - } -} - -int -nvd0_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb, - struct nouveau_channel *chan, u32 swap_interval) -{ - struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); - struct nvd0_display *disp = nvd0_display(crtc->dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct evo *evo = &disp->evo[EVO_FLIP(nv_crtc->index)]; - u64 offset; - u32 *push; - int ret; - - swap_interval <<= 4; - if (swap_interval == 0) - swap_interval |= 0x100; - - push = evo_wait(crtc->dev, evo->idx, 128); - if (unlikely(push == NULL)) - return -EBUSY; - - /* synchronise with the rendering channel, if necessary */ - if (likely(chan)) { - ret = RING_SPACE(chan, 10); - if (ret) - return ret; - - - offset = nvc0_fence_crtc(chan, nv_crtc->index); - offset += evo->sem.offset; - - BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset)); - OUT_RING (chan, 0xf00d0000 | evo->sem.value); - OUT_RING (chan, 0x1002); - BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); - OUT_RING (chan, upper_32_bits(offset)); - OUT_RING (chan, lower_32_bits(offset ^ 0x10)); - OUT_RING (chan, 0x74b1e000); - OUT_RING (chan, 0x1001); - FIRE_RING (chan); - } else { - nouveau_bo_wr32(disp->sync, evo->sem.offset / 4, - 0xf00d0000 | evo->sem.value); - evo_sync(crtc->dev, EVO_MASTER); - } - - /* queue the flip */ - evo_mthd(push, 0x0100, 1); - evo_data(push, 0xfffe0000); - evo_mthd(push, 0x0084, 1); - evo_data(push, swap_interval); - if (!(swap_interval & 0x00000100)) { - evo_mthd(push, 0x00e0, 1); - evo_data(push, 0x40000000); - } - evo_mthd(push, 0x0088, 4); - evo_data(push, evo->sem.offset); - evo_data(push, 0xf00d0000 | evo->sem.value); - evo_data(push, 0x74b1e000); - evo_data(push, NvEvoSync); - evo_mthd(push, 0x00a0, 2); - evo_data(push, 0x00000000); - evo_data(push, 0x00000000); - evo_mthd(push, 0x00c0, 1); - evo_data(push, nv_fb->r_dma); - evo_mthd(push, 0x0110, 2); - evo_data(push, 0x00000000); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0400, 5); - evo_data(push, nv_fb->nvbo->bo.offset >> 8); - evo_data(push, 0); - evo_data(push, (fb->height << 16) | fb->width); - evo_data(push, nv_fb->r_pitch); - evo_data(push, nv_fb->r_format); - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - evo_kick(push, crtc->dev, evo->idx); - - evo->sem.offset ^= 0x10; - evo->sem.value++; - return 0; -} - -/****************************************************************************** - * CRTC - *****************************************************************************/ -static int -nvd0_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update) -{ - struct nouveau_drm *drm = nouveau_drm(nv_crtc->base.dev); - struct drm_device *dev = nv_crtc->base.dev; - struct nouveau_connector *nv_connector; - struct drm_connector *connector; - u32 *push, mode = 0x00; - u32 mthd; - - nv_connector = nouveau_crtc_connector_get(nv_crtc); - connector = &nv_connector->base; - if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) { - if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3) - mode = DITHERING_MODE_DYNAMIC2X2; - } else { - mode = nv_connector->dithering_mode; - } - - if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) { - if (connector->display_info.bpc >= 8) - mode |= DITHERING_DEPTH_8BPC; - } else { - mode |= nv_connector->dithering_depth; - } - - if (nv_device(drm->device)->card_type < NV_E0) - mthd = 0x0490 + (nv_crtc->index * 0x0300); - else - mthd = 0x04a0 + (nv_crtc->index * 0x0300); - - push = evo_wait(dev, EVO_MASTER, 4); - if (push) { - evo_mthd(push, mthd, 1); - evo_data(push, mode); - if (update) { - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - } - evo_kick(push, dev, EVO_MASTER); - } - - return 0; -} - -static int -nvd0_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update) -{ - struct drm_display_mode *omode, *umode = &nv_crtc->base.mode; - struct drm_device *dev = nv_crtc->base.dev; - struct drm_crtc *crtc = &nv_crtc->base; - struct nouveau_connector *nv_connector; - int mode = DRM_MODE_SCALE_NONE; - u32 oX, oY, *push; - - /* start off at the resolution we programmed the crtc for, this - * effectively handles NONE/FULL scaling - */ - nv_connector = nouveau_crtc_connector_get(nv_crtc); - if (nv_connector && nv_connector->native_mode) - mode = nv_connector->scaling_mode; - - if (mode != DRM_MODE_SCALE_NONE) - omode = nv_connector->native_mode; - else - omode = umode; - - oX = omode->hdisplay; - oY = omode->vdisplay; - if (omode->flags & DRM_MODE_FLAG_DBLSCAN) - oY *= 2; - - /* add overscan compensation if necessary, will keep the aspect - * ratio the same as the backend mode unless overridden by the - * user setting both hborder and vborder properties. - */ - if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON || - (nv_connector->underscan == UNDERSCAN_AUTO && - nv_connector->edid && - drm_detect_hdmi_monitor(nv_connector->edid)))) { - u32 bX = nv_connector->underscan_hborder; - u32 bY = nv_connector->underscan_vborder; - u32 aspect = (oY << 19) / oX; - - if (bX) { - oX -= (bX * 2); - if (bY) oY -= (bY * 2); - else oY = ((oX * aspect) + (aspect / 2)) >> 19; - } else { - oX -= (oX >> 4) + 32; - if (bY) oY -= (bY * 2); - else oY = ((oX * aspect) + (aspect / 2)) >> 19; - } - } - - /* handle CENTER/ASPECT scaling, taking into account the areas - * removed already for overscan compensation - */ - switch (mode) { - case DRM_MODE_SCALE_CENTER: - oX = min((u32)umode->hdisplay, oX); - oY = min((u32)umode->vdisplay, oY); - /* fall-through */ - case DRM_MODE_SCALE_ASPECT: - if (oY < oX) { - u32 aspect = (umode->hdisplay << 19) / umode->vdisplay; - oX = ((oY * aspect) + (aspect / 2)) >> 19; - } else { - u32 aspect = (umode->vdisplay << 19) / umode->hdisplay; - oY = ((oX * aspect) + (aspect / 2)) >> 19; - } - break; - default: - break; - } - - push = evo_wait(dev, EVO_MASTER, 8); - if (push) { - evo_mthd(push, 0x04c0 + (nv_crtc->index * 0x300), 3); - evo_data(push, (oY << 16) | oX); - evo_data(push, (oY << 16) | oX); - evo_data(push, (oY << 16) | oX); - evo_mthd(push, 0x0494 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x04b8 + (nv_crtc->index * 0x300), 1); - evo_data(push, (umode->vdisplay << 16) | umode->hdisplay); - evo_kick(push, dev, EVO_MASTER); - if (update) { - nvd0_display_flip_stop(crtc); - nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); - } - } - - return 0; -} - -static int -nvd0_crtc_set_image(struct nouveau_crtc *nv_crtc, struct drm_framebuffer *fb, - int x, int y, bool update) -{ - struct nouveau_framebuffer *nvfb = nouveau_framebuffer(fb); - u32 *push; - - push = evo_wait(fb->dev, EVO_MASTER, 16); - if (push) { - evo_mthd(push, 0x0460 + (nv_crtc->index * 0x300), 1); - evo_data(push, nvfb->nvbo->bo.offset >> 8); - evo_mthd(push, 0x0468 + (nv_crtc->index * 0x300), 4); - evo_data(push, (fb->height << 16) | fb->width); - evo_data(push, nvfb->r_pitch); - evo_data(push, nvfb->r_format); - evo_data(push, nvfb->r_dma); - evo_mthd(push, 0x04b0 + (nv_crtc->index * 0x300), 1); - evo_data(push, (y << 16) | x); - if (update) { - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - } - evo_kick(push, fb->dev, EVO_MASTER); - } - - nv_crtc->fb.tile_flags = nvfb->r_dma; - return 0; -} - -static void -nvd0_crtc_cursor_show(struct nouveau_crtc *nv_crtc, bool show, bool update) -{ - struct drm_device *dev = nv_crtc->base.dev; - u32 *push = evo_wait(dev, EVO_MASTER, 16); - if (push) { - if (show) { - evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 2); - evo_data(push, 0x85000000); - evo_data(push, nv_crtc->cursor.nvbo->bo.offset >> 8); - evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); - evo_data(push, NvEvoVRAM); - } else { - evo_mthd(push, 0x0480 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x05000000); - evo_mthd(push, 0x048c + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x00000000); - } - - if (update) { - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - } - - evo_kick(push, dev, EVO_MASTER); - } -} - -static void -nvd0_crtc_dpms(struct drm_crtc *crtc, int mode) -{ -} - -static void -nvd0_crtc_prepare(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 *push; - - nvd0_display_flip_stop(crtc); - - push = evo_wait(crtc->dev, EVO_MASTER, 2); - if (push) { - evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x03000000); - evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x00000000); - evo_kick(push, crtc->dev, EVO_MASTER); - } - - nvd0_crtc_cursor_show(nv_crtc, false, false); -} - -static void -nvd0_crtc_commit(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 *push; - - push = evo_wait(crtc->dev, EVO_MASTER, 32); - if (push) { - evo_mthd(push, 0x0474 + (nv_crtc->index * 0x300), 1); - evo_data(push, nv_crtc->fb.tile_flags); - evo_mthd(push, 0x0440 + (nv_crtc->index * 0x300), 4); - evo_data(push, 0x83000000); - evo_data(push, nv_crtc->lut.nvbo->bo.offset >> 8); - evo_data(push, 0x00000000); - evo_data(push, 0x00000000); - evo_mthd(push, 0x045c + (nv_crtc->index * 0x300), 1); - evo_data(push, NvEvoVRAM); - evo_mthd(push, 0x0430 + (nv_crtc->index * 0x300), 1); - evo_data(push, 0xffffff00); - evo_kick(push, crtc->dev, EVO_MASTER); - } - - nvd0_crtc_cursor_show(nv_crtc, nv_crtc->cursor.visible, true); - nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); -} - -static bool -nvd0_crtc_mode_fixup(struct drm_crtc *crtc, const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - return true; -} - -static int -nvd0_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb) -{ - struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb); - int ret; - - ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM); - if (ret) - return ret; - - if (old_fb) { - nvfb = nouveau_framebuffer(old_fb); - nouveau_bo_unpin(nvfb->nvbo); - } - - return 0; -} - -static int -nvd0_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode, - struct drm_display_mode *mode, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct nouveau_connector *nv_connector; - u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1; - u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1; - u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks; - u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks; - u32 vblan2e = 0, vblan2s = 1; - u32 *push; - int ret; - - hactive = mode->htotal; - hsynce = mode->hsync_end - mode->hsync_start - 1; - hbackp = mode->htotal - mode->hsync_end; - hblanke = hsynce + hbackp; - hfrontp = mode->hsync_start - mode->hdisplay; - hblanks = mode->htotal - hfrontp - 1; - - vactive = mode->vtotal * vscan / ilace; - vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1; - vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace; - vblanke = vsynce + vbackp; - vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace; - vblanks = vactive - vfrontp - 1; - if (mode->flags & DRM_MODE_FLAG_INTERLACE) { - vblan2e = vactive + vsynce + vbackp; - vblan2s = vblan2e + (mode->vdisplay * vscan / ilace); - vactive = (vactive * 2) + 1; - } - - ret = nvd0_crtc_swap_fbs(crtc, old_fb); - if (ret) - return ret; - - push = evo_wait(crtc->dev, EVO_MASTER, 64); - if (push) { - evo_mthd(push, 0x0410 + (nv_crtc->index * 0x300), 6); - evo_data(push, 0x00000000); - evo_data(push, (vactive << 16) | hactive); - evo_data(push, ( vsynce << 16) | hsynce); - evo_data(push, (vblanke << 16) | hblanke); - evo_data(push, (vblanks << 16) | hblanks); - evo_data(push, (vblan2e << 16) | vblan2s); - evo_mthd(push, 0x042c + (nv_crtc->index * 0x300), 1); - evo_data(push, 0x00000000); /* ??? */ - evo_mthd(push, 0x0450 + (nv_crtc->index * 0x300), 3); - evo_data(push, mode->clock * 1000); - evo_data(push, 0x00200000); /* ??? */ - evo_data(push, mode->clock * 1000); - evo_mthd(push, 0x04d0 + (nv_crtc->index * 0x300), 2); - evo_data(push, 0x00000311); - evo_data(push, 0x00000100); - evo_kick(push, crtc->dev, EVO_MASTER); - } - - nv_connector = nouveau_crtc_connector_get(nv_crtc); - nvd0_crtc_set_dither(nv_crtc, false); - nvd0_crtc_set_scale(nv_crtc, false); - nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, false); - return 0; -} - -static int -nvd0_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y, - struct drm_framebuffer *old_fb) -{ - struct nouveau_drm *drm = nouveau_drm(crtc->dev); - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - int ret; - - if (!crtc->fb) { - NV_DEBUG(drm, "No FB bound\n"); - return 0; - } - - ret = nvd0_crtc_swap_fbs(crtc, old_fb); - if (ret) - return ret; - - nvd0_display_flip_stop(crtc); - nvd0_crtc_set_image(nv_crtc, crtc->fb, x, y, true); - nvd0_display_flip_next(crtc, crtc->fb, NULL, 1); - return 0; -} - -static int -nvd0_crtc_mode_set_base_atomic(struct drm_crtc *crtc, - struct drm_framebuffer *fb, int x, int y, - enum mode_set_atomic state) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - nvd0_display_flip_stop(crtc); - nvd0_crtc_set_image(nv_crtc, fb, x, y, true); - return 0; -} - -static void -nvd0_crtc_lut_load(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo); - int i; - - for (i = 0; i < 256; i++) { - writew(0x6000 + (nv_crtc->lut.r[i] >> 2), lut + (i * 0x20) + 0); - writew(0x6000 + (nv_crtc->lut.g[i] >> 2), lut + (i * 0x20) + 2); - writew(0x6000 + (nv_crtc->lut.b[i] >> 2), lut + (i * 0x20) + 4); - } -} - -static int -nvd0_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv, - uint32_t handle, uint32_t width, uint32_t height) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - struct drm_device *dev = crtc->dev; - struct drm_gem_object *gem; - struct nouveau_bo *nvbo; - bool visible = (handle != 0); - int i, ret = 0; - - if (visible) { - if (width != 64 || height != 64) - return -EINVAL; - - gem = drm_gem_object_lookup(dev, file_priv, handle); - if (unlikely(!gem)) - return -ENOENT; - nvbo = nouveau_gem_object(gem); - - ret = nouveau_bo_map(nvbo); - if (ret == 0) { - for (i = 0; i < 64 * 64; i++) { - u32 v = nouveau_bo_rd32(nvbo, i); - nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, v); - } - nouveau_bo_unmap(nvbo); - } - - drm_gem_object_unreference_unlocked(gem); - } - - if (visible != nv_crtc->cursor.visible) { - nvd0_crtc_cursor_show(nv_crtc, visible, true); - nv_crtc->cursor.visible = visible; - } - - return ret; -} - -static int -nvd0_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - int ch = EVO_CURS(nv_crtc->index); - - evo_piow(crtc->dev, ch, 0x0084, (y << 16) | (x & 0xffff)); - evo_piow(crtc->dev, ch, 0x0080, 0x00000000); - return 0; -} - -static void -nvd0_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, - uint32_t start, uint32_t size) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - u32 end = max(start + size, (u32)256); - u32 i; - - for (i = start; i < end; i++) { - nv_crtc->lut.r[i] = r[i]; - nv_crtc->lut.g[i] = g[i]; - nv_crtc->lut.b[i] = b[i]; - } - - nvd0_crtc_lut_load(crtc); -} - -static void -nvd0_crtc_destroy(struct drm_crtc *crtc) -{ - struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc); - nouveau_bo_unmap(nv_crtc->cursor.nvbo); - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - nouveau_bo_unmap(nv_crtc->lut.nvbo); - nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); - drm_crtc_cleanup(crtc); - kfree(crtc); -} - -static const struct drm_crtc_helper_funcs nvd0_crtc_hfunc = { - .dpms = nvd0_crtc_dpms, - .prepare = nvd0_crtc_prepare, - .commit = nvd0_crtc_commit, - .mode_fixup = nvd0_crtc_mode_fixup, - .mode_set = nvd0_crtc_mode_set, - .mode_set_base = nvd0_crtc_mode_set_base, - .mode_set_base_atomic = nvd0_crtc_mode_set_base_atomic, - .load_lut = nvd0_crtc_lut_load, -}; - -static const struct drm_crtc_funcs nvd0_crtc_func = { - .cursor_set = nvd0_crtc_cursor_set, - .cursor_move = nvd0_crtc_cursor_move, - .gamma_set = nvd0_crtc_gamma_set, - .set_config = drm_crtc_helper_set_config, - .destroy = nvd0_crtc_destroy, - .page_flip = nouveau_crtc_page_flip, -}; - -static void -nvd0_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y) -{ -} - -static void -nvd0_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset) -{ -} - -static int -nvd0_crtc_create(struct drm_device *dev, int index) -{ - struct nouveau_crtc *nv_crtc; - struct drm_crtc *crtc; - int ret, i; - - nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL); - if (!nv_crtc) - return -ENOMEM; - - nv_crtc->index = index; - nv_crtc->set_dither = nvd0_crtc_set_dither; - nv_crtc->set_scale = nvd0_crtc_set_scale; - nv_crtc->cursor.set_offset = nvd0_cursor_set_offset; - nv_crtc->cursor.set_pos = nvd0_cursor_set_pos; - for (i = 0; i < 256; i++) { - nv_crtc->lut.r[i] = i << 8; - nv_crtc->lut.g[i] = i << 8; - nv_crtc->lut.b[i] = i << 8; - } - - crtc = &nv_crtc->base; - drm_crtc_init(dev, crtc, &nvd0_crtc_func); - drm_crtc_helper_add(crtc, &nvd0_crtc_hfunc); - drm_mode_crtc_set_gamma_size(crtc, 256); - - ret = nouveau_bo_new(dev, 64 * 64 * 4, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &nv_crtc->cursor.nvbo); - if (!ret) { - ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(nv_crtc->cursor.nvbo); - if (ret) - nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo); - } - - if (ret) - goto out; - - ret = nouveau_bo_new(dev, 8192, 0x100, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &nv_crtc->lut.nvbo); - if (!ret) { - ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(nv_crtc->lut.nvbo); - if (ret) - nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo); - } - - if (ret) - goto out; - - nvd0_crtc_lut_load(crtc); - -out: - if (ret) - nvd0_crtc_destroy(crtc); - return ret; -} - -/****************************************************************************** - * DAC - *****************************************************************************/ -static void -nvd0_dac_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - int or = nv_encoder->or; - u32 dpms_ctrl; - - dpms_ctrl = 0x80000000; - if (mode == DRM_MODE_DPMS_STANDBY || mode == DRM_MODE_DPMS_OFF) - dpms_ctrl |= 0x00000001; - if (mode == DRM_MODE_DPMS_SUSPEND || mode == DRM_MODE_DPMS_OFF) - dpms_ctrl |= 0x00000004; - - nv_wait(device, 0x61a004 + (or * 0x0800), 0x80000000, 0x00000000); - nv_mask(device, 0x61a004 + (or * 0x0800), 0xc000007f, dpms_ctrl); - nv_wait(device, 0x61a004 + (or * 0x0800), 0x80000000, 0x00000000); -} - -static bool -nvd0_dac_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (nv_connector && nv_connector->native_mode) { - if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { - int id = adjusted_mode->base.id; - *adjusted_mode = *nv_connector->native_mode; - adjusted_mode->base.id = id; - } - } - - return true; -} - -static void -nvd0_dac_commit(struct drm_encoder *encoder) -{ -} - -static void -nvd0_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - u32 syncs, magic, *push; - - syncs = 0x00000001; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - syncs |= 0x00000008; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - syncs |= 0x00000010; - - magic = 0x31ec6000 | (nv_crtc->index << 25); - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - magic |= 0x00000001; - - nvd0_dac_dpms(encoder, DRM_MODE_DPMS_ON); - - push = evo_wait(encoder->dev, EVO_MASTER, 8); - if (push) { - evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); - evo_data(push, syncs); - evo_data(push, magic); - evo_mthd(push, 0x0180 + (nv_encoder->or * 0x020), 2); - evo_data(push, 1 << nv_crtc->index); - evo_data(push, 0x00ff); - evo_kick(push, encoder->dev, EVO_MASTER); - } - - nv_encoder->crtc = encoder->crtc; -} - -static void -nvd0_dac_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - u32 *push; - - if (nv_encoder->crtc) { - nvd0_crtc_prepare(nv_encoder->crtc); - - push = evo_wait(dev, EVO_MASTER, 4); - if (push) { - evo_mthd(push, 0x0180 + (nv_encoder->or * 0x20), 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - evo_kick(push, dev, EVO_MASTER); - } - - nv_encoder->crtc = NULL; - } -} - -static enum drm_connector_status -nvd0_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector) -{ - enum drm_connector_status status = connector_status_disconnected; - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - int or = nv_encoder->or; - u32 load; - - nv_wr32(device, 0x61a00c + (or * 0x800), 0x00100000); - udelay(9500); - nv_wr32(device, 0x61a00c + (or * 0x800), 0x80000000); - - load = nv_rd32(device, 0x61a00c + (or * 0x800)); - if ((load & 0x38000000) == 0x38000000) - status = connector_status_connected; - - nv_wr32(device, 0x61a00c + (or * 0x800), 0x00000000); - return status; -} - -static void -nvd0_dac_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); - kfree(encoder); -} - -static const struct drm_encoder_helper_funcs nvd0_dac_hfunc = { - .dpms = nvd0_dac_dpms, - .mode_fixup = nvd0_dac_mode_fixup, - .prepare = nvd0_dac_disconnect, - .commit = nvd0_dac_commit, - .mode_set = nvd0_dac_mode_set, - .disable = nvd0_dac_disconnect, - .get_crtc = nvd0_display_crtc_get, - .detect = nvd0_dac_detect -}; - -static const struct drm_encoder_funcs nvd0_dac_func = { - .destroy = nvd0_dac_destroy, -}; - -static int -nvd0_dac_create(struct drm_connector *connector, struct dcb_output *dcbe) -{ - struct drm_device *dev = connector->dev; - struct nouveau_encoder *nv_encoder; - struct drm_encoder *encoder; - - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; - - encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; - drm_encoder_init(dev, encoder, &nvd0_dac_func, DRM_MODE_ENCODER_DAC); - drm_encoder_helper_add(encoder, &nvd0_dac_hfunc); - - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} - -/****************************************************************************** - * Audio - *****************************************************************************/ -static void -nvd0_audio_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector; - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - int i, or = nv_encoder->or * 0x30; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (!drm_detect_monitor_audio(nv_connector->edid)) - return; - - nv_mask(device, 0x10ec10 + or, 0x80000003, 0x80000001); - - drm_edid_to_eld(&nv_connector->base, nv_connector->edid); - if (nv_connector->base.eld[0]) { - u8 *eld = nv_connector->base.eld; - - for (i = 0; i < eld[2] * 4; i++) - nv_wr32(device, 0x10ec00 + or, (i << 8) | eld[i]); - for (i = eld[2] * 4; i < 0x60; i++) - nv_wr32(device, 0x10ec00 + or, (i << 8) | 0x00); - - nv_mask(device, 0x10ec10 + or, 0x80000002, 0x80000002); - } -} - -static void -nvd0_audio_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - int or = nv_encoder->or * 0x30; - - nv_mask(device, 0x10ec10 + or, 0x80000003, 0x80000000); -} - -/****************************************************************************** - * HDMI - *****************************************************************************/ -static void -nvd0_hdmi_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nouveau_connector *nv_connector; - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - int head = nv_crtc->index * 0x800; - u32 rekey = 56; /* binary driver, and tegra constant */ - u32 max_ac_packet; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (!drm_detect_hdmi_monitor(nv_connector->edid)) - return; - - max_ac_packet = mode->htotal - mode->hdisplay; - max_ac_packet -= rekey; - max_ac_packet -= 18; /* constant from tegra */ - max_ac_packet /= 32; - - /* AVI InfoFrame */ - nv_mask(device, 0x616714 + head, 0x00000001, 0x00000000); - nv_wr32(device, 0x61671c + head, 0x000d0282); - nv_wr32(device, 0x616720 + head, 0x0000006f); - nv_wr32(device, 0x616724 + head, 0x00000000); - nv_wr32(device, 0x616728 + head, 0x00000000); - nv_wr32(device, 0x61672c + head, 0x00000000); - nv_mask(device, 0x616714 + head, 0x00000001, 0x00000001); - - /* ??? InfoFrame? */ - nv_mask(device, 0x6167a4 + head, 0x00000001, 0x00000000); - nv_wr32(device, 0x6167ac + head, 0x00000010); - nv_mask(device, 0x6167a4 + head, 0x00000001, 0x00000001); - - /* HDMI_CTRL */ - nv_mask(device, 0x616798 + head, 0x401f007f, 0x40000000 | rekey | - max_ac_packet << 16); - - /* NFI, audio doesn't work without it though.. */ - nv_mask(device, 0x616548 + head, 0x00000070, 0x00000000); - - nvd0_audio_mode_set(encoder, mode); -} - -static void -nvd0_hdmi_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc); - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - int head = nv_crtc->index * 0x800; - - nvd0_audio_disconnect(encoder); - - nv_mask(device, 0x616798 + head, 0x40000000, 0x00000000); - nv_mask(device, 0x6167a4 + head, 0x00000001, 0x00000000); - nv_mask(device, 0x616714 + head, 0x00000001, 0x00000000); -} - -/****************************************************************************** - * SOR - *****************************************************************************/ -static inline u32 -nvd0_sor_dp_lane_map(struct drm_device *dev, struct dcb_output *dcb, u8 lane) -{ - static const u8 nvd0[] = { 16, 8, 0, 24 }; - return nvd0[lane]; -} - -static void -nvd0_sor_dp_train_set(struct drm_device *dev, struct dcb_output *dcb, u8 pattern) -{ - struct nouveau_device *device = nouveau_dev(dev); - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 loff = (or * 0x800) + (link * 0x80); - nv_mask(device, 0x61c110 + loff, 0x0f0f0f0f, 0x01010101 * pattern); -} - -static void -nvd0_sor_dp_train_adj(struct drm_device *dev, struct dcb_output *dcb, - u8 lane, u8 swing, u8 preem) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 loff = (or * 0x800) + (link * 0x80); - u32 shift = nvd0_sor_dp_lane_map(dev, dcb, lane); - u32 mask = 0x000000ff << shift; - u8 *table, *entry, *config = NULL; - - switch (swing) { - case 0: preem += 0; break; - case 1: preem += 4; break; - case 2: preem += 7; break; - case 3: preem += 9; break; - } - - table = nouveau_dp_bios_data(dev, dcb, &entry); - if (table) { - if (table[0] == 0x30) { - config = entry + table[4]; - config += table[5] * preem; - } else - if (table[0] == 0x40) { - config = table + table[1]; - config += table[2] * table[3]; - config += table[6] * preem; - } - } - - if (!config) { - NV_ERROR(drm, "PDISP: unsupported DP table for chipset\n"); - return; - } - - nv_mask(device, 0x61c118 + loff, mask, config[1] << shift); - nv_mask(device, 0x61c120 + loff, mask, config[2] << shift); - nv_mask(device, 0x61c130 + loff, 0x0000ff00, config[3] << 8); - nv_mask(device, 0x61c13c + loff, 0x00000000, 0x00000000); -} - -static void -nvd0_sor_dp_link_set(struct drm_device *dev, struct dcb_output *dcb, int crtc, - int link_nr, u32 link_bw, bool enhframe) -{ - struct nouveau_device *device = nouveau_dev(dev); - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 loff = (or * 0x800) + (link * 0x80); - const u32 soff = (or * 0x800); - u32 dpctrl = nv_rd32(device, 0x61c10c + loff) & ~0x001f4000; - u32 clksor = nv_rd32(device, 0x612300 + soff) & ~0x007c0000; - u32 script = 0x0000, lane_mask = 0; - u8 *table, *entry; - int i; - - link_bw /= 27000; - - table = nouveau_dp_bios_data(dev, dcb, &entry); - if (table) { - if (table[0] == 0x30) entry = ROMPTR(dev, entry[10]); - else if (table[0] == 0x40) entry = ROMPTR(dev, entry[9]); - else entry = NULL; - - while (entry) { - if (entry[0] >= link_bw) - break; - entry += 3; - } - - nouveau_bios_run_init_table(dev, script, dcb, crtc); - } - - clksor |= link_bw << 18; - dpctrl |= ((1 << link_nr) - 1) << 16; - if (enhframe) - dpctrl |= 0x00004000; - - for (i = 0; i < link_nr; i++) - lane_mask |= 1 << (nvd0_sor_dp_lane_map(dev, dcb, i) >> 3); - - nv_wr32(device, 0x612300 + soff, clksor); - nv_wr32(device, 0x61c10c + loff, dpctrl); - nv_mask(device, 0x61c130 + loff, 0x0000000f, lane_mask); -} - -static void -nvd0_sor_dp_link_get(struct drm_device *dev, struct dcb_output *dcb, - u32 *link_nr, u32 *link_bw) -{ - struct nouveau_device *device = nouveau_dev(dev); - const u32 or = ffs(dcb->or) - 1, link = !(dcb->sorconf.link & 1); - const u32 loff = (or * 0x800) + (link * 0x80); - const u32 soff = (or * 0x800); - u32 dpctrl = nv_rd32(device, 0x61c10c + loff) & 0x000f0000; - u32 clksor = nv_rd32(device, 0x612300 + soff); - - if (dpctrl > 0x00030000) *link_nr = 4; - else if (dpctrl > 0x00010000) *link_nr = 2; - else *link_nr = 1; - - *link_bw = (clksor & 0x007c0000) >> 18; - *link_bw *= 27000; -} - -static void -nvd0_sor_dp_calc_tu(struct drm_device *dev, struct dcb_output *dcb, - u32 crtc, u32 datarate) -{ - struct nouveau_device *device = nouveau_dev(dev); - const u32 symbol = 100000; - const u32 TU = 64; - u32 link_nr, link_bw; - u64 ratio, value; - - nvd0_sor_dp_link_get(dev, dcb, &link_nr, &link_bw); - - ratio = datarate; - ratio *= symbol; - do_div(ratio, link_nr * link_bw); - - value = (symbol - ratio) * TU; - value *= ratio; - do_div(value, symbol); - do_div(value, symbol); - - value += 5; - value |= 0x08000000; - - nv_wr32(device, 0x616610 + (crtc * 0x800), value); -} - -static void -nvd0_sor_dpms(struct drm_encoder *encoder, int mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - struct nouveau_device *device = nouveau_dev(dev); - struct drm_encoder *partner; - int or = nv_encoder->or; - u32 dpms_ctrl; - - nv_encoder->last_dpms = mode; - - list_for_each_entry(partner, &dev->mode_config.encoder_list, head) { - struct nouveau_encoder *nv_partner = nouveau_encoder(partner); - - if (partner->encoder_type != DRM_MODE_ENCODER_TMDS) - continue; - - if (nv_partner != nv_encoder && - nv_partner->dcb->or == nv_encoder->dcb->or) { - if (nv_partner->last_dpms == DRM_MODE_DPMS_ON) - return; - break; - } - } - - dpms_ctrl = (mode == DRM_MODE_DPMS_ON); - dpms_ctrl |= 0x80000000; - - nv_wait(device, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); - nv_mask(device, 0x61c004 + (or * 0x0800), 0x80000001, dpms_ctrl); - nv_wait(device, 0x61c004 + (or * 0x0800), 0x80000000, 0x00000000); - nv_wait(device, 0x61c030 + (or * 0x0800), 0x10000000, 0x00000000); - - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - struct dp_train_func func = { - .link_set = nvd0_sor_dp_link_set, - .train_set = nvd0_sor_dp_train_set, - .train_adj = nvd0_sor_dp_train_adj - }; - - nouveau_dp_dpms(encoder, mode, nv_encoder->dp.datarate, &func); - } -} - -static bool -nvd0_sor_mode_fixup(struct drm_encoder *encoder, - const struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_connector *nv_connector; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - if (nv_connector && nv_connector->native_mode) { - if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) { - int id = adjusted_mode->base.id; - *adjusted_mode = *nv_connector->native_mode; - adjusted_mode->base.id = id; - } - } - - return true; -} - -static void -nvd0_sor_disconnect(struct drm_encoder *encoder) -{ - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct drm_device *dev = encoder->dev; - u32 *push; - - if (nv_encoder->crtc) { - nvd0_crtc_prepare(nv_encoder->crtc); - - push = evo_wait(dev, EVO_MASTER, 4); - if (push) { - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x20), 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0080, 1); - evo_data(push, 0x00000000); - evo_kick(push, dev, EVO_MASTER); - } - - nvd0_hdmi_disconnect(encoder); - - nv_encoder->crtc = NULL; - nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; - } -} - -static void -nvd0_sor_prepare(struct drm_encoder *encoder) -{ - nvd0_sor_disconnect(encoder); - if (nouveau_encoder(encoder)->dcb->type == DCB_OUTPUT_DP) - evo_sync(encoder->dev, EVO_MASTER); -} - -static void -nvd0_sor_commit(struct drm_encoder *encoder) -{ -} - -static void -nvd0_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode, - struct drm_display_mode *mode) -{ - struct drm_device *dev = encoder->dev; - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc); - struct nouveau_connector *nv_connector; - struct nvbios *bios = &drm->vbios; - u32 mode_ctrl = (1 << nv_crtc->index); - u32 syncs, magic, *push; - u32 or_config; - - syncs = 0x00000001; - if (mode->flags & DRM_MODE_FLAG_NHSYNC) - syncs |= 0x00000008; - if (mode->flags & DRM_MODE_FLAG_NVSYNC) - syncs |= 0x00000010; - - magic = 0x31ec6000 | (nv_crtc->index << 25); - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - magic |= 0x00000001; - - nv_connector = nouveau_encoder_connector_get(nv_encoder); - switch (nv_encoder->dcb->type) { - case DCB_OUTPUT_TMDS: - if (nv_encoder->dcb->sorconf.link & 1) { - if (mode->clock < 165000) - mode_ctrl |= 0x00000100; - else - mode_ctrl |= 0x00000500; - } else { - mode_ctrl |= 0x00000200; - } - - or_config = (mode_ctrl & 0x00000f00) >> 8; - if (mode->clock >= 165000) - or_config |= 0x0100; - - nvd0_hdmi_mode_set(encoder, mode); - break; - case DCB_OUTPUT_LVDS: - or_config = (mode_ctrl & 0x00000f00) >> 8; - if (bios->fp_no_ddc) { - if (bios->fp.dual_link) - or_config |= 0x0100; - if (bios->fp.if_is_24bit) - or_config |= 0x0200; - } else { - if (nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) { - if (((u8 *)nv_connector->edid)[121] == 2) - or_config |= 0x0100; - } else - if (mode->clock >= bios->fp.duallink_transition_clk) { - or_config |= 0x0100; - } - - if (or_config & 0x0100) { - if (bios->fp.strapless_is_24bit & 2) - or_config |= 0x0200; - } else { - if (bios->fp.strapless_is_24bit & 1) - or_config |= 0x0200; - } - - if (nv_connector->base.display_info.bpc == 8) - or_config |= 0x0200; - - } - break; - case DCB_OUTPUT_DP: - if (nv_connector->base.display_info.bpc == 6) { - nv_encoder->dp.datarate = mode->clock * 18 / 8; - syncs |= 0x00000002 << 6; - } else { - nv_encoder->dp.datarate = mode->clock * 24 / 8; - syncs |= 0x00000005 << 6; - } - - if (nv_encoder->dcb->sorconf.link & 1) - mode_ctrl |= 0x00000800; - else - mode_ctrl |= 0x00000900; - - or_config = (mode_ctrl & 0x00000f00) >> 8; - break; - default: - BUG_ON(1); - break; - } - - nvd0_sor_dpms(encoder, DRM_MODE_DPMS_ON); - - if (nv_encoder->dcb->type == DCB_OUTPUT_DP) { - nvd0_sor_dp_calc_tu(dev, nv_encoder->dcb, nv_crtc->index, - nv_encoder->dp.datarate); - } - - push = evo_wait(dev, EVO_MASTER, 8); - if (push) { - evo_mthd(push, 0x0404 + (nv_crtc->index * 0x300), 2); - evo_data(push, syncs); - evo_data(push, magic); - evo_mthd(push, 0x0200 + (nv_encoder->or * 0x020), 2); - evo_data(push, mode_ctrl); - evo_data(push, or_config); - evo_kick(push, dev, EVO_MASTER); - } - - nv_encoder->crtc = encoder->crtc; -} - -static void -nvd0_sor_destroy(struct drm_encoder *encoder) -{ - drm_encoder_cleanup(encoder); - kfree(encoder); -} - -static const struct drm_encoder_helper_funcs nvd0_sor_hfunc = { - .dpms = nvd0_sor_dpms, - .mode_fixup = nvd0_sor_mode_fixup, - .prepare = nvd0_sor_prepare, - .commit = nvd0_sor_commit, - .mode_set = nvd0_sor_mode_set, - .disable = nvd0_sor_disconnect, - .get_crtc = nvd0_display_crtc_get, -}; - -static const struct drm_encoder_funcs nvd0_sor_func = { - .destroy = nvd0_sor_destroy, -}; - -static int -nvd0_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) -{ - struct drm_device *dev = connector->dev; - struct nouveau_encoder *nv_encoder; - struct drm_encoder *encoder; - - nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL); - if (!nv_encoder) - return -ENOMEM; - nv_encoder->dcb = dcbe; - nv_encoder->or = ffs(dcbe->or) - 1; - nv_encoder->last_dpms = DRM_MODE_DPMS_OFF; - - encoder = to_drm_encoder(nv_encoder); - encoder->possible_crtcs = dcbe->heads; - encoder->possible_clones = 0; - drm_encoder_init(dev, encoder, &nvd0_sor_func, DRM_MODE_ENCODER_TMDS); - drm_encoder_helper_add(encoder, &nvd0_sor_hfunc); - - drm_mode_connector_attach_encoder(connector, encoder); - return 0; -} - -/****************************************************************************** - * IRQ - *****************************************************************************/ -static struct dcb_output * -lookup_dcb(struct drm_device *dev, int id, u32 mc) -{ - struct nouveau_drm *drm = nouveau_drm(dev); - int type, or, i, link = -1; - - if (id < 4) { - type = DCB_OUTPUT_ANALOG; - or = id; - } else { - switch (mc & 0x00000f00) { - case 0x00000000: link = 0; type = DCB_OUTPUT_LVDS; break; - case 0x00000100: link = 0; type = DCB_OUTPUT_TMDS; break; - case 0x00000200: link = 1; type = DCB_OUTPUT_TMDS; break; - case 0x00000500: link = 0; type = DCB_OUTPUT_TMDS; break; - case 0x00000800: link = 0; type = DCB_OUTPUT_DP; break; - case 0x00000900: link = 1; type = DCB_OUTPUT_DP; break; - default: - NV_ERROR(drm, "PDISP: unknown SOR mc 0x%08x\n", mc); - return NULL; - } - - or = id - 4; - } - - for (i = 0; i < drm->vbios.dcb.entries; i++) { - struct dcb_output *dcb = &drm->vbios.dcb.entry[i]; - if (dcb->type == type && (dcb->or & (1 << or)) && - (link < 0 || link == !(dcb->sorconf.link & 1))) - return dcb; - } - - NV_ERROR(drm, "PDISP: DCB for %d/0x%08x not found\n", id, mc); - return NULL; -} - -static void -nvd0_display_unk1_handler(struct drm_device *dev, u32 crtc, u32 mask) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct dcb_output *dcb; - int i; - - for (i = 0; mask && i < 8; i++) { - u32 mcc = nv_rd32(device, 0x640180 + (i * 0x20)); - if (!(mcc & (1 << crtc))) - continue; - - dcb = lookup_dcb(dev, i, mcc); - if (!dcb) - continue; - - nouveau_bios_run_display_table(dev, 0x0000, -1, dcb, crtc); - } - - nv_wr32(device, 0x6101d4, 0x00000000); - nv_wr32(device, 0x6109d4, 0x00000000); - nv_wr32(device, 0x6101d0, 0x80000000); -} - -static void -nvd0_display_unk2_handler(struct drm_device *dev, u32 crtc, u32 mask) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct dcb_output *dcb; - u32 or, tmp, pclk; - int i; - - for (i = 0; mask && i < 8; i++) { - u32 mcc = nv_rd32(device, 0x640180 + (i * 0x20)); - if (!(mcc & (1 << crtc))) - continue; - - dcb = lookup_dcb(dev, i, mcc); - if (!dcb) - continue; - - nouveau_bios_run_display_table(dev, 0x0000, -2, dcb, crtc); - } - - pclk = nv_rd32(device, 0x660450 + (crtc * 0x300)) / 1000; - NV_DEBUG(drm, "PDISP: crtc %d pclk %d mask 0x%08x\n", - crtc, pclk, mask); - if (pclk && (mask & 0x00010000)) { - nv50_crtc_set_clock(dev, crtc, pclk); - } - - for (i = 0; mask && i < 8; i++) { - u32 mcp = nv_rd32(device, 0x660180 + (i * 0x20)); - u32 cfg = nv_rd32(device, 0x660184 + (i * 0x20)); - if (!(mcp & (1 << crtc))) - continue; - - dcb = lookup_dcb(dev, i, mcp); - if (!dcb) - continue; - or = ffs(dcb->or) - 1; - - nouveau_bios_run_display_table(dev, cfg, pclk, dcb, crtc); - - nv_wr32(device, 0x612200 + (crtc * 0x800), 0x00000000); - switch (dcb->type) { - case DCB_OUTPUT_ANALOG: - nv_wr32(device, 0x612280 + (or * 0x800), 0x00000000); - break; - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - if (cfg & 0x00000100) - tmp = 0x00000101; - else - tmp = 0x00000000; - - nv_mask(device, 0x612300 + (or * 0x800), 0x00000707, tmp); - break; - default: - break; - } - - break; - } - - nv_wr32(device, 0x6101d4, 0x00000000); - nv_wr32(device, 0x6109d4, 0x00000000); - nv_wr32(device, 0x6101d0, 0x80000000); -} - -static void -nvd0_display_unk4_handler(struct drm_device *dev, u32 crtc, u32 mask) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct dcb_output *dcb; - int pclk, i; - - pclk = nv_rd32(device, 0x660450 + (crtc * 0x300)) / 1000; - - for (i = 0; mask && i < 8; i++) { - u32 mcp = nv_rd32(device, 0x660180 + (i * 0x20)); - u32 cfg = nv_rd32(device, 0x660184 + (i * 0x20)); - if (!(mcp & (1 << crtc))) - continue; - - dcb = lookup_dcb(dev, i, mcp); - if (!dcb) - continue; - - nouveau_bios_run_display_table(dev, cfg, -pclk, dcb, crtc); - } - - nv_wr32(device, 0x6101d4, 0x00000000); - nv_wr32(device, 0x6109d4, 0x00000000); - nv_wr32(device, 0x6101d0, 0x80000000); -} - -static void -nvd0_display_bh(unsigned long data) -{ - struct drm_device *dev = (struct drm_device *)data; - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nvd0_display *disp = nvd0_display(dev); - u32 mask = 0, crtc = ~0; - int i; - - if (drm_debug & (DRM_UT_DRIVER | DRM_UT_KMS)) { - NV_INFO(drm, "PDISP: modeset req %d\n", disp->modeset); - NV_INFO(drm, " STAT: 0x%08x 0x%08x 0x%08x\n", - nv_rd32(device, 0x6101d0), - nv_rd32(device, 0x6101d4), nv_rd32(device, 0x6109d4)); - for (i = 0; i < 8; i++) { - NV_INFO(drm, " %s%d: 0x%08x 0x%08x\n", - i < 4 ? "DAC" : "SOR", i, - nv_rd32(device, 0x640180 + (i * 0x20)), - nv_rd32(device, 0x660180 + (i * 0x20))); - } - } - - while (!mask && ++crtc < dev->mode_config.num_crtc) - mask = nv_rd32(device, 0x6101d4 + (crtc * 0x800)); - - if (disp->modeset & 0x00000001) - nvd0_display_unk1_handler(dev, crtc, mask); - if (disp->modeset & 0x00000002) - nvd0_display_unk2_handler(dev, crtc, mask); - if (disp->modeset & 0x00000004) - nvd0_display_unk4_handler(dev, crtc, mask); -} - -void -nvd0_display_intr(struct drm_device *dev) -{ - struct nvd0_display *disp = nvd0_display(dev); - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - u32 intr = nv_rd32(device, 0x610088); - - if (intr & 0x00000001) { - u32 stat = nv_rd32(device, 0x61008c); - nv_wr32(device, 0x61008c, stat); - intr &= ~0x00000001; - } - - if (intr & 0x00000002) { - u32 stat = nv_rd32(device, 0x61009c); - int chid = ffs(stat) - 1; - if (chid >= 0) { - u32 mthd = nv_rd32(device, 0x6101f0 + (chid * 12)); - u32 data = nv_rd32(device, 0x6101f4 + (chid * 12)); - u32 unkn = nv_rd32(device, 0x6101f8 + (chid * 12)); - - NV_INFO(drm, "EvoCh: chid %d mthd 0x%04x data 0x%08x " - "0x%08x 0x%08x\n", - chid, (mthd & 0x0000ffc), data, mthd, unkn); - nv_wr32(device, 0x61009c, (1 << chid)); - nv_wr32(device, 0x6101f0 + (chid * 12), 0x90000000); - } - - intr &= ~0x00000002; - } - - if (intr & 0x00100000) { - u32 stat = nv_rd32(device, 0x6100ac); - - if (stat & 0x00000007) { - disp->modeset = stat; - tasklet_schedule(&disp->tasklet); - - nv_wr32(device, 0x6100ac, (stat & 0x00000007)); - stat &= ~0x00000007; - } - - if (stat) { - NV_INFO(drm, "PDISP: unknown intr24 0x%08x\n", stat); - nv_wr32(device, 0x6100ac, stat); - } - - intr &= ~0x00100000; - } - - intr &= ~0x0f000000; /* vblank, handled in core */ - if (intr) - NV_INFO(drm, "PDISP: unknown intr 0x%08x\n", intr); -} - -/****************************************************************************** - * Init - *****************************************************************************/ -void -nvd0_display_fini(struct drm_device *dev) -{ - int i; - - /* fini cursors + overlays + flips */ - for (i = 1; i >= 0; i--) { - evo_fini_pio(dev, EVO_CURS(i)); - evo_fini_pio(dev, EVO_OIMM(i)); - evo_fini_dma(dev, EVO_OVLY(i)); - evo_fini_dma(dev, EVO_FLIP(i)); - } - - /* fini master */ - evo_fini_dma(dev, EVO_MASTER); -} - -int -nvd0_display_init(struct drm_device *dev) -{ - struct nvd0_display *disp = nvd0_display(dev); - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - int ret, i; - u32 *push; - - if (nv_rd32(device, 0x6100ac) & 0x00000100) { - nv_wr32(device, 0x6100ac, 0x00000100); - nv_mask(device, 0x6194e8, 0x00000001, 0x00000000); - if (!nv_wait(device, 0x6194e8, 0x00000002, 0x00000000)) { - NV_ERROR(drm, "PDISP: 0x6194e8 0x%08x\n", - nv_rd32(device, 0x6194e8)); - return -EBUSY; - } - } - - /* nfi what these are exactly, i do know that SOR_MODE_CTRL won't - * work at all unless you do the SOR part below. - */ - for (i = 0; i < 3; i++) { - u32 dac = nv_rd32(device, 0x61a000 + (i * 0x800)); - nv_wr32(device, 0x6101c0 + (i * 0x800), dac); - } - - for (i = 0; i < 4; i++) { - u32 sor = nv_rd32(device, 0x61c000 + (i * 0x800)); - nv_wr32(device, 0x6301c4 + (i * 0x800), sor); - } - - for (i = 0; i < dev->mode_config.num_crtc; i++) { - u32 crtc0 = nv_rd32(device, 0x616104 + (i * 0x800)); - u32 crtc1 = nv_rd32(device, 0x616108 + (i * 0x800)); - u32 crtc2 = nv_rd32(device, 0x61610c + (i * 0x800)); - nv_wr32(device, 0x6101b4 + (i * 0x800), crtc0); - nv_wr32(device, 0x6101b8 + (i * 0x800), crtc1); - nv_wr32(device, 0x6101bc + (i * 0x800), crtc2); - } - - /* point at our hash table / objects, enable interrupts */ - nv_wr32(device, 0x610010, (disp->mem->addr >> 8) | 9); - nv_mask(device, 0x6100b0, 0x00000307, 0x00000307); - - /* init master */ - ret = evo_init_dma(dev, EVO_MASTER); - if (ret) - goto error; - - /* init flips + overlays + cursors */ - for (i = 0; i < dev->mode_config.num_crtc; i++) { - if ((ret = evo_init_dma(dev, EVO_FLIP(i))) || - (ret = evo_init_dma(dev, EVO_OVLY(i))) || - (ret = evo_init_pio(dev, EVO_OIMM(i))) || - (ret = evo_init_pio(dev, EVO_CURS(i)))) - goto error; - } - - push = evo_wait(dev, EVO_MASTER, 32); - if (!push) { - ret = -EBUSY; - goto error; - } - evo_mthd(push, 0x0088, 1); - evo_data(push, NvEvoSync); - evo_mthd(push, 0x0084, 1); - evo_data(push, 0x00000000); - evo_mthd(push, 0x0084, 1); - evo_data(push, 0x80000000); - evo_mthd(push, 0x008c, 1); - evo_data(push, 0x00000000); - evo_kick(push, dev, EVO_MASTER); - -error: - if (ret) - nvd0_display_fini(dev); - return ret; -} - -void -nvd0_display_destroy(struct drm_device *dev) -{ - struct nvd0_display *disp = nvd0_display(dev); - struct pci_dev *pdev = dev->pdev; - int i; - - for (i = 0; i < EVO_DMA_NR; i++) { - struct evo *evo = &disp->evo[i]; - pci_free_consistent(pdev, PAGE_SIZE, evo->ptr, evo->handle); - } - - nouveau_gpuobj_ref(NULL, &disp->mem); - nouveau_bo_unmap(disp->sync); - nouveau_bo_ref(NULL, &disp->sync); - - nouveau_display(dev)->priv = NULL; - kfree(disp); -} - -int -nvd0_display_create(struct drm_device *dev) -{ - struct nouveau_device *device = nouveau_dev(dev); - struct nouveau_drm *drm = nouveau_drm(dev); - struct nouveau_bar *bar = nouveau_bar(device); - struct nouveau_fb *pfb = nouveau_fb(device); - struct dcb_table *dcb = &drm->vbios.dcb; - struct drm_connector *connector, *tmp; - struct pci_dev *pdev = dev->pdev; - struct nvd0_display *disp; - struct dcb_output *dcbe; - int crtcs, ret, i; - - disp = kzalloc(sizeof(*disp), GFP_KERNEL); - if (!disp) - return -ENOMEM; - - nouveau_display(dev)->priv = disp; - nouveau_display(dev)->dtor = nvd0_display_destroy; - nouveau_display(dev)->init = nvd0_display_init; - nouveau_display(dev)->fini = nvd0_display_fini; - - /* create crtc objects to represent the hw heads */ - crtcs = nv_rd32(device, 0x022448); - for (i = 0; i < crtcs; i++) { - ret = nvd0_crtc_create(dev, i); - if (ret) - goto out; - } - - /* create encoder/connector objects based on VBIOS DCB table */ - for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++) { - connector = nouveau_connector_create(dev, dcbe->connector); - if (IS_ERR(connector)) - continue; - - if (dcbe->location != DCB_LOC_ON_CHIP) { - NV_WARN(drm, "skipping off-chip encoder %d/%d\n", - dcbe->type, ffs(dcbe->or) - 1); - continue; - } - - switch (dcbe->type) { - case DCB_OUTPUT_TMDS: - case DCB_OUTPUT_LVDS: - case DCB_OUTPUT_DP: - nvd0_sor_create(connector, dcbe); - break; - case DCB_OUTPUT_ANALOG: - nvd0_dac_create(connector, dcbe); - break; - default: - NV_WARN(drm, "skipping unsupported encoder %d/%d\n", - dcbe->type, ffs(dcbe->or) - 1); - continue; - } - } - - /* cull any connectors we created that don't have an encoder */ - list_for_each_entry_safe(connector, tmp, &dev->mode_config.connector_list, head) { - if (connector->encoder_ids[0]) - continue; - - NV_WARN(drm, "%s has no encoders, removing\n", - drm_get_connector_name(connector)); - connector->funcs->destroy(connector); - } - - /* setup interrupt handling */ - tasklet_init(&disp->tasklet, nvd0_display_bh, (unsigned long)dev); - - /* small shared memory area we use for notifiers and semaphores */ - ret = nouveau_bo_new(dev, 4096, 0x1000, TTM_PL_FLAG_VRAM, - 0, 0x0000, NULL, &disp->sync); - if (!ret) { - ret = nouveau_bo_pin(disp->sync, TTM_PL_FLAG_VRAM); - if (!ret) - ret = nouveau_bo_map(disp->sync); - if (ret) - nouveau_bo_ref(NULL, &disp->sync); - } - - if (ret) - goto out; - - /* hash table and dma objects for the memory areas we care about */ - ret = nouveau_gpuobj_new(nv_object(device), NULL, 0x4000, 0x10000, - NVOBJ_FLAG_ZERO_ALLOC, &disp->mem); - if (ret) - goto out; - - /* create evo dma channels */ - for (i = 0; i < EVO_DMA_NR; i++) { - struct evo *evo = &disp->evo[i]; - u64 offset = disp->sync->bo.offset; - u32 dmao = 0x1000 + (i * 0x100); - u32 hash = 0x0000 + (i * 0x040); - - evo->idx = i; - evo->sem.offset = EVO_SYNC(evo->idx, 0x00); - evo->ptr = pci_alloc_consistent(pdev, PAGE_SIZE, &evo->handle); - if (!evo->ptr) { - ret = -ENOMEM; - goto out; - } - - nv_wo32(disp->mem, dmao + 0x00, 0x00000049); - nv_wo32(disp->mem, dmao + 0x04, (offset + 0x0000) >> 8); - nv_wo32(disp->mem, dmao + 0x08, (offset + 0x0fff) >> 8); - nv_wo32(disp->mem, dmao + 0x0c, 0x00000000); - nv_wo32(disp->mem, dmao + 0x10, 0x00000000); - nv_wo32(disp->mem, dmao + 0x14, 0x00000000); - nv_wo32(disp->mem, hash + 0x00, NvEvoSync); - nv_wo32(disp->mem, hash + 0x04, 0x00000001 | (i << 27) | - ((dmao + 0x00) << 9)); - - nv_wo32(disp->mem, dmao + 0x20, 0x00000049); - nv_wo32(disp->mem, dmao + 0x24, 0x00000000); - nv_wo32(disp->mem, dmao + 0x28, (pfb->ram.size - 1) >> 8); - nv_wo32(disp->mem, dmao + 0x2c, 0x00000000); - nv_wo32(disp->mem, dmao + 0x30, 0x00000000); - nv_wo32(disp->mem, dmao + 0x34, 0x00000000); - nv_wo32(disp->mem, hash + 0x08, NvEvoVRAM); - nv_wo32(disp->mem, hash + 0x0c, 0x00000001 | (i << 27) | - ((dmao + 0x20) << 9)); - - nv_wo32(disp->mem, dmao + 0x40, 0x00000009); - nv_wo32(disp->mem, dmao + 0x44, 0x00000000); - nv_wo32(disp->mem, dmao + 0x48, (pfb->ram.size - 1) >> 8); - nv_wo32(disp->mem, dmao + 0x4c, 0x00000000); - nv_wo32(disp->mem, dmao + 0x50, 0x00000000); - nv_wo32(disp->mem, dmao + 0x54, 0x00000000); - nv_wo32(disp->mem, hash + 0x10, NvEvoVRAM_LP); - nv_wo32(disp->mem, hash + 0x14, 0x00000001 | (i << 27) | - ((dmao + 0x40) << 9)); - - nv_wo32(disp->mem, dmao + 0x60, 0x0fe00009); - nv_wo32(disp->mem, dmao + 0x64, 0x00000000); - nv_wo32(disp->mem, dmao + 0x68, (pfb->ram.size - 1) >> 8); - nv_wo32(disp->mem, dmao + 0x6c, 0x00000000); - nv_wo32(disp->mem, dmao + 0x70, 0x00000000); - nv_wo32(disp->mem, dmao + 0x74, 0x00000000); - nv_wo32(disp->mem, hash + 0x18, NvEvoFB32); - nv_wo32(disp->mem, hash + 0x1c, 0x00000001 | (i << 27) | - ((dmao + 0x60) << 9)); - } - - bar->flush(bar); - -out: - if (ret) - nvd0_display_destroy(dev); - return ret; -} diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c index 53b98520ebd..074410371e2 100644 --- a/drivers/gpu/drm/tegra/dc.c +++ b/drivers/gpu/drm/tegra/dc.c @@ -218,7 +218,7 @@ static int tegra_crtc_mode_set(struct drm_crtc *crtc, } bpp = crtc->fb->bits_per_pixel / 8; - win.stride = win.outw * bpp; + win.stride = crtc->fb->pitches[0]; /* program window registers */ value = tegra_dc_readl(dc, DC_CMD_DISPLAY_WINDOW_HEADER); @@ -818,6 +818,7 @@ static int tegra_dc_remove(struct platform_device *pdev) } static struct of_device_id tegra_dc_of_match[] = { + { .compatible = "nvidia,tegra30-dc", }, { .compatible = "nvidia,tegra20-dc", }, { }, }; diff --git a/drivers/gpu/drm/tegra/hdmi.c b/drivers/gpu/drm/tegra/hdmi.c index 58f55dc6d59..ab4016412bb 100644 --- a/drivers/gpu/drm/tegra/hdmi.c +++ b/drivers/gpu/drm/tegra/hdmi.c @@ -1318,8 +1318,8 @@ static int tegra_hdmi_remove(struct platform_device *pdev) } static struct of_device_id tegra_hdmi_of_match[] = { - { .compatible = "nvidia,tegra20-hdmi", }, { .compatible = "nvidia,tegra30-hdmi", }, + { .compatible = "nvidia,tegra20-hdmi", }, { }, }; diff --git a/drivers/gpu/drm/tegra/host1x.c b/drivers/gpu/drm/tegra/host1x.c index 1f728cd7f61..bdb97a564d8 100644 --- a/drivers/gpu/drm/tegra/host1x.c +++ b/drivers/gpu/drm/tegra/host1x.c @@ -68,6 +68,8 @@ static int host1x_parse_dt(struct host1x *host1x) static const char * const compat[] = { "nvidia,tegra20-dc", "nvidia,tegra20-hdmi", + "nvidia,tegra30-dc", + "nvidia,tegra30-hdmi", }; unsigned int i; int err; @@ -268,6 +270,7 @@ int host1x_unregister_client(struct host1x *host1x, } static struct of_device_id tegra_host1x_of_match[] = { + { .compatible = "nvidia,tegra30-host1x", }, { .compatible = "nvidia,tegra20-host1x", }, { }, }; diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c index 7426fe59108..2c54c3d414b 100644 --- a/drivers/gpu/drm/ttm/ttm_bo.c +++ b/drivers/gpu/drm/ttm/ttm_bo.c @@ -696,6 +696,7 @@ static void ttm_bo_release(struct kref *kref) struct ttm_bo_device *bdev = bo->bdev; struct ttm_mem_type_manager *man = &bdev->man[bo->mem.mem_type]; + write_lock(&bdev->vm_lock); if (likely(bo->vm_node != NULL)) { rb_erase(&bo->vm_rb, &bdev->addr_space_rb); drm_mm_put_block(bo->vm_node); @@ -707,18 +708,14 @@ static void ttm_bo_release(struct kref *kref) ttm_mem_io_unlock(man); ttm_bo_cleanup_refs_or_queue(bo); kref_put(&bo->list_kref, ttm_bo_release_list); - write_lock(&bdev->vm_lock); } void ttm_bo_unref(struct ttm_buffer_object **p_bo) { struct ttm_buffer_object *bo = *p_bo; - struct ttm_bo_device *bdev = bo->bdev; *p_bo = NULL; - write_lock(&bdev->vm_lock); kref_put(&bo->kref, ttm_bo_release); - write_unlock(&bdev->vm_lock); } EXPORT_SYMBOL(ttm_bo_unref); diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c index 3ba72dbdc4b..74705f329d9 100644 --- a/drivers/gpu/drm/ttm/ttm_bo_vm.c +++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c @@ -259,8 +259,8 @@ int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma, read_lock(&bdev->vm_lock); bo = ttm_bo_vm_lookup_rb(bdev, vma->vm_pgoff, (vma->vm_end - vma->vm_start) >> PAGE_SHIFT); - if (likely(bo != NULL)) - ttm_bo_reference(bo); + if (likely(bo != NULL) && !kref_get_unless_zero(&bo->kref)) + bo = NULL; read_unlock(&bdev->vm_lock); if (unlikely(bo == NULL)) { diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c index f18eeb45813..58a5f3261c0 100644 --- a/drivers/gpu/drm/ttm/ttm_object.c +++ b/drivers/gpu/drm/ttm/ttm_object.c @@ -157,11 +157,11 @@ int ttm_base_object_init(struct ttm_object_file *tfile, base->refcount_release = refcount_release; base->ref_obj_release = ref_obj_release; base->object_type = object_type; - spin_lock(&tdev->object_lock); kref_init(&base->refcount); - ret = drm_ht_just_insert_please(&tdev->object_hash, - &base->hash, - (unsigned long)base, 31, 0, 0); + spin_lock(&tdev->object_lock); + ret = drm_ht_just_insert_please_rcu(&tdev->object_hash, + &base->hash, + (unsigned long)base, 31, 0, 0); spin_unlock(&tdev->object_lock); if (unlikely(ret != 0)) goto out_err0; @@ -174,7 +174,9 @@ int ttm_base_object_init(struct ttm_object_file *tfile, return 0; out_err1: - (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); + spin_lock(&tdev->object_lock); + (void)drm_ht_remove_item_rcu(&tdev->object_hash, &base->hash); + spin_unlock(&tdev->object_lock); out_err0: return ret; } @@ -187,8 +189,15 @@ static void ttm_release_base(struct kref *kref) struct ttm_object_device *tdev = base->tfile->tdev; spin_lock(&tdev->object_lock); - (void)drm_ht_remove_item(&tdev->object_hash, &base->hash); + (void)drm_ht_remove_item_rcu(&tdev->object_hash, &base->hash); spin_unlock(&tdev->object_lock); + + /* + * Note: We don't use synchronize_rcu() here because it's far + * too slow. It's up to the user to free the object using + * call_rcu() or ttm_base_object_kfree(). + */ + if (base->refcount_release) { ttm_object_file_unref(&base->tfile); base->refcount_release(&base); @@ -214,7 +223,7 @@ struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile, int ret; rcu_read_lock(); - ret = drm_ht_find_item(&tdev->object_hash, key, &hash); + ret = drm_ht_find_item_rcu(&tdev->object_hash, key, &hash); if (likely(ret == 0)) { base = drm_hash_entry(hash, struct ttm_base_object, hash); diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile index 586869c8c11..2cc6cd91ac1 100644 --- a/drivers/gpu/drm/vmwgfx/Makefile +++ b/drivers/gpu/drm/vmwgfx/Makefile @@ -5,6 +5,7 @@ vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \ vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \ vmwgfx_overlay.o vmwgfx_marker.o vmwgfx_gmrid_manager.o \ - vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o + vmwgfx_fence.o vmwgfx_dmabuf.o vmwgfx_scrn.o vmwgfx_context.o \ + vmwgfx_surface.o obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o diff --git a/drivers/gpu/drm/vmwgfx/svga3d_surfacedefs.h b/drivers/gpu/drm/vmwgfx/svga3d_surfacedefs.h new file mode 100644 index 00000000000..8369c3ba10f --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/svga3d_surfacedefs.h @@ -0,0 +1,909 @@ +/************************************************************************** + * + * Copyright © 2008-2012 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifdef __KERNEL__ + +#include <drm/vmwgfx_drm.h> +#define surf_size_struct struct drm_vmw_size + +#else /* __KERNEL__ */ + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(_A) (sizeof(_A) / sizeof((_A)[0])) +#endif /* ARRAY_SIZE */ + +#define DIV_ROUND_UP(x, y) (((x) + (y) - 1) / (y)) +#define max_t(type, x, y) ((x) > (y) ? (x) : (y)) +#define surf_size_struct SVGA3dSize +#define u32 uint32 + +#endif /* __KERNEL__ */ + +#include "svga3d_reg.h" + +/* + * enum svga3d_block_desc describes the active data channels in a block. + * + * There can be at-most four active channels in a block: + * 1. Red, bump W, luminance and depth are stored in the first channel. + * 2. Green, bump V and stencil are stored in the second channel. + * 3. Blue and bump U are stored in the third channel. + * 4. Alpha and bump Q are stored in the fourth channel. + * + * Block channels can be used to store compressed and buffer data: + * 1. For compressed formats, only the data channel is used and its size + * is equal to that of a singular block in the compression scheme. + * 2. For buffer formats, only the data channel is used and its size is + * exactly one byte in length. + * 3. In each case the bit depth represent the size of a singular block. + * + * Note: Compressed and IEEE formats do not use the bitMask structure. + */ + +enum svga3d_block_desc { + SVGA3DBLOCKDESC_NONE = 0, /* No channels are active */ + SVGA3DBLOCKDESC_BLUE = 1 << 0, /* Block with red channel + data */ + SVGA3DBLOCKDESC_U = 1 << 0, /* Block with bump U channel + data */ + SVGA3DBLOCKDESC_UV_VIDEO = 1 << 7, /* Block with alternating video + U and V */ + SVGA3DBLOCKDESC_GREEN = 1 << 1, /* Block with green channel + data */ + SVGA3DBLOCKDESC_V = 1 << 1, /* Block with bump V channel + data */ + SVGA3DBLOCKDESC_STENCIL = 1 << 1, /* Block with a stencil + channel */ + SVGA3DBLOCKDESC_RED = 1 << 2, /* Block with blue channel + data */ + SVGA3DBLOCKDESC_W = 1 << 2, /* Block with bump W channel + data */ + SVGA3DBLOCKDESC_LUMINANCE = 1 << 2, /* Block with luminance channel + data */ + SVGA3DBLOCKDESC_Y = 1 << 2, /* Block with video luminance + data */ + SVGA3DBLOCKDESC_DEPTH = 1 << 2, /* Block with depth channel */ + SVGA3DBLOCKDESC_ALPHA = 1 << 3, /* Block with an alpha + channel */ + SVGA3DBLOCKDESC_Q = 1 << 3, /* Block with bump Q channel + data */ + SVGA3DBLOCKDESC_BUFFER = 1 << 4, /* Block stores 1 byte of + data */ + SVGA3DBLOCKDESC_COMPRESSED = 1 << 5, /* Block stores n bytes of + data depending on the + compression method used */ + SVGA3DBLOCKDESC_IEEE_FP = 1 << 6, /* Block stores data in an IEEE + floating point + representation in + all channels */ + SVGA3DBLOCKDESC_PLANAR_YUV = 1 << 8, /* Three separate blocks store + data. */ + SVGA3DBLOCKDESC_U_VIDEO = 1 << 9, /* Block with U video data */ + SVGA3DBLOCKDESC_V_VIDEO = 1 << 10, /* Block with V video data */ + SVGA3DBLOCKDESC_EXP = 1 << 11, /* Shared exponent */ + SVGA3DBLOCKDESC_SRGB = 1 << 12, /* Data is in sRGB format */ + SVGA3DBLOCKDESC_2PLANAR_YUV = 1 << 13, /* 2 planes of Y, UV, + e.g., NV12. */ + SVGA3DBLOCKDESC_3PLANAR_YUV = 1 << 14, /* 3 planes of separate + Y, U, V, e.g., YV12. */ + + SVGA3DBLOCKDESC_RG = SVGA3DBLOCKDESC_RED | + SVGA3DBLOCKDESC_GREEN, + SVGA3DBLOCKDESC_RGB = SVGA3DBLOCKDESC_RG | + SVGA3DBLOCKDESC_BLUE, + SVGA3DBLOCKDESC_RGB_SRGB = SVGA3DBLOCKDESC_RGB | + SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_RGBA = SVGA3DBLOCKDESC_RGB | + SVGA3DBLOCKDESC_ALPHA, + SVGA3DBLOCKDESC_RGBA_SRGB = SVGA3DBLOCKDESC_RGBA | + SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_UV = SVGA3DBLOCKDESC_U | + SVGA3DBLOCKDESC_V, + SVGA3DBLOCKDESC_UVL = SVGA3DBLOCKDESC_UV | + SVGA3DBLOCKDESC_LUMINANCE, + SVGA3DBLOCKDESC_UVW = SVGA3DBLOCKDESC_UV | + SVGA3DBLOCKDESC_W, + SVGA3DBLOCKDESC_UVWA = SVGA3DBLOCKDESC_UVW | + SVGA3DBLOCKDESC_ALPHA, + SVGA3DBLOCKDESC_UVWQ = SVGA3DBLOCKDESC_U | + SVGA3DBLOCKDESC_V | + SVGA3DBLOCKDESC_W | + SVGA3DBLOCKDESC_Q, + SVGA3DBLOCKDESC_LA = SVGA3DBLOCKDESC_LUMINANCE | + SVGA3DBLOCKDESC_ALPHA, + SVGA3DBLOCKDESC_R_FP = SVGA3DBLOCKDESC_RED | + SVGA3DBLOCKDESC_IEEE_FP, + SVGA3DBLOCKDESC_RG_FP = SVGA3DBLOCKDESC_R_FP | + SVGA3DBLOCKDESC_GREEN, + SVGA3DBLOCKDESC_RGB_FP = SVGA3DBLOCKDESC_RG_FP | + SVGA3DBLOCKDESC_BLUE, + SVGA3DBLOCKDESC_RGBA_FP = SVGA3DBLOCKDESC_RGB_FP | + SVGA3DBLOCKDESC_ALPHA, + SVGA3DBLOCKDESC_DS = SVGA3DBLOCKDESC_DEPTH | + SVGA3DBLOCKDESC_STENCIL, + SVGA3DBLOCKDESC_YUV = SVGA3DBLOCKDESC_UV_VIDEO | + SVGA3DBLOCKDESC_Y, + SVGA3DBLOCKDESC_AYUV = SVGA3DBLOCKDESC_ALPHA | + SVGA3DBLOCKDESC_Y | + SVGA3DBLOCKDESC_U_VIDEO | + SVGA3DBLOCKDESC_V_VIDEO, + SVGA3DBLOCKDESC_RGBE = SVGA3DBLOCKDESC_RGB | + SVGA3DBLOCKDESC_EXP, + SVGA3DBLOCKDESC_COMPRESSED_SRGB = SVGA3DBLOCKDESC_COMPRESSED | + SVGA3DBLOCKDESC_SRGB, + SVGA3DBLOCKDESC_NV12 = SVGA3DBLOCKDESC_PLANAR_YUV | + SVGA3DBLOCKDESC_2PLANAR_YUV, + SVGA3DBLOCKDESC_YV12 = SVGA3DBLOCKDESC_PLANAR_YUV | + SVGA3DBLOCKDESC_3PLANAR_YUV, +}; + +/* + * SVGA3dSurfaceDesc describes the actual pixel data. + * + * This structure provides the following information: + * 1. Block description. + * 2. Dimensions of a block in the surface. + * 3. Size of block in bytes. + * 4. Bit depth of the pixel data. + * 5. Channel bit depths and masks (if applicable). + */ +#define SVGA3D_CHANNEL_DEF(type) \ + struct { \ + union { \ + type blue; \ + type u; \ + type uv_video; \ + type u_video; \ + }; \ + union { \ + type green; \ + type v; \ + type stencil; \ + type v_video; \ + }; \ + union { \ + type red; \ + type w; \ + type luminance; \ + type y; \ + type depth; \ + type data; \ + }; \ + union { \ + type alpha; \ + type q; \ + type exp; \ + }; \ + } + +struct svga3d_surface_desc { + enum svga3d_block_desc block_desc; + surf_size_struct block_size; + u32 bytes_per_block; + u32 pitch_bytes_per_block; + + struct { + u32 total; + SVGA3D_CHANNEL_DEF(uint8); + } bit_depth; + + struct { + SVGA3D_CHANNEL_DEF(uint8); + } bit_offset; +}; + +static const struct svga3d_surface_desc svga3d_surface_descs[] = { + {SVGA3DBLOCKDESC_NONE, + {1, 1, 1}, 0, 0, {0, {{0}, {0}, {0}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_FORMAT_INVALID */ + + {SVGA3DBLOCKDESC_RGB, + {1, 1, 1}, 4, 4, {24, {{8}, {8}, {8}, {0} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_X8R8G8B8 */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_A8R8G8B8 */ + + {SVGA3DBLOCKDESC_RGB, + {1, 1, 1}, 2, 2, {16, {{5}, {6}, {5}, {0} } }, + {{{0}, {5}, {11}, {0} } } }, /* SVGA3D_R5G6B5 */ + + {SVGA3DBLOCKDESC_RGB, + {1, 1, 1}, 2, 2, {15, {{5}, {5}, {5}, {0} } }, + {{{0}, {5}, {10}, {0} } } }, /* SVGA3D_X1R5G5B5 */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 2, 2, {16, {{5}, {5}, {5}, {1} } }, + {{{0}, {5}, {10}, {15} } } }, /* SVGA3D_A1R5G5B5 */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 2, 2, {16, {{4}, {4}, {4}, {4} } }, + {{{0}, {4}, {8}, {12} } } }, /* SVGA3D_A4R4G4B4 */ + + {SVGA3DBLOCKDESC_DEPTH, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_Z_D32 */ + + {SVGA3DBLOCKDESC_DEPTH, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_Z_D16 */ + + {SVGA3DBLOCKDESC_DS, + {1, 1, 1}, 4, 4, {32, {{0}, {8}, {24}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_Z_D24S8 */ + + {SVGA3DBLOCKDESC_DS, + {1, 1, 1}, 2, 2, {16, {{0}, {1}, {15}, {0} } }, + {{{0}, {15}, {0}, {0} } } }, /* SVGA3D_Z_D15S1 */ + + {SVGA3DBLOCKDESC_LUMINANCE, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_LUMINANCE8 */ + + {SVGA3DBLOCKDESC_LA, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {4}, {4} } }, + {{{0}, {0}, {0}, {4} } } }, /* SVGA3D_LUMINANCE4_ALPHA4 */ + + {SVGA3DBLOCKDESC_LUMINANCE, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_LUMINANCE16 */ + + {SVGA3DBLOCKDESC_LA, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {8}, {8} } }, + {{{0}, {0}, {0}, {8} } } }, /* SVGA3D_LUMINANCE8_ALPHA8 */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 8, 8, {64, {{0}, {0}, {64}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_DXT1 */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_DXT2 */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_DXT3 */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_DXT4 */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_DXT5 */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {8}, {8} } }, + {{{0}, {0}, {0}, {8} } } }, /* SVGA3D_BUMPU8V8 */ + + {SVGA3DBLOCKDESC_UVL, + {1, 1, 1}, 2, 2, {16, {{5}, {5}, {6}, {0} } }, + {{{11}, {6}, {0}, {0} } } }, /* SVGA3D_BUMPL6V5U5 */ + + {SVGA3DBLOCKDESC_UVL, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {0} } }, + {{{16}, {8}, {0}, {0} } } }, /* SVGA3D_BUMPX8L8V8U8 */ + + {SVGA3DBLOCKDESC_UVL, + {1, 1, 1}, 3, 3, {24, {{8}, {8}, {8}, {0} } }, + {{{16}, {8}, {0}, {0} } } }, /* SVGA3D_BUMPL8V8U8 */ + + {SVGA3DBLOCKDESC_RGBA_FP, + {1, 1, 1}, 8, 8, {64, {{16}, {16}, {16}, {16} } }, + {{{32}, {16}, {0}, {48} } } }, /* SVGA3D_ARGB_S10E5 */ + + {SVGA3DBLOCKDESC_RGBA_FP, + {1, 1, 1}, 16, 16, {128, {{32}, {32}, {32}, {32} } }, + {{{64}, {32}, {0}, {96} } } }, /* SVGA3D_ARGB_S23E8 */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{10}, {10}, {10}, {2} } }, + {{{0}, {10}, {20}, {30} } } }, /* SVGA3D_A2R10G10B10 */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 2, 2, {16, {{8}, {8}, {0}, {0} } }, + {{{8}, {0}, {0}, {0} } } }, /* SVGA3D_V8U8 */ + + {SVGA3DBLOCKDESC_UVWQ, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{24}, {16}, {8}, {0} } } }, /* SVGA3D_Q8W8V8U8 */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 2, 2, {16, {{8}, {8}, {0}, {0} } }, + {{{8}, {0}, {0}, {0} } } }, /* SVGA3D_CxV8U8 */ + + {SVGA3DBLOCKDESC_UVL, + {1, 1, 1}, 4, 4, {24, {{8}, {8}, {8}, {0} } }, + {{{16}, {8}, {0}, {0} } } }, /* SVGA3D_X8L8V8U8 */ + + {SVGA3DBLOCKDESC_UVWA, + {1, 1, 1}, 4, 4, {32, {{10}, {10}, {10}, {2} } }, + {{{0}, {10}, {20}, {30} } } }, /* SVGA3D_A2W10V10U10 */ + + {SVGA3DBLOCKDESC_ALPHA, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {0}, {8} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_ALPHA8 */ + + {SVGA3DBLOCKDESC_R_FP, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R_S10E5 */ + + {SVGA3DBLOCKDESC_R_FP, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R_S23E8 */ + + {SVGA3DBLOCKDESC_RG_FP, + {1, 1, 1}, 4, 4, {32, {{0}, {16}, {16}, {0} } }, + {{{0}, {16}, {0}, {0} } } }, /* SVGA3D_RG_S10E5 */ + + {SVGA3DBLOCKDESC_RG_FP, + {1, 1, 1}, 8, 8, {64, {{0}, {32}, {32}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_RG_S23E8 */ + + {SVGA3DBLOCKDESC_BUFFER, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BUFFER */ + + {SVGA3DBLOCKDESC_DEPTH, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {24}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_Z_D24X8 */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 4, 4, {32, {{16}, {16}, {0}, {0} } }, + {{{16}, {0}, {0}, {0} } } }, /* SVGA3D_V16U16 */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 4, 4, {32, {{0}, {16}, {16}, {0} } }, + {{{0}, {0}, {16}, {0} } } }, /* SVGA3D_G16R16 */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 8, 8, {64, {{16}, {16}, {16}, {16} } }, + {{{32}, {16}, {0}, {48} } } }, /* SVGA3D_A16B16G16R16 */ + + {SVGA3DBLOCKDESC_YUV, + {1, 1, 1}, 2, 2, {16, {{8}, {0}, {8}, {0} } }, + {{{0}, {0}, {8}, {0} } } }, /* SVGA3D_UYVY */ + + {SVGA3DBLOCKDESC_YUV, + {1, 1, 1}, 2, 2, {16, {{8}, {0}, {8}, {0} } }, + {{{8}, {0}, {0}, {0} } } }, /* SVGA3D_YUY2 */ + + {SVGA3DBLOCKDESC_NV12, + {2, 2, 1}, 6, 2, {48, {{0}, {0}, {48}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_NV12 */ + + {SVGA3DBLOCKDESC_AYUV, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_AYUV */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 16, 16, {128, {{32}, {32}, {32}, {32} } }, + {{{64}, {32}, {0}, {96} } } }, /* SVGA3D_R32G32B32A32_TYPELESS */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 16, 16, {128, {{32}, {32}, {32}, {32} } }, + {{{64}, {32}, {0}, {96} } } }, /* SVGA3D_R32G32B32A32_UINT */ + + {SVGA3DBLOCKDESC_UVWQ, + {1, 1, 1}, 16, 16, {128, {{32}, {32}, {32}, {32} } }, + {{{64}, {32}, {0}, {96} } } }, /* SVGA3D_R32G32B32A32_SINT */ + + {SVGA3DBLOCKDESC_RGB, + {1, 1, 1}, 12, 12, {96, {{32}, {32}, {32}, {0} } }, + {{{64}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32B32_TYPELESS */ + + {SVGA3DBLOCKDESC_RGB_FP, + {1, 1, 1}, 12, 12, {96, {{32}, {32}, {32}, {0} } }, + {{{64}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32B32_FLOAT */ + + {SVGA3DBLOCKDESC_RGB, + {1, 1, 1}, 12, 12, {96, {{32}, {32}, {32}, {0} } }, + {{{64}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32B32_UINT */ + + {SVGA3DBLOCKDESC_UVW, + {1, 1, 1}, 12, 12, {96, {{32}, {32}, {32}, {0} } }, + {{{64}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32B32_SINT */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 8, 8, {64, {{16}, {16}, {16}, {16} } }, + {{{32}, {16}, {0}, {48} } } }, /* SVGA3D_R16G16B16A16_TYPELESS */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 8, 8, {64, {{16}, {16}, {16}, {16} } }, + {{{32}, {16}, {0}, {48} } } }, /* SVGA3D_R16G16B16A16_UINT */ + + {SVGA3DBLOCKDESC_UVWQ, + {1, 1, 1}, 8, 8, {64, {{16}, {16}, {16}, {16} } }, + {{{32}, {16}, {0}, {48} } } }, /* SVGA3D_R16G16B16A16_SNORM */ + + {SVGA3DBLOCKDESC_UVWQ, + {1, 1, 1}, 8, 8, {64, {{16}, {16}, {16}, {16} } }, + {{{32}, {16}, {0}, {48} } } }, /* SVGA3D_R16G16B16A16_SINT */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 8, 8, {64, {{0}, {32}, {32}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32_TYPELESS */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 8, 8, {64, {{0}, {32}, {32}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32_UINT */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 8, 8, {64, {{0}, {32}, {32}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_R32G32_SINT */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 8, 8, {64, {{0}, {8}, {32}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_R32G8X24_TYPELESS */ + + {SVGA3DBLOCKDESC_DS, + {1, 1, 1}, 8, 8, {64, {{0}, {8}, {32}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_D32_FLOAT_S8X24_UINT */ + + {SVGA3DBLOCKDESC_R_FP, + {1, 1, 1}, 8, 8, {64, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R32_FLOAT_X8_X24_TYPELESS */ + + {SVGA3DBLOCKDESC_GREEN, + {1, 1, 1}, 8, 8, {64, {{0}, {8}, {0}, {0} } }, + {{{0}, {32}, {0}, {0} } } }, /* SVGA3D_X32_TYPELESS_G8X24_UINT */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{10}, {10}, {10}, {2} } }, + {{{0}, {10}, {20}, {30} } } }, /* SVGA3D_R10G10B10A2_TYPELESS */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{10}, {10}, {10}, {2} } }, + {{{0}, {10}, {20}, {30} } } }, /* SVGA3D_R10G10B10A2_UINT */ + + {SVGA3DBLOCKDESC_RGB_FP, + {1, 1, 1}, 4, 4, {32, {{10}, {11}, {11}, {0} } }, + {{{0}, {10}, {21}, {0} } } }, /* SVGA3D_R11G11B10_FLOAT */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{16}, {8}, {0}, {24} } } }, /* SVGA3D_R8G8B8A8_TYPELESS */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{16}, {8}, {0}, {24} } } }, /* SVGA3D_R8G8B8A8_UNORM */ + + {SVGA3DBLOCKDESC_RGBA_SRGB, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{16}, {8}, {0}, {24} } } }, /* SVGA3D_R8G8B8A8_UNORM_SRGB */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{16}, {8}, {0}, {24} } } }, /* SVGA3D_R8G8B8A8_UINT */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{16}, {8}, {0}, {24} } } }, /* SVGA3D_R8G8B8A8_SINT */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 4, 4, {32, {{0}, {16}, {16}, {0} } }, + {{{0}, {16}, {0}, {0} } } }, /* SVGA3D_R16G16_TYPELESS */ + + {SVGA3DBLOCKDESC_RG_FP, + {1, 1, 1}, 4, 4, {32, {{0}, {16}, {16}, {0} } }, + {{{0}, {16}, {0}, {0} } } }, /* SVGA3D_R16G16_UINT */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 4, 4, {32, {{0}, {16}, {16}, {0} } }, + {{{0}, {16}, {0}, {0} } } }, /* SVGA3D_R16G16_SINT */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R32_TYPELESS */ + + {SVGA3DBLOCKDESC_DEPTH, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_D32_FLOAT */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R32_UINT */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {32}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R32_SINT */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 4, 4, {32, {{0}, {8}, {24}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_R24G8_TYPELESS */ + + {SVGA3DBLOCKDESC_DS, + {1, 1, 1}, 4, 4, {32, {{0}, {8}, {24}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_D24_UNORM_S8_UINT */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 4, 4, {32, {{0}, {0}, {24}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R24_UNORM_X8_TYPELESS */ + + {SVGA3DBLOCKDESC_GREEN, + {1, 1, 1}, 4, 4, {32, {{0}, {8}, {0}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_X24_TYPELESS_G8_UINT */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 2, 2, {16, {{0}, {8}, {8}, {0} } }, + {{{0}, {8}, {0}, {0} } } }, /* SVGA3D_R8G8_TYPELESS */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 2, 2, {16, {{0}, {8}, {8}, {0} } }, + {{{0}, {8}, {0}, {0} } } }, /* SVGA3D_R8G8_UNORM */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 2, 2, {16, {{0}, {8}, {8}, {0} } }, + {{{0}, {8}, {0}, {0} } } }, /* SVGA3D_R8G8_UINT */ + + {SVGA3DBLOCKDESC_UV, + {1, 1, 1}, 2, 2, {16, {{0}, {8}, {8}, {0} } }, + {{{0}, {8}, {0}, {0} } } }, /* SVGA3D_R8G8_SINT */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R16_TYPELESS */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R16_UNORM */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R16_UINT */ + + {SVGA3DBLOCKDESC_U, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R16_SNORM */ + + {SVGA3DBLOCKDESC_U, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R16_SINT */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R8_TYPELESS */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R8_UNORM */ + + {SVGA3DBLOCKDESC_RED, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R8_UINT */ + + {SVGA3DBLOCKDESC_U, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R8_SNORM */ + + {SVGA3DBLOCKDESC_U, + {1, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R8_SINT */ + + {SVGA3DBLOCKDESC_RED, + {8, 1, 1}, 1, 1, {8, {{0}, {0}, {8}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_R1_UNORM */ + + {SVGA3DBLOCKDESC_RGBE, + {1, 1, 1}, 4, 4, {32, {{9}, {9}, {9}, {5} } }, + {{{18}, {9}, {0}, {27} } } }, /* SVGA3D_R9G9B9E5_SHAREDEXP */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 2, 2, {16, {{0}, {8}, {8}, {0} } }, + {{{0}, {8}, {0}, {0} } } }, /* SVGA3D_R8G8_B8G8_UNORM */ + + {SVGA3DBLOCKDESC_RG, + {1, 1, 1}, 2, 2, {16, {{0}, {8}, {8}, {0} } }, + {{{0}, {8}, {0}, {0} } } }, /* SVGA3D_G8R8_G8B8_UNORM */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 8, 8, {64, {{0}, {0}, {64}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC1_TYPELESS */ + + {SVGA3DBLOCKDESC_COMPRESSED_SRGB, + {4, 4, 1}, 8, 8, {64, {{0}, {0}, {64}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC1_UNORM_SRGB */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC2_TYPELESS */ + + {SVGA3DBLOCKDESC_COMPRESSED_SRGB, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC2_UNORM_SRGB */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC3_TYPELESS */ + + {SVGA3DBLOCKDESC_COMPRESSED_SRGB, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC3_UNORM_SRGB */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 8, 8, {64, {{0}, {0}, {64}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC4_TYPELESS */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 8, 8, {64, {{0}, {0}, {64}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC4_UNORM */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 8, 8, {64, {{0}, {0}, {64}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC4_SNORM */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC5_TYPELESS */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC5_UNORM */ + + {SVGA3DBLOCKDESC_COMPRESSED, + {4, 4, 1}, 16, 16, {128, {{0}, {0}, {128}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_BC5_SNORM */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{10}, {10}, {10}, {2} } }, + {{{0}, {10}, {20}, {30} } } }, /* SVGA3D_R10G10B10_XR_BIAS_A2_UNORM */ + + {SVGA3DBLOCKDESC_RGBA, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_B8G8R8A8_TYPELESS */ + + {SVGA3DBLOCKDESC_RGBA_SRGB, + {1, 1, 1}, 4, 4, {32, {{8}, {8}, {8}, {8} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_B8G8R8A8_UNORM_SRGB */ + + {SVGA3DBLOCKDESC_RGB, + {1, 1, 1}, 4, 4, {24, {{8}, {8}, {8}, {0} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_B8G8R8X8_TYPELESS */ + + {SVGA3DBLOCKDESC_RGB_SRGB, + {1, 1, 1}, 4, 4, {24, {{8}, {8}, {8}, {0} } }, + {{{0}, {8}, {16}, {24} } } }, /* SVGA3D_B8G8R8X8_UNORM_SRGB */ + + {SVGA3DBLOCKDESC_DEPTH, + {1, 1, 1}, 2, 2, {16, {{0}, {0}, {16}, {0} } }, + {{{0}, {0}, {0}, {0} } } }, /* SVGA3D_Z_DF16 */ + + {SVGA3DBLOCKDESC_DS, + {1, 1, 1}, 4, 4, {32, {{0}, {8}, {24}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_Z_DF24 */ + + {SVGA3DBLOCKDESC_DS, + {1, 1, 1}, 4, 4, {32, {{0}, {8}, {24}, {0} } }, + {{{0}, {24}, {0}, {0} } } }, /* SVGA3D_Z_D24S8_INT */ +}; + +static inline u32 clamped_umul32(u32 a, u32 b) +{ + uint64_t tmp = (uint64_t) a*b; + return (tmp > (uint64_t) ((u32) -1)) ? (u32) -1 : tmp; +} + +static inline const struct svga3d_surface_desc * +svga3dsurface_get_desc(SVGA3dSurfaceFormat format) +{ + if (format < ARRAY_SIZE(svga3d_surface_descs)) + return &svga3d_surface_descs[format]; + + return &svga3d_surface_descs[SVGA3D_FORMAT_INVALID]; +} + +/* + *---------------------------------------------------------------------- + * + * svga3dsurface_get_mip_size -- + * + * Given a base level size and the mip level, compute the size of + * the mip level. + * + * Results: + * See above. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static inline surf_size_struct +svga3dsurface_get_mip_size(surf_size_struct base_level, u32 mip_level) +{ + surf_size_struct size; + + size.width = max_t(u32, base_level.width >> mip_level, 1); + size.height = max_t(u32, base_level.height >> mip_level, 1); + size.depth = max_t(u32, base_level.depth >> mip_level, 1); + return size; +} + +static inline void +svga3dsurface_get_size_in_blocks(const struct svga3d_surface_desc *desc, + const surf_size_struct *pixel_size, + surf_size_struct *block_size) +{ + block_size->width = DIV_ROUND_UP(pixel_size->width, + desc->block_size.width); + block_size->height = DIV_ROUND_UP(pixel_size->height, + desc->block_size.height); + block_size->depth = DIV_ROUND_UP(pixel_size->depth, + desc->block_size.depth); +} + +static inline bool +svga3dsurface_is_planar_surface(const struct svga3d_surface_desc *desc) +{ + return (desc->block_desc & SVGA3DBLOCKDESC_PLANAR_YUV) != 0; +} + +static inline u32 +svga3dsurface_calculate_pitch(const struct svga3d_surface_desc *desc, + const surf_size_struct *size) +{ + u32 pitch; + surf_size_struct blocks; + + svga3dsurface_get_size_in_blocks(desc, size, &blocks); + + pitch = blocks.width * desc->pitch_bytes_per_block; + + return pitch; +} + +/* + *----------------------------------------------------------------------------- + * + * svga3dsurface_get_image_buffer_size -- + * + * Return the number of bytes of buffer space required to store + * one image of a surface, optionally using the specified pitch. + * + * If pitch is zero, it is assumed that rows are tightly packed. + * + * This function is overflow-safe. If the result would have + * overflowed, instead we return MAX_UINT32. + * + * Results: + * Byte count. + * + * Side effects: + * None. + * + *----------------------------------------------------------------------------- + */ + +static inline u32 +svga3dsurface_get_image_buffer_size(const struct svga3d_surface_desc *desc, + const surf_size_struct *size, + u32 pitch) +{ + surf_size_struct image_blocks; + u32 slice_size, total_size; + + svga3dsurface_get_size_in_blocks(desc, size, &image_blocks); + + if (svga3dsurface_is_planar_surface(desc)) { + total_size = clamped_umul32(image_blocks.width, + image_blocks.height); + total_size = clamped_umul32(total_size, image_blocks.depth); + total_size = clamped_umul32(total_size, desc->bytes_per_block); + return total_size; + } + + if (pitch == 0) + pitch = svga3dsurface_calculate_pitch(desc, size); + + slice_size = clamped_umul32(image_blocks.height, pitch); + total_size = clamped_umul32(slice_size, image_blocks.depth); + + return total_size; +} + +static inline u32 +svga3dsurface_get_serialized_size(SVGA3dSurfaceFormat format, + surf_size_struct base_level_size, + u32 num_mip_levels, + bool cubemap) +{ + const struct svga3d_surface_desc *desc = svga3dsurface_get_desc(format); + u32 total_size = 0; + u32 mip; + + for (mip = 0; mip < num_mip_levels; mip++) { + surf_size_struct size = + svga3dsurface_get_mip_size(base_level_size, mip); + total_size += svga3dsurface_get_image_buffer_size(desc, + &size, 0); + } + + if (cubemap) + total_size *= SVGA3D_MAX_SURFACE_FACES; + + return total_size; +} + + +/** + * svga3dsurface_get_pixel_offset - Compute the offset (in bytes) to a pixel + * in an image (or volume). + * + * @width: The image width in pixels. + * @height: The image height in pixels + */ +static inline u32 +svga3dsurface_get_pixel_offset(SVGA3dSurfaceFormat format, + u32 width, u32 height, + u32 x, u32 y, u32 z) +{ + const struct svga3d_surface_desc *desc = svga3dsurface_get_desc(format); + const u32 bw = desc->block_size.width, bh = desc->block_size.height; + const u32 bd = desc->block_size.depth; + const u32 rowstride = DIV_ROUND_UP(width, bw) * desc->bytes_per_block; + const u32 imgstride = DIV_ROUND_UP(height, bh) * rowstride; + const u32 offset = (z / bd * imgstride + + y / bh * rowstride + + x / bw * desc->bytes_per_block); + return offset; +} + + +static inline u32 +svga3dsurface_get_image_offset(SVGA3dSurfaceFormat format, + surf_size_struct baseLevelSize, + u32 numMipLevels, + u32 face, + u32 mip) + +{ + u32 offset; + u32 mipChainBytes; + u32 mipChainBytesToLevel; + u32 i; + const struct svga3d_surface_desc *desc; + surf_size_struct mipSize; + u32 bytes; + + desc = svga3dsurface_get_desc(format); + + mipChainBytes = 0; + mipChainBytesToLevel = 0; + for (i = 0; i < numMipLevels; i++) { + mipSize = svga3dsurface_get_mip_size(baseLevelSize, i); + bytes = svga3dsurface_get_image_buffer_size(desc, &mipSize, 0); + mipChainBytes += bytes; + if (i < mip) + mipChainBytesToLevel += bytes; + } + + offset = mipChainBytes * face + mipChainBytesToLevel; + + return offset; +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c index ef1109c8fec..96dc84dc34d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c @@ -248,13 +248,12 @@ void vmw_evict_flags(struct ttm_buffer_object *bo, *placement = vmw_sys_placement; } -/** - * FIXME: Proper access checks on buffers. - */ - static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp) { - return 0; + struct ttm_object_file *tfile = + vmw_fpriv((struct drm_file *)filp->private_data)->tfile; + + return vmw_user_dmabuf_verify_access(bo, tfile); } static int vmw_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_context.c b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c new file mode 100644 index 00000000000..00ae0925aca --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_context.c @@ -0,0 +1,274 @@ +/************************************************************************** + * + * Copyright © 2009-2012 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" +#include "ttm/ttm_placement.h" + +struct vmw_user_context { + struct ttm_base_object base; + struct vmw_resource res; +}; + +static void vmw_user_context_free(struct vmw_resource *res); +static struct vmw_resource * +vmw_user_context_base_to_res(struct ttm_base_object *base); + +static uint64_t vmw_user_context_size; + +static const struct vmw_user_resource_conv user_context_conv = { + .object_type = VMW_RES_CONTEXT, + .base_obj_to_res = vmw_user_context_base_to_res, + .res_free = vmw_user_context_free +}; + +const struct vmw_user_resource_conv *user_context_converter = + &user_context_conv; + + +static const struct vmw_res_func vmw_legacy_context_func = { + .res_type = vmw_res_context, + .needs_backup = false, + .may_evict = false, + .type_name = "legacy contexts", + .backup_placement = NULL, + .create = NULL, + .destroy = NULL, + .bind = NULL, + .unbind = NULL +}; + +/** + * Context management: + */ + +static void vmw_hw_context_destroy(struct vmw_resource *res) +{ + + struct vmw_private *dev_priv = res->dev_priv; + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDestroyContext body; + } *cmd; + + + vmw_execbuf_release_pinned_bo(dev_priv); + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "destruction.\n"); + return; + } + + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY); + cmd->header.size = cpu_to_le32(sizeof(cmd->body)); + cmd->body.cid = cpu_to_le32(res->id); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + vmw_3d_resource_dec(dev_priv, false); +} + +static int vmw_context_init(struct vmw_private *dev_priv, + struct vmw_resource *res, + void (*res_free) (struct vmw_resource *res)) +{ + int ret; + + struct { + SVGA3dCmdHeader header; + SVGA3dCmdDefineContext body; + } *cmd; + + ret = vmw_resource_init(dev_priv, res, false, + res_free, &vmw_legacy_context_func); + + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate a resource id.\n"); + goto out_early; + } + + if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { + DRM_ERROR("Out of hw context ids.\n"); + vmw_resource_unreference(&res); + return -ENOMEM; + } + + cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Fifo reserve failed.\n"); + vmw_resource_unreference(&res); + return -ENOMEM; + } + + cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE); + cmd->header.size = cpu_to_le32(sizeof(cmd->body)); + cmd->body.cid = cpu_to_le32(res->id); + + vmw_fifo_commit(dev_priv, sizeof(*cmd)); + (void) vmw_3d_resource_inc(dev_priv, false); + vmw_resource_activate(res, vmw_hw_context_destroy); + return 0; + +out_early: + if (res_free == NULL) + kfree(res); + else + res_free(res); + return ret; +} + +struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) +{ + struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL); + int ret; + + if (unlikely(res == NULL)) + return NULL; + + ret = vmw_context_init(dev_priv, res, NULL); + + return (ret == 0) ? res : NULL; +} + +/** + * User-space context management: + */ + +static struct vmw_resource * +vmw_user_context_base_to_res(struct ttm_base_object *base) +{ + return &(container_of(base, struct vmw_user_context, base)->res); +} + +static void vmw_user_context_free(struct vmw_resource *res) +{ + struct vmw_user_context *ctx = + container_of(res, struct vmw_user_context, res); + struct vmw_private *dev_priv = res->dev_priv; + + ttm_base_object_kfree(ctx, base); + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); +} + +/** + * This function is called when user space has no more references on the + * base object. It releases the base-object's reference on the resource object. + */ + +static void vmw_user_context_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_context *ctx = + container_of(base, struct vmw_user_context, base); + struct vmw_resource *res = &ctx->res; + + *p_base = NULL; + vmw_resource_unreference(&res); +} + +int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + + return ttm_ref_object_base_unref(tfile, arg->cid, TTM_REF_USAGE); +} + +int vmw_context_define_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_context *ctx; + struct vmw_resource *res; + struct vmw_resource *tmp; + struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_master *vmaster = vmw_master(file_priv->master); + int ret; + + + /* + * Approximate idr memory usage with 128 bytes. It will be limited + * by maximum number_of contexts anyway. + */ + + if (unlikely(vmw_user_context_size == 0)) + vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + vmw_user_context_size, + false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for context" + " creation.\n"); + goto out_unlock; + } + + ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); + if (unlikely(ctx == NULL)) { + ttm_mem_global_free(vmw_mem_glob(dev_priv), + vmw_user_context_size); + ret = -ENOMEM; + goto out_unlock; + } + + res = &ctx->res; + ctx->base.shareable = false; + ctx->base.tfile = NULL; + + /* + * From here on, the destructor takes over resource freeing. + */ + + ret = vmw_context_init(dev_priv, res, vmw_user_context_free); + if (unlikely(ret != 0)) + goto out_unlock; + + tmp = vmw_resource_reference(&ctx->res); + ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, + &vmw_user_context_base_release, NULL); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + goto out_err; + } + + arg->cid = ctx->base.hash.key; +out_err: + vmw_resource_unreference(&res); +out_unlock: + ttm_read_unlock(&vmaster->lock); + return ret; + +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c index bd78257cba8..655d57f188d 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_dmabuf.c @@ -60,7 +60,7 @@ int vmw_dmabuf_to_placement(struct vmw_private *dev_priv, if (unlikely(ret != 0)) return ret; - vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + vmw_execbuf_release_pinned_bo(dev_priv); ret = ttm_bo_reserve(bo, interruptible, false, false, 0); if (unlikely(ret != 0)) @@ -105,7 +105,7 @@ int vmw_dmabuf_to_vram_or_gmr(struct vmw_private *dev_priv, return ret; if (pin) - vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + vmw_execbuf_release_pinned_bo(dev_priv); ret = ttm_bo_reserve(bo, interruptible, false, false, 0); if (unlikely(ret != 0)) @@ -214,8 +214,7 @@ int vmw_dmabuf_to_start_of_vram(struct vmw_private *dev_priv, return ret; if (pin) - vmw_execbuf_release_pinned_bo(dev_priv, false, 0); - + vmw_execbuf_release_pinned_bo(dev_priv); ret = ttm_bo_reserve(bo, interruptible, false, false, 0); if (unlikely(ret != 0)) goto err_unlock; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c index 56973cd4173..91581fd5004 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c @@ -432,6 +432,7 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) struct vmw_private *dev_priv; int ret; uint32_t svga_id; + enum vmw_res_type i; dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL); if (unlikely(dev_priv == NULL)) { @@ -448,15 +449,18 @@ static int vmw_driver_load(struct drm_device *dev, unsigned long chipset) mutex_init(&dev_priv->cmdbuf_mutex); mutex_init(&dev_priv->release_mutex); rwlock_init(&dev_priv->resource_lock); - idr_init(&dev_priv->context_idr); - idr_init(&dev_priv->surface_idr); - idr_init(&dev_priv->stream_idr); + + for (i = vmw_res_context; i < vmw_res_max; ++i) { + idr_init(&dev_priv->res_idr[i]); + INIT_LIST_HEAD(&dev_priv->res_lru[i]); + } + mutex_init(&dev_priv->init_mutex); init_waitqueue_head(&dev_priv->fence_queue); init_waitqueue_head(&dev_priv->fifo_queue); dev_priv->fence_queue_waiters = 0; atomic_set(&dev_priv->fifo_queue_waiters, 0); - INIT_LIST_HEAD(&dev_priv->surface_lru); + dev_priv->used_memory_size = 0; dev_priv->io_start = pci_resource_start(dev->pdev, 0); @@ -670,9 +674,9 @@ out_err2: out_err1: vmw_ttm_global_release(dev_priv); out_err0: - idr_destroy(&dev_priv->surface_idr); - idr_destroy(&dev_priv->context_idr); - idr_destroy(&dev_priv->stream_idr); + for (i = vmw_res_context; i < vmw_res_max; ++i) + idr_destroy(&dev_priv->res_idr[i]); + kfree(dev_priv); return ret; } @@ -680,9 +684,12 @@ out_err0: static int vmw_driver_unload(struct drm_device *dev) { struct vmw_private *dev_priv = vmw_priv(dev); + enum vmw_res_type i; unregister_pm_notifier(&dev_priv->pm_nb); + if (dev_priv->ctx.res_ht_initialized) + drm_ht_remove(&dev_priv->ctx.res_ht); if (dev_priv->ctx.cmd_bounce) vfree(dev_priv->ctx.cmd_bounce); if (dev_priv->enable_fb) { @@ -709,9 +716,9 @@ static int vmw_driver_unload(struct drm_device *dev) (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM); (void)ttm_bo_device_release(&dev_priv->bdev); vmw_ttm_global_release(dev_priv); - idr_destroy(&dev_priv->surface_idr); - idr_destroy(&dev_priv->context_idr); - idr_destroy(&dev_priv->stream_idr); + + for (i = vmw_res_context; i < vmw_res_max; ++i) + idr_destroy(&dev_priv->res_idr[i]); kfree(dev_priv); @@ -935,7 +942,7 @@ static void vmw_master_drop(struct drm_device *dev, vmw_fp->locked_master = drm_master_get(file_priv->master); ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile); - vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + vmw_execbuf_release_pinned_bo(dev_priv); if (unlikely((ret != 0))) { DRM_ERROR("Unable to lock TTM at VT switch.\n"); @@ -987,7 +994,8 @@ static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val, * This empties VRAM and unbinds all GMR bindings. * Buffer contents is moved to swappable memory. */ - vmw_execbuf_release_pinned_bo(dev_priv, false, 0); + vmw_execbuf_release_pinned_bo(dev_priv); + vmw_resource_evict_all(dev_priv); ttm_bo_swapout_all(&dev_priv->bdev); break; diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h index 7c6f6e3a3c8..13aeda71280 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h @@ -67,31 +67,46 @@ struct vmw_fpriv { struct vmw_dma_buffer { struct ttm_buffer_object base; - struct list_head validate_list; - bool gmr_bound; - uint32_t cur_validate_node; - bool on_validate_list; + struct list_head res_list; }; +/** + * struct vmw_validate_buffer - Carries validation info about buffers. + * + * @base: Validation info for TTM. + * @hash: Hash entry for quick lookup of the TTM buffer object. + * + * This structure contains also driver private validation info + * on top of the info needed by TTM. + */ +struct vmw_validate_buffer { + struct ttm_validate_buffer base; + struct drm_hash_item hash; +}; + +struct vmw_res_func; struct vmw_resource { struct kref kref; struct vmw_private *dev_priv; - struct idr *idr; int id; - enum ttm_object_type res_type; bool avail; - void (*remove_from_lists) (struct vmw_resource *res); - void (*hw_destroy) (struct vmw_resource *res); + unsigned long backup_size; + bool res_dirty; /* Protected by backup buffer reserved */ + bool backup_dirty; /* Protected by backup buffer reserved */ + struct vmw_dma_buffer *backup; + unsigned long backup_offset; + const struct vmw_res_func *func; + struct list_head lru_head; /* Protected by the resource lock */ + struct list_head mob_head; /* Protected by @backup reserved */ void (*res_free) (struct vmw_resource *res); - struct list_head validate_head; - struct list_head query_head; /* Protected by the cmdbuf mutex */ - /* TODO is a generic snooper needed? */ -#if 0 - void (*snoop)(struct vmw_resource *res, - struct ttm_object_file *tfile, - SVGA3dCmdHeader *header); - void *snoop_priv; -#endif + void (*hw_destroy) (struct vmw_resource *res); +}; + +enum vmw_res_type { + vmw_res_context, + vmw_res_surface, + vmw_res_stream, + vmw_res_max }; struct vmw_cursor_snooper { @@ -105,20 +120,18 @@ struct vmw_surface_offset; struct vmw_surface { struct vmw_resource res; - struct list_head lru_head; /* Protected by the resource lock */ uint32_t flags; uint32_t format; uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES]; + struct drm_vmw_size base_size; struct drm_vmw_size *sizes; uint32_t num_sizes; - bool scanout; - /* TODO so far just a extra pointer */ struct vmw_cursor_snooper snooper; - struct ttm_buffer_object *backup; struct vmw_surface_offset *offsets; - uint32_t backup_size; + SVGA3dTextureFilter autogen_filter; + uint32_t multisample_count; }; struct vmw_marker_queue { @@ -145,29 +158,46 @@ struct vmw_relocation { uint32_t index; }; +/** + * struct vmw_res_cache_entry - resource information cache entry + * + * @valid: Whether the entry is valid, which also implies that the execbuf + * code holds a reference to the resource, and it's placed on the + * validation list. + * @handle: User-space handle of a resource. + * @res: Non-ref-counted pointer to the resource. + * + * Used to avoid frequent repeated user-space handle lookups of the + * same resource. + */ +struct vmw_res_cache_entry { + bool valid; + uint32_t handle; + struct vmw_resource *res; + struct vmw_resource_val_node *node; +}; + struct vmw_sw_context{ - struct ida bo_list; - uint32_t last_cid; - bool cid_valid; + struct drm_open_hash res_ht; + bool res_ht_initialized; bool kernel; /**< is the called made from the kernel */ - struct vmw_resource *cur_ctx; - uint32_t last_sid; - uint32_t sid_translation; - bool sid_valid; struct ttm_object_file *tfile; struct list_head validate_nodes; struct vmw_relocation relocs[VMWGFX_MAX_RELOCATIONS]; uint32_t cur_reloc; - struct ttm_validate_buffer val_bufs[VMWGFX_MAX_VALIDATIONS]; + struct vmw_validate_buffer val_bufs[VMWGFX_MAX_VALIDATIONS]; uint32_t cur_val_buf; uint32_t *cmd_bounce; uint32_t cmd_bounce_size; struct list_head resource_list; uint32_t fence_flags; - struct list_head query_list; struct ttm_buffer_object *cur_query_bo; - uint32_t cur_query_cid; - bool query_cid_valid; + struct list_head res_relocations; + uint32_t *buf_start; + struct vmw_res_cache_entry res_cache[vmw_res_max]; + struct vmw_resource *last_query_ctx; + bool needs_post_query_barrier; + struct vmw_resource *error_resource; }; struct vmw_legacy_display; @@ -242,10 +272,7 @@ struct vmw_private { */ rwlock_t resource_lock; - struct idr context_idr; - struct idr surface_idr; - struct idr stream_idr; - + struct idr res_idr[vmw_res_max]; /* * Block lastclose from racing with firstopen. */ @@ -320,6 +347,7 @@ struct vmw_private { struct ttm_buffer_object *dummy_query_bo; struct ttm_buffer_object *pinned_bo; uint32_t query_cid; + uint32_t query_cid_valid; bool dummy_query_bo_pinned; /* @@ -329,10 +357,15 @@ struct vmw_private { * protected by the cmdbuf mutex for simplicity. */ - struct list_head surface_lru; + struct list_head res_lru[vmw_res_max]; uint32_t used_memory_size; }; +static inline struct vmw_surface *vmw_res_to_srf(struct vmw_resource *res) +{ + return container_of(res, struct vmw_surface, res); +} + static inline struct vmw_private *vmw_priv(struct drm_device *dev) { return (struct vmw_private *)dev->dev_private; @@ -381,10 +414,16 @@ extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id); /** * Resource utilities - vmwgfx_resource.c */ +struct vmw_user_resource_conv; +extern const struct vmw_user_resource_conv *user_surface_converter; +extern const struct vmw_user_resource_conv *user_context_converter; extern struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv); extern void vmw_resource_unreference(struct vmw_resource **p_res); extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res); +extern int vmw_resource_validate(struct vmw_resource *res); +extern int vmw_resource_reserve(struct vmw_resource *res, bool no_backup); +extern bool vmw_resource_needs_backup(const struct vmw_resource *res); extern int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_context_define_ioctl(struct drm_device *dev, void *data, @@ -398,14 +437,13 @@ extern int vmw_user_lookup_handle(struct vmw_private *dev_priv, uint32_t handle, struct vmw_surface **out_surf, struct vmw_dma_buffer **out_buf); +extern int vmw_user_resource_lookup_handle( + struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_user_resource_conv *converter, + struct vmw_resource **p_res); extern void vmw_surface_res_free(struct vmw_resource *res); -extern int vmw_surface_init(struct vmw_private *dev_priv, - struct vmw_surface *srf, - void (*res_free) (struct vmw_resource *res)); -extern int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t handle, - struct vmw_surface **out); extern int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_surface_define_ioctl(struct drm_device *dev, void *data, @@ -423,6 +461,8 @@ extern int vmw_dmabuf_init(struct vmw_private *dev_priv, size_t size, struct ttm_placement *placement, bool interuptable, void (*bo_free) (struct ttm_buffer_object *bo)); +extern int vmw_user_dmabuf_verify_access(struct ttm_buffer_object *bo, + struct ttm_object_file *tfile); extern int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv); extern int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, @@ -440,7 +480,14 @@ extern int vmw_user_stream_lookup(struct vmw_private *dev_priv, struct ttm_object_file *tfile, uint32_t *inout_id, struct vmw_resource **out); -extern void vmw_resource_unreserve(struct list_head *list); +extern void vmw_resource_unreserve(struct vmw_resource *res, + struct vmw_dma_buffer *new_backup, + unsigned long new_backup_offset); +extern void vmw_resource_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem); +extern void vmw_fence_single_bo(struct ttm_buffer_object *bo, + struct vmw_fence_obj *fence); +extern void vmw_resource_evict_all(struct vmw_private *dev_priv); /** * DMA buffer helper routines - vmwgfx_dmabuf.c @@ -538,10 +585,9 @@ extern int vmw_execbuf_process(struct drm_file *file_priv, struct drm_vmw_fence_rep __user *user_fence_rep, struct vmw_fence_obj **out_fence); - -extern void -vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, - bool only_on_cid_match, uint32_t cid); +extern void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, + struct vmw_fence_obj *fence); +extern void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv); extern int vmw_execbuf_fence_commands(struct drm_file *file_priv, struct vmw_private *dev_priv, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index e5775a0db49..534c96703c3 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -30,6 +30,181 @@ #include <drm/ttm/ttm_bo_api.h> #include <drm/ttm/ttm_placement.h> +#define VMW_RES_HT_ORDER 12 + +/** + * struct vmw_resource_relocation - Relocation info for resources + * + * @head: List head for the software context's relocation list. + * @res: Non-ref-counted pointer to the resource. + * @offset: Offset of 4 byte entries into the command buffer where the + * id that needs fixup is located. + */ +struct vmw_resource_relocation { + struct list_head head; + const struct vmw_resource *res; + unsigned long offset; +}; + +/** + * struct vmw_resource_val_node - Validation info for resources + * + * @head: List head for the software context's resource list. + * @hash: Hash entry for quick resouce to val_node lookup. + * @res: Ref-counted pointer to the resource. + * @switch_backup: Boolean whether to switch backup buffer on unreserve. + * @new_backup: Refcounted pointer to the new backup buffer. + * @new_backup_offset: New backup buffer offset if @new_backup is non-NUll. + * @first_usage: Set to true the first time the resource is referenced in + * the command stream. + * @no_buffer_needed: Resources do not need to allocate buffer backup on + * reservation. The command stream will provide one. + */ +struct vmw_resource_val_node { + struct list_head head; + struct drm_hash_item hash; + struct vmw_resource *res; + struct vmw_dma_buffer *new_backup; + unsigned long new_backup_offset; + bool first_usage; + bool no_buffer_needed; +}; + +/** + * vmw_resource_unreserve - unreserve resources previously reserved for + * command submission. + * + * @list_head: list of resources to unreserve. + * @backoff: Whether command submission failed. + */ +static void vmw_resource_list_unreserve(struct list_head *list, + bool backoff) +{ + struct vmw_resource_val_node *val; + + list_for_each_entry(val, list, head) { + struct vmw_resource *res = val->res; + struct vmw_dma_buffer *new_backup = + backoff ? NULL : val->new_backup; + + vmw_resource_unreserve(res, new_backup, + val->new_backup_offset); + vmw_dmabuf_unreference(&val->new_backup); + } +} + + +/** + * vmw_resource_val_add - Add a resource to the software context's + * resource list if it's not already on it. + * + * @sw_context: Pointer to the software context. + * @res: Pointer to the resource. + * @p_node On successful return points to a valid pointer to a + * struct vmw_resource_val_node, if non-NULL on entry. + */ +static int vmw_resource_val_add(struct vmw_sw_context *sw_context, + struct vmw_resource *res, + struct vmw_resource_val_node **p_node) +{ + struct vmw_resource_val_node *node; + struct drm_hash_item *hash; + int ret; + + if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) res, + &hash) == 0)) { + node = container_of(hash, struct vmw_resource_val_node, hash); + node->first_usage = false; + if (unlikely(p_node != NULL)) + *p_node = node; + return 0; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (unlikely(node == NULL)) { + DRM_ERROR("Failed to allocate a resource validation " + "entry.\n"); + return -ENOMEM; + } + + node->hash.key = (unsigned long) res; + ret = drm_ht_insert_item(&sw_context->res_ht, &node->hash); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to initialize a resource validation " + "entry.\n"); + kfree(node); + return ret; + } + list_add_tail(&node->head, &sw_context->resource_list); + node->res = vmw_resource_reference(res); + node->first_usage = true; + + if (unlikely(p_node != NULL)) + *p_node = node; + + return 0; +} + +/** + * vmw_resource_relocation_add - Add a relocation to the relocation list + * + * @list: Pointer to head of relocation list. + * @res: The resource. + * @offset: Offset into the command buffer currently being parsed where the + * id that needs fixup is located. Granularity is 4 bytes. + */ +static int vmw_resource_relocation_add(struct list_head *list, + const struct vmw_resource *res, + unsigned long offset) +{ + struct vmw_resource_relocation *rel; + + rel = kmalloc(sizeof(*rel), GFP_KERNEL); + if (unlikely(rel == NULL)) { + DRM_ERROR("Failed to allocate a resource relocation.\n"); + return -ENOMEM; + } + + rel->res = res; + rel->offset = offset; + list_add_tail(&rel->head, list); + + return 0; +} + +/** + * vmw_resource_relocations_free - Free all relocations on a list + * + * @list: Pointer to the head of the relocation list. + */ +static void vmw_resource_relocations_free(struct list_head *list) +{ + struct vmw_resource_relocation *rel, *n; + + list_for_each_entry_safe(rel, n, list, head) { + list_del(&rel->head); + kfree(rel); + } +} + +/** + * vmw_resource_relocations_apply - Apply all relocations on a list + * + * @cb: Pointer to the start of the command buffer bein patch. This need + * not be the same buffer as the one being parsed when the relocation + * list was built, but the contents must be the same modulo the + * resource ids. + * @list: Pointer to the head of the relocation list. + */ +static void vmw_resource_relocations_apply(uint32_t *cb, + struct list_head *list) +{ + struct vmw_resource_relocation *rel; + + list_for_each_entry(rel, list, head) + cb[rel->offset] = rel->res->id; +} + static int vmw_cmd_invalid(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) @@ -44,25 +219,11 @@ static int vmw_cmd_ok(struct vmw_private *dev_priv, return 0; } -static void vmw_resource_to_validate_list(struct vmw_sw_context *sw_context, - struct vmw_resource **p_res) -{ - struct vmw_resource *res = *p_res; - - if (list_empty(&res->validate_head)) { - list_add_tail(&res->validate_head, &sw_context->resource_list); - *p_res = NULL; - } else - vmw_resource_unreference(p_res); -} - /** * vmw_bo_to_validate_list - add a bo to a validate list * * @sw_context: The software context used for this command submission batch. * @bo: The buffer object to add. - * @fence_flags: Fence flags to be or'ed with any other fence flags for - * this buffer on this submission batch. * @p_val_node: If non-NULL Will be updated with the validate node number * on return. * @@ -74,21 +235,37 @@ static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, uint32_t *p_val_node) { uint32_t val_node; + struct vmw_validate_buffer *vval_buf; struct ttm_validate_buffer *val_buf; + struct drm_hash_item *hash; + int ret; - val_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf); - - if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { - DRM_ERROR("Max number of DMA buffers per submission" - " exceeded.\n"); - return -EINVAL; - } - - val_buf = &sw_context->val_bufs[val_node]; - if (unlikely(val_node == sw_context->cur_val_buf)) { + if (likely(drm_ht_find_item(&sw_context->res_ht, (unsigned long) bo, + &hash) == 0)) { + vval_buf = container_of(hash, struct vmw_validate_buffer, + hash); + val_buf = &vval_buf->base; + val_node = vval_buf - sw_context->val_bufs; + } else { + val_node = sw_context->cur_val_buf; + if (unlikely(val_node >= VMWGFX_MAX_VALIDATIONS)) { + DRM_ERROR("Max number of DMA buffers per submission " + "exceeded.\n"); + return -EINVAL; + } + vval_buf = &sw_context->val_bufs[val_node]; + vval_buf->hash.key = (unsigned long) bo; + ret = drm_ht_insert_item(&sw_context->res_ht, &vval_buf->hash); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to initialize a buffer validation " + "entry.\n"); + return ret; + } + ++sw_context->cur_val_buf; + val_buf = &vval_buf->base; val_buf->bo = ttm_bo_reference(bo); + val_buf->reserved = false; list_add_tail(&val_buf->head, &sw_context->validate_nodes); - ++sw_context->cur_val_buf; } sw_context->fence_flags |= DRM_VMW_FENCE_FLAG_EXEC; @@ -99,85 +276,174 @@ static int vmw_bo_to_validate_list(struct vmw_sw_context *sw_context, return 0; } -static int vmw_cmd_cid_check(struct vmw_private *dev_priv, - struct vmw_sw_context *sw_context, - SVGA3dCmdHeader *header) +/** + * vmw_resources_reserve - Reserve all resources on the sw_context's + * resource list. + * + * @sw_context: Pointer to the software context. + * + * Note that since vmware's command submission currently is protected by + * the cmdbuf mutex, no fancy deadlock avoidance is required for resources, + * since only a single thread at once will attempt this. + */ +static int vmw_resources_reserve(struct vmw_sw_context *sw_context) { - struct vmw_resource *ctx; - - struct vmw_cid_cmd { - SVGA3dCmdHeader header; - __le32 cid; - } *cmd; + struct vmw_resource_val_node *val; int ret; - cmd = container_of(header, struct vmw_cid_cmd, header); - if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid)) - return 0; + list_for_each_entry(val, &sw_context->resource_list, head) { + struct vmw_resource *res = val->res; - ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid, - &ctx); - if (unlikely(ret != 0)) { - DRM_ERROR("Could not find or use context %u\n", - (unsigned) cmd->cid); - return ret; + ret = vmw_resource_reserve(res, val->no_buffer_needed); + if (unlikely(ret != 0)) + return ret; + + if (res->backup) { + struct ttm_buffer_object *bo = &res->backup->base; + + ret = vmw_bo_to_validate_list + (sw_context, bo, NULL); + + if (unlikely(ret != 0)) + return ret; + } } + return 0; +} - sw_context->last_cid = cmd->cid; - sw_context->cid_valid = true; - sw_context->cur_ctx = ctx; - vmw_resource_to_validate_list(sw_context, &ctx); +/** + * vmw_resources_validate - Validate all resources on the sw_context's + * resource list. + * + * @sw_context: Pointer to the software context. + * + * Before this function is called, all resource backup buffers must have + * been validated. + */ +static int vmw_resources_validate(struct vmw_sw_context *sw_context) +{ + struct vmw_resource_val_node *val; + int ret; + + list_for_each_entry(val, &sw_context->resource_list, head) { + struct vmw_resource *res = val->res; + ret = vmw_resource_validate(res); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Failed to validate resource.\n"); + return ret; + } + } return 0; } -static int vmw_cmd_sid_check(struct vmw_private *dev_priv, +/** + * vmw_cmd_res_check - Check that a resource is present and if so, put it + * on the resource validate list unless it's already there. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @res_type: Resource type. + * @converter: User-space visisble type specific information. + * @id: Pointer to the location in the command buffer currently being + * parsed from where the user-space resource id handle is located. + */ +static int vmw_cmd_res_check(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, - uint32_t *sid) + enum vmw_res_type res_type, + const struct vmw_user_resource_conv *converter, + uint32_t *id, + struct vmw_resource_val_node **p_val) { - struct vmw_surface *srf; - int ret; + struct vmw_res_cache_entry *rcache = + &sw_context->res_cache[res_type]; struct vmw_resource *res; + struct vmw_resource_val_node *node; + int ret; - if (*sid == SVGA3D_INVALID_ID) + if (*id == SVGA3D_INVALID_ID) return 0; - if (likely((sw_context->sid_valid && - *sid == sw_context->last_sid))) { - *sid = sw_context->sid_translation; - return 0; - } + /* + * Fastpath in case of repeated commands referencing the same + * resource + */ - ret = vmw_user_surface_lookup_handle(dev_priv, - sw_context->tfile, - *sid, &srf); - if (unlikely(ret != 0)) { - DRM_ERROR("Could ot find or use surface 0x%08x " - "address 0x%08lx\n", - (unsigned int) *sid, - (unsigned long) sid); - return ret; + if (likely(rcache->valid && *id == rcache->handle)) { + const struct vmw_resource *res = rcache->res; + + rcache->node->first_usage = false; + if (p_val) + *p_val = rcache->node; + + return vmw_resource_relocation_add + (&sw_context->res_relocations, res, + id - sw_context->buf_start); } - ret = vmw_surface_validate(dev_priv, srf); + ret = vmw_user_resource_lookup_handle(dev_priv, + sw_context->tfile, + *id, + converter, + &res); if (unlikely(ret != 0)) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Could not validate surface.\n"); - vmw_surface_unreference(&srf); + DRM_ERROR("Could not find or use resource 0x%08x.\n", + (unsigned) *id); + dump_stack(); return ret; } - sw_context->last_sid = *sid; - sw_context->sid_valid = true; - sw_context->sid_translation = srf->res.id; - *sid = sw_context->sid_translation; + rcache->valid = true; + rcache->res = res; + rcache->handle = *id; - res = &srf->res; - vmw_resource_to_validate_list(sw_context, &res); + ret = vmw_resource_relocation_add(&sw_context->res_relocations, + res, + id - sw_context->buf_start); + if (unlikely(ret != 0)) + goto out_no_reloc; + + ret = vmw_resource_val_add(sw_context, res, &node); + if (unlikely(ret != 0)) + goto out_no_reloc; + rcache->node = node; + if (p_val) + *p_val = node; + vmw_resource_unreference(&res); return 0; + +out_no_reloc: + BUG_ON(sw_context->error_resource != NULL); + sw_context->error_resource = res; + + return ret; } +/** + * vmw_cmd_cid_check - Check a command header for valid context information. + * + * @dev_priv: Pointer to a device private structure. + * @sw_context: Pointer to the software context. + * @header: A command header with an embedded user-space context handle. + * + * Convenience function: Call vmw_cmd_res_check with the user-space context + * handle embedded in @header. + */ +static int vmw_cmd_cid_check(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_cid_cmd { + SVGA3dCmdHeader header; + __le32 cid; + } *cmd; + + cmd = container_of(header, struct vmw_cid_cmd, header); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + user_context_converter, &cmd->cid, NULL); +} static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, @@ -194,7 +460,9 @@ static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv, return ret; cmd = container_of(header, struct vmw_sid_cmd, header); - ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.target.sid); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cmd->body.target.sid, NULL); return ret; } @@ -209,10 +477,14 @@ static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv, int ret; cmd = container_of(header, struct vmw_sid_cmd, header); - ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cmd->body.src.sid, NULL); if (unlikely(ret != 0)) return ret; - return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cmd->body.dest.sid, NULL); } static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, @@ -226,10 +498,14 @@ static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv, int ret; cmd = container_of(header, struct vmw_sid_cmd, header); - ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cmd->body.src.sid, NULL); if (unlikely(ret != 0)) return ret; - return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cmd->body.dest.sid, NULL); } static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, @@ -248,7 +524,9 @@ static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv, return -EPERM; } - return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cmd->body.srcImage.sid, NULL); } static int vmw_cmd_present_check(struct vmw_private *dev_priv, @@ -268,14 +546,15 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv, return -EPERM; } - return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid); + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, &cmd->body.sid, + NULL); } /** * vmw_query_bo_switch_prepare - Prepare to switch pinned buffer for queries. * * @dev_priv: The device private structure. - * @cid: The hardware context for the next query. * @new_query_bo: The new buffer holding query results. * @sw_context: The software context used for this command submission. * @@ -283,18 +562,18 @@ static int vmw_cmd_present_check(struct vmw_private *dev_priv, * query results, and if another buffer currently is pinned for query * results. If so, the function prepares the state of @sw_context for * switching pinned buffers after successful submission of the current - * command batch. It also checks whether we're using a new query context. - * In that case, it makes sure we emit a query barrier for the old - * context before the current query buffer is fenced. + * command batch. */ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, - uint32_t cid, struct ttm_buffer_object *new_query_bo, struct vmw_sw_context *sw_context) { + struct vmw_res_cache_entry *ctx_entry = + &sw_context->res_cache[vmw_res_context]; int ret; - bool add_cid = false; - uint32_t cid_to_add; + + BUG_ON(!ctx_entry->valid); + sw_context->last_query_ctx = ctx_entry->res; if (unlikely(new_query_bo != sw_context->cur_query_bo)) { @@ -304,9 +583,7 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, } if (unlikely(sw_context->cur_query_bo != NULL)) { - BUG_ON(!sw_context->query_cid_valid); - add_cid = true; - cid_to_add = sw_context->cur_query_cid; + sw_context->needs_post_query_barrier = true; ret = vmw_bo_to_validate_list(sw_context, sw_context->cur_query_bo, NULL); @@ -323,27 +600,6 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, } - if (unlikely(cid != sw_context->cur_query_cid && - sw_context->query_cid_valid)) { - add_cid = true; - cid_to_add = sw_context->cur_query_cid; - } - - sw_context->cur_query_cid = cid; - sw_context->query_cid_valid = true; - - if (add_cid) { - struct vmw_resource *ctx = sw_context->cur_ctx; - - if (list_empty(&ctx->query_head)) - list_add_tail(&ctx->query_head, - &sw_context->query_list); - ret = vmw_bo_to_validate_list(sw_context, - dev_priv->dummy_query_bo, - NULL); - if (unlikely(ret != 0)) - return ret; - } return 0; } @@ -355,10 +611,9 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, * @sw_context: The software context used for this command submission batch. * * This function will check if we're switching query buffers, and will then, - * if no other query waits are issued this command submission batch, * issue a dummy occlusion query wait used as a query barrier. When the fence * object following that query wait has signaled, we are sure that all - * preseding queries have finished, and the old query buffer can be unpinned. + * preceding queries have finished, and the old query buffer can be unpinned. * However, since both the new query buffer and the old one are fenced with * that fence, we can do an asynchronus unpin now, and be sure that the * old query buffer won't be moved until the fence has signaled. @@ -369,20 +624,19 @@ static int vmw_query_bo_switch_prepare(struct vmw_private *dev_priv, static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context) { - - struct vmw_resource *ctx, *next_ctx; - int ret; - /* * The validate list should still hold references to all * contexts here. */ - list_for_each_entry_safe(ctx, next_ctx, &sw_context->query_list, - query_head) { - list_del_init(&ctx->query_head); + if (sw_context->needs_post_query_barrier) { + struct vmw_res_cache_entry *ctx_entry = + &sw_context->res_cache[vmw_res_context]; + struct vmw_resource *ctx; + int ret; - BUG_ON(list_empty(&ctx->validate_head)); + BUG_ON(!ctx_entry->valid); + ctx = ctx_entry->res; ret = vmw_fifo_emit_dummy_query(dev_priv, ctx->id); @@ -396,40 +650,46 @@ static void vmw_query_bo_switch_commit(struct vmw_private *dev_priv, ttm_bo_unref(&dev_priv->pinned_bo); } - vmw_bo_pin(sw_context->cur_query_bo, true); + if (!sw_context->needs_post_query_barrier) { + vmw_bo_pin(sw_context->cur_query_bo, true); - /* - * We pin also the dummy_query_bo buffer so that we - * don't need to validate it when emitting - * dummy queries in context destroy paths. - */ + /* + * We pin also the dummy_query_bo buffer so that we + * don't need to validate it when emitting + * dummy queries in context destroy paths. + */ - vmw_bo_pin(dev_priv->dummy_query_bo, true); - dev_priv->dummy_query_bo_pinned = true; + vmw_bo_pin(dev_priv->dummy_query_bo, true); + dev_priv->dummy_query_bo_pinned = true; - dev_priv->query_cid = sw_context->cur_query_cid; - dev_priv->pinned_bo = - ttm_bo_reference(sw_context->cur_query_bo); + BUG_ON(sw_context->last_query_ctx == NULL); + dev_priv->query_cid = sw_context->last_query_ctx->id; + dev_priv->query_cid_valid = true; + dev_priv->pinned_bo = + ttm_bo_reference(sw_context->cur_query_bo); + } } } /** - * vmw_query_switch_backoff - clear query barrier list - * @sw_context: The sw context used for this submission batch. + * vmw_translate_guest_pointer - Prepare to translate a user-space buffer + * handle to a valid SVGAGuestPtr * - * This function is used as part of an error path, where a previously - * set up list of query barriers needs to be cleared. + * @dev_priv: Pointer to a device private structure. + * @sw_context: The software context used for this command batch validation. + * @ptr: Pointer to the user-space handle to be translated. + * @vmw_bo_p: Points to a location that, on successful return will carry + * a reference-counted pointer to the DMA buffer identified by the + * user-space handle in @id. * + * This function saves information needed to translate a user-space buffer + * handle to a valid SVGAGuestPtr. The translation does not take place + * immediately, but during a call to vmw_apply_relocations(). + * This function builds a relocation list and a list of buffers to validate. + * The former needs to be freed using either vmw_apply_relocations() or + * vmw_free_relocations(). The latter needs to be freed using + * vmw_clear_validations. */ -static void vmw_query_switch_backoff(struct vmw_sw_context *sw_context) -{ - struct list_head *list, *next; - - list_for_each_safe(list, next, &sw_context->query_list) { - list_del_init(list); - } -} - static int vmw_translate_guest_ptr(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGAGuestPtr *ptr, @@ -471,6 +731,37 @@ out_no_reloc: return ret; } +/** + * vmw_cmd_begin_query - validate a SVGA_3D_CMD_BEGIN_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_begin_query(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_begin_query_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdBeginQuery q; + } *cmd; + + cmd = container_of(header, struct vmw_begin_query_cmd, + header); + + return vmw_cmd_res_check(dev_priv, sw_context, vmw_res_context, + user_context_converter, &cmd->q.cid, + NULL); +} + +/** + * vmw_cmd_end_query - validate a SVGA_3D_CMD_END_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ static int vmw_cmd_end_query(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) @@ -493,13 +784,19 @@ static int vmw_cmd_end_query(struct vmw_private *dev_priv, if (unlikely(ret != 0)) return ret; - ret = vmw_query_bo_switch_prepare(dev_priv, cmd->q.cid, - &vmw_bo->base, sw_context); + ret = vmw_query_bo_switch_prepare(dev_priv, &vmw_bo->base, sw_context); vmw_dmabuf_unreference(&vmw_bo); return ret; } +/* + * vmw_cmd_wait_query - validate a SVGA_3D_CMD_WAIT_QUERY command. + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context used for this command submission. + * @header: Pointer to the command header in the command stream. + */ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, SVGA3dCmdHeader *header) @@ -510,7 +807,6 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, SVGA3dCmdWaitForQuery q; } *cmd; int ret; - struct vmw_resource *ctx; cmd = container_of(header, struct vmw_query_cmd, header); ret = vmw_cmd_cid_check(dev_priv, sw_context, header); @@ -524,16 +820,6 @@ static int vmw_cmd_wait_query(struct vmw_private *dev_priv, return ret; vmw_dmabuf_unreference(&vmw_bo); - - /* - * This wait will act as a barrier for previous waits for this - * context. - */ - - ctx = sw_context->cur_ctx; - if (!list_empty(&ctx->query_head)) - list_del_init(&ctx->query_head); - return 0; } @@ -542,14 +828,12 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, SVGA3dCmdHeader *header) { struct vmw_dma_buffer *vmw_bo = NULL; - struct ttm_buffer_object *bo; struct vmw_surface *srf = NULL; struct vmw_dma_cmd { SVGA3dCmdHeader header; SVGA3dCmdSurfaceDMA dma; } *cmd; int ret; - struct vmw_resource *res; cmd = container_of(header, struct vmw_dma_cmd, header); ret = vmw_translate_guest_ptr(dev_priv, sw_context, @@ -558,37 +842,20 @@ static int vmw_cmd_dma(struct vmw_private *dev_priv, if (unlikely(ret != 0)) return ret; - bo = &vmw_bo->base; - ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile, - cmd->dma.host.sid, &srf); - if (ret) { - DRM_ERROR("could not find surface\n"); - goto out_no_reloc; - } - - ret = vmw_surface_validate(dev_priv, srf); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, &cmd->dma.host.sid, + NULL); if (unlikely(ret != 0)) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Culd not validate surface.\n"); - goto out_no_validate; + if (unlikely(ret != -ERESTARTSYS)) + DRM_ERROR("could not find surface for DMA.\n"); + goto out_no_surface; } - /* - * Patch command stream with device SID. - */ - cmd->dma.host.sid = srf->res.id; - vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header); - - vmw_dmabuf_unreference(&vmw_bo); - - res = &srf->res; - vmw_resource_to_validate_list(sw_context, &res); + srf = vmw_res_to_srf(sw_context->res_cache[vmw_res_surface].res); - return 0; + vmw_kms_cursor_snoop(srf, sw_context->tfile, &vmw_bo->base, header); -out_no_validate: - vmw_surface_unreference(&srf); -out_no_reloc: +out_no_surface: vmw_dmabuf_unreference(&vmw_bo); return ret; } @@ -621,8 +888,9 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv, } for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) { - ret = vmw_cmd_sid_check(dev_priv, sw_context, - &decl->array.surfaceId); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &decl->array.surfaceId, NULL); if (unlikely(ret != 0)) return ret; } @@ -636,8 +904,9 @@ static int vmw_cmd_draw(struct vmw_private *dev_priv, range = (SVGA3dPrimitiveRange *) decl; for (i = 0; i < cmd->body.numRanges; ++i, ++range) { - ret = vmw_cmd_sid_check(dev_priv, sw_context, - &range->indexArray.surfaceId); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &range->indexArray.surfaceId, NULL); if (unlikely(ret != 0)) return ret; } @@ -668,8 +937,9 @@ static int vmw_cmd_tex_state(struct vmw_private *dev_priv, if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE)) continue; - ret = vmw_cmd_sid_check(dev_priv, sw_context, - &cur_state->value); + ret = vmw_cmd_res_check(dev_priv, sw_context, vmw_res_surface, + user_surface_converter, + &cur_state->value, NULL); if (unlikely(ret != 0)) return ret; } @@ -700,6 +970,34 @@ static int vmw_cmd_check_define_gmrfb(struct vmw_private *dev_priv, return ret; } +/** + * vmw_cmd_set_shader - Validate an SVGA_3D_CMD_SET_SHADER + * command + * + * @dev_priv: Pointer to a device private struct. + * @sw_context: The software context being used for this batch. + * @header: Pointer to the command header in the command stream. + */ +static int vmw_cmd_set_shader(struct vmw_private *dev_priv, + struct vmw_sw_context *sw_context, + SVGA3dCmdHeader *header) +{ + struct vmw_set_shader_cmd { + SVGA3dCmdHeader header; + SVGA3dCmdSetShader body; + } *cmd; + int ret; + + cmd = container_of(header, struct vmw_set_shader_cmd, + header); + + ret = vmw_cmd_cid_check(dev_priv, sw_context, header); + if (unlikely(ret != 0)) + return ret; + + return 0; +} + static int vmw_cmd_check_not_3d(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context, void *buf, uint32_t *size) @@ -773,16 +1071,20 @@ static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = { VMW_CMD_DEF(SVGA_3D_CMD_PRESENT, &vmw_cmd_present_check), VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DEFINE, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_cid_check), - VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_cid_check), + VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_set_shader), VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_cid_check), VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw), VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check), - VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check), + VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_begin_query), VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_end_query), VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_wait_query), VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok), VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN, - &vmw_cmd_blt_surf_screen_check) + &vmw_cmd_blt_surf_screen_check), + VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE_V2, &vmw_cmd_invalid), + VMW_CMD_DEF(SVGA_3D_CMD_GENERATE_MIPMAPS, &vmw_cmd_invalid), + VMW_CMD_DEF(SVGA_3D_CMD_ACTIVATE_SURFACE, &vmw_cmd_invalid), + VMW_CMD_DEF(SVGA_3D_CMD_DEACTIVATE_SURFACE, &vmw_cmd_invalid), }; static int vmw_cmd_check(struct vmw_private *dev_priv, @@ -829,6 +1131,8 @@ static int vmw_cmd_check_all(struct vmw_private *dev_priv, int32_t cur_size = size; int ret; + sw_context->buf_start = buf; + while (cur_size > 0) { size = cur_size; ret = vmw_cmd_check(dev_priv, sw_context, buf, &size); @@ -860,43 +1164,63 @@ static void vmw_apply_relocations(struct vmw_sw_context *sw_context) for (i = 0; i < sw_context->cur_reloc; ++i) { reloc = &sw_context->relocs[i]; - validate = &sw_context->val_bufs[reloc->index]; + validate = &sw_context->val_bufs[reloc->index].base; bo = validate->bo; - if (bo->mem.mem_type == TTM_PL_VRAM) { + switch (bo->mem.mem_type) { + case TTM_PL_VRAM: reloc->location->offset += bo->offset; reloc->location->gmrId = SVGA_GMR_FRAMEBUFFER; - } else + break; + case VMW_PL_GMR: reloc->location->gmrId = bo->mem.start; + break; + default: + BUG(); + } } vmw_free_relocations(sw_context); } +/** + * vmw_resource_list_unrefererence - Free up a resource list and unreference + * all resources referenced by it. + * + * @list: The resource list. + */ +static void vmw_resource_list_unreference(struct list_head *list) +{ + struct vmw_resource_val_node *val, *val_next; + + /* + * Drop references to resources held during command submission. + */ + + list_for_each_entry_safe(val, val_next, list, head) { + list_del_init(&val->head); + vmw_resource_unreference(&val->res); + kfree(val); + } +} + static void vmw_clear_validations(struct vmw_sw_context *sw_context) { - struct ttm_validate_buffer *entry, *next; - struct vmw_resource *res, *res_next; + struct vmw_validate_buffer *entry, *next; + struct vmw_resource_val_node *val; /* * Drop references to DMA buffers held during command submission. */ list_for_each_entry_safe(entry, next, &sw_context->validate_nodes, - head) { - list_del(&entry->head); - vmw_dmabuf_validate_clear(entry->bo); - ttm_bo_unref(&entry->bo); + base.head) { + list_del(&entry->base.head); + ttm_bo_unref(&entry->base.bo); + (void) drm_ht_remove_item(&sw_context->res_ht, &entry->hash); sw_context->cur_val_buf--; } BUG_ON(sw_context->cur_val_buf != 0); - /* - * Drop references to resources held during command submission. - */ - vmw_resource_unreserve(&sw_context->resource_list); - list_for_each_entry_safe(res, res_next, &sw_context->resource_list, - validate_head) { - list_del_init(&res->validate_head); - vmw_resource_unreference(&res); - } + list_for_each_entry(val, &sw_context->resource_list, head) + (void) drm_ht_remove_item(&sw_context->res_ht, &val->hash); } static int vmw_validate_single_buffer(struct vmw_private *dev_priv, @@ -939,11 +1263,11 @@ static int vmw_validate_single_buffer(struct vmw_private *dev_priv, static int vmw_validate_buffers(struct vmw_private *dev_priv, struct vmw_sw_context *sw_context) { - struct ttm_validate_buffer *entry; + struct vmw_validate_buffer *entry; int ret; - list_for_each_entry(entry, &sw_context->validate_nodes, head) { - ret = vmw_validate_single_buffer(dev_priv, entry->bo); + list_for_each_entry(entry, &sw_context->validate_nodes, base.head) { + ret = vmw_validate_single_buffer(dev_priv, entry->base.bo); if (unlikely(ret != 0)) return ret; } @@ -1106,6 +1430,8 @@ int vmw_execbuf_process(struct drm_file *file_priv, { struct vmw_sw_context *sw_context = &dev_priv->ctx; struct vmw_fence_obj *fence = NULL; + struct vmw_resource *error_resource; + struct list_head resource_list; uint32_t handle; void *cmd; int ret; @@ -1135,24 +1461,33 @@ int vmw_execbuf_process(struct drm_file *file_priv, sw_context->kernel = true; sw_context->tfile = vmw_fpriv(file_priv)->tfile; - sw_context->cid_valid = false; - sw_context->sid_valid = false; sw_context->cur_reloc = 0; sw_context->cur_val_buf = 0; sw_context->fence_flags = 0; - INIT_LIST_HEAD(&sw_context->query_list); INIT_LIST_HEAD(&sw_context->resource_list); sw_context->cur_query_bo = dev_priv->pinned_bo; - sw_context->cur_query_cid = dev_priv->query_cid; - sw_context->query_cid_valid = (dev_priv->pinned_bo != NULL); - + sw_context->last_query_ctx = NULL; + sw_context->needs_post_query_barrier = false; + memset(sw_context->res_cache, 0, sizeof(sw_context->res_cache)); INIT_LIST_HEAD(&sw_context->validate_nodes); + INIT_LIST_HEAD(&sw_context->res_relocations); + if (!sw_context->res_ht_initialized) { + ret = drm_ht_create(&sw_context->res_ht, VMW_RES_HT_ORDER); + if (unlikely(ret != 0)) + goto out_unlock; + sw_context->res_ht_initialized = true; + } + INIT_LIST_HEAD(&resource_list); ret = vmw_cmd_check_all(dev_priv, sw_context, kernel_commands, command_size); if (unlikely(ret != 0)) goto out_err; + ret = vmw_resources_reserve(sw_context); + if (unlikely(ret != 0)) + goto out_err; + ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes); if (unlikely(ret != 0)) goto out_err; @@ -1161,24 +1496,31 @@ int vmw_execbuf_process(struct drm_file *file_priv, if (unlikely(ret != 0)) goto out_err; - vmw_apply_relocations(sw_context); + ret = vmw_resources_validate(sw_context); + if (unlikely(ret != 0)) + goto out_err; if (throttle_us) { ret = vmw_wait_lag(dev_priv, &dev_priv->fifo.marker_queue, throttle_us); if (unlikely(ret != 0)) - goto out_throttle; + goto out_err; } cmd = vmw_fifo_reserve(dev_priv, command_size); if (unlikely(cmd == NULL)) { DRM_ERROR("Failed reserving fifo space for commands.\n"); ret = -ENOMEM; - goto out_throttle; + goto out_err; } + vmw_apply_relocations(sw_context); memcpy(cmd, kernel_commands, command_size); + + vmw_resource_relocations_apply(cmd, &sw_context->res_relocations); + vmw_resource_relocations_free(&sw_context->res_relocations); + vmw_fifo_commit(dev_priv, command_size); vmw_query_bo_switch_commit(dev_priv, sw_context); @@ -1194,9 +1536,14 @@ int vmw_execbuf_process(struct drm_file *file_priv, if (ret != 0) DRM_ERROR("Fence submission error. Syncing.\n"); + vmw_resource_list_unreserve(&sw_context->resource_list, false); ttm_eu_fence_buffer_objects(&sw_context->validate_nodes, (void *) fence); + if (unlikely(dev_priv->pinned_bo != NULL && + !dev_priv->query_cid_valid)) + __vmw_execbuf_release_pinned_bo(dev_priv, fence); + vmw_clear_validations(sw_context); vmw_execbuf_copy_fence_user(dev_priv, vmw_fpriv(file_priv), ret, user_fence_rep, fence, handle); @@ -1209,17 +1556,40 @@ int vmw_execbuf_process(struct drm_file *file_priv, vmw_fence_obj_unreference(&fence); } + list_splice_init(&sw_context->resource_list, &resource_list); mutex_unlock(&dev_priv->cmdbuf_mutex); + + /* + * Unreference resources outside of the cmdbuf_mutex to + * avoid deadlocks in resource destruction paths. + */ + vmw_resource_list_unreference(&resource_list); + return 0; out_err: + vmw_resource_relocations_free(&sw_context->res_relocations); vmw_free_relocations(sw_context); -out_throttle: - vmw_query_switch_backoff(sw_context); ttm_eu_backoff_reservation(&sw_context->validate_nodes); + vmw_resource_list_unreserve(&sw_context->resource_list, true); vmw_clear_validations(sw_context); + if (unlikely(dev_priv->pinned_bo != NULL && + !dev_priv->query_cid_valid)) + __vmw_execbuf_release_pinned_bo(dev_priv, NULL); out_unlock: + list_splice_init(&sw_context->resource_list, &resource_list); + error_resource = sw_context->error_resource; + sw_context->error_resource = NULL; mutex_unlock(&dev_priv->cmdbuf_mutex); + + /* + * Unreference resources outside of the cmdbuf_mutex to + * avoid deadlocks in resource destruction paths. + */ + vmw_resource_list_unreference(&resource_list); + if (unlikely(error_resource != NULL)) + vmw_resource_unreference(&error_resource); + return ret; } @@ -1244,13 +1614,13 @@ static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv) /** - * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * __vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned * query bo. * * @dev_priv: The device private structure. - * @only_on_cid_match: Only flush and unpin if the current active query cid - * matches @cid. - * @cid: Optional context id to match. + * @fence: If non-NULL should point to a struct vmw_fence_obj issued + * _after_ a query barrier that flushes all queries touching the current + * buffer pointed to by @dev_priv->pinned_bo * * This function should be used to unpin the pinned query bo, or * as a query barrier when we need to make sure that all queries have @@ -1263,23 +1633,21 @@ static void vmw_execbuf_unpin_panic(struct vmw_private *dev_priv) * * The function will synchronize on the previous query barrier, and will * thus not finish until that barrier has executed. + * + * the @dev_priv->cmdbuf_mutex needs to be held by the current thread + * before calling this function. */ -void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, - bool only_on_cid_match, uint32_t cid) +void __vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, + struct vmw_fence_obj *fence) { int ret = 0; struct list_head validate_list; struct ttm_validate_buffer pinned_val, query_val; - struct vmw_fence_obj *fence; - - mutex_lock(&dev_priv->cmdbuf_mutex); + struct vmw_fence_obj *lfence = NULL; if (dev_priv->pinned_bo == NULL) goto out_unlock; - if (only_on_cid_match && cid != dev_priv->query_cid) - goto out_unlock; - INIT_LIST_HEAD(&validate_list); pinned_val.bo = ttm_bo_reference(dev_priv->pinned_bo); @@ -1297,25 +1665,34 @@ void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv, goto out_no_reserve; } - ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); - if (unlikely(ret != 0)) { - vmw_execbuf_unpin_panic(dev_priv); - goto out_no_emit; + if (dev_priv->query_cid_valid) { + BUG_ON(fence != NULL); + ret = vmw_fifo_emit_dummy_query(dev_priv, dev_priv->query_cid); + if (unlikely(ret != 0)) { + vmw_execbuf_unpin_panic(dev_priv); + goto out_no_emit; + } + dev_priv->query_cid_valid = false; } vmw_bo_pin(dev_priv->pinned_bo, false); vmw_bo_pin(dev_priv->dummy_query_bo, false); dev_priv->dummy_query_bo_pinned = false; - (void) vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + if (fence == NULL) { + (void) vmw_execbuf_fence_commands(NULL, dev_priv, &lfence, + NULL); + fence = lfence; + } ttm_eu_fence_buffer_objects(&validate_list, (void *) fence); + if (lfence != NULL) + vmw_fence_obj_unreference(&lfence); ttm_bo_unref(&query_val.bo); ttm_bo_unref(&pinned_val.bo); ttm_bo_unref(&dev_priv->pinned_bo); out_unlock: - mutex_unlock(&dev_priv->cmdbuf_mutex); return; out_no_emit: @@ -1324,6 +1701,31 @@ out_no_reserve: ttm_bo_unref(&query_val.bo); ttm_bo_unref(&pinned_val.bo); ttm_bo_unref(&dev_priv->pinned_bo); +} + +/** + * vmw_execbuf_release_pinned_bo - Flush queries and unpin the pinned + * query bo. + * + * @dev_priv: The device private structure. + * + * This function should be used to unpin the pinned query bo, or + * as a query barrier when we need to make sure that all queries have + * finished before the next fifo command. (For example on hardware + * context destructions where the hardware may otherwise leak unfinished + * queries). + * + * This function does not return any failure codes, but make attempts + * to do safe unpinning in case of errors. + * + * The function will synchronize on the previous query barrier, and will + * thus not finish until that barrier has executed. + */ +void vmw_execbuf_release_pinned_bo(struct vmw_private *dev_priv) +{ + mutex_lock(&dev_priv->cmdbuf_mutex); + if (dev_priv->query_cid_valid) + __vmw_execbuf_release_pinned_bo(dev_priv, NULL); mutex_unlock(&dev_priv->cmdbuf_mutex); } diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c index bc187fafd58..c62d20e8a6f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fence.c @@ -537,7 +537,7 @@ static void vmw_user_fence_destroy(struct vmw_fence_obj *fence) container_of(fence, struct vmw_user_fence, fence); struct vmw_fence_manager *fman = fence->fman; - kfree(ufence); + ttm_base_object_kfree(ufence, base); /* * Free kernel space accounting. */ diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c index b07ca2e4d04..2f7c08ebf56 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c @@ -131,6 +131,7 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, struct drm_vmw_rect *clips = NULL; struct drm_mode_object *obj; struct vmw_framebuffer *vfb; + struct vmw_resource *res; uint32_t num_clips; int ret; @@ -178,11 +179,13 @@ int vmw_present_ioctl(struct drm_device *dev, void *data, if (unlikely(ret != 0)) goto out_no_ttm_lock; - ret = vmw_user_surface_lookup_handle(dev_priv, tfile, arg->sid, - &surface); + ret = vmw_user_resource_lookup_handle(dev_priv, tfile, arg->sid, + user_surface_converter, + &res); if (ret) goto out_no_surface; + surface = vmw_res_to_srf(res); ret = vmw_kms_present(dev_priv, file_priv, vfb, surface, arg->sid, arg->dest_x, arg->dest_y, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c index 292c988c54e..0def4ff5b62 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c @@ -30,17 +30,7 @@ #include <drm/ttm/ttm_object.h> #include <drm/ttm/ttm_placement.h> #include <drm/drmP.h> - -struct vmw_user_context { - struct ttm_base_object base; - struct vmw_resource res; -}; - -struct vmw_user_surface { - struct ttm_base_object base; - struct vmw_surface srf; - uint32_t size; -}; +#include "vmwgfx_resource_priv.h" struct vmw_user_dma_buffer { struct ttm_base_object base; @@ -62,17 +52,21 @@ struct vmw_user_stream { struct vmw_stream stream; }; -struct vmw_surface_offset { - uint32_t face; - uint32_t mip; - uint32_t bo_offset; -}; - -static uint64_t vmw_user_context_size; -static uint64_t vmw_user_surface_size; static uint64_t vmw_user_stream_size; +static const struct vmw_res_func vmw_stream_func = { + .res_type = vmw_res_stream, + .needs_backup = false, + .may_evict = false, + .type_name = "video streams", + .backup_placement = NULL, + .create = NULL, + .destroy = NULL, + .bind = NULL, + .unbind = NULL +}; + static inline struct vmw_dma_buffer * vmw_dma_buffer(struct ttm_buffer_object *bo) { @@ -100,13 +94,14 @@ struct vmw_resource *vmw_resource_reference(struct vmw_resource *res) * * Release the resource id to the resource id manager and set it to -1 */ -static void vmw_resource_release_id(struct vmw_resource *res) +void vmw_resource_release_id(struct vmw_resource *res) { struct vmw_private *dev_priv = res->dev_priv; + struct idr *idr = &dev_priv->res_idr[res->func->res_type]; write_lock(&dev_priv->resource_lock); if (res->id != -1) - idr_remove(res->idr, res->id); + idr_remove(idr, res->id); res->id = -1; write_unlock(&dev_priv->resource_lock); } @@ -116,17 +111,33 @@ static void vmw_resource_release(struct kref *kref) struct vmw_resource *res = container_of(kref, struct vmw_resource, kref); struct vmw_private *dev_priv = res->dev_priv; - int id = res->id; - struct idr *idr = res->idr; + int id; + struct idr *idr = &dev_priv->res_idr[res->func->res_type]; res->avail = false; - if (res->remove_from_lists != NULL) - res->remove_from_lists(res); + list_del_init(&res->lru_head); write_unlock(&dev_priv->resource_lock); + if (res->backup) { + struct ttm_buffer_object *bo = &res->backup->base; + + ttm_bo_reserve(bo, false, false, false, 0); + if (!list_empty(&res->mob_head) && + res->func->unbind != NULL) { + struct ttm_validate_buffer val_buf; + + val_buf.bo = bo; + res->func->unbind(res, false, &val_buf); + } + res->backup_dirty = false; + list_del_init(&res->mob_head); + ttm_bo_unreserve(bo); + vmw_dmabuf_unreference(&res->backup); + } if (likely(res->hw_destroy != NULL)) res->hw_destroy(res); + id = res->id; if (res->res_free != NULL) res->res_free(res); else @@ -153,25 +164,25 @@ void vmw_resource_unreference(struct vmw_resource **p_res) /** * vmw_resource_alloc_id - release a resource id to the id manager. * - * @dev_priv: Pointer to the device private structure. * @res: Pointer to the resource. * * Allocate the lowest free resource from the resource manager, and set * @res->id to that id. Returns 0 on success and -ENOMEM on failure. */ -static int vmw_resource_alloc_id(struct vmw_private *dev_priv, - struct vmw_resource *res) +int vmw_resource_alloc_id(struct vmw_resource *res) { + struct vmw_private *dev_priv = res->dev_priv; int ret; + struct idr *idr = &dev_priv->res_idr[res->func->res_type]; BUG_ON(res->id != -1); do { - if (unlikely(idr_pre_get(res->idr, GFP_KERNEL) == 0)) + if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0)) return -ENOMEM; write_lock(&dev_priv->resource_lock); - ret = idr_get_new_above(res->idr, res, 1, &res->id); + ret = idr_get_new_above(idr, res, 1, &res->id); write_unlock(&dev_priv->resource_lock); } while (ret == -EAGAIN); @@ -179,31 +190,39 @@ static int vmw_resource_alloc_id(struct vmw_private *dev_priv, return ret; } - -static int vmw_resource_init(struct vmw_private *dev_priv, - struct vmw_resource *res, - struct idr *idr, - enum ttm_object_type obj_type, - bool delay_id, - void (*res_free) (struct vmw_resource *res), - void (*remove_from_lists) - (struct vmw_resource *res)) +/** + * vmw_resource_init - initialize a struct vmw_resource + * + * @dev_priv: Pointer to a device private struct. + * @res: The struct vmw_resource to initialize. + * @obj_type: Resource object type. + * @delay_id: Boolean whether to defer device id allocation until + * the first validation. + * @res_free: Resource destructor. + * @func: Resource function table. + */ +int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, + bool delay_id, + void (*res_free) (struct vmw_resource *res), + const struct vmw_res_func *func) { kref_init(&res->kref); res->hw_destroy = NULL; res->res_free = res_free; - res->remove_from_lists = remove_from_lists; - res->res_type = obj_type; - res->idr = idr; res->avail = false; res->dev_priv = dev_priv; - INIT_LIST_HEAD(&res->query_head); - INIT_LIST_HEAD(&res->validate_head); + res->func = func; + INIT_LIST_HEAD(&res->lru_head); + INIT_LIST_HEAD(&res->mob_head); res->id = -1; + res->backup = NULL; + res->backup_offset = 0; + res->backup_dirty = false; + res->res_dirty = false; if (delay_id) return 0; else - return vmw_resource_alloc_id(dev_priv, res); + return vmw_resource_alloc_id(res); } /** @@ -218,9 +237,8 @@ static int vmw_resource_init(struct vmw_private *dev_priv, * Activate basically means that the function vmw_resource_lookup will * find it. */ - -static void vmw_resource_activate(struct vmw_resource *res, - void (*hw_destroy) (struct vmw_resource *)) +void vmw_resource_activate(struct vmw_resource *res, + void (*hw_destroy) (struct vmw_resource *)) { struct vmw_private *dev_priv = res->dev_priv; @@ -250,990 +268,41 @@ struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv, } /** - * Context management: - */ - -static void vmw_hw_context_destroy(struct vmw_resource *res) -{ - - struct vmw_private *dev_priv = res->dev_priv; - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDestroyContext body; - } *cmd; - - - vmw_execbuf_release_pinned_bo(dev_priv, true, res->id); - - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for surface " - "destruction.\n"); - return; - } - - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY); - cmd->header.size = cpu_to_le32(sizeof(cmd->body)); - cmd->body.cid = cpu_to_le32(res->id); - - vmw_fifo_commit(dev_priv, sizeof(*cmd)); - vmw_3d_resource_dec(dev_priv, false); -} - -static int vmw_context_init(struct vmw_private *dev_priv, - struct vmw_resource *res, - void (*res_free) (struct vmw_resource *res)) -{ - int ret; - - struct { - SVGA3dCmdHeader header; - SVGA3dCmdDefineContext body; - } *cmd; - - ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr, - VMW_RES_CONTEXT, false, res_free, NULL); - - if (unlikely(ret != 0)) { - DRM_ERROR("Failed to allocate a resource id.\n"); - goto out_early; - } - - if (unlikely(res->id >= SVGA3D_MAX_CONTEXT_IDS)) { - DRM_ERROR("Out of hw context ids.\n"); - vmw_resource_unreference(&res); - return -ENOMEM; - } - - cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd)); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Fifo reserve failed.\n"); - vmw_resource_unreference(&res); - return -ENOMEM; - } - - cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE); - cmd->header.size = cpu_to_le32(sizeof(cmd->body)); - cmd->body.cid = cpu_to_le32(res->id); - - vmw_fifo_commit(dev_priv, sizeof(*cmd)); - (void) vmw_3d_resource_inc(dev_priv, false); - vmw_resource_activate(res, vmw_hw_context_destroy); - return 0; - -out_early: - if (res_free == NULL) - kfree(res); - else - res_free(res); - return ret; -} - -struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv) -{ - struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL); - int ret; - - if (unlikely(res == NULL)) - return NULL; - - ret = vmw_context_init(dev_priv, res, NULL); - return (ret == 0) ? res : NULL; -} - -/** - * User-space context management: - */ - -static void vmw_user_context_free(struct vmw_resource *res) -{ - struct vmw_user_context *ctx = - container_of(res, struct vmw_user_context, res); - struct vmw_private *dev_priv = res->dev_priv; - - ttm_base_object_kfree(ctx, base); - ttm_mem_global_free(vmw_mem_glob(dev_priv), - vmw_user_context_size); -} - -/** - * This function is called when user space has no more references on the - * base object. It releases the base-object's reference on the resource object. - */ - -static void vmw_user_context_base_release(struct ttm_base_object **p_base) -{ - struct ttm_base_object *base = *p_base; - struct vmw_user_context *ctx = - container_of(base, struct vmw_user_context, base); - struct vmw_resource *res = &ctx->res; - - *p_base = NULL; - vmw_resource_unreference(&res); -} - -int vmw_context_destroy_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_resource *res; - struct vmw_user_context *ctx; - struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - int ret = 0; - - res = vmw_resource_lookup(dev_priv, &dev_priv->context_idr, arg->cid); - if (unlikely(res == NULL)) - return -EINVAL; - - if (res->res_free != &vmw_user_context_free) { - ret = -EINVAL; - goto out; - } - - ctx = container_of(res, struct vmw_user_context, res); - if (ctx->base.tfile != tfile && !ctx->base.shareable) { - ret = -EPERM; - goto out; - } - - ttm_ref_object_base_unref(tfile, ctx->base.hash.key, TTM_REF_USAGE); -out: - vmw_resource_unreference(&res); - return ret; -} - -int vmw_context_define_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_context *ctx; - struct vmw_resource *res; - struct vmw_resource *tmp; - struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct vmw_master *vmaster = vmw_master(file_priv->master); - int ret; - - - /* - * Approximate idr memory usage with 128 bytes. It will be limited - * by maximum number_of contexts anyway. - */ - - if (unlikely(vmw_user_context_size == 0)) - vmw_user_context_size = ttm_round_pot(sizeof(*ctx)) + 128; - - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), - vmw_user_context_size, - false, true); - if (unlikely(ret != 0)) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Out of graphics memory for context" - " creation.\n"); - goto out_unlock; - } - - ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); - if (unlikely(ctx == NULL)) { - ttm_mem_global_free(vmw_mem_glob(dev_priv), - vmw_user_context_size); - ret = -ENOMEM; - goto out_unlock; - } - - res = &ctx->res; - ctx->base.shareable = false; - ctx->base.tfile = NULL; - - /* - * From here on, the destructor takes over resource freeing. - */ - - ret = vmw_context_init(dev_priv, res, vmw_user_context_free); - if (unlikely(ret != 0)) - goto out_unlock; - - tmp = vmw_resource_reference(&ctx->res); - ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT, - &vmw_user_context_base_release, NULL); - - if (unlikely(ret != 0)) { - vmw_resource_unreference(&tmp); - goto out_err; - } - - arg->cid = res->id; -out_err: - vmw_resource_unreference(&res); -out_unlock: - ttm_read_unlock(&vmaster->lock); - return ret; - -} - -int vmw_context_check(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - int id, - struct vmw_resource **p_res) -{ - struct vmw_resource *res; - int ret = 0; - - read_lock(&dev_priv->resource_lock); - res = idr_find(&dev_priv->context_idr, id); - if (res && res->avail) { - struct vmw_user_context *ctx = - container_of(res, struct vmw_user_context, res); - if (ctx->base.tfile != tfile && !ctx->base.shareable) - ret = -EPERM; - if (p_res) - *p_res = vmw_resource_reference(res); - } else - ret = -EINVAL; - read_unlock(&dev_priv->resource_lock); - - return ret; -} - -struct vmw_bpp { - uint8_t bpp; - uint8_t s_bpp; -}; - -/* - * Size table for the supported SVGA3D surface formats. It consists of - * two values. The bpp value and the s_bpp value which is short for - * "stride bits per pixel" The values are given in such a way that the - * minimum stride for the image is calculated using - * - * min_stride = w*s_bpp - * - * and the total memory requirement for the image is - * - * h*min_stride*bpp/s_bpp - * - */ -static const struct vmw_bpp vmw_sf_bpp[] = { - [SVGA3D_FORMAT_INVALID] = {0, 0}, - [SVGA3D_X8R8G8B8] = {32, 32}, - [SVGA3D_A8R8G8B8] = {32, 32}, - [SVGA3D_R5G6B5] = {16, 16}, - [SVGA3D_X1R5G5B5] = {16, 16}, - [SVGA3D_A1R5G5B5] = {16, 16}, - [SVGA3D_A4R4G4B4] = {16, 16}, - [SVGA3D_Z_D32] = {32, 32}, - [SVGA3D_Z_D16] = {16, 16}, - [SVGA3D_Z_D24S8] = {32, 32}, - [SVGA3D_Z_D15S1] = {16, 16}, - [SVGA3D_LUMINANCE8] = {8, 8}, - [SVGA3D_LUMINANCE4_ALPHA4] = {8, 8}, - [SVGA3D_LUMINANCE16] = {16, 16}, - [SVGA3D_LUMINANCE8_ALPHA8] = {16, 16}, - [SVGA3D_DXT1] = {4, 16}, - [SVGA3D_DXT2] = {8, 32}, - [SVGA3D_DXT3] = {8, 32}, - [SVGA3D_DXT4] = {8, 32}, - [SVGA3D_DXT5] = {8, 32}, - [SVGA3D_BUMPU8V8] = {16, 16}, - [SVGA3D_BUMPL6V5U5] = {16, 16}, - [SVGA3D_BUMPX8L8V8U8] = {32, 32}, - [SVGA3D_ARGB_S10E5] = {16, 16}, - [SVGA3D_ARGB_S23E8] = {32, 32}, - [SVGA3D_A2R10G10B10] = {32, 32}, - [SVGA3D_V8U8] = {16, 16}, - [SVGA3D_Q8W8V8U8] = {32, 32}, - [SVGA3D_CxV8U8] = {16, 16}, - [SVGA3D_X8L8V8U8] = {32, 32}, - [SVGA3D_A2W10V10U10] = {32, 32}, - [SVGA3D_ALPHA8] = {8, 8}, - [SVGA3D_R_S10E5] = {16, 16}, - [SVGA3D_R_S23E8] = {32, 32}, - [SVGA3D_RG_S10E5] = {16, 16}, - [SVGA3D_RG_S23E8] = {32, 32}, - [SVGA3D_BUFFER] = {8, 8}, - [SVGA3D_Z_D24X8] = {32, 32}, - [SVGA3D_V16U16] = {32, 32}, - [SVGA3D_G16R16] = {32, 32}, - [SVGA3D_A16B16G16R16] = {64, 64}, - [SVGA3D_UYVY] = {12, 12}, - [SVGA3D_YUY2] = {12, 12}, - [SVGA3D_NV12] = {12, 8}, - [SVGA3D_AYUV] = {32, 32}, - [SVGA3D_BC4_UNORM] = {4, 16}, - [SVGA3D_BC5_UNORM] = {8, 32}, - [SVGA3D_Z_DF16] = {16, 16}, - [SVGA3D_Z_DF24] = {24, 24}, - [SVGA3D_Z_D24S8_INT] = {32, 32} -}; - - -/** - * Surface management. - */ - -struct vmw_surface_dma { - SVGA3dCmdHeader header; - SVGA3dCmdSurfaceDMA body; - SVGA3dCopyBox cb; - SVGA3dCmdSurfaceDMASuffix suffix; -}; - -struct vmw_surface_define { - SVGA3dCmdHeader header; - SVGA3dCmdDefineSurface body; -}; - -struct vmw_surface_destroy { - SVGA3dCmdHeader header; - SVGA3dCmdDestroySurface body; -}; - - -/** - * vmw_surface_dma_size - Compute fifo size for a dma command. - * - * @srf: Pointer to a struct vmw_surface - * - * Computes the required size for a surface dma command for backup or - * restoration of the surface represented by @srf. - */ -static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) -{ - return srf->num_sizes * sizeof(struct vmw_surface_dma); -} - - -/** - * vmw_surface_define_size - Compute fifo size for a surface define command. - * - * @srf: Pointer to a struct vmw_surface - * - * Computes the required size for a surface define command for the definition - * of the surface represented by @srf. - */ -static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) -{ - return sizeof(struct vmw_surface_define) + srf->num_sizes * - sizeof(SVGA3dSize); -} - - -/** - * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. - * - * Computes the required size for a surface destroy command for the destruction - * of a hw surface. - */ -static inline uint32_t vmw_surface_destroy_size(void) -{ - return sizeof(struct vmw_surface_destroy); -} - -/** - * vmw_surface_destroy_encode - Encode a surface_destroy command. - * - * @id: The surface id - * @cmd_space: Pointer to memory area in which the commands should be encoded. - */ -static void vmw_surface_destroy_encode(uint32_t id, - void *cmd_space) -{ - struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) - cmd_space; - - cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; - cmd->header.size = sizeof(cmd->body); - cmd->body.sid = id; -} - -/** - * vmw_surface_define_encode - Encode a surface_define command. - * - * @srf: Pointer to a struct vmw_surface object. - * @cmd_space: Pointer to memory area in which the commands should be encoded. - */ -static void vmw_surface_define_encode(const struct vmw_surface *srf, - void *cmd_space) -{ - struct vmw_surface_define *cmd = (struct vmw_surface_define *) - cmd_space; - struct drm_vmw_size *src_size; - SVGA3dSize *cmd_size; - uint32_t cmd_len; - int i; - - cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); - - cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; - cmd->header.size = cmd_len; - cmd->body.sid = srf->res.id; - cmd->body.surfaceFlags = srf->flags; - cmd->body.format = cpu_to_le32(srf->format); - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) - cmd->body.face[i].numMipLevels = srf->mip_levels[i]; - - cmd += 1; - cmd_size = (SVGA3dSize *) cmd; - src_size = srf->sizes; - - for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { - cmd_size->width = src_size->width; - cmd_size->height = src_size->height; - cmd_size->depth = src_size->depth; - } -} - - -/** - * vmw_surface_dma_encode - Encode a surface_dma command. - * - * @srf: Pointer to a struct vmw_surface object. - * @cmd_space: Pointer to memory area in which the commands should be encoded. - * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents - * should be placed or read from. - * @to_surface: Boolean whether to DMA to the surface or from the surface. - */ -static void vmw_surface_dma_encode(struct vmw_surface *srf, - void *cmd_space, - const SVGAGuestPtr *ptr, - bool to_surface) -{ - uint32_t i; - uint32_t bpp = vmw_sf_bpp[srf->format].bpp; - uint32_t stride_bpp = vmw_sf_bpp[srf->format].s_bpp; - struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; - - for (i = 0; i < srf->num_sizes; ++i) { - SVGA3dCmdHeader *header = &cmd->header; - SVGA3dCmdSurfaceDMA *body = &cmd->body; - SVGA3dCopyBox *cb = &cmd->cb; - SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; - const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; - const struct drm_vmw_size *cur_size = &srf->sizes[i]; - - header->id = SVGA_3D_CMD_SURFACE_DMA; - header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); - - body->guest.ptr = *ptr; - body->guest.ptr.offset += cur_offset->bo_offset; - body->guest.pitch = (cur_size->width * stride_bpp + 7) >> 3; - body->host.sid = srf->res.id; - body->host.face = cur_offset->face; - body->host.mipmap = cur_offset->mip; - body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : - SVGA3D_READ_HOST_VRAM); - cb->x = 0; - cb->y = 0; - cb->z = 0; - cb->srcx = 0; - cb->srcy = 0; - cb->srcz = 0; - cb->w = cur_size->width; - cb->h = cur_size->height; - cb->d = cur_size->depth; - - suffix->suffixSize = sizeof(*suffix); - suffix->maximumOffset = body->guest.pitch*cur_size->height* - cur_size->depth*bpp / stride_bpp; - suffix->flags.discard = 0; - suffix->flags.unsynchronized = 0; - suffix->flags.reserved = 0; - ++cmd; - } -}; - - -static void vmw_hw_surface_destroy(struct vmw_resource *res) -{ - - struct vmw_private *dev_priv = res->dev_priv; - struct vmw_surface *srf; - void *cmd; - - if (res->id != -1) { - - cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size()); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for surface " - "destruction.\n"); - return; - } - - vmw_surface_destroy_encode(res->id, cmd); - vmw_fifo_commit(dev_priv, vmw_surface_destroy_size()); - - /* - * used_memory_size_atomic, or separate lock - * to avoid taking dev_priv::cmdbuf_mutex in - * the destroy path. - */ - - mutex_lock(&dev_priv->cmdbuf_mutex); - srf = container_of(res, struct vmw_surface, res); - dev_priv->used_memory_size -= srf->backup_size; - mutex_unlock(&dev_priv->cmdbuf_mutex); - - } - vmw_3d_resource_dec(dev_priv, false); -} - -void vmw_surface_res_free(struct vmw_resource *res) -{ - struct vmw_surface *srf = container_of(res, struct vmw_surface, res); - - if (srf->backup) - ttm_bo_unref(&srf->backup); - kfree(srf->offsets); - kfree(srf->sizes); - kfree(srf->snooper.image); - kfree(srf); -} - - -/** - * vmw_surface_do_validate - make a surface available to the device. - * - * @dev_priv: Pointer to a device private struct. - * @srf: Pointer to a struct vmw_surface. - * - * If the surface doesn't have a hw id, allocate one, and optionally - * DMA the backed up surface contents to the device. - * - * Returns -EBUSY if there wasn't sufficient device resources to - * complete the validation. Retry after freeing up resources. - * - * May return other errors if the kernel is out of guest resources. - */ -int vmw_surface_do_validate(struct vmw_private *dev_priv, - struct vmw_surface *srf) -{ - struct vmw_resource *res = &srf->res; - struct list_head val_list; - struct ttm_validate_buffer val_buf; - uint32_t submit_size; - uint8_t *cmd; - int ret; - - if (likely(res->id != -1)) - return 0; - - if (unlikely(dev_priv->used_memory_size + srf->backup_size >= - dev_priv->memory_size)) - return -EBUSY; - - /* - * Reserve- and validate the backup DMA bo. - */ - - if (srf->backup) { - INIT_LIST_HEAD(&val_list); - val_buf.bo = ttm_bo_reference(srf->backup); - list_add_tail(&val_buf.head, &val_list); - ret = ttm_eu_reserve_buffers(&val_list); - if (unlikely(ret != 0)) - goto out_no_reserve; - - ret = ttm_bo_validate(srf->backup, &vmw_srf_placement, - true, false, false); - if (unlikely(ret != 0)) - goto out_no_validate; - } - - /* - * Alloc id for the resource. - */ - - ret = vmw_resource_alloc_id(dev_priv, res); - if (unlikely(ret != 0)) { - DRM_ERROR("Failed to allocate a surface id.\n"); - goto out_no_id; - } - if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) { - ret = -EBUSY; - goto out_no_fifo; - } - - - /* - * Encode surface define- and dma commands. - */ - - submit_size = vmw_surface_define_size(srf); - if (srf->backup) - submit_size += vmw_surface_dma_size(srf); - - cmd = vmw_fifo_reserve(dev_priv, submit_size); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for surface " - "validation.\n"); - ret = -ENOMEM; - goto out_no_fifo; - } - - vmw_surface_define_encode(srf, cmd); - if (srf->backup) { - SVGAGuestPtr ptr; - - cmd += vmw_surface_define_size(srf); - vmw_bo_get_guest_ptr(srf->backup, &ptr); - vmw_surface_dma_encode(srf, cmd, &ptr, true); - } - - vmw_fifo_commit(dev_priv, submit_size); - - /* - * Create a fence object and fence the backup buffer. - */ - - if (srf->backup) { - struct vmw_fence_obj *fence; - - (void) vmw_execbuf_fence_commands(NULL, dev_priv, - &fence, NULL); - ttm_eu_fence_buffer_objects(&val_list, fence); - if (likely(fence != NULL)) - vmw_fence_obj_unreference(&fence); - ttm_bo_unref(&val_buf.bo); - ttm_bo_unref(&srf->backup); - } - - /* - * Surface memory usage accounting. - */ - - dev_priv->used_memory_size += srf->backup_size; - - return 0; - -out_no_fifo: - vmw_resource_release_id(res); -out_no_id: -out_no_validate: - if (srf->backup) - ttm_eu_backoff_reservation(&val_list); -out_no_reserve: - if (srf->backup) - ttm_bo_unref(&val_buf.bo); - return ret; -} - -/** - * vmw_surface_evict - Evict a hw surface. - * - * @dev_priv: Pointer to a device private struct. - * @srf: Pointer to a struct vmw_surface - * - * DMA the contents of a hw surface to a backup guest buffer object, - * and destroy the hw surface, releasing its id. - */ -int vmw_surface_evict(struct vmw_private *dev_priv, - struct vmw_surface *srf) -{ - struct vmw_resource *res = &srf->res; - struct list_head val_list; - struct ttm_validate_buffer val_buf; - uint32_t submit_size; - uint8_t *cmd; - int ret; - struct vmw_fence_obj *fence; - SVGAGuestPtr ptr; - - BUG_ON(res->id == -1); - - /* - * Create a surface backup buffer object. - */ - - if (!srf->backup) { - ret = ttm_bo_create(&dev_priv->bdev, srf->backup_size, - ttm_bo_type_device, - &vmw_srf_placement, 0, true, - NULL, &srf->backup); - if (unlikely(ret != 0)) - return ret; - } - - /* - * Reserve- and validate the backup DMA bo. - */ - - INIT_LIST_HEAD(&val_list); - val_buf.bo = ttm_bo_reference(srf->backup); - list_add_tail(&val_buf.head, &val_list); - ret = ttm_eu_reserve_buffers(&val_list); - if (unlikely(ret != 0)) - goto out_no_reserve; - - ret = ttm_bo_validate(srf->backup, &vmw_srf_placement, - true, false, false); - if (unlikely(ret != 0)) - goto out_no_validate; - - - /* - * Encode the dma- and surface destroy commands. - */ - - submit_size = vmw_surface_dma_size(srf) + vmw_surface_destroy_size(); - cmd = vmw_fifo_reserve(dev_priv, submit_size); - if (unlikely(cmd == NULL)) { - DRM_ERROR("Failed reserving FIFO space for surface " - "eviction.\n"); - ret = -ENOMEM; - goto out_no_fifo; - } - - vmw_bo_get_guest_ptr(srf->backup, &ptr); - vmw_surface_dma_encode(srf, cmd, &ptr, false); - cmd += vmw_surface_dma_size(srf); - vmw_surface_destroy_encode(res->id, cmd); - vmw_fifo_commit(dev_priv, submit_size); - - /* - * Surface memory usage accounting. - */ - - dev_priv->used_memory_size -= srf->backup_size; - - /* - * Create a fence object and fence the DMA buffer. - */ - - (void) vmw_execbuf_fence_commands(NULL, dev_priv, - &fence, NULL); - ttm_eu_fence_buffer_objects(&val_list, fence); - if (likely(fence != NULL)) - vmw_fence_obj_unreference(&fence); - ttm_bo_unref(&val_buf.bo); - - /* - * Release the surface ID. - */ - - vmw_resource_release_id(res); - - return 0; - -out_no_fifo: -out_no_validate: - if (srf->backup) - ttm_eu_backoff_reservation(&val_list); -out_no_reserve: - ttm_bo_unref(&val_buf.bo); - ttm_bo_unref(&srf->backup); - return ret; -} - - -/** - * vmw_surface_validate - make a surface available to the device, evicting - * other surfaces if needed. - * - * @dev_priv: Pointer to a device private struct. - * @srf: Pointer to a struct vmw_surface. + * vmw_user_resource_lookup_handle - lookup a struct resource from a + * TTM user-space handle and perform basic type checks * - * Try to validate a surface and if it fails due to limited device resources, - * repeatedly try to evict other surfaces until the request can be - * acommodated. + * @dev_priv: Pointer to a device private struct + * @tfile: Pointer to a struct ttm_object_file identifying the caller + * @handle: The TTM user-space handle + * @converter: Pointer to an object describing the resource type + * @p_res: On successful return the location pointed to will contain + * a pointer to a refcounted struct vmw_resource. * - * May return errors if out of resources. + * If the handle can't be found or is associated with an incorrect resource + * type, -EINVAL will be returned. */ -int vmw_surface_validate(struct vmw_private *dev_priv, - struct vmw_surface *srf) +int vmw_user_resource_lookup_handle(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, + const struct vmw_user_resource_conv + *converter, + struct vmw_resource **p_res) { - int ret; - struct vmw_surface *evict_srf; - - do { - write_lock(&dev_priv->resource_lock); - list_del_init(&srf->lru_head); - write_unlock(&dev_priv->resource_lock); - - ret = vmw_surface_do_validate(dev_priv, srf); - if (likely(ret != -EBUSY)) - break; - - write_lock(&dev_priv->resource_lock); - if (list_empty(&dev_priv->surface_lru)) { - DRM_ERROR("Out of device memory for surfaces.\n"); - ret = -EBUSY; - write_unlock(&dev_priv->resource_lock); - break; - } - - evict_srf = vmw_surface_reference - (list_first_entry(&dev_priv->surface_lru, - struct vmw_surface, - lru_head)); - list_del_init(&evict_srf->lru_head); - - write_unlock(&dev_priv->resource_lock); - (void) vmw_surface_evict(dev_priv, evict_srf); - - vmw_surface_unreference(&evict_srf); - - } while (1); - - if (unlikely(ret != 0 && srf->res.id != -1)) { - write_lock(&dev_priv->resource_lock); - list_add_tail(&srf->lru_head, &dev_priv->surface_lru); - write_unlock(&dev_priv->resource_lock); - } - - return ret; -} - - -/** - * vmw_surface_remove_from_lists - Remove surface resources from lookup lists - * - * @res: Pointer to a struct vmw_resource embedded in a struct vmw_surface - * - * As part of the resource destruction, remove the surface from any - * lookup lists. - */ -static void vmw_surface_remove_from_lists(struct vmw_resource *res) -{ - struct vmw_surface *srf = container_of(res, struct vmw_surface, res); - - list_del_init(&srf->lru_head); -} - -int vmw_surface_init(struct vmw_private *dev_priv, - struct vmw_surface *srf, - void (*res_free) (struct vmw_resource *res)) -{ - int ret; - struct vmw_resource *res = &srf->res; - - BUG_ON(res_free == NULL); - INIT_LIST_HEAD(&srf->lru_head); - ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr, - VMW_RES_SURFACE, true, res_free, - vmw_surface_remove_from_lists); - - if (unlikely(ret != 0)) - res_free(res); - - /* - * The surface won't be visible to hardware until a - * surface validate. - */ - - (void) vmw_3d_resource_inc(dev_priv, false); - vmw_resource_activate(res, vmw_hw_surface_destroy); - return ret; -} - -static void vmw_user_surface_free(struct vmw_resource *res) -{ - struct vmw_surface *srf = container_of(res, struct vmw_surface, res); - struct vmw_user_surface *user_srf = - container_of(srf, struct vmw_user_surface, srf); - struct vmw_private *dev_priv = srf->res.dev_priv; - uint32_t size = user_srf->size; - - if (srf->backup) - ttm_bo_unref(&srf->backup); - kfree(srf->offsets); - kfree(srf->sizes); - kfree(srf->snooper.image); - ttm_base_object_kfree(user_srf, base); - ttm_mem_global_free(vmw_mem_glob(dev_priv), size); -} - -/** - * vmw_resource_unreserve - unreserve resources previously reserved for - * command submission. - * - * @list_head: list of resources to unreserve. - * - * Currently only surfaces are considered, and unreserving a surface - * means putting it back on the device's surface lru list, - * so that it can be evicted if necessary. - * This function traverses the resource list and - * checks whether resources are surfaces, and in that case puts them back - * on the device's surface LRU list. - */ -void vmw_resource_unreserve(struct list_head *list) -{ - struct vmw_resource *res; - struct vmw_surface *srf; - rwlock_t *lock = NULL; - - list_for_each_entry(res, list, validate_head) { - - if (res->res_free != &vmw_surface_res_free && - res->res_free != &vmw_user_surface_free) - continue; - - if (unlikely(lock == NULL)) { - lock = &res->dev_priv->resource_lock; - write_lock(lock); - } - - srf = container_of(res, struct vmw_surface, res); - list_del_init(&srf->lru_head); - list_add_tail(&srf->lru_head, &res->dev_priv->surface_lru); - } - - if (lock != NULL) - write_unlock(lock); -} - -/** - * Helper function that looks either a surface or dmabuf. - * - * The pointer this pointed at by out_surf and out_buf needs to be null. - */ -int vmw_user_lookup_handle(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t handle, - struct vmw_surface **out_surf, - struct vmw_dma_buffer **out_buf) -{ - int ret; - - BUG_ON(*out_surf || *out_buf); - - ret = vmw_user_surface_lookup_handle(dev_priv, tfile, handle, out_surf); - if (!ret) - return 0; - - ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf); - return ret; -} - - -int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t handle, struct vmw_surface **out) -{ - struct vmw_resource *res; - struct vmw_surface *srf; - struct vmw_user_surface *user_srf; struct ttm_base_object *base; + struct vmw_resource *res; int ret = -EINVAL; base = ttm_base_object_lookup(tfile, handle); if (unlikely(base == NULL)) return -EINVAL; - if (unlikely(base->object_type != VMW_RES_SURFACE)) + if (unlikely(base->object_type != converter->object_type)) goto out_bad_resource; - user_srf = container_of(base, struct vmw_user_surface, base); - srf = &user_srf->srf; - res = &srf->res; + res = converter->base_obj_to_res(base); read_lock(&dev_priv->resource_lock); - - if (!res->avail || res->res_free != &vmw_user_surface_free) { + if (!res->avail || res->res_free != converter->res_free) { read_unlock(&dev_priv->resource_lock); goto out_bad_resource; } @@ -1241,7 +310,7 @@ int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv, kref_get(&res->kref); read_unlock(&dev_priv->resource_lock); - *out = srf; + *p_res = res; ret = 0; out_bad_resource: @@ -1250,286 +319,32 @@ out_bad_resource: return ret; } -static void vmw_user_surface_base_release(struct ttm_base_object **p_base) -{ - struct ttm_base_object *base = *p_base; - struct vmw_user_surface *user_srf = - container_of(base, struct vmw_user_surface, base); - struct vmw_resource *res = &user_srf->srf.res; - - *p_base = NULL; - vmw_resource_unreference(&res); -} - -int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - - return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); -} - -int vmw_surface_define_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) +/** + * Helper function that looks either a surface or dmabuf. + * + * The pointer this pointed at by out_surf and out_buf needs to be null. + */ +int vmw_user_lookup_handle(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t handle, + struct vmw_surface **out_surf, + struct vmw_dma_buffer **out_buf) { - struct vmw_private *dev_priv = vmw_priv(dev); - struct vmw_user_surface *user_srf; - struct vmw_surface *srf; struct vmw_resource *res; - struct vmw_resource *tmp; - union drm_vmw_surface_create_arg *arg = - (union drm_vmw_surface_create_arg *)data; - struct drm_vmw_surface_create_req *req = &arg->req; - struct drm_vmw_surface_arg *rep = &arg->rep; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct drm_vmw_size __user *user_sizes; int ret; - int i, j; - uint32_t cur_bo_offset; - struct drm_vmw_size *cur_size; - struct vmw_surface_offset *cur_offset; - uint32_t stride_bpp; - uint32_t bpp; - uint32_t num_sizes; - uint32_t size; - struct vmw_master *vmaster = vmw_master(file_priv->master); - - if (unlikely(vmw_user_surface_size == 0)) - vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + - 128; - - num_sizes = 0; - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) - num_sizes += req->mip_levels[i]; - - if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * - DRM_VMW_MAX_MIP_LEVELS) - return -EINVAL; - - size = vmw_user_surface_size + 128 + - ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + - ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); - - - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) - return ret; - - ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), - size, false, true); - if (unlikely(ret != 0)) { - if (ret != -ERESTARTSYS) - DRM_ERROR("Out of graphics memory for surface" - " creation.\n"); - goto out_unlock; - } - - user_srf = kmalloc(sizeof(*user_srf), GFP_KERNEL); - if (unlikely(user_srf == NULL)) { - ret = -ENOMEM; - goto out_no_user_srf; - } - - srf = &user_srf->srf; - res = &srf->res; - - srf->flags = req->flags; - srf->format = req->format; - srf->scanout = req->scanout; - srf->backup = NULL; - - memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); - srf->num_sizes = num_sizes; - user_srf->size = size; - - srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); - if (unlikely(srf->sizes == NULL)) { - ret = -ENOMEM; - goto out_no_sizes; - } - srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), - GFP_KERNEL); - if (unlikely(srf->sizes == NULL)) { - ret = -ENOMEM; - goto out_no_offsets; - } - - user_sizes = (struct drm_vmw_size __user *)(unsigned long) - req->size_addr; - - ret = copy_from_user(srf->sizes, user_sizes, - srf->num_sizes * sizeof(*srf->sizes)); - if (unlikely(ret != 0)) { - ret = -EFAULT; - goto out_no_copy; - } - - cur_bo_offset = 0; - cur_offset = srf->offsets; - cur_size = srf->sizes; - - bpp = vmw_sf_bpp[srf->format].bpp; - stride_bpp = vmw_sf_bpp[srf->format].s_bpp; - - for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { - for (j = 0; j < srf->mip_levels[i]; ++j) { - uint32_t stride = - (cur_size->width * stride_bpp + 7) >> 3; - - cur_offset->face = i; - cur_offset->mip = j; - cur_offset->bo_offset = cur_bo_offset; - cur_bo_offset += stride * cur_size->height * - cur_size->depth * bpp / stride_bpp; - ++cur_offset; - ++cur_size; - } - } - srf->backup_size = cur_bo_offset; - - if (srf->scanout && - srf->num_sizes == 1 && - srf->sizes[0].width == 64 && - srf->sizes[0].height == 64 && - srf->format == SVGA3D_A8R8G8B8) { - - /* allocate image area and clear it */ - srf->snooper.image = kzalloc(64 * 64 * 4, GFP_KERNEL); - if (!srf->snooper.image) { - DRM_ERROR("Failed to allocate cursor_image\n"); - ret = -ENOMEM; - goto out_no_copy; - } - } else { - srf->snooper.image = NULL; - } - srf->snooper.crtc = NULL; - - user_srf->base.shareable = false; - user_srf->base.tfile = NULL; - - /** - * From this point, the generic resource management functions - * destroy the object on failure. - */ - - ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); - if (unlikely(ret != 0)) - goto out_unlock; - - tmp = vmw_resource_reference(&srf->res); - ret = ttm_base_object_init(tfile, &user_srf->base, - req->shareable, VMW_RES_SURFACE, - &vmw_user_surface_base_release, NULL); - - if (unlikely(ret != 0)) { - vmw_resource_unreference(&tmp); - vmw_resource_unreference(&res); - goto out_unlock; - } - - rep->sid = user_srf->base.hash.key; - if (rep->sid == SVGA3D_INVALID_ID) - DRM_ERROR("Created bad Surface ID.\n"); - - vmw_resource_unreference(&res); - - ttm_read_unlock(&vmaster->lock); - return 0; -out_no_copy: - kfree(srf->offsets); -out_no_offsets: - kfree(srf->sizes); -out_no_sizes: - kfree(user_srf); -out_no_user_srf: - ttm_mem_global_free(vmw_mem_glob(dev_priv), size); -out_unlock: - ttm_read_unlock(&vmaster->lock); - return ret; -} - -int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, - struct drm_file *file_priv) -{ - union drm_vmw_surface_reference_arg *arg = - (union drm_vmw_surface_reference_arg *)data; - struct drm_vmw_surface_arg *req = &arg->req; - struct drm_vmw_surface_create_req *rep = &arg->rep; - struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; - struct vmw_surface *srf; - struct vmw_user_surface *user_srf; - struct drm_vmw_size __user *user_sizes; - struct ttm_base_object *base; - int ret = -EINVAL; - - base = ttm_base_object_lookup(tfile, req->sid); - if (unlikely(base == NULL)) { - DRM_ERROR("Could not find surface to reference.\n"); - return -EINVAL; - } - - if (unlikely(base->object_type != VMW_RES_SURFACE)) - goto out_bad_resource; - user_srf = container_of(base, struct vmw_user_surface, base); - srf = &user_srf->srf; + BUG_ON(*out_surf || *out_buf); - ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL); - if (unlikely(ret != 0)) { - DRM_ERROR("Could not add a reference to a surface.\n"); - goto out_no_reference; + ret = vmw_user_resource_lookup_handle(dev_priv, tfile, handle, + user_surface_converter, + &res); + if (!ret) { + *out_surf = vmw_res_to_srf(res); + return 0; } - rep->flags = srf->flags; - rep->format = srf->format; - memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels)); - user_sizes = (struct drm_vmw_size __user *)(unsigned long) - rep->size_addr; - - if (user_sizes) - ret = copy_to_user(user_sizes, srf->sizes, - srf->num_sizes * sizeof(*srf->sizes)); - if (unlikely(ret != 0)) { - DRM_ERROR("copy_to_user failed %p %u\n", - user_sizes, srf->num_sizes); - ret = -EFAULT; - } -out_bad_resource: -out_no_reference: - ttm_base_object_unref(&base); - - return ret; -} - -int vmw_surface_check(struct vmw_private *dev_priv, - struct ttm_object_file *tfile, - uint32_t handle, int *id) -{ - struct ttm_base_object *base; - struct vmw_user_surface *user_srf; - - int ret = -EPERM; - - base = ttm_base_object_lookup(tfile, handle); - if (unlikely(base == NULL)) - return -EINVAL; - - if (unlikely(base->object_type != VMW_RES_SURFACE)) - goto out_bad_surface; - - user_srf = container_of(base, struct vmw_user_surface, base); - *id = user_srf->srf.res.id; - ret = 0; - -out_bad_surface: - /** - * FIXME: May deadlock here when called from the - * command parsing code. - */ - - ttm_base_object_unref(&base); + *out_surf = NULL; + ret = vmw_user_dmabuf_lookup(tfile, handle, out_buf); return ret; } @@ -1558,7 +373,7 @@ int vmw_dmabuf_init(struct vmw_private *dev_priv, acc_size = ttm_bo_acc_size(bdev, size, sizeof(struct vmw_dma_buffer)); memset(vmw_bo, 0, sizeof(*vmw_bo)); - INIT_LIST_HEAD(&vmw_bo->validate_list); + INIT_LIST_HEAD(&vmw_bo->res_list); ret = ttm_bo_init(bdev, &vmw_bo->base, size, ttm_bo_type_device, placement, @@ -1590,6 +405,79 @@ static void vmw_user_dmabuf_release(struct ttm_base_object **p_base) ttm_bo_unref(&bo); } +/** + * vmw_user_dmabuf_alloc - Allocate a user dma buffer + * + * @dev_priv: Pointer to a struct device private. + * @tfile: Pointer to a struct ttm_object_file on which to register the user + * object. + * @size: Size of the dma buffer. + * @shareable: Boolean whether the buffer is shareable with other open files. + * @handle: Pointer to where the handle value should be assigned. + * @p_dma_buf: Pointer to where the refcounted struct vmw_dma_buffer pointer + * should be assigned. + */ +int vmw_user_dmabuf_alloc(struct vmw_private *dev_priv, + struct ttm_object_file *tfile, + uint32_t size, + bool shareable, + uint32_t *handle, + struct vmw_dma_buffer **p_dma_buf) +{ + struct vmw_user_dma_buffer *user_bo; + struct ttm_buffer_object *tmp; + int ret; + + user_bo = kzalloc(sizeof(*user_bo), GFP_KERNEL); + if (unlikely(user_bo == NULL)) { + DRM_ERROR("Failed to allocate a buffer.\n"); + return -ENOMEM; + } + + ret = vmw_dmabuf_init(dev_priv, &user_bo->dma, size, + &vmw_vram_sys_placement, true, + &vmw_user_dmabuf_destroy); + if (unlikely(ret != 0)) + return ret; + + tmp = ttm_bo_reference(&user_bo->dma.base); + ret = ttm_base_object_init(tfile, + &user_bo->base, + shareable, + ttm_buffer_type, + &vmw_user_dmabuf_release, NULL); + if (unlikely(ret != 0)) { + ttm_bo_unref(&tmp); + goto out_no_base_object; + } + + *p_dma_buf = &user_bo->dma; + *handle = user_bo->base.hash.key; + +out_no_base_object: + return ret; +} + +/** + * vmw_user_dmabuf_verify_access - verify access permissions on this + * buffer object. + * + * @bo: Pointer to the buffer object being accessed + * @tfile: Identifying the caller. + */ +int vmw_user_dmabuf_verify_access(struct ttm_buffer_object *bo, + struct ttm_object_file *tfile) +{ + struct vmw_user_dma_buffer *vmw_user_bo; + + if (unlikely(bo->destroy != vmw_user_dmabuf_destroy)) + return -EPERM; + + vmw_user_bo = vmw_user_dma_buffer(bo); + return (vmw_user_bo->base.tfile == tfile || + vmw_user_bo->base.shareable) ? 0 : -EPERM; +} + int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { @@ -1598,44 +486,27 @@ int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data, (union drm_vmw_alloc_dmabuf_arg *)data; struct drm_vmw_alloc_dmabuf_req *req = &arg->req; struct drm_vmw_dmabuf_rep *rep = &arg->rep; - struct vmw_user_dma_buffer *vmw_user_bo; - struct ttm_buffer_object *tmp; + struct vmw_dma_buffer *dma_buf; + uint32_t handle; struct vmw_master *vmaster = vmw_master(file_priv->master); int ret; - vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL); - if (unlikely(vmw_user_bo == NULL)) - return -ENOMEM; - ret = ttm_read_lock(&vmaster->lock, true); - if (unlikely(ret != 0)) { - kfree(vmw_user_bo); + if (unlikely(ret != 0)) return ret; - } - ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, req->size, - &vmw_vram_sys_placement, true, - &vmw_user_dmabuf_destroy); + ret = vmw_user_dmabuf_alloc(dev_priv, vmw_fpriv(file_priv)->tfile, + req->size, false, &handle, &dma_buf); if (unlikely(ret != 0)) goto out_no_dmabuf; - tmp = ttm_bo_reference(&vmw_user_bo->dma.base); - ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile, - &vmw_user_bo->base, - false, - ttm_buffer_type, - &vmw_user_dmabuf_release, NULL); - if (unlikely(ret != 0)) - goto out_no_base_object; - else { - rep->handle = vmw_user_bo->base.hash.key; - rep->map_handle = vmw_user_bo->dma.base.addr_space_offset; - rep->cur_gmr_id = vmw_user_bo->base.hash.key; - rep->cur_gmr_offset = 0; - } + rep->handle = handle; + rep->map_handle = dma_buf->base.addr_space_offset; + rep->cur_gmr_id = handle; + rep->cur_gmr_offset = 0; + + vmw_dmabuf_unreference(&dma_buf); -out_no_base_object: - ttm_bo_unref(&tmp); out_no_dmabuf: ttm_read_unlock(&vmaster->lock); @@ -1653,27 +524,6 @@ int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data, TTM_REF_USAGE); } -uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo, - uint32_t cur_validate_node) -{ - struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); - - if (likely(vmw_bo->on_validate_list)) - return vmw_bo->cur_validate_node; - - vmw_bo->cur_validate_node = cur_validate_node; - vmw_bo->on_validate_list = true; - - return cur_validate_node; -} - -void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo) -{ - struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo); - - vmw_bo->on_validate_list = false; -} - int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, uint32_t handle, struct vmw_dma_buffer **out) { @@ -1702,6 +552,18 @@ int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile, return 0; } +int vmw_user_dmabuf_reference(struct ttm_object_file *tfile, + struct vmw_dma_buffer *dma_buf) +{ + struct vmw_user_dma_buffer *user_bo; + + if (dma_buf->base.destroy != vmw_user_dmabuf_destroy) + return -EINVAL; + + user_bo = container_of(dma_buf, struct vmw_user_dma_buffer, dma); + return ttm_ref_object_add(tfile, &user_bo->base, TTM_REF_USAGE, NULL); +} + /* * Stream management */ @@ -1726,8 +588,8 @@ static int vmw_stream_init(struct vmw_private *dev_priv, struct vmw_resource *res = &stream->res; int ret; - ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr, - VMW_RES_STREAM, false, res_free, NULL); + ret = vmw_resource_init(dev_priv, res, false, res_free, + &vmw_stream_func); if (unlikely(ret != 0)) { if (res_free == NULL) @@ -1749,10 +611,6 @@ static int vmw_stream_init(struct vmw_private *dev_priv, return 0; } -/** - * User-space context management: - */ - static void vmw_user_stream_free(struct vmw_resource *res) { struct vmw_user_stream *stream = @@ -1788,9 +646,11 @@ int vmw_stream_unref_ioctl(struct drm_device *dev, void *data, struct vmw_user_stream *stream; struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data; struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct idr *idr = &dev_priv->res_idr[vmw_res_stream]; int ret = 0; - res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id); + + res = vmw_resource_lookup(dev_priv, idr, arg->stream_id); if (unlikely(res == NULL)) return -EINVAL; @@ -1891,7 +751,8 @@ int vmw_user_stream_lookup(struct vmw_private *dev_priv, struct vmw_resource *res; int ret; - res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id); + res = vmw_resource_lookup(dev_priv, &dev_priv->res_idr[vmw_res_stream], + *inout_id); if (unlikely(res == NULL)) return -EINVAL; @@ -1986,3 +847,453 @@ int vmw_dumb_destroy(struct drm_file *file_priv, return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile, handle, TTM_REF_USAGE); } + +/** + * vmw_resource_buf_alloc - Allocate a backup buffer for a resource. + * + * @res: The resource for which to allocate a backup buffer. + * @interruptible: Whether any sleeps during allocation should be + * performed while interruptible. + */ +static int vmw_resource_buf_alloc(struct vmw_resource *res, + bool interruptible) +{ + unsigned long size = + (res->backup_size + PAGE_SIZE - 1) & PAGE_MASK; + struct vmw_dma_buffer *backup; + int ret; + + if (likely(res->backup)) { + BUG_ON(res->backup->base.num_pages * PAGE_SIZE < size); + return 0; + } + + backup = kzalloc(sizeof(*backup), GFP_KERNEL); + if (unlikely(backup == NULL)) + return -ENOMEM; + + ret = vmw_dmabuf_init(res->dev_priv, backup, res->backup_size, + res->func->backup_placement, + interruptible, + &vmw_dmabuf_bo_free); + if (unlikely(ret != 0)) + goto out_no_dmabuf; + + res->backup = backup; + +out_no_dmabuf: + return ret; +} + +/** + * vmw_resource_do_validate - Make a resource up-to-date and visible + * to the device. + * + * @res: The resource to make visible to the device. + * @val_buf: Information about a buffer possibly + * containing backup data if a bind operation is needed. + * + * On hardware resource shortage, this function returns -EBUSY and + * should be retried once resources have been freed up. + */ +static int vmw_resource_do_validate(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf) +{ + int ret = 0; + const struct vmw_res_func *func = res->func; + + if (unlikely(res->id == -1)) { + ret = func->create(res); + if (unlikely(ret != 0)) + return ret; + } + + if (func->bind && + ((func->needs_backup && list_empty(&res->mob_head) && + val_buf->bo != NULL) || + (!func->needs_backup && val_buf->bo != NULL))) { + ret = func->bind(res, val_buf); + if (unlikely(ret != 0)) + goto out_bind_failed; + if (func->needs_backup) + list_add_tail(&res->mob_head, &res->backup->res_list); + } + + /* + * Only do this on write operations, and move to + * vmw_resource_unreserve if it can be called after + * backup buffers have been unreserved. Otherwise + * sort out locking. + */ + res->res_dirty = true; + + return 0; + +out_bind_failed: + func->destroy(res); + + return ret; +} + +/** + * vmw_resource_unreserve - Unreserve a resource previously reserved for + * command submission. + * + * @res: Pointer to the struct vmw_resource to unreserve. + * @new_backup: Pointer to new backup buffer if command submission + * switched. + * @new_backup_offset: New backup offset if @new_backup is !NULL. + * + * Currently unreserving a resource means putting it back on the device's + * resource lru list, so that it can be evicted if necessary. + */ +void vmw_resource_unreserve(struct vmw_resource *res, + struct vmw_dma_buffer *new_backup, + unsigned long new_backup_offset) +{ + struct vmw_private *dev_priv = res->dev_priv; + + if (!list_empty(&res->lru_head)) + return; + + if (new_backup && new_backup != res->backup) { + + if (res->backup) { + BUG_ON(atomic_read(&res->backup->base.reserved) == 0); + list_del_init(&res->mob_head); + vmw_dmabuf_unreference(&res->backup); + } + + res->backup = vmw_dmabuf_reference(new_backup); + BUG_ON(atomic_read(&new_backup->base.reserved) == 0); + list_add_tail(&res->mob_head, &new_backup->res_list); + } + if (new_backup) + res->backup_offset = new_backup_offset; + + if (!res->func->may_evict) + return; + + write_lock(&dev_priv->resource_lock); + list_add_tail(&res->lru_head, + &res->dev_priv->res_lru[res->func->res_type]); + write_unlock(&dev_priv->resource_lock); +} + +/** + * vmw_resource_check_buffer - Check whether a backup buffer is needed + * for a resource and in that case, allocate + * one, reserve and validate it. + * + * @res: The resource for which to allocate a backup buffer. + * @interruptible: Whether any sleeps during allocation should be + * performed while interruptible. + * @val_buf: On successful return contains data about the + * reserved and validated backup buffer. + */ +int vmw_resource_check_buffer(struct vmw_resource *res, + bool interruptible, + struct ttm_validate_buffer *val_buf) +{ + struct list_head val_list; + bool backup_dirty = false; + int ret; + + if (unlikely(res->backup == NULL)) { + ret = vmw_resource_buf_alloc(res, interruptible); + if (unlikely(ret != 0)) + return ret; + } + + INIT_LIST_HEAD(&val_list); + val_buf->bo = ttm_bo_reference(&res->backup->base); + list_add_tail(&val_buf->head, &val_list); + ret = ttm_eu_reserve_buffers(&val_list); + if (unlikely(ret != 0)) + goto out_no_reserve; + + if (res->func->needs_backup && list_empty(&res->mob_head)) + return 0; + + backup_dirty = res->backup_dirty; + ret = ttm_bo_validate(&res->backup->base, + res->func->backup_placement, + true, false, false); + + if (unlikely(ret != 0)) + goto out_no_validate; + + return 0; + +out_no_validate: + ttm_eu_backoff_reservation(&val_list); +out_no_reserve: + ttm_bo_unref(&val_buf->bo); + if (backup_dirty) + vmw_dmabuf_unreference(&res->backup); + + return ret; +} + +/** + * vmw_resource_reserve - Reserve a resource for command submission + * + * @res: The resource to reserve. + * + * This function takes the resource off the LRU list and make sure + * a backup buffer is present for guest-backed resources. However, + * the buffer may not be bound to the resource at this point. + * + */ +int vmw_resource_reserve(struct vmw_resource *res, bool no_backup) +{ + struct vmw_private *dev_priv = res->dev_priv; + int ret; + + write_lock(&dev_priv->resource_lock); + list_del_init(&res->lru_head); + write_unlock(&dev_priv->resource_lock); + + if (res->func->needs_backup && res->backup == NULL && + !no_backup) { + ret = vmw_resource_buf_alloc(res, true); + if (unlikely(ret != 0)) + return ret; + } + + return 0; +} + +/** + * vmw_resource_backoff_reservation - Unreserve and unreference a + * backup buffer + *. + * @val_buf: Backup buffer information. + */ +void vmw_resource_backoff_reservation(struct ttm_validate_buffer *val_buf) +{ + struct list_head val_list; + + if (likely(val_buf->bo == NULL)) + return; + + INIT_LIST_HEAD(&val_list); + list_add_tail(&val_buf->head, &val_list); + ttm_eu_backoff_reservation(&val_list); + ttm_bo_unref(&val_buf->bo); +} + +/** + * vmw_resource_do_evict - Evict a resource, and transfer its data + * to a backup buffer. + * + * @res: The resource to evict. + */ +int vmw_resource_do_evict(struct vmw_resource *res) +{ + struct ttm_validate_buffer val_buf; + const struct vmw_res_func *func = res->func; + int ret; + + BUG_ON(!func->may_evict); + + val_buf.bo = NULL; + ret = vmw_resource_check_buffer(res, true, &val_buf); + if (unlikely(ret != 0)) + return ret; + + if (unlikely(func->unbind != NULL && + (!func->needs_backup || !list_empty(&res->mob_head)))) { + ret = func->unbind(res, res->res_dirty, &val_buf); + if (unlikely(ret != 0)) + goto out_no_unbind; + list_del_init(&res->mob_head); + } + ret = func->destroy(res); + res->backup_dirty = true; + res->res_dirty = false; +out_no_unbind: + vmw_resource_backoff_reservation(&val_buf); + + return ret; +} + + +/** + * vmw_resource_validate - Make a resource up-to-date and visible + * to the device. + * + * @res: The resource to make visible to the device. + * + * On succesful return, any backup DMA buffer pointed to by @res->backup will + * be reserved and validated. + * On hardware resource shortage, this function will repeatedly evict + * resources of the same type until the validation succeeds. + */ +int vmw_resource_validate(struct vmw_resource *res) +{ + int ret; + struct vmw_resource *evict_res; + struct vmw_private *dev_priv = res->dev_priv; + struct list_head *lru_list = &dev_priv->res_lru[res->func->res_type]; + struct ttm_validate_buffer val_buf; + + if (likely(!res->func->may_evict)) + return 0; + + val_buf.bo = NULL; + if (res->backup) + val_buf.bo = &res->backup->base; + do { + ret = vmw_resource_do_validate(res, &val_buf); + if (likely(ret != -EBUSY)) + break; + + write_lock(&dev_priv->resource_lock); + if (list_empty(lru_list) || !res->func->may_evict) { + DRM_ERROR("Out of device device id entries " + "for %s.\n", res->func->type_name); + ret = -EBUSY; + write_unlock(&dev_priv->resource_lock); + break; + } + + evict_res = vmw_resource_reference + (list_first_entry(lru_list, struct vmw_resource, + lru_head)); + list_del_init(&evict_res->lru_head); + + write_unlock(&dev_priv->resource_lock); + vmw_resource_do_evict(evict_res); + vmw_resource_unreference(&evict_res); + } while (1); + + if (unlikely(ret != 0)) + goto out_no_validate; + else if (!res->func->needs_backup && res->backup) { + list_del_init(&res->mob_head); + vmw_dmabuf_unreference(&res->backup); + } + + return 0; + +out_no_validate: + return ret; +} + +/** + * vmw_fence_single_bo - Utility function to fence a single TTM buffer + * object without unreserving it. + * + * @bo: Pointer to the struct ttm_buffer_object to fence. + * @fence: Pointer to the fence. If NULL, this function will + * insert a fence into the command stream.. + * + * Contrary to the ttm_eu version of this function, it takes only + * a single buffer object instead of a list, and it also doesn't + * unreserve the buffer object, which needs to be done separately. + */ +void vmw_fence_single_bo(struct ttm_buffer_object *bo, + struct vmw_fence_obj *fence) +{ + struct ttm_bo_device *bdev = bo->bdev; + struct ttm_bo_driver *driver = bdev->driver; + struct vmw_fence_obj *old_fence_obj; + struct vmw_private *dev_priv = + container_of(bdev, struct vmw_private, bdev); + + if (fence == NULL) + vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); + else + driver->sync_obj_ref(fence); + + spin_lock(&bdev->fence_lock); + + old_fence_obj = bo->sync_obj; + bo->sync_obj = fence; + + spin_unlock(&bdev->fence_lock); + + if (old_fence_obj) + vmw_fence_obj_unreference(&old_fence_obj); +} + +/** + * vmw_resource_move_notify - TTM move_notify_callback + * + * @bo: The TTM buffer object about to move. + * @mem: The truct ttm_mem_reg indicating to what memory + * region the move is taking place. + * + * For now does nothing. + */ +void vmw_resource_move_notify(struct ttm_buffer_object *bo, + struct ttm_mem_reg *mem) +{ +} + +/** + * vmw_resource_needs_backup - Return whether a resource needs a backup buffer. + * + * @res: The resource being queried. + */ +bool vmw_resource_needs_backup(const struct vmw_resource *res) +{ + return res->func->needs_backup; +} + +/** + * vmw_resource_evict_type - Evict all resources of a specific type + * + * @dev_priv: Pointer to a device private struct + * @type: The resource type to evict + * + * To avoid thrashing starvation or as part of the hibernation sequence, + * evict all evictable resources of a specific type. + */ +static void vmw_resource_evict_type(struct vmw_private *dev_priv, + enum vmw_res_type type) +{ + struct list_head *lru_list = &dev_priv->res_lru[type]; + struct vmw_resource *evict_res; + + do { + write_lock(&dev_priv->resource_lock); + + if (list_empty(lru_list)) + goto out_unlock; + + evict_res = vmw_resource_reference( + list_first_entry(lru_list, struct vmw_resource, + lru_head)); + list_del_init(&evict_res->lru_head); + write_unlock(&dev_priv->resource_lock); + vmw_resource_do_evict(evict_res); + vmw_resource_unreference(&evict_res); + } while (1); + +out_unlock: + write_unlock(&dev_priv->resource_lock); +} + +/** + * vmw_resource_evict_all - Evict all evictable resources + * + * @dev_priv: Pointer to a device private struct + * + * To avoid thrashing starvation or as part of the hibernation sequence, + * evict all evictable resources. In particular this means that all + * guest-backed resources that are registered with the device are + * evicted and the OTable becomes clean. + */ +void vmw_resource_evict_all(struct vmw_private *dev_priv) +{ + enum vmw_res_type type; + + mutex_lock(&dev_priv->cmdbuf_mutex); + + for (type = 0; type < vmw_res_max; ++type) + vmw_resource_evict_type(dev_priv, type); + + mutex_unlock(&dev_priv->cmdbuf_mutex); +} diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h new file mode 100644 index 00000000000..f3adeed2854 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource_priv.h @@ -0,0 +1,84 @@ +/************************************************************************** + * + * Copyright © 2012 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#ifndef _VMWGFX_RESOURCE_PRIV_H_ +#define _VMWGFX_RESOURCE_PRIV_H_ + +#include "vmwgfx_drv.h" + +/** + * struct vmw_user_resource_conv - Identify a derived user-exported resource + * type and provide a function to convert its ttm_base_object pointer to + * a struct vmw_resource + */ +struct vmw_user_resource_conv { + enum ttm_object_type object_type; + struct vmw_resource *(*base_obj_to_res)(struct ttm_base_object *base); + void (*res_free) (struct vmw_resource *res); +}; + +/** + * struct vmw_res_func - members and functions common for a resource type + * + * @res_type: Enum that identifies the lru list to use for eviction. + * @needs_backup: Whether the resource is guest-backed and needs + * persistent buffer storage. + * @type_name: String that identifies the resource type. + * @backup_placement: TTM placement for backup buffers. + * @may_evict Whether the resource may be evicted. + * @create: Create a hardware resource. + * @destroy: Destroy a hardware resource. + * @bind: Bind a hardware resource to persistent buffer storage. + * @unbind: Unbind a hardware resource from persistent + * buffer storage. + */ + +struct vmw_res_func { + enum vmw_res_type res_type; + bool needs_backup; + const char *type_name; + struct ttm_placement *backup_placement; + bool may_evict; + + int (*create) (struct vmw_resource *res); + int (*destroy) (struct vmw_resource *res); + int (*bind) (struct vmw_resource *res, + struct ttm_validate_buffer *val_buf); + int (*unbind) (struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf); +}; + +int vmw_resource_alloc_id(struct vmw_resource *res); +void vmw_resource_release_id(struct vmw_resource *res); +int vmw_resource_init(struct vmw_private *dev_priv, struct vmw_resource *res, + bool delay_id, + void (*res_free) (struct vmw_resource *res), + const struct vmw_res_func *func); +void vmw_resource_activate(struct vmw_resource *res, + void (*hw_destroy) (struct vmw_resource *)); +#endif diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c new file mode 100644 index 00000000000..58281433974 --- /dev/null +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_surface.c @@ -0,0 +1,893 @@ +/************************************************************************** + * + * Copyright © 2009-2012 VMware, Inc., Palo Alto, CA., USA + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sub license, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The 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 COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + **************************************************************************/ + +#include "vmwgfx_drv.h" +#include "vmwgfx_resource_priv.h" +#include <ttm/ttm_placement.h> +#include "svga3d_surfacedefs.h" + +/** + * struct vmw_user_surface - User-space visible surface resource + * + * @base: The TTM base object handling user-space visibility. + * @srf: The surface metadata. + * @size: TTM accounting size for the surface. + */ +struct vmw_user_surface { + struct ttm_base_object base; + struct vmw_surface srf; + uint32_t size; + uint32_t backup_handle; +}; + +/** + * struct vmw_surface_offset - Backing store mip level offset info + * + * @face: Surface face. + * @mip: Mip level. + * @bo_offset: Offset into backing store of this mip level. + * + */ +struct vmw_surface_offset { + uint32_t face; + uint32_t mip; + uint32_t bo_offset; +}; + +static void vmw_user_surface_free(struct vmw_resource *res); +static struct vmw_resource * +vmw_user_surface_base_to_res(struct ttm_base_object *base); +static int vmw_legacy_srf_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf); +static int vmw_legacy_srf_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf); +static int vmw_legacy_srf_create(struct vmw_resource *res); +static int vmw_legacy_srf_destroy(struct vmw_resource *res); + +static const struct vmw_user_resource_conv user_surface_conv = { + .object_type = VMW_RES_SURFACE, + .base_obj_to_res = vmw_user_surface_base_to_res, + .res_free = vmw_user_surface_free +}; + +const struct vmw_user_resource_conv *user_surface_converter = + &user_surface_conv; + + +static uint64_t vmw_user_surface_size; + +static const struct vmw_res_func vmw_legacy_surface_func = { + .res_type = vmw_res_surface, + .needs_backup = false, + .may_evict = true, + .type_name = "legacy surfaces", + .backup_placement = &vmw_srf_placement, + .create = &vmw_legacy_srf_create, + .destroy = &vmw_legacy_srf_destroy, + .bind = &vmw_legacy_srf_bind, + .unbind = &vmw_legacy_srf_unbind +}; + +/** + * struct vmw_surface_dma - SVGA3D DMA command + */ +struct vmw_surface_dma { + SVGA3dCmdHeader header; + SVGA3dCmdSurfaceDMA body; + SVGA3dCopyBox cb; + SVGA3dCmdSurfaceDMASuffix suffix; +}; + +/** + * struct vmw_surface_define - SVGA3D Surface Define command + */ +struct vmw_surface_define { + SVGA3dCmdHeader header; + SVGA3dCmdDefineSurface body; +}; + +/** + * struct vmw_surface_destroy - SVGA3D Surface Destroy command + */ +struct vmw_surface_destroy { + SVGA3dCmdHeader header; + SVGA3dCmdDestroySurface body; +}; + + +/** + * vmw_surface_dma_size - Compute fifo size for a dma command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface dma command for backup or + * restoration of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_dma_size(const struct vmw_surface *srf) +{ + return srf->num_sizes * sizeof(struct vmw_surface_dma); +} + + +/** + * vmw_surface_define_size - Compute fifo size for a surface define command. + * + * @srf: Pointer to a struct vmw_surface + * + * Computes the required size for a surface define command for the definition + * of the surface represented by @srf. + */ +static inline uint32_t vmw_surface_define_size(const struct vmw_surface *srf) +{ + return sizeof(struct vmw_surface_define) + srf->num_sizes * + sizeof(SVGA3dSize); +} + + +/** + * vmw_surface_destroy_size - Compute fifo size for a surface destroy command. + * + * Computes the required size for a surface destroy command for the destruction + * of a hw surface. + */ +static inline uint32_t vmw_surface_destroy_size(void) +{ + return sizeof(struct vmw_surface_destroy); +} + +/** + * vmw_surface_destroy_encode - Encode a surface_destroy command. + * + * @id: The surface id + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_destroy_encode(uint32_t id, + void *cmd_space) +{ + struct vmw_surface_destroy *cmd = (struct vmw_surface_destroy *) + cmd_space; + + cmd->header.id = SVGA_3D_CMD_SURFACE_DESTROY; + cmd->header.size = sizeof(cmd->body); + cmd->body.sid = id; +} + +/** + * vmw_surface_define_encode - Encode a surface_define command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + */ +static void vmw_surface_define_encode(const struct vmw_surface *srf, + void *cmd_space) +{ + struct vmw_surface_define *cmd = (struct vmw_surface_define *) + cmd_space; + struct drm_vmw_size *src_size; + SVGA3dSize *cmd_size; + uint32_t cmd_len; + int i; + + cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize); + + cmd->header.id = SVGA_3D_CMD_SURFACE_DEFINE; + cmd->header.size = cmd_len; + cmd->body.sid = srf->res.id; + cmd->body.surfaceFlags = srf->flags; + cmd->body.format = cpu_to_le32(srf->format); + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + cmd->body.face[i].numMipLevels = srf->mip_levels[i]; + + cmd += 1; + cmd_size = (SVGA3dSize *) cmd; + src_size = srf->sizes; + + for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) { + cmd_size->width = src_size->width; + cmd_size->height = src_size->height; + cmd_size->depth = src_size->depth; + } +} + +/** + * vmw_surface_dma_encode - Encode a surface_dma command. + * + * @srf: Pointer to a struct vmw_surface object. + * @cmd_space: Pointer to memory area in which the commands should be encoded. + * @ptr: Pointer to an SVGAGuestPtr indicating where the surface contents + * should be placed or read from. + * @to_surface: Boolean whether to DMA to the surface or from the surface. + */ +static void vmw_surface_dma_encode(struct vmw_surface *srf, + void *cmd_space, + const SVGAGuestPtr *ptr, + bool to_surface) +{ + uint32_t i; + struct vmw_surface_dma *cmd = (struct vmw_surface_dma *)cmd_space; + const struct svga3d_surface_desc *desc = + svga3dsurface_get_desc(srf->format); + + for (i = 0; i < srf->num_sizes; ++i) { + SVGA3dCmdHeader *header = &cmd->header; + SVGA3dCmdSurfaceDMA *body = &cmd->body; + SVGA3dCopyBox *cb = &cmd->cb; + SVGA3dCmdSurfaceDMASuffix *suffix = &cmd->suffix; + const struct vmw_surface_offset *cur_offset = &srf->offsets[i]; + const struct drm_vmw_size *cur_size = &srf->sizes[i]; + + header->id = SVGA_3D_CMD_SURFACE_DMA; + header->size = sizeof(*body) + sizeof(*cb) + sizeof(*suffix); + + body->guest.ptr = *ptr; + body->guest.ptr.offset += cur_offset->bo_offset; + body->guest.pitch = svga3dsurface_calculate_pitch(desc, + cur_size); + body->host.sid = srf->res.id; + body->host.face = cur_offset->face; + body->host.mipmap = cur_offset->mip; + body->transfer = ((to_surface) ? SVGA3D_WRITE_HOST_VRAM : + SVGA3D_READ_HOST_VRAM); + cb->x = 0; + cb->y = 0; + cb->z = 0; + cb->srcx = 0; + cb->srcy = 0; + cb->srcz = 0; + cb->w = cur_size->width; + cb->h = cur_size->height; + cb->d = cur_size->depth; + + suffix->suffixSize = sizeof(*suffix); + suffix->maximumOffset = + svga3dsurface_get_image_buffer_size(desc, cur_size, + body->guest.pitch); + suffix->flags.discard = 0; + suffix->flags.unsynchronized = 0; + suffix->flags.reserved = 0; + ++cmd; + } +}; + + +/** + * vmw_hw_surface_destroy - destroy a Device surface + * + * @res: Pointer to a struct vmw_resource embedded in a struct + * vmw_surface. + * + * Destroys a the device surface associated with a struct vmw_surface if + * any, and adjusts accounting and resource count accordingly. + */ +static void vmw_hw_surface_destroy(struct vmw_resource *res) +{ + + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_surface *srf; + void *cmd; + + if (res->id != -1) { + + cmd = vmw_fifo_reserve(dev_priv, vmw_surface_destroy_size()); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "destruction.\n"); + return; + } + + vmw_surface_destroy_encode(res->id, cmd); + vmw_fifo_commit(dev_priv, vmw_surface_destroy_size()); + + /* + * used_memory_size_atomic, or separate lock + * to avoid taking dev_priv::cmdbuf_mutex in + * the destroy path. + */ + + mutex_lock(&dev_priv->cmdbuf_mutex); + srf = vmw_res_to_srf(res); + dev_priv->used_memory_size -= res->backup_size; + mutex_unlock(&dev_priv->cmdbuf_mutex); + } + vmw_3d_resource_dec(dev_priv, false); +} + +/** + * vmw_legacy_srf_create - Create a device surface as part of the + * resource validation process. + * + * @res: Pointer to a struct vmw_surface. + * + * If the surface doesn't have a hw id. + * + * Returns -EBUSY if there wasn't sufficient device resources to + * complete the validation. Retry after freeing up resources. + * + * May return other errors if the kernel is out of guest resources. + */ +static int vmw_legacy_srf_create(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + struct vmw_surface *srf; + uint32_t submit_size; + uint8_t *cmd; + int ret; + + if (likely(res->id != -1)) + return 0; + + srf = vmw_res_to_srf(res); + if (unlikely(dev_priv->used_memory_size + res->backup_size >= + dev_priv->memory_size)) + return -EBUSY; + + /* + * Alloc id for the resource. + */ + + ret = vmw_resource_alloc_id(res); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed to allocate a surface id.\n"); + goto out_no_id; + } + + if (unlikely(res->id >= SVGA3D_MAX_SURFACE_IDS)) { + ret = -EBUSY; + goto out_no_fifo; + } + + /* + * Encode surface define- commands. + */ + + submit_size = vmw_surface_define_size(srf); + cmd = vmw_fifo_reserve(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "creation.\n"); + ret = -ENOMEM; + goto out_no_fifo; + } + + vmw_surface_define_encode(srf, cmd); + vmw_fifo_commit(dev_priv, submit_size); + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size += res->backup_size; + return 0; + +out_no_fifo: + vmw_resource_release_id(res); +out_no_id: + return ret; +} + +/** + * vmw_legacy_srf_dma - Copy backup data to or from a legacy surface. + * + * @res: Pointer to a struct vmw_res embedded in a struct + * vmw_surface. + * @val_buf: Pointer to a struct ttm_validate_buffer containing + * information about the backup buffer. + * @bind: Boolean wether to DMA to the surface. + * + * Transfer backup data to or from a legacy surface as part of the + * validation process. + * May return other errors if the kernel is out of guest resources. + * The backup buffer will be fenced or idle upon successful completion, + * and if the surface needs persistent backup storage, the backup buffer + * will also be returned reserved iff @bind is true. + */ +static int vmw_legacy_srf_dma(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf, + bool bind) +{ + SVGAGuestPtr ptr; + struct vmw_fence_obj *fence; + uint32_t submit_size; + struct vmw_surface *srf = vmw_res_to_srf(res); + uint8_t *cmd; + struct vmw_private *dev_priv = res->dev_priv; + + BUG_ON(val_buf->bo == NULL); + + submit_size = vmw_surface_dma_size(srf); + cmd = vmw_fifo_reserve(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "DMA.\n"); + return -ENOMEM; + } + vmw_bo_get_guest_ptr(val_buf->bo, &ptr); + vmw_surface_dma_encode(srf, cmd, &ptr, bind); + + vmw_fifo_commit(dev_priv, submit_size); + + /* + * Create a fence object and fence the backup buffer. + */ + + (void) vmw_execbuf_fence_commands(NULL, dev_priv, + &fence, NULL); + + vmw_fence_single_bo(val_buf->bo, fence); + + if (likely(fence != NULL)) + vmw_fence_obj_unreference(&fence); + + return 0; +} + +/** + * vmw_legacy_srf_bind - Perform a legacy surface bind as part of the + * surface validation process. + * + * @res: Pointer to a struct vmw_res embedded in a struct + * vmw_surface. + * @val_buf: Pointer to a struct ttm_validate_buffer containing + * information about the backup buffer. + * + * This function will copy backup data to the surface if the + * backup buffer is dirty. + */ +static int vmw_legacy_srf_bind(struct vmw_resource *res, + struct ttm_validate_buffer *val_buf) +{ + if (!res->backup_dirty) + return 0; + + return vmw_legacy_srf_dma(res, val_buf, true); +} + + +/** + * vmw_legacy_srf_unbind - Perform a legacy surface unbind as part of the + * surface eviction process. + * + * @res: Pointer to a struct vmw_res embedded in a struct + * vmw_surface. + * @val_buf: Pointer to a struct ttm_validate_buffer containing + * information about the backup buffer. + * + * This function will copy backup data from the surface. + */ +static int vmw_legacy_srf_unbind(struct vmw_resource *res, + bool readback, + struct ttm_validate_buffer *val_buf) +{ + if (unlikely(readback)) + return vmw_legacy_srf_dma(res, val_buf, false); + return 0; +} + +/** + * vmw_legacy_srf_destroy - Destroy a device surface as part of a + * resource eviction process. + * + * @res: Pointer to a struct vmw_res embedded in a struct + * vmw_surface. + */ +static int vmw_legacy_srf_destroy(struct vmw_resource *res) +{ + struct vmw_private *dev_priv = res->dev_priv; + uint32_t submit_size; + uint8_t *cmd; + + BUG_ON(res->id == -1); + + /* + * Encode the dma- and surface destroy commands. + */ + + submit_size = vmw_surface_destroy_size(); + cmd = vmw_fifo_reserve(dev_priv, submit_size); + if (unlikely(cmd == NULL)) { + DRM_ERROR("Failed reserving FIFO space for surface " + "eviction.\n"); + return -ENOMEM; + } + + vmw_surface_destroy_encode(res->id, cmd); + vmw_fifo_commit(dev_priv, submit_size); + + /* + * Surface memory usage accounting. + */ + + dev_priv->used_memory_size -= res->backup_size; + + /* + * Release the surface ID. + */ + + vmw_resource_release_id(res); + + return 0; +} + + +/** + * vmw_surface_init - initialize a struct vmw_surface + * + * @dev_priv: Pointer to a device private struct. + * @srf: Pointer to the struct vmw_surface to initialize. + * @res_free: Pointer to a resource destructor used to free + * the object. + */ +static int vmw_surface_init(struct vmw_private *dev_priv, + struct vmw_surface *srf, + void (*res_free) (struct vmw_resource *res)) +{ + int ret; + struct vmw_resource *res = &srf->res; + + BUG_ON(res_free == NULL); + (void) vmw_3d_resource_inc(dev_priv, false); + ret = vmw_resource_init(dev_priv, res, true, res_free, + &vmw_legacy_surface_func); + + if (unlikely(ret != 0)) { + vmw_3d_resource_dec(dev_priv, false); + res_free(res); + return ret; + } + + /* + * The surface won't be visible to hardware until a + * surface validate. + */ + + vmw_resource_activate(res, vmw_hw_surface_destroy); + return ret; +} + +/** + * vmw_user_surface_base_to_res - TTM base object to resource converter for + * user visible surfaces + * + * @base: Pointer to a TTM base object + * + * Returns the struct vmw_resource embedded in a struct vmw_surface + * for the user-visible object identified by the TTM base object @base. + */ +static struct vmw_resource * +vmw_user_surface_base_to_res(struct ttm_base_object *base) +{ + return &(container_of(base, struct vmw_user_surface, base)->srf.res); +} + +/** + * vmw_user_surface_free - User visible surface resource destructor + * + * @res: A struct vmw_resource embedded in a struct vmw_surface. + */ +static void vmw_user_surface_free(struct vmw_resource *res) +{ + struct vmw_surface *srf = vmw_res_to_srf(res); + struct vmw_user_surface *user_srf = + container_of(srf, struct vmw_user_surface, srf); + struct vmw_private *dev_priv = srf->res.dev_priv; + uint32_t size = user_srf->size; + + kfree(srf->offsets); + kfree(srf->sizes); + kfree(srf->snooper.image); + ttm_base_object_kfree(user_srf, base); + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +} + +/** + * vmw_user_surface_free - User visible surface TTM base object destructor + * + * @p_base: Pointer to a pointer to a TTM base object + * embedded in a struct vmw_user_surface. + * + * Drops the base object's reference on its resource, and the + * pointer pointed to by *p_base is set to NULL. + */ +static void vmw_user_surface_base_release(struct ttm_base_object **p_base) +{ + struct ttm_base_object *base = *p_base; + struct vmw_user_surface *user_srf = + container_of(base, struct vmw_user_surface, base); + struct vmw_resource *res = &user_srf->srf.res; + + *p_base = NULL; + vmw_resource_unreference(&res); +} + +/** + * vmw_user_surface_destroy_ioctl - Ioctl function implementing + * the user surface destroy functionality. + * + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + + return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE); +} + +/** + * vmw_user_surface_define_ioctl - Ioctl function implementing + * the user surface define functionality. + * + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +int vmw_surface_define_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + struct vmw_private *dev_priv = vmw_priv(dev); + struct vmw_user_surface *user_srf; + struct vmw_surface *srf; + struct vmw_resource *res; + struct vmw_resource *tmp; + union drm_vmw_surface_create_arg *arg = + (union drm_vmw_surface_create_arg *)data; + struct drm_vmw_surface_create_req *req = &arg->req; + struct drm_vmw_surface_arg *rep = &arg->rep; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct drm_vmw_size __user *user_sizes; + int ret; + int i, j; + uint32_t cur_bo_offset; + struct drm_vmw_size *cur_size; + struct vmw_surface_offset *cur_offset; + uint32_t num_sizes; + uint32_t size; + struct vmw_master *vmaster = vmw_master(file_priv->master); + const struct svga3d_surface_desc *desc; + + if (unlikely(vmw_user_surface_size == 0)) + vmw_user_surface_size = ttm_round_pot(sizeof(*user_srf)) + + 128; + + num_sizes = 0; + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) + num_sizes += req->mip_levels[i]; + + if (num_sizes > DRM_VMW_MAX_SURFACE_FACES * + DRM_VMW_MAX_MIP_LEVELS) + return -EINVAL; + + size = vmw_user_surface_size + 128 + + ttm_round_pot(num_sizes * sizeof(struct drm_vmw_size)) + + ttm_round_pot(num_sizes * sizeof(struct vmw_surface_offset)); + + + desc = svga3dsurface_get_desc(req->format); + if (unlikely(desc->block_desc == SVGA3DBLOCKDESC_NONE)) { + DRM_ERROR("Invalid surface format for surface creation.\n"); + return -EINVAL; + } + + ret = ttm_read_lock(&vmaster->lock, true); + if (unlikely(ret != 0)) + return ret; + + ret = ttm_mem_global_alloc(vmw_mem_glob(dev_priv), + size, false, true); + if (unlikely(ret != 0)) { + if (ret != -ERESTARTSYS) + DRM_ERROR("Out of graphics memory for surface" + " creation.\n"); + goto out_unlock; + } + + user_srf = kzalloc(sizeof(*user_srf), GFP_KERNEL); + if (unlikely(user_srf == NULL)) { + ret = -ENOMEM; + goto out_no_user_srf; + } + + srf = &user_srf->srf; + res = &srf->res; + + srf->flags = req->flags; + srf->format = req->format; + srf->scanout = req->scanout; + + memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels)); + srf->num_sizes = num_sizes; + user_srf->size = size; + + srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL); + if (unlikely(srf->sizes == NULL)) { + ret = -ENOMEM; + goto out_no_sizes; + } + srf->offsets = kmalloc(srf->num_sizes * sizeof(*srf->offsets), + GFP_KERNEL); + if (unlikely(srf->sizes == NULL)) { + ret = -ENOMEM; + goto out_no_offsets; + } + + user_sizes = (struct drm_vmw_size __user *)(unsigned long) + req->size_addr; + + ret = copy_from_user(srf->sizes, user_sizes, + srf->num_sizes * sizeof(*srf->sizes)); + if (unlikely(ret != 0)) { + ret = -EFAULT; + goto out_no_copy; + } + + srf->base_size = *srf->sizes; + srf->autogen_filter = SVGA3D_TEX_FILTER_NONE; + srf->multisample_count = 1; + + cur_bo_offset = 0; + cur_offset = srf->offsets; + cur_size = srf->sizes; + + for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) { + for (j = 0; j < srf->mip_levels[i]; ++j) { + uint32_t stride = svga3dsurface_calculate_pitch + (desc, cur_size); + + cur_offset->face = i; + cur_offset->mip = j; + cur_offset->bo_offset = cur_bo_offset; + cur_bo_offset += svga3dsurface_get_image_buffer_size + (desc, cur_size, stride); + ++cur_offset; + ++cur_size; + } + } + res->backup_size = cur_bo_offset; + if (srf->scanout && + srf->num_sizes == 1 && + srf->sizes[0].width == 64 && + srf->sizes[0].height == 64 && + srf->format == SVGA3D_A8R8G8B8) { + + srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL); + /* clear the image */ + if (srf->snooper.image) { + memset(srf->snooper.image, 0x00, 64 * 64 * 4); + } else { + DRM_ERROR("Failed to allocate cursor_image\n"); + ret = -ENOMEM; + goto out_no_copy; + } + } else { + srf->snooper.image = NULL; + } + srf->snooper.crtc = NULL; + + user_srf->base.shareable = false; + user_srf->base.tfile = NULL; + + /** + * From this point, the generic resource management functions + * destroy the object on failure. + */ + + ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free); + if (unlikely(ret != 0)) + goto out_unlock; + + tmp = vmw_resource_reference(&srf->res); + ret = ttm_base_object_init(tfile, &user_srf->base, + req->shareable, VMW_RES_SURFACE, + &vmw_user_surface_base_release, NULL); + + if (unlikely(ret != 0)) { + vmw_resource_unreference(&tmp); + vmw_resource_unreference(&res); + goto out_unlock; + } + + rep->sid = user_srf->base.hash.key; + vmw_resource_unreference(&res); + + ttm_read_unlock(&vmaster->lock); + return 0; +out_no_copy: + kfree(srf->offsets); +out_no_offsets: + kfree(srf->sizes); +out_no_sizes: + ttm_base_object_kfree(user_srf, base); +out_no_user_srf: + ttm_mem_global_free(vmw_mem_glob(dev_priv), size); +out_unlock: + ttm_read_unlock(&vmaster->lock); + return ret; +} + +/** + * vmw_user_surface_define_ioctl - Ioctl function implementing + * the user surface reference functionality. + * + * @dev: Pointer to a struct drm_device. + * @data: Pointer to data copied from / to user-space. + * @file_priv: Pointer to a drm file private structure. + */ +int vmw_surface_reference_ioctl(struct drm_device *dev, void *data, + struct drm_file *file_priv) +{ + union drm_vmw_surface_reference_arg *arg = + (union drm_vmw_surface_reference_arg *)data; + struct drm_vmw_surface_arg *req = &arg->req; + struct drm_vmw_surface_create_req *rep = &arg->rep; + struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; + struct vmw_surface *srf; + struct vmw_user_surface *user_srf; + struct drm_vmw_size __user *user_sizes; + struct ttm_base_object *base; + int ret = -EINVAL; + + base = ttm_base_object_lookup(tfile, req->sid); + if (unlikely(base == NULL)) { + DRM_ERROR("Could not find surface to reference.\n"); + return -EINVAL; + } + + if (unlikely(base->object_type != VMW_RES_SURFACE)) + goto out_bad_resource; + + user_srf = container_of(base, struct vmw_user_surface, base); + srf = &user_srf->srf; + + ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL); + if (unlikely(ret != 0)) { + DRM_ERROR("Could not add a reference to a surface.\n"); + goto out_no_reference; + } + + rep->flags = srf->flags; + rep->format = srf->format; + memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels)); + user_sizes = (struct drm_vmw_size __user *)(unsigned long) + rep->size_addr; + + if (user_sizes) + ret = copy_to_user(user_sizes, srf->sizes, + srf->num_sizes * sizeof(*srf->sizes)); + if (unlikely(ret != 0)) { + DRM_ERROR("copy_to_user failed %p %u\n", + user_sizes, srf->num_sizes); + ret = -EFAULT; + } +out_bad_resource: +out_no_reference: + ttm_base_object_unref(&base); + + return ret; +} |