drm/nouveau: Add DRM driver for NVIDIA GPUs

This adds a drm/kms staging non-API stable driver for GPUs from NVIDIA.

This driver is a KMS-based driver and requires a compatible nouveau
userspace libdrm and nouveau X.org driver.

This driver requires firmware files not available in this kernel tree,
interested parties can find them via the nouveau project git archive.

This driver is reverse engineered, and is in no way supported by nVidia.

Support for nearly the complete range of nvidia hw from nv04->g80 (nv50)
is available, and the kms driver should support driving nearly all
output types (displayport is under development still) along with supporting
suspend/resume.

This work is all from the upstream nouveau project found at
nouveau.freedesktop.org.

The original authors list from nouveau git tree is:
Anssi Hannula <anssi.hannula@iki.fi>
Ben Skeggs <bskeggs@redhat.com>
Francisco Jerez <currojerez@riseup.net>
Maarten Maathuis <madman2003@gmail.com>
Marcin Kościelnicki <koriakin@0x04.net>
Matthew Garrett <mjg@redhat.com>
Matt Parnell <mparnell@gmail.com>
Patrice Mandin <patmandin@gmail.com>
Pekka Paalanen <pq@iki.fi>
Xavier Chantry <shiningxc@gmail.com>
along with project founder Stephane Marchesin <marchesin@icps.u-strasbg.fr>

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
This commit is contained in:
Ben Skeggs 2009-12-11 19:24:15 +10:00 committed by Dave Airlie
parent d1ede145ce
commit 6ee738610f
85 changed files with 36161 additions and 0 deletions

View File

@ -31,3 +31,5 @@ obj-$(CONFIG_DRM_I915) += i915/
obj-$(CONFIG_DRM_SIS) += sis/
obj-$(CONFIG_DRM_SAVAGE)+= savage/
obj-$(CONFIG_DRM_VIA) +=via/
obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
obj-y += i2c/

View File

@ -0,0 +1,4 @@
ccflags-y := -Iinclude/drm
ch7006-y := ch7006_drv.o ch7006_mode.o
obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o

View File

@ -0,0 +1,531 @@
/*
* Copyright (C) 2009 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 "ch7006_priv.h"
/* DRM encoder functions */
static void ch7006_encoder_set_config(struct drm_encoder *encoder,
void *params)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
priv->params = params;
}
static void ch7006_encoder_destroy(struct drm_encoder *encoder)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
drm_property_destroy(encoder->dev, priv->scale_property);
kfree(priv);
to_encoder_slave(encoder)->slave_priv = NULL;
drm_i2c_encoder_destroy(encoder);
}
static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_state *state = &priv->state;
ch7006_dbg(client, "\n");
if (mode == priv->last_dpms)
return;
priv->last_dpms = mode;
ch7006_setup_power_state(encoder);
ch7006_load_reg(client, state, CH7006_POWER);
}
static void ch7006_encoder_save(struct drm_encoder *encoder)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
ch7006_dbg(client, "\n");
ch7006_state_save(client, &priv->saved_state);
}
static void ch7006_encoder_restore(struct drm_encoder *encoder)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
ch7006_dbg(client, "\n");
ch7006_state_load(client, &priv->saved_state);
}
static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
/* The ch7006 is painfully picky with the input timings so no
* custom modes for now... */
priv->mode = ch7006_lookup_mode(encoder, mode);
return !!priv->mode;
}
static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
if (ch7006_lookup_mode(encoder, mode))
return MODE_OK;
else
return MODE_BAD;
}
static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *drm_mode,
struct drm_display_mode *adjusted_mode)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_encoder_params *params = priv->params;
struct ch7006_state *state = &priv->state;
uint8_t *regs = state->regs;
struct ch7006_mode *mode = priv->mode;
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
int start_active;
ch7006_dbg(client, "\n");
regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
regs[CH7006_BWIDTH] = 0;
regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
params->input_format);
regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
| bitf(CH7006_CLKMODE_XCM, params->xcm)
| bitf(CH7006_CLKMODE_PCM, params->pcm);
if (params->clock_mode)
regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
if (params->clock_edge)
regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
regs[CH7006_INPUT_SYNC] = 0;
if (params->sync_direction)
regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
if (params->sync_encoding)
regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
regs[CH7006_DETECT] = 0;
regs[CH7006_BCLKOUT] = 0;
regs[CH7006_SUBC_INC3] = 0;
if (params->pout_level)
regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
regs[CH7006_SUBC_INC4] = 0;
if (params->active_detect)
regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
ch7006_setup_levels(encoder);
ch7006_setup_subcarrier(encoder);
ch7006_setup_pll(encoder);
ch7006_setup_power_state(encoder);
ch7006_setup_properties(encoder);
ch7006_state_load(client, state);
}
static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_state *state = &priv->state;
int det;
ch7006_dbg(client, "\n");
ch7006_save_reg(client, state, CH7006_DETECT);
ch7006_save_reg(client, state, CH7006_POWER);
ch7006_save_reg(client, state, CH7006_CLKMODE);
ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
bitfs(CH7006_POWER_LEVEL, NORMAL));
ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
ch7006_write(client, CH7006_DETECT, 0);
det = ch7006_read(client, CH7006_DETECT);
ch7006_load_reg(client, state, CH7006_CLKMODE);
ch7006_load_reg(client, state, CH7006_POWER);
ch7006_load_reg(client, state, CH7006_DETECT);
if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
CH7006_DETECT_SVIDEO_C_TEST|
CH7006_DETECT_CVBS_TEST)) == 0)
priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
CH7006_DETECT_SVIDEO_C_TEST)) == 0)
priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
else
priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
drm_connector_property_set_value(connector,
encoder->dev->mode_config.tv_subconnector_property,
priv->subconnector);
return priv->subconnector ? connector_status_connected :
connector_status_disconnected;
}
static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_mode *mode;
int n = 0;
for (mode = ch7006_modes; mode->mode.clock; mode++) {
if (~mode->valid_scales & 1<<priv->scale ||
~mode->valid_norms & 1<<priv->norm)
continue;
drm_mode_probed_add(connector,
drm_mode_duplicate(encoder->dev, &mode->mode));
n++;
}
return n;
}
static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct drm_device *dev = encoder->dev;
struct drm_mode_config *conf = &dev->mode_config;
drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
"scale", 2);
priv->scale_property->values[0] = 0;
priv->scale_property->values[1] = 2;
drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
priv->select_subconnector);
drm_connector_attach_property(connector, conf->tv_subconnector_property,
priv->subconnector);
drm_connector_attach_property(connector, conf->tv_left_margin_property,
priv->hmargin);
drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
priv->vmargin);
drm_connector_attach_property(connector, conf->tv_mode_property,
priv->norm);
drm_connector_attach_property(connector, conf->tv_brightness_property,
priv->brightness);
drm_connector_attach_property(connector, conf->tv_contrast_property,
priv->contrast);
drm_connector_attach_property(connector, conf->tv_flicker_reduction_property,
priv->flicker);
drm_connector_attach_property(connector, priv->scale_property,
priv->scale);
return 0;
}
static int ch7006_encoder_set_property(struct drm_encoder *encoder,
struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_state *state = &priv->state;
struct drm_mode_config *conf = &encoder->dev->mode_config;
struct drm_crtc *crtc = encoder->crtc;
bool modes_changed = false;
ch7006_dbg(client, "\n");
if (property == conf->tv_select_subconnector_property) {
priv->select_subconnector = val;
ch7006_setup_power_state(encoder);
ch7006_load_reg(client, state, CH7006_POWER);
} else if (property == conf->tv_left_margin_property) {
priv->hmargin = val;
ch7006_setup_properties(encoder);
ch7006_load_reg(client, state, CH7006_POV);
ch7006_load_reg(client, state, CH7006_HPOS);
} else if (property == conf->tv_bottom_margin_property) {
priv->vmargin = val;
ch7006_setup_properties(encoder);
ch7006_load_reg(client, state, CH7006_POV);
ch7006_load_reg(client, state, CH7006_VPOS);
} else if (property == conf->tv_mode_property) {
if (connector->dpms != DRM_MODE_DPMS_OFF)
return -EINVAL;
priv->norm = val;
modes_changed = true;
} else if (property == conf->tv_brightness_property) {
priv->brightness = val;
ch7006_setup_levels(encoder);
ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
} else if (property == conf->tv_contrast_property) {
priv->contrast = val;
ch7006_setup_properties(encoder);
ch7006_load_reg(client, state, CH7006_CONTRAST);
} else if (property == conf->tv_flicker_reduction_property) {
priv->flicker = val;
ch7006_setup_properties(encoder);
ch7006_load_reg(client, state, CH7006_FFILTER);
} else if (property == priv->scale_property) {
if (connector->dpms != DRM_MODE_DPMS_OFF)
return -EINVAL;
priv->scale = val;
modes_changed = true;
} else {
return -EINVAL;
}
if (modes_changed) {
drm_helper_probe_single_connector_modes(connector, 0, 0);
/* Disable the crtc to ensure a full modeset is
* performed whenever it's turned on again. */
if (crtc) {
struct drm_mode_set modeset = {
.crtc = crtc,
};
crtc->funcs->set_config(&modeset);
}
}
return 0;
}
static struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
.set_config = ch7006_encoder_set_config,
.destroy = ch7006_encoder_destroy,
.dpms = ch7006_encoder_dpms,
.save = ch7006_encoder_save,
.restore = ch7006_encoder_restore,
.mode_fixup = ch7006_encoder_mode_fixup,
.mode_valid = ch7006_encoder_mode_valid,
.mode_set = ch7006_encoder_mode_set,
.detect = ch7006_encoder_detect,
.get_modes = ch7006_encoder_get_modes,
.create_resources = ch7006_encoder_create_resources,
.set_property = ch7006_encoder_set_property,
};
/* I2C driver functions */
static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
uint8_t addr = CH7006_VERSION_ID;
uint8_t val;
int ret;
ch7006_dbg(client, "\n");
ret = i2c_master_send(client, &addr, sizeof(addr));
if (ret < 0)
goto fail;
ret = i2c_master_recv(client, &val, sizeof(val));
if (ret < 0)
goto fail;
ch7006_info(client, "Detected version ID: %x\n", val);
return 0;
fail:
ch7006_err(client, "Error %d reading version ID\n", ret);
return -ENODEV;
}
static int ch7006_remove(struct i2c_client *client)
{
ch7006_dbg(client, "\n");
return 0;
}
static int ch7006_encoder_init(struct i2c_client *client,
struct drm_device *dev,
struct drm_encoder_slave *encoder)
{
struct ch7006_priv *priv;
int i;
ch7006_dbg(client, "\n");
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
encoder->slave_priv = priv;
encoder->slave_funcs = &ch7006_encoder_funcs;
priv->norm = TV_NORM_PAL;
priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
priv->scale = 1;
priv->contrast = 50;
priv->brightness = 50;
priv->flicker = 50;
priv->hmargin = 50;
priv->vmargin = 50;
priv->last_dpms = -1;
if (ch7006_tv_norm) {
for (i = 0; i < NUM_TV_NORMS; i++) {
if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
priv->norm = i;
break;
}
}
if (i == NUM_TV_NORMS)
ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
ch7006_tv_norm);
}
if (ch7006_scale >= 0 && ch7006_scale <= 2)
priv->scale = ch7006_scale;
else
ch7006_err(client, "Invalid scale setting \"%d\".\n",
ch7006_scale);
return 0;
}
static struct i2c_device_id ch7006_ids[] = {
{ "ch7006", 0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ch7006_ids);
static struct drm_i2c_encoder_driver ch7006_driver = {
.i2c_driver = {
.probe = ch7006_probe,
.remove = ch7006_remove,
.driver = {
.name = "ch7006",
},
.id_table = ch7006_ids,
},
.encoder_init = ch7006_encoder_init,
};
/* Module initialization */
static int __init ch7006_init(void)
{
return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
}
static void __exit ch7006_exit(void)
{
drm_i2c_encoder_unregister(&ch7006_driver);
}
int ch7006_debug;
module_param_named(debug, ch7006_debug, int, 0600);
MODULE_PARM_DESC(debug, "Enable debug output.");
char *ch7006_tv_norm;
module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
"\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
"\t\tDefault: PAL");
int ch7006_scale = 1;
module_param_named(scale, ch7006_scale, int, 0600);
MODULE_PARM_DESC(scale, "Default scale.\n"
"\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
"\t\t\t1 -> Select default video modes.\n"
"\t\t\t2 -> Select video modes with a lower blanking ratio.");
MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
MODULE_LICENSE("GPL and additional rights");
module_init(ch7006_init);
module_exit(ch7006_exit);

View File

@ -0,0 +1,473 @@
/*
* Copyright (C) 2009 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 "ch7006_priv.h"
char *ch7006_tv_norm_names[] = {
[TV_NORM_PAL] = "PAL",
[TV_NORM_PAL_M] = "PAL-M",
[TV_NORM_PAL_N] = "PAL-N",
[TV_NORM_PAL_NC] = "PAL-Nc",
[TV_NORM_PAL_60] = "PAL-60",
[TV_NORM_NTSC_M] = "NTSC-M",
[TV_NORM_NTSC_J] = "NTSC-J",
};
#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
.vdisplay = 480, \
.vtotal = 525, \
.hvirtual = 660
#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
.vdisplay = 576, \
.vtotal = 625, \
.hvirtual = 810
struct ch7006_tv_norm_info ch7006_tv_norms[] = {
[TV_NORM_NTSC_M] = {
NTSC_LIKE_TIMINGS,
.black_level = 0.339 * fixed1,
.subc_freq = 3579545 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
.voffset = 0,
},
[TV_NORM_NTSC_J] = {
NTSC_LIKE_TIMINGS,
.black_level = 0.286 * fixed1,
.subc_freq = 3579545 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
.voffset = 0,
},
[TV_NORM_PAL] = {
PAL_LIKE_TIMINGS,
.black_level = 0.3 * fixed1,
.subc_freq = 4433618.75 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
.voffset = 0,
},
[TV_NORM_PAL_M] = {
NTSC_LIKE_TIMINGS,
.black_level = 0.339 * fixed1,
.subc_freq = 3575611.433 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
.voffset = 16,
},
/* The following modes seem to work right but they're
* undocumented */
[TV_NORM_PAL_N] = {
PAL_LIKE_TIMINGS,
.black_level = 0.339 * fixed1,
.subc_freq = 4433618.75 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
.voffset = 0,
},
[TV_NORM_PAL_NC] = {
PAL_LIKE_TIMINGS,
.black_level = 0.3 * fixed1,
.subc_freq = 3582056.25 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
.voffset = 0,
},
[TV_NORM_PAL_60] = {
NTSC_LIKE_TIMINGS,
.black_level = 0.3 * fixed1,
.subc_freq = 4433618.75 * fixed1,
.dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
.voffset = 16,
},
};
#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
.mode = { \
.name = #hd "x" #vd, \
.status = 0, \
.type = DRM_MODE_TYPE_DRIVER, \
.clock = f, \
.hdisplay = hd, \
.hsync_start = e_hd + 16, \
.hsync_end = e_hd + 80, \
.htotal = ht, \
.hskew = 0, \
.vdisplay = vd, \
.vsync_start = vd + 10, \
.vsync_end = vd + 26, \
.vtotal = vt, \
.vscan = 0, \
.flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
DRM_MODE_FLAG_##vsynp##VSYNC, \
.vrefresh = 0, \
}, \
.enc_hdisp = e_hd, \
.enc_vdisp = e_vd, \
.subc_coeff = subc * fixed1, \
.dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
.valid_scales = scale_mask, \
.valid_norms = norm_mask \
}
#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
subc, scale, scale_mask, norm_mask) \
__MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
scale_mask, norm_mask, hd, vd)
#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
struct ch7006_mode ch7006_modes[] = {
MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
__MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
{}
};
struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
struct drm_display_mode *drm_mode)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_mode *mode;
for (mode = ch7006_modes; mode->mode.clock; mode++) {
if (~mode->valid_norms & 1<<priv->norm)
continue;
if (mode->mode.hdisplay != drm_mode->hdisplay ||
mode->mode.vdisplay != drm_mode->vdisplay ||
mode->mode.vtotal != drm_mode->vtotal ||
mode->mode.htotal != drm_mode->htotal ||
mode->mode.clock != drm_mode->clock)
continue;
return mode;
}
return NULL;
}
/* Some common HW state calculation code */
void ch7006_setup_levels(struct drm_encoder *encoder)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
uint8_t *regs = priv->state.regs;
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
int gain;
int black_level;
/* Set DAC_GAIN if the voltage drop between white and black is
* high enough. */
if (norm->black_level < 339*fixed1/1000) {
gain = 76;
regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
} else {
gain = 71;
regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
}
black_level = round_fixed(norm->black_level*26625)/gain;
/* Correct it with the specified brightness. */
black_level = interpolate(90, black_level, 208, priv->brightness);
regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
ch7006_dbg(client, "black level: %d\n", black_level);
}
void ch7006_setup_subcarrier(struct drm_encoder *encoder)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_state *state = &priv->state;
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
struct ch7006_mode *mode = priv->mode;
uint32_t subc_inc;
subc_inc = round_fixed((mode->subc_coeff >> 8)
* (norm->subc_freq >> 24));
setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
}
void ch7006_setup_pll(struct drm_encoder *encoder)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
uint8_t *regs = priv->state.regs;
struct ch7006_mode *mode = priv->mode;
int n, best_n = 0;
int m, best_m = 0;
int freq, best_freq = 0;
for (n = 0; n < CH7006_MAXN; n++) {
for (m = 0; m < CH7006_MAXM; m++) {
freq = CH7006_FREQ0*(n+2)/(m+2);
if (abs(freq - mode->mode.clock) <
abs(best_freq - mode->mode.clock)) {
best_freq = freq;
best_n = n;
best_m = m;
}
}
}
regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
bitf(CH7006_PLLOV_M_8, best_m);
regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
if (best_n < 108)
regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
else
regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
best_n, best_m, best_freq, best_n < 108);
}
void ch7006_setup_power_state(struct drm_encoder *encoder)
{
struct ch7006_priv *priv = to_ch7006_priv(encoder);
uint8_t *power = &priv->state.regs[CH7006_POWER];
int subconnector;
subconnector = priv->select_subconnector ? priv->select_subconnector :
priv->subconnector;
*power = CH7006_POWER_RESET;
if (priv->last_dpms == DRM_MODE_DPMS_ON) {
switch (subconnector) {
case DRM_MODE_SUBCONNECTOR_SVIDEO:
*power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
break;
case DRM_MODE_SUBCONNECTOR_Composite:
*power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
break;
case DRM_MODE_SUBCONNECTOR_SCART:
*power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
CH7006_POWER_SCART;
break;
}
} else {
*power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
}
}
void ch7006_setup_properties(struct drm_encoder *encoder)
{
struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
struct ch7006_priv *priv = to_ch7006_priv(encoder);
struct ch7006_state *state = &priv->state;
struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
struct ch7006_mode *ch_mode = priv->mode;
struct drm_display_mode *mode = &ch_mode->mode;
uint8_t *regs = state->regs;
int flicker, contrast, hpos, vpos;
uint64_t scale, aspect;
flicker = interpolate(0, 2, 3, priv->flicker);
regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
bitf(CH7006_FFILTER_LUMA, flicker) |
bitf(CH7006_FFILTER_CHROMA, 1);
contrast = interpolate(0, 5, 7, priv->contrast);
regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
scale = norm->vtotal*fixed1;
do_div(scale, mode->vtotal);
aspect = ch_mode->enc_hdisp*fixed1;
do_div(aspect, ch_mode->enc_vdisp);
hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
* priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
setbitf(state, CH7006_POV, HPOS_8, hpos);
setbitf(state, CH7006_HPOS, 0, hpos);
vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
+ norm->voffset) * priv->vmargin / 100 / 2;
setbitf(state, CH7006_POV, VPOS_8, vpos);
setbitf(state, CH7006_VPOS, 0, vpos);
ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
}
/* HW access functions */
void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
{
uint8_t buf[] = {addr, val};
int ret;
ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
if (ret < 0)
ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
ret, addr);
}
uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
{
uint8_t val;
int ret;
ret = i2c_master_send(client, &addr, sizeof(addr));
if (ret < 0)
goto fail;
ret = i2c_master_recv(client, &val, sizeof(val));
if (ret < 0)
goto fail;
return val;
fail:
ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
ret, addr);
return 0;
}
void ch7006_state_load(struct i2c_client *client,
struct ch7006_state *state)
{
ch7006_load_reg(client, state, CH7006_POWER);
ch7006_load_reg(client, state, CH7006_DISPMODE);
ch7006_load_reg(client, state, CH7006_FFILTER);
ch7006_load_reg(client, state, CH7006_BWIDTH);
ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
ch7006_load_reg(client, state, CH7006_CLKMODE);
ch7006_load_reg(client, state, CH7006_START_ACTIVE);
ch7006_load_reg(client, state, CH7006_POV);
ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
ch7006_load_reg(client, state, CH7006_HPOS);
ch7006_load_reg(client, state, CH7006_VPOS);
ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
ch7006_load_reg(client, state, CH7006_DETECT);
ch7006_load_reg(client, state, CH7006_CONTRAST);
ch7006_load_reg(client, state, CH7006_PLLOV);
ch7006_load_reg(client, state, CH7006_PLLM);
ch7006_load_reg(client, state, CH7006_PLLN);
ch7006_load_reg(client, state, CH7006_BCLKOUT);
ch7006_load_reg(client, state, CH7006_SUBC_INC0);
ch7006_load_reg(client, state, CH7006_SUBC_INC1);
ch7006_load_reg(client, state, CH7006_SUBC_INC2);
ch7006_load_reg(client, state, CH7006_SUBC_INC3);
ch7006_load_reg(client, state, CH7006_SUBC_INC4);
ch7006_load_reg(client, state, CH7006_SUBC_INC5);
ch7006_load_reg(client, state, CH7006_SUBC_INC6);
ch7006_load_reg(client, state, CH7006_SUBC_INC7);
ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
/* I don't know what this is for, but otherwise I get no
* signal.
*/
ch7006_write(client, 0x3d, 0x0);
}
void ch7006_state_save(struct i2c_client *client,
struct ch7006_state *state)
{
ch7006_save_reg(client, state, CH7006_POWER);
ch7006_save_reg(client, state, CH7006_DISPMODE);
ch7006_save_reg(client, state, CH7006_FFILTER);
ch7006_save_reg(client, state, CH7006_BWIDTH);
ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
ch7006_save_reg(client, state, CH7006_CLKMODE);
ch7006_save_reg(client, state, CH7006_START_ACTIVE);
ch7006_save_reg(client, state, CH7006_POV);
ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
ch7006_save_reg(client, state, CH7006_HPOS);
ch7006_save_reg(client, state, CH7006_VPOS);
ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
ch7006_save_reg(client, state, CH7006_DETECT);
ch7006_save_reg(client, state, CH7006_CONTRAST);
ch7006_save_reg(client, state, CH7006_PLLOV);
ch7006_save_reg(client, state, CH7006_PLLM);
ch7006_save_reg(client, state, CH7006_PLLN);
ch7006_save_reg(client, state, CH7006_BCLKOUT);
ch7006_save_reg(client, state, CH7006_SUBC_INC0);
ch7006_save_reg(client, state, CH7006_SUBC_INC1);
ch7006_save_reg(client, state, CH7006_SUBC_INC2);
ch7006_save_reg(client, state, CH7006_SUBC_INC3);
ch7006_save_reg(client, state, CH7006_SUBC_INC4);
ch7006_save_reg(client, state, CH7006_SUBC_INC5);
ch7006_save_reg(client, state, CH7006_SUBC_INC6);
ch7006_save_reg(client, state, CH7006_SUBC_INC7);
ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
(state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
(state->regs[CH7006_FFILTER] & 0x03) << 2;
}

View File

@ -0,0 +1,344 @@
/*
* Copyright (C) 2009 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.
*
*/
#ifndef __DRM_I2C_CH7006_PRIV_H__
#define __DRM_I2C_CH7006_PRIV_H__
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "drm_encoder_slave.h"
#include "i2c/ch7006.h"
typedef int64_t fixed;
#define fixed1 (1LL << 32)
enum ch7006_tv_norm {
TV_NORM_PAL,
TV_NORM_PAL_M,
TV_NORM_PAL_N,
TV_NORM_PAL_NC,
TV_NORM_PAL_60,
TV_NORM_NTSC_M,
TV_NORM_NTSC_J,
NUM_TV_NORMS
};
struct ch7006_tv_norm_info {
fixed vrefresh;
int vdisplay;
int vtotal;
int hvirtual;
fixed subc_freq;
fixed black_level;
uint32_t dispmode;
int voffset;
};
struct ch7006_mode {
struct drm_display_mode mode;
int enc_hdisp;
int enc_vdisp;
fixed subc_coeff;
uint32_t dispmode;
uint32_t valid_scales;
uint32_t valid_norms;
};
struct ch7006_state {
uint8_t regs[0x26];
};
struct ch7006_priv {
struct ch7006_encoder_params *params;
struct ch7006_mode *mode;
struct ch7006_state state;
struct ch7006_state saved_state;
struct drm_property *scale_property;
int select_subconnector;
int subconnector;
int hmargin;
int vmargin;
enum ch7006_tv_norm norm;
int brightness;
int contrast;
int flicker;
int scale;
int last_dpms;
};
#define to_ch7006_priv(x) \
((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
extern int ch7006_debug;
extern char *ch7006_tv_norm;
extern int ch7006_scale;
extern char *ch7006_tv_norm_names[];
extern struct ch7006_tv_norm_info ch7006_tv_norms[];
extern struct ch7006_mode ch7006_modes[];
struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
struct drm_display_mode *drm_mode);
void ch7006_setup_levels(struct drm_encoder *encoder);
void ch7006_setup_subcarrier(struct drm_encoder *encoder);
void ch7006_setup_pll(struct drm_encoder *encoder);
void ch7006_setup_power_state(struct drm_encoder *encoder);
void ch7006_setup_properties(struct drm_encoder *encoder);
void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
void ch7006_state_load(struct i2c_client *client,
struct ch7006_state *state);
void ch7006_state_save(struct i2c_client *client,
struct ch7006_state *state);
/* Some helper macros */
#define ch7006_dbg(client, format, ...) do { \
if (ch7006_debug) \
dev_printk(KERN_DEBUG, &client->dev, \
"%s: " format, __func__, ## __VA_ARGS__); \
} while (0)
#define ch7006_info(client, format, ...) \
dev_info(&client->dev, format, __VA_ARGS__)
#define ch7006_err(client, format, ...) \
dev_err(&client->dev, format, __VA_ARGS__)
#define __mask(src, bitfield) \
(((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
#define mask(bitfield) __mask(bitfield)
#define __bitf(src, bitfield, x) \
(((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield))
#define bitf(bitfield, x) __bitf(bitfield, x)
#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
#define setbitf(state, reg, bitfield, x) \
state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \
| bitf(reg##_##bitfield, x)
#define __unbitf(src, bitfield, x) \
((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
#define unbitf(bitfield, x) __unbitf(bitfield, x)
static inline int interpolate(int y0, int y1, int y2, int x)
{
return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
}
static inline int32_t round_fixed(fixed x)
{
return (x + fixed1/2) >> 32;
}
#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
/* Fixed hardware specs */
#define CH7006_FREQ0 14318
#define CH7006_MAXN 650
#define CH7006_MAXM 315
/* Register definitions */
#define CH7006_DISPMODE 0x00
#define CH7006_DISPMODE_INPUT_RES 0, 7:5
#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
#define CH7006_FFILTER 0x01
#define CH7006_FFILTER_TEXT 0, 5:4
#define CH7006_FFILTER_LUMA 0, 3:2
#define CH7006_FFILTER_CHROMA 0, 1:0
#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
#define CH7006_BWIDTH 0x03
#define CH7006_BWIDTH_5L_FFILER (1 << 7)
#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
#define CH7006_BWIDTH_CHROMA 0, 5:4
#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
#define CH7006_INPUT_FORMAT 0x04
#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
#define CH7006_CLKMODE 0x06
#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
#define CH7006_CLKMODE_MASTER (1 << 6)
#define CH7006_CLKMODE_POS_EDGE (1 << 4)
#define CH7006_CLKMODE_XCM 0, 3:2
#define CH7006_CLKMODE_PCM 0, 1:0
#define CH7006_START_ACTIVE 0x07
#define CH7006_START_ACTIVE_0 0, 7:0
#define CH7006_POV 0x08
#define CH7006_POV_START_ACTIVE_8 8, 2:2
#define CH7006_POV_HPOS_8 8, 1:1
#define CH7006_POV_VPOS_8 8, 0:0
#define CH7006_BLACK_LEVEL 0x09
#define CH7006_BLACK_LEVEL_0 0, 7:0
#define CH7006_HPOS 0x0a
#define CH7006_HPOS_0 0, 7:0
#define CH7006_VPOS 0x0b
#define CH7006_VPOS_0 0, 7:0
#define CH7006_INPUT_SYNC 0x0d
#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
#define CH7006_POWER 0x0e
#define CH7006_POWER_SCART (1 << 4)
#define CH7006_POWER_RESET (1 << 3)
#define CH7006_POWER_LEVEL 0, 2:0
#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
#define CH7006_POWER_LEVEL_POWER_OFF 0x1
#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
#define CH7006_POWER_LEVEL_NORMAL 0x3
#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
#define CH7006_DETECT 0x10
#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
#define CH7006_DETECT_CVBS_TEST (1 << 1)
#define CH7006_DETECT_SENSE (1 << 0)
#define CH7006_CONTRAST 0x11
#define CH7006_CONTRAST_0 0, 2:0
#define CH7006_PLLOV 0x13
#define CH7006_PLLOV_N_8 8, 2:1
#define CH7006_PLLOV_M_8 8, 0:0
#define CH7006_PLLM 0x14
#define CH7006_PLLM_0 0, 7:0
#define CH7006_PLLN 0x15
#define CH7006_PLLN_0 0, 7:0
#define CH7006_BCLKOUT 0x17
#define CH7006_SUBC_INC0 0x18
#define CH7006_SUBC_INC0_28 28, 3:0
#define CH7006_SUBC_INC1 0x19
#define CH7006_SUBC_INC1_24 24, 3:0
#define CH7006_SUBC_INC2 0x1a
#define CH7006_SUBC_INC2_20 20, 3:0
#define CH7006_SUBC_INC3 0x1b
#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
#define CH7006_SUBC_INC3_16 16, 3:0
#define CH7006_SUBC_INC4 0x1c
#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
#define CH7006_SUBC_INC4_12 12, 3:0
#define CH7006_SUBC_INC5 0x1d
#define CH7006_SUBC_INC5_8 8, 3:0
#define CH7006_SUBC_INC6 0x1e
#define CH7006_SUBC_INC6_4 4, 3:0
#define CH7006_SUBC_INC7 0x1f
#define CH7006_SUBC_INC7_0 0, 3:0
#define CH7006_PLL_CONTROL 0x20
#define CH7006_PLL_CONTROL_CPI (1 << 5)
#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
#define CH7006_CALC_SUBC_INC0 0x21
#define CH7006_CALC_SUBC_INC0_24 24, 4:3
#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
#define CH7006_CALC_SUBC_INC1 0x22
#define CH7006_CALC_SUBC_INC1_16 16, 7:0
#define CH7006_CALC_SUBC_INC2 0x23
#define CH7006_CALC_SUBC_INC2_8 8, 7:0
#define CH7006_CALC_SUBC_INC3 0x24
#define CH7006_CALC_SUBC_INC3_0 0, 7:0
#define CH7006_VERSION_ID 0x25
#endif

View File

@ -0,0 +1,44 @@
config DRM_NOUVEAU
tristate "Nouveau (nVidia) cards"
depends on DRM
select FW_LOADER
select DRM_KMS_HELPER
select DRM_TTM
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
select FB
select FRAMEBUFFER_CONSOLE if !EMBEDDED
select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
help
Choose this option for open-source nVidia support.
config DRM_NOUVEAU_BACKLIGHT
bool "Support for backlight control"
depends on DRM_NOUVEAU
default y
help
Say Y here if you want to control the backlight of your display
(e.g. a laptop panel).
config DRM_NOUVEAU_DEBUG
bool "Build in Nouveau's debugfs support"
depends on DRM_NOUVEAU && DEBUG_FS
default y
help
Say Y here if you want Nouveau to output debugging information
via debugfs.
menu "I2C encoder or helper chips"
depends on DRM
config DRM_I2C_CH7006
tristate "Chrontel ch7006 TV encoder"
default m if DRM_NOUVEAU
help
Support for Chrontel ch7006 and similar TV encoders, found
on some nVidia video cards.
This driver is currently only useful if you're also using
the nouveau driver.
endmenu

View File

@ -0,0 +1,31 @@
#
# Makefile for the drm device driver. This driver provides support for the
# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
ccflags-y := -Iinclude/drm
nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nouveau_object.o nouveau_irq.o nouveau_notifier.o \
nouveau_sgdma.o nouveau_dma.o \
nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
nouveau_dp.o \
nv04_timer.o \
nv04_mc.o nv40_mc.o nv50_mc.o \
nv04_fb.o nv10_fb.o nv40_fb.o \
nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
nv04_graph.o nv10_graph.o nv20_graph.o \
nv40_graph.o nv50_graph.o \
nv04_instmem.o nv50_instmem.o \
nv50_crtc.o nv50_dac.o nv50_sor.o \
nv50_cursor.o nv50_display.o nv50_fbcon.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
nv17_gpio.o
nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o

View File

@ -0,0 +1,125 @@
#include <linux/pci.h>
#include <linux/acpi.h>
#include <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include "drmP.h"
#include "drm.h"
#include "drm_sarea.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nv50_display.h"
#define NOUVEAU_DSM_SUPPORTED 0x00
#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
#define NOUVEAU_DSM_ACTIVE 0x01
#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
#define NOUVEAU_DSM_LED 0x02
#define NOUVEAU_DSM_LED_STATE 0x00
#define NOUVEAU_DSM_LED_OFF 0x10
#define NOUVEAU_DSM_LED_STAMINA 0x11
#define NOUVEAU_DSM_LED_SPEED 0x12
#define NOUVEAU_DSM_POWER 0x03
#define NOUVEAU_DSM_POWER_STATE 0x00
#define NOUVEAU_DSM_POWER_SPEED 0x01
#define NOUVEAU_DSM_POWER_STAMINA 0x02
static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
{
static char muid[] = {
0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
};
struct pci_dev *pdev = dev->pdev;
struct acpi_handle *handle;
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
struct acpi_object_list input;
union acpi_object params[4];
union acpi_object *obj;
int err;
handle = DEVICE_ACPI_HANDLE(&pdev->dev);
if (!handle)
return -ENODEV;
input.count = 4;
input.pointer = params;
params[0].type = ACPI_TYPE_BUFFER;
params[0].buffer.length = sizeof(muid);
params[0].buffer.pointer = (char *)muid;
params[1].type = ACPI_TYPE_INTEGER;
params[1].integer.value = 0x00000102;
params[2].type = ACPI_TYPE_INTEGER;
params[2].integer.value = func;
params[3].type = ACPI_TYPE_INTEGER;
params[3].integer.value = arg;
err = acpi_evaluate_object(handle, "_DSM", &input, &output);
if (err) {
NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
return err;
}
obj = (union acpi_object *)output.pointer;
if (obj->type == ACPI_TYPE_INTEGER)
if (obj->integer.value == 0x80000002)
return -ENODEV;
if (obj->type == ACPI_TYPE_BUFFER) {
if (obj->buffer.length == 4 && result) {
*result = 0;
*result |= obj->buffer.pointer[0];
*result |= (obj->buffer.pointer[1] << 8);
*result |= (obj->buffer.pointer[2] << 16);
*result |= (obj->buffer.pointer[3] << 24);
}
}
kfree(output.pointer);
return 0;
}
int nouveau_hybrid_setup(struct drm_device *dev)
{
int result;
if (nouveau_dsm(dev, NOUVEAU_DSM_ACTIVE, NOUVEAU_DSM_ACTIVE_QUERY,
&result))
return -ENODEV;
NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
if (result & 0x1) { /* Stamina mode - disable the external GPU */
nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
NULL);
nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
NULL);
} else { /* Ensure that the external GPU is enabled */
nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
NULL);
}
return 0;
}
bool nouveau_dsm_probe(struct drm_device *dev)
{
int support = 0;
if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
return false;
if (!support)
return false;
return true;
}

View File

@ -0,0 +1,155 @@
/*
* Copyright (C) 2009 Red Hat <mjg@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE 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.
*
*/
/*
* Authors:
* Matthew Garrett <mjg@redhat.com>
*
* Register locations derived from NVClock by Roderick Colenbrander
*/
#include <linux/backlight.h>
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_reg.h"
static int nv40_get_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
>> 16;
return val;
}
static int nv40_set_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
int val = bd->props.brightness;
int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT);
nv_wr32(dev, NV40_PMC_BACKLIGHT,
(val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
return 0;
}
static struct backlight_ops nv40_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nv40_get_intensity,
.update_status = nv40_set_intensity,
};
static int nv50_get_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
}
static int nv50_set_intensity(struct backlight_device *bd)
{
struct drm_device *dev = bl_get_data(bd);
int val = bd->props.brightness;
nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
return 0;
}
static struct backlight_ops nv50_bl_ops = {
.options = BL_CORE_SUSPENDRESUME,
.get_brightness = nv50_get_intensity,
.update_status = nv50_set_intensity,
};
static int nouveau_nv40_backlight_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct backlight_device *bd;
if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
return 0;
bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
&nv40_bl_ops);
if (IS_ERR(bd))
return PTR_ERR(bd);
dev_priv->backlight = bd;
bd->props.max_brightness = 31;
bd->props.brightness = nv40_get_intensity(bd);
backlight_update_status(bd);
return 0;
}
static int nouveau_nv50_backlight_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct backlight_device *bd;
if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
return 0;
bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
&nv50_bl_ops);
if (IS_ERR(bd))
return PTR_ERR(bd);
dev_priv->backlight = bd;
bd->props.max_brightness = 1025;
bd->props.brightness = nv50_get_intensity(bd);
backlight_update_status(bd);
return 0;
}
int nouveau_backlight_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
switch (dev_priv->card_type) {
case NV_40:
return nouveau_nv40_backlight_init(dev);
case NV_50:
return nouveau_nv50_backlight_init(dev);
default:
break;
}
return 0;
}
void nouveau_backlight_exit(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->backlight) {
backlight_device_unregister(dev_priv->backlight);
dev_priv->backlight = NULL;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,289 @@
/*
* Copyright 2007-2008 Nouveau Project
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#ifndef __NOUVEAU_BIOS_H__
#define __NOUVEAU_BIOS_H__
#include "nvreg.h"
#include "nouveau_i2c.h"
#define DCB_MAX_NUM_ENTRIES 16
#define DCB_MAX_NUM_I2C_ENTRIES 16
#define DCB_MAX_NUM_GPIO_ENTRIES 32
#define DCB_MAX_NUM_CONNECTOR_ENTRIES 16
#define DCB_LOC_ON_CHIP 0
struct dcb_entry {
int index; /* may not be raw dcb index if merging has happened */
uint8_t type;
uint8_t i2c_index;
uint8_t heads;
uint8_t connector;
uint8_t bus;
uint8_t location;
uint8_t or;
bool duallink_possible;
union {
struct sor_conf {
int link;
} sorconf;
struct {
int maxfreq;
} crtconf;
struct {
struct sor_conf sor;
bool use_straps_for_mode;
bool use_power_scripts;
} lvdsconf;
struct {
bool has_component_output;
} tvconf;
struct {
struct sor_conf sor;
int link_nr;
int link_bw;
} dpconf;
struct {
struct sor_conf sor;
} tmdsconf;
};
bool i2c_upper_default;
};
struct dcb_i2c_entry {
uint8_t port_type;
uint8_t read, write;
struct nouveau_i2c_chan *chan;
};
struct parsed_dcb {
int entries;
struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
};
enum dcb_gpio_tag {
DCB_GPIO_TVDAC0 = 0xc,
DCB_GPIO_TVDAC1 = 0x2d,
};
struct dcb_gpio_entry {
enum dcb_gpio_tag tag;
int line;
bool invert;
};
struct parsed_dcb_gpio {
int entries;
struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
};
struct dcb_connector_table_entry {
uint32_t entry;
uint8_t type;
uint8_t index;
uint8_t gpio_tag;
};
struct dcb_connector_table {
int entries;
struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
};
struct bios_parsed_dcb {
uint8_t version;
struct parsed_dcb dcb;
uint8_t *i2c_table;
uint8_t i2c_default_indices;
uint16_t gpio_table_ptr;
struct parsed_dcb_gpio gpio;
uint16_t connector_table_ptr;
struct dcb_connector_table connector;
};
enum nouveau_encoder_type {
OUTPUT_ANALOG = 0,
OUTPUT_TV = 1,
OUTPUT_TMDS = 2,
OUTPUT_LVDS = 3,
OUTPUT_DP = 6,
OUTPUT_ANY = -1
};
enum nouveau_or {
OUTPUT_A = (1 << 0),
OUTPUT_B = (1 << 1),
OUTPUT_C = (1 << 2)
};
enum LVDS_script {
/* Order *does* matter here */
LVDS_INIT = 1,
LVDS_RESET,
LVDS_BACKLIGHT_ON,
LVDS_BACKLIGHT_OFF,
LVDS_PANEL_ON,
LVDS_PANEL_OFF
};
/* changing these requires matching changes to reg tables in nv_get_clock */
#define MAX_PLL_TYPES 4
enum pll_types {
NVPLL,
MPLL,
VPLL1,
VPLL2
};
struct pll_lims {
struct {
int minfreq;
int maxfreq;
int min_inputfreq;
int max_inputfreq;
uint8_t min_m;
uint8_t max_m;
uint8_t min_n;
uint8_t max_n;
} vco1, vco2;
uint8_t max_log2p;
/*
* for most pre nv50 cards setting a log2P of 7 (the common max_log2p
* value) is no different to 6 (at least for vplls) so allowing the MNP
* calc to use 7 causes the generated clock to be out by a factor of 2.
* however, max_log2p cannot be fixed-up during parsing as the
* unmodified max_log2p value is still needed for setting mplls, hence
* an additional max_usable_log2p member
*/
uint8_t max_usable_log2p;
uint8_t log2p_bias;
uint8_t min_p;
uint8_t max_p;
int refclk;
};
struct nouveau_bios_info {
struct parsed_dcb *dcb;
uint8_t chip_version;
uint32_t dactestval;
uint32_t tvdactestval;
uint8_t digital_min_front_porch;
bool fp_no_ddc;
};
struct nvbios {
struct drm_device *dev;
struct nouveau_bios_info pub;
uint8_t data[NV_PROM_SIZE];
unsigned int length;
bool execute;
uint8_t major_version;
uint8_t feature_byte;
bool is_mobile;
uint32_t fmaxvco, fminvco;
bool old_style_init;
uint16_t init_script_tbls_ptr;
uint16_t extra_init_script_tbl_ptr;
uint16_t macro_index_tbl_ptr;
uint16_t macro_tbl_ptr;
uint16_t condition_tbl_ptr;
uint16_t io_condition_tbl_ptr;
uint16_t io_flag_condition_tbl_ptr;
uint16_t init_function_tbl_ptr;
uint16_t pll_limit_tbl_ptr;
uint16_t ram_restrict_tbl_ptr;
uint16_t some_script_ptr; /* BIT I + 14 */
uint16_t init96_tbl_ptr; /* BIT I + 16 */
struct bios_parsed_dcb bdcb;
struct {
int crtchead;
/* these need remembering across suspend */
uint32_t saved_nv_pfb_cfg0;
} state;
struct {
struct dcb_entry *output;
uint16_t script_table_ptr;
uint16_t dp_table_ptr;
} display;
struct {
uint16_t fptablepointer; /* also used by tmds */
uint16_t fpxlatetableptr;
int xlatwidth;
uint16_t lvdsmanufacturerpointer;
uint16_t fpxlatemanufacturertableptr;
uint16_t mode_ptr;
uint16_t xlated_entry;
bool power_off_for_reset;
bool reset_after_pclk_change;
bool dual_link;
bool link_c_increment;
bool BITbit1;
bool if_is_24bit;
int duallink_transition_clk;
uint8_t strapless_is_24bit;
uint8_t *edid;
/* will need resetting after suspend */
int last_script_invoc;
bool lvds_init_run;
} fp;
struct {
uint16_t output0_script_ptr;
uint16_t output1_script_ptr;
} tmds;
struct {
uint16_t mem_init_tbl_ptr;
uint16_t sdr_seq_tbl_ptr;
uint16_t ddr_seq_tbl_ptr;
struct {
uint8_t crt, tv, panel;
} i2c_indices;
uint16_t lvds_single_a_script_ptr;
} legacy;
};
#endif

View File

@ -0,0 +1,671 @@
/*
* Copyright 2007 Dave Airlied
* 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
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
/*
* Authors: Dave Airlied <airlied@linux.ie>
* Ben Skeggs <darktama@iinet.net.au>
* Jeremy Kolb <jkolb@brandeis.edu>
*/
#include "drmP.h"
#include "nouveau_drm.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
static void
nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
struct nouveau_bo *nvbo = nouveau_bo(bo);
ttm_bo_kunmap(&nvbo->kmap);
if (unlikely(nvbo->gem))
DRM_ERROR("bo %p still attached to GEM object\n", bo);
spin_lock(&dev_priv->ttm.bo_list_lock);
list_del(&nvbo->head);
spin_unlock(&dev_priv->ttm.bo_list_lock);
kfree(nvbo);
}
int
nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
int size, int align, uint32_t flags, uint32_t tile_mode,
uint32_t tile_flags, bool no_vm, bool mappable,
struct nouveau_bo **pnvbo)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_bo *nvbo;
int ret, n = 0;
nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
if (!nvbo)
return -ENOMEM;
INIT_LIST_HEAD(&nvbo->head);
INIT_LIST_HEAD(&nvbo->entry);
nvbo->mappable = mappable;
nvbo->no_vm = no_vm;
nvbo->tile_mode = tile_mode;
nvbo->tile_flags = tile_flags;
/*
* Some of the tile_flags have a periodic structure of N*4096 bytes,
* align to to that as well as the page size. Overallocate memory to
* avoid corruption of other buffer objects.
*/
switch (tile_flags) {
case 0x1800:
case 0x2800:
case 0x4800:
case 0x7a00:
if (dev_priv->chipset >= 0xA0) {
/* This is based on high end cards with 448 bits
* memory bus, could be different elsewhere.*/
size += 6 * 28672;
/* 8 * 28672 is the actual alignment requirement,
* but we must also align to page size. */
align = 2 * 8 * 28672;
} else if (dev_priv->chipset >= 0x90) {
size += 3 * 16384;
align = 12 * 16834;
} else {
size += 3 * 8192;
/* 12 * 8192 is the actual alignment requirement,
* but we must also align to page size. */
align = 2 * 12 * 8192;
}
break;
default:
break;
}
align >>= PAGE_SHIFT;
size = (size + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);
if (dev_priv->card_type == NV_50) {
size = (size + 65535) & ~65535;
if (align < (65536 / PAGE_SIZE))
align = (65536 / PAGE_SIZE);
}
if (flags & TTM_PL_FLAG_VRAM)
nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
if (flags & TTM_PL_FLAG_TT)
nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
nvbo->placement.fpfn = 0;
nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
nvbo->placement.placement = nvbo->placements;
nvbo->placement.busy_placement = nvbo->placements;
nvbo->placement.num_placement = n;
nvbo->placement.num_busy_placement = n;
nvbo->channel = chan;
nouveau_bo_placement_set(nvbo, flags);
ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
ttm_bo_type_device, &nvbo->placement, align, 0,
false, NULL, size, nouveau_bo_del_ttm);
nvbo->channel = NULL;
if (ret) {
/* ttm will call nouveau_bo_del_ttm if it fails.. */
return ret;
}
spin_lock(&dev_priv->ttm.bo_list_lock);
list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
spin_unlock(&dev_priv->ttm.bo_list_lock);
*pnvbo = nvbo;
return 0;
}
void
nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype)
{
int n = 0;
if (memtype & TTM_PL_FLAG_VRAM)
nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
if (memtype & TTM_PL_FLAG_TT)
nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
if (memtype & TTM_PL_FLAG_SYSTEM)
nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
nvbo->placement.placement = nvbo->placements;
nvbo->placement.busy_placement = nvbo->placements;
nvbo->placement.num_placement = n;
nvbo->placement.num_busy_placement = n;
}
int
nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
struct ttm_buffer_object *bo = &nvbo->bo;
int ret, i;
if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {
NV_ERROR(nouveau_bdev(bo->bdev)->dev,
"bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo,
1 << bo->mem.mem_type, memtype);
return -EINVAL;
}
if (nvbo->pin_refcnt++)
return 0;
ret = ttm_bo_reserve(bo, false, false, false, 0);
if (ret)
goto out;
nouveau_bo_placement_set(nvbo, memtype);
for (i = 0; i < nvbo->placement.num_placement; i++)
nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
if (ret == 0) {
switch (bo->mem.mem_type) {
case TTM_PL_VRAM:
dev_priv->fb_aper_free -= bo->mem.size;
break;
case TTM_PL_TT:
dev_priv->gart_info.aper_free -= bo->mem.size;
break;
default:
break;
}
}
ttm_bo_unreserve(bo);
out:
if (unlikely(ret))
nvbo->pin_refcnt--;
return ret;
}
int
nouveau_bo_unpin(struct nouveau_bo *nvbo)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
struct ttm_buffer_object *bo = &nvbo->bo;
int ret, i;
if (--nvbo->pin_refcnt)
return 0;
ret = ttm_bo_reserve(bo, false, false, false, 0);
if (ret)
return ret;
for (i = 0; i < nvbo->placement.num_placement; i++)
nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
if (ret == 0) {
switch (bo->mem.mem_type) {
case TTM_PL_VRAM:
dev_priv->fb_aper_free += bo->mem.size;
break;
case TTM_PL_TT:
dev_priv->gart_info.aper_free += bo->mem.size;
break;
default:
break;
}
}
ttm_bo_unreserve(bo);
return ret;
}
int
nouveau_bo_map(struct nouveau_bo *nvbo)
{
int ret;
ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
if (ret)
return ret;
ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, &nvbo->kmap);
ttm_bo_unreserve(&nvbo->bo);
return ret;
}
void
nouveau_bo_unmap(struct nouveau_bo *nvbo)
{
ttm_bo_kunmap(&nvbo->kmap);
}
u16
nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index)
{
bool is_iomem;
u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
mem = &mem[index];
if (is_iomem)
return ioread16_native((void __force __iomem *)mem);
else
return *mem;
}
void
nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val)
{
bool is_iomem;
u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
mem = &mem[index];
if (is_iomem)
iowrite16_native(val, (void __force __iomem *)mem);
else
*mem = val;
}
u32
nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index)
{
bool is_iomem;
u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
mem = &mem[index];
if (is_iomem)
return ioread32_native((void __force __iomem *)mem);
else
return *mem;
}
void
nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val)
{
bool is_iomem;
u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
mem = &mem[index];
if (is_iomem)
iowrite32_native(val, (void __force __iomem *)mem);
else
*mem = val;
}
static struct ttm_backend *
nouveau_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
struct drm_device *dev = dev_priv->dev;
switch (dev_priv->gart_info.type) {
case NOUVEAU_GART_AGP:
return ttm_agp_backend_init(bdev, dev->agp->bridge);
case NOUVEAU_GART_SGDMA:
return nouveau_sgdma_init_ttm(dev);
default:
NV_ERROR(dev, "Unknown GART type %d\n",
dev_priv->gart_info.type);
break;
}
return NULL;
}
static int
nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
{
/* We'll do this from user space. */
return 0;
}
static int
nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
struct ttm_mem_type_manager *man)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
struct drm_device *dev = dev_priv->dev;
switch (type) {
case TTM_PL_SYSTEM:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
case TTM_PL_VRAM:
man->flags = TTM_MEMTYPE_FLAG_FIXED |
TTM_MEMTYPE_FLAG_MAPPABLE |
TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
man->available_caching = TTM_PL_FLAG_UNCACHED |
TTM_PL_FLAG_WC;
man->default_caching = TTM_PL_FLAG_WC;
man->io_addr = NULL;
man->io_offset = drm_get_resource_start(dev, 1);
man->io_size = drm_get_resource_len(dev, 1);
if (man->io_size > nouveau_mem_fb_amount(dev))
man->io_size = nouveau_mem_fb_amount(dev);
man->gpu_offset = dev_priv->vm_vram_base;
break;
case TTM_PL_TT:
switch (dev_priv->gart_info.type) {
case NOUVEAU_GART_AGP:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
man->available_caching = TTM_PL_FLAG_UNCACHED;
man->default_caching = TTM_PL_FLAG_UNCACHED;
break;
case NOUVEAU_GART_SGDMA:
man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
TTM_MEMTYPE_FLAG_CMA;
man->available_caching = TTM_PL_MASK_CACHING;
man->default_caching = TTM_PL_FLAG_CACHED;
break;
default:
NV_ERROR(dev, "Unknown GART type: %d\n",
dev_priv->gart_info.type);
return -EINVAL;
}
man->io_offset = dev_priv->gart_info.aper_base;
man->io_size = dev_priv->gart_info.aper_size;
man->io_addr = NULL;
man->gpu_offset = dev_priv->vm_gart_base;
break;
default:
NV_ERROR(dev, "Unsupported memory type %u\n", (unsigned)type);
return -EINVAL;
}
return 0;
}
static void
nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
{
struct nouveau_bo *nvbo = nouveau_bo(bo);
switch (bo->mem.mem_type) {
default:
nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM);
break;
}
}
/* GPU-assisted copy using NV_MEMORY_TO_MEMORY_FORMAT, can access
* TTM_PL_{VRAM,TT} directly.
*/
static int
nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
struct nouveau_bo *nvbo, bool evict, bool no_wait,
struct ttm_mem_reg *new_mem)
{
struct nouveau_fence *fence = NULL;
int ret;
ret = nouveau_fence_new(chan, &fence, true);
if (ret)
return ret;
ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL,
evict, no_wait, new_mem);
nouveau_fence_unref((void *)&fence);
return ret;
}
static inline uint32_t
nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
struct ttm_mem_reg *mem)
{
if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) {
if (mem->mem_type == TTM_PL_TT)
return NvDmaGART;
return NvDmaVRAM;
}
if (mem->mem_type == TTM_PL_TT)
return chan->gart_handle;
return chan->vram_handle;
}
static int
nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, int no_wait,
struct ttm_mem_reg *old_mem, struct ttm_mem_reg *new_mem)
{
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
struct nouveau_channel *chan;
uint64_t src_offset, dst_offset;
uint32_t page_count;
int ret;
chan = nvbo->channel;
if (!chan || nvbo->tile_flags || nvbo->no_vm) {
chan = dev_priv->channel;
if (!chan)
return -EINVAL;
}
src_offset = old_mem->mm_node->start << PAGE_SHIFT;
dst_offset = new_mem->mm_node->start << PAGE_SHIFT;
if (chan != dev_priv->channel) {
if (old_mem->mem_type == TTM_PL_TT)
src_offset += dev_priv->vm_gart_base;
else
src_offset += dev_priv->vm_vram_base;
if (new_mem->mem_type == TTM_PL_TT)
dst_offset += dev_priv->vm_gart_base;
else
dst_offset += dev_priv->vm_vram_base;
}
ret = RING_SPACE(chan, 3);
if (ret)
return ret;
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2);
OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, old_mem));
OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, new_mem));
if (dev_priv->card_type >= NV_50) {
ret = RING_SPACE(chan, 4);
if (ret)
return ret;
BEGIN_RING(chan, NvSubM2MF, 0x0200, 1);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSubM2MF, 0x021c, 1);
OUT_RING(chan, 1);
}
page_count = new_mem->num_pages;
while (page_count) {
int line_count = (page_count > 2047) ? 2047 : page_count;
if (dev_priv->card_type >= NV_50) {
ret = RING_SPACE(chan, 3);
if (ret)
return ret;
BEGIN_RING(chan, NvSubM2MF, 0x0238, 2);
OUT_RING(chan, upper_32_bits(src_offset));
OUT_RING(chan, upper_32_bits(dst_offset));
}
ret = RING_SPACE(chan, 11);
if (ret)
return ret;
BEGIN_RING(chan, NvSubM2MF,
NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
OUT_RING(chan, lower_32_bits(src_offset));
OUT_RING(chan, lower_32_bits(dst_offset));
OUT_RING(chan, PAGE_SIZE); /* src_pitch */
OUT_RING(chan, PAGE_SIZE); /* dst_pitch */
OUT_RING(chan, PAGE_SIZE); /* line_length */
OUT_RING(chan, line_count);
OUT_RING(chan, (1<<8)|(1<<0));
OUT_RING(chan, 0);
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1);
OUT_RING(chan, 0);
page_count -= line_count;
src_offset += (PAGE_SIZE * line_count);
dst_offset += (PAGE_SIZE * line_count);
}
return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait, new_mem);
}
static int
nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
bool no_wait, struct ttm_mem_reg *new_mem)
{
u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
struct ttm_placement placement;
struct ttm_mem_reg tmp_mem;
int ret;
placement.fpfn = placement.lpfn = 0;
placement.num_placement = placement.num_busy_placement = 1;
placement.placement = &placement_memtype;
tmp_mem = *new_mem;
tmp_mem.mm_node = NULL;
ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
if (ret)
return ret;
ret = ttm_tt_bind(bo->ttm, &tmp_mem);
if (ret)
goto out;
ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, &tmp_mem);
if (ret)
goto out;
ret = ttm_bo_move_ttm(bo, evict, no_wait, new_mem);
out:
if (tmp_mem.mm_node) {
spin_lock(&bo->bdev->glob->lru_lock);
drm_mm_put_block(tmp_mem.mm_node);
spin_unlock(&bo->bdev->glob->lru_lock);
}
return ret;
}
static int
nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
bool no_wait, struct ttm_mem_reg *new_mem)
{
u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
struct ttm_placement placement;
struct ttm_mem_reg tmp_mem;
int ret;
placement.fpfn = placement.lpfn = 0;
placement.num_placement = placement.num_busy_placement = 1;
placement.placement = &placement_memtype;
tmp_mem = *new_mem;
tmp_mem.mm_node = NULL;
ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
if (ret)
return ret;
ret = ttm_bo_move_ttm(bo, evict, no_wait, &tmp_mem);
if (ret)
goto out;
ret = nouveau_bo_move_m2mf(bo, true, no_wait, &bo->mem, new_mem);
if (ret)
goto out;
out:
if (tmp_mem.mm_node) {
spin_lock(&bo->bdev->glob->lru_lock);
drm_mm_put_block(tmp_mem.mm_node);
spin_unlock(&bo->bdev->glob->lru_lock);
}
return ret;
}
static int
nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
bool no_wait, struct ttm_mem_reg *new_mem)
{
struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct drm_device *dev = dev_priv->dev;
struct ttm_mem_reg *old_mem = &bo->mem;
int ret;
if (dev_priv->card_type == NV_50 && new_mem->mem_type == TTM_PL_VRAM &&
!nvbo->no_vm) {
uint64_t offset = new_mem->mm_node->start << PAGE_SHIFT;
ret = nv50_mem_vm_bind_linear(dev,
offset + dev_priv->vm_vram_base,
new_mem->size, nvbo->tile_flags,
offset);
if (ret)
return ret;
}
if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE)
return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
BUG_ON(bo->mem.mm_node != NULL);
bo->mem = *new_mem;
new_mem->mm_node = NULL;
return 0;
}
if (new_mem->mem_type == TTM_PL_SYSTEM) {
if (old_mem->mem_type == TTM_PL_SYSTEM)
return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
if (nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem))
return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
} else if (old_mem->mem_type == TTM_PL_SYSTEM) {
if (nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem))
return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
} else {
if (nouveau_bo_move_m2mf(bo, evict, no_wait, old_mem, new_mem))
return ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
}
return 0;
}
static int
nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
{
return 0;
}
struct ttm_bo_driver nouveau_bo_driver = {
.create_ttm_backend_entry = nouveau_bo_create_ttm_backend_entry,
.invalidate_caches = nouveau_bo_invalidate_caches,
.init_mem_type = nouveau_bo_init_mem_type,
.evict_flags = nouveau_bo_evict_flags,
.move = nouveau_bo_move,
.verify_access = nouveau_bo_verify_access,
.sync_obj_signaled = nouveau_fence_signalled,
.sync_obj_wait = nouveau_fence_wait,
.sync_obj_flush = nouveau_fence_flush,
.sync_obj_unref = nouveau_fence_unref,
.sync_obj_ref = nouveau_fence_ref,
};

View File

@ -0,0 +1,478 @@
/*
* Copyright 1993-2003 NVIDIA, Corporation
* Copyright 2007-2009 Stuart Bennett
*
* 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 AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
* OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
/****************************************************************************\
* *
* The video arbitration routines calculate some "magic" numbers. Fixes *
* the snow seen when accessing the framebuffer without it. *
* It just works (I hope). *
* *
\****************************************************************************/
struct nv_fifo_info {
int lwm;
int burst;
};
struct nv_sim_state {
int pclk_khz;
int mclk_khz;
int nvclk_khz;
int bpp;
int mem_page_miss;
int mem_latency;
int memory_type;
int memory_width;
int two_heads;
};
static void
nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
{
int pagemiss, cas, width, bpp;
int nvclks, mclks, pclks, crtpagemiss;
int found, mclk_extra, mclk_loop, cbs, m1, p1;
int mclk_freq, pclk_freq, nvclk_freq;
int us_m, us_n, us_p, crtc_drain_rate;
int cpm_us, us_crt, clwm;
pclk_freq = arb->pclk_khz;
mclk_freq = arb->mclk_khz;
nvclk_freq = arb->nvclk_khz;
pagemiss = arb->mem_page_miss;
cas = arb->mem_latency;
width = arb->memory_width >> 6;
bpp = arb->bpp;
cbs = 128;
pclks = 2;
nvclks = 10;
mclks = 13 + cas;
mclk_extra = 3;
found = 0;
while (!found) {
found = 1;
mclk_loop = mclks + mclk_extra;
us_m = mclk_loop * 1000 * 1000 / mclk_freq;
us_n = nvclks * 1000 * 1000 / nvclk_freq;
us_p = nvclks * 1000 * 1000 / pclk_freq;
crtc_drain_rate = pclk_freq * bpp / 8;
crtpagemiss = 2;
crtpagemiss += 1;
cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
us_crt = cpm_us + us_m + us_n + us_p;
clwm = us_crt * crtc_drain_rate / (1000 * 1000);
clwm++;
m1 = clwm + cbs - 512;
p1 = m1 * pclk_freq / mclk_freq;
p1 = p1 * bpp / 8;
if ((p1 < m1 && m1 > 0) || clwm > 519) {
found = !mclk_extra;
mclk_extra--;
}
if (clwm < 384)
clwm = 384;
fifo->lwm = clwm;
fifo->burst = cbs;
}
}
static void
nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
{
int fill_rate, drain_rate;
int pclks, nvclks, mclks, xclks;
int pclk_freq, nvclk_freq, mclk_freq;
int fill_lat, extra_lat;
int max_burst_o, max_burst_l;
int fifo_len, min_lwm, max_lwm;
const int burst_lat = 80; /* Maximum allowable latency due
* to the CRTC FIFO burst. (ns) */
pclk_freq = arb->pclk_khz;
nvclk_freq = arb->nvclk_khz;
mclk_freq = arb->mclk_khz;
fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */
drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */
fifo_len = arb->two_heads ? 1536 : 1024; /* B */
/* Fixed FIFO refill latency. */
pclks = 4; /* lwm detect. */
nvclks = 3 /* lwm -> sync. */
+ 2 /* fbi bus cycles (1 req + 1 busy) */
+ 1 /* 2 edge sync. may be very close to edge so
* just put one. */
+ 1 /* fbi_d_rdv_n */
+ 1 /* Fbi_d_rdata */
+ 1; /* crtfifo load */
mclks = 1 /* 2 edge sync. may be very close to edge so
* just put one. */
+ 1 /* arb_hp_req */
+ 5 /* tiling pipeline */
+ 2 /* latency fifo */
+ 2 /* memory request to fbio block */
+ 7; /* data returned from fbio block */
/* Need to accumulate 256 bits for read */
mclks += (arb->memory_type == 0 ? 2 : 1)
* arb->memory_width / 32;
fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */
+ nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */
+ pclks * 1000 * 1000 / pclk_freq; /* pclk latency */
/* Conditional FIFO refill latency. */
xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to
* the overlay. */
+ 2 * arb->mem_page_miss /* Extra pagemiss latency. */
+ (arb->bpp == 32 ? 8 : 4); /* Margin of error. */
extra_lat = xclks * 1000 * 1000 / mclk_freq;
if (arb->two_heads)
/* Account for another CRTC. */
extra_lat += fill_lat + extra_lat + burst_lat;
/* FIFO burst */
/* Max burst not leading to overflows. */
max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000))
* (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000);
fifo->burst = min(max_burst_o, 1024);
/* Max burst value with an acceptable latency. */
max_burst_l = burst_lat * fill_rate / (1000 * 1000);
fifo->burst = min(max_burst_l, fifo->burst);
fifo->burst = rounddown_pow_of_two(fifo->burst);
/* FIFO low watermark */
min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1;
max_lwm = fifo_len - fifo->burst
+ fill_lat * drain_rate / (1000 * 1000)
+ fifo->burst * drain_rate / fill_rate;
fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */
}
static void
nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
int *burst, int *lwm)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv_fifo_info fifo_data;
struct nv_sim_state sim_data;
int MClk = nouveau_hw_get_clock(dev, MPLL);
int NVClk = nouveau_hw_get_clock(dev, NVPLL);
uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1);
sim_data.pclk_khz = VClk;
sim_data.mclk_khz = MClk;
sim_data.nvclk_khz = NVClk;
sim_data.bpp = bpp;
sim_data.two_heads = nv_two_heads(dev);
if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
(dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
uint32_t type;
pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type);
sim_data.memory_type = (type >> 12) & 1;
sim_data.memory_width = 64;
sim_data.mem_latency = 3;
sim_data.mem_page_miss = 10;
} else {
sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1;
sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
sim_data.mem_latency = cfg1 & 0xf;
sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
}
if (dev_priv->card_type == NV_04)
nv04_calc_arb(&fifo_data, &sim_data);
else
nv10_calc_arb(&fifo_data, &sim_data);
*burst = ilog2(fifo_data.burst >> 4);
*lwm = fifo_data.lwm >> 3;
}
static void
nv30_update_arb(int *burst, int *lwm)
{
unsigned int fifo_size, burst_size, graphics_lwm;
fifo_size = 2048;
burst_size = 512;
graphics_lwm = fifo_size - burst_size;
*burst = ilog2(burst_size >> 5);
*lwm = graphics_lwm >> 3;
}
void
nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->card_type < NV_30)
nv04_update_arb(dev, vclk, bpp, burst, lwm);
else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
(dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
*burst = 128;
*lwm = 0x0480;
} else
nv30_update_arb(burst, lwm);
}
static int
getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
struct nouveau_pll_vals *bestpv)
{
/* Find M, N and P for a single stage PLL
*
* Note that some bioses (NV3x) have lookup tables of precomputed MNP
* values, but we're too lazy to use those atm
*
* "clk" parameter in kHz
* returns calculated clock
*/
struct drm_nouveau_private *dev_priv = dev->dev_private;
int cv = dev_priv->vbios->chip_version;
int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
int minU = pll_lim->vco1.min_inputfreq;
int maxU = pll_lim->vco1.max_inputfreq;
int minP = pll_lim->max_p ? pll_lim->min_p : 0;
int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p;
int crystal = pll_lim->refclk;
int M, N, thisP, P;
int clkP, calcclk;
int delta, bestdelta = INT_MAX;
int bestclk = 0;
/* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
/* possibly correlated with introduction of 27MHz crystal */
if (dev_priv->card_type < NV_50) {
if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
if (clk > 250000)
maxM = 6;
if (clk > 340000)
maxM = 2;
} else if (cv < 0x40) {
if (clk > 150000)
maxM = 6;
if (clk > 200000)
maxM = 4;
if (clk > 340000)
maxM = 2;
}
}
P = pll_lim->max_p ? maxP : (1 << maxP);
if ((clk * P) < minvco) {
minvco = clk * maxP;
maxvco = minvco * 2;
}
if (clk + clk/200 > maxvco) /* +0.5% */
maxvco = clk + clk/200;
/* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
for (thisP = minP; thisP <= maxP; thisP++) {
P = pll_lim->max_p ? thisP : (1 << thisP);
clkP = clk * P;
if (clkP < minvco)
continue;
if (clkP > maxvco)
return bestclk;
for (M = minM; M <= maxM; M++) {
if (crystal/M < minU)
return bestclk;
if (crystal/M > maxU)
continue;
/* add crystal/2 to round better */
N = (clkP * M + crystal/2) / crystal;
if (N < minN)
continue;
if (N > maxN)
break;
/* more rounding additions */
calcclk = ((N * crystal + P/2) / P + M/2) / M;
delta = abs(calcclk - clk);
/* we do an exhaustive search rather than terminating
* on an optimality condition...
*/
if (delta < bestdelta) {
bestdelta = delta;
bestclk = calcclk;
bestpv->N1 = N;
bestpv->M1 = M;
bestpv->log2P = thisP;
if (delta == 0) /* except this one */
return bestclk;
}
}
}
return bestclk;
}
static int
getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
struct nouveau_pll_vals *bestpv)
{
/* Find M, N and P for a two stage PLL
*
* Note that some bioses (NV30+) have lookup tables of precomputed MNP
* values, but we're too lazy to use those atm
*
* "clk" parameter in kHz
* returns calculated clock
*/
struct drm_nouveau_private *dev_priv = dev->dev_private;
int chip_version = dev_priv->vbios->chip_version;
int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
int maxlog2P = pll_lim->max_usable_log2p;
int crystal = pll_lim->refclk;
bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
int M1, N1, M2, N2, log2P;
int clkP, calcclk1, calcclk2, calcclkout;
int delta, bestdelta = INT_MAX;
int bestclk = 0;
int vco2 = (maxvco2 - maxvco2/200) / 2;
for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
;
clkP = clk << log2P;
if (maxvco2 < clk + clk/200) /* +0.5% */
maxvco2 = clk + clk/200;
for (M1 = minM1; M1 <= maxM1; M1++) {
if (crystal/M1 < minU1)
return bestclk;
if (crystal/M1 > maxU1)
continue;
for (N1 = minN1; N1 <= maxN1; N1++) {
calcclk1 = crystal * N1 / M1;
if (calcclk1 < minvco1)
continue;
if (calcclk1 > maxvco1)
break;
for (M2 = minM2; M2 <= maxM2; M2++) {
if (calcclk1/M2 < minU2)
break;
if (calcclk1/M2 > maxU2)
continue;
/* add calcclk1/2 to round better */
N2 = (clkP * M2 + calcclk1/2) / calcclk1;
if (N2 < minN2)
continue;
if (N2 > maxN2)
break;
if (!fixedgain2) {
if (chip_version < 0x60)
if (N2/M2 < 4 || N2/M2 > 10)
continue;
calcclk2 = calcclk1 * N2 / M2;
if (calcclk2 < minvco2)
break;
if (calcclk2 > maxvco2)
continue;
} else
calcclk2 = calcclk1;
calcclkout = calcclk2 >> log2P;
delta = abs(calcclkout - clk);
/* we do an exhaustive search rather than terminating
* on an optimality condition...
*/
if (delta < bestdelta) {
bestdelta = delta;
bestclk = calcclkout;
bestpv->N1 = N1;
bestpv->M1 = M1;
bestpv->N2 = N2;
bestpv->M2 = M2;
bestpv->log2P = log2P;
if (delta == 0) /* except this one */
return bestclk;
}
}
}
}
return bestclk;
}
int
nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
struct nouveau_pll_vals *pv)
{
int outclk;
if (!pll_lim->vco2.maxfreq)
outclk = getMNP_single(dev, pll_lim, clk, pv);
else
outclk = getMNP_double(dev, pll_lim, clk, pv);
if (!outclk)
NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
return outclk;
}

View File

@ -0,0 +1,468 @@
/*
* Copyright 2005-2006 Stephane Marchesin
* 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
* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_dma.h"
static int
nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_bo *pb = chan->pushbuf_bo;
struct nouveau_gpuobj *pushbuf = NULL;
uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT;
int ret;
if (pb->bo.mem.mem_type == TTM_PL_TT) {
ret = nouveau_gpuobj_gart_dma_new(chan, 0,
dev_priv->gart_info.aper_size,
NV_DMA_ACCESS_RO, &pushbuf,
NULL);
chan->pushbuf_base = start;
} else
if (dev_priv->card_type != NV_04) {
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
dev_priv->fb_available_size,
NV_DMA_ACCESS_RO,
NV_DMA_TARGET_VIDMEM, &pushbuf);
chan->pushbuf_base = start;
} else {
/* NV04 cmdbuf hack, from original ddx.. not sure of it's
* exact reason for existing :) PCI access to cmdbuf in
* VRAM.
*/
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
drm_get_resource_start(dev, 1),
dev_priv->fb_available_size,
NV_DMA_ACCESS_RO,
NV_DMA_TARGET_PCI, &pushbuf);
chan->pushbuf_base = start;
}
ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
if (ret) {
NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret);
if (pushbuf != dev_priv->gart_info.sg_ctxdma)
nouveau_gpuobj_del(dev, &pushbuf);
return ret;
}
return 0;
}
static struct nouveau_bo *
nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
{
struct nouveau_bo *pushbuf = NULL;
int location, ret;
if (nouveau_vram_pushbuf)
location = TTM_PL_FLAG_VRAM;
else
location = TTM_PL_FLAG_TT;
ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false,
true, &pushbuf);
if (ret) {
NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
return NULL;
}
ret = nouveau_bo_pin(pushbuf, location);
if (ret) {
NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
nouveau_bo_ref(NULL, &pushbuf);
return NULL;
}
return pushbuf;
}
/* allocates and initializes a fifo for user space consumption */
int
nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
struct drm_file *file_priv,
uint32_t vram_handle, uint32_t tt_handle)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan;
int channel, user;
int ret;
/*
* Alright, here is the full story
* Nvidia cards have multiple hw fifo contexts (praise them for that,
* no complicated crash-prone context switches)
* We allocate a new context for each app and let it write to it
* directly (woo, full userspace command submission !)
* When there are no more contexts, you lost
*/
for (channel = 0; channel < pfifo->channels; channel++) {
if (dev_priv->fifos[channel] == NULL)
break;
}
/* no more fifos. you lost. */
if (channel == pfifo->channels)
return -EINVAL;
dev_priv->fifos[channel] = kzalloc(sizeof(struct nouveau_channel),
GFP_KERNEL);
if (!dev_priv->fifos[channel])
return -ENOMEM;
dev_priv->fifo_alloc_count++;
chan = dev_priv->fifos[channel];
INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
INIT_LIST_HEAD(&chan->fence.pending);
chan->dev = dev;
chan->id = channel;
chan->file_priv = file_priv;
chan->vram_handle = vram_handle;
chan->gart_handle = tt_handle;
NV_INFO(dev, "Allocating FIFO number %d\n", channel);
/* Allocate DMA push buffer */
chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
if (!chan->pushbuf_bo) {
ret = -ENOMEM;
NV_ERROR(dev, "pushbuf %d\n", ret);
nouveau_channel_free(chan);
return ret;
}
/* Locate channel's user control regs */
if (dev_priv->card_type < NV_40)
user = NV03_USER(channel);
else
if (dev_priv->card_type < NV_50)
user = NV40_USER(channel);
else
user = NV50_USER(channel);
chan->user = ioremap(pci_resource_start(dev->pdev, 0) + user,
PAGE_SIZE);
if (!chan->user) {
NV_ERROR(dev, "ioremap of regs failed.\n");
nouveau_channel_free(chan);
return -ENOMEM;
}
chan->user_put = 0x40;
chan->user_get = 0x44;
/* Allocate space for per-channel fixed notifier memory */
ret = nouveau_notifier_init_channel(chan);
if (ret) {
NV_ERROR(dev, "ntfy %d\n", ret);
nouveau_channel_free(chan);
return ret;
}
/* Setup channel's default objects */
ret = nouveau_gpuobj_channel_init(chan, vram_handle, tt_handle);
if (ret) {
NV_ERROR(dev, "gpuobj %d\n", ret);
nouveau_channel_free(chan);
return ret;
}
/* Create a dma object for the push buffer */
ret = nouveau_channel_pushbuf_ctxdma_init(chan);
if (ret) {
NV_ERROR(dev, "pbctxdma %d\n", ret);
nouveau_channel_free(chan);
return ret;
}
/* disable the fifo caches */
pfifo->reassign(dev, false);
/* Create a graphics context for new channel */
ret = pgraph->create_context(chan);
if (ret) {
nouveau_channel_free(chan);
return ret;
}
/* Construct inital RAMFC for new channel */
ret = pfifo->create_context(chan);
if (ret) {
nouveau_channel_free(chan);
return ret;
}
pfifo->reassign(dev, true);
ret = nouveau_dma_init(chan);
if (!ret)
ret = nouveau_fence_init(chan);
if (ret) {
nouveau_channel_free(chan);
return ret;
}
nouveau_debugfs_channel_init(chan);
NV_INFO(dev, "%s: initialised FIFO %d\n", __func__, channel);
*chan_ret = chan;
return 0;
}
int
nouveau_channel_idle(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
uint32_t caches;
int idle;
if (!chan) {
NV_ERROR(dev, "no channel...\n");
return 1;
}
caches = nv_rd32(dev, NV03_PFIFO_CACHES);
nv_wr32(dev, NV03_PFIFO_CACHES, caches & ~1);
if (engine->fifo.channel_id(dev) != chan->id) {
struct nouveau_gpuobj *ramfc =
chan->ramfc ? chan->ramfc->gpuobj : NULL;
if (!ramfc) {
NV_ERROR(dev, "No RAMFC for channel %d\n", chan->id);
return 1;
}
engine->instmem.prepare_access(dev, false);
if (nv_ro32(dev, ramfc, 0) != nv_ro32(dev, ramfc, 1))
idle = 0;
else
idle = 1;
engine->instmem.finish_access(dev);
} else {
idle = (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET) ==
nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
}
nv_wr32(dev, NV03_PFIFO_CACHES, caches);
return idle;
}
/* stops a fifo */
void
nouveau_channel_free(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
unsigned long flags;
int ret;
NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id);
nouveau_debugfs_channel_fini(chan);
/* Give outstanding push buffers a chance to complete */
spin_lock_irqsave(&chan->fence.lock, flags);
nouveau_fence_update(chan);
spin_unlock_irqrestore(&chan->fence.lock, flags);
if (chan->fence.sequence != chan->fence.sequence_ack) {
struct nouveau_fence *fence = NULL;
ret = nouveau_fence_new(chan, &fence, true);
if (ret == 0) {
ret = nouveau_fence_wait(fence, NULL, false, false);
nouveau_fence_unref((void *)&fence);
}
if (ret)
NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
}
/* Ensure all outstanding fences are signaled. They should be if the
* above attempts at idling were OK, but if we failed this'll tell TTM
* we're done with the buffers.
*/
nouveau_fence_fini(chan);
/* Ensure the channel is no longer active on the GPU */
pfifo->reassign(dev, false);
if (pgraph->channel(dev) == chan) {
pgraph->fifo_access(dev, false);
pgraph->unload_context(dev);
pgraph->fifo_access(dev, true);
}
pgraph->destroy_context(chan);
if (pfifo->channel_id(dev) == chan->id) {
pfifo->disable(dev);
pfifo->unload_context(dev);
pfifo->enable(dev);
}
pfifo->destroy_context(chan);
pfifo->reassign(dev, true);
/* Release the channel's resources */
nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
if (chan->pushbuf_bo) {
nouveau_bo_unpin(chan->pushbuf_bo);
nouveau_bo_ref(NULL, &chan->pushbuf_bo);
}
nouveau_gpuobj_channel_takedown(chan);
nouveau_notifier_takedown_channel(chan);
if (chan->user)
iounmap(chan->user);
dev_priv->fifos[chan->id] = NULL;
dev_priv->fifo_alloc_count--;
kfree(chan);
}
/* cleans up all the fifos from file_priv */
void
nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
int i;
NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
for (i = 0; i < engine->fifo.channels; i++) {
struct nouveau_channel *chan = dev_priv->fifos[i];
if (chan && chan->file_priv == file_priv)
nouveau_channel_free(chan);
}
}
int
nouveau_channel_owner(struct drm_device *dev, struct drm_file *file_priv,
int channel)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
if (channel >= engine->fifo.channels)
return 0;
if (dev_priv->fifos[channel] == NULL)
return 0;
return (dev_priv->fifos[channel]->file_priv == file_priv);
}
/***********************************
* ioctls wrapping the functions
***********************************/
static int
nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_nouveau_channel_alloc *init = data;
struct nouveau_channel *chan;
int ret;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
if (dev_priv->engine.graph.accel_blocked)
return -ENODEV;
if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
return -EINVAL;
ret = nouveau_channel_alloc(dev, &chan, file_priv,
init->fb_ctxdma_handle,
init->tt_ctxdma_handle);
if (ret)
return ret;
init->channel = chan->id;
init->subchan[0].handle = NvM2MF;
if (dev_priv->card_type < NV_50)
init->subchan[0].grclass = 0x0039;
else
init->subchan[0].grclass = 0x5039;
init->nr_subchan = 1;
/* Named memory object area */
ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
&init->notifier_handle);
if (ret) {
nouveau_channel_free(chan);
return ret;
}
return 0;
}
static int
nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_channel_free *cfree = data;
struct nouveau_channel *chan;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan);
nouveau_channel_free(chan);
return 0;
}
/***********************************
* finally, the ioctl table
***********************************/
struct drm_ioctl_desc nouveau_ioctls[] = {
DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_FREE, nouveau_ioctl_fifo_free, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GROBJ_ALLOC, nouveau_ioctl_grobj_alloc, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_ioctl_notifier_alloc, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH),
DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH),
};
int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);

View File

@ -0,0 +1,824 @@
/*
* 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 "drmP.h"
#include "drm_edid.h"
#include "drm_crtc_helper.h"
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
#include "nouveau_connector.h"
#include "nouveau_hw.h"
static inline struct drm_encoder_slave_funcs *
get_slave_funcs(struct nouveau_encoder *enc)
{
return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
}
static struct nouveau_encoder *
find_encoder_by_type(struct drm_connector *connector, int type)
{
struct drm_device *dev = connector->dev;
struct nouveau_encoder *nv_encoder;
struct drm_mode_object *obj;
int i, id;
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
id = connector->encoder_ids[i];
if (!id)
break;
obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
if (!obj)
continue;
nv_encoder = nouveau_encoder(obj_to_encoder(obj));
if (type == OUTPUT_ANY || nv_encoder->dcb->type == type)
return nv_encoder;
}
return NULL;
}
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
{
struct drm_device *dev = to_drm_encoder(encoder)->dev;
struct drm_connector *drm_connector;
list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
if (drm_connector->encoder == to_drm_encoder(encoder))
return nouveau_connector(drm_connector);
}
return NULL;
}
static void
nouveau_connector_destroy(struct drm_connector *drm_connector)
{
struct nouveau_connector *connector = nouveau_connector(drm_connector);
struct drm_device *dev = connector->base.dev;
NV_DEBUG(dev, "\n");
if (!connector)
return;
drm_sysfs_connector_remove(drm_connector);
drm_connector_cleanup(drm_connector);
kfree(drm_connector);
}
static void
nouveau_connector_ddc_prepare(struct drm_connector *connector, int *flags)
{
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
if (dev_priv->card_type >= NV_50)
return;
*flags = 0;
if (NVLockVgaCrtcs(dev_priv->dev, false))
*flags |= 1;
if (nv_heads_tied(dev_priv->dev))
*flags |= 2;
if (*flags & 2)
NVSetOwner(dev_priv->dev, 0); /* necessary? */
}
static void
nouveau_connector_ddc_finish(struct drm_connector *connector, int flags)
{
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
if (dev_priv->card_type >= NV_50)
return;
if (flags & 2)
NVSetOwner(dev_priv->dev, 4);
if (flags & 1)
NVLockVgaCrtcs(dev_priv->dev, true);
}
static struct nouveau_i2c_chan *
nouveau_connector_ddc_detect(struct drm_connector *connector,
struct nouveau_encoder **pnv_encoder)
{
struct drm_device *dev = connector->dev;
uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
int ret, flags, i;
struct i2c_msg msgs[] = {
{
.addr = 0x50,
.flags = 0,
.len = 1,
.buf = out_buf,
},
{
.addr = 0x50,
.flags = I2C_M_RD,
.len = 1,
.buf = buf,
}
};
for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
struct nouveau_i2c_chan *i2c = NULL;
struct nouveau_encoder *nv_encoder;
struct drm_mode_object *obj;
int id;
id = connector->encoder_ids[i];
if (!id)
break;
obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
if (!obj)
continue;
nv_encoder = nouveau_encoder(obj_to_encoder(obj));
if (nv_encoder->dcb->i2c_index < 0xf)
i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
if (!i2c)
continue;
nouveau_connector_ddc_prepare(connector, &flags);
ret = i2c_transfer(&i2c->adapter, msgs, 2);
nouveau_connector_ddc_finish(connector, flags);
if (ret == 2) {
*pnv_encoder = nv_encoder;
return i2c;
}
}
return NULL;
}
static void
nouveau_connector_set_encoder(struct drm_connector *connector,
struct nouveau_encoder *nv_encoder)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct drm_device *dev = connector->dev;
if (nv_connector->detected_encoder == nv_encoder)
return;
nv_connector->detected_encoder = nv_encoder;
if (nv_encoder->dcb->type == OUTPUT_LVDS ||
nv_encoder->dcb->type == OUTPUT_TMDS) {
connector->doublescan_allowed = false;
connector->interlace_allowed = false;
} else {
connector->doublescan_allowed = true;
if (dev_priv->card_type == NV_20 ||
(dev_priv->card_type == NV_10 &&
(dev->pci_device & 0x0ff0) != 0x0100 &&
(dev->pci_device & 0x0ff0) != 0x0150))
/* HW is broken */
connector->interlace_allowed = false;
else
connector->interlace_allowed = true;
}
if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
drm_connector_property_set_value(connector,
dev->mode_config.dvi_i_subconnector_property,
nv_encoder->dcb->type == OUTPUT_TMDS ?
DRM_MODE_SUBCONNECTOR_DVID :
DRM_MODE_SUBCONNECTOR_DVIA);
}
}
static enum drm_connector_status
nouveau_connector_detect(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = NULL;
struct nouveau_i2c_chan *i2c;
int type, flags;
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
if (nv_encoder && nv_connector->native_mode) {
nouveau_connector_set_encoder(connector, nv_encoder);
return connector_status_connected;
}
i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
if (i2c) {
nouveau_connector_ddc_prepare(connector, &flags);
nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
nouveau_connector_ddc_finish(connector, flags);
drm_mode_connector_update_edid_property(connector,
nv_connector->edid);
if (!nv_connector->edid) {
NV_ERROR(dev, "DDC responded, but no EDID for %s\n",
drm_get_connector_name(connector));
return connector_status_disconnected;
}
if (nv_encoder->dcb->type == OUTPUT_DP &&
!nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
NV_ERROR(dev, "Detected %s, but failed init\n",
drm_get_connector_name(connector));
return connector_status_disconnected;
}
/* Override encoder type for DVI-I based on whether EDID
* says the display is digital or analog, both use the
* same i2c channel so the value returned from ddc_detect
* isn't necessarily correct.
*/
if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
type = OUTPUT_TMDS;
else
type = OUTPUT_ANALOG;
nv_encoder = find_encoder_by_type(connector, type);
if (!nv_encoder) {
NV_ERROR(dev, "Detected %d encoder on %s, "
"but no object!\n", type,
drm_get_connector_name(connector));
return connector_status_disconnected;
}
}
nouveau_connector_set_encoder(connector, nv_encoder);
return connector_status_connected;
}
nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
if (!nv_encoder)
nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
if (nv_encoder) {
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
struct drm_encoder_helper_funcs *helper =
encoder->helper_private;
if (helper->detect(encoder, connector) ==
connector_status_connected) {
nouveau_connector_set_encoder(connector, nv_encoder);
return connector_status_connected;
}
}
return connector_status_disconnected;
}
static void
nouveau_connector_force(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct nouveau_encoder *nv_encoder;
int type;
if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
if (connector->force == DRM_FORCE_ON_DIGITAL)
type = OUTPUT_TMDS;
else
type = OUTPUT_ANALOG;
} else
type = OUTPUT_ANY;
nv_encoder = find_encoder_by_type(connector, type);
if (!nv_encoder) {
NV_ERROR(dev, "can't find encoder to force %s on!\n",
drm_get_connector_name(connector));
connector->status = connector_status_disconnected;
return;
}
nouveau_connector_set_encoder(connector, nv_encoder);
}
static int
nouveau_connector_set_property(struct drm_connector *connector,
struct drm_property *property, uint64_t value)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct drm_device *dev = connector->dev;
int ret;
/* Scaling mode */
if (property == dev->mode_config.scaling_mode_property) {
struct nouveau_crtc *nv_crtc = NULL;
bool modeset = false;
switch (value) {
case DRM_MODE_SCALE_NONE:
case DRM_MODE_SCALE_FULLSCREEN:
case DRM_MODE_SCALE_CENTER:
case DRM_MODE_SCALE_ASPECT:
break;
default:
return -EINVAL;
}
/* LVDS always needs gpu scaling */
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
value == DRM_MODE_SCALE_NONE)
return -EINVAL;
/* Changing between GPU and panel scaling requires a full
* modeset
*/
if ((nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) ||
(value == DRM_MODE_SCALE_NONE))
modeset = true;
nv_connector->scaling_mode = value;
if (connector->encoder && connector->encoder->crtc)
nv_crtc = nouveau_crtc(connector->encoder->crtc);
if (!nv_crtc)
return 0;
if (modeset || !nv_crtc->set_scale) {
ret = drm_crtc_helper_set_mode(&nv_crtc->base,
&nv_crtc->base.mode,
nv_crtc->base.x,
nv_crtc->base.y, NULL);
if (!ret)
return -EINVAL;
} else {
ret = nv_crtc->set_scale(nv_crtc, value, true);
if (ret)
return ret;
}
return 0;
}
/* Dithering */
if (property == dev->mode_config.dithering_mode_property) {
struct nouveau_crtc *nv_crtc = NULL;
if (value == DRM_MODE_DITHERING_ON)
nv_connector->use_dithering = true;
else
nv_connector->use_dithering = false;
if (connector->encoder && connector->encoder->crtc)
nv_crtc = nouveau_crtc(connector->encoder->crtc);
if (!nv_crtc || !nv_crtc->set_dither)
return 0;
return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering,
true);
}
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
return get_slave_funcs(nv_encoder)->
set_property(to_drm_encoder(nv_encoder), connector, property, value);
return -EINVAL;
}
static struct drm_display_mode *
nouveau_connector_native_mode(struct nouveau_connector *connector)
{
struct drm_device *dev = connector->base.dev;
struct drm_display_mode *mode, *largest = NULL;
int high_w = 0, high_h = 0, high_v = 0;
/* Use preferred mode if there is one.. */
list_for_each_entry(mode, &connector->base.probed_modes, head) {
if (mode->type & DRM_MODE_TYPE_PREFERRED) {
NV_DEBUG(dev, "native mode from preferred\n");
return drm_mode_duplicate(dev, mode);
}
}
/* Otherwise, take the resolution with the largest width, then height,
* then vertical refresh
*/
list_for_each_entry(mode, &connector->base.probed_modes, head) {
if (mode->hdisplay < high_w)
continue;
if (mode->hdisplay == high_w && mode->vdisplay < high_h)
continue;
if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
mode->vrefresh < high_v)
continue;
high_w = mode->hdisplay;
high_h = mode->vdisplay;
high_v = mode->vrefresh;
largest = mode;
}
NV_DEBUG(dev, "native mode from largest: %dx%d@%d\n",
high_w, high_h, high_v);
return largest ? drm_mode_duplicate(dev, largest) : NULL;
}
struct moderec {
int hdisplay;
int vdisplay;
};
static struct moderec scaler_modes[] = {
{ 1920, 1200 },
{ 1920, 1080 },
{ 1680, 1050 },
{ 1600, 1200 },
{ 1400, 1050 },
{ 1280, 1024 },
{ 1280, 960 },
{ 1152, 864 },
{ 1024, 768 },
{ 800, 600 },
{ 720, 400 },
{ 640, 480 },
{ 640, 400 },
{ 640, 350 },
{}
};
static int
nouveau_connector_scaler_modes_add(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct drm_display_mode *native = nv_connector->native_mode, *m;
struct drm_device *dev = connector->dev;
struct moderec *mode = &scaler_modes[0];
int modes = 0;
if (!native)
return 0;
while (mode->hdisplay) {
if (mode->hdisplay <= native->hdisplay &&
mode->vdisplay <= native->vdisplay) {
m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay,
drm_mode_vrefresh(native), false,
false, false);
if (!m)
continue;
m->type |= DRM_MODE_TYPE_DRIVER;
drm_mode_probed_add(connector, m);
modes++;
}
mode++;
}
return modes;
}
static int
nouveau_connector_get_modes(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
int ret = 0;
/* If we're not LVDS, destroy the previous native mode, the attached
* monitor could have changed.
*/
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
nv_connector->native_mode) {
drm_mode_destroy(dev, nv_connector->native_mode);
nv_connector->native_mode = NULL;
}
if (nv_connector->edid)
ret = drm_add_edid_modes(connector, nv_connector->edid);
/* Find the native mode if this is a digital panel, if we didn't
* find any modes through DDC previously add the native mode to
* the list of modes.
*/
if (!nv_connector->native_mode)
nv_connector->native_mode =
nouveau_connector_native_mode(nv_connector);
if (ret == 0 && nv_connector->native_mode) {
struct drm_display_mode *mode;
mode = drm_mode_duplicate(dev, nv_connector->native_mode);
drm_mode_probed_add(connector, mode);
ret = 1;
}
if (nv_encoder->dcb->type == OUTPUT_TV)
ret = get_slave_funcs(nv_encoder)->
get_modes(to_drm_encoder(nv_encoder), connector);
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
ret += nouveau_connector_scaler_modes_add(connector);
return ret;
}
static int
nouveau_connector_mode_valid(struct drm_connector *connector,
struct drm_display_mode *mode)
{
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
unsigned min_clock = 25000, max_clock = min_clock;
unsigned clock = mode->clock;
switch (nv_encoder->dcb->type) {
case OUTPUT_LVDS:
BUG_ON(!nv_connector->native_mode);
if (mode->hdisplay > nv_connector->native_mode->hdisplay ||
mode->vdisplay > nv_connector->native_mode->vdisplay)
return MODE_PANEL;
min_clock = 0;
max_clock = 400000;
break;
case OUTPUT_TMDS:
if ((dev_priv->card_type >= NV_50 && !nouveau_duallink) ||
(dev_priv->card_type < NV_50 &&
!nv_encoder->dcb->duallink_possible))
max_clock = 165000;
else
max_clock = 330000;
break;
case OUTPUT_ANALOG:
max_clock = nv_encoder->dcb->crtconf.maxfreq;
if (!max_clock)
max_clock = 350000;
break;
case OUTPUT_TV:
return get_slave_funcs(nv_encoder)->
mode_valid(to_drm_encoder(nv_encoder), mode);
case OUTPUT_DP:
if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
max_clock = nv_encoder->dp.link_nr * 270000;
else
max_clock = nv_encoder->dp.link_nr * 162000;
clock *= 3;
break;
}
if (clock < min_clock)
return MODE_CLOCK_LOW;
if (clock > max_clock)
return MODE_CLOCK_HIGH;
return MODE_OK;
}
static struct drm_encoder *
nouveau_connector_best_encoder(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
if (nv_connector->detected_encoder)
return to_drm_encoder(nv_connector->detected_encoder);
return NULL;
}
static const struct drm_connector_helper_funcs
nouveau_connector_helper_funcs = {
.get_modes = nouveau_connector_get_modes,
.mode_valid = nouveau_connector_mode_valid,
.best_encoder = nouveau_connector_best_encoder,
};
static const struct drm_connector_funcs
nouveau_connector_funcs = {
.dpms = drm_helper_connector_dpms,
.save = NULL,
.restore = NULL,
.detect = nouveau_connector_detect,
.destroy = nouveau_connector_destroy,
.fill_modes = drm_helper_probe_single_connector_modes,
.set_property = nouveau_connector_set_property,
.force = nouveau_connector_force
};
static int
nouveau_connector_create_lvds(struct drm_device *dev,
struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *i2c = NULL;
struct nouveau_encoder *nv_encoder;
struct drm_display_mode native, *mode, *temp;
bool dummy, if_is_24bit = false;
int ret, flags;
nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
if (!nv_encoder)
return -ENODEV;
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit);
if (ret) {
NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n");
return ret;
}
nv_connector->use_dithering = !if_is_24bit;
/* Firstly try getting EDID over DDC, if allowed and I2C channel
* is available.
*/
if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
if (i2c) {
nouveau_connector_ddc_prepare(connector, &flags);
nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
nouveau_connector_ddc_finish(connector, flags);
}
/* If no EDID found above, and the VBIOS indicates a hardcoded
* modeline is avalilable for the panel, set it as the panel's
* native mode and exit.
*/
if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
(nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
dev_priv->VBIOS.pub.fp_no_ddc)) {
nv_connector->native_mode = drm_mode_duplicate(dev, &native);
goto out;
}
/* Still nothing, some VBIOS images have a hardcoded EDID block
* stored for the panel stored in them.
*/
if (!nv_connector->edid && !nv_connector->native_mode &&
!dev_priv->VBIOS.pub.fp_no_ddc) {
nv_connector->edid =
(struct edid *)nouveau_bios_embedded_edid(dev);
}
if (!nv_connector->edid)
goto out;
/* We didn't find/use a panel mode from the VBIOS, so parse the EDID
* block and look for the preferred mode there.
*/
ret = drm_add_edid_modes(connector, nv_connector->edid);
if (ret == 0)
goto out;
nv_connector->detected_encoder = nv_encoder;
nv_connector->native_mode = nouveau_connector_native_mode(nv_connector);
list_for_each_entry_safe(mode, temp, &connector->probed_modes, head)
drm_mode_remove(connector, mode);
out:
if (!nv_connector->native_mode) {
NV_ERROR(dev, "LVDS present in DCB table, but couldn't "
"determine its native mode. Disabling.\n");
return -ENODEV;
}
drm_mode_connector_update_edid_property(connector, nv_connector->edid);
return 0;
}
int
nouveau_connector_create(struct drm_device *dev, int index, int type)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_connector *nv_connector = NULL;
struct drm_connector *connector;
struct drm_encoder *encoder;
int ret;
NV_DEBUG(dev, "\n");
nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
if (!nv_connector)
return -ENOMEM;
nv_connector->dcb = nouveau_bios_connector_entry(dev, index);
connector = &nv_connector->base;
switch (type) {
case DRM_MODE_CONNECTOR_VGA:
NV_INFO(dev, "Detected a VGA connector\n");
break;
case DRM_MODE_CONNECTOR_DVID:
NV_INFO(dev, "Detected a DVI-D connector\n");
break;
case DRM_MODE_CONNECTOR_DVII:
NV_INFO(dev, "Detected a DVI-I connector\n");
break;
case DRM_MODE_CONNECTOR_LVDS:
NV_INFO(dev, "Detected a LVDS connector\n");
break;
case DRM_MODE_CONNECTOR_TV:
NV_INFO(dev, "Detected a TV connector\n");
break;
case DRM_MODE_CONNECTOR_DisplayPort:
NV_INFO(dev, "Detected a DisplayPort connector\n");
break;
default:
NV_ERROR(dev, "Unknown connector, this is not good.\n");
break;
}
/* defaults, will get overridden in detect() */
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
/* Init DVI-I specific properties */
if (type == DRM_MODE_CONNECTOR_DVII) {
drm_mode_create_dvi_i_properties(dev);
drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
}
if (type != DRM_MODE_CONNECTOR_LVDS)
nv_connector->use_dithering = false;
if (type == DRM_MODE_CONNECTOR_DVID ||
type == DRM_MODE_CONNECTOR_DVII ||
type == DRM_MODE_CONNECTOR_LVDS ||
type == DRM_MODE_CONNECTOR_DisplayPort) {
nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property,
nv_connector->scaling_mode);
drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property,
nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
: DRM_MODE_DITHERING_OFF);
} else {
nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
if (type == DRM_MODE_CONNECTOR_VGA &&
dev_priv->card_type >= NV_50) {
drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property,
nv_connector->scaling_mode);
}
}
/* attach encoders */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (nv_encoder->dcb->connector != index)
continue;
if (get_slave_funcs(nv_encoder))
get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
drm_mode_connector_attach_encoder(connector, encoder);
}
drm_sysfs_connector_add(connector);
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
ret = nouveau_connector_create_lvds(dev, connector);
if (ret) {
connector->funcs->destroy(connector);
return ret;
}
}
return 0;
}

View File

@ -0,0 +1,54 @@
/*
* 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 __NOUVEAU_CONNECTOR_H__
#define __NOUVEAU_CONNECTOR_H__
#include "drm_edid.h"
#include "nouveau_i2c.h"
struct nouveau_connector {
struct drm_connector base;
struct dcb_connector_table_entry *dcb;
int scaling_mode;
bool use_dithering;
struct nouveau_encoder *detected_encoder;
struct edid *edid;
struct drm_display_mode *native_mode;
};
static inline struct nouveau_connector *nouveau_connector(
struct drm_connector *con)
{
return container_of(con, struct nouveau_connector, base);
}
int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
#endif /* __NOUVEAU_CONNECTOR_H__ */

View File

@ -0,0 +1,95 @@
/*
* 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 __NOUVEAU_CRTC_H__
#define __NOUVEAU_CRTC_H__
struct nouveau_crtc {
struct drm_crtc base;
int index;
struct drm_display_mode *mode;
uint32_t dpms_saved_fp_control;
uint32_t fp_users;
int saturation;
int sharpness;
int last_dpms;
struct {
int cpp;
bool blanked;
uint32_t offset;
uint32_t tile_flags;
} fb;
struct {
struct nouveau_bo *nvbo;
bool visible;
uint32_t offset;
void (*set_offset)(struct nouveau_crtc *, uint32_t offset);
void (*set_pos)(struct nouveau_crtc *, int x, int y);
void (*hide)(struct nouveau_crtc *, bool update);
void (*show)(struct nouveau_crtc *, bool update);
} cursor;
struct {
struct nouveau_bo *nvbo;
uint16_t r[256];
uint16_t g[256];
uint16_t b[256];
int depth;
} lut;
int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
};
static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
{
return container_of(crtc, struct nouveau_crtc, base);
}
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_cursor_init(struct nouveau_crtc *);
void nv50_cursor_fini(struct nouveau_crtc *);
int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
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 *);
struct nouveau_connector *
nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
#endif /* __NOUVEAU_CRTC_H__ */

View File

@ -0,0 +1,155 @@
/*
* Copyright (C) 2009 Red Hat <bskeggs@redhat.com>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE 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.
*
*/
/*
* Authors:
* Ben Skeggs <bskeggs@redhat.com>
*/
#include <linux/debugfs.h>
#include "drmP.h"
#include "nouveau_drv.h"
static int
nouveau_debugfs_channel_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct nouveau_channel *chan = node->info_ent->data;
seq_printf(m, "channel id : %d\n", chan->id);
seq_printf(m, "cpu fifo state:\n");
seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base);
seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2);
seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2);
seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2);
seq_printf(m, " free: 0x%08x\n", chan->dma.free << 2);
seq_printf(m, "gpu fifo state:\n");
seq_printf(m, " get: 0x%08x\n",
nvchan_rd32(chan, chan->user_get));
seq_printf(m, " put: 0x%08x\n",
nvchan_rd32(chan, chan->user_put));
seq_printf(m, "last fence : %d\n", chan->fence.sequence);
seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack);
return 0;
}
int
nouveau_debugfs_channel_init(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_minor *minor = chan->dev->primary;
int ret;
if (!dev_priv->debugfs.channel_root) {
dev_priv->debugfs.channel_root =
debugfs_create_dir("channel", minor->debugfs_root);
if (!dev_priv->debugfs.channel_root)
return -ENOENT;
}
snprintf(chan->debugfs.name, 32, "%d", chan->id);
chan->debugfs.info.name = chan->debugfs.name;
chan->debugfs.info.show = nouveau_debugfs_channel_info;
chan->debugfs.info.driver_features = 0;
chan->debugfs.info.data = chan;
ret = drm_debugfs_create_files(&chan->debugfs.info, 1,
dev_priv->debugfs.channel_root,
chan->dev->primary);
if (ret == 0)
chan->debugfs.active = true;
return ret;
}
void
nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
if (!chan->debugfs.active)
return;
drm_debugfs_remove_files(&chan->debugfs.info, 1, chan->dev->primary);
chan->debugfs.active = false;
if (chan == dev_priv->channel) {
debugfs_remove(dev_priv->debugfs.channel_root);
dev_priv->debugfs.channel_root = NULL;
}
}
static int
nouveau_debugfs_chipset_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_minor *minor = node->minor;
struct drm_device *dev = minor->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t ppci_0;
ppci_0 = nv_rd32(dev, dev_priv->chipset >= 0x40 ? 0x88000 : 0x1800);
seq_printf(m, "PMC_BOOT_0: 0x%08x\n", nv_rd32(dev, NV03_PMC_BOOT_0));
seq_printf(m, "PCI ID : 0x%04x:0x%04x\n",
ppci_0 & 0xffff, ppci_0 >> 16);
return 0;
}
static int
nouveau_debugfs_memory_info(struct seq_file *m, void *data)
{
struct drm_info_node *node = (struct drm_info_node *) m->private;
struct drm_minor *minor = node->minor;
struct drm_device *dev = minor->dev;
seq_printf(m, "VRAM total: %dKiB\n",
(int)(nouveau_mem_fb_amount(dev) >> 10));
return 0;
}
static struct drm_info_list nouveau_debugfs_list[] = {
{ "chipset", nouveau_debugfs_chipset_info, 0, NULL },
{ "memory", nouveau_debugfs_memory_info, 0, NULL },
};
#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
int
nouveau_debugfs_init(struct drm_minor *minor)
{
drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
minor->debugfs_root, minor);
return 0;
}
void
nouveau_debugfs_takedown(struct drm_minor *minor)
{
drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
minor);
}

View File

@ -0,0 +1,115 @@
/*
* 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 "drmP.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_fb.h"
#include "nouveau_fbcon.h"
static void
nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
{
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
struct drm_device *dev = drm_fb->dev;
if (drm_fb->fbdev)
nouveau_fbcon_remove(dev, drm_fb);
if (fb->nvbo) {
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(fb->nvbo->gem);
mutex_unlock(&dev->struct_mutex);
}
drm_framebuffer_cleanup(drm_fb);
kfree(fb);
}
static int
nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,
struct drm_file *file_priv,
unsigned int *handle)
{
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);
}
static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
.destroy = nouveau_user_framebuffer_destroy,
.create_handle = nouveau_user_framebuffer_create_handle,
};
struct drm_framebuffer *
nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo,
struct drm_mode_fb_cmd *mode_cmd)
{
struct nouveau_framebuffer *fb;
int ret;
fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
if (!fb)
return NULL;
ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs);
if (ret) {
kfree(fb);
return NULL;
}
drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
fb->nvbo = nvbo;
return &fb->base;
}
static struct drm_framebuffer *
nouveau_user_framebuffer_create(struct drm_device *dev,
struct drm_file *file_priv,
struct drm_mode_fb_cmd *mode_cmd)
{
struct drm_framebuffer *fb;
struct drm_gem_object *gem;
gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
if (!gem)
return NULL;
fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd);
if (!fb) {
drm_gem_object_unreference(gem);
return NULL;
}
return fb;
}
const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
.fb_create = nouveau_user_framebuffer_create,
.fb_changed = nouveau_fbcon_probe,
};

View File

@ -0,0 +1,206 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
int
nouveau_dma_init(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *m2mf = NULL;
int ret, i;
/* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
0x0039 : 0x5039, &m2mf);
if (ret)
return ret;
ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
if (ret)
return ret;
/* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
if (ret)
return ret;
/* Map push buffer */
ret = nouveau_bo_map(chan->pushbuf_bo);
if (ret)
return ret;
/* Map M2MF notifier object - fbcon. */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
ret = nouveau_bo_map(chan->notifier_bo);
if (ret)
return ret;
}
/* Initialise DMA vars */
chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
chan->dma.put = 0;
chan->dma.cur = chan->dma.put;
chan->dma.free = chan->dma.max - chan->dma.cur;
/* Insert NOPS for NOUVEAU_DMA_SKIPS */
ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
if (ret)
return ret;
for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
OUT_RING(chan, 0);
/* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
ret = RING_SPACE(chan, 4);
if (ret)
return ret;
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
OUT_RING(chan, NvM2MF);
BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
OUT_RING(chan, NvNotify0);
/* Sit back and pray the channel works.. */
FIRE_RING(chan);
return 0;
}
void
OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
{
bool is_iomem;
u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);
mem = &mem[chan->dma.cur];
if (is_iomem)
memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);
else
memcpy(mem, data, nr_dwords * 4);
chan->dma.cur += nr_dwords;
}
static inline bool
READ_GET(struct nouveau_channel *chan, uint32_t *get)
{
uint32_t val;
val = nvchan_rd32(chan, chan->user_get);
if (val < chan->pushbuf_base ||
val >= chan->pushbuf_base + chan->pushbuf_bo->bo.mem.size) {
/* meaningless to dma_wait() except to know whether the
* GPU has stalled or not
*/
*get = val;
return false;
}
*get = (val - chan->pushbuf_base) >> 2;
return true;
}
int
nouveau_dma_wait(struct nouveau_channel *chan, int size)
{
uint32_t get, prev_get = 0, cnt = 0;
bool get_valid;
while (chan->dma.free < size) {
/* reset counter as long as GET is still advancing, this is
* to avoid misdetecting a GPU lockup if the GPU happens to
* just be processing an operation that takes a long time
*/
get_valid = READ_GET(chan, &get);
if (get != prev_get) {
prev_get = get;
cnt = 0;
}
if ((++cnt & 0xff) == 0) {
DRM_UDELAY(1);
if (cnt > 100000)
return -EBUSY;
}
/* loop until we have a usable GET pointer. the value
* we read from the GPU may be outside the main ring if
* PFIFO is processing a buffer called from the main ring,
* discard these values until something sensible is seen.
*
* the other case we discard GET is while the GPU is fetching
* from the SKIPS area, so the code below doesn't have to deal
* with some fun corner cases.
*/
if (!get_valid || get < NOUVEAU_DMA_SKIPS)
continue;
if (get <= chan->dma.cur) {
/* engine is fetching behind us, or is completely
* idle (GET == PUT) so we have free space up until
* the end of the push buffer
*
* we can only hit that path once per call due to
* looping back to the beginning of the push buffer,
* we'll hit the fetching-ahead-of-us path from that
* point on.
*
* the *one* exception to that rule is if we read
* GET==PUT, in which case the below conditional will
* always succeed and break us out of the wait loop.
*/
chan->dma.free = chan->dma.max - chan->dma.cur;
if (chan->dma.free >= size)
break;
/* not enough space left at the end of the push buffer,
* instruct the GPU to jump back to the start right
* after processing the currently pending commands.
*/
OUT_RING(chan, chan->pushbuf_base | 0x20000000);
WRITE_PUT(NOUVEAU_DMA_SKIPS);
/* we're now submitting commands at the start of
* the push buffer.
*/
chan->dma.cur =
chan->dma.put = NOUVEAU_DMA_SKIPS;
}
/* engine fetching ahead of us, we have space up until the
* current GET pointer. the "- 1" is to ensure there's
* space left to emit a jump back to the beginning of the
* push buffer if we require it. we can never get GET == PUT
* here, so this is safe.
*/
chan->dma.free = get - chan->dma.cur - 1;
}
return 0;
}

View File

@ -0,0 +1,157 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 __NOUVEAU_DMA_H__
#define __NOUVEAU_DMA_H__
#ifndef NOUVEAU_DMA_DEBUG
#define NOUVEAU_DMA_DEBUG 0
#endif
/*
* There's a hw race condition where you can't jump to your PUT offset,
* to avoid this we jump to offset + SKIPS and fill the difference with
* NOPs.
*
* xf86-video-nv configures the DMA fetch size to 32 bytes, and uses
* a SKIPS value of 8. Lets assume that the race condition is to do
* with writing into the fetch area, we configure a fetch size of 128
* bytes so we need a larger SKIPS value.
*/
#define NOUVEAU_DMA_SKIPS (128 / 4)
/* Hardcoded object assignments to subchannels (subchannel id). */
enum {
NvSubM2MF = 0,
NvSub2D = 1,
NvSubCtxSurf2D = 1,
NvSubGdiRect = 2,
NvSubImageBlit = 3
};
/* Object handles. */
enum {
NvM2MF = 0x80000001,
NvDmaFB = 0x80000002,
NvDmaTT = 0x80000003,
NvDmaVRAM = 0x80000004,
NvDmaGART = 0x80000005,
NvNotify0 = 0x80000006,
Nv2D = 0x80000007,
NvCtxSurf2D = 0x80000008,
NvRop = 0x80000009,
NvImagePatt = 0x8000000a,
NvClipRect = 0x8000000b,
NvGdiRect = 0x8000000c,
NvImageBlit = 0x8000000d,
/* G80+ display objects */
NvEvoVRAM = 0x01000000,
NvEvoFB16 = 0x01000001,
NvEvoFB32 = 0x01000002
};
#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
#define NV_MEMORY_TO_MEMORY_FORMAT_NAME 0x00000000
#define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF 0x00000050
#define NV_MEMORY_TO_MEMORY_FORMAT_NOP 0x00000100
#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE 0x00000000
#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN 0x00000001
#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY 0x00000180
#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE 0x00000184
#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
#define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039
#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200 0x00000200
#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C 0x0000021c
#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH 0x00000238
#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH 0x0000023c
static __must_check inline int
RING_SPACE(struct nouveau_channel *chan, int size)
{
if (chan->dma.free < size) {
int ret;
ret = nouveau_dma_wait(chan, size);
if (ret)
return ret;
}
chan->dma.free -= size;
return 0;
}
static inline void
OUT_RING(struct nouveau_channel *chan, int data)
{
if (NOUVEAU_DMA_DEBUG) {
NV_INFO(chan->dev, "Ch%d/0x%08x: 0x%08x\n",
chan->id, chan->dma.cur << 2, data);
}
nouveau_bo_wr32(chan->pushbuf_bo, chan->dma.cur++, data);
}
extern void
OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords);
static inline void
BEGIN_RING(struct nouveau_channel *chan, int subc, int mthd, int size)
{
OUT_RING(chan, (subc << 13) | (size << 18) | mthd);
}
#define WRITE_PUT(val) do { \
DRM_MEMORYBARRIER(); \
nouveau_bo_rd32(chan->pushbuf_bo, 0); \
nvchan_wr32(chan, chan->user_put, ((val) << 2) + chan->pushbuf_base); \
} while (0)
static inline void
FIRE_RING(struct nouveau_channel *chan)
{
if (NOUVEAU_DMA_DEBUG) {
NV_INFO(chan->dev, "Ch%d/0x%08x: PUSH!\n",
chan->id, chan->dma.cur << 2);
}
if (chan->dma.cur == chan->dma.put)
return;
chan->accel_done = true;
WRITE_PUT(chan->dma.cur);
chan->dma.put = chan->dma.cur;
}
static inline void
WIND_RING(struct nouveau_channel *chan)
{
chan->dma.cur = chan->dma.put;
}
#endif

View File

@ -0,0 +1,569 @@
/*
* Copyright 2009 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_i2c.h"
#include "nouveau_encoder.h"
static int
auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_i2c_chan *auxch;
int ret;
auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
if (!auxch)
return -ENODEV;
ret = nouveau_dp_auxch(auxch, 9, address, buf, size);
if (ret)
return ret;
return 0;
}
static int
auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_i2c_chan *auxch;
int ret;
auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
if (!auxch)
return -ENODEV;
ret = nouveau_dp_auxch(auxch, 8, address, buf, size);
return ret;
}
static int
nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
uint32_t tmp;
int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED |
NV50_SOR_DP_CTRL_LANE_MASK);
tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16;
if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN)
tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED;
nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1);
}
static int
nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
uint32_t tmp;
int reg = 0x614300 + (nv_encoder->or * 0x800);
tmp = nv_rd32(dev, reg);
tmp &= 0xfff3ffff;
if (cmd == DP_LINK_BW_2_7)
tmp |= 0x00040000;
nv_wr32(dev, reg, tmp);
return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1);
}
static int
nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
uint32_t tmp;
uint8_t cmd;
int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
int ret;
tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN;
tmp |= (pattern << 24);
nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
if (ret)
return ret;
cmd &= ~DP_TRAINING_PATTERN_MASK;
cmd |= (pattern & DP_TRAINING_PATTERN_MASK);
return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
}
static int
nouveau_dp_max_voltage_swing(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct bit_displayport_encoder_table_entry *dpse;
struct bit_displayport_encoder_table *dpe;
int i, dpe_headerlen, max_vs = 0;
dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
if (!dpe)
return false;
dpse = (void *)((char *)dpe + dpe_headerlen);
for (i = 0; i < dpe_headerlen; i++, dpse++) {
if (dpse->vs_level > max_vs)
max_vs = dpse->vs_level;
}
return max_vs;
}
static int
nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct bit_displayport_encoder_table_entry *dpse;
struct bit_displayport_encoder_table *dpe;
int i, dpe_headerlen, max_pre = 0;
dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
if (!dpe)
return false;
dpse = (void *)((char *)dpe + dpe_headerlen);
for (i = 0; i < dpe_headerlen; i++, dpse++) {
if (dpse->vs_level != vs)
continue;
if (dpse->pre_level > max_pre)
max_pre = dpse->pre_level;
}
return max_pre;
}
static bool
nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct bit_displayport_encoder_table_entry *dpse;
struct bit_displayport_encoder_table *dpe;
int ret, i, dpe_headerlen, vs = 0, pre = 0;
uint8_t request[2];
dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
if (!dpe)
return false;
dpse = (void *)((char *)dpe + dpe_headerlen);
ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2);
if (ret)
return false;
NV_DEBUG(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]);
/* Keep all lanes at the same level.. */
for (i = 0; i < nv_encoder->dp.link_nr; i++) {
int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf;
int lane_vs = lane_req & 3;
int lane_pre = (lane_req >> 2) & 3;
if (lane_vs > vs)
vs = lane_vs;
if (lane_pre > pre)
pre = lane_pre;
}
if (vs >= nouveau_dp_max_voltage_swing(encoder)) {
vs = nouveau_dp_max_voltage_swing(encoder);
vs |= 4;
}
if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) {
pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3);
pre |= 4;
}
/* Update the configuration for all lanes.. */
for (i = 0; i < nv_encoder->dp.link_nr; i++)
config[i] = (pre << 3) | vs;
return true;
}
static bool
nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct bit_displayport_encoder_table_entry *dpse;
struct bit_displayport_encoder_table *dpe;
int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
int dpe_headerlen, ret, i;
NV_DEBUG(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n",
config[0], config[1], config[2], config[3]);
dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
if (!dpe)
return false;
dpse = (void *)((char *)dpe + dpe_headerlen);
for (i = 0; i < dpe->record_nr; i++, dpse++) {
if (dpse->vs_level == (config[0] & 3) &&
dpse->pre_level == ((config[0] >> 3) & 3))
break;
}
BUG_ON(i == dpe->record_nr);
for (i = 0; i < nv_encoder->dp.link_nr; i++) {
const int shift[4] = { 16, 8, 0, 24 };
uint32_t mask = 0xff << shift[i];
uint32_t reg0, reg1, reg2;
reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask;
reg0 |= (dpse->reg0 << shift[i]);
reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask;
reg1 |= (dpse->reg1 << shift[i]);
reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff;
reg2 |= (dpse->reg2 << 8);
nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0);
nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1);
nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2);
}
ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4);
if (ret)
return false;
return true;
}
bool
nouveau_dp_link_train(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
uint8_t config[4];
uint8_t status[3];
bool cr_done, cr_max_vs, eq_done;
int ret = 0, i, tries, voltage;
NV_DEBUG(dev, "link training!!\n");
train:
cr_done = eq_done = false;
/* set link configuration */
NV_DEBUG(dev, "\tbegin train: bw %d, lanes %d\n",
nv_encoder->dp.link_bw, nv_encoder->dp.link_nr);
ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw);
if (ret)
return false;
config[0] = nv_encoder->dp.link_nr;
if (nv_encoder->dp.dpcd_version >= 0x11)
config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
ret = nouveau_dp_lane_count_set(encoder, config[0]);
if (ret)
return false;
/* clock recovery */
NV_DEBUG(dev, "\tbegin cr\n");
ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1);
if (ret)
goto stop;
tries = 0;
voltage = -1;
memset(config, 0x00, sizeof(config));
for (;;) {
if (!nouveau_dp_link_train_commit(encoder, config))
break;
udelay(100);
ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2);
if (ret)
break;
NV_DEBUG(dev, "\t\tstatus: 0x%02x 0x%02x\n",
status[0], status[1]);
cr_done = true;
cr_max_vs = false;
for (i = 0; i < nv_encoder->dp.link_nr; i++) {
int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
if (!(lane & DP_LANE_CR_DONE)) {
cr_done = false;
if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED)
cr_max_vs = true;
break;
}
}
if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
tries = 0;
}
if (cr_done || cr_max_vs || (++tries == 5))
break;
if (!nouveau_dp_link_train_adjust(encoder, config))
break;
}
if (!cr_done)
goto stop;
/* channel equalisation */
NV_DEBUG(dev, "\tbegin eq\n");
ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2);
if (ret)
goto stop;
for (tries = 0; tries <= 5; tries++) {
udelay(400);
ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3);
if (ret)
break;
NV_DEBUG(dev, "\t\tstatus: 0x%02x 0x%02x\n",
status[0], status[1]);
eq_done = true;
if (!(status[2] & DP_INTERLANE_ALIGN_DONE))
eq_done = false;
for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) {
int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
if (!(lane & DP_LANE_CR_DONE)) {
cr_done = false;
break;
}
if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
!(lane & DP_LANE_SYMBOL_LOCKED)) {
eq_done = false;
break;
}
}
if (eq_done || !cr_done)
break;
if (!nouveau_dp_link_train_adjust(encoder, config) ||
!nouveau_dp_link_train_commit(encoder, config))
break;
}
stop:
/* end link training */
ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE);
if (ret)
return false;
/* retry at a lower setting, if possible */
if (!ret && !(eq_done && cr_done)) {
NV_DEBUG(dev, "\twe failed\n");
if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) {
NV_DEBUG(dev, "retry link training at low rate\n");
nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
goto train;
}
}
return eq_done;
}
bool
nouveau_dp_detect(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
uint8_t dpcd[4];
int ret;
ret = auxch_rd(encoder, 0x0000, dpcd, 4);
if (ret)
return false;
NV_DEBUG(dev, "encoder: link_bw %d, link_nr %d\n"
"display: link_bw %d, link_nr %d version 0x%02x\n",
nv_encoder->dcb->dpconf.link_bw,
nv_encoder->dcb->dpconf.link_nr,
dpcd[1], dpcd[2] & 0x0f, dpcd[0]);
nv_encoder->dp.dpcd_version = dpcd[0];
nv_encoder->dp.link_bw = dpcd[1];
if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 &&
!nv_encoder->dcb->dpconf.link_bw)
nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
nv_encoder->dp.link_nr = dpcd[2] & 0xf;
if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr)
nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
return true;
}
int
nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr)
{
struct drm_device *dev = auxch->dev;
uint32_t tmp, ctrl, stat = 0, data32[4] = {};
int ret = 0, i, index = auxch->rd;
NV_DEBUG(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr);
tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000);
tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
if (!(tmp & 0x01000000)) {
NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp);
ret = -EIO;
goto out;
}
for (i = 0; i < 3; i++) {
tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd));
if (tmp & NV50_AUXCH_STAT_STATE_READY)
break;
udelay(100);
}
if (i == 3) {
ret = -EBUSY;
goto out;
}
if (!(cmd & 1)) {
memcpy(data32, data, data_nr);
for (i = 0; i < 4; i++) {
NV_DEBUG(dev, "wr %d: 0x%08x\n", i, data32[i]);
nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]);
}
}
nv_wr32(dev, NV50_AUXCH_ADDR(index), addr);
ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index));
ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN);
ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT);
ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT);
for (;;) {
nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000);
nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl);
nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000);
if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
nv_rd32(dev, NV50_AUXCH_CTRL(index)));
return -EBUSY;
}
udelay(400);
stat = nv_rd32(dev, NV50_AUXCH_STAT(index));
if ((stat & NV50_AUXCH_STAT_REPLY_AUX) !=
NV50_AUXCH_STAT_REPLY_AUX_DEFER)
break;
}
if (cmd & 1) {
for (i = 0; i < 4; i++) {
data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
NV_DEBUG(dev, "rd %d: 0x%08x\n", i, data32[i]);
}
memcpy(data, data32, data_nr);
}
out:
tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000);
tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
if (tmp & 0x01000000) {
NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp);
ret = -EIO;
}
udelay(400);
return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
}
int
nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
uint8_t write_byte, uint8_t *read_byte)
{
struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter;
struct drm_device *dev = auxch->dev;
int ret = 0, cmd, addr = algo_data->address;
uint8_t *buf;
if (mode == MODE_I2C_READ) {
cmd = AUX_I2C_READ;
buf = read_byte;
} else {
cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE;
buf = &write_byte;
}
if (!(mode & MODE_I2C_STOP))
cmd |= AUX_I2C_MOT;
if (mode & MODE_I2C_START)
return 1;
for (;;) {
ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1);
if (ret < 0)
return ret;
switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
case NV50_AUXCH_STAT_REPLY_I2C_ACK:
return 1;
case NV50_AUXCH_STAT_REPLY_I2C_NACK:
return -EREMOTEIO;
case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
udelay(100);
break;
default:
NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret);
return -EREMOTEIO;
}
}
}

View File

@ -0,0 +1,405 @@
/*
* Copyright 2005 Stephane Marchesin.
* 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
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#include <linux/console.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include "nouveau_fb.h"
#include "nouveau_fbcon.h"
#include "nv50_display.h"
#include "drm_pciids.h"
MODULE_PARM_DESC(noagp, "Disable AGP");
int nouveau_noagp;
module_param_named(noagp, nouveau_noagp, int, 0400);
MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
static int nouveau_modeset = -1; /* kms */
module_param_named(modeset, nouveau_modeset, int, 0400);
MODULE_PARM_DESC(vbios, "Override default VBIOS location");
char *nouveau_vbios;
module_param_named(vbios, nouveau_vbios, charp, 0400);
MODULE_PARM_DESC(vram_pushbuf, "Force DMA push buffers to be in VRAM");
int nouveau_vram_pushbuf;
module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400);
MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM");
int nouveau_vram_notify;
module_param_named(vram_notify, nouveau_vram_notify, int, 0400);
MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)");
int nouveau_duallink = 1;
module_param_named(duallink, nouveau_duallink, int, 0400);
MODULE_PARM_DESC(uscript_lvds, "LVDS output script table ID (>=GeForce 8)");
int nouveau_uscript_lvds = -1;
module_param_named(uscript_lvds, nouveau_uscript_lvds, int, 0400);
MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)");
int nouveau_uscript_tmds = -1;
module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400);
MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
"\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
"\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
"\t\tDefault: PAL\n"
"\t\t*NOTE* Ignored for cards with external TV encoders.");
char *nouveau_tv_norm;
module_param_named(tv_norm, nouveau_tv_norm, charp, 0400);
MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n"
"\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n"
"\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n"
"\t\t0x100 vgaattr, 0x200 EVO (G80+). ");
int nouveau_reg_debug;
module_param_named(reg_debug, nouveau_reg_debug, int, 0600);
int nouveau_fbpercrtc;
#if 0
module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
#endif
static struct pci_device_id pciidlist[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
.class = PCI_BASE_CLASS_DISPLAY << 16,
.class_mask = 0xff << 16,
},
{
PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),
.class = PCI_BASE_CLASS_DISPLAY << 16,
.class_mask = 0xff << 16,
},
{}
};
MODULE_DEVICE_TABLE(pci, pciidlist);
static struct drm_driver driver;
static int __devinit
nouveau_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
return drm_get_dev(pdev, ent, &driver);
}
static void
nouveau_pci_remove(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
drm_put_dev(dev);
}
static int
nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan;
struct drm_crtc *crtc;
uint32_t fbdev_flags;
int ret, i;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
if (pm_state.event == PM_EVENT_PRETHAW)
return 0;
fbdev_flags = dev_priv->fbdev_info->flags;
dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
nouveau_fb = nouveau_framebuffer(crtc->fb);
if (!nouveau_fb || !nouveau_fb->nvbo)
continue;
nouveau_bo_unpin(nouveau_fb->nvbo);
}
NV_INFO(dev, "Evicting buffers...\n");
ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
NV_INFO(dev, "Idling channels...\n");
for (i = 0; i < pfifo->channels; i++) {
struct nouveau_fence *fence = NULL;
chan = dev_priv->fifos[i];
if (!chan || (dev_priv->card_type >= NV_50 &&
chan == dev_priv->fifos[0]))
continue;
ret = nouveau_fence_new(chan, &fence, true);
if (ret == 0) {
ret = nouveau_fence_wait(fence, NULL, false, false);
nouveau_fence_unref((void *)&fence);
}
if (ret) {
NV_ERROR(dev, "Failed to idle channel %d for suspend\n",
chan->id);
}
}
pgraph->fifo_access(dev, false);
nouveau_wait_for_idle(dev);
pfifo->reassign(dev, false);
pfifo->disable(dev);
pfifo->unload_context(dev);
pgraph->unload_context(dev);
NV_INFO(dev, "Suspending GPU objects...\n");
ret = nouveau_gpuobj_suspend(dev);
if (ret) {
NV_ERROR(dev, "... failed: %d\n", ret);
goto out_abort;
}
ret = pinstmem->suspend(dev);
if (ret) {
NV_ERROR(dev, "... failed: %d\n", ret);
nouveau_gpuobj_suspend_cleanup(dev);
goto out_abort;
}
NV_INFO(dev, "And we're gone!\n");
pci_save_state(pdev);
if (pm_state.event == PM_EVENT_SUSPEND) {
pci_disable_device(pdev);
pci_set_power_state(pdev, PCI_D3hot);
}
acquire_console_sem();
fb_set_suspend(dev_priv->fbdev_info, 1);
release_console_sem();
dev_priv->fbdev_info->flags = fbdev_flags;
return 0;
out_abort:
NV_INFO(dev, "Re-enabling acceleration..\n");
pfifo->enable(dev);
pfifo->reassign(dev, true);
pgraph->fifo_access(dev, true);
return ret;
}
static int
nouveau_pci_resume(struct pci_dev *pdev)
{
struct drm_device *dev = pci_get_drvdata(pdev);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
struct drm_crtc *crtc;
uint32_t fbdev_flags;
int ret, i;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -ENODEV;
fbdev_flags = dev_priv->fbdev_info->flags;
dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
NV_INFO(dev, "We're back, enabling device...\n");
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
if (pci_enable_device(pdev))
return -1;
pci_set_master(dev->pdev);
NV_INFO(dev, "POSTing device...\n");
ret = nouveau_run_vbios_init(dev);
if (ret)
return ret;
if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
ret = nouveau_mem_init_agp(dev);
if (ret) {
NV_ERROR(dev, "error reinitialising AGP: %d\n", ret);
return ret;
}
}
NV_INFO(dev, "Reinitialising engines...\n");
engine->instmem.resume(dev);
engine->mc.init(dev);
engine->timer.init(dev);
engine->fb.init(dev);
engine->graph.init(dev);
engine->fifo.init(dev);
NV_INFO(dev, "Restoring GPU objects...\n");
nouveau_gpuobj_resume(dev);
nouveau_irq_postinstall(dev);
/* Re-write SKIPS, they'll have been lost over the suspend */
if (nouveau_vram_pushbuf) {
struct nouveau_channel *chan;
int j;
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
chan = dev_priv->fifos[i];
if (!chan)
continue;
for (j = 0; j < NOUVEAU_DMA_SKIPS; j++)
nouveau_bo_wr32(chan->pushbuf_bo, i, 0);
}
}
NV_INFO(dev, "Restoring mode...\n");
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
nouveau_fb = nouveau_framebuffer(crtc->fb);
if (!nouveau_fb || !nouveau_fb->nvbo)
continue;
nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM);
}
if (dev_priv->card_type < NV_50) {
nv04_display_restore(dev);
NVLockVgaCrtcs(dev, false);
} else
nv50_display_init(dev);
/* Force CLUT to get re-loaded during modeset */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
nv_crtc->lut.depth = 0;
}
acquire_console_sem();
fb_set_suspend(dev_priv->fbdev_info, 0);
release_console_sem();
nouveau_fbcon_zfill(dev);
drm_helper_resume_force_mode(dev);
dev_priv->fbdev_info->flags = fbdev_flags;
return 0;
}
static struct drm_driver driver = {
.driver_features =
DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,
.load = nouveau_load,
.firstopen = nouveau_firstopen,
.lastclose = nouveau_lastclose,
.unload = nouveau_unload,
.preclose = nouveau_preclose,
#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
.debugfs_init = nouveau_debugfs_init,
.debugfs_cleanup = nouveau_debugfs_takedown,
#endif
.irq_preinstall = nouveau_irq_preinstall,
.irq_postinstall = nouveau_irq_postinstall,
.irq_uninstall = nouveau_irq_uninstall,
.irq_handler = nouveau_irq_handler,
.reclaim_buffers = drm_core_reclaim_buffers,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = nouveau_ioctls,
.fops = {
.owner = THIS_MODULE,
.open = drm_open,
.release = drm_release,
.ioctl = drm_ioctl,
.mmap = nouveau_ttm_mmap,
.poll = drm_poll,
.fasync = drm_fasync,
#if defined(CONFIG_COMPAT)
.compat_ioctl = nouveau_compat_ioctl,
#endif
},
.pci_driver = {
.name = DRIVER_NAME,
.id_table = pciidlist,
.probe = nouveau_pci_probe,
.remove = nouveau_pci_remove,
.suspend = nouveau_pci_suspend,
.resume = nouveau_pci_resume
},
.gem_init_object = nouveau_gem_object_new,
.gem_free_object = nouveau_gem_object_del,
.name = DRIVER_NAME,
.desc = DRIVER_DESC,
#ifdef GIT_REVISION
.date = GIT_REVISION,
#else
.date = DRIVER_DATE,
#endif
.major = DRIVER_MAJOR,
.minor = DRIVER_MINOR,
.patchlevel = DRIVER_PATCHLEVEL,
};
static int __init nouveau_init(void)
{
driver.num_ioctls = nouveau_max_ioctl;
if (nouveau_modeset == -1) {
#ifdef CONFIG_VGA_CONSOLE
if (vgacon_text_force())
nouveau_modeset = 0;
else
#endif
nouveau_modeset = 1;
}
if (nouveau_modeset == 1)
driver.driver_features |= DRIVER_MODESET;
return drm_init(&driver);
}
static void __exit nouveau_exit(void)
{
drm_exit(&driver);
}
module_init(nouveau_init);
module_exit(nouveau_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL and additional rights");

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,91 @@
/*
* 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 __NOUVEAU_ENCODER_H__
#define __NOUVEAU_ENCODER_H__
#include "drm_encoder_slave.h"
#include "nouveau_drv.h"
#define NV_DPMS_CLEARED 0x80
struct nouveau_encoder {
struct drm_encoder_slave base;
struct dcb_entry *dcb;
int or;
struct drm_display_mode mode;
int last_dpms;
struct nv04_output_reg restore;
void (*disconnect)(struct nouveau_encoder *encoder);
union {
struct {
int dpcd_version;
int link_nr;
int link_bw;
} dp;
};
};
static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
{
struct drm_encoder_slave *slave = to_encoder_slave(enc);
return container_of(slave, struct nouveau_encoder, base);
}
static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
{
return &enc->base.base;
}
struct nouveau_connector *
nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
struct bit_displayport_encoder_table {
uint32_t match;
uint8_t record_nr;
uint8_t unknown;
uint16_t script0;
uint16_t script1;
uint16_t unknown_table;
} __attribute__ ((packed));
struct bit_displayport_encoder_table_entry {
uint8_t vs_level;
uint8_t pre_level;
uint8_t reg0;
uint8_t reg1;
uint8_t reg2;
} __attribute__ ((packed));
#endif /* __NOUVEAU_ENCODER_H__ */

View File

@ -0,0 +1,47 @@
/*
* 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 __NOUVEAU_FB_H__
#define __NOUVEAU_FB_H__
struct nouveau_framebuffer {
struct drm_framebuffer base;
struct nouveau_bo *nvbo;
};
static inline struct nouveau_framebuffer *
nouveau_framebuffer(struct drm_framebuffer *fb)
{
return container_of(fb, struct nouveau_framebuffer, base);
}
extern const struct drm_mode_config_funcs nouveau_mode_config_funcs;
struct drm_framebuffer *
nouveau_framebuffer_create(struct drm_device *, struct nouveau_bo *,
struct drm_mode_fb_cmd *);
#endif /* __NOUVEAU_FB_H__ */

View File

@ -0,0 +1,380 @@
/*
* Copyright © 2007 David Airlie
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* Authors:
* David Airlie
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/slab.h>
#include <linux/sysrq.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/screen_info.h>
#include "drmP.h"
#include "drm.h"
#include "drm_crtc.h"
#include "drm_crtc_helper.h"
#include "drm_fb_helper.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_crtc.h"
#include "nouveau_fb.h"
#include "nouveau_fbcon.h"
#include "nouveau_dma.h"
static int
nouveau_fbcon_sync(struct fb_info *info)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
int ret, i;
if (!chan->accel_done ||
info->state != FBINFO_STATE_RUNNING ||
info->flags & FBINFO_HWACCEL_DISABLED)
return 0;
if (RING_SPACE(chan, 4)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
return 0;
}
BEGIN_RING(chan, 0, 0x0104, 1);
OUT_RING(chan, 0);
BEGIN_RING(chan, 0, 0x0100, 1);
OUT_RING(chan, 0);
nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
FIRE_RING(chan);
ret = -EBUSY;
for (i = 0; i < 100000; i++) {
if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) {
ret = 0;
break;
}
DRM_UDELAY(1);
}
if (ret) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
return 0;
}
chan->accel_done = false;
return 0;
}
static struct fb_ops nouveau_fbcon_ops = {
.owner = THIS_MODULE,
.fb_check_var = drm_fb_helper_check_var,
.fb_set_par = drm_fb_helper_set_par,
.fb_setcolreg = drm_fb_helper_setcolreg,
.fb_fillrect = cfb_fillrect,
.fb_copyarea = cfb_copyarea,
.fb_imageblit = cfb_imageblit,
.fb_sync = nouveau_fbcon_sync,
.fb_pan_display = drm_fb_helper_pan_display,
.fb_blank = drm_fb_helper_blank,
.fb_setcmap = drm_fb_helper_setcmap,
};
static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
u16 blue, int regno)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
nv_crtc->lut.r[regno] = red;
nv_crtc->lut.g[regno] = green;
nv_crtc->lut.b[regno] = blue;
}
static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
u16 *blue, int regno)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
*red = nv_crtc->lut.r[regno];
*green = nv_crtc->lut.g[regno];
*blue = nv_crtc->lut.b[regno];
}
static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
.gamma_set = nouveau_fbcon_gamma_set,
.gamma_get = nouveau_fbcon_gamma_get
};
#if defined(__i386__) || defined(__x86_64__)
static bool
nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev)
{
struct pci_dev *pdev = dev->pdev;
int ramin;
if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB &&
screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
return false;
if (screen_info.lfb_base < pci_resource_start(pdev, 1))
goto not_fb;
if (screen_info.lfb_base + screen_info.lfb_size >=
pci_resource_start(pdev, 1) + pci_resource_len(pdev, 1))
goto not_fb;
return true;
not_fb:
ramin = 2;
if (pci_resource_len(pdev, ramin) == 0) {
ramin = 3;
if (pci_resource_len(pdev, ramin) == 0)
return false;
}
if (screen_info.lfb_base < pci_resource_start(pdev, ramin))
return false;
if (screen_info.lfb_base + screen_info.lfb_size >=
pci_resource_start(pdev, ramin) + pci_resource_len(pdev, ramin))
return false;
return true;
}
#endif
void
nouveau_fbcon_zfill(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct fb_info *info = dev_priv->fbdev_info;
struct fb_fillrect rect;
/* Clear the entire fbcon. The drm will program every connector
* with it's preferred mode. If the sizes differ, one display will
* quite likely have garbage around the console.
*/
rect.dx = rect.dy = 0;
rect.width = info->var.xres_virtual;
rect.height = info->var.yres_virtual;
rect.color = 0;
rect.rop = ROP_COPY;
info->fbops->fb_fillrect(info, &rect);
}
static int
nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
uint32_t fb_height, uint32_t surface_width,
uint32_t surface_height, uint32_t surface_depth,
uint32_t surface_bpp, struct drm_framebuffer **pfb)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct fb_info *info;
struct nouveau_fbcon_par *par;
struct drm_framebuffer *fb;
struct nouveau_framebuffer *nouveau_fb;
struct nouveau_bo *nvbo;
struct drm_mode_fb_cmd mode_cmd;
struct device *device = &dev->pdev->dev;
int size, ret;
mode_cmd.width = surface_width;
mode_cmd.height = surface_height;
mode_cmd.bpp = surface_bpp;
mode_cmd.pitch = mode_cmd.width * (mode_cmd.bpp >> 3);
mode_cmd.pitch = ALIGN(mode_cmd.pitch, 256);
mode_cmd.depth = surface_depth;
size = mode_cmd.pitch * mode_cmd.height;
size = ALIGN(size, PAGE_SIZE);
ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, TTM_PL_FLAG_VRAM,
0, 0x0000, false, true, &nvbo);
if (ret) {
NV_ERROR(dev, "failed to allocate framebuffer\n");
goto out;
}
ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM);
if (ret) {
NV_ERROR(dev, "failed to pin fb: %d\n", ret);
nouveau_bo_ref(NULL, &nvbo);
goto out;
}
ret = nouveau_bo_map(nvbo);
if (ret) {
NV_ERROR(dev, "failed to map fb: %d\n", ret);
nouveau_bo_unpin(nvbo);
nouveau_bo_ref(NULL, &nvbo);
goto out;
}
mutex_lock(&dev->struct_mutex);
fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd);
if (!fb) {
ret = -ENOMEM;
NV_ERROR(dev, "failed to allocate fb.\n");
goto out_unref;
}
list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
nouveau_fb = nouveau_framebuffer(fb);
*pfb = fb;
info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device);
if (!info) {
ret = -ENOMEM;
goto out_unref;
}
par = info->par;
par->helper.funcs = &nouveau_fbcon_helper_funcs;
par->helper.dev = dev;
ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
if (ret)
goto out_unref;
dev_priv->fbdev_info = info;
strcpy(info->fix.id, "nouveaufb");
info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT;
info->fbops = &nouveau_fbcon_ops;
info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
dev_priv->vm_vram_base;
info->fix.smem_len = size;
info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo);
info->screen_size = size;
drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
/* FIXME: we really shouldn't expose mmio space at all */
info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
/* Set aperture base/size for vesafb takeover */
#if defined(__i386__) || defined(__x86_64__)
if (nouveau_fbcon_has_vesafb_or_efifb(dev)) {
/* Some NVIDIA VBIOS' are stupid and decide to put the
* framebuffer in the middle of the PRAMIN BAR for
* whatever reason. We need to know the exact lfb_base
* to get vesafb kicked off, and the only reliable way
* we have left is to find out lfb_base the same way
* vesafb did.
*/
info->aperture_base = screen_info.lfb_base;
info->aperture_size = screen_info.lfb_size;
if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB)
info->aperture_size *= 65536;
} else
#endif
{
info->aperture_base = info->fix.mmio_start;
info->aperture_size = info->fix.mmio_len;
}
info->pixmap.size = 64*1024;
info->pixmap.buf_align = 8;
info->pixmap.access_align = 32;
info->pixmap.flags = FB_PIXMAP_SYSTEM;
info->pixmap.scan_align = 1;
fb->fbdev = info;
par->nouveau_fb = nouveau_fb;
par->dev = dev;
switch (dev_priv->card_type) {
case NV_50:
nv50_fbcon_accel_init(info);
break;
default:
nv04_fbcon_accel_init(info);
break;
};
nouveau_fbcon_zfill(dev);
/* To allow resizeing without swapping buffers */
NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
nouveau_fb->base.width,
nouveau_fb->base.height,
nvbo->bo.offset, nvbo);
mutex_unlock(&dev->struct_mutex);
return 0;
out_unref:
mutex_unlock(&dev->struct_mutex);
out:
return ret;
}
int
nouveau_fbcon_probe(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create);
}
int
nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
{
struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb);
struct fb_info *info;
if (!fb)
return -EINVAL;
info = fb->fbdev;
if (info) {
struct nouveau_fbcon_par *par = info->par;
unregister_framebuffer(info);
nouveau_bo_unmap(nouveau_fb->nvbo);
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(nouveau_fb->nvbo->gem);
nouveau_fb->nvbo = NULL;
mutex_unlock(&dev->struct_mutex);
if (par)
drm_fb_helper_free(&par->helper);
framebuffer_release(info);
}
return 0;
}

View File

@ -0,0 +1,47 @@
/*
* 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 __NOUVEAU_FBCON_H__
#define __NOUVEAU_FBCON_H__
#include "drm_fb_helper.h"
struct nouveau_fbcon_par {
struct drm_fb_helper helper;
struct drm_device *dev;
struct nouveau_framebuffer *nouveau_fb;
};
int nouveau_fbcon_probe(struct drm_device *dev);
int nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb);
void nouveau_fbcon_restore(void);
void nouveau_fbcon_zfill(struct drm_device *dev);
int nv04_fbcon_accel_init(struct fb_info *info);
int nv50_fbcon_accel_init(struct fb_info *info);
#endif /* __NV50_FBCON_H__ */

View File

@ -0,0 +1,262 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
#define USE_REFCNT (dev_priv->card_type >= NV_10)
struct nouveau_fence {
struct nouveau_channel *channel;
struct kref refcount;
struct list_head entry;
uint32_t sequence;
bool signalled;
};
static inline struct nouveau_fence *
nouveau_fence(void *sync_obj)
{
return (struct nouveau_fence *)sync_obj;
}
static void
nouveau_fence_del(struct kref *ref)
{
struct nouveau_fence *fence =
container_of(ref, struct nouveau_fence, refcount);
kfree(fence);
}
void
nouveau_fence_update(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct list_head *entry, *tmp;
struct nouveau_fence *fence;
uint32_t sequence;
if (USE_REFCNT)
sequence = nvchan_rd32(chan, 0x48);
else
sequence = chan->fence.last_sequence_irq;
if (chan->fence.sequence_ack == sequence)
return;
chan->fence.sequence_ack = sequence;
list_for_each_safe(entry, tmp, &chan->fence.pending) {
fence = list_entry(entry, struct nouveau_fence, entry);
sequence = fence->sequence;
fence->signalled = true;
list_del(&fence->entry);
kref_put(&fence->refcount, nouveau_fence_del);
if (sequence == chan->fence.sequence_ack)
break;
}
}
int
nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,
bool emit)
{
struct nouveau_fence *fence;
int ret = 0;
fence = kzalloc(sizeof(*fence), GFP_KERNEL);
if (!fence)
return -ENOMEM;
kref_init(&fence->refcount);
fence->channel = chan;
if (emit)
ret = nouveau_fence_emit(fence);
if (ret)
nouveau_fence_unref((void *)&fence);
*pfence = fence;
return ret;
}
struct nouveau_channel *
nouveau_fence_channel(struct nouveau_fence *fence)
{
return fence ? fence->channel : NULL;
}
int
nouveau_fence_emit(struct nouveau_fence *fence)
{
struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
struct nouveau_channel *chan = fence->channel;
unsigned long flags;
int ret;
ret = RING_SPACE(chan, 2);
if (ret)
return ret;
if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
spin_lock_irqsave(&chan->fence.lock, flags);
nouveau_fence_update(chan);
spin_unlock_irqrestore(&chan->fence.lock, flags);
BUG_ON(chan->fence.sequence ==
chan->fence.sequence_ack - 1);
}
fence->sequence = ++chan->fence.sequence;
kref_get(&fence->refcount);
spin_lock_irqsave(&chan->fence.lock, flags);
list_add_tail(&fence->entry, &chan->fence.pending);
spin_unlock_irqrestore(&chan->fence.lock, flags);
BEGIN_RING(chan, NvSubM2MF, USE_REFCNT ? 0x0050 : 0x0150, 1);
OUT_RING(chan, fence->sequence);
FIRE_RING(chan);
return 0;
}
void
nouveau_fence_unref(void **sync_obj)
{
struct nouveau_fence *fence = nouveau_fence(*sync_obj);
if (fence)
kref_put(&fence->refcount, nouveau_fence_del);
*sync_obj = NULL;
}
void *
nouveau_fence_ref(void *sync_obj)
{
struct nouveau_fence *fence = nouveau_fence(sync_obj);
kref_get(&fence->refcount);
return sync_obj;
}
bool
nouveau_fence_signalled(void *sync_obj, void *sync_arg)
{
struct nouveau_fence *fence = nouveau_fence(sync_obj);
struct nouveau_channel *chan = fence->channel;
unsigned long flags;
if (fence->signalled)
return true;
spin_lock_irqsave(&chan->fence.lock, flags);
nouveau_fence_update(chan);
spin_unlock_irqrestore(&chan->fence.lock, flags);
return fence->signalled;
}
int
nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
{
unsigned long timeout = jiffies + (3 * DRM_HZ);
int ret = 0;
__set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
while (1) {
if (nouveau_fence_signalled(sync_obj, sync_arg))
break;
if (time_after_eq(jiffies, timeout)) {
ret = -EBUSY;
break;
}
if (lazy)
schedule_timeout(1);
if (intr && signal_pending(current)) {
ret = -ERESTART;
break;
}
}
__set_current_state(TASK_RUNNING);
return ret;
}
int
nouveau_fence_flush(void *sync_obj, void *sync_arg)
{
return 0;
}
void
nouveau_fence_handler(struct drm_device *dev, int channel)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = NULL;
if (channel >= 0 && channel < dev_priv->engine.fifo.channels)
chan = dev_priv->fifos[channel];
if (chan) {
spin_lock_irq(&chan->fence.lock);
nouveau_fence_update(chan);
spin_unlock_irq(&chan->fence.lock);
}
}
int
nouveau_fence_init(struct nouveau_channel *chan)
{
INIT_LIST_HEAD(&chan->fence.pending);
spin_lock_init(&chan->fence.lock);
return 0;
}
void
nouveau_fence_fini(struct nouveau_channel *chan)
{
struct list_head *entry, *tmp;
struct nouveau_fence *fence;
list_for_each_safe(entry, tmp, &chan->fence.pending) {
fence = list_entry(entry, struct nouveau_fence, entry);
fence->signalled = true;
list_del(&fence->entry);
kref_put(&fence->refcount, nouveau_fence_del);
}
}

View File

@ -0,0 +1,992 @@
/*
* Copyright (C) 2008 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_dma.h"
#define nouveau_gem_pushbuf_sync(chan) 0
int
nouveau_gem_object_new(struct drm_gem_object *gem)
{
return 0;
}
void
nouveau_gem_object_del(struct drm_gem_object *gem)
{
struct nouveau_bo *nvbo = gem->driver_private;
struct ttm_buffer_object *bo = &nvbo->bo;
if (!nvbo)
return;
nvbo->gem = NULL;
if (unlikely(nvbo->cpu_filp))
ttm_bo_synccpu_write_release(bo);
if (unlikely(nvbo->pin_refcnt)) {
nvbo->pin_refcnt = 1;
nouveau_bo_unpin(nvbo);
}
ttm_bo_unref(&bo);
}
int
nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
int size, int align, uint32_t flags, uint32_t tile_mode,
uint32_t tile_flags, bool no_vm, bool mappable,
struct nouveau_bo **pnvbo)
{
struct nouveau_bo *nvbo;
int ret;
ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode,
tile_flags, no_vm, mappable, pnvbo);
if (ret)
return ret;
nvbo = *pnvbo;
nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
if (!nvbo->gem) {
nouveau_bo_ref(NULL, pnvbo);
return -ENOMEM;
}
nvbo->bo.persistant_swap_storage = nvbo->gem->filp;
nvbo->gem->driver_private = nvbo;
return 0;
}
static int
nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep)
{
struct nouveau_bo *nvbo = nouveau_gem_object(gem);
if (nvbo->bo.mem.mem_type == TTM_PL_TT)
rep->domain = NOUVEAU_GEM_DOMAIN_GART;
else
rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
rep->offset = nvbo->bo.offset;
rep->map_handle = nvbo->mappable ? nvbo->bo.addr_space_offset : 0;
rep->tile_mode = nvbo->tile_mode;
rep->tile_flags = nvbo->tile_flags;
return 0;
}
static bool
nouveau_gem_tile_flags_valid(struct drm_device *dev, uint32_t tile_flags) {
switch (tile_flags) {
case 0x0000:
case 0x1800:
case 0x2800:
case 0x4800:
case 0x7000:
case 0x7400:
case 0x7a00:
case 0xe000:
break;
default:
NV_ERROR(dev, "bad page flags: 0x%08x\n", tile_flags);
return false;
}
return true;
}
int
nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_nouveau_gem_new *req = data;
struct nouveau_bo *nvbo = NULL;
struct nouveau_channel *chan = NULL;
uint32_t flags = 0;
int ret = 0;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping;
if (req->channel_hint) {
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel_hint,
file_priv, chan);
}
if (req->info.domain & NOUVEAU_GEM_DOMAIN_VRAM)
flags |= TTM_PL_FLAG_VRAM;
if (req->info.domain & NOUVEAU_GEM_DOMAIN_GART)
flags |= TTM_PL_FLAG_TT;
if (!flags || req->info.domain & NOUVEAU_GEM_DOMAIN_CPU)
flags |= TTM_PL_FLAG_SYSTEM;
if (!nouveau_gem_tile_flags_valid(dev, req->info.tile_flags))
return -EINVAL;
ret = nouveau_gem_new(dev, chan, req->info.size, req->align, flags,
req->info.tile_mode, req->info.tile_flags, false,
(req->info.domain & NOUVEAU_GEM_DOMAIN_MAPPABLE),
&nvbo);
if (ret)
return ret;
ret = nouveau_gem_info(nvbo->gem, &req->info);
if (ret)
goto out;
ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
out:
mutex_lock(&dev->struct_mutex);
drm_gem_object_handle_unreference(nvbo->gem);
mutex_unlock(&dev->struct_mutex);
if (ret)
drm_gem_object_unreference(nvbo->gem);
return ret;
}
static int
nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
uint32_t write_domains, uint32_t valid_domains)
{
struct nouveau_bo *nvbo = gem->driver_private;
struct ttm_buffer_object *bo = &nvbo->bo;
uint64_t flags;
if (!valid_domains || (!read_domains && !write_domains))
return -EINVAL;
if (write_domains) {
if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
(write_domains & NOUVEAU_GEM_DOMAIN_VRAM))
flags = TTM_PL_FLAG_VRAM;
else
if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
(write_domains & NOUVEAU_GEM_DOMAIN_GART))
flags = TTM_PL_FLAG_TT;
else
return -EINVAL;
} else {
if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
(read_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
bo->mem.mem_type == TTM_PL_VRAM)
flags = TTM_PL_FLAG_VRAM;
else
if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
(read_domains & NOUVEAU_GEM_DOMAIN_GART) &&
bo->mem.mem_type == TTM_PL_TT)
flags = TTM_PL_FLAG_TT;
else
if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
(read_domains & NOUVEAU_GEM_DOMAIN_VRAM))
flags = TTM_PL_FLAG_VRAM;
else
flags = TTM_PL_FLAG_TT;
}
nouveau_bo_placement_set(nvbo, flags);
return 0;
}
struct validate_op {
struct nouveau_fence *fence;
struct list_head vram_list;
struct list_head gart_list;
struct list_head both_list;
};
static void
validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
{
struct list_head *entry, *tmp;
struct nouveau_bo *nvbo;
list_for_each_safe(entry, tmp, list) {
nvbo = list_entry(entry, struct nouveau_bo, entry);
if (likely(fence)) {
struct nouveau_fence *prev_fence;
spin_lock(&nvbo->bo.lock);
prev_fence = nvbo->bo.sync_obj;
nvbo->bo.sync_obj = nouveau_fence_ref(fence);
spin_unlock(&nvbo->bo.lock);
nouveau_fence_unref((void *)&prev_fence);
}
list_del(&nvbo->entry);
nvbo->reserved_by = NULL;
ttm_bo_unreserve(&nvbo->bo);
drm_gem_object_unreference(nvbo->gem);
}
}
static void
validate_fini(struct validate_op *op, bool success)
{
struct nouveau_fence *fence = op->fence;
if (unlikely(!success))
op->fence = NULL;
validate_fini_list(&op->vram_list, op->fence);
validate_fini_list(&op->gart_list, op->fence);
validate_fini_list(&op->both_list, op->fence);
nouveau_fence_unref((void *)&fence);
}
static int
validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
struct drm_nouveau_gem_pushbuf_bo *pbbo,
int nr_buffers, struct validate_op *op)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t sequence;
int trycnt = 0;
int ret, i;
sequence = atomic_add_return(1, &dev_priv->ttm.validate_sequence);
retry:
if (++trycnt > 100000) {
NV_ERROR(dev, "%s failed and gave up.\n", __func__);
return -EINVAL;
}
for (i = 0; i < nr_buffers; i++) {
struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[i];
struct drm_gem_object *gem;
struct nouveau_bo *nvbo;
gem = drm_gem_object_lookup(dev, file_priv, b->handle);
if (!gem) {
NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
validate_fini(op, NULL);
return -EINVAL;
}
nvbo = gem->driver_private;
if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
NV_ERROR(dev, "multiple instances of buffer %d on "
"validation list\n", b->handle);
validate_fini(op, NULL);
return -EINVAL;
}
ret = ttm_bo_reserve(&nvbo->bo, false, false, true, sequence);
if (ret) {
validate_fini(op, NULL);
if (ret == -EAGAIN)
ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
drm_gem_object_unreference(gem);
if (ret)
return ret;
goto retry;
}
nvbo->reserved_by = file_priv;
nvbo->pbbo_index = i;
if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
(b->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
list_add_tail(&nvbo->entry, &op->both_list);
else
if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
list_add_tail(&nvbo->entry, &op->vram_list);
else
if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
list_add_tail(&nvbo->entry, &op->gart_list);
else {
NV_ERROR(dev, "invalid valid domains: 0x%08x\n",
b->valid_domains);
validate_fini(op, NULL);
return -EINVAL;
}
if (unlikely(atomic_read(&nvbo->bo.cpu_writers) > 0)) {
validate_fini(op, NULL);
if (nvbo->cpu_filp == file_priv) {
NV_ERROR(dev, "bo %p mapped by process trying "
"to validate it!\n", nvbo);
return -EINVAL;
}
ret = ttm_bo_wait_cpu(&nvbo->bo, false);
if (ret == -ERESTART)
ret = -EAGAIN;
if (ret)
return ret;
goto retry;
}
}
return 0;
}
static int
validate_list(struct nouveau_channel *chan, struct list_head *list,
struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
{
struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
(void __force __user *)(uintptr_t)user_pbbo_ptr;
struct nouveau_bo *nvbo;
int ret, relocs = 0;
list_for_each_entry(nvbo, list, entry) {
struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index];
struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
if (prev_fence && nouveau_fence_channel(prev_fence) != chan) {
spin_lock(&nvbo->bo.lock);
ret = ttm_bo_wait(&nvbo->bo, false, false, false);
spin_unlock(&nvbo->bo.lock);
if (unlikely(ret))
return ret;
}
ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
b->write_domains,
b->valid_domains);
if (unlikely(ret))
return ret;
nvbo->channel = chan;
ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement,
false, false);
nvbo->channel = NULL;
if (unlikely(ret))
return ret;
if (nvbo->bo.offset == b->presumed_offset &&
((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
(nvbo->bo.mem.mem_type == TTM_PL_TT &&
b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART)))
continue;
if (nvbo->bo.mem.mem_type == TTM_PL_TT)
b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART;
else
b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM;
b->presumed_offset = nvbo->bo.offset;
b->presumed_ok = 0;
relocs++;
if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b)))
return -EFAULT;
}
return relocs;
}
static int
nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
struct drm_file *file_priv,
struct drm_nouveau_gem_pushbuf_bo *pbbo,
uint64_t user_buffers, int nr_buffers,
struct validate_op *op, int *apply_relocs)
{
int ret, relocs = 0;
INIT_LIST_HEAD(&op->vram_list);
INIT_LIST_HEAD(&op->gart_list);
INIT_LIST_HEAD(&op->both_list);
ret = nouveau_fence_new(chan, &op->fence, false);
if (ret)
return ret;
if (nr_buffers == 0)
return 0;
ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
if (unlikely(ret))
return ret;
ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
if (unlikely(ret < 0)) {
validate_fini(op, NULL);
return ret;
}
relocs += ret;
ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
if (unlikely(ret < 0)) {
validate_fini(op, NULL);
return ret;
}
relocs += ret;
ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
if (unlikely(ret < 0)) {
validate_fini(op, NULL);
return ret;
}
relocs += ret;
*apply_relocs = relocs;
return 0;
}
static inline void *
u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
{
void *mem;
void __user *userptr = (void __force __user *)(uintptr_t)user;
mem = kmalloc(nmemb * size, GFP_KERNEL);
if (!mem)
return ERR_PTR(-ENOMEM);
if (DRM_COPY_FROM_USER(mem, userptr, nmemb * size)) {
kfree(mem);
return ERR_PTR(-EFAULT);
}
return mem;
}
static int
nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo,
struct drm_nouveau_gem_pushbuf_bo *bo,
int nr_relocs, uint64_t ptr_relocs,
int nr_dwords, int first_dword,
uint32_t *pushbuf, bool is_iomem)
{
struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
struct drm_device *dev = chan->dev;
int ret = 0, i;
reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc));
if (IS_ERR(reloc))
return PTR_ERR(reloc);
for (i = 0; i < nr_relocs; i++) {
struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i];
struct drm_nouveau_gem_pushbuf_bo *b;
uint32_t data;
if (r->bo_index >= nr_bo || r->reloc_index < first_dword ||
r->reloc_index >= first_dword + nr_dwords) {
NV_ERROR(dev, "Bad relocation %d\n", i);
NV_ERROR(dev, " bo: %d max %d\n", r->bo_index, nr_bo);
NV_ERROR(dev, " id: %d max %d\n", r->reloc_index, nr_dwords);
ret = -EINVAL;
break;
}
b = &bo[r->bo_index];
if (b->presumed_ok)
continue;
if (r->flags & NOUVEAU_GEM_RELOC_LOW)
data = b->presumed_offset + r->data;
else
if (r->flags & NOUVEAU_GEM_RELOC_HIGH)
data = (b->presumed_offset + r->data) >> 32;
else
data = r->data;
if (r->flags & NOUVEAU_GEM_RELOC_OR) {
if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART)
data |= r->tor;
else
data |= r->vor;
}
if (is_iomem)
iowrite32_native(data, (void __force __iomem *)
&pushbuf[r->reloc_index]);
else
pushbuf[r->reloc_index] = data;
}
kfree(reloc);
return ret;
}
int
nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gem_pushbuf *req = data;
struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
struct nouveau_channel *chan;
struct validate_op op;
uint32_t *pushbuf = NULL;
int ret = 0, do_reloc = 0, i;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
if (req->nr_dwords >= chan->dma.max ||
req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
NV_ERROR(dev, " dwords : %d max %d\n", req->nr_dwords,
chan->dma.max - 1);
NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers,
NOUVEAU_GEM_MAX_BUFFERS);
NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs,
NOUVEAU_GEM_MAX_RELOCS);
return -EINVAL;
}
pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t));
if (IS_ERR(pushbuf))
return PTR_ERR(pushbuf);
bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
if (IS_ERR(bo)) {
kfree(pushbuf);
return PTR_ERR(bo);
}
mutex_lock(&dev->struct_mutex);
/* Validate buffer list */
ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
req->nr_buffers, &op, &do_reloc);
if (ret)
goto out;
/* Apply any relocations that are required */
if (do_reloc) {
ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers,
bo, req->nr_relocs,
req->relocs,
req->nr_dwords, 0,
pushbuf, false);
if (ret)
goto out;
}
/* Emit push buffer to the hw
*/
ret = RING_SPACE(chan, req->nr_dwords);
if (ret)
goto out;
OUT_RINGp(chan, pushbuf, req->nr_dwords);
ret = nouveau_fence_emit(op.fence);
if (ret) {
NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
WIND_RING(chan);
goto out;
}
if (nouveau_gem_pushbuf_sync(chan)) {
ret = nouveau_fence_wait(op.fence, NULL, false, false);
if (ret) {
for (i = 0; i < req->nr_dwords; i++)
NV_ERROR(dev, "0x%08x\n", pushbuf[i]);
NV_ERROR(dev, "^^ above push buffer is fail :(\n");
}
}
out:
validate_fini(&op, ret == 0);
mutex_unlock(&dev->struct_mutex);
kfree(pushbuf);
kfree(bo);
return ret;
}
#define PUSHBUF_CAL (dev_priv->card_type >= NV_20)
int
nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_nouveau_gem_pushbuf_call *req = data;
struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
struct nouveau_channel *chan;
struct drm_gem_object *gem;
struct nouveau_bo *pbbo;
struct validate_op op;
int i, ret = 0, do_reloc = 0;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
if (unlikely(req->handle == 0))
goto out_next;
if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers,
NOUVEAU_GEM_MAX_BUFFERS);
NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs,
NOUVEAU_GEM_MAX_RELOCS);
return -EINVAL;
}
bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
if (IS_ERR(bo))
return PTR_ERR(bo);
mutex_lock(&dev->struct_mutex);
/* Validate buffer list */
ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
req->nr_buffers, &op, &do_reloc);
if (ret) {
NV_ERROR(dev, "validate: %d\n", ret);
goto out;
}
/* Validate DMA push buffer */
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem) {
NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle);
ret = -EINVAL;
goto out;
}
pbbo = nouveau_gem_object(gem);
ret = ttm_bo_reserve(&pbbo->bo, false, false, true,
chan->fence.sequence);
if (ret) {
NV_ERROR(dev, "resv pb: %d\n", ret);
drm_gem_object_unreference(gem);
goto out;
}
nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type);
ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false);
if (ret) {
NV_ERROR(dev, "validate pb: %d\n", ret);
ttm_bo_unreserve(&pbbo->bo);
drm_gem_object_unreference(gem);
goto out;
}
list_add_tail(&pbbo->entry, &op.both_list);
/* If presumed return address doesn't match, we need to map the
* push buffer and fix it..
*/
if (!PUSHBUF_CAL) {
uint32_t retaddy;
if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) {
ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS);
if (ret) {
NV_ERROR(dev, "jmp_space: %d\n", ret);
goto out;
}
}
retaddy = chan->pushbuf_base + ((chan->dma.cur + 2) << 2);
retaddy |= 0x20000000;
if (retaddy != req->suffix0) {
req->suffix0 = retaddy;
do_reloc = 1;
}
}
/* Apply any relocations that are required */
if (do_reloc) {
void *pbvirt;
bool is_iomem;
ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages,
&pbbo->kmap);
if (ret) {
NV_ERROR(dev, "kmap pb: %d\n", ret);
goto out;
}
pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem);
ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo,
req->nr_relocs,
req->relocs,
req->nr_dwords,
req->offset / 4,
pbvirt, is_iomem);
if (!PUSHBUF_CAL) {
nouveau_bo_wr32(pbbo,
req->offset / 4 + req->nr_dwords - 2,
req->suffix0);
}
ttm_bo_kunmap(&pbbo->kmap);
if (ret) {
NV_ERROR(dev, "reloc apply: %d\n", ret);
goto out;
}
}
if (PUSHBUF_CAL) {
ret = RING_SPACE(chan, 2);
if (ret) {
NV_ERROR(dev, "cal_space: %d\n", ret);
goto out;
}
OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
req->offset) | 2);
OUT_RING(chan, 0);
} else {
ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS);
if (ret) {
NV_ERROR(dev, "jmp_space: %d\n", ret);
goto out;
}
OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
req->offset) | 0x20000000);
OUT_RING(chan, 0);
/* Space the jumps apart with NOPs. */
for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
OUT_RING(chan, 0);
}
ret = nouveau_fence_emit(op.fence);
if (ret) {
NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
WIND_RING(chan);
goto out;
}
out:
validate_fini(&op, ret == 0);
mutex_unlock(&dev->struct_mutex);
kfree(bo);
out_next:
if (PUSHBUF_CAL) {
req->suffix0 = 0x00020000;
req->suffix1 = 0x00000000;
} else {
req->suffix0 = 0x20000000 |
(chan->pushbuf_base + ((chan->dma.cur + 2) << 2));
req->suffix1 = 0x00000000;
}
return ret;
}
int
nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_nouveau_gem_pushbuf_call *req = data;
req->vram_available = dev_priv->fb_aper_free;
req->gart_available = dev_priv->gart_info.aper_free;
return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv);
}
static inline uint32_t
domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
{
uint32_t flags = 0;
if (domain & NOUVEAU_GEM_DOMAIN_VRAM)
flags |= TTM_PL_FLAG_VRAM;
if (domain & NOUVEAU_GEM_DOMAIN_GART)
flags |= TTM_PL_FLAG_TT;
return flags;
}
int
nouveau_gem_ioctl_pin(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gem_pin *req = data;
struct drm_gem_object *gem;
struct nouveau_bo *nvbo;
int ret = 0;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
NV_ERROR(dev, "pin only allowed without kernel modesetting\n");
return -EINVAL;
}
if (!DRM_SUSER(DRM_CURPROC))
return -EPERM;
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
return -EINVAL;
nvbo = nouveau_gem_object(gem);
ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain));
if (ret)
goto out;
req->offset = nvbo->bo.offset;
if (nvbo->bo.mem.mem_type == TTM_PL_TT)
req->domain = NOUVEAU_GEM_DOMAIN_GART;
else
req->domain = NOUVEAU_GEM_DOMAIN_VRAM;
out:
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&dev->struct_mutex);
return ret;
}
int
nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gem_pin *req = data;
struct drm_gem_object *gem;
int ret;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
if (drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
return -EINVAL;
ret = nouveau_bo_unpin(nouveau_gem_object(gem));
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&dev->struct_mutex);
return ret;
}
int
nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gem_cpu_prep *req = data;
struct drm_gem_object *gem;
struct nouveau_bo *nvbo;
bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
int ret = -EINVAL;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
return ret;
nvbo = nouveau_gem_object(gem);
if (nvbo->cpu_filp) {
if (nvbo->cpu_filp == file_priv)
goto out;
ret = ttm_bo_wait_cpu(&nvbo->bo, no_wait);
if (ret == -ERESTART)
ret = -EAGAIN;
if (ret)
goto out;
}
if (req->flags & NOUVEAU_GEM_CPU_PREP_NOBLOCK) {
ret = ttm_bo_wait(&nvbo->bo, false, false, no_wait);
} else {
ret = ttm_bo_synccpu_write_grab(&nvbo->bo, no_wait);
if (ret == -ERESTART)
ret = -EAGAIN;
else
if (ret == 0)
nvbo->cpu_filp = file_priv;
}
out:
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&dev->struct_mutex);
return ret;
}
int
nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gem_cpu_prep *req = data;
struct drm_gem_object *gem;
struct nouveau_bo *nvbo;
int ret = -EINVAL;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
return ret;
nvbo = nouveau_gem_object(gem);
if (nvbo->cpu_filp != file_priv)
goto out;
nvbo->cpu_filp = NULL;
ttm_bo_synccpu_write_release(&nvbo->bo);
ret = 0;
out:
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&dev->struct_mutex);
return ret;
}
int
nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_gem_info *req = data;
struct drm_gem_object *gem;
int ret;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
gem = drm_gem_object_lookup(dev, file_priv, req->handle);
if (!gem)
return -EINVAL;
ret = nouveau_gem_info(gem, req);
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&dev->struct_mutex);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,455 @@
/*
* Copyright 2008 Stuart Bennett
*
* 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 AUTHORS 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 __NOUVEAU_HW_H__
#define __NOUVEAU_HW_H__
#include "drmP.h"
#include "nouveau_drv.h"
#define MASK(field) ( \
(0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
#define XLATE(src, srclowbit, outfield) ( \
(((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield))
void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value);
uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index);
void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value);
uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index);
void NVSetOwner(struct drm_device *, int owner);
void NVBlankScreen(struct drm_device *, int head, bool blank);
void nouveau_hw_setpll(struct drm_device *, uint32_t reg1,
struct nouveau_pll_vals *pv);
int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype,
struct nouveau_pll_vals *pllvals);
int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals);
int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype);
void nouveau_hw_save_vga_fonts(struct drm_device *, bool save);
void nouveau_hw_save_state(struct drm_device *, int head,
struct nv04_mode_state *state);
void nouveau_hw_load_state(struct drm_device *, int head,
struct nv04_mode_state *state);
void nouveau_hw_load_state_palette(struct drm_device *, int head,
struct nv04_mode_state *state);
/* nouveau_calc.c */
extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
int *burst, int *lwm);
extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim,
int clk, struct nouveau_pll_vals *pv);
static inline uint32_t
nvReadMC(struct drm_device *dev, uint32_t reg)
{
uint32_t val = nv_rd32(dev, reg);
NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
return val;
}
static inline void
nvWriteMC(struct drm_device *dev, uint32_t reg, uint32_t val)
{
NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
nv_wr32(dev, reg, val);
}
static inline uint32_t
nvReadVIDEO(struct drm_device *dev, uint32_t reg)
{
uint32_t val = nv_rd32(dev, reg);
NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
return val;
}
static inline void
nvWriteVIDEO(struct drm_device *dev, uint32_t reg, uint32_t val)
{
NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
nv_wr32(dev, reg, val);
}
static inline uint32_t
nvReadFB(struct drm_device *dev, uint32_t reg)
{
uint32_t val = nv_rd32(dev, reg);
NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
return val;
}
static inline void
nvWriteFB(struct drm_device *dev, uint32_t reg, uint32_t val)
{
NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
nv_wr32(dev, reg, val);
}
static inline uint32_t
nvReadEXTDEV(struct drm_device *dev, uint32_t reg)
{
uint32_t val = nv_rd32(dev, reg);
NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
return val;
}
static inline void
nvWriteEXTDEV(struct drm_device *dev, uint32_t reg, uint32_t val)
{
NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
nv_wr32(dev, reg, val);
}
static inline uint32_t NVReadCRTC(struct drm_device *dev,
int head, uint32_t reg)
{
uint32_t val;
if (head)
reg += NV_PCRTC0_SIZE;
val = nv_rd32(dev, reg);
NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
return val;
}
static inline void NVWriteCRTC(struct drm_device *dev,
int head, uint32_t reg, uint32_t val)
{
if (head)
reg += NV_PCRTC0_SIZE;
NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
nv_wr32(dev, reg, val);
}
static inline uint32_t NVReadRAMDAC(struct drm_device *dev,
int head, uint32_t reg)
{
uint32_t val;
if (head)
reg += NV_PRAMDAC0_SIZE;
val = nv_rd32(dev, reg);
NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
head, reg, val);
return val;
}
static inline void NVWriteRAMDAC(struct drm_device *dev,
int head, uint32_t reg, uint32_t val)
{
if (head)
reg += NV_PRAMDAC0_SIZE;
NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
head, reg, val);
nv_wr32(dev, reg, val);
}
static inline uint8_t nv_read_tmds(struct drm_device *dev,
int or, int dl, uint8_t address)
{
int ramdac = (or & OUTPUT_C) >> 2;
NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8,
NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address);
return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8);
}
static inline void nv_write_tmds(struct drm_device *dev,
int or, int dl, uint8_t address,
uint8_t data)
{
int ramdac = (or & OUTPUT_C) >> 2;
NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data);
NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address);
}
static inline void NVWriteVgaCrtc(struct drm_device *dev,
int head, uint8_t index, uint8_t value)
{
NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
head, index, value);
nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
nv_wr08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value);
}
static inline uint8_t NVReadVgaCrtc(struct drm_device *dev,
int head, uint8_t index)
{
uint8_t val;
nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
val = nv_rd08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE);
NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
head, index, val);
return val;
}
/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58
* I suspect they in fact do nothing, but are merely a way to carry useful
* per-head variables around
*
* Known uses:
* CR57 CR58
* 0x00 index to the appropriate dcb entry (or 7f for inactive)
* 0x02 dcb entry's "or" value (or 00 for inactive)
* 0x03 bit0 set for dual link (LVDS, possibly elsewhere too)
* 0x08 or 0x09 pxclk in MHz
* 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap
* high nibble for xlat strap value
*/
static inline void
NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value)
{
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value);
}
static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index)
{
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58);
}
static inline uint8_t NVReadPRMVIO(struct drm_device *dev,
int head, uint32_t reg)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint8_t val;
/* Only NV4x have two pvio ranges; other twoHeads cards MUST call
* NVSetOwner for the relevant head to be programmed */
if (head && dev_priv->card_type == NV_40)
reg += NV_PRMVIO_SIZE;
val = nv_rd08(dev, reg);
NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n", head, reg, val);
return val;
}
static inline void NVWritePRMVIO(struct drm_device *dev,
int head, uint32_t reg, uint8_t value)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
/* Only NV4x have two pvio ranges; other twoHeads cards MUST call
* NVSetOwner for the relevant head to be programmed */
if (head && dev_priv->card_type == NV_40)
reg += NV_PRMVIO_SIZE;
NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n",
head, reg, value);
nv_wr08(dev, reg, value);
}
static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable)
{
nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20);
}
static inline bool NVGetEnablePalette(struct drm_device *dev, int head)
{
nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
return !(nv_rd08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20);
}
static inline void NVWriteVgaAttr(struct drm_device *dev,
int head, uint8_t index, uint8_t value)
{
if (NVGetEnablePalette(dev, head))
index &= ~0x20;
else
index |= 0x20;
nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
head, index, value);
nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
nv_wr08(dev, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value);
}
static inline uint8_t NVReadVgaAttr(struct drm_device *dev,
int head, uint8_t index)
{
uint8_t val;
if (NVGetEnablePalette(dev, head))
index &= ~0x20;
else
index |= 0x20;
nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
val = nv_rd08(dev, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE);
NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
head, index, val);
return val;
}
static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start)
{
NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3);
}
static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect)
{
uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
if (protect) {
NVVgaSeqReset(dev, head, true);
NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
} else {
/* Reenable sequencer, then turn on screen */
NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */
NVVgaSeqReset(dev, head, false);
}
NVSetEnablePalette(dev, head, protect);
}
static inline bool
nv_heads_tied(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->chipset == 0x11)
return !!(nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28));
return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4;
}
/* makes cr0-7 on the specified head read-only */
static inline bool
nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock)
{
uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX);
bool waslocked = cr11 & 0x80;
if (lock)
cr11 |= 0x80;
else
cr11 &= ~0x80;
NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11);
return waslocked;
}
static inline void
nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock)
{
/* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs
* bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB
* bit6: seems to have some effect on CR09 (double scan, VBS_9)
* bit5: unlocks HDE
* bit4: unlocks VDE
* bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR
* bit2: same as bit 1 of 0x60?804
* bit0: same as bit 0 of 0x60?804
*/
uint8_t cr21 = lock;
if (lock < 0)
/* 0xfa is generic "unlock all" mask */
cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa;
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21);
}
/* renders the extended crtc regs (cr19+) on all crtcs impervious:
* immutable and unreadable
*/
static inline bool
NVLockVgaCrtcs(struct drm_device *dev, bool lock)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX,
lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE);
/* NV11 has independently lockable extended crtcs, except when tied */
if (dev_priv->chipset == 0x11 && !nv_heads_tied(dev))
NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX,
lock ? NV_CIO_SR_LOCK_VALUE :
NV_CIO_SR_UNLOCK_RW_VALUE);
return waslocked;
}
/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */
#define NV04_CURSOR_SIZE 32
/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */
#define NV10_CURSOR_SIZE 64
static inline int nv_cursor_width(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
return dev_priv->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE;
}
static inline void
nv_fix_nv40_hw_cursor(struct drm_device *dev, int head)
{
/* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40,
* the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS
* for changes to the CRTC CURCTL regs to take effect, whether changing
* the pixmap location, or just showing/hiding the cursor
*/
uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos);
}
static inline void
nv_show_cursor(struct drm_device *dev, int head, bool show)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint8_t *curctl1 =
&dev_priv->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX];
if (show)
*curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
else
*curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1);
if (dev_priv->card_type == NV_40)
nv_fix_nv40_hw_cursor(dev, head);
}
static inline uint32_t
nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int mask;
if (bpp == 15)
bpp = 16;
if (bpp == 24)
bpp = 8;
/* Alignment requirements taken from the Haiku driver */
if (dev_priv->card_type == NV_04)
mask = 128 / bpp - 1;
else
mask = 512 / bpp - 1;
return (width + mask) & ~mask;
}
#endif /* __NOUVEAU_HW_H__ */

View File

@ -0,0 +1,269 @@
/*
* Copyright 2009 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_i2c.h"
#include "nouveau_hw.h"
static void
nv04_i2c_setscl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
}
static void
nv04_i2c_setsda(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
}
static int
nv04_i2c_getscl(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4);
}
static int
nv04_i2c_getsda(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8);
}
static void
nv4e_i2c_setscl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
nv_wr32(dev, i2c->wr, val | 0x01);
}
static void
nv4e_i2c_setsda(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
nv_wr32(dev, i2c->wr, val | 0x01);
}
static int
nv4e_i2c_getscl(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
}
static int
nv4e_i2c_getsda(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
}
static int
nv50_i2c_getscl(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(nv_rd32(dev, i2c->rd) & 1);
}
static int
nv50_i2c_getsda(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(nv_rd32(dev, i2c->rd) & 2);
}
static void
nv50_i2c_setscl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
}
static void
nv50_i2c_setsda(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
nv_wr32(dev, i2c->wr,
(nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0));
i2c->data = state;
}
static const uint32_t nv50_i2c_port[] = {
0x00e138, 0x00e150, 0x00e168, 0x00e180,
0x00e254, 0x00e274, 0x00e764, 0x00e780,
0x00e79c, 0x00e7b8
};
#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
int
nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *i2c;
int ret;
if (entry->chan)
return -EEXIST;
if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) {
NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
return -EINVAL;
}
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
if (i2c == NULL)
return -ENOMEM;
switch (entry->port_type) {
case 0:
i2c->algo.bit.setsda = nv04_i2c_setsda;
i2c->algo.bit.setscl = nv04_i2c_setscl;
i2c->algo.bit.getsda = nv04_i2c_getsda;
i2c->algo.bit.getscl = nv04_i2c_getscl;
i2c->rd = entry->read;
i2c->wr = entry->write;
break;
case 4:
i2c->algo.bit.setsda = nv4e_i2c_setsda;
i2c->algo.bit.setscl = nv4e_i2c_setscl;
i2c->algo.bit.getsda = nv4e_i2c_getsda;
i2c->algo.bit.getscl = nv4e_i2c_getscl;
i2c->rd = 0x600800 + entry->read;
i2c->wr = 0x600800 + entry->write;
break;
case 5:
i2c->algo.bit.setsda = nv50_i2c_setsda;
i2c->algo.bit.setscl = nv50_i2c_setscl;
i2c->algo.bit.getsda = nv50_i2c_getsda;
i2c->algo.bit.getscl = nv50_i2c_getscl;
i2c->rd = nv50_i2c_port[entry->read];
i2c->wr = i2c->rd;
break;
case 6:
i2c->rd = entry->read;
i2c->wr = entry->write;
break;
default:
NV_ERROR(dev, "DCB I2C port type %d unknown\n",
entry->port_type);
kfree(i2c);
return -EINVAL;
}
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
"nouveau-%s-%d", pci_name(dev->pdev), index);
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.dev.parent = &dev->pdev->dev;
i2c->dev = dev;
i2c_set_adapdata(&i2c->adapter, i2c);
if (entry->port_type < 6) {
i2c->adapter.algo_data = &i2c->algo.bit;
i2c->algo.bit.udelay = 40;
i2c->algo.bit.timeout = usecs_to_jiffies(5000);
i2c->algo.bit.data = i2c;
ret = i2c_bit_add_bus(&i2c->adapter);
} else {
i2c->adapter.algo_data = &i2c->algo.dp;
i2c->algo.dp.running = false;
i2c->algo.dp.address = 0;
i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch;
ret = i2c_dp_aux_add_bus(&i2c->adapter);
}
if (ret) {
NV_ERROR(dev, "Failed to register i2c %d\n", index);
kfree(i2c);
return ret;
}
entry->chan = i2c;
return 0;
}
void
nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry)
{
if (!entry->chan)
return;
i2c_del_adapter(&entry->chan->adapter);
kfree(entry->chan);
entry->chan = NULL;
}
struct nouveau_i2c_chan *
nouveau_i2c_find(struct drm_device *dev, int index)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->VBIOS;
if (index > DCB_MAX_NUM_I2C_ENTRIES)
return NULL;
if (!bios->bdcb.dcb.i2c[index].chan) {
if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index))
return NULL;
}
return bios->bdcb.dcb.i2c[index].chan;
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2009 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.
*/
#ifndef __NOUVEAU_I2C_H__
#define __NOUVEAU_I2C_H__
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/i2c-algo-bit.h>
#include "drm_dp_helper.h"
struct dcb_i2c_entry;
struct nouveau_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
union {
struct i2c_algo_bit_data bit;
struct i2c_algo_dp_aux_data dp;
} algo;
unsigned rd;
unsigned wr;
unsigned data;
};
int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
uint8_t *read_byte);
#endif /* __NOUVEAU_I2C_H__ */

View File

@ -0,0 +1,72 @@
/**
* \file mga_ioc32.c
*
* 32-bit ioctl compatibility routines for the MGA DRM.
*
* \author Dave Airlie <airlied@linux.ie> with code from patches by Egbert Eich
*
*
* Copyright (C) Paul Mackerras 2005
* Copyright (C) Egbert Eich 2003,2004
* Copyright (C) Dave Airlie 2005
* 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 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <linux/compat.h>
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
/**
* Called whenever a 32-bit process running under a 64-bit kernel
* performs an ioctl on /dev/dri/card<n>.
*
* \param filp file pointer.
* \param cmd command.
* \param arg user argument.
* \return zero on success or negative number on failure.
*/
long nouveau_compat_ioctl(struct file *filp, unsigned int cmd,
unsigned long arg)
{
unsigned int nr = DRM_IOCTL_NR(cmd);
drm_ioctl_compat_t *fn = NULL;
int ret;
if (nr < DRM_COMMAND_BASE)
return drm_compat_ioctl(filp, cmd, arg);
#if 0
if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE];
#endif
lock_kernel(); /* XXX for now */
if (fn != NULL)
ret = (*fn)(filp, cmd, arg);
else
ret = drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
unlock_kernel();
return ret;
}

View File

@ -0,0 +1,702 @@
/*
* Copyright (C) 2006 Ben Skeggs.
*
* 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.
*
*/
/*
* Authors:
* Ben Skeggs <darktama@iinet.net.au>
*/
#include "drmP.h"
#include "drm.h"
#include "nouveau_drm.h"
#include "nouveau_drv.h"
#include "nouveau_reg.h"
#include <linux/ratelimit.h>
/* needed for hotplug irq */
#include "nouveau_connector.h"
#include "nv50_display.h"
void
nouveau_irq_preinstall(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
/* Master disable */
nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
if (dev_priv->card_type == NV_50) {
INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
INIT_LIST_HEAD(&dev_priv->vbl_waiting);
}
}
int
nouveau_irq_postinstall(struct drm_device *dev)
{
/* Master enable */
nv_wr32(dev, NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE);
return 0;
}
void
nouveau_irq_uninstall(struct drm_device *dev)
{
/* Master disable */
nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
}
static int
nouveau_call_method(struct nouveau_channel *chan, int class, int mthd, int data)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct nouveau_pgraph_object_method *grm;
struct nouveau_pgraph_object_class *grc;
grc = dev_priv->engine.graph.grclass;
while (grc->id) {
if (grc->id == class)
break;
grc++;
}
if (grc->id != class || !grc->methods)
return -ENOENT;
grm = grc->methods;
while (grm->id) {
if (grm->id == mthd)
return grm->exec(chan, class, mthd, data);
grm++;
}
return -ENOENT;
}
static bool
nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data)
{
struct drm_device *dev = chan->dev;
const int subc = (addr >> 13) & 0x7;
const int mthd = addr & 0x1ffc;
if (mthd == 0x0000) {
struct nouveau_gpuobj_ref *ref = NULL;
if (nouveau_gpuobj_ref_find(chan, data, &ref))
return false;
if (ref->gpuobj->engine != NVOBJ_ENGINE_SW)
return false;
chan->sw_subchannel[subc] = ref->gpuobj->class;
nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev,
NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4));
return true;
}
/* hw object */
if (nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE) & (1 << (subc*4)))
return false;
if (nouveau_call_method(chan, chan->sw_subchannel[subc], mthd, data))
return false;
return true;
}
static void
nouveau_fifo_irq_handler(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
uint32_t status, reassign;
int cnt = 0;
reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1;
while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
struct nouveau_channel *chan = NULL;
uint32_t chid, get;
nv_wr32(dev, NV03_PFIFO_CACHES, 0);
chid = engine->fifo.channel_id(dev);
if (chid >= 0 && chid < engine->fifo.channels)
chan = dev_priv->fifos[chid];
get = nv_rd32(dev, NV03_PFIFO_CACHE1_GET);
if (status & NV_PFIFO_INTR_CACHE_ERROR) {
uint32_t mthd, data;
int ptr;
/* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
* wrapping on my G80 chips, but CACHE1 isn't big
* enough for this much data.. Tests show that it
* wraps around to the start at GET=0x800.. No clue
* as to why..
*/
ptr = (get & 0x7ff) >> 2;
if (dev_priv->card_type < NV_40) {
mthd = nv_rd32(dev,
NV04_PFIFO_CACHE1_METHOD(ptr));
data = nv_rd32(dev,
NV04_PFIFO_CACHE1_DATA(ptr));
} else {
mthd = nv_rd32(dev,
NV40_PFIFO_CACHE1_METHOD(ptr));
data = nv_rd32(dev,
NV40_PFIFO_CACHE1_DATA(ptr));
}
if (!chan || !nouveau_fifo_swmthd(chan, mthd, data)) {
NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d "
"Mthd 0x%04x Data 0x%08x\n",
chid, (mthd >> 13) & 7, mthd & 0x1ffc,
data);
}
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
nv_wr32(dev, NV03_PFIFO_INTR_0,
NV_PFIFO_INTR_CACHE_ERROR);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1);
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1);
nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH,
nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
status &= ~NV_PFIFO_INTR_CACHE_ERROR;
}
if (status & NV_PFIFO_INTR_DMA_PUSHER) {
NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid);
status &= ~NV_PFIFO_INTR_DMA_PUSHER;
nv_wr32(dev, NV03_PFIFO_INTR_0,
NV_PFIFO_INTR_DMA_PUSHER);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, 0x00000000);
if (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT) != get)
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET,
get + 4);
}
if (status) {
NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n",
status, chid);
nv_wr32(dev, NV03_PFIFO_INTR_0, status);
status = 0;
}
nv_wr32(dev, NV03_PFIFO_CACHES, reassign);
}
if (status) {
NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt);
nv_wr32(dev, 0x2140, 0);
nv_wr32(dev, 0x140, 0);
}
nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING);
}
struct nouveau_bitfield_names {
uint32_t mask;
const char *name;
};
static struct nouveau_bitfield_names nstatus_names[] =
{
{ NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
{ NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
{ NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
{ NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }
};
static struct nouveau_bitfield_names nstatus_names_nv10[] =
{
{ NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
{ NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
{ NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
{ NV10_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }
};
static struct nouveau_bitfield_names nsource_names[] =
{
{ NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" },
{ NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" },
{ NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" },
{ NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" },
{ NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" },
{ NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" },
{ NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" },
{ NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" },
{ NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" },
{ NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" },
{ NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" },
{ NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" },
{ NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" },
{ NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" },
{ NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" },
{ NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" },
{ NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" },
{ NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" },
{ NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" },
};
static void
nouveau_print_bitfield_names_(uint32_t value,
const struct nouveau_bitfield_names *namelist,
const int namelist_len)
{
/*
* Caller must have already printed the KERN_* log level for us.
* Also the caller is responsible for adding the newline.
*/
int i;
for (i = 0; i < namelist_len; ++i) {
uint32_t mask = namelist[i].mask;
if (value & mask) {
printk(" %s", namelist[i].name);
value &= ~mask;
}
}
if (value)
printk(" (unknown bits 0x%08x)", value);
}
#define nouveau_print_bitfield_names(val, namelist) \
nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist))
static int
nouveau_graph_chid_from_grctx(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t inst;
int i;
if (dev_priv->card_type < NV_40)
return dev_priv->engine.fifo.channels;
else
if (dev_priv->card_type < NV_50) {
inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 4;
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
struct nouveau_channel *chan = dev_priv->fifos[i];
if (!chan || !chan->ramin_grctx)
continue;
if (inst == chan->ramin_grctx->instance)
break;
}
} else {
inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 12;
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
struct nouveau_channel *chan = dev_priv->fifos[i];
if (!chan || !chan->ramin)
continue;
if (inst == chan->ramin->instance)
break;
}
}
return i;
}
static int
nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
int channel;
if (dev_priv->card_type < NV_10)
channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf;
else
if (dev_priv->card_type < NV_40)
channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
else
channel = nouveau_graph_chid_from_grctx(dev);
if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) {
NV_ERROR(dev, "AIII, invalid/inactive channel id %d\n", channel);
return -EINVAL;
}
*channel_ret = channel;
return 0;
}
struct nouveau_pgraph_trap {
int channel;
int class;
int subc, mthd, size;
uint32_t data, data2;
uint32_t nsource, nstatus;
};
static void
nouveau_graph_trap_info(struct drm_device *dev,
struct nouveau_pgraph_trap *trap)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t address;
trap->nsource = trap->nstatus = 0;
if (dev_priv->card_type < NV_50) {
trap->nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
trap->nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS);
}
if (nouveau_graph_trapped_channel(dev, &trap->channel))
trap->channel = -1;
address = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR);
trap->mthd = address & 0x1FFC;
trap->data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA);
if (dev_priv->card_type < NV_10) {
trap->subc = (address >> 13) & 0x7;
} else {
trap->subc = (address >> 16) & 0x7;
trap->data2 = nv_rd32(dev, NV10_PGRAPH_TRAPPED_DATA_HIGH);
}
if (dev_priv->card_type < NV_10)
trap->class = nv_rd32(dev, 0x400180 + trap->subc*4) & 0xFF;
else if (dev_priv->card_type < NV_40)
trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFF;
else if (dev_priv->card_type < NV_50)
trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFFF;
else
trap->class = nv_rd32(dev, 0x400814);
}
static void
nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id,
struct nouveau_pgraph_trap *trap)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t nsource = trap->nsource, nstatus = trap->nstatus;
NV_INFO(dev, "%s - nSource:", id);
nouveau_print_bitfield_names(nsource, nsource_names);
printk(", nStatus:");
if (dev_priv->card_type < NV_10)
nouveau_print_bitfield_names(nstatus, nstatus_names);
else
nouveau_print_bitfield_names(nstatus, nstatus_names_nv10);
printk("\n");
NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x "
"Data 0x%08x:0x%08x\n",
id, trap->channel, trap->subc,
trap->class, trap->mthd,
trap->data2, trap->data);
}
static int
nouveau_pgraph_intr_swmthd(struct drm_device *dev,
struct nouveau_pgraph_trap *trap)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (trap->channel < 0 ||
trap->channel >= dev_priv->engine.fifo.channels ||
!dev_priv->fifos[trap->channel])
return -ENODEV;
return nouveau_call_method(dev_priv->fifos[trap->channel],
trap->class, trap->mthd, trap->data);
}
static inline void
nouveau_pgraph_intr_notify(struct drm_device *dev, uint32_t nsource)
{
struct nouveau_pgraph_trap trap;
int unhandled = 0;
nouveau_graph_trap_info(dev, &trap);
if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
if (nouveau_pgraph_intr_swmthd(dev, &trap))
unhandled = 1;
} else {
unhandled = 1;
}
if (unhandled)
nouveau_graph_dump_trap_info(dev, "PGRAPH_NOTIFY", &trap);
}
static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20);
static int nouveau_ratelimit(void)
{
return __ratelimit(&nouveau_ratelimit_state);
}
static inline void
nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource)
{
struct nouveau_pgraph_trap trap;
int unhandled = 0;
nouveau_graph_trap_info(dev, &trap);
trap.nsource = nsource;
if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
if (nouveau_pgraph_intr_swmthd(dev, &trap))
unhandled = 1;
} else {
unhandled = 1;
}
if (unhandled && nouveau_ratelimit())
nouveau_graph_dump_trap_info(dev, "PGRAPH_ERROR", &trap);
}
static inline void
nouveau_pgraph_intr_context_switch(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
uint32_t chid;
chid = engine->fifo.channel_id(dev);
NV_DEBUG(dev, "PGRAPH context switch interrupt channel %x\n", chid);
switch (dev_priv->card_type) {
case NV_04:
nv04_graph_context_switch(dev);
break;
case NV_10:
nv10_graph_context_switch(dev);
break;
default:
NV_ERROR(dev, "Context switch not implemented\n");
break;
}
}
static void
nouveau_pgraph_irq_handler(struct drm_device *dev)
{
uint32_t status;
while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
if (status & NV_PGRAPH_INTR_NOTIFY) {
nouveau_pgraph_intr_notify(dev, nsource);
status &= ~NV_PGRAPH_INTR_NOTIFY;
nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_NOTIFY);
}
if (status & NV_PGRAPH_INTR_ERROR) {
nouveau_pgraph_intr_error(dev, nsource);
status &= ~NV_PGRAPH_INTR_ERROR;
nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_ERROR);
}
if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
nouveau_pgraph_intr_context_switch(dev);
status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
nv_wr32(dev, NV03_PGRAPH_INTR,
NV_PGRAPH_INTR_CONTEXT_SWITCH);
}
if (status) {
NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status);
nv_wr32(dev, NV03_PGRAPH_INTR, status);
}
if ((nv_rd32(dev, NV04_PGRAPH_FIFO) & (1 << 0)) == 0)
nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
}
nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
}
static void
nv50_pgraph_irq_handler(struct drm_device *dev)
{
uint32_t status, nsource;
status = nv_rd32(dev, NV03_PGRAPH_INTR);
nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
if (status & 0x00000001) {
nouveau_pgraph_intr_notify(dev, nsource);
status &= ~0x00000001;
nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001);
}
if (status & 0x00000010) {
nouveau_pgraph_intr_error(dev, nsource |
NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD);
status &= ~0x00000010;
nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010);
}
if (status & 0x00001000) {
nv_wr32(dev, 0x400500, 0x00000000);
nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_CONTEXT_SWITCH);
nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
NV40_PGRAPH_INTR_EN) & ~NV_PGRAPH_INTR_CONTEXT_SWITCH);
nv_wr32(dev, 0x400500, 0x00010001);
nv50_graph_context_switch(dev);
status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
}
if (status & 0x00100000) {
nouveau_pgraph_intr_error(dev, nsource |
NV03_PGRAPH_NSOURCE_DATA_ERROR);
status &= ~0x00100000;
nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000);
}
if (status & 0x00200000) {
int r;
nouveau_pgraph_intr_error(dev, nsource |
NV03_PGRAPH_NSOURCE_PROTECTION_ERROR);
NV_ERROR(dev, "magic set 1:\n");
for (r = 0x408900; r <= 0x408910; r += 4)
NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
nv_wr32(dev, 0x408900, nv_rd32(dev, 0x408904) | 0xc0000000);
for (r = 0x408e08; r <= 0x408e24; r += 4)
NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
nv_wr32(dev, 0x408e08, nv_rd32(dev, 0x408e08) | 0xc0000000);
NV_ERROR(dev, "magic set 2:\n");
for (r = 0x409900; r <= 0x409910; r += 4)
NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
nv_wr32(dev, 0x409900, nv_rd32(dev, 0x409904) | 0xc0000000);
for (r = 0x409e08; r <= 0x409e24; r += 4)
NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r, nv_rd32(dev, r));
nv_wr32(dev, 0x409e08, nv_rd32(dev, 0x409e08) | 0xc0000000);
status &= ~0x00200000;
nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource);
nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000);
}
if (status) {
NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status);
nv_wr32(dev, NV03_PGRAPH_INTR, status);
}
{
const int isb = (1 << 16) | (1 << 0);
if ((nv_rd32(dev, 0x400500) & isb) != isb)
nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | isb);
}
nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
}
static void
nouveau_crtc_irq_handler(struct drm_device *dev, int crtc)
{
if (crtc & 1)
nv_wr32(dev, NV_CRTC0_INTSTAT, NV_CRTC_INTR_VBLANK);
if (crtc & 2)
nv_wr32(dev, NV_CRTC1_INTSTAT, NV_CRTC_INTR_VBLANK);
}
irqreturn_t
nouveau_irq_handler(DRM_IRQ_ARGS)
{
struct drm_device *dev = (struct drm_device *)arg;
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t status, fbdev_flags = 0;
status = nv_rd32(dev, NV03_PMC_INTR_0);
if (!status)
return IRQ_NONE;
if (dev_priv->fbdev_info) {
fbdev_flags = dev_priv->fbdev_info->flags;
dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (status & NV_PMC_INTR_0_PFIFO_PENDING) {
nouveau_fifo_irq_handler(dev);
status &= ~NV_PMC_INTR_0_PFIFO_PENDING;
}
if (status & NV_PMC_INTR_0_PGRAPH_PENDING) {
if (dev_priv->card_type >= NV_50)
nv50_pgraph_irq_handler(dev);
else
nouveau_pgraph_irq_handler(dev);
status &= ~NV_PMC_INTR_0_PGRAPH_PENDING;
}
if (status & NV_PMC_INTR_0_CRTCn_PENDING) {
nouveau_crtc_irq_handler(dev, (status>>24)&3);
status &= ~NV_PMC_INTR_0_CRTCn_PENDING;
}
if (status & (NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
NV_PMC_INTR_0_NV50_I2C_PENDING)) {
nv50_display_irq_handler(dev);
status &= ~(NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
NV_PMC_INTR_0_NV50_I2C_PENDING);
}
if (status)
NV_ERROR(dev, "Unhandled PMC INTR status bits 0x%08x\n", status);
if (dev_priv->fbdev_info)
dev_priv->fbdev_info->flags = fbdev_flags;
return IRQ_HANDLED;
}

View File

@ -0,0 +1,568 @@
/*
* Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
* Copyright 2005 Stephane Marchesin
*
* The Weather Channel (TM) funded Tungsten Graphics to develop the
* initial release of the Radeon 8500 driver under the XFree86 license.
* This notice must be preserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS AND/OR THEIR 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.
*
* Authors:
* Keith Whitwell <keith@tungstengraphics.com>
*/
#include "drmP.h"
#include "drm.h"
#include "drm_sarea.h"
#include "nouveau_drv.h"
static struct mem_block *
split_block(struct mem_block *p, uint64_t start, uint64_t size,
struct drm_file *file_priv)
{
/* Maybe cut off the start of an existing block */
if (start > p->start) {
struct mem_block *newblock =
kmalloc(sizeof(*newblock), GFP_KERNEL);
if (!newblock)
goto out;
newblock->start = start;
newblock->size = p->size - (start - p->start);
newblock->file_priv = NULL;
newblock->next = p->next;
newblock->prev = p;
p->next->prev = newblock;
p->next = newblock;
p->size -= newblock->size;
p = newblock;
}
/* Maybe cut off the end of an existing block */
if (size < p->size) {
struct mem_block *newblock =
kmalloc(sizeof(*newblock), GFP_KERNEL);
if (!newblock)
goto out;
newblock->start = start + size;
newblock->size = p->size - size;
newblock->file_priv = NULL;
newblock->next = p->next;
newblock->prev = p;
p->next->prev = newblock;
p->next = newblock;
p->size = size;
}
out:
/* Our block is in the middle */
p->file_priv = file_priv;
return p;
}
struct mem_block *
nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
int align2, struct drm_file *file_priv, int tail)
{
struct mem_block *p;
uint64_t mask = (1 << align2) - 1;
if (!heap)
return NULL;
if (tail) {
list_for_each_prev(p, heap) {
uint64_t start = ((p->start + p->size) - size) & ~mask;
if (p->file_priv == NULL && start >= p->start &&
start + size <= p->start + p->size)
return split_block(p, start, size, file_priv);
}
} else {
list_for_each(p, heap) {
uint64_t start = (p->start + mask) & ~mask;
if (p->file_priv == NULL &&
start + size <= p->start + p->size)
return split_block(p, start, size, file_priv);
}
}
return NULL;
}
void nouveau_mem_free_block(struct mem_block *p)
{
p->file_priv = NULL;
/* Assumes a single contiguous range. Needs a special file_priv in
* 'heap' to stop it being subsumed.
*/
if (p->next->file_priv == NULL) {
struct mem_block *q = p->next;
p->size += q->size;
p->next = q->next;
p->next->prev = p;
kfree(q);
}
if (p->prev->file_priv == NULL) {
struct mem_block *q = p->prev;
q->size += p->size;
q->next = p->next;
q->next->prev = q;
kfree(p);
}
}
/* Initialize. How to check for an uninitialized heap?
*/
int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start,
uint64_t size)
{
struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
if (!blocks)
return -ENOMEM;
*heap = kmalloc(sizeof(**heap), GFP_KERNEL);
if (!*heap) {
kfree(blocks);
return -ENOMEM;
}
blocks->start = start;
blocks->size = size;
blocks->file_priv = NULL;
blocks->next = blocks->prev = *heap;
memset(*heap, 0, sizeof(**heap));
(*heap)->file_priv = (struct drm_file *) -1;
(*heap)->next = (*heap)->prev = blocks;
return 0;
}
/*
* Free all blocks associated with the releasing file_priv
*/
void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap)
{
struct mem_block *p;
if (!heap || !heap->next)
return;
list_for_each(p, heap) {
if (p->file_priv == file_priv)
p->file_priv = NULL;
}
/* Assumes a single contiguous range. Needs a special file_priv in
* 'heap' to stop it being subsumed.
*/
list_for_each(p, heap) {
while ((p->file_priv == NULL) &&
(p->next->file_priv == NULL) &&
(p->next != heap)) {
struct mem_block *q = p->next;
p->size += q->size;
p->next = q->next;
p->next->prev = p;
kfree(q);
}
}
}
/*
* NV50 VM helpers
*/
int
nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
uint32_t flags, uint64_t phys)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj **pgt;
unsigned psz, pfl, pages;
if (virt >= dev_priv->vm_gart_base &&
(virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
psz = 12;
pgt = &dev_priv->gart_info.sg_ctxdma;
pfl = 0x21;
virt -= dev_priv->vm_gart_base;
} else
if (virt >= dev_priv->vm_vram_base &&
(virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
psz = 16;
pgt = dev_priv->vm_vram_pt;
pfl = 0x01;
virt -= dev_priv->vm_vram_base;
} else {
NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
virt, virt + size - 1);
return -EINVAL;
}
pages = size >> psz;
dev_priv->engine.instmem.prepare_access(dev, true);
if (flags & 0x80000000) {
while (pages--) {
struct nouveau_gpuobj *pt = pgt[virt >> 29];
unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
nv_wo32(dev, pt, pte++, 0x00000000);
nv_wo32(dev, pt, pte++, 0x00000000);
virt += (1 << psz);
}
} else {
while (pages--) {
struct nouveau_gpuobj *pt = pgt[virt >> 29];
unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
unsigned offset_h = upper_32_bits(phys) & 0xff;
unsigned offset_l = lower_32_bits(phys);
nv_wo32(dev, pt, pte++, offset_l | pfl);
nv_wo32(dev, pt, pte++, offset_h | flags);
phys += (1 << psz);
virt += (1 << psz);
}
}
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, 0x100c80, 0x00050001);
if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
return -EBUSY;
}
nv_wr32(dev, 0x100c80, 0x00000001);
if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
return -EBUSY;
}
return 0;
}
void
nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
{
nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
}
/*
* Cleanup everything
*/
void nouveau_mem_takedown(struct mem_block **heap)
{
struct mem_block *p;
if (!*heap)
return;
for (p = (*heap)->next; p != *heap;) {
struct mem_block *q = p;
p = p->next;
kfree(q);
}
kfree(*heap);
*heap = NULL;
}
void nouveau_mem_close(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->ttm.bdev.man[TTM_PL_PRIV0].has_type)
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_PRIV0);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
ttm_bo_device_release(&dev_priv->ttm.bdev);
nouveau_ttm_global_release(dev_priv);
if (drm_core_has_AGP(dev) && dev->agp &&
drm_core_check_feature(dev, DRIVER_MODESET)) {
struct drm_agp_mem *entry, *tempe;
/* Remove AGP resources, but leave dev->agp
intact until drv_cleanup is called. */
list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) {
if (entry->bound)
drm_unbind_agp(entry->memory);
drm_free_agp(entry->memory, entry->pages);
kfree(entry);
}
INIT_LIST_HEAD(&dev->agp->memory);
if (dev->agp->acquired)
drm_agp_release(dev);
dev->agp->acquired = 0;
dev->agp->enabled = 0;
}
if (dev_priv->fb_mtrr) {
drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1),
drm_get_resource_len(dev, 1), DRM_MTRR_WC);
dev_priv->fb_mtrr = 0;
}
}
/*XXX won't work on BSD because of pci_read_config_dword */
static uint32_t
nouveau_mem_fb_amount_igp(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct pci_dev *bridge;
uint32_t mem;
bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
if (!bridge) {
NV_ERROR(dev, "no bridge device\n");
return 0;
}
if (dev_priv->flags&NV_NFORCE) {
pci_read_config_dword(bridge, 0x7C, &mem);
return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
} else
if (dev_priv->flags&NV_NFORCE2) {
pci_read_config_dword(bridge, 0x84, &mem);
return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
}
NV_ERROR(dev, "impossible!\n");
return 0;
}
/* returns the amount of FB ram in bytes */
uint64_t nouveau_mem_fb_amount(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t boot0;
switch (dev_priv->card_type) {
case NV_04:
boot0 = nv_rd32(dev, NV03_BOOT_0);
if (boot0 & 0x00000100)
return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
case NV04_BOOT_0_RAM_AMOUNT_32MB:
return 32 * 1024 * 1024;
case NV04_BOOT_0_RAM_AMOUNT_16MB:
return 16 * 1024 * 1024;
case NV04_BOOT_0_RAM_AMOUNT_8MB:
return 8 * 1024 * 1024;
case NV04_BOOT_0_RAM_AMOUNT_4MB:
return 4 * 1024 * 1024;
}
break;
case NV_10:
case NV_20:
case NV_30:
case NV_40:
case NV_50:
default:
if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
return nouveau_mem_fb_amount_igp(dev);
} else {
uint64_t mem;
mem = (nv_rd32(dev, NV04_FIFO_DATA) &
NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >>
NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT;
return mem * 1024 * 1024;
}
break;
}
NV_ERROR(dev,
"Unable to detect video ram size. Please report your setup to "
DRIVER_EMAIL "\n");
return 0;
}
static void nouveau_mem_reset_agp(struct drm_device *dev)
{
uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable;
saved_pci_nv_1 = nv_rd32(dev, NV04_PBUS_PCI_NV_1);
saved_pci_nv_19 = nv_rd32(dev, NV04_PBUS_PCI_NV_19);
/* clear busmaster bit */
nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4);
/* clear SBA and AGP bits */
nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff);
/* power cycle pgraph, if enabled */
pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE);
if (pmc_enable & NV_PMC_ENABLE_PGRAPH) {
nv_wr32(dev, NV03_PMC_ENABLE,
pmc_enable & ~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
NV_PMC_ENABLE_PGRAPH);
}
/* and restore (gives effect of resetting AGP) */
nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19);
nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1);
}
int
nouveau_mem_init_agp(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_agp_info info;
struct drm_agp_mode mode;
int ret;
if (nouveau_noagp)
return 0;
nouveau_mem_reset_agp(dev);
if (!dev->agp->acquired) {
ret = drm_agp_acquire(dev);
if (ret) {
NV_ERROR(dev, "Unable to acquire AGP: %d\n", ret);
return ret;
}
}
ret = drm_agp_info(dev, &info);
if (ret) {
NV_ERROR(dev, "Unable to get AGP info: %d\n", ret);
return ret;
}
/* see agp.h for the AGPSTAT_* modes available */
mode.mode = info.mode;
ret = drm_agp_enable(dev, mode);
if (ret) {
NV_ERROR(dev, "Unable to enable AGP: %d\n", ret);
return ret;
}
dev_priv->gart_info.type = NOUVEAU_GART_AGP;
dev_priv->gart_info.aper_base = info.aperture_base;
dev_priv->gart_info.aper_size = info.aperture_size;
return 0;
}
int
nouveau_mem_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
int ret, dma_bits = 32;
dev_priv->fb_phys = drm_get_resource_start(dev, 1);
dev_priv->gart_info.type = NOUVEAU_GART_NONE;
if (dev_priv->card_type >= NV_50 &&
pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
dma_bits = 40;
ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits));
if (ret) {
NV_ERROR(dev, "Error setting DMA mask: %d\n", ret);
return ret;
}
ret = nouveau_ttm_global_init(dev_priv);
if (ret)
return ret;
ret = ttm_bo_device_init(&dev_priv->ttm.bdev,
dev_priv->ttm.bo_global_ref.ref.object,
&nouveau_bo_driver, DRM_FILE_PAGE_OFFSET,
dma_bits <= 32 ? true : false);
if (ret) {
NV_ERROR(dev, "Error initialising bo driver: %d\n", ret);
return ret;
}
INIT_LIST_HEAD(&dev_priv->ttm.bo_list);
spin_lock_init(&dev_priv->ttm.bo_list_lock);
dev_priv->fb_available_size = nouveau_mem_fb_amount(dev);
dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
if (dev_priv->fb_mappable_pages > drm_get_resource_len(dev, 1))
dev_priv->fb_mappable_pages = drm_get_resource_len(dev, 1);
dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20));
/* remove reserved space at end of vram from available amount */
dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
dev_priv->fb_aper_free = dev_priv->fb_available_size;
/* mappable vram */
ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
dev_priv->fb_available_size >> PAGE_SHIFT);
if (ret) {
NV_ERROR(dev, "Failed VRAM mm init: %d\n", ret);
return ret;
}
/* GART */
#if !defined(__powerpc__) && !defined(__ia64__)
if (drm_device_is_agp(dev) && dev->agp) {
ret = nouveau_mem_init_agp(dev);
if (ret)
NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
}
#endif
if (dev_priv->gart_info.type == NOUVEAU_GART_NONE) {
ret = nouveau_sgdma_init(dev);
if (ret) {
NV_ERROR(dev, "Error initialising PCI(E): %d\n", ret);
return ret;
}
}
NV_INFO(dev, "%d MiB GART (aperture)\n",
(int)(dev_priv->gart_info.aper_size >> 20));
dev_priv->gart_info.aper_free = dev_priv->gart_info.aper_size;
ret = ttm_bo_init_mm(bdev, TTM_PL_TT,
dev_priv->gart_info.aper_size >> PAGE_SHIFT);
if (ret) {
NV_ERROR(dev, "Failed TT mm init: %d\n", ret);
return ret;
}
dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1),
drm_get_resource_len(dev, 1),
DRM_MTRR_WC);
return 0;
}

View File

@ -0,0 +1,196 @@
/*
* Copyright (C) 2007 Ben Skeggs.
*
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
int
nouveau_notifier_init_channel(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct nouveau_bo *ntfy = NULL;
int ret;
ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, nouveau_vram_notify ?
TTM_PL_FLAG_VRAM : TTM_PL_FLAG_TT,
0, 0x0000, false, true, &ntfy);
if (ret)
return ret;
ret = nouveau_bo_pin(ntfy, TTM_PL_FLAG_VRAM);
if (ret)
goto out_err;
ret = nouveau_bo_map(ntfy);
if (ret)
goto out_err;
ret = nouveau_mem_init_heap(&chan->notifier_heap, 0, ntfy->bo.mem.size);
if (ret)
goto out_err;
chan->notifier_bo = ntfy;
out_err:
if (ret) {
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(ntfy->gem);
mutex_unlock(&dev->struct_mutex);
}
return ret;
}
void
nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
if (!chan->notifier_bo)
return;
nouveau_bo_unmap(chan->notifier_bo);
mutex_lock(&dev->struct_mutex);
nouveau_bo_unpin(chan->notifier_bo);
drm_gem_object_unreference(chan->notifier_bo->gem);
mutex_unlock(&dev->struct_mutex);
nouveau_mem_takedown(&chan->notifier_heap);
}
static void
nouveau_notifier_gpuobj_dtor(struct drm_device *dev,
struct nouveau_gpuobj *gpuobj)
{
NV_DEBUG(dev, "\n");
if (gpuobj->priv)
nouveau_mem_free_block(gpuobj->priv);
}
int
nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
int size, uint32_t *b_offset)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *nobj = NULL;
struct mem_block *mem;
uint32_t offset;
int target, ret;
if (!chan->notifier_heap) {
NV_ERROR(dev, "Channel %d doesn't have a notifier heap!\n",
chan->id);
return -EINVAL;
}
mem = nouveau_mem_alloc_block(chan->notifier_heap, size, 0,
(struct drm_file *)-2, 0);
if (!mem) {
NV_ERROR(dev, "Channel %d notifier block full\n", chan->id);
return -ENOMEM;
}
offset = chan->notifier_bo->bo.mem.mm_node->start << PAGE_SHIFT;
if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) {
target = NV_DMA_TARGET_VIDMEM;
} else
if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_TT) {
if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA &&
dev_priv->card_type < NV_50) {
ret = nouveau_sgdma_get_page(dev, offset, &offset);
if (ret)
return ret;
target = NV_DMA_TARGET_PCI;
} else {
target = NV_DMA_TARGET_AGP;
}
} else {
NV_ERROR(dev, "Bad DMA target, mem_type %d!\n",
chan->notifier_bo->bo.mem.mem_type);
return -EINVAL;
}
offset += mem->start;
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset,
mem->size, NV_DMA_ACCESS_RW, target,
&nobj);
if (ret) {
nouveau_mem_free_block(mem);
NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret);
return ret;
}
nobj->dtor = nouveau_notifier_gpuobj_dtor;
nobj->priv = mem;
ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL);
if (ret) {
nouveau_gpuobj_del(dev, &nobj);
nouveau_mem_free_block(mem);
NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret);
return ret;
}
*b_offset = mem->start;
return 0;
}
int
nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset)
{
if (!nobj || nobj->dtor != nouveau_notifier_gpuobj_dtor)
return -EINVAL;
if (poffset) {
struct mem_block *mem = nobj->priv;
if (*poffset >= mem->size)
return false;
*poffset += mem->start;
}
return 0;
}
int
nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_notifierobj_alloc *na = data;
struct nouveau_channel *chan;
int ret;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan);
ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset);
if (ret)
return ret;
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,836 @@
#define NV03_BOOT_0 0x00100000
# define NV03_BOOT_0_RAM_AMOUNT 0x00000003
# define NV03_BOOT_0_RAM_AMOUNT_8MB 0x00000000
# define NV03_BOOT_0_RAM_AMOUNT_2MB 0x00000001
# define NV03_BOOT_0_RAM_AMOUNT_4MB 0x00000002
# define NV03_BOOT_0_RAM_AMOUNT_8MB_SDRAM 0x00000003
# define NV04_BOOT_0_RAM_AMOUNT_32MB 0x00000000
# define NV04_BOOT_0_RAM_AMOUNT_4MB 0x00000001
# define NV04_BOOT_0_RAM_AMOUNT_8MB 0x00000002
# define NV04_BOOT_0_RAM_AMOUNT_16MB 0x00000003
#define NV04_FIFO_DATA 0x0010020c
# define NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000
# define NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20
#define NV_RAMIN 0x00700000
#define NV_RAMHT_HANDLE_OFFSET 0
#define NV_RAMHT_CONTEXT_OFFSET 4
# define NV_RAMHT_CONTEXT_VALID (1<<31)
# define NV_RAMHT_CONTEXT_CHANNEL_SHIFT 24
# define NV_RAMHT_CONTEXT_ENGINE_SHIFT 16
# define NV_RAMHT_CONTEXT_ENGINE_SOFTWARE 0
# define NV_RAMHT_CONTEXT_ENGINE_GRAPHICS 1
# define NV_RAMHT_CONTEXT_INSTANCE_SHIFT 0
# define NV40_RAMHT_CONTEXT_CHANNEL_SHIFT 23
# define NV40_RAMHT_CONTEXT_ENGINE_SHIFT 20
# define NV40_RAMHT_CONTEXT_INSTANCE_SHIFT 0
/* DMA object defines */
#define NV_DMA_ACCESS_RW 0
#define NV_DMA_ACCESS_RO 1
#define NV_DMA_ACCESS_WO 2
#define NV_DMA_TARGET_VIDMEM 0
#define NV_DMA_TARGET_PCI 2
#define NV_DMA_TARGET_AGP 3
/* The following is not a real value used by the card, it's changed by
* nouveau_object_dma_create */
#define NV_DMA_TARGET_PCI_NONLINEAR 8
/* Some object classes we care about in the drm */
#define NV_CLASS_DMA_FROM_MEMORY 0x00000002
#define NV_CLASS_DMA_TO_MEMORY 0x00000003
#define NV_CLASS_NULL 0x00000030
#define NV_CLASS_DMA_IN_MEMORY 0x0000003D
#define NV03_USER(i) (0x00800000+(i*NV03_USER_SIZE))
#define NV03_USER__SIZE 16
#define NV10_USER__SIZE 32
#define NV03_USER_SIZE 0x00010000
#define NV03_USER_DMA_PUT(i) (0x00800040+(i*NV03_USER_SIZE))
#define NV03_USER_DMA_PUT__SIZE 16
#define NV10_USER_DMA_PUT__SIZE 32
#define NV03_USER_DMA_GET(i) (0x00800044+(i*NV03_USER_SIZE))
#define NV03_USER_DMA_GET__SIZE 16
#define NV10_USER_DMA_GET__SIZE 32
#define NV03_USER_REF_CNT(i) (0x00800048+(i*NV03_USER_SIZE))
#define NV03_USER_REF_CNT__SIZE 16
#define NV10_USER_REF_CNT__SIZE 32
#define NV40_USER(i) (0x00c00000+(i*NV40_USER_SIZE))
#define NV40_USER_SIZE 0x00001000
#define NV40_USER_DMA_PUT(i) (0x00c00040+(i*NV40_USER_SIZE))
#define NV40_USER_DMA_PUT__SIZE 32
#define NV40_USER_DMA_GET(i) (0x00c00044+(i*NV40_USER_SIZE))
#define NV40_USER_DMA_GET__SIZE 32
#define NV40_USER_REF_CNT(i) (0x00c00048+(i*NV40_USER_SIZE))
#define NV40_USER_REF_CNT__SIZE 32
#define NV50_USER(i) (0x00c00000+(i*NV50_USER_SIZE))
#define NV50_USER_SIZE 0x00002000
#define NV50_USER_DMA_PUT(i) (0x00c00040+(i*NV50_USER_SIZE))
#define NV50_USER_DMA_PUT__SIZE 128
#define NV50_USER_DMA_GET(i) (0x00c00044+(i*NV50_USER_SIZE))
#define NV50_USER_DMA_GET__SIZE 128
#define NV50_USER_REF_CNT(i) (0x00c00048+(i*NV50_USER_SIZE))
#define NV50_USER_REF_CNT__SIZE 128
#define NV03_FIFO_SIZE 0x8000UL
#define NV03_PMC_BOOT_0 0x00000000
#define NV03_PMC_BOOT_1 0x00000004
#define NV03_PMC_INTR_0 0x00000100
# define NV_PMC_INTR_0_PFIFO_PENDING (1<<8)
# define NV_PMC_INTR_0_PGRAPH_PENDING (1<<12)
# define NV_PMC_INTR_0_NV50_I2C_PENDING (1<<21)
# define NV_PMC_INTR_0_CRTC0_PENDING (1<<24)
# define NV_PMC_INTR_0_CRTC1_PENDING (1<<25)
# define NV_PMC_INTR_0_NV50_DISPLAY_PENDING (1<<26)
# define NV_PMC_INTR_0_CRTCn_PENDING (3<<24)
#define NV03_PMC_INTR_EN_0 0x00000140
# define NV_PMC_INTR_EN_0_MASTER_ENABLE (1<<0)
#define NV03_PMC_ENABLE 0x00000200
# define NV_PMC_ENABLE_PFIFO (1<<8)
# define NV_PMC_ENABLE_PGRAPH (1<<12)
/* Disabling the below bit breaks newer (G7X only?) mobile chipsets,
* the card will hang early on in the X init process.
*/
# define NV_PMC_ENABLE_UNK13 (1<<13)
#define NV40_PMC_BACKLIGHT 0x000015f0
# define NV40_PMC_BACKLIGHT_MASK 0x001f0000
#define NV40_PMC_1700 0x00001700
#define NV40_PMC_1704 0x00001704
#define NV40_PMC_1708 0x00001708
#define NV40_PMC_170C 0x0000170C
/* probably PMC ? */
#define NV50_PUNK_BAR0_PRAMIN 0x00001700
#define NV50_PUNK_BAR_CFG_BASE 0x00001704
#define NV50_PUNK_BAR_CFG_BASE_VALID (1<<30)
#define NV50_PUNK_BAR1_CTXDMA 0x00001708
#define NV50_PUNK_BAR1_CTXDMA_VALID (1<<31)
#define NV50_PUNK_BAR3_CTXDMA 0x0000170C
#define NV50_PUNK_BAR3_CTXDMA_VALID (1<<31)
#define NV50_PUNK_UNK1710 0x00001710
#define NV04_PBUS_PCI_NV_1 0x00001804
#define NV04_PBUS_PCI_NV_19 0x0000184C
#define NV04_PBUS_PCI_NV_20 0x00001850
# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
#define NV04_PTIMER_INTR_0 0x00009100
#define NV04_PTIMER_INTR_EN_0 0x00009140
#define NV04_PTIMER_NUMERATOR 0x00009200
#define NV04_PTIMER_DENOMINATOR 0x00009210
#define NV04_PTIMER_TIME_0 0x00009400
#define NV04_PTIMER_TIME_1 0x00009410
#define NV04_PTIMER_ALARM_0 0x00009420
#define NV04_PFB_CFG0 0x00100200
#define NV04_PFB_CFG1 0x00100204
#define NV40_PFB_020C 0x0010020C
#define NV10_PFB_TILE(i) (0x00100240 + (i*16))
#define NV10_PFB_TILE__SIZE 8
#define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16))
#define NV10_PFB_TSIZE(i) (0x00100248 + (i*16))
#define NV10_PFB_TSTATUS(i) (0x0010024C + (i*16))
#define NV10_PFB_CLOSE_PAGE2 0x0010033C
#define NV40_PFB_TILE(i) (0x00100600 + (i*16))
#define NV40_PFB_TILE__SIZE_0 12
#define NV40_PFB_TILE__SIZE_1 15
#define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16))
#define NV40_PFB_TSIZE(i) (0x00100608 + (i*16))
#define NV40_PFB_TSTATUS(i) (0x0010060C + (i*16))
#define NV40_PFB_UNK_800 0x00100800
#define NV04_PGRAPH_DEBUG_0 0x00400080
#define NV04_PGRAPH_DEBUG_1 0x00400084
#define NV04_PGRAPH_DEBUG_2 0x00400088
#define NV04_PGRAPH_DEBUG_3 0x0040008c
#define NV10_PGRAPH_DEBUG_4 0x00400090
#define NV03_PGRAPH_INTR 0x00400100
#define NV03_PGRAPH_NSTATUS 0x00400104
# define NV04_PGRAPH_NSTATUS_STATE_IN_USE (1<<11)
# define NV04_PGRAPH_NSTATUS_INVALID_STATE (1<<12)
# define NV04_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<13)
# define NV04_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<14)
# define NV10_PGRAPH_NSTATUS_STATE_IN_USE (1<<23)
# define NV10_PGRAPH_NSTATUS_INVALID_STATE (1<<24)
# define NV10_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<25)
# define NV10_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<26)
#define NV03_PGRAPH_NSOURCE 0x00400108
# define NV03_PGRAPH_NSOURCE_NOTIFICATION (1<<0)
# define NV03_PGRAPH_NSOURCE_DATA_ERROR (1<<1)
# define NV03_PGRAPH_NSOURCE_PROTECTION_ERROR (1<<2)
# define NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION (1<<3)
# define NV03_PGRAPH_NSOURCE_LIMIT_COLOR (1<<4)
# define NV03_PGRAPH_NSOURCE_LIMIT_ZETA (1<<5)
# define NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD (1<<6)
# define NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION (1<<7)
# define NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION (1<<8)
# define NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION (1<<9)
# define NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION (1<<10)
# define NV03_PGRAPH_NSOURCE_STATE_INVALID (1<<11)
# define NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY (1<<12)
# define NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE (1<<13)
# define NV03_PGRAPH_NSOURCE_METHOD_CNT (1<<14)
# define NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION (1<<15)
# define NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION (1<<16)
# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_A (1<<17)
# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_B (1<<18)
#define NV03_PGRAPH_INTR_EN 0x00400140
#define NV40_PGRAPH_INTR_EN 0x0040013C
# define NV_PGRAPH_INTR_NOTIFY (1<<0)
# define NV_PGRAPH_INTR_MISSING_HW (1<<4)
# define NV_PGRAPH_INTR_CONTEXT_SWITCH (1<<12)
# define NV_PGRAPH_INTR_BUFFER_NOTIFY (1<<16)
# define NV_PGRAPH_INTR_ERROR (1<<20)
#define NV10_PGRAPH_CTX_CONTROL 0x00400144
#define NV10_PGRAPH_CTX_USER 0x00400148
#define NV10_PGRAPH_CTX_SWITCH1 0x0040014C
#define NV10_PGRAPH_CTX_SWITCH2 0x00400150
#define NV10_PGRAPH_CTX_SWITCH3 0x00400154
#define NV10_PGRAPH_CTX_SWITCH4 0x00400158
#define NV10_PGRAPH_CTX_SWITCH5 0x0040015C
#define NV04_PGRAPH_CTX_SWITCH1 0x00400160
#define NV10_PGRAPH_CTX_CACHE1 0x00400160
#define NV04_PGRAPH_CTX_SWITCH2 0x00400164
#define NV04_PGRAPH_CTX_SWITCH3 0x00400168
#define NV04_PGRAPH_CTX_SWITCH4 0x0040016C
#define NV04_PGRAPH_CTX_CONTROL 0x00400170
#define NV04_PGRAPH_CTX_USER 0x00400174
#define NV04_PGRAPH_CTX_CACHE1 0x00400180
#define NV10_PGRAPH_CTX_CACHE2 0x00400180
#define NV03_PGRAPH_CTX_CONTROL 0x00400190
#define NV03_PGRAPH_CTX_USER 0x00400194
#define NV04_PGRAPH_CTX_CACHE2 0x004001A0
#define NV10_PGRAPH_CTX_CACHE3 0x004001A0
#define NV04_PGRAPH_CTX_CACHE3 0x004001C0
#define NV10_PGRAPH_CTX_CACHE4 0x004001C0
#define NV04_PGRAPH_CTX_CACHE4 0x004001E0
#define NV10_PGRAPH_CTX_CACHE5 0x004001E0
#define NV40_PGRAPH_CTXCTL_0304 0x00400304
#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001
#define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308
#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_MASK 0xff000000
#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT 24
#define NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK 0x00ffffff
#define NV40_PGRAPH_CTXCTL_0310 0x00400310
#define NV40_PGRAPH_CTXCTL_0310_XFER_SAVE 0x00000020
#define NV40_PGRAPH_CTXCTL_0310_XFER_LOAD 0x00000040
#define NV40_PGRAPH_CTXCTL_030C 0x0040030c
#define NV40_PGRAPH_CTXCTL_UCODE_INDEX 0x00400324
#define NV40_PGRAPH_CTXCTL_UCODE_DATA 0x00400328
#define NV40_PGRAPH_CTXCTL_CUR 0x0040032c
#define NV40_PGRAPH_CTXCTL_CUR_LOADED 0x01000000
#define NV40_PGRAPH_CTXCTL_CUR_INSTANCE 0x000FFFFF
#define NV40_PGRAPH_CTXCTL_NEXT 0x00400330
#define NV40_PGRAPH_CTXCTL_NEXT_INSTANCE 0x000fffff
#define NV50_PGRAPH_CTXCTL_CUR 0x0040032c
#define NV50_PGRAPH_CTXCTL_CUR_LOADED 0x80000000
#define NV50_PGRAPH_CTXCTL_CUR_INSTANCE 0x00ffffff
#define NV50_PGRAPH_CTXCTL_NEXT 0x00400330
#define NV50_PGRAPH_CTXCTL_NEXT_INSTANCE 0x00ffffff
#define NV03_PGRAPH_ABS_X_RAM 0x00400400
#define NV03_PGRAPH_ABS_Y_RAM 0x00400480
#define NV03_PGRAPH_X_MISC 0x00400500
#define NV03_PGRAPH_Y_MISC 0x00400504
#define NV04_PGRAPH_VALID1 0x00400508
#define NV04_PGRAPH_SOURCE_COLOR 0x0040050C
#define NV04_PGRAPH_MISC24_0 0x00400510
#define NV03_PGRAPH_XY_LOGIC_MISC0 0x00400514
#define NV03_PGRAPH_XY_LOGIC_MISC1 0x00400518
#define NV03_PGRAPH_XY_LOGIC_MISC2 0x0040051C
#define NV03_PGRAPH_XY_LOGIC_MISC3 0x00400520
#define NV03_PGRAPH_CLIPX_0 0x00400524
#define NV03_PGRAPH_CLIPX_1 0x00400528
#define NV03_PGRAPH_CLIPY_0 0x0040052C
#define NV03_PGRAPH_CLIPY_1 0x00400530
#define NV03_PGRAPH_ABS_ICLIP_XMAX 0x00400534
#define NV03_PGRAPH_ABS_ICLIP_YMAX 0x00400538
#define NV03_PGRAPH_ABS_UCLIP_XMIN 0x0040053C
#define NV03_PGRAPH_ABS_UCLIP_YMIN 0x00400540
#define NV03_PGRAPH_ABS_UCLIP_XMAX 0x00400544
#define NV03_PGRAPH_ABS_UCLIP_YMAX 0x00400548
#define NV03_PGRAPH_ABS_UCLIPA_XMIN 0x00400560
#define NV03_PGRAPH_ABS_UCLIPA_YMIN 0x00400564
#define NV03_PGRAPH_ABS_UCLIPA_XMAX 0x00400568
#define NV03_PGRAPH_ABS_UCLIPA_YMAX 0x0040056C
#define NV04_PGRAPH_MISC24_1 0x00400570
#define NV04_PGRAPH_MISC24_2 0x00400574
#define NV04_PGRAPH_VALID2 0x00400578
#define NV04_PGRAPH_PASSTHRU_0 0x0040057C
#define NV04_PGRAPH_PASSTHRU_1 0x00400580
#define NV04_PGRAPH_PASSTHRU_2 0x00400584
#define NV10_PGRAPH_DIMX_TEXTURE 0x00400588
#define NV10_PGRAPH_WDIMX_TEXTURE 0x0040058C
#define NV04_PGRAPH_COMBINE_0_ALPHA 0x00400590
#define NV04_PGRAPH_COMBINE_0_COLOR 0x00400594
#define NV04_PGRAPH_COMBINE_1_ALPHA 0x00400598
#define NV04_PGRAPH_COMBINE_1_COLOR 0x0040059C
#define NV04_PGRAPH_FORMAT_0 0x004005A8
#define NV04_PGRAPH_FORMAT_1 0x004005AC
#define NV04_PGRAPH_FILTER_0 0x004005B0
#define NV04_PGRAPH_FILTER_1 0x004005B4
#define NV03_PGRAPH_MONO_COLOR0 0x00400600
#define NV04_PGRAPH_ROP3 0x00400604
#define NV04_PGRAPH_BETA_AND 0x00400608
#define NV04_PGRAPH_BETA_PREMULT 0x0040060C
#define NV04_PGRAPH_LIMIT_VIOL_PIX 0x00400610
#define NV04_PGRAPH_FORMATS 0x00400618
#define NV10_PGRAPH_DEBUG_2 0x00400620
#define NV04_PGRAPH_BOFFSET0 0x00400640
#define NV04_PGRAPH_BOFFSET1 0x00400644
#define NV04_PGRAPH_BOFFSET2 0x00400648
#define NV04_PGRAPH_BOFFSET3 0x0040064C
#define NV04_PGRAPH_BOFFSET4 0x00400650
#define NV04_PGRAPH_BOFFSET5 0x00400654
#define NV04_PGRAPH_BBASE0 0x00400658
#define NV04_PGRAPH_BBASE1 0x0040065C
#define NV04_PGRAPH_BBASE2 0x00400660
#define NV04_PGRAPH_BBASE3 0x00400664
#define NV04_PGRAPH_BBASE4 0x00400668
#define NV04_PGRAPH_BBASE5 0x0040066C
#define NV04_PGRAPH_BPITCH0 0x00400670
#define NV04_PGRAPH_BPITCH1 0x00400674
#define NV04_PGRAPH_BPITCH2 0x00400678
#define NV04_PGRAPH_BPITCH3 0x0040067C
#define NV04_PGRAPH_BPITCH4 0x00400680
#define NV04_PGRAPH_BLIMIT0 0x00400684
#define NV04_PGRAPH_BLIMIT1 0x00400688
#define NV04_PGRAPH_BLIMIT2 0x0040068C
#define NV04_PGRAPH_BLIMIT3 0x00400690
#define NV04_PGRAPH_BLIMIT4 0x00400694
#define NV04_PGRAPH_BLIMIT5 0x00400698
#define NV04_PGRAPH_BSWIZZLE2 0x0040069C
#define NV04_PGRAPH_BSWIZZLE5 0x004006A0
#define NV03_PGRAPH_STATUS 0x004006B0
#define NV04_PGRAPH_STATUS 0x00400700
#define NV04_PGRAPH_TRAPPED_ADDR 0x00400704
#define NV04_PGRAPH_TRAPPED_DATA 0x00400708
#define NV04_PGRAPH_SURFACE 0x0040070C
#define NV10_PGRAPH_TRAPPED_DATA_HIGH 0x0040070C
#define NV04_PGRAPH_STATE 0x00400710
#define NV10_PGRAPH_SURFACE 0x00400710
#define NV04_PGRAPH_NOTIFY 0x00400714
#define NV10_PGRAPH_STATE 0x00400714
#define NV10_PGRAPH_NOTIFY 0x00400718
#define NV04_PGRAPH_FIFO 0x00400720
#define NV04_PGRAPH_BPIXEL 0x00400724
#define NV10_PGRAPH_RDI_INDEX 0x00400750
#define NV04_PGRAPH_FFINTFC_ST2 0x00400754
#define NV10_PGRAPH_RDI_DATA 0x00400754
#define NV04_PGRAPH_DMA_PITCH 0x00400760
#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
#define NV04_PGRAPH_DVD_COLORFMT 0x00400764
#define NV04_PGRAPH_SCALED_FORMAT 0x00400768
#define NV10_PGRAPH_DMA_PITCH 0x00400770
#define NV10_PGRAPH_DVD_COLORFMT 0x00400774
#define NV10_PGRAPH_SCALED_FORMAT 0x00400778
#define NV20_PGRAPH_CHANNEL_CTX_TABLE 0x00400780
#define NV20_PGRAPH_CHANNEL_CTX_POINTER 0x00400784
#define NV20_PGRAPH_CHANNEL_CTX_XFER 0x00400788
#define NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD 0x00000001
#define NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE 0x00000002
#define NV04_PGRAPH_PATT_COLOR0 0x00400800
#define NV04_PGRAPH_PATT_COLOR1 0x00400804
#define NV04_PGRAPH_PATTERN 0x00400808
#define NV04_PGRAPH_PATTERN_SHAPE 0x00400810
#define NV04_PGRAPH_CHROMA 0x00400814
#define NV04_PGRAPH_CONTROL0 0x00400818
#define NV04_PGRAPH_CONTROL1 0x0040081C
#define NV04_PGRAPH_CONTROL2 0x00400820
#define NV04_PGRAPH_BLEND 0x00400824
#define NV04_PGRAPH_STORED_FMT 0x00400830
#define NV04_PGRAPH_PATT_COLORRAM 0x00400900
#define NV40_PGRAPH_TILE0(i) (0x00400900 + (i*16))
#define NV40_PGRAPH_TLIMIT0(i) (0x00400904 + (i*16))
#define NV40_PGRAPH_TSIZE0(i) (0x00400908 + (i*16))
#define NV40_PGRAPH_TSTATUS0(i) (0x0040090C + (i*16))
#define NV10_PGRAPH_TILE(i) (0x00400B00 + (i*16))
#define NV10_PGRAPH_TLIMIT(i) (0x00400B04 + (i*16))
#define NV10_PGRAPH_TSIZE(i) (0x00400B08 + (i*16))
#define NV10_PGRAPH_TSTATUS(i) (0x00400B0C + (i*16))
#define NV04_PGRAPH_U_RAM 0x00400D00
#define NV47_PGRAPH_TILE0(i) (0x00400D00 + (i*16))
#define NV47_PGRAPH_TLIMIT0(i) (0x00400D04 + (i*16))
#define NV47_PGRAPH_TSIZE0(i) (0x00400D08 + (i*16))
#define NV47_PGRAPH_TSTATUS0(i) (0x00400D0C + (i*16))
#define NV04_PGRAPH_V_RAM 0x00400D40
#define NV04_PGRAPH_W_RAM 0x00400D80
#define NV10_PGRAPH_COMBINER0_IN_ALPHA 0x00400E40
#define NV10_PGRAPH_COMBINER1_IN_ALPHA 0x00400E44
#define NV10_PGRAPH_COMBINER0_IN_RGB 0x00400E48
#define NV10_PGRAPH_COMBINER1_IN_RGB 0x00400E4C
#define NV10_PGRAPH_COMBINER_COLOR0 0x00400E50
#define NV10_PGRAPH_COMBINER_COLOR1 0x00400E54
#define NV10_PGRAPH_COMBINER0_OUT_ALPHA 0x00400E58
#define NV10_PGRAPH_COMBINER1_OUT_ALPHA 0x00400E5C
#define NV10_PGRAPH_COMBINER0_OUT_RGB 0x00400E60
#define NV10_PGRAPH_COMBINER1_OUT_RGB 0x00400E64
#define NV10_PGRAPH_COMBINER_FINAL0 0x00400E68
#define NV10_PGRAPH_COMBINER_FINAL1 0x00400E6C
#define NV10_PGRAPH_WINDOWCLIP_HORIZONTAL 0x00400F00
#define NV10_PGRAPH_WINDOWCLIP_VERTICAL 0x00400F20
#define NV10_PGRAPH_XFMODE0 0x00400F40
#define NV10_PGRAPH_XFMODE1 0x00400F44
#define NV10_PGRAPH_GLOBALSTATE0 0x00400F48
#define NV10_PGRAPH_GLOBALSTATE1 0x00400F4C
#define NV10_PGRAPH_PIPE_ADDRESS 0x00400F50
#define NV10_PGRAPH_PIPE_DATA 0x00400F54
#define NV04_PGRAPH_DMA_START_0 0x00401000
#define NV04_PGRAPH_DMA_START_1 0x00401004
#define NV04_PGRAPH_DMA_LENGTH 0x00401008
#define NV04_PGRAPH_DMA_MISC 0x0040100C
#define NV04_PGRAPH_DMA_DATA_0 0x00401020
#define NV04_PGRAPH_DMA_DATA_1 0x00401024
#define NV04_PGRAPH_DMA_RM 0x00401030
#define NV04_PGRAPH_DMA_A_XLATE_INST 0x00401040
#define NV04_PGRAPH_DMA_A_CONTROL 0x00401044
#define NV04_PGRAPH_DMA_A_LIMIT 0x00401048
#define NV04_PGRAPH_DMA_A_TLB_PTE 0x0040104C
#define NV04_PGRAPH_DMA_A_TLB_TAG 0x00401050
#define NV04_PGRAPH_DMA_A_ADJ_OFFSET 0x00401054
#define NV04_PGRAPH_DMA_A_OFFSET 0x00401058
#define NV04_PGRAPH_DMA_A_SIZE 0x0040105C
#define NV04_PGRAPH_DMA_A_Y_SIZE 0x00401060
#define NV04_PGRAPH_DMA_B_XLATE_INST 0x00401080
#define NV04_PGRAPH_DMA_B_CONTROL 0x00401084
#define NV04_PGRAPH_DMA_B_LIMIT 0x00401088
#define NV04_PGRAPH_DMA_B_TLB_PTE 0x0040108C
#define NV04_PGRAPH_DMA_B_TLB_TAG 0x00401090
#define NV04_PGRAPH_DMA_B_ADJ_OFFSET 0x00401094
#define NV04_PGRAPH_DMA_B_OFFSET 0x00401098
#define NV04_PGRAPH_DMA_B_SIZE 0x0040109C
#define NV04_PGRAPH_DMA_B_Y_SIZE 0x004010A0
#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))
/* It's a guess that this works on NV03. Confirmed on NV04, though */
#define NV04_PFIFO_DELAY_0 0x00002040
#define NV04_PFIFO_DMA_TIMESLICE 0x00002044
#define NV04_PFIFO_NEXT_CHANNEL 0x00002050
#define NV03_PFIFO_INTR_0 0x00002100
#define NV03_PFIFO_INTR_EN_0 0x00002140
# define NV_PFIFO_INTR_CACHE_ERROR (1<<0)
# define NV_PFIFO_INTR_RUNOUT (1<<4)
# define NV_PFIFO_INTR_RUNOUT_OVERFLOW (1<<8)
# define NV_PFIFO_INTR_DMA_PUSHER (1<<12)
# define NV_PFIFO_INTR_DMA_PT (1<<16)
# define NV_PFIFO_INTR_SEMAPHORE (1<<20)
# define NV_PFIFO_INTR_ACQUIRE_TIMEOUT (1<<24)
#define NV03_PFIFO_RAMHT 0x00002210
#define NV03_PFIFO_RAMFC 0x00002214
#define NV03_PFIFO_RAMRO 0x00002218
#define NV40_PFIFO_RAMFC 0x00002220
#define NV03_PFIFO_CACHES 0x00002500
#define NV04_PFIFO_MODE 0x00002504
#define NV04_PFIFO_DMA 0x00002508
#define NV04_PFIFO_SIZE 0x0000250c
#define NV50_PFIFO_CTX_TABLE(c) (0x2600+(c)*4)
#define NV50_PFIFO_CTX_TABLE__SIZE 128
#define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED (1<<31)
#define NV50_PFIFO_CTX_TABLE_UNK30_BAD (1<<30)
#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80 0x0FFFFFFF
#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84 0x00FFFFFF
#define NV03_PFIFO_CACHE0_PUSH0 0x00003000
#define NV03_PFIFO_CACHE0_PULL0 0x00003040
#define NV04_PFIFO_CACHE0_PULL0 0x00003050
#define NV04_PFIFO_CACHE0_PULL1 0x00003054
#define NV03_PFIFO_CACHE1_PUSH0 0x00003200
#define NV03_PFIFO_CACHE1_PUSH1 0x00003204
#define NV03_PFIFO_CACHE1_PUSH1_DMA (1<<8)
#define NV40_PFIFO_CACHE1_PUSH1_DMA (1<<16)
#define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000000f
#define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000001f
#define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000007f
#define NV03_PFIFO_CACHE1_PUT 0x00003210
#define NV04_PFIFO_CACHE1_DMA_PUSH 0x00003220
#define NV04_PFIFO_CACHE1_DMA_FETCH 0x00003224
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x00000000
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x00000008
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x00000010
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x00000018
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x00000020
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x00000028
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x00000030
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x00000038
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x00000040
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x00000048
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0x00000050
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0x00000058
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0x00000060
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0x00000068
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0x00000070
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0x00000078
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x00000080
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x00000088
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x00000090
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x00000098
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x000000A0
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x000000A8
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x000000B0
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x000000B8
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x000000C0
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x000000C8
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x000000D0
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x000000D8
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x000000E0
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x000000E8
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x000000F0
# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x000000F8
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE 0x0000E000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x00000000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x00002000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x00004000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x00006000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x00008000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x0000A000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x0000C000
# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x0000E000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 0x001F0000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0 0x00000000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1 0x00010000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2 0x00020000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3 0x00030000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4 0x00040000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5 0x00050000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6 0x00060000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7 0x00070000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 0x00080000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9 0x00090000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10 0x000A0000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11 0x000B0000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12 0x000C0000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13 0x000D0000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14 0x000E0000
# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15 0x000F0000
# define NV_PFIFO_CACHE1_ENDIAN 0x80000000
# define NV_PFIFO_CACHE1_LITTLE_ENDIAN 0x7FFFFFFF
# define NV_PFIFO_CACHE1_BIG_ENDIAN 0x80000000
#define NV04_PFIFO_CACHE1_DMA_STATE 0x00003228
#define NV04_PFIFO_CACHE1_DMA_INSTANCE 0x0000322c
#define NV04_PFIFO_CACHE1_DMA_CTL 0x00003230
#define NV04_PFIFO_CACHE1_DMA_PUT 0x00003240
#define NV04_PFIFO_CACHE1_DMA_GET 0x00003244
#define NV10_PFIFO_CACHE1_REF_CNT 0x00003248
#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C
#define NV03_PFIFO_CACHE1_PULL0 0x00003240
#define NV04_PFIFO_CACHE1_PULL0 0x00003250
#define NV03_PFIFO_CACHE1_PULL1 0x00003250
#define NV04_PFIFO_CACHE1_PULL1 0x00003254
#define NV04_PFIFO_CACHE1_HASH 0x00003258
#define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT 0x00003260
#define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP 0x00003264
#define NV10_PFIFO_CACHE1_ACQUIRE_VALUE 0x00003268
#define NV10_PFIFO_CACHE1_SEMAPHORE 0x0000326C
#define NV03_PFIFO_CACHE1_GET 0x00003270
#define NV04_PFIFO_CACHE1_ENGINE 0x00003280
#define NV04_PFIFO_CACHE1_DMA_DCOUNT 0x000032A0
#define NV40_PFIFO_GRCTX_INSTANCE 0x000032E0
#define NV40_PFIFO_UNK32E4 0x000032E4
#define NV04_PFIFO_CACHE1_METHOD(i) (0x00003800+(i*8))
#define NV04_PFIFO_CACHE1_DATA(i) (0x00003804+(i*8))
#define NV40_PFIFO_CACHE1_METHOD(i) (0x00090000+(i*8))
#define NV40_PFIFO_CACHE1_DATA(i) (0x00090004+(i*8))
#define NV_CRTC0_INTSTAT 0x00600100
#define NV_CRTC0_INTEN 0x00600140
#define NV_CRTC1_INTSTAT 0x00602100
#define NV_CRTC1_INTEN 0x00602140
# define NV_CRTC_INTR_VBLANK (1<<0)
#define NV04_PRAMIN 0x00700000
/* Fifo commands. These are not regs, neither masks */
#define NV03_FIFO_CMD_JUMP 0x20000000
#define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc
#define NV03_FIFO_CMD_REWIND (NV03_FIFO_CMD_JUMP | (0 & NV03_FIFO_CMD_JUMP_OFFSET_MASK))
/* This is a partial import from rules-ng, a few things may be duplicated.
* Eventually we should completely import everything from rules-ng.
* For the moment check rules-ng for docs.
*/
#define NV50_PMC 0x00000000
#define NV50_PMC__LEN 0x1
#define NV50_PMC__ESIZE 0x2000
# define NV50_PMC_BOOT_0 0x00000000
# define NV50_PMC_BOOT_0_REVISION 0x000000ff
# define NV50_PMC_BOOT_0_REVISION__SHIFT 0
# define NV50_PMC_BOOT_0_ARCH 0x0ff00000
# define NV50_PMC_BOOT_0_ARCH__SHIFT 20
# define NV50_PMC_INTR_0 0x00000100
# define NV50_PMC_INTR_0_PFIFO (1<<8)
# define NV50_PMC_INTR_0_PGRAPH (1<<12)
# define NV50_PMC_INTR_0_PTIMER (1<<20)
# define NV50_PMC_INTR_0_HOTPLUG (1<<21)
# define NV50_PMC_INTR_0_DISPLAY (1<<26)
# define NV50_PMC_INTR_EN_0 0x00000140
# define NV50_PMC_INTR_EN_0_MASTER (1<<0)
# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0)
# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0)
# define NV50_PMC_ENABLE 0x00000200
# define NV50_PMC_ENABLE_PFIFO (1<<8)
# define NV50_PMC_ENABLE_PGRAPH (1<<12)
#define NV50_PCONNECTOR 0x0000e000
#define NV50_PCONNECTOR__LEN 0x1
#define NV50_PCONNECTOR__ESIZE 0x1000
# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18)
# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19)
# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18)
# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19)
# define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104
# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2)
# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6)
# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10)
# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14)
# define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138
# define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150
# define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168
# define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180
# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240
# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258
#define NV50_AUXCH_DATA_OUT(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0)
#define NV50_AUXCH_DATA_OUT__SIZE 4
#define NV50_AUXCH_DATA_IN(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0)
#define NV50_AUXCH_DATA_IN__SIZE 4
#define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0)
#define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4)
#define NV50_AUXCH_CTRL_LINKSTAT 0x01000000
#define NV50_AUXCH_CTRL_LINKSTAT_NOT_READY 0x00000000
#define NV50_AUXCH_CTRL_LINKSTAT_READY 0x01000000
#define NV50_AUXCH_CTRL_LINKEN 0x00100000
#define NV50_AUXCH_CTRL_LINKEN_DISABLED 0x00000000
#define NV50_AUXCH_CTRL_LINKEN_ENABLED 0x00100000
#define NV50_AUXCH_CTRL_EXEC 0x00010000
#define NV50_AUXCH_CTRL_EXEC_COMPLETE 0x00000000
#define NV50_AUXCH_CTRL_EXEC_IN_PROCESS 0x00010000
#define NV50_AUXCH_CTRL_CMD 0x0000f000
#define NV50_AUXCH_CTRL_CMD_SHIFT 12
#define NV50_AUXCH_CTRL_LEN 0x0000000f
#define NV50_AUXCH_CTRL_LEN_SHIFT 0
#define NV50_AUXCH_STAT(i) ((i) * 0x50 + 0x0000e4e8)
#define NV50_AUXCH_STAT_STATE 0x10000000
#define NV50_AUXCH_STAT_STATE_NOT_READY 0x00000000
#define NV50_AUXCH_STAT_STATE_READY 0x10000000
#define NV50_AUXCH_STAT_REPLY 0x000f0000
#define NV50_AUXCH_STAT_REPLY_AUX 0x00030000
#define NV50_AUXCH_STAT_REPLY_AUX_ACK 0x00000000
#define NV50_AUXCH_STAT_REPLY_AUX_NACK 0x00010000
#define NV50_AUXCH_STAT_REPLY_AUX_DEFER 0x00020000
#define NV50_AUXCH_STAT_REPLY_I2C 0x000c0000
#define NV50_AUXCH_STAT_REPLY_I2C_ACK 0x00000000
#define NV50_AUXCH_STAT_REPLY_I2C_NACK 0x00040000
#define NV50_AUXCH_STAT_REPLY_I2C_DEFER 0x00080000
#define NV50_AUXCH_STAT_COUNT 0x0000001f
#define NV50_PBUS 0x00088000
#define NV50_PBUS__LEN 0x1
#define NV50_PBUS__ESIZE 0x1000
# define NV50_PBUS_PCI_ID 0x00088000
# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff
# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0
# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000
# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16
#define NV50_PFB 0x00100000
#define NV50_PFB__LEN 0x1
#define NV50_PFB__ESIZE 0x1000
#define NV50_PEXTDEV 0x00101000
#define NV50_PEXTDEV__LEN 0x1
#define NV50_PEXTDEV__ESIZE 0x1000
#define NV50_PROM 0x00300000
#define NV50_PROM__LEN 0x1
#define NV50_PROM__ESIZE 0x10000
#define NV50_PGRAPH 0x00400000
#define NV50_PGRAPH__LEN 0x1
#define NV50_PGRAPH__ESIZE 0x10000
#define NV50_PDISPLAY 0x00610000
#define NV50_PDISPLAY_OBJECTS 0x00610010
#define NV50_PDISPLAY_INTR_0 0x00610020
#define NV50_PDISPLAY_INTR_1 0x00610024
#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC 0x0000000c
#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_SHIFT 2
#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(n) (1 << ((n) + 2))
#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0 0x00000004
#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1 0x00000008
#define NV50_PDISPLAY_INTR_1_CLK_UNK10 0x00000010
#define NV50_PDISPLAY_INTR_1_CLK_UNK20 0x00000020
#define NV50_PDISPLAY_INTR_1_CLK_UNK40 0x00000040
#define NV50_PDISPLAY_INTR_EN 0x0061002c
#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC 0x0000000c
#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(n) (1 << ((n) + 2))
#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_0 0x00000004
#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_1 0x00000008
#define NV50_PDISPLAY_INTR_EN_CLK_UNK10 0x00000010
#define NV50_PDISPLAY_INTR_EN_CLK_UNK20 0x00000020
#define NV50_PDISPLAY_INTR_EN_CLK_UNK40 0x00000040
#define NV50_PDISPLAY_UNK30_CTRL 0x00610030
#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 0x00000200
#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 0x00000400
#define NV50_PDISPLAY_UNK30_CTRL_PENDING 0x80000000
#define NV50_PDISPLAY_TRAPPED_ADDR 0x00610080
#define NV50_PDISPLAY_TRAPPED_DATA 0x00610084
#define NV50_PDISPLAY_CHANNEL_STAT(i) ((i) * 0x10 + 0x00610200)
#define NV50_PDISPLAY_CHANNEL_STAT_DMA 0x00000010
#define NV50_PDISPLAY_CHANNEL_STAT_DMA_DISABLED 0x00000000
#define NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED 0x00000010
#define NV50_PDISPLAY_CHANNEL_DMA_CB(i) ((i) * 0x10 + 0x00610204)
#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION 0x00000002
#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM 0x00000000
#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_SYSTEM 0x00000002
#define NV50_PDISPLAY_CHANNEL_DMA_CB_VALID 0x00000001
#define NV50_PDISPLAY_CHANNEL_UNK2(i) ((i) * 0x10 + 0x00610208)
#define NV50_PDISPLAY_CHANNEL_UNK3(i) ((i) * 0x10 + 0x0061020c)
#define NV50_PDISPLAY_CURSOR 0x00610270
#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) ((i) * 0x10 + 0x00610270)
#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON 0x00000001
#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS 0x00030000
#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE 0x00010000
#define NV50_PDISPLAY_CTRL_STATE 0x00610300
#define NV50_PDISPLAY_CTRL_STATE_PENDING 0x80000000
#define NV50_PDISPLAY_CTRL_STATE_METHOD 0x00001ffc
#define NV50_PDISPLAY_CTRL_STATE_ENABLE 0x00000001
#define NV50_PDISPLAY_CTRL_VAL 0x00610304
#define NV50_PDISPLAY_UNK_380 0x00610380
#define NV50_PDISPLAY_RAM_AMOUNT 0x00610384
#define NV50_PDISPLAY_UNK_388 0x00610388
#define NV50_PDISPLAY_UNK_38C 0x0061038c
#define NV50_PDISPLAY_CRTC_P(i, r) ((i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
#define NV50_PDISPLAY_CRTC_C(i, r) (4 + (i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
#define NV50_PDISPLAY_CRTC_UNK_0A18 /* mthd 0x0900 */ 0x00610a18
#define NV50_PDISPLAY_CRTC_CLUT_MODE 0x00610a24
#define NV50_PDISPLAY_CRTC_INTERLACE 0x00610a48
#define NV50_PDISPLAY_CRTC_SCALE_CTRL 0x00610a50
#define NV50_PDISPLAY_CRTC_CURSOR_CTRL 0x00610a58
#define NV50_PDISPLAY_CRTC_UNK0A78 /* mthd 0x0904 */ 0x00610a78
#define NV50_PDISPLAY_CRTC_UNK0AB8 0x00610ab8
#define NV50_PDISPLAY_CRTC_DEPTH 0x00610ac8
#define NV50_PDISPLAY_CRTC_CLOCK 0x00610ad0
#define NV50_PDISPLAY_CRTC_COLOR_CTRL 0x00610ae0
#define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END 0x00610ae8
#define NV50_PDISPLAY_CRTC_MODE_UNK1 0x00610af0
#define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL 0x00610af8
#define NV50_PDISPLAY_CRTC_SYNC_DURATION 0x00610b00
#define NV50_PDISPLAY_CRTC_MODE_UNK2 0x00610b08
#define NV50_PDISPLAY_CRTC_UNK_0B10 /* mthd 0x0828 */ 0x00610b10
#define NV50_PDISPLAY_CRTC_FB_SIZE 0x00610b18
#define NV50_PDISPLAY_CRTC_FB_PITCH 0x00610b20
#define NV50_PDISPLAY_CRTC_FB_PITCH_LINEAR 0x00100000
#define NV50_PDISPLAY_CRTC_FB_POS 0x00610b28
#define NV50_PDISPLAY_CRTC_SCALE_CENTER_OFFSET 0x00610b38
#define NV50_PDISPLAY_CRTC_REAL_RES 0x00610b40
#define NV50_PDISPLAY_CRTC_SCALE_RES1 0x00610b48
#define NV50_PDISPLAY_CRTC_SCALE_RES2 0x00610b50
#define NV50_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
#define NV50_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
#define NV50_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610b70 + (i) * 0x8)
#define NV50_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610b74 + (i) * 0x8)
#define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610bdc + (i) * 0x8)
#define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610be0 + (i) * 0x8)
#define NV90_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610794 + (i) * 0x8)
#define NV90_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610798 + (i) * 0x8)
#define NV90_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
#define NV90_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
#define NV90_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610b80 + (i) * 0x8)
#define NV90_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610b84 + (i) * 0x8)
#define NV50_PDISPLAY_CRTC_CLK 0x00614000
#define NV50_PDISPLAY_CRTC_CLK_CTRL1(i) ((i) * 0x800 + 0x614100)
#define NV50_PDISPLAY_CRTC_CLK_CTRL1_CONNECTED 0x00000600
#define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) ((i) * 0x800 + 0x614104)
#define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) ((i) * 0x800 + 0x614108)
#define NV50_PDISPLAY_CRTC_CLK_CTRL2(i) ((i) * 0x800 + 0x614200)
#define NV50_PDISPLAY_DAC_CLK 0x00614000
#define NV50_PDISPLAY_DAC_CLK_CTRL2(i) ((i) * 0x800 + 0x614280)
#define NV50_PDISPLAY_SOR_CLK 0x00614000
#define NV50_PDISPLAY_SOR_CLK_CTRL2(i) ((i) * 0x800 + 0x614300)
#define NV50_PDISPLAY_VGACRTC(r) ((r) + 0x619400)
#define NV50_PDISPLAY_DAC 0x0061a000
#define NV50_PDISPLAY_DAC_DPMS_CTRL(i) (0x0061a004 + (i) * 0x800)
#define NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF 0x00000001
#define NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF 0x00000004
#define NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED 0x00000010
#define NV50_PDISPLAY_DAC_DPMS_CTRL_OFF 0x00000040
#define NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING 0x80000000
#define NV50_PDISPLAY_DAC_LOAD_CTRL(i) (0x0061a00c + (i) * 0x800)
#define NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE 0x00100000
#define NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT 0x38000000
#define NV50_PDISPLAY_DAC_LOAD_CTRL_DONE 0x80000000
#define NV50_PDISPLAY_DAC_CLK_CTRL1(i) (0x0061a010 + (i) * 0x800)
#define NV50_PDISPLAY_DAC_CLK_CTRL1_CONNECTED 0x00000600
#define NV50_PDISPLAY_SOR 0x0061c000
#define NV50_PDISPLAY_SOR_DPMS_CTRL(i) (0x0061c004 + (i) * 0x800)
#define NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING 0x80000000
#define NV50_PDISPLAY_SOR_DPMS_CTRL_ON 0x00000001
#define NV50_PDISPLAY_SOR_CLK_CTRL1(i) (0x0061c008 + (i) * 0x800)
#define NV50_PDISPLAY_SOR_CLK_CTRL1_CONNECTED 0x00000600
#define NV50_PDISPLAY_SOR_DPMS_STATE(i) (0x0061c030 + (i) * 0x800)
#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000
#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000
#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000
#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084
#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000
#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff
#define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
#define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000
#define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000
#define NV50_SOR_DP_CTRL_LANE_1_ENABLED 0x00020000
#define NV50_SOR_DP_CTRL_LANE_2_ENABLED 0x00040000
#define NV50_SOR_DP_CTRL_LANE_3_ENABLED 0x00080000
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN 0x0f000000
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
#define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
#define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
#define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000)
#define NV50_PDISPLAY_USER_GET(i) ((i) * 0x1000 + 0x00640004)
#define NV50_PDISPLAY_CURSOR_USER 0x00647000
#define NV50_PDISPLAY_CURSOR_USER_POS_CTRL(i) ((i) * 0x1000 + 0x00647080)
#define NV50_PDISPLAY_CURSOR_USER_POS(i) ((i) * 0x1000 + 0x00647084)

View File

@ -0,0 +1,321 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include <linux/pagemap.h>
#define NV_CTXDMA_PAGE_SHIFT 12
#define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT)
#define NV_CTXDMA_PAGE_MASK (NV_CTXDMA_PAGE_SIZE - 1)
struct nouveau_sgdma_be {
struct ttm_backend backend;
struct drm_device *dev;
dma_addr_t *pages;
unsigned nr_pages;
unsigned pte_start;
bool bound;
};
static int
nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
struct page **pages, struct page *dummy_read_page)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev;
NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
if (nvbe->pages)
return -EINVAL;
nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);
if (!nvbe->pages)
return -ENOMEM;
nvbe->nr_pages = 0;
while (num_pages--) {
nvbe->pages[nvbe->nr_pages] =
pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
if (pci_dma_mapping_error(dev->pdev,
nvbe->pages[nvbe->nr_pages])) {
be->func->clear(be);
return -EFAULT;
}
nvbe->nr_pages++;
}
return 0;
}
static void
nouveau_sgdma_clear(struct ttm_backend *be)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev;
NV_DEBUG(nvbe->dev, "\n");
if (nvbe && nvbe->pages) {
if (nvbe->bound)
be->func->unbind(be);
while (nvbe->nr_pages--) {
pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
}
kfree(nvbe->pages);
nvbe->pages = NULL;
nvbe->nr_pages = 0;
}
}
static inline unsigned
nouveau_sgdma_pte(struct drm_device *dev, uint64_t offset)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
unsigned pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
if (dev_priv->card_type < NV_50)
return pte + 2;
return pte << 1;
}
static int
nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
unsigned i, j, pte;
NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start);
dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT);
nvbe->pte_start = pte;
for (i = 0; i < nvbe->nr_pages; i++) {
dma_addr_t dma_offset = nvbe->pages[i];
uint32_t offset_l = lower_32_bits(dma_offset);
uint32_t offset_h = upper_32_bits(dma_offset);
for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
if (dev_priv->card_type < NV_50)
nv_wo32(dev, gpuobj, pte++, offset_l | 3);
else {
nv_wo32(dev, gpuobj, pte++, offset_l | 0x21);
nv_wo32(dev, gpuobj, pte++, offset_h & 0xff);
}
dma_offset += NV_CTXDMA_PAGE_SIZE;
}
}
dev_priv->engine.instmem.finish_access(nvbe->dev);
if (dev_priv->card_type == NV_50) {
nv_wr32(dev, 0x100c80, 0x00050001);
if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
NV_ERROR(dev, "0x100c80 = 0x%08x\n",
nv_rd32(dev, 0x100c80));
return -EBUSY;
}
nv_wr32(dev, 0x100c80, 0x00000001);
if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
NV_ERROR(dev, "0x100c80 = 0x%08x\n",
nv_rd32(dev, 0x100c80));
return -EBUSY;
}
}
nvbe->bound = true;
return 0;
}
static int
nouveau_sgdma_unbind(struct ttm_backend *be)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
struct drm_device *dev = nvbe->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
unsigned i, j, pte;
NV_DEBUG(dev, "\n");
if (!nvbe->bound)
return 0;
dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
pte = nvbe->pte_start;
for (i = 0; i < nvbe->nr_pages; i++) {
dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus;
for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
if (dev_priv->card_type < NV_50)
nv_wo32(dev, gpuobj, pte++, dma_offset | 3);
else {
nv_wo32(dev, gpuobj, pte++, dma_offset | 0x21);
nv_wo32(dev, gpuobj, pte++, 0x00000000);
}
dma_offset += NV_CTXDMA_PAGE_SIZE;
}
}
dev_priv->engine.instmem.finish_access(nvbe->dev);
nvbe->bound = false;
return 0;
}
static void
nouveau_sgdma_destroy(struct ttm_backend *be)
{
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
if (be) {
NV_DEBUG(nvbe->dev, "\n");
if (nvbe) {
if (nvbe->pages)
be->func->clear(be);
kfree(nvbe);
}
}
}
static struct ttm_backend_func nouveau_sgdma_backend = {
.populate = nouveau_sgdma_populate,
.clear = nouveau_sgdma_clear,
.bind = nouveau_sgdma_bind,
.unbind = nouveau_sgdma_unbind,
.destroy = nouveau_sgdma_destroy
};
struct ttm_backend *
nouveau_sgdma_init_ttm(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_sgdma_be *nvbe;
if (!dev_priv->gart_info.sg_ctxdma)
return NULL;
nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);
if (!nvbe)
return NULL;
nvbe->dev = dev;
nvbe->backend.func = &nouveau_sgdma_backend;
return &nvbe->backend;
}
int
nouveau_sgdma_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *gpuobj = NULL;
uint32_t aper_size, obj_size;
int i, ret;
if (dev_priv->card_type < NV_50) {
aper_size = (64 * 1024 * 1024);
obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4;
obj_size += 8; /* ctxdma header */
} else {
/* 1 entire VM page table */
aper_size = (512 * 1024 * 1024);
obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 8;
}
ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16,
NVOBJ_FLAG_ALLOW_NO_REFS |
NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, &gpuobj);
if (ret) {
NV_ERROR(dev, "Error creating sgdma object: %d\n", ret);
return ret;
}
dev_priv->gart_info.sg_dummy_page =
alloc_page(GFP_KERNEL|__GFP_DMA32);
set_bit(PG_locked, &dev_priv->gart_info.sg_dummy_page->flags);
dev_priv->gart_info.sg_dummy_bus =
pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0,
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
dev_priv->engine.instmem.prepare_access(dev, true);
if (dev_priv->card_type < NV_50) {
/* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and
* confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE
* on those cards? */
nv_wo32(dev, gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |
(1 << 12) /* PT present */ |
(0 << 13) /* PT *not* linear */ |
(NV_DMA_ACCESS_RW << 14) |
(NV_DMA_TARGET_PCI << 16));
nv_wo32(dev, gpuobj, 1, aper_size - 1);
for (i = 2; i < 2 + (aper_size >> 12); i++) {
nv_wo32(dev, gpuobj, i,
dev_priv->gart_info.sg_dummy_bus | 3);
}
} else {
for (i = 0; i < obj_size; i += 8) {
nv_wo32(dev, gpuobj, (i+0)/4,
dev_priv->gart_info.sg_dummy_bus | 0x21);
nv_wo32(dev, gpuobj, (i+4)/4, 0);
}
}
dev_priv->engine.instmem.finish_access(dev);
dev_priv->gart_info.type = NOUVEAU_GART_SGDMA;
dev_priv->gart_info.aper_base = 0;
dev_priv->gart_info.aper_size = aper_size;
dev_priv->gart_info.sg_ctxdma = gpuobj;
return 0;
}
void
nouveau_sgdma_takedown(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->gart_info.sg_dummy_page) {
pci_unmap_page(dev->pdev, dev_priv->gart_info.sg_dummy_bus,
NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
unlock_page(dev_priv->gart_info.sg_dummy_page);
__free_page(dev_priv->gart_info.sg_dummy_page);
dev_priv->gart_info.sg_dummy_page = NULL;
dev_priv->gart_info.sg_dummy_bus = 0;
}
nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma);
}
int
nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
int pte;
pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
if (dev_priv->card_type < NV_50) {
instmem->prepare_access(dev, false);
*page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK;
instmem->finish_access(dev);
return 0;
}
NV_ERROR(dev, "Unimplemented on NV50\n");
return -EINVAL;
}

View File

@ -0,0 +1,811 @@
/*
* Copyright 2005 Stephane Marchesin
* Copyright 2008 Stuart Bennett
* 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
* PRECISION INSIGHT 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 <linux/swab.h>
#include "drmP.h"
#include "drm.h"
#include "drm_sarea.h"
#include "drm_crtc_helper.h"
#include <linux/vgaarb.h>
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nv50_display.h"
static int nouveau_stub_init(struct drm_device *dev) { return 0; }
static void nouveau_stub_takedown(struct drm_device *dev) {}
static int nouveau_init_engine_ptrs(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
switch (dev_priv->chipset & 0xf0) {
case 0x00:
engine->instmem.init = nv04_instmem_init;
engine->instmem.takedown = nv04_instmem_takedown;
engine->instmem.suspend = nv04_instmem_suspend;
engine->instmem.resume = nv04_instmem_resume;
engine->instmem.populate = nv04_instmem_populate;
engine->instmem.clear = nv04_instmem_clear;
engine->instmem.bind = nv04_instmem_bind;
engine->instmem.unbind = nv04_instmem_unbind;
engine->instmem.prepare_access = nv04_instmem_prepare_access;
engine->instmem.finish_access = nv04_instmem_finish_access;
engine->mc.init = nv04_mc_init;
engine->mc.takedown = nv04_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv04_fb_init;
engine->fb.takedown = nv04_fb_takedown;
engine->graph.grclass = nv04_graph_grclass;
engine->graph.init = nv04_graph_init;
engine->graph.takedown = nv04_graph_takedown;
engine->graph.fifo_access = nv04_graph_fifo_access;
engine->graph.channel = nv04_graph_channel;
engine->graph.create_context = nv04_graph_create_context;
engine->graph.destroy_context = nv04_graph_destroy_context;
engine->graph.load_context = nv04_graph_load_context;
engine->graph.unload_context = nv04_graph_unload_context;
engine->fifo.channels = 16;
engine->fifo.init = nv04_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
engine->fifo.channel_id = nv04_fifo_channel_id;
engine->fifo.create_context = nv04_fifo_create_context;
engine->fifo.destroy_context = nv04_fifo_destroy_context;
engine->fifo.load_context = nv04_fifo_load_context;
engine->fifo.unload_context = nv04_fifo_unload_context;
break;
case 0x10:
engine->instmem.init = nv04_instmem_init;
engine->instmem.takedown = nv04_instmem_takedown;
engine->instmem.suspend = nv04_instmem_suspend;
engine->instmem.resume = nv04_instmem_resume;
engine->instmem.populate = nv04_instmem_populate;
engine->instmem.clear = nv04_instmem_clear;
engine->instmem.bind = nv04_instmem_bind;
engine->instmem.unbind = nv04_instmem_unbind;
engine->instmem.prepare_access = nv04_instmem_prepare_access;
engine->instmem.finish_access = nv04_instmem_finish_access;
engine->mc.init = nv04_mc_init;
engine->mc.takedown = nv04_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv10_fb_init;
engine->fb.takedown = nv10_fb_takedown;
engine->graph.grclass = nv10_graph_grclass;
engine->graph.init = nv10_graph_init;
engine->graph.takedown = nv10_graph_takedown;
engine->graph.channel = nv10_graph_channel;
engine->graph.create_context = nv10_graph_create_context;
engine->graph.destroy_context = nv10_graph_destroy_context;
engine->graph.fifo_access = nv04_graph_fifo_access;
engine->graph.load_context = nv10_graph_load_context;
engine->graph.unload_context = nv10_graph_unload_context;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv10_fifo_create_context;
engine->fifo.destroy_context = nv10_fifo_destroy_context;
engine->fifo.load_context = nv10_fifo_load_context;
engine->fifo.unload_context = nv10_fifo_unload_context;
break;
case 0x20:
engine->instmem.init = nv04_instmem_init;
engine->instmem.takedown = nv04_instmem_takedown;
engine->instmem.suspend = nv04_instmem_suspend;
engine->instmem.resume = nv04_instmem_resume;
engine->instmem.populate = nv04_instmem_populate;
engine->instmem.clear = nv04_instmem_clear;
engine->instmem.bind = nv04_instmem_bind;
engine->instmem.unbind = nv04_instmem_unbind;
engine->instmem.prepare_access = nv04_instmem_prepare_access;
engine->instmem.finish_access = nv04_instmem_finish_access;
engine->mc.init = nv04_mc_init;
engine->mc.takedown = nv04_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv10_fb_init;
engine->fb.takedown = nv10_fb_takedown;
engine->graph.grclass = nv20_graph_grclass;
engine->graph.init = nv20_graph_init;
engine->graph.takedown = nv20_graph_takedown;
engine->graph.channel = nv10_graph_channel;
engine->graph.create_context = nv20_graph_create_context;
engine->graph.destroy_context = nv20_graph_destroy_context;
engine->graph.fifo_access = nv04_graph_fifo_access;
engine->graph.load_context = nv20_graph_load_context;
engine->graph.unload_context = nv20_graph_unload_context;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv10_fifo_create_context;
engine->fifo.destroy_context = nv10_fifo_destroy_context;
engine->fifo.load_context = nv10_fifo_load_context;
engine->fifo.unload_context = nv10_fifo_unload_context;
break;
case 0x30:
engine->instmem.init = nv04_instmem_init;
engine->instmem.takedown = nv04_instmem_takedown;
engine->instmem.suspend = nv04_instmem_suspend;
engine->instmem.resume = nv04_instmem_resume;
engine->instmem.populate = nv04_instmem_populate;
engine->instmem.clear = nv04_instmem_clear;
engine->instmem.bind = nv04_instmem_bind;
engine->instmem.unbind = nv04_instmem_unbind;
engine->instmem.prepare_access = nv04_instmem_prepare_access;
engine->instmem.finish_access = nv04_instmem_finish_access;
engine->mc.init = nv04_mc_init;
engine->mc.takedown = nv04_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv10_fb_init;
engine->fb.takedown = nv10_fb_takedown;
engine->graph.grclass = nv30_graph_grclass;
engine->graph.init = nv30_graph_init;
engine->graph.takedown = nv20_graph_takedown;
engine->graph.fifo_access = nv04_graph_fifo_access;
engine->graph.channel = nv10_graph_channel;
engine->graph.create_context = nv20_graph_create_context;
engine->graph.destroy_context = nv20_graph_destroy_context;
engine->graph.load_context = nv20_graph_load_context;
engine->graph.unload_context = nv20_graph_unload_context;
engine->fifo.channels = 32;
engine->fifo.init = nv10_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv10_fifo_create_context;
engine->fifo.destroy_context = nv10_fifo_destroy_context;
engine->fifo.load_context = nv10_fifo_load_context;
engine->fifo.unload_context = nv10_fifo_unload_context;
break;
case 0x40:
case 0x60:
engine->instmem.init = nv04_instmem_init;
engine->instmem.takedown = nv04_instmem_takedown;
engine->instmem.suspend = nv04_instmem_suspend;
engine->instmem.resume = nv04_instmem_resume;
engine->instmem.populate = nv04_instmem_populate;
engine->instmem.clear = nv04_instmem_clear;
engine->instmem.bind = nv04_instmem_bind;
engine->instmem.unbind = nv04_instmem_unbind;
engine->instmem.prepare_access = nv04_instmem_prepare_access;
engine->instmem.finish_access = nv04_instmem_finish_access;
engine->mc.init = nv40_mc_init;
engine->mc.takedown = nv40_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nv40_fb_init;
engine->fb.takedown = nv40_fb_takedown;
engine->graph.grclass = nv40_graph_grclass;
engine->graph.init = nv40_graph_init;
engine->graph.takedown = nv40_graph_takedown;
engine->graph.fifo_access = nv04_graph_fifo_access;
engine->graph.channel = nv40_graph_channel;
engine->graph.create_context = nv40_graph_create_context;
engine->graph.destroy_context = nv40_graph_destroy_context;
engine->graph.load_context = nv40_graph_load_context;
engine->graph.unload_context = nv40_graph_unload_context;
engine->fifo.channels = 32;
engine->fifo.init = nv40_fifo_init;
engine->fifo.takedown = nouveau_stub_takedown;
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
engine->fifo.channel_id = nv10_fifo_channel_id;
engine->fifo.create_context = nv40_fifo_create_context;
engine->fifo.destroy_context = nv40_fifo_destroy_context;
engine->fifo.load_context = nv40_fifo_load_context;
engine->fifo.unload_context = nv40_fifo_unload_context;
break;
case 0x50:
case 0x80: /* gotta love NVIDIA's consistency.. */
case 0x90:
case 0xA0:
engine->instmem.init = nv50_instmem_init;
engine->instmem.takedown = nv50_instmem_takedown;
engine->instmem.suspend = nv50_instmem_suspend;
engine->instmem.resume = nv50_instmem_resume;
engine->instmem.populate = nv50_instmem_populate;
engine->instmem.clear = nv50_instmem_clear;
engine->instmem.bind = nv50_instmem_bind;
engine->instmem.unbind = nv50_instmem_unbind;
engine->instmem.prepare_access = nv50_instmem_prepare_access;
engine->instmem.finish_access = nv50_instmem_finish_access;
engine->mc.init = nv50_mc_init;
engine->mc.takedown = nv50_mc_takedown;
engine->timer.init = nv04_timer_init;
engine->timer.read = nv04_timer_read;
engine->timer.takedown = nv04_timer_takedown;
engine->fb.init = nouveau_stub_init;
engine->fb.takedown = nouveau_stub_takedown;
engine->graph.grclass = nv50_graph_grclass;
engine->graph.init = nv50_graph_init;
engine->graph.takedown = nv50_graph_takedown;
engine->graph.fifo_access = nv50_graph_fifo_access;
engine->graph.channel = nv50_graph_channel;
engine->graph.create_context = nv50_graph_create_context;
engine->graph.destroy_context = nv50_graph_destroy_context;
engine->graph.load_context = nv50_graph_load_context;
engine->graph.unload_context = nv50_graph_unload_context;
engine->fifo.channels = 128;
engine->fifo.init = nv50_fifo_init;
engine->fifo.takedown = nv50_fifo_takedown;
engine->fifo.disable = nv04_fifo_disable;
engine->fifo.enable = nv04_fifo_enable;
engine->fifo.reassign = nv04_fifo_reassign;
engine->fifo.channel_id = nv50_fifo_channel_id;
engine->fifo.create_context = nv50_fifo_create_context;
engine->fifo.destroy_context = nv50_fifo_destroy_context;
engine->fifo.load_context = nv50_fifo_load_context;
engine->fifo.unload_context = nv50_fifo_unload_context;
break;
default:
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
return 1;
}
return 0;
}
static unsigned int
nouveau_vga_set_decode(void *priv, bool state)
{
if (state)
return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
else
return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
}
int
nouveau_card_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine;
struct nouveau_gpuobj *gpuobj;
int ret;
NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
if (dev_priv->init_state == NOUVEAU_CARD_INIT_DONE)
return 0;
vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
/* Initialise internal driver API hooks */
ret = nouveau_init_engine_ptrs(dev);
if (ret)
return ret;
engine = &dev_priv->engine;
dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
/* Parse BIOS tables / Run init tables if card not POSTed */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
ret = nouveau_bios_init(dev);
if (ret)
return ret;
}
ret = nouveau_gpuobj_early_init(dev);
if (ret)
return ret;
/* Initialise instance memory, must happen before mem_init so we
* know exactly how much VRAM we're able to use for "normal"
* purposes.
*/
ret = engine->instmem.init(dev);
if (ret)
return ret;
/* Setup the memory manager */
ret = nouveau_mem_init(dev);
if (ret)
return ret;
ret = nouveau_gpuobj_init(dev);
if (ret)
return ret;
/* PMC */
ret = engine->mc.init(dev);
if (ret)
return ret;
/* PTIMER */
ret = engine->timer.init(dev);
if (ret)
return ret;
/* PFB */
ret = engine->fb.init(dev);
if (ret)
return ret;
/* PGRAPH */
ret = engine->graph.init(dev);
if (ret)
return ret;
/* PFIFO */
ret = engine->fifo.init(dev);
if (ret)
return ret;
/* this call irq_preinstall, register irq handler and
* call irq_postinstall
*/
ret = drm_irq_install(dev);
if (ret)
return ret;
ret = drm_vblank_init(dev, 0);
if (ret)
return ret;
/* what about PVIDEO/PCRTC/PRAMDAC etc? */
ret = nouveau_channel_alloc(dev, &dev_priv->channel,
(struct drm_file *)-2,
NvDmaFB, NvDmaTT);
if (ret)
return ret;
gpuobj = NULL;
ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
0, nouveau_mem_fb_amount(dev),
NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
&gpuobj);
if (ret)
return ret;
ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM,
gpuobj, NULL);
if (ret) {
nouveau_gpuobj_del(dev, &gpuobj);
return ret;
}
gpuobj = NULL;
ret = nouveau_gpuobj_gart_dma_new(dev_priv->channel, 0,
dev_priv->gart_info.aper_size,
NV_DMA_ACCESS_RW, &gpuobj, NULL);
if (ret)
return ret;
ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART,
gpuobj, NULL);
if (ret) {
nouveau_gpuobj_del(dev, &gpuobj);
return ret;
}
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
if (dev_priv->card_type >= NV_50) {
ret = nv50_display_create(dev);
if (ret)
return ret;
} else {
ret = nv04_display_create(dev);
if (ret)
return ret;
}
}
ret = nouveau_backlight_init(dev);
if (ret)
NV_ERROR(dev, "Error %d registering backlight\n", ret);
dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_helper_initial_config(dev);
return 0;
}
static void nouveau_card_takedown(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) {
nouveau_backlight_exit(dev);
if (dev_priv->channel) {
nouveau_channel_free(dev_priv->channel);
dev_priv->channel = NULL;
}
engine->fifo.takedown(dev);
engine->graph.takedown(dev);
engine->fb.takedown(dev);
engine->timer.takedown(dev);
engine->mc.takedown(dev);
mutex_lock(&dev->struct_mutex);
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
mutex_unlock(&dev->struct_mutex);
nouveau_sgdma_takedown(dev);
nouveau_gpuobj_takedown(dev);
nouveau_mem_close(dev);
engine->instmem.takedown(dev);
if (drm_core_check_feature(dev, DRIVER_MODESET))
drm_irq_uninstall(dev);
nouveau_gpuobj_late_takedown(dev);
nouveau_bios_takedown(dev);
vga_client_register(dev->pdev, NULL, NULL, NULL);
dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
}
}
/* here a client dies, release the stuff that was allocated for its
* file_priv */
void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
{
nouveau_channel_cleanup(dev, file_priv);
}
/* first module load, setup the mmio/fb mapping */
/* KMS: we need mmio at load time, not when the first drm client opens. */
int nouveau_firstopen(struct drm_device *dev)
{
return 0;
}
/* if we have an OF card, copy vbios to RAMIN */
static void nouveau_OF_copy_vbios_to_ramin(struct drm_device *dev)
{
#if defined(__powerpc__)
int size, i;
const uint32_t *bios;
struct device_node *dn = pci_device_to_OF_node(dev->pdev);
if (!dn) {
NV_INFO(dev, "Unable to get the OF node\n");
return;
}
bios = of_get_property(dn, "NVDA,BMP", &size);
if (bios) {
for (i = 0; i < size; i += 4)
nv_wi32(dev, i, bios[i/4]);
NV_INFO(dev, "OF bios successfully copied (%d bytes)\n", size);
} else {
NV_INFO(dev, "Unable to get the OF bios\n");
}
#endif
}
int nouveau_load(struct drm_device *dev, unsigned long flags)
{
struct drm_nouveau_private *dev_priv;
uint32_t reg0;
resource_size_t mmio_start_offs;
dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
if (!dev_priv)
return -ENOMEM;
dev->dev_private = dev_priv;
dev_priv->dev = dev;
dev_priv->flags = flags & NOUVEAU_FLAGS;
dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
dev->pci_vendor, dev->pci_device, dev->pdev->class);
dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
if (dev_priv->acpi_dsm)
nouveau_hybrid_setup(dev);
dev_priv->wq = create_workqueue("nouveau");
if (!dev_priv->wq)
return -EINVAL;
/* resource 0 is mmio regs */
/* resource 1 is linear FB */
/* resource 2 is RAMIN (mmio regs + 0x1000000) */
/* resource 6 is bios */
/* map the mmio regs */
mmio_start_offs = pci_resource_start(dev->pdev, 0);
dev_priv->mmio = ioremap(mmio_start_offs, 0x00800000);
if (!dev_priv->mmio) {
NV_ERROR(dev, "Unable to initialize the mmio mapping. "
"Please report your setup to " DRIVER_EMAIL "\n");
return -EINVAL;
}
NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
(unsigned long long)mmio_start_offs);
#ifdef __BIG_ENDIAN
/* Put the card in BE mode if it's not */
if (nv_rd32(dev, NV03_PMC_BOOT_1))
nv_wr32(dev, NV03_PMC_BOOT_1, 0x00000001);
DRM_MEMORYBARRIER();
#endif
/* Time to determine the card architecture */
reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
/* We're dealing with >=NV10 */
if ((reg0 & 0x0f000000) > 0) {
/* Bit 27-20 contain the architecture in hex */
dev_priv->chipset = (reg0 & 0xff00000) >> 20;
/* NV04 or NV05 */
} else if ((reg0 & 0xff00fff0) == 0x20004000) {
dev_priv->chipset = 0x04;
} else
dev_priv->chipset = 0xff;
switch (dev_priv->chipset & 0xf0) {
case 0x00:
case 0x10:
case 0x20:
case 0x30:
dev_priv->card_type = dev_priv->chipset & 0xf0;
break;
case 0x40:
case 0x60:
dev_priv->card_type = NV_40;
break;
case 0x50:
case 0x80:
case 0x90:
case 0xa0:
dev_priv->card_type = NV_50;
break;
default:
NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
return -EINVAL;
}
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
dev_priv->card_type, reg0);
/* map larger RAMIN aperture on NV40 cards */
dev_priv->ramin = NULL;
if (dev_priv->card_type >= NV_40) {
int ramin_bar = 2;
if (pci_resource_len(dev->pdev, ramin_bar) == 0)
ramin_bar = 3;
dev_priv->ramin_size = pci_resource_len(dev->pdev, ramin_bar);
dev_priv->ramin = ioremap(
pci_resource_start(dev->pdev, ramin_bar),
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to init RAMIN mapping, "
"limited instance memory available\n");
}
}
/* On older cards (or if the above failed), create a map covering
* the BAR0 PRAMIN aperture */
if (!dev_priv->ramin) {
dev_priv->ramin_size = 1 * 1024 * 1024;
dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN,
dev_priv->ramin_size);
if (!dev_priv->ramin) {
NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
return -ENOMEM;
}
}
nouveau_OF_copy_vbios_to_ramin(dev);
/* Special flags */
if (dev->pci_device == 0x01a0)
dev_priv->flags |= NV_NFORCE;
else if (dev->pci_device == 0x01f0)
dev_priv->flags |= NV_NFORCE2;
/* For kernel modesetting, init card now and bring up fbcon */
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
int ret = nouveau_card_init(dev);
if (ret)
return ret;
}
return 0;
}
static void nouveau_close(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
/* In the case of an error dev_priv may not be be allocated yet */
if (dev_priv && dev_priv->card_type)
nouveau_card_takedown(dev);
}
/* KMS: we need mmio at load time, not when the first drm client opens. */
void nouveau_lastclose(struct drm_device *dev)
{
if (drm_core_check_feature(dev, DRIVER_MODESET))
return;
nouveau_close(dev);
}
int nouveau_unload(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (drm_core_check_feature(dev, DRIVER_MODESET)) {
if (dev_priv->card_type >= NV_50)
nv50_display_destroy(dev);
else
nv04_display_destroy(dev);
nouveau_close(dev);
}
iounmap(dev_priv->mmio);
iounmap(dev_priv->ramin);
kfree(dev_priv);
dev->dev_private = NULL;
return 0;
}
int
nouveau_ioctl_card_init(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
return nouveau_card_init(dev);
}
int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_nouveau_getparam *getparam = data;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
switch (getparam->param) {
case NOUVEAU_GETPARAM_CHIPSET_ID:
getparam->value = dev_priv->chipset;
break;
case NOUVEAU_GETPARAM_PCI_VENDOR:
getparam->value = dev->pci_vendor;
break;
case NOUVEAU_GETPARAM_PCI_DEVICE:
getparam->value = dev->pci_device;
break;
case NOUVEAU_GETPARAM_BUS_TYPE:
if (drm_device_is_agp(dev))
getparam->value = NV_AGP;
else if (drm_device_is_pcie(dev))
getparam->value = NV_PCIE;
else
getparam->value = NV_PCI;
break;
case NOUVEAU_GETPARAM_FB_PHYSICAL:
getparam->value = dev_priv->fb_phys;
break;
case NOUVEAU_GETPARAM_AGP_PHYSICAL:
getparam->value = dev_priv->gart_info.aper_base;
break;
case NOUVEAU_GETPARAM_PCI_PHYSICAL:
if (dev->sg) {
getparam->value = (unsigned long)dev->sg->virtual;
} else {
NV_ERROR(dev, "Requested PCIGART address, "
"while no PCIGART was created\n");
return -EINVAL;
}
break;
case NOUVEAU_GETPARAM_FB_SIZE:
getparam->value = dev_priv->fb_available_size;
break;
case NOUVEAU_GETPARAM_AGP_SIZE:
getparam->value = dev_priv->gart_info.aper_size;
break;
case NOUVEAU_GETPARAM_VM_VRAM_BASE:
getparam->value = dev_priv->vm_vram_base;
break;
default:
NV_ERROR(dev, "unknown parameter %lld\n", getparam->param);
return -EINVAL;
}
return 0;
}
int
nouveau_ioctl_setparam(struct drm_device *dev, void *data,
struct drm_file *file_priv)
{
struct drm_nouveau_setparam *setparam = data;
NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
switch (setparam->param) {
default:
NV_ERROR(dev, "unknown parameter %lld\n", setparam->param);
return -EINVAL;
}
return 0;
}
/* Wait until (value(reg) & mask) == val, up until timeout has hit */
bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout,
uint32_t reg, uint32_t mask, uint32_t val)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
uint64_t start = ptimer->read(dev);
do {
if ((nv_rd32(dev, reg) & mask) == val)
return true;
} while (ptimer->read(dev) - start < timeout);
return false;
}
/* Waits for PGRAPH to go completely idle */
bool nouveau_wait_for_idle(struct drm_device *dev)
{
if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) {
NV_ERROR(dev, "PGRAPH idle timed out with status 0x%08x\n",
nv_rd32(dev, NV04_PGRAPH_STATUS));
return false;
}
return true;
}

View File

@ -0,0 +1,131 @@
/*
* Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA,
* All Rights Reserved.
* Copyright (c) 2009 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 "drmP.h"
#include "nouveau_drv.h"
static struct vm_operations_struct nouveau_ttm_vm_ops;
static const struct vm_operations_struct *ttm_vm_ops;
static int
nouveau_ttm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct ttm_buffer_object *bo = vma->vm_private_data;
int ret;
if (unlikely(bo == NULL))
return VM_FAULT_NOPAGE;
ret = ttm_vm_ops->fault(vma, vmf);
return ret;
}
int
nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma)
{
struct drm_file *file_priv = filp->private_data;
struct drm_nouveau_private *dev_priv =
file_priv->minor->dev->dev_private;
int ret;
if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
return drm_mmap(filp, vma);
ret = ttm_bo_mmap(filp, vma, &dev_priv->ttm.bdev);
if (unlikely(ret != 0))
return ret;
if (unlikely(ttm_vm_ops == NULL)) {
ttm_vm_ops = vma->vm_ops;
nouveau_ttm_vm_ops = *ttm_vm_ops;
nouveau_ttm_vm_ops.fault = &nouveau_ttm_fault;
}
vma->vm_ops = &nouveau_ttm_vm_ops;
return 0;
}
static int
nouveau_ttm_mem_global_init(struct ttm_global_reference *ref)
{
return ttm_mem_global_init(ref->object);
}
static void
nouveau_ttm_mem_global_release(struct ttm_global_reference *ref)
{
ttm_mem_global_release(ref->object);
}
int
nouveau_ttm_global_init(struct drm_nouveau_private *dev_priv)
{
struct ttm_global_reference *global_ref;
int ret;
global_ref = &dev_priv->ttm.mem_global_ref;
global_ref->global_type = TTM_GLOBAL_TTM_MEM;
global_ref->size = sizeof(struct ttm_mem_global);
global_ref->init = &nouveau_ttm_mem_global_init;
global_ref->release = &nouveau_ttm_mem_global_release;
ret = ttm_global_item_ref(global_ref);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed setting up TTM memory accounting\n");
dev_priv->ttm.mem_global_ref.release = NULL;
return ret;
}
dev_priv->ttm.bo_global_ref.mem_glob = global_ref->object;
global_ref = &dev_priv->ttm.bo_global_ref.ref;
global_ref->global_type = TTM_GLOBAL_TTM_BO;
global_ref->size = sizeof(struct ttm_bo_global);
global_ref->init = &ttm_bo_global_init;
global_ref->release = &ttm_bo_global_release;
ret = ttm_global_item_ref(global_ref);
if (unlikely(ret != 0)) {
DRM_ERROR("Failed setting up TTM BO subsystem\n");
ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
dev_priv->ttm.mem_global_ref.release = NULL;
return ret;
}
return 0;
}
void
nouveau_ttm_global_release(struct drm_nouveau_private *dev_priv)
{
if (dev_priv->ttm.mem_global_ref.release == NULL)
return;
ttm_global_item_unref(&dev_priv->ttm.bo_global_ref.ref);
ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
dev_priv->ttm.mem_global_ref.release = NULL;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,70 @@
#include "drmP.h"
#include "drm_mode.h"
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
static void
nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
{
nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true);
}
static void
nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
{
nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false);
}
static void
nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
{
NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index,
NV_PRAMDAC_CU_START_POS,
XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) |
XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X));
}
static void
crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
{
NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
crtcstate->CRTC[index]);
}
static void
nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
{
struct drm_device *dev = nv_crtc->base.dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
struct drm_crtc *crtc = &nv_crtc->base;
regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] =
MASK(NV_CIO_CRE_HCUR_ASI) |
XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR);
regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] =
XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR);
if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |=
MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL);
regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24;
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
if (dev_priv->card_type == NV_40)
nv_fix_nv40_hw_cursor(dev, nv_crtc->index);
}
int
nv04_cursor_init(struct nouveau_crtc *crtc)
{
crtc->cursor.set_offset = nv04_cursor_set_offset;
crtc->cursor.set_pos = nv04_cursor_set_pos;
crtc->cursor.hide = nv04_cursor_hide;
crtc->cursor.show = nv04_cursor_show;
return 0;
}

View File

@ -0,0 +1,528 @@
/*
* Copyright 2003 NVIDIA, Corporation
* Copyright 2006 Dave Airlie
* Copyright 2007 Maarten Maathuis
* Copyright 2007-2009 Stuart Bennett
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
#include "nvreg.h"
int nv04_dac_output_offset(struct drm_encoder *encoder)
{
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
int offset = 0;
if (dcb->or & (8 | OUTPUT_C))
offset += 0x68;
if (dcb->or & (8 | OUTPUT_B))
offset += 0x2000;
return offset;
}
/*
* arbitrary limit to number of sense oscillations tolerated in one sample
* period (observed to be at least 13 in "nvidia")
*/
#define MAX_HBLANK_OSC 20
/*
* arbitrary limit to number of conflicting sample pairs to tolerate at a
* voltage step (observed to be at least 5 in "nvidia")
*/
#define MAX_SAMPLE_PAIRS 10
static int sample_load_twice(struct drm_device *dev, bool sense[2])
{
int i;
for (i = 0; i < 2; i++) {
bool sense_a, sense_b, sense_b_prime;
int j = 0;
/*
* wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
* then wait for transition 0x4->0x5->0x4: enter hblank, leave
* hblank again
* use a 10ms timeout (guards against crtc being inactive, in
* which case blank state would never change)
*/
if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
0x00000001, 0x00000000))
return -EBUSY;
if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
0x00000001, 0x00000001))
return -EBUSY;
if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
0x00000001, 0x00000000))
return -EBUSY;
udelay(100);
/* when level triggers, sense is _LO_ */
sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
/* take another reading until it agrees with sense_a... */
do {
udelay(100);
sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
if (sense_a != sense_b) {
sense_b_prime =
nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
if (sense_b == sense_b_prime) {
/* ... unless two consecutive subsequent
* samples agree; sense_a is replaced */
sense_a = sense_b;
/* force mis-match so we loop */
sense_b = !sense_a;
}
}
} while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
if (j == MAX_HBLANK_OSC)
/* with so much oscillation, default to sense:LO */
sense[i] = false;
else
sense[i] = sense_a;
}
return 0;
}
static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct drm_device *dev = encoder->dev;
uint8_t saved_seq1, saved_pi, saved_rpc1;
uint8_t saved_palette0[3], saved_palette_mask;
uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
int i;
uint8_t blue;
bool sense = true;
/*
* for this detection to work, there needs to be a mode set up on the
* CRTC. this is presumed to be the case
*/
if (nv_two_heads(dev))
/* only implemented for head A for now */
NVSetOwner(dev, 0);
saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
msleep(10);
saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
for (i = 0; i < 3; i++)
saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
(saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
blue = 8; /* start of test range */
do {
bool sense_pair[2];
nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
/* testing blue won't find monochrome monitors. I don't care */
nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
i = 0;
/* take sample pairs until both samples in the pair agree */
do {
if (sample_load_twice(dev, sense_pair))
goto out;
} while ((sense_pair[0] != sense_pair[1]) &&
++i < MAX_SAMPLE_PAIRS);
if (i == MAX_SAMPLE_PAIRS)
/* too much oscillation defaults to LO */
sense = false;
else
sense = sense_pair[0];
/*
* if sense goes LO before blue ramps to 0x18, monitor is not connected.
* ergo, if blue gets to 0x18, monitor must be connected
*/
} while (++blue < 0x18 && sense);
out:
nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
for (i = 0; i < 3; i++)
nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
if (blue == 0x18) {
NV_TRACE(dev, "Load detected on head A\n");
return connector_status_connected;
}
return connector_status_disconnected;
}
enum drm_connector_status nv17_dac_detect(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput;
int head, present = 0;
#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
if (dcb->type == OUTPUT_TV) {
testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
if (dev_priv->vbios->tvdactestval)
testval = dev_priv->vbios->tvdactestval;
} else {
testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
if (dev_priv->vbios->dactestval)
testval = dev_priv->vbios->dactestval;
}
saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
if (regoffset == 0x68) {
saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
}
saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1);
saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0);
nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV);
nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV);
msleep(4);
saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
head = (saved_routput & 0x100) >> 8;
#if 0
/* if there's a spare crtc, using it will minimise flicker for the case
* where the in-use crtc is in use by an off-chip tmds encoder */
if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
head ^= 1;
#endif
/* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
routput = (saved_routput & 0xfffffece) | head << 8;
if (dev_priv->card_type >= NV_40) {
if (dcb->type == OUTPUT_TV)
routput |= 0x1a << 16;
else
routput &= ~(0x1a << 16);
}
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
msleep(1);
temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
msleep(5);
temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
if (dcb->type == OUTPUT_TV)
present = (nv17_tv_detect(encoder, connector, temp)
== connector_status_connected);
else
present = temp & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI;
temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
/* bios does something more complex for restoring, but I think this is good enough */
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
if (regoffset == 0x68)
nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1);
nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0);
if (present) {
NV_INFO(dev, "Load detected on output %c\n", '@' + ffs(dcb->or));
return connector_status_connected;
}
return connector_status_disconnected;
}
static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
return true;
}
static void nv04_dac_prepare(struct drm_encoder *encoder)
{
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int head = nouveau_crtc(encoder->crtc)->index;
struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
helper->dpms(encoder, DRM_MODE_DPMS_OFF);
nv04_dfp_disable(dev, head);
/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
* at LCD__INDEX which we don't alter
*/
if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
}
static void nv04_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 drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int head = nouveau_crtc(encoder->crtc)->index;
NV_TRACE(dev, "%s called for encoder %d\n", __func__,
nv_encoder->dcb->index);
if (nv_gf4_disp_arch(dev)) {
struct drm_encoder *rebind;
uint32_t dac_offset = nv04_dac_output_offset(encoder);
uint32_t otherdac;
/* bit 16-19 are bits that are set on some G70 cards,
* but don't seem to have much effect */
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
/* force any other vga encoders to bind to the other crtc */
list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
if (rebind == encoder
|| nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG)
continue;
dac_offset = nv04_dac_output_offset(rebind);
otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
(otherdac & ~0x0100) | (head ^ 1) << 8);
}
}
/* This could use refinement for flatpanels, but it should work this way */
if (dev_priv->chipset < 0x44)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
}
static void nv04_dac_commit(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
if (nv_gf4_disp_arch(dev)) {
uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1];
int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
if (enable) {
*dac_users |= 1 << dcb->index;
NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
} else {
*dac_users &= ~(1 << dcb->index);
if (!*dac_users)
NVWriteRAMDAC(dev, 0, dacclk_off,
dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
}
}
}
static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (nv_encoder->last_dpms == mode)
return;
nv_encoder->last_dpms = mode;
NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
mode, nv_encoder->dcb->index);
nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
}
static void nv04_dac_save(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
if (nv_gf4_disp_arch(dev))
nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
nv04_dac_output_offset(encoder));
}
static void nv04_dac_restore(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
if (nv_gf4_disp_arch(dev))
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
nv_encoder->restore.output);
nv_encoder->last_dpms = NV_DPMS_CLEARED;
}
static void nv04_dac_destroy(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
NV_DEBUG(encoder->dev, "\n");
drm_encoder_cleanup(encoder);
kfree(nv_encoder);
}
static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
.dpms = nv04_dac_dpms,
.save = nv04_dac_save,
.restore = nv04_dac_restore,
.mode_fixup = nv04_dac_mode_fixup,
.prepare = nv04_dac_prepare,
.commit = nv04_dac_commit,
.mode_set = nv04_dac_mode_set,
.detect = nv04_dac_detect
};
static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
.dpms = nv04_dac_dpms,
.save = nv04_dac_save,
.restore = nv04_dac_restore,
.mode_fixup = nv04_dac_mode_fixup,
.prepare = nv04_dac_prepare,
.commit = nv04_dac_commit,
.mode_set = nv04_dac_mode_set,
.detect = nv17_dac_detect
};
static const struct drm_encoder_funcs nv04_dac_funcs = {
.destroy = nv04_dac_destroy,
};
int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
{
const struct drm_encoder_helper_funcs *helper;
struct drm_encoder *encoder;
struct nouveau_encoder *nv_encoder = NULL;
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;
if (nv_gf4_disp_arch(dev))
helper = &nv17_dac_helper_funcs;
else
helper = &nv04_dac_helper_funcs;
drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
drm_encoder_helper_add(encoder, helper);
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
return 0;
}

View File

@ -0,0 +1,621 @@
/*
* Copyright 2003 NVIDIA, Corporation
* Copyright 2006 Dave Airlie
* Copyright 2007 Maarten Maathuis
* Copyright 2007-2009 Stuart Bennett
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
#include "nvreg.h"
#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \
NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \
NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
static inline bool is_fpc_off(uint32_t fpc)
{
return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) ==
FP_TG_CONTROL_OFF);
}
int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent)
{
/* special case of nv_read_tmds to find crtc associated with an output.
* this does not give a correct answer for off-chip dvi, but there's no
* use for such an answer anyway
*/
int ramdac = (dcbent->or & OUTPUT_C) >> 2;
NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac;
}
void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
int head, bool dl)
{
/* The BIOS scripts don't do this for us, sadly
* Luckily we do know the values ;-)
*
* head < 0 indicates we wish to force a setting with the overrideval
* (for VT restore etc.)
*/
int ramdac = (dcbent->or & OUTPUT_C) >> 2;
uint8_t tmds04 = 0x80;
if (head != ramdac)
tmds04 = 0x88;
if (dcbent->type == OUTPUT_LVDS)
tmds04 |= 0x01;
nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
if (dl) /* dual link */
nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
}
void nv04_dfp_disable(struct drm_device *dev, int head)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
FP_TG_CONTROL_ON) {
/* digital remnants must be cleaned before new crtc
* values programmed. delay is time for the vga stuff
* to realise it's in control again
*/
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
FP_TG_CONTROL_OFF);
msleep(50);
}
/* don't inadvertently turn it on when state written later */
crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
}
void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_crtc *crtc;
struct nouveau_crtc *nv_crtc;
uint32_t *fpc;
if (mode == DRM_MODE_DPMS_ON) {
nv_crtc = nouveau_crtc(encoder->crtc);
fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
if (is_fpc_off(*fpc)) {
/* using saved value is ok, as (is_digital && dpms_on &&
* fp_control==OFF) is (at present) *only* true when
* fpc's most recent change was by below "off" code
*/
*fpc = nv_crtc->dpms_saved_fp_control;
}
nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index;
NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
} else {
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
nv_crtc = nouveau_crtc(crtc);
fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index);
if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
nv_crtc->dpms_saved_fp_control = *fpc;
/* cut the FP output */
*fpc &= ~FP_TG_CONTROL_ON;
*fpc |= FP_TG_CONTROL_OFF;
NVWriteRAMDAC(dev, nv_crtc->index,
NV_PRAMDAC_FP_TG_CONTROL, *fpc);
}
}
}
}
static bool nv04_dfp_mode_fixup(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_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
/* For internal panels and gpu scaling on DVI we need the native mode */
if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
if (!nv_connector->native_mode)
return false;
nv_encoder->mode = *nv_connector->native_mode;
adjusted_mode->clock = nv_connector->native_mode->clock;
} else {
nv_encoder->mode = *adjusted_mode;
}
return true;
}
static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
struct nouveau_encoder *nv_encoder, int head)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_mode_state *state = &dev_priv->mode_reg;
uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000;
if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
return;
/* SEL_CLK is only used on the primary ramdac
* It toggles spread spectrum PLL output and sets the bindings of PLLs
* to heads on digital outputs
*/
if (head)
state->sel_clk |= bits1618;
else
state->sel_clk &= ~bits1618;
/* nv30:
* bit 0 NVClk spread spectrum on/off
* bit 2 MemClk spread spectrum on/off
* bit 4 PixClk1 spread spectrum on/off toggle
* bit 6 PixClk2 spread spectrum on/off toggle
*
* nv40 (observations from bios behaviour and mmio traces):
* bits 4&6 as for nv30
* bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6;
* maybe a different spread mode
* bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts)
* The logic behind turning spread spectrum on/off in the first place,
* and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table
* entry has the necessary info)
*/
if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) {
int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
state->sel_clk &= ~0xf0;
state->sel_clk |= (head ? 0x40 : 0x10) << shift;
}
}
static void nv04_dfp_prepare(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int head = nouveau_crtc(encoder->crtc)->index;
struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
helper->dpms(encoder, DRM_MODE_DPMS_OFF);
nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
* at LCD__INDEX which we don't alter
*/
if (!(*cr_lcd & 0x44)) {
*cr_lcd = 0x3;
if (nv_two_heads(dev)) {
if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
*cr_lcd |= head ? 0x0 : 0x8;
else {
*cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
if (nv_encoder->dcb->type == OUTPUT_LVDS)
*cr_lcd |= 0x30;
if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
/* avoid being connected to both crtcs */
*cr_lcd_oth &= ~0x30;
NVWriteVgaCrtc(dev, head ^ 1,
NV_CIO_CRE_LCD__INDEX,
*cr_lcd_oth);
}
}
}
}
}
static void nv04_dfp_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_display_mode *output_mode = &nv_encoder->mode;
uint32_t mode_ratio, panel_ratio;
NV_DEBUG(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
drm_mode_debug_printmodeline(output_mode);
/* Initialize the FP registers in this CRTC. */
regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
if (!nv_gf4_disp_arch(dev) ||
(output_mode->hsync_start - output_mode->hdisplay) >=
dev_priv->vbios->digital_min_front_porch)
regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
else
regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1;
regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1;
regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
regp->fp_vert_regs[FP_VALID_START] = 0;
regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1;
/* bit26: a bit seen on some g7x, no as yet discernable purpose */
regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
(savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
/* Deal with vsync/hsync polarity */
/* LVDS screens do set this, but modes with +ve syncs are very rare */
if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
/* panel scaling first, as native would get set otherwise */
if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
else if (adjusted_mode->hdisplay == output_mode->hdisplay &&
adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
else /* gpu needs to scale */
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
output_mode->clock > 165000)
regp->fp_control |= (2 << 24);
if (nv_encoder->dcb->type == OUTPUT_LVDS) {
bool duallink, dummy;
nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
clock, &duallink, &dummy);
if (duallink)
regp->fp_control |= (8 << 28);
} else
if (output_mode->clock > 165000)
regp->fp_control |= (8 << 28);
regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
/* We want automatic scaling */
regp->fp_debug_1 = 0;
/* This can override HTOTAL and VTOTAL */
regp->fp_debug_2 = 0;
/* Use 20.12 fixed point format to avoid floats */
mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay;
panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay;
/* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
* get treated the same as SCALE_FULLSCREEN */
if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
mode_ratio != panel_ratio) {
uint32_t diff, scale;
bool divide_by_2 = nv_gf4_disp_arch(dev);
if (mode_ratio < panel_ratio) {
/* vertical needs to expand to glass size (automatic)
* horizontal needs to be scaled at vertical scale factor
* to maintain aspect */
scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay;
regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
/* restrict area of screen used, horizontally */
diff = output_mode->hdisplay -
output_mode->vdisplay * mode_ratio / (1 << 12);
regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
}
if (mode_ratio > panel_ratio) {
/* horizontal needs to expand to glass size (automatic)
* vertical needs to be scaled at horizontal scale factor
* to maintain aspect */
scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay;
regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
/* restrict area of screen used, vertically */
diff = output_mode->vdisplay -
(1 << 12) * output_mode->hdisplay / mode_ratio;
regp->fp_vert_regs[FP_VALID_START] += diff / 2;
regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
}
}
/* Output property. */
if (nv_connector->use_dithering) {
if (dev_priv->chipset == 0x11)
regp->dither = savep->dither | 0x00010000;
else {
int i;
regp->dither = savep->dither | 0x00000001;
for (i = 0; i < 3; i++) {
regp->dither_regs[i] = 0xe4e4e4e4;
regp->dither_regs[i + 3] = 0x44444444;
}
}
} else {
if (dev_priv->chipset != 0x11) {
/* reset them */
int i;
for (i = 0; i < 3; i++) {
regp->dither_regs[i] = savep->dither_regs[i];
regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
}
}
regp->dither = savep->dither;
}
regp->fp_margin_color = 0;
}
static void nv04_dfp_commit(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct dcb_entry *dcbe = nv_encoder->dcb;
int head = nouveau_crtc(encoder->crtc)->index;
NV_TRACE(dev, "%s called for encoder %d\n", __func__, nv_encoder->dcb->index);
if (dcbe->type == OUTPUT_TMDS)
run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
else if (dcbe->type == OUTPUT_LVDS)
call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock);
/* update fp_control state for any changes made by scripts,
* so correct value is written at DPMS on */
dev_priv->mode_reg.crtc_reg[head].fp_control =
NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
/* This could use refinement for flatpanels, but it should work this way */
if (dev_priv->chipset < 0x44)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
static inline bool is_powersaving_dpms(int mode)
{
return (mode != DRM_MODE_DPMS_ON);
}
static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_crtc *crtc = encoder->crtc;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
if (nv_encoder->last_dpms == mode)
return;
nv_encoder->last_dpms = mode;
NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
mode, nv_encoder->dcb->index);
if (was_powersaving && is_powersaving_dpms(mode))
return;
if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
/* when removing an output, crtc may not be set, but PANEL_OFF
* must still be run
*/
int head = crtc ? nouveau_crtc(crtc)->index :
nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
if (mode == DRM_MODE_DPMS_ON) {
if (!nv_connector->native_mode) {
NV_ERROR(dev, "Not turning on LVDS without native mode\n");
return;
}
call_lvds_script(dev, nv_encoder->dcb, head,
LVDS_PANEL_ON, nv_connector->native_mode->clock);
} else
/* pxclk of 0 is fine for PANEL_OFF, and for a
* disconnected LVDS encoder there is no native_mode
*/
call_lvds_script(dev, nv_encoder->dcb, head,
LVDS_PANEL_OFF, 0);
}
nv04_dfp_update_fp_control(encoder, mode);
if (mode == DRM_MODE_DPMS_ON)
nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
else {
dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
dev_priv->mode_reg.sel_clk &= ~0xf0;
}
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
}
static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (nv_encoder->last_dpms == mode)
return;
nv_encoder->last_dpms = mode;
NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
mode, nv_encoder->dcb->index);
nv04_dfp_update_fp_control(encoder, mode);
}
static void nv04_dfp_save(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
if (nv_two_heads(dev))
nv_encoder->restore.head =
nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
}
static void nv04_dfp_restore(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int head = nv_encoder->restore.head;
if (nv_encoder->dcb->type == OUTPUT_LVDS) {
struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode;
if (native_mode)
call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
native_mode->clock);
else
NV_ERROR(dev, "Not restoring LVDS without native mode\n");
} else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
int clock = nouveau_hw_pllvals_to_clk
(&dev_priv->saved_reg.crtc_reg[head].pllvals);
run_tmds_table(dev, nv_encoder->dcb, head, clock);
}
nv_encoder->last_dpms = NV_DPMS_CLEARED;
}
static void nv04_dfp_destroy(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
NV_DEBUG(encoder->dev, "\n");
drm_encoder_cleanup(encoder);
kfree(nv_encoder);
}
static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
.dpms = nv04_lvds_dpms,
.save = nv04_dfp_save,
.restore = nv04_dfp_restore,
.mode_fixup = nv04_dfp_mode_fixup,
.prepare = nv04_dfp_prepare,
.commit = nv04_dfp_commit,
.mode_set = nv04_dfp_mode_set,
.detect = NULL,
};
static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
.dpms = nv04_tmds_dpms,
.save = nv04_dfp_save,
.restore = nv04_dfp_restore,
.mode_fixup = nv04_dfp_mode_fixup,
.prepare = nv04_dfp_prepare,
.commit = nv04_dfp_commit,
.mode_set = nv04_dfp_mode_set,
.detect = NULL,
};
static const struct drm_encoder_funcs nv04_dfp_funcs = {
.destroy = nv04_dfp_destroy,
};
int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
{
const struct drm_encoder_helper_funcs *helper;
struct drm_encoder *encoder;
struct nouveau_encoder *nv_encoder = NULL;
int type;
switch (entry->type) {
case OUTPUT_TMDS:
type = DRM_MODE_ENCODER_TMDS;
helper = &nv04_tmds_helper_funcs;
break;
case OUTPUT_LVDS:
type = DRM_MODE_ENCODER_LVDS;
helper = &nv04_lvds_helper_funcs;
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;
drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
drm_encoder_helper_add(encoder, helper);
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
return 0;
}

View File

@ -0,0 +1,288 @@
/*
* Copyright 2009 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.
*
* Author: Ben Skeggs
*/
#include "drmP.h"
#include "drm.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_fb.h"
#include "nouveau_hw.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#define MULTIPLE_ENCODERS(e) (e & (e - 1))
static void
nv04_display_store_initial_head_owner(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->chipset != 0x11) {
dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44);
goto ownerknown;
}
/* reading CR44 is broken on nv11, so we attempt to infer it */
if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28)) /* heads tied, restore both */
dev_priv->crtc_owner = 0x4;
else {
uint8_t slaved_on_A, slaved_on_B;
bool tvA = false;
bool tvB = false;
NVLockVgaCrtcs(dev, false);
slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) &
0x80;
if (slaved_on_B)
tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) &
MASK(NV_CIO_CRE_LCD_LCD_SELECT));
slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) &
0x80;
if (slaved_on_A)
tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) &
MASK(NV_CIO_CRE_LCD_LCD_SELECT));
NVLockVgaCrtcs(dev, true);
if (slaved_on_A && !tvA)
dev_priv->crtc_owner = 0x0;
else if (slaved_on_B && !tvB)
dev_priv->crtc_owner = 0x3;
else if (slaved_on_A)
dev_priv->crtc_owner = 0x0;
else if (slaved_on_B)
dev_priv->crtc_owner = 0x3;
else
dev_priv->crtc_owner = 0x0;
}
ownerknown:
NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner);
/* we need to ensure the heads are not tied henceforth, or reading any
* 8 bit reg on head B will fail
* setting a single arbitrary head solves that */
NVSetOwner(dev, 0);
}
int
nv04_display_create(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct parsed_dcb *dcb = dev_priv->vbios->dcb;
struct drm_encoder *encoder;
struct drm_crtc *crtc;
uint16_t connector[16] = { 0 };
int i, ret;
NV_DEBUG(dev, "\n");
if (nv_two_heads(dev))
nv04_display_store_initial_head_owner(dev);
drm_mode_config_init(dev);
drm_mode_create_scaling_mode_property(dev);
drm_mode_create_dithering_property(dev);
dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
dev->mode_config.min_width = 0;
dev->mode_config.min_height = 0;
switch (dev_priv->card_type) {
case NV_04:
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
break;
default:
dev->mode_config.max_width = 4096;
dev->mode_config.max_height = 4096;
break;
}
dev->mode_config.fb_base = dev_priv->fb_phys;
nv04_crtc_create(dev, 0);
if (nv_two_heads(dev))
nv04_crtc_create(dev, 1);
for (i = 0; i < dcb->entries; i++) {
struct dcb_entry *dcbent = &dcb->entry[i];
switch (dcbent->type) {
case OUTPUT_ANALOG:
ret = nv04_dac_create(dev, dcbent);
break;
case OUTPUT_LVDS:
case OUTPUT_TMDS:
ret = nv04_dfp_create(dev, dcbent);
break;
case OUTPUT_TV:
if (dcbent->location == DCB_LOC_ON_CHIP)
ret = nv17_tv_create(dev, dcbent);
else
ret = nv04_tv_create(dev, dcbent);
break;
default:
NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
continue;
}
if (ret)
continue;
connector[dcbent->connector] |= (1 << dcbent->type);
}
for (i = 0; i < dcb->entries; i++) {
struct dcb_entry *dcbent = &dcb->entry[i];
uint16_t encoders;
int type;
encoders = connector[dcbent->connector];
if (!(encoders & (1 << dcbent->type)))
continue;
connector[dcbent->connector] = 0;
switch (dcbent->type) {
case OUTPUT_ANALOG:
if (!MULTIPLE_ENCODERS(encoders))
type = DRM_MODE_CONNECTOR_VGA;
else
type = DRM_MODE_CONNECTOR_DVII;
break;
case OUTPUT_TMDS:
if (!MULTIPLE_ENCODERS(encoders))
type = DRM_MODE_CONNECTOR_DVID;
else
type = DRM_MODE_CONNECTOR_DVII;
break;
case OUTPUT_LVDS:
type = DRM_MODE_CONNECTOR_LVDS;
#if 0
/* don't create i2c adapter when lvds ddc not allowed */
if (dcbent->lvdsconf.use_straps_for_mode ||
dev_priv->vbios->fp_no_ddc)
i2c_index = 0xf;
#endif
break;
case OUTPUT_TV:
type = DRM_MODE_CONNECTOR_TV;
break;
default:
type = DRM_MODE_CONNECTOR_Unknown;
continue;
}
nouveau_connector_create(dev, dcbent->connector, type);
}
/* Save previous state */
NVLockVgaCrtcs(dev, false);
nouveau_hw_save_vga_fonts(dev, 1);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
crtc->funcs->save(crtc);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct drm_encoder_helper_funcs *func = encoder->helper_private;
func->save(encoder);
}
return 0;
}
void
nv04_display_destroy(struct drm_device *dev)
{
struct drm_encoder *encoder;
struct drm_crtc *crtc;
NV_DEBUG(dev, "\n");
/* Turn every CRTC off. */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct drm_mode_set modeset = {
.crtc = crtc,
};
crtc->funcs->set_config(&modeset);
}
/* Restore state */
NVLockVgaCrtcs(dev, false);
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct drm_encoder_helper_funcs *func = encoder->helper_private;
func->restore(encoder);
}
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
crtc->funcs->restore(crtc);
nouveau_hw_save_vga_fonts(dev, 0);
drm_mode_config_cleanup(dev);
}
void
nv04_display_restore(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_encoder *encoder;
struct drm_crtc *crtc;
NVLockVgaCrtcs(dev, false);
/* meh.. modeset apparently doesn't setup all the regs and depends
* on pre-existing state, for now load the state of the card *before*
* nouveau was loaded, and then do a modeset.
*
* best thing to do probably is to make save/restore routines not
* save/restore "pre-load" state, but more general so we can save
* on suspend too.
*/
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct drm_encoder_helper_funcs *func = encoder->helper_private;
func->restore(encoder);
}
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
crtc->funcs->restore(crtc);
if (nv_two_heads(dev)) {
NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n",
dev_priv->crtc_owner);
NVSetOwner(dev, dev_priv->crtc_owner);
}
NVLockVgaCrtcs(dev, true);
}

View File

@ -0,0 +1,21 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
int
nv04_fb_init(struct drm_device *dev)
{
/* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
* nvidia reading PFB_CFG_0, then writing back its original value.
* (which was 0x701114 in this case)
*/
nv_wr32(dev, NV04_PFB_CFG0, 0x1114);
return 0;
}
void
nv04_fb_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,316 @@
/*
* Copyright 2009 Ben Skeggs
* Copyright 2008 Stuart Bennett
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
#include "nouveau_fbcon.h"
static void
nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_copyarea(info, region);
return;
}
BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3);
OUT_RING(chan, (region->sy << 16) | region->sx);
OUT_RING(chan, (region->dy << 16) | region->dx);
OUT_RING(chan, (region->height << 16) | region->width);
FIRE_RING(chan);
}
static void
nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
uint32_t color = ((uint32_t *) info->pseudo_palette)[rect->color];
if (info->state != FBINFO_STATE_RUNNING)
return;
if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_fillrect(info, rect);
return;
}
BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3);
BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1);
OUT_RING(chan, color);
BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2);
OUT_RING(chan, (rect->dx << 16) | rect->dy);
OUT_RING(chan, (rect->width << 16) | rect->height);
FIRE_RING(chan);
}
static void
nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
uint32_t fg;
uint32_t bg;
uint32_t dsize;
uint32_t width;
uint32_t *data = (uint32_t *)image->data;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (image->depth != 1) {
cfb_imageblit(info, image);
return;
}
if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_imageblit(info, image);
return;
}
width = (image->width + 31) & ~31;
dsize = (width * image->height) >> 5;
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
} else {
fg = image->fg_color;
bg = image->bg_color;
}
BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7);
OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
OUT_RING(chan, ((image->dy + image->height) << 16) |
((image->dx + image->width) & 0xffff));
OUT_RING(chan, bg);
OUT_RING(chan, fg);
OUT_RING(chan, (image->height << 16) | image->width);
OUT_RING(chan, (image->height << 16) | width);
OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
while (dsize) {
int iter_len = dsize > 128 ? 128 : dsize;
if (RING_SPACE(chan, iter_len + 1)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
cfb_imageblit(info, image);
return;
}
BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
OUT_RINGp(chan, data, iter_len);
data += iter_len;
dsize -= iter_len;
}
FIRE_RING(chan);
}
static int
nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *obj = NULL;
int ret;
ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj);
if (ret)
return ret;
ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL);
if (ret)
return ret;
return 0;
}
int
nv04_fbcon_accel_init(struct fb_info *info)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
int surface_fmt, pattern_fmt, rect_fmt;
int ret;
switch (info->var.bits_per_pixel) {
case 8:
surface_fmt = 1;
pattern_fmt = 3;
rect_fmt = 3;
break;
case 16:
surface_fmt = 4;
pattern_fmt = 1;
rect_fmt = 1;
break;
case 32:
switch (info->var.transp.length) {
case 0: /* depth 24 */
case 8: /* depth 32 */
break;
default:
return -EINVAL;
}
surface_fmt = 6;
pattern_fmt = 3;
rect_fmt = 3;
break;
default:
return -EINVAL;
}
ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
0x0062 : 0x0042, NvCtxSurf2D);
if (ret)
return ret;
ret = nv04_fbcon_grobj_new(dev, 0x0019, NvClipRect);
if (ret)
return ret;
ret = nv04_fbcon_grobj_new(dev, 0x0043, NvRop);
if (ret)
return ret;
ret = nv04_fbcon_grobj_new(dev, 0x0044, NvImagePatt);
if (ret)
return ret;
ret = nv04_fbcon_grobj_new(dev, 0x004a, NvGdiRect);
if (ret)
return ret;
ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
0x009f : 0x005f, NvImageBlit);
if (ret)
return ret;
if (RING_SPACE(chan, 49)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
return 0;
}
BEGIN_RING(chan, 1, 0x0000, 1);
OUT_RING(chan, NvCtxSurf2D);
BEGIN_RING(chan, 1, 0x0184, 2);
OUT_RING(chan, NvDmaFB);
OUT_RING(chan, NvDmaFB);
BEGIN_RING(chan, 1, 0x0300, 4);
OUT_RING(chan, surface_fmt);
OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16));
OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
BEGIN_RING(chan, 1, 0x0000, 1);
OUT_RING(chan, NvRop);
BEGIN_RING(chan, 1, 0x0300, 1);
OUT_RING(chan, 0x55);
BEGIN_RING(chan, 1, 0x0000, 1);
OUT_RING(chan, NvImagePatt);
BEGIN_RING(chan, 1, 0x0300, 8);
OUT_RING(chan, pattern_fmt);
#ifdef __BIG_ENDIAN
OUT_RING(chan, 2);
#else
OUT_RING(chan, 1);
#endif
OUT_RING(chan, 0);
OUT_RING(chan, 1);
OUT_RING(chan, ~0);
OUT_RING(chan, ~0);
OUT_RING(chan, ~0);
OUT_RING(chan, ~0);
BEGIN_RING(chan, 1, 0x0000, 1);
OUT_RING(chan, NvClipRect);
BEGIN_RING(chan, 1, 0x0300, 2);
OUT_RING(chan, 0);
OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual);
BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1);
OUT_RING(chan, NvImageBlit);
BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1);
OUT_RING(chan, NvCtxSurf2D);
BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1);
OUT_RING(chan, 3);
BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1);
OUT_RING(chan, NvGdiRect);
BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1);
OUT_RING(chan, NvCtxSurf2D);
BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2);
OUT_RING(chan, NvImagePatt);
OUT_RING(chan, NvRop);
BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1);
OUT_RING(chan, rect_fmt);
BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
OUT_RING(chan, 3);
FIRE_RING(chan);
info->fbops->fb_fillrect = nv04_fbcon_fillrect;
info->fbops->fb_copyarea = nv04_fbcon_copyarea;
info->fbops->fb_imageblit = nv04_fbcon_imageblit;
return 0;
}

View File

@ -0,0 +1,271 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE))
#define NV04_RAMFC__SIZE 32
#define NV04_RAMFC_DMA_PUT 0x00
#define NV04_RAMFC_DMA_GET 0x04
#define NV04_RAMFC_DMA_INSTANCE 0x08
#define NV04_RAMFC_DMA_STATE 0x0C
#define NV04_RAMFC_DMA_FETCH 0x10
#define NV04_RAMFC_ENGINE 0x14
#define NV04_RAMFC_PULL1_ENGINE 0x18
#define RAMFC_WR(offset, val) nv_wo32(dev, chan->ramfc->gpuobj, \
NV04_RAMFC_##offset/4, (val))
#define RAMFC_RD(offset) nv_ro32(dev, chan->ramfc->gpuobj, \
NV04_RAMFC_##offset/4)
void
nv04_fifo_disable(struct drm_device *dev)
{
uint32_t tmp;
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, tmp & ~1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0);
tmp = nv_rd32(dev, NV03_PFIFO_CACHE1_PULL1);
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, tmp & ~1);
}
void
nv04_fifo_enable(struct drm_device *dev)
{
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
}
bool
nv04_fifo_reassign(struct drm_device *dev, bool enable)
{
uint32_t reassign = nv_rd32(dev, NV03_PFIFO_CACHES);
nv_wr32(dev, NV03_PFIFO_CACHES, enable ? 1 : 0);
return (reassign == 1);
}
int
nv04_fifo_channel_id(struct drm_device *dev)
{
return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
NV03_PFIFO_CACHE1_PUSH1_CHID_MASK;
}
int
nv04_fifo_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int ret;
ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
NV04_RAMFC__SIZE,
NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE,
NULL, &chan->ramfc);
if (ret)
return ret;
/* Setup initial state */
dev_priv->engine.instmem.prepare_access(dev, true);
RAMFC_WR(DMA_PUT, chan->pushbuf_base);
RAMFC_WR(DMA_GET, chan->pushbuf_base);
RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4);
RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
#ifdef __BIG_ENDIAN
NV_PFIFO_CACHE1_BIG_ENDIAN |
#endif
0));
dev_priv->engine.instmem.finish_access(dev);
/* enable the fifo dma operation */
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
return 0;
}
void
nv04_fifo_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
}
static void
nv04_fifo_do_load_context(struct drm_device *dev, int chid)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t fc = NV04_RAMFC(chid), tmp;
dev_priv->engine.instmem.prepare_access(dev, false);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
tmp = nv_ri32(dev, fc + 8);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 12));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 16));
nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20));
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24));
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
}
int
nv04_fifo_load_context(struct nouveau_channel *chan)
{
uint32_t tmp;
nv_wr32(chan->dev, NV03_PFIFO_CACHE1_PUSH1,
NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
nv04_fifo_do_load_context(chan->dev, chan->id);
nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
/* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
tmp = nv_rd32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
return 0;
}
int
nv04_fifo_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan = NULL;
uint32_t tmp;
int chid;
chid = pfifo->channel_id(dev);
if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
return 0;
chan = dev_priv->fifos[chid];
if (!chan) {
NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
return -EINVAL;
}
dev_priv->engine.instmem.prepare_access(dev, true);
RAMFC_WR(DMA_PUT, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
RAMFC_WR(DMA_GET, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16;
tmp |= nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE);
RAMFC_WR(DMA_INSTANCE, tmp);
RAMFC_WR(DMA_STATE, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
RAMFC_WR(DMA_FETCH, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
RAMFC_WR(ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
RAMFC_WR(PULL1_ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
dev_priv->engine.instmem.finish_access(dev);
nv04_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
return 0;
}
static void
nv04_fifo_init_reset(struct drm_device *dev)
{
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
nv_wr32(dev, 0x003224, 0x000f0078);
nv_wr32(dev, 0x002044, 0x0101ffff);
nv_wr32(dev, 0x002040, 0x000000ff);
nv_wr32(dev, 0x002500, 0x00000000);
nv_wr32(dev, 0x003000, 0x00000000);
nv_wr32(dev, 0x003050, 0x00000000);
nv_wr32(dev, 0x003200, 0x00000000);
nv_wr32(dev, 0x003250, 0x00000000);
nv_wr32(dev, 0x003220, 0x00000000);
nv_wr32(dev, 0x003250, 0x00000000);
nv_wr32(dev, 0x003270, 0x00000000);
nv_wr32(dev, 0x003210, 0x00000000);
}
static void
nv04_fifo_init_ramxx(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
((dev_priv->ramht_bits - 9) << 16) |
(dev_priv->ramht_offset >> 8));
nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
}
static void
nv04_fifo_init_intr(struct drm_device *dev)
{
nv_wr32(dev, 0x002100, 0xffffffff);
nv_wr32(dev, 0x002140, 0xffffffff);
}
int
nv04_fifo_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
int i;
nv04_fifo_init_reset(dev);
nv04_fifo_init_ramxx(dev);
nv04_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
nv04_fifo_init_intr(dev);
pfifo->enable(dev);
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
if (dev_priv->fifos[i]) {
uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
}
}
return 0;
}

View File

@ -0,0 +1,579 @@
/*
* Copyright 2007 Stephane Marchesin
* 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
* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "nouveau_drm.h"
#include "nouveau_drv.h"
static uint32_t nv04_graph_ctx_regs[] = {
NV04_PGRAPH_CTX_SWITCH1,
NV04_PGRAPH_CTX_SWITCH2,
NV04_PGRAPH_CTX_SWITCH3,
NV04_PGRAPH_CTX_SWITCH4,
NV04_PGRAPH_CTX_CACHE1,
NV04_PGRAPH_CTX_CACHE2,
NV04_PGRAPH_CTX_CACHE3,
NV04_PGRAPH_CTX_CACHE4,
0x00400184,
0x004001a4,
0x004001c4,
0x004001e4,
0x00400188,
0x004001a8,
0x004001c8,
0x004001e8,
0x0040018c,
0x004001ac,
0x004001cc,
0x004001ec,
0x00400190,
0x004001b0,
0x004001d0,
0x004001f0,
0x00400194,
0x004001b4,
0x004001d4,
0x004001f4,
0x00400198,
0x004001b8,
0x004001d8,
0x004001f8,
0x0040019c,
0x004001bc,
0x004001dc,
0x004001fc,
0x00400174,
NV04_PGRAPH_DMA_START_0,
NV04_PGRAPH_DMA_START_1,
NV04_PGRAPH_DMA_LENGTH,
NV04_PGRAPH_DMA_MISC,
NV04_PGRAPH_DMA_PITCH,
NV04_PGRAPH_BOFFSET0,
NV04_PGRAPH_BBASE0,
NV04_PGRAPH_BLIMIT0,
NV04_PGRAPH_BOFFSET1,
NV04_PGRAPH_BBASE1,
NV04_PGRAPH_BLIMIT1,
NV04_PGRAPH_BOFFSET2,
NV04_PGRAPH_BBASE2,
NV04_PGRAPH_BLIMIT2,
NV04_PGRAPH_BOFFSET3,
NV04_PGRAPH_BBASE3,
NV04_PGRAPH_BLIMIT3,
NV04_PGRAPH_BOFFSET4,
NV04_PGRAPH_BBASE4,
NV04_PGRAPH_BLIMIT4,
NV04_PGRAPH_BOFFSET5,
NV04_PGRAPH_BBASE5,
NV04_PGRAPH_BLIMIT5,
NV04_PGRAPH_BPITCH0,
NV04_PGRAPH_BPITCH1,
NV04_PGRAPH_BPITCH2,
NV04_PGRAPH_BPITCH3,
NV04_PGRAPH_BPITCH4,
NV04_PGRAPH_SURFACE,
NV04_PGRAPH_STATE,
NV04_PGRAPH_BSWIZZLE2,
NV04_PGRAPH_BSWIZZLE5,
NV04_PGRAPH_BPIXEL,
NV04_PGRAPH_NOTIFY,
NV04_PGRAPH_PATT_COLOR0,
NV04_PGRAPH_PATT_COLOR1,
NV04_PGRAPH_PATT_COLORRAM+0x00,
NV04_PGRAPH_PATT_COLORRAM+0x01,
NV04_PGRAPH_PATT_COLORRAM+0x02,
NV04_PGRAPH_PATT_COLORRAM+0x03,
NV04_PGRAPH_PATT_COLORRAM+0x04,
NV04_PGRAPH_PATT_COLORRAM+0x05,
NV04_PGRAPH_PATT_COLORRAM+0x06,
NV04_PGRAPH_PATT_COLORRAM+0x07,
NV04_PGRAPH_PATT_COLORRAM+0x08,
NV04_PGRAPH_PATT_COLORRAM+0x09,
NV04_PGRAPH_PATT_COLORRAM+0x0A,
NV04_PGRAPH_PATT_COLORRAM+0x0B,
NV04_PGRAPH_PATT_COLORRAM+0x0C,
NV04_PGRAPH_PATT_COLORRAM+0x0D,
NV04_PGRAPH_PATT_COLORRAM+0x0E,
NV04_PGRAPH_PATT_COLORRAM+0x0F,
NV04_PGRAPH_PATT_COLORRAM+0x10,
NV04_PGRAPH_PATT_COLORRAM+0x11,
NV04_PGRAPH_PATT_COLORRAM+0x12,
NV04_PGRAPH_PATT_COLORRAM+0x13,
NV04_PGRAPH_PATT_COLORRAM+0x14,
NV04_PGRAPH_PATT_COLORRAM+0x15,
NV04_PGRAPH_PATT_COLORRAM+0x16,
NV04_PGRAPH_PATT_COLORRAM+0x17,
NV04_PGRAPH_PATT_COLORRAM+0x18,
NV04_PGRAPH_PATT_COLORRAM+0x19,
NV04_PGRAPH_PATT_COLORRAM+0x1A,
NV04_PGRAPH_PATT_COLORRAM+0x1B,
NV04_PGRAPH_PATT_COLORRAM+0x1C,
NV04_PGRAPH_PATT_COLORRAM+0x1D,
NV04_PGRAPH_PATT_COLORRAM+0x1E,
NV04_PGRAPH_PATT_COLORRAM+0x1F,
NV04_PGRAPH_PATT_COLORRAM+0x20,
NV04_PGRAPH_PATT_COLORRAM+0x21,
NV04_PGRAPH_PATT_COLORRAM+0x22,
NV04_PGRAPH_PATT_COLORRAM+0x23,
NV04_PGRAPH_PATT_COLORRAM+0x24,
NV04_PGRAPH_PATT_COLORRAM+0x25,
NV04_PGRAPH_PATT_COLORRAM+0x26,
NV04_PGRAPH_PATT_COLORRAM+0x27,
NV04_PGRAPH_PATT_COLORRAM+0x28,
NV04_PGRAPH_PATT_COLORRAM+0x29,
NV04_PGRAPH_PATT_COLORRAM+0x2A,
NV04_PGRAPH_PATT_COLORRAM+0x2B,
NV04_PGRAPH_PATT_COLORRAM+0x2C,
NV04_PGRAPH_PATT_COLORRAM+0x2D,
NV04_PGRAPH_PATT_COLORRAM+0x2E,
NV04_PGRAPH_PATT_COLORRAM+0x2F,
NV04_PGRAPH_PATT_COLORRAM+0x30,
NV04_PGRAPH_PATT_COLORRAM+0x31,
NV04_PGRAPH_PATT_COLORRAM+0x32,
NV04_PGRAPH_PATT_COLORRAM+0x33,
NV04_PGRAPH_PATT_COLORRAM+0x34,
NV04_PGRAPH_PATT_COLORRAM+0x35,
NV04_PGRAPH_PATT_COLORRAM+0x36,
NV04_PGRAPH_PATT_COLORRAM+0x37,
NV04_PGRAPH_PATT_COLORRAM+0x38,
NV04_PGRAPH_PATT_COLORRAM+0x39,
NV04_PGRAPH_PATT_COLORRAM+0x3A,
NV04_PGRAPH_PATT_COLORRAM+0x3B,
NV04_PGRAPH_PATT_COLORRAM+0x3C,
NV04_PGRAPH_PATT_COLORRAM+0x3D,
NV04_PGRAPH_PATT_COLORRAM+0x3E,
NV04_PGRAPH_PATT_COLORRAM+0x3F,
NV04_PGRAPH_PATTERN,
0x0040080c,
NV04_PGRAPH_PATTERN_SHAPE,
0x00400600,
NV04_PGRAPH_ROP3,
NV04_PGRAPH_CHROMA,
NV04_PGRAPH_BETA_AND,
NV04_PGRAPH_BETA_PREMULT,
NV04_PGRAPH_CONTROL0,
NV04_PGRAPH_CONTROL1,
NV04_PGRAPH_CONTROL2,
NV04_PGRAPH_BLEND,
NV04_PGRAPH_STORED_FMT,
NV04_PGRAPH_SOURCE_COLOR,
0x00400560,
0x00400568,
0x00400564,
0x0040056c,
0x00400400,
0x00400480,
0x00400404,
0x00400484,
0x00400408,
0x00400488,
0x0040040c,
0x0040048c,
0x00400410,
0x00400490,
0x00400414,
0x00400494,
0x00400418,
0x00400498,
0x0040041c,
0x0040049c,
0x00400420,
0x004004a0,
0x00400424,
0x004004a4,
0x00400428,
0x004004a8,
0x0040042c,
0x004004ac,
0x00400430,
0x004004b0,
0x00400434,
0x004004b4,
0x00400438,
0x004004b8,
0x0040043c,
0x004004bc,
0x00400440,
0x004004c0,
0x00400444,
0x004004c4,
0x00400448,
0x004004c8,
0x0040044c,
0x004004cc,
0x00400450,
0x004004d0,
0x00400454,
0x004004d4,
0x00400458,
0x004004d8,
0x0040045c,
0x004004dc,
0x00400460,
0x004004e0,
0x00400464,
0x004004e4,
0x00400468,
0x004004e8,
0x0040046c,
0x004004ec,
0x00400470,
0x004004f0,
0x00400474,
0x004004f4,
0x00400478,
0x004004f8,
0x0040047c,
0x004004fc,
0x0040053c,
0x00400544,
0x00400540,
0x00400548,
0x00400560,
0x00400568,
0x00400564,
0x0040056c,
0x00400534,
0x00400538,
0x00400514,
0x00400518,
0x0040051c,
0x00400520,
0x00400524,
0x00400528,
0x0040052c,
0x00400530,
0x00400d00,
0x00400d40,
0x00400d80,
0x00400d04,
0x00400d44,
0x00400d84,
0x00400d08,
0x00400d48,
0x00400d88,
0x00400d0c,
0x00400d4c,
0x00400d8c,
0x00400d10,
0x00400d50,
0x00400d90,
0x00400d14,
0x00400d54,
0x00400d94,
0x00400d18,
0x00400d58,
0x00400d98,
0x00400d1c,
0x00400d5c,
0x00400d9c,
0x00400d20,
0x00400d60,
0x00400da0,
0x00400d24,
0x00400d64,
0x00400da4,
0x00400d28,
0x00400d68,
0x00400da8,
0x00400d2c,
0x00400d6c,
0x00400dac,
0x00400d30,
0x00400d70,
0x00400db0,
0x00400d34,
0x00400d74,
0x00400db4,
0x00400d38,
0x00400d78,
0x00400db8,
0x00400d3c,
0x00400d7c,
0x00400dbc,
0x00400590,
0x00400594,
0x00400598,
0x0040059c,
0x004005a8,
0x004005ac,
0x004005b0,
0x004005b4,
0x004005c0,
0x004005c4,
0x004005c8,
0x004005cc,
0x004005d0,
0x004005d4,
0x004005d8,
0x004005dc,
0x004005e0,
NV04_PGRAPH_PASSTHRU_0,
NV04_PGRAPH_PASSTHRU_1,
NV04_PGRAPH_PASSTHRU_2,
NV04_PGRAPH_DVD_COLORFMT,
NV04_PGRAPH_SCALED_FORMAT,
NV04_PGRAPH_MISC24_0,
NV04_PGRAPH_MISC24_1,
NV04_PGRAPH_MISC24_2,
0x00400500,
0x00400504,
NV04_PGRAPH_VALID1,
NV04_PGRAPH_VALID2
};
struct graph_state {
int nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
};
struct nouveau_channel *
nv04_graph_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int chid = dev_priv->engine.fifo.channels;
if (nv_rd32(dev, NV04_PGRAPH_CTX_CONTROL) & 0x00010000)
chid = nv_rd32(dev, NV04_PGRAPH_CTX_USER) >> 24;
if (chid >= dev_priv->engine.fifo.channels)
return NULL;
return dev_priv->fifos[chid];
}
void
nv04_graph_context_switch(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_channel *chan = NULL;
int chid;
pgraph->fifo_access(dev, false);
nouveau_wait_for_idle(dev);
/* If previous context is valid, we need to save it */
pgraph->unload_context(dev);
/* Load context for next channel */
chid = dev_priv->engine.fifo.channel_id(dev);
chan = dev_priv->fifos[chid];
if (chan)
nv04_graph_load_context(chan);
pgraph->fifo_access(dev, true);
}
int nv04_graph_create_context(struct nouveau_channel *chan)
{
struct graph_state *pgraph_ctx;
NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id);
chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
GFP_KERNEL);
if (pgraph_ctx == NULL)
return -ENOMEM;
/* dev_priv->fifos[channel].pgraph_ctx_user = channel << 24; */
pgraph_ctx->nv04[0] = 0x0001ffff;
/* is it really needed ??? */
#if 0
dev_priv->fifos[channel].pgraph_ctx[1] =
nv_rd32(dev, NV_PGRAPH_DEBUG_4);
dev_priv->fifos[channel].pgraph_ctx[2] =
nv_rd32(dev, 0x004006b0);
#endif
return 0;
}
void nv04_graph_destroy_context(struct nouveau_channel *chan)
{
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
kfree(pgraph_ctx);
chan->pgraph_ctx = NULL;
}
int nv04_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
uint32_t tmp;
int i;
for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
nv_wr32(dev, nv04_graph_ctx_regs[i], pgraph_ctx->nv04[i]);
nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10010100);
nv_wr32(dev, NV04_PGRAPH_CTX_USER, chan->id << 24);
tmp = nv_rd32(dev, NV04_PGRAPH_FFINTFC_ST2);
nv_wr32(dev, NV04_PGRAPH_FFINTFC_ST2, tmp & 0x000fffff);
return 0;
}
int
nv04_graph_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_channel *chan = NULL;
struct graph_state *ctx;
uint32_t tmp;
int i;
chan = pgraph->channel(dev);
if (!chan)
return 0;
ctx = chan->pgraph_ctx;
for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]);
nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10000000);
tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
return 0;
}
int nv04_graph_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t tmp;
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
NV_PMC_ENABLE_PGRAPH);
/* Enable PGRAPH interrupts */
nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_VALID1, 0);
nv_wr32(dev, NV04_PGRAPH_VALID2, 0);
/*nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x000001FF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000);
/*1231C000 blob, 001 haiku*/
//*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/
nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100);
/*0x72111100 blob , 01 haiku*/
/*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/
nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f071);
/*haiku same*/
/*nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/
nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31);
/*haiku and blob 10d4*/
nv_wr32(dev, NV04_PGRAPH_STATE , 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL , 0x10000100);
tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= dev_priv->engine.fifo.channels << 24;
nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
/* These don't belong here, they're part of a per-channel context */
nv_wr32(dev, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000);
nv_wr32(dev, NV04_PGRAPH_BETA_AND , 0xFFFFFFFF);
return 0;
}
void nv04_graph_takedown(struct drm_device *dev)
{
}
void
nv04_graph_fifo_access(struct drm_device *dev, bool enabled)
{
if (enabled)
nv_wr32(dev, NV04_PGRAPH_FIFO,
nv_rd32(dev, NV04_PGRAPH_FIFO) | 1);
else
nv_wr32(dev, NV04_PGRAPH_FIFO,
nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1);
}
static int
nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
chan->fence.last_sequence_irq = data;
nouveau_fence_handler(chan->dev, chan->id);
return 0;
}
static int
nv04_graph_mthd_set_operation(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
struct drm_device *dev = chan->dev;
uint32_t instance = nv_rd32(dev, NV04_PGRAPH_CTX_SWITCH4) & 0xffff;
int subc = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7;
uint32_t tmp;
tmp = nv_ri32(dev, instance);
tmp &= ~0x00038000;
tmp |= ((data & 7) << 15);
nv_wi32(dev, instance, tmp);
nv_wr32(dev, NV04_PGRAPH_CTX_SWITCH1, tmp);
nv_wr32(dev, NV04_PGRAPH_CTX_CACHE1 + subc, tmp);
return 0;
}
static struct nouveau_pgraph_object_method nv04_graph_mthds_m2mf[] = {
{ 0x0150, nv04_graph_mthd_set_ref },
{}
};
static struct nouveau_pgraph_object_method nv04_graph_mthds_set_operation[] = {
{ 0x02fc, nv04_graph_mthd_set_operation },
{},
};
struct nouveau_pgraph_object_class nv04_graph_grclass[] = {
{ 0x0039, false, nv04_graph_mthds_m2mf },
{ 0x004a, false, nv04_graph_mthds_set_operation }, /* gdirect */
{ 0x005f, false, nv04_graph_mthds_set_operation }, /* imageblit */
{ 0x0061, false, nv04_graph_mthds_set_operation }, /* ifc */
{ 0x0077, false, nv04_graph_mthds_set_operation }, /* sifm */
{ 0x0030, false, NULL }, /* null */
{ 0x0042, false, NULL }, /* surf2d */
{ 0x0043, false, NULL }, /* rop */
{ 0x0012, false, NULL }, /* beta1 */
{ 0x0072, false, NULL }, /* beta4 */
{ 0x0019, false, NULL }, /* cliprect */
{ 0x0044, false, NULL }, /* pattern */
{ 0x0052, false, NULL }, /* swzsurf */
{ 0x0053, false, NULL }, /* surf3d */
{ 0x0054, false, NULL }, /* tex_tri */
{ 0x0055, false, NULL }, /* multitex_tri */
{}
};

View File

@ -0,0 +1,208 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
/* returns the size of fifo context */
static int
nouveau_fifo_ctx_size(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->chipset >= 0x40)
return 128;
else
if (dev_priv->chipset >= 0x17)
return 64;
return 32;
}
static void
nv04_instmem_determine_amount(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int i;
/* Figure out how much instance memory we need */
if (dev_priv->card_type >= NV_40) {
/* We'll want more instance memory than this on some NV4x cards.
* There's a 16MB aperture to play with that maps onto the end
* of vram. For now, only reserve a small piece until we know
* more about what each chipset requires.
*/
switch (dev_priv->chipset & 0xf0) {
case 0x40:
case 0x47:
case 0x49:
case 0x4b:
dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
break;
default:
dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
break;
}
} else {
/*XXX: what *are* the limits on <NV40 cards?
*/
dev_priv->ramin_rsvd_vram = (512 * 1024);
}
NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
/* Clear all of it, except the BIOS image that's in the first 64KiB */
dev_priv->engine.instmem.prepare_access(dev, true);
for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
nv_wi32(dev, i, 0x00000000);
dev_priv->engine.instmem.finish_access(dev);
}
static void
nv04_instmem_configure_fixed_tables(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_engine *engine = &dev_priv->engine;
/* FIFO hash table (RAMHT)
* use 4k hash table at RAMIN+0x10000
* TODO: extend the hash table
*/
dev_priv->ramht_offset = 0x10000;
dev_priv->ramht_bits = 9;
dev_priv->ramht_size = (1 << dev_priv->ramht_bits); /* nr entries */
dev_priv->ramht_size *= 8; /* 2 32-bit values per entry in RAMHT */
NV_DEBUG(dev, "RAMHT offset=0x%x, size=%d\n", dev_priv->ramht_offset,
dev_priv->ramht_size);
/* FIFO runout table (RAMRO) - 512k at 0x11200 */
dev_priv->ramro_offset = 0x11200;
dev_priv->ramro_size = 512;
NV_DEBUG(dev, "RAMRO offset=0x%x, size=%d\n", dev_priv->ramro_offset,
dev_priv->ramro_size);
/* FIFO context table (RAMFC)
* NV40 : Not sure exactly how to position RAMFC on some cards,
* 0x30002 seems to position it at RAMIN+0x20000 on these
* cards. RAMFC is 4kb (32 fifos, 128byte entries).
* Others: Position RAMFC at RAMIN+0x11400
*/
dev_priv->ramfc_size = engine->fifo.channels *
nouveau_fifo_ctx_size(dev);
switch (dev_priv->card_type) {
case NV_40:
dev_priv->ramfc_offset = 0x20000;
break;
case NV_30:
case NV_20:
case NV_10:
case NV_04:
default:
dev_priv->ramfc_offset = 0x11400;
break;
}
NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset,
dev_priv->ramfc_size);
}
int nv04_instmem_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t offset;
int ret = 0;
nv04_instmem_determine_amount(dev);
nv04_instmem_configure_fixed_tables(dev);
/* Create a heap to manage RAMIN allocations, we don't allocate
* the space that was reserved for RAMHT/FC/RO.
*/
offset = dev_priv->ramfc_offset + dev_priv->ramfc_size;
/* It appears RAMRO (or something?) is controlled by 0x2220/0x2230
* on certain NV4x chipsets as well as RAMFC. When 0x2230 == 0
* ("new style" control) the upper 16-bits of 0x2220 points at this
* other mysterious table that's clobbering important things.
*
* We're now pointing this at RAMIN+0x30000 to avoid RAMFC getting
* smashed to pieces on us, so reserve 0x30000-0x40000 too..
*/
if (dev_priv->card_type >= NV_40) {
if (offset < 0x40000)
offset = 0x40000;
}
ret = nouveau_mem_init_heap(&dev_priv->ramin_heap,
offset, dev_priv->ramin_rsvd_vram - offset);
if (ret) {
dev_priv->ramin_heap = NULL;
NV_ERROR(dev, "Failed to init RAMIN heap\n");
}
return ret;
}
void
nv04_instmem_takedown(struct drm_device *dev)
{
}
int
nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz)
{
if (gpuobj->im_backing)
return -EINVAL;
return 0;
}
void
nv04_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (gpuobj && gpuobj->im_backing) {
if (gpuobj->im_bound)
dev_priv->engine.instmem.unbind(dev, gpuobj);
gpuobj->im_backing = NULL;
}
}
int
nv04_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
if (!gpuobj->im_pramin || gpuobj->im_bound)
return -EINVAL;
gpuobj->im_bound = 1;
return 0;
}
int
nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
if (gpuobj->im_bound == 0)
return -EINVAL;
gpuobj->im_bound = 0;
return 0;
}
void
nv04_instmem_prepare_access(struct drm_device *dev, bool write)
{
}
void
nv04_instmem_finish_access(struct drm_device *dev)
{
}
int
nv04_instmem_suspend(struct drm_device *dev)
{
return 0;
}
void
nv04_instmem_resume(struct drm_device *dev)
{
}

View File

@ -0,0 +1,20 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
int
nv04_mc_init(struct drm_device *dev)
{
/* Power up everything, resetting each individual unit will
* be done later if needed.
*/
nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
return 0;
}
void
nv04_mc_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,51 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
int
nv04_timer_init(struct drm_device *dev)
{
nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
/* Just use the pre-existing values when possible for now; these regs
* are not written in nv (driver writer missed a /4 on the address), and
* writing 8 and 3 to the correct regs breaks the timings on the LVDS
* hardware sequencing microcode.
* A correct solution (involving calculations with the GPU PLL) can
* be done when kernel modesetting lands
*/
if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008);
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003);
}
return 0;
}
uint64_t
nv04_timer_read(struct drm_device *dev)
{
uint32_t low;
/* From kmmio dumps on nv28 this looks like how the blob does this.
* It reads the high dword twice, before and after.
* The only explanation seems to be that the 64-bit timer counter
* advances between high and low dword reads and may corrupt the
* result. Not confirmed.
*/
uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
uint32_t high1;
do {
high1 = high2;
low = nv_rd32(dev, NV04_PTIMER_TIME_0);
high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
} while (high1 != high2);
return (((uint64_t)high2) << 32) | (uint64_t)low;
}
void
nv04_timer_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,305 @@
/*
* Copyright (C) 2009 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 "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
#include "drm_crtc_helper.h"
#include "i2c/ch7006.h"
static struct {
struct i2c_board_info board_info;
struct drm_encoder_funcs funcs;
struct drm_encoder_helper_funcs hfuncs;
void *params;
} nv04_tv_encoder_info[] = {
{
.board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
.params = &(struct ch7006_encoder_params) {
CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
0, 0, 0,
CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
},
},
};
static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
{
struct i2c_msg msg = {
.addr = addr,
.len = 0,
};
return i2c_transfer(adapter, &msg, 1) == 1;
}
int nv04_tv_identify(struct drm_device *dev, int i2c_index)
{
struct nouveau_i2c_chan *i2c;
bool was_locked;
int i, ret;
NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
i2c = nouveau_i2c_find(dev, i2c_index);
if (!i2c)
return -ENODEV;
was_locked = NVLockVgaCrtcs(dev, false);
for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
if (probe_i2c_addr(&i2c->adapter,
nv04_tv_encoder_info[i].board_info.addr)) {
ret = i;
break;
}
}
if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
NV_TRACE(dev, "Detected TV encoder: %s\n",
nv04_tv_encoder_info[i].board_info.type);
} else {
NV_TRACE(dev, "No TV encoders found.\n");
i = -ENODEV;
}
NVLockVgaCrtcs(dev, was_locked);
return i;
}
#define PLLSEL_TV_CRTC1_MASK \
(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
| NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
#define PLLSEL_TV_CRTC2_MASK \
(NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
| NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_mode_state *state = &dev_priv->mode_reg;
uint8_t crtc1A;
NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
mode, nv_encoder->dcb->index);
state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
if (mode == DRM_MODE_DPMS_ON) {
int head = nouveau_crtc(encoder->crtc)->index;
crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK :
PLLSEL_TV_CRTC1_MASK;
/* Inhibit hsync */
crtc1A |= 0x80;
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
}
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
}
static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
state->tv_setup = 0;
if (bind) {
state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
state->CRTC[NV_CIO_CRE_49] |= 0x10;
} else {
state->CRTC[NV_CIO_CRE_49] &= ~0x10;
}
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
state->CRTC[NV_CIO_CRE_LCD__INDEX]);
NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
state->CRTC[NV_CIO_CRE_49]);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
state->tv_setup);
}
static void nv04_tv_prepare(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
int head = nouveau_crtc(encoder->crtc)->index;
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
helper->dpms(encoder, DRM_MODE_DPMS_OFF);
nv04_dfp_disable(dev, head);
if (nv_two_heads(dev))
nv04_tv_bind(dev, head ^ 1, false);
nv04_tv_bind(dev, head, true);
}
static void nv04_tv_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
regp->tv_htotal = adjusted_mode->htotal;
regp->tv_vtotal = adjusted_mode->vtotal;
/* These delay the TV signals with respect to the VGA port,
* they might be useful if we ever allow a CRTC to drive
* multiple outputs.
*/
regp->tv_hskew = 1;
regp->tv_hsync_delay = 1;
regp->tv_hsync_delay2 = 64;
regp->tv_vskew = 1;
regp->tv_vsync_delay = 1;
to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
}
static void nv04_tv_commit(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index,
'@' + ffs(nv_encoder->dcb->or));
}
static void nv04_tv_destroy(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
drm_encoder_cleanup(encoder);
kfree(nv_encoder);
}
int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
{
struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct i2c_adapter *adap;
struct drm_encoder_funcs *funcs = NULL;
struct drm_encoder_helper_funcs *hfuncs = NULL;
struct drm_encoder_slave_funcs *sfuncs = NULL;
int i2c_index = entry->i2c_index;
int type, ret;
bool was_locked;
/* Ensure that we can talk to this encoder */
type = nv04_tv_identify(dev, i2c_index);
if (type < 0)
return type;
/* Allocate the necessary memory */
nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
if (!nv_encoder)
return -ENOMEM;
/* Initialize the common members */
encoder = to_drm_encoder(nv_encoder);
funcs = &nv04_tv_encoder_info[type].funcs;
hfuncs = &nv04_tv_encoder_info[type].hfuncs;
drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
drm_encoder_helper_add(encoder, hfuncs);
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
nv_encoder->dcb = entry;
nv_encoder->or = ffs(entry->or) - 1;
/* Run the slave-specific initialization */
adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
was_locked = NVLockVgaCrtcs(dev, false);
ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
&nv04_tv_encoder_info[type].board_info);
NVLockVgaCrtcs(dev, was_locked);
if (ret < 0)
goto fail;
/* Fill the function pointers */
sfuncs = to_encoder_slave(encoder)->slave_funcs;
*funcs = (struct drm_encoder_funcs) {
.destroy = nv04_tv_destroy,
};
*hfuncs = (struct drm_encoder_helper_funcs) {
.dpms = nv04_tv_dpms,
.save = sfuncs->save,
.restore = sfuncs->restore,
.mode_fixup = sfuncs->mode_fixup,
.prepare = nv04_tv_prepare,
.commit = nv04_tv_commit,
.mode_set = nv04_tv_mode_set,
.detect = sfuncs->detect,
};
/* Set the slave encoder configuration */
sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
return 0;
fail:
drm_encoder_cleanup(encoder);
kfree(nv_encoder);
return ret;
}

View File

@ -0,0 +1,24 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
int
nv10_fb_init(struct drm_device *dev)
{
uint32_t fb_bar_size;
int i;
fb_bar_size = drm_get_resource_len(dev, 0) - 1;
for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
nv_wr32(dev, NV10_PFB_TILE(i), 0);
nv_wr32(dev, NV10_PFB_TLIMIT(i), fb_bar_size);
}
return 0;
}
void
nv10_fb_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,260 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE))
#define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32)
int
nv10_fifo_channel_id(struct drm_device *dev)
{
return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
NV10_PFIFO_CACHE1_PUSH1_CHID_MASK;
}
int
nv10_fifo_create_context(struct nouveau_channel *chan)
{
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
struct drm_device *dev = chan->dev;
uint32_t fc = NV10_RAMFC(chan->id);
int ret;
ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0,
NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
if (ret)
return ret;
/* Fill entries that are seen filled in dumps of nvidia driver just
* after channel's is put into DMA mode
*/
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wi32(dev, fc + 0, chan->pushbuf_base);
nv_wi32(dev, fc + 4, chan->pushbuf_base);
nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
nv_wi32(dev, fc + 20, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
#ifdef __BIG_ENDIAN
NV_PFIFO_CACHE1_BIG_ENDIAN |
#endif
0);
dev_priv->engine.instmem.finish_access(dev);
/* enable the fifo dma operation */
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
return 0;
}
void
nv10_fifo_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
}
static void
nv10_fifo_do_load_context(struct drm_device *dev, int chid)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t fc = NV10_RAMFC(chid), tmp;
dev_priv->engine.instmem.prepare_access(dev, false);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
tmp = nv_ri32(dev, fc + 12);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 16));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 20));
nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 24));
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 28));
if (dev_priv->chipset < 0x17)
goto out;
nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 32));
tmp = nv_ri32(dev, fc + 36);
nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 40));
nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 44));
nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48));
out:
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
}
int
nv10_fifo_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
uint32_t tmp;
nv10_fifo_do_load_context(dev, chan->id);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
/* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
return 0;
}
int
nv10_fifo_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
uint32_t fc, tmp;
int chid;
chid = pfifo->channel_id(dev);
if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
return 0;
fc = NV10_RAMFC(chid);
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE) & 0xFFFF;
tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16);
nv_wi32(dev, fc + 12, tmp);
nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
nv_wi32(dev, fc + 24, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
if (dev_priv->chipset < 0x17)
goto out;
nv_wi32(dev, fc + 32, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
nv_wi32(dev, fc + 36, tmp);
nv_wi32(dev, fc + 40, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
out:
dev_priv->engine.instmem.finish_access(dev);
nv10_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
return 0;
}
static void
nv10_fifo_init_reset(struct drm_device *dev)
{
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
nv_wr32(dev, 0x003224, 0x000f0078);
nv_wr32(dev, 0x002044, 0x0101ffff);
nv_wr32(dev, 0x002040, 0x000000ff);
nv_wr32(dev, 0x002500, 0x00000000);
nv_wr32(dev, 0x003000, 0x00000000);
nv_wr32(dev, 0x003050, 0x00000000);
nv_wr32(dev, 0x003258, 0x00000000);
nv_wr32(dev, 0x003210, 0x00000000);
nv_wr32(dev, 0x003270, 0x00000000);
}
static void
nv10_fifo_init_ramxx(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
((dev_priv->ramht_bits - 9) << 16) |
(dev_priv->ramht_offset >> 8));
nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
if (dev_priv->chipset < 0x17) {
nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
} else {
nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) |
(1 << 16) /* 64 Bytes entry*/);
/* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */
}
}
static void
nv10_fifo_init_intr(struct drm_device *dev)
{
nv_wr32(dev, 0x002100, 0xffffffff);
nv_wr32(dev, 0x002140, 0xffffffff);
}
int
nv10_fifo_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
int i;
nv10_fifo_init_reset(dev);
nv10_fifo_init_ramxx(dev);
nv10_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
nv10_fifo_init_intr(dev);
pfifo->enable(dev);
pfifo->reassign(dev, true);
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
if (dev_priv->fifos[i]) {
uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
}
}
return 0;
}

View File

@ -0,0 +1,892 @@
/*
* Copyright 2007 Matthieu CASTET <castet.matthieu@free.fr>
* 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
* PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
#include "drmP.h"
#include "drm.h"
#include "nouveau_drm.h"
#include "nouveau_drv.h"
#define NV10_FIFO_NUMBER 32
struct pipe_state {
uint32_t pipe_0x0000[0x040/4];
uint32_t pipe_0x0040[0x010/4];
uint32_t pipe_0x0200[0x0c0/4];
uint32_t pipe_0x4400[0x080/4];
uint32_t pipe_0x6400[0x3b0/4];
uint32_t pipe_0x6800[0x2f0/4];
uint32_t pipe_0x6c00[0x030/4];
uint32_t pipe_0x7000[0x130/4];
uint32_t pipe_0x7400[0x0c0/4];
uint32_t pipe_0x7800[0x0c0/4];
};
static int nv10_graph_ctx_regs[] = {
NV10_PGRAPH_CTX_SWITCH1,
NV10_PGRAPH_CTX_SWITCH2,
NV10_PGRAPH_CTX_SWITCH3,
NV10_PGRAPH_CTX_SWITCH4,
NV10_PGRAPH_CTX_SWITCH5,
NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */
NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */
NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */
NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */
NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */
0x00400164,
0x00400184,
0x004001a4,
0x004001c4,
0x004001e4,
0x00400168,
0x00400188,
0x004001a8,
0x004001c8,
0x004001e8,
0x0040016c,
0x0040018c,
0x004001ac,
0x004001cc,
0x004001ec,
0x00400170,
0x00400190,
0x004001b0,
0x004001d0,
0x004001f0,
0x00400174,
0x00400194,
0x004001b4,
0x004001d4,
0x004001f4,
0x00400178,
0x00400198,
0x004001b8,
0x004001d8,
0x004001f8,
0x0040017c,
0x0040019c,
0x004001bc,
0x004001dc,
0x004001fc,
NV10_PGRAPH_CTX_USER,
NV04_PGRAPH_DMA_START_0,
NV04_PGRAPH_DMA_START_1,
NV04_PGRAPH_DMA_LENGTH,
NV04_PGRAPH_DMA_MISC,
NV10_PGRAPH_DMA_PITCH,
NV04_PGRAPH_BOFFSET0,
NV04_PGRAPH_BBASE0,
NV04_PGRAPH_BLIMIT0,
NV04_PGRAPH_BOFFSET1,
NV04_PGRAPH_BBASE1,
NV04_PGRAPH_BLIMIT1,
NV04_PGRAPH_BOFFSET2,
NV04_PGRAPH_BBASE2,
NV04_PGRAPH_BLIMIT2,
NV04_PGRAPH_BOFFSET3,
NV04_PGRAPH_BBASE3,
NV04_PGRAPH_BLIMIT3,
NV04_PGRAPH_BOFFSET4,
NV04_PGRAPH_BBASE4,
NV04_PGRAPH_BLIMIT4,
NV04_PGRAPH_BOFFSET5,
NV04_PGRAPH_BBASE5,
NV04_PGRAPH_BLIMIT5,
NV04_PGRAPH_BPITCH0,
NV04_PGRAPH_BPITCH1,
NV04_PGRAPH_BPITCH2,
NV04_PGRAPH_BPITCH3,
NV04_PGRAPH_BPITCH4,
NV10_PGRAPH_SURFACE,
NV10_PGRAPH_STATE,
NV04_PGRAPH_BSWIZZLE2,
NV04_PGRAPH_BSWIZZLE5,
NV04_PGRAPH_BPIXEL,
NV10_PGRAPH_NOTIFY,
NV04_PGRAPH_PATT_COLOR0,
NV04_PGRAPH_PATT_COLOR1,
NV04_PGRAPH_PATT_COLORRAM, /* 64 values from 0x400900 to 0x4009fc */
0x00400904,
0x00400908,
0x0040090c,
0x00400910,
0x00400914,
0x00400918,
0x0040091c,
0x00400920,
0x00400924,
0x00400928,
0x0040092c,
0x00400930,
0x00400934,
0x00400938,
0x0040093c,
0x00400940,
0x00400944,
0x00400948,
0x0040094c,
0x00400950,
0x00400954,
0x00400958,
0x0040095c,
0x00400960,
0x00400964,
0x00400968,
0x0040096c,
0x00400970,
0x00400974,
0x00400978,
0x0040097c,
0x00400980,
0x00400984,
0x00400988,
0x0040098c,
0x00400990,
0x00400994,
0x00400998,
0x0040099c,
0x004009a0,
0x004009a4,
0x004009a8,
0x004009ac,
0x004009b0,
0x004009b4,
0x004009b8,
0x004009bc,
0x004009c0,
0x004009c4,
0x004009c8,
0x004009cc,
0x004009d0,
0x004009d4,
0x004009d8,
0x004009dc,
0x004009e0,
0x004009e4,
0x004009e8,
0x004009ec,
0x004009f0,
0x004009f4,
0x004009f8,
0x004009fc,
NV04_PGRAPH_PATTERN, /* 2 values from 0x400808 to 0x40080c */
0x0040080c,
NV04_PGRAPH_PATTERN_SHAPE,
NV03_PGRAPH_MONO_COLOR0,
NV04_PGRAPH_ROP3,
NV04_PGRAPH_CHROMA,
NV04_PGRAPH_BETA_AND,
NV04_PGRAPH_BETA_PREMULT,
0x00400e70,
0x00400e74,
0x00400e78,
0x00400e7c,
0x00400e80,
0x00400e84,
0x00400e88,
0x00400e8c,
0x00400ea0,
0x00400ea4,
0x00400ea8,
0x00400e90,
0x00400e94,
0x00400e98,
0x00400e9c,
NV10_PGRAPH_WINDOWCLIP_HORIZONTAL, /* 8 values from 0x400f00-0x400f1c */
NV10_PGRAPH_WINDOWCLIP_VERTICAL, /* 8 values from 0x400f20-0x400f3c */
0x00400f04,
0x00400f24,
0x00400f08,
0x00400f28,
0x00400f0c,
0x00400f2c,
0x00400f10,
0x00400f30,
0x00400f14,
0x00400f34,
0x00400f18,
0x00400f38,
0x00400f1c,
0x00400f3c,
NV10_PGRAPH_XFMODE0,
NV10_PGRAPH_XFMODE1,
NV10_PGRAPH_GLOBALSTATE0,
NV10_PGRAPH_GLOBALSTATE1,
NV04_PGRAPH_STORED_FMT,
NV04_PGRAPH_SOURCE_COLOR,
NV03_PGRAPH_ABS_X_RAM, /* 32 values from 0x400400 to 0x40047c */
NV03_PGRAPH_ABS_Y_RAM, /* 32 values from 0x400480 to 0x4004fc */
0x00400404,
0x00400484,
0x00400408,
0x00400488,
0x0040040c,
0x0040048c,
0x00400410,
0x00400490,
0x00400414,
0x00400494,
0x00400418,
0x00400498,
0x0040041c,
0x0040049c,
0x00400420,
0x004004a0,
0x00400424,
0x004004a4,
0x00400428,
0x004004a8,
0x0040042c,
0x004004ac,
0x00400430,
0x004004b0,
0x00400434,
0x004004b4,
0x00400438,
0x004004b8,
0x0040043c,
0x004004bc,
0x00400440,
0x004004c0,
0x00400444,
0x004004c4,
0x00400448,
0x004004c8,
0x0040044c,
0x004004cc,
0x00400450,
0x004004d0,
0x00400454,
0x004004d4,
0x00400458,
0x004004d8,
0x0040045c,
0x004004dc,
0x00400460,
0x004004e0,
0x00400464,
0x004004e4,
0x00400468,
0x004004e8,
0x0040046c,
0x004004ec,
0x00400470,
0x004004f0,
0x00400474,
0x004004f4,
0x00400478,
0x004004f8,
0x0040047c,
0x004004fc,
NV03_PGRAPH_ABS_UCLIP_XMIN,
NV03_PGRAPH_ABS_UCLIP_XMAX,
NV03_PGRAPH_ABS_UCLIP_YMIN,
NV03_PGRAPH_ABS_UCLIP_YMAX,
0x00400550,
0x00400558,
0x00400554,
0x0040055c,
NV03_PGRAPH_ABS_UCLIPA_XMIN,
NV03_PGRAPH_ABS_UCLIPA_XMAX,
NV03_PGRAPH_ABS_UCLIPA_YMIN,
NV03_PGRAPH_ABS_UCLIPA_YMAX,
NV03_PGRAPH_ABS_ICLIP_XMAX,
NV03_PGRAPH_ABS_ICLIP_YMAX,
NV03_PGRAPH_XY_LOGIC_MISC0,
NV03_PGRAPH_XY_LOGIC_MISC1,
NV03_PGRAPH_XY_LOGIC_MISC2,
NV03_PGRAPH_XY_LOGIC_MISC3,
NV03_PGRAPH_CLIPX_0,
NV03_PGRAPH_CLIPX_1,
NV03_PGRAPH_CLIPY_0,
NV03_PGRAPH_CLIPY_1,
NV10_PGRAPH_COMBINER0_IN_ALPHA,
NV10_PGRAPH_COMBINER1_IN_ALPHA,
NV10_PGRAPH_COMBINER0_IN_RGB,
NV10_PGRAPH_COMBINER1_IN_RGB,
NV10_PGRAPH_COMBINER_COLOR0,
NV10_PGRAPH_COMBINER_COLOR1,
NV10_PGRAPH_COMBINER0_OUT_ALPHA,
NV10_PGRAPH_COMBINER1_OUT_ALPHA,
NV10_PGRAPH_COMBINER0_OUT_RGB,
NV10_PGRAPH_COMBINER1_OUT_RGB,
NV10_PGRAPH_COMBINER_FINAL0,
NV10_PGRAPH_COMBINER_FINAL1,
0x00400e00,
0x00400e04,
0x00400e08,
0x00400e0c,
0x00400e10,
0x00400e14,
0x00400e18,
0x00400e1c,
0x00400e20,
0x00400e24,
0x00400e28,
0x00400e2c,
0x00400e30,
0x00400e34,
0x00400e38,
0x00400e3c,
NV04_PGRAPH_PASSTHRU_0,
NV04_PGRAPH_PASSTHRU_1,
NV04_PGRAPH_PASSTHRU_2,
NV10_PGRAPH_DIMX_TEXTURE,
NV10_PGRAPH_WDIMX_TEXTURE,
NV10_PGRAPH_DVD_COLORFMT,
NV10_PGRAPH_SCALED_FORMAT,
NV04_PGRAPH_MISC24_0,
NV04_PGRAPH_MISC24_1,
NV04_PGRAPH_MISC24_2,
NV03_PGRAPH_X_MISC,
NV03_PGRAPH_Y_MISC,
NV04_PGRAPH_VALID1,
NV04_PGRAPH_VALID2,
};
static int nv17_graph_ctx_regs[] = {
NV10_PGRAPH_DEBUG_4,
0x004006b0,
0x00400eac,
0x00400eb0,
0x00400eb4,
0x00400eb8,
0x00400ebc,
0x00400ec0,
0x00400ec4,
0x00400ec8,
0x00400ecc,
0x00400ed0,
0x00400ed4,
0x00400ed8,
0x00400edc,
0x00400ee0,
0x00400a00,
0x00400a04,
};
struct graph_state {
int nv10[ARRAY_SIZE(nv10_graph_ctx_regs)];
int nv17[ARRAY_SIZE(nv17_graph_ctx_regs)];
struct pipe_state pipe_state;
};
static void nv10_graph_save_pipe(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
int i;
#define PIPE_SAVE(addr) \
do { \
nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
for (i = 0; i < ARRAY_SIZE(fifo_pipe_state->pipe_##addr); i++) \
fifo_pipe_state->pipe_##addr[i] = nv_rd32(dev, NV10_PGRAPH_PIPE_DATA); \
} while (0)
PIPE_SAVE(0x4400);
PIPE_SAVE(0x0200);
PIPE_SAVE(0x6400);
PIPE_SAVE(0x6800);
PIPE_SAVE(0x6c00);
PIPE_SAVE(0x7000);
PIPE_SAVE(0x7400);
PIPE_SAVE(0x7800);
PIPE_SAVE(0x0040);
PIPE_SAVE(0x0000);
#undef PIPE_SAVE
}
static void nv10_graph_load_pipe(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
int i;
uint32_t xfmode0, xfmode1;
#define PIPE_RESTORE(addr) \
do { \
nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
for (i = 0; i < ARRAY_SIZE(fifo_pipe_state->pipe_##addr); i++) \
nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, fifo_pipe_state->pipe_##addr[i]); \
} while (0)
nouveau_wait_for_idle(dev);
/* XXX check haiku comments */
xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0);
xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1);
nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000);
nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0);
for (i = 0; i < 4; i++)
nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
for (i = 0; i < 4; i++)
nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0);
for (i = 0; i < 3; i++)
nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80);
for (i = 0; i < 3; i++)
nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040);
nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008);
PIPE_RESTORE(0x0200);
nouveau_wait_for_idle(dev);
/* restore XFMODE */
nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0);
nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1);
PIPE_RESTORE(0x6400);
PIPE_RESTORE(0x6800);
PIPE_RESTORE(0x6c00);
PIPE_RESTORE(0x7000);
PIPE_RESTORE(0x7400);
PIPE_RESTORE(0x7800);
PIPE_RESTORE(0x4400);
PIPE_RESTORE(0x0000);
PIPE_RESTORE(0x0040);
nouveau_wait_for_idle(dev);
#undef PIPE_RESTORE
}
static void nv10_graph_create_pipe(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
uint32_t *fifo_pipe_state_addr;
int i;
#define PIPE_INIT(addr) \
do { \
fifo_pipe_state_addr = fifo_pipe_state->pipe_##addr; \
} while (0)
#define PIPE_INIT_END(addr) \
do { \
uint32_t *__end_addr = fifo_pipe_state->pipe_##addr + \
ARRAY_SIZE(fifo_pipe_state->pipe_##addr); \
if (fifo_pipe_state_addr != __end_addr) \
NV_ERROR(dev, "incomplete pipe init for 0x%x : %p/%p\n", \
addr, fifo_pipe_state_addr, __end_addr); \
} while (0)
#define NV_WRITE_PIPE_INIT(value) *(fifo_pipe_state_addr++) = value
PIPE_INIT(0x0200);
for (i = 0; i < 48; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x0200);
PIPE_INIT(0x6400);
for (i = 0; i < 211; i++)
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x3f800000);
NV_WRITE_PIPE_INIT(0x40000000);
NV_WRITE_PIPE_INIT(0x40000000);
NV_WRITE_PIPE_INIT(0x40000000);
NV_WRITE_PIPE_INIT(0x40000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x3f800000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x3f000000);
NV_WRITE_PIPE_INIT(0x3f000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x3f800000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x3f800000);
NV_WRITE_PIPE_INIT(0x3f800000);
NV_WRITE_PIPE_INIT(0x3f800000);
NV_WRITE_PIPE_INIT(0x3f800000);
PIPE_INIT_END(0x6400);
PIPE_INIT(0x6800);
for (i = 0; i < 162; i++)
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x3f800000);
for (i = 0; i < 25; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x6800);
PIPE_INIT(0x6c00);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0xbf800000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x6c00);
PIPE_INIT(0x7000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x00000000);
NV_WRITE_PIPE_INIT(0x7149f2ca);
for (i = 0; i < 35; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x7000);
PIPE_INIT(0x7400);
for (i = 0; i < 48; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x7400);
PIPE_INIT(0x7800);
for (i = 0; i < 48; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x7800);
PIPE_INIT(0x4400);
for (i = 0; i < 32; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x4400);
PIPE_INIT(0x0000);
for (i = 0; i < 16; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x0000);
PIPE_INIT(0x0040);
for (i = 0; i < 4; i++)
NV_WRITE_PIPE_INIT(0x00000000);
PIPE_INIT_END(0x0040);
#undef PIPE_INIT
#undef PIPE_INIT_END
#undef NV_WRITE_PIPE_INIT
}
static int nv10_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
{
int i;
for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) {
if (nv10_graph_ctx_regs[i] == reg)
return i;
}
NV_ERROR(dev, "unknow offset nv10_ctx_regs %d\n", reg);
return -1;
}
static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
{
int i;
for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) {
if (nv17_graph_ctx_regs[i] == reg)
return i;
}
NV_ERROR(dev, "unknow offset nv17_ctx_regs %d\n", reg);
return -1;
}
int nv10_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
uint32_t tmp;
int i;
for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
nv_wr32(dev, nv10_graph_ctx_regs[i], pgraph_ctx->nv10[i]);
if (dev_priv->chipset >= 0x17) {
for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
nv_wr32(dev, nv17_graph_ctx_regs[i],
pgraph_ctx->nv17[i]);
}
nv10_graph_load_pipe(chan);
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
nv_wr32(dev, NV10_PGRAPH_CTX_USER, (tmp & 0xffffff) | chan->id << 24);
tmp = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2);
nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, tmp & 0xcfffffff);
return 0;
}
int
nv10_graph_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan;
struct graph_state *ctx;
uint32_t tmp;
int i;
chan = pgraph->channel(dev);
if (!chan)
return 0;
ctx = chan->pgraph_ctx;
for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]);
if (dev_priv->chipset >= 0x17) {
for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
ctx->nv17[i] = nv_rd32(dev, nv17_graph_ctx_regs[i]);
}
nv10_graph_save_pipe(chan);
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= (pfifo->channels - 1) << 24;
nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
return 0;
}
void
nv10_graph_context_switch(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_channel *chan = NULL;
int chid;
pgraph->fifo_access(dev, false);
nouveau_wait_for_idle(dev);
/* If previous context is valid, we need to save it */
nv10_graph_unload_context(dev);
/* Load context for next channel */
chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
chan = dev_priv->fifos[chid];
if (chan)
nv10_graph_load_context(chan);
pgraph->fifo_access(dev, true);
}
#define NV_WRITE_CTX(reg, val) do { \
int offset = nv10_graph_ctx_regs_find_offset(dev, reg); \
if (offset > 0) \
pgraph_ctx->nv10[offset] = val; \
} while (0)
#define NV17_WRITE_CTX(reg, val) do { \
int offset = nv17_graph_ctx_regs_find_offset(dev, reg); \
if (offset > 0) \
pgraph_ctx->nv17[offset] = val; \
} while (0)
struct nouveau_channel *
nv10_graph_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int chid = dev_priv->engine.fifo.channels;
if (nv_rd32(dev, NV10_PGRAPH_CTX_CONTROL) & 0x00010000)
chid = nv_rd32(dev, NV10_PGRAPH_CTX_USER) >> 24;
if (chid >= dev_priv->engine.fifo.channels)
return NULL;
return dev_priv->fifos[chid];
}
int nv10_graph_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct graph_state *pgraph_ctx;
NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id);
chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
GFP_KERNEL);
if (pgraph_ctx == NULL)
return -ENOMEM;
NV_WRITE_CTX(0x00400e88, 0x08000000);
NV_WRITE_CTX(0x00400e9c, 0x4b7fffff);
NV_WRITE_CTX(NV03_PGRAPH_XY_LOGIC_MISC0, 0x0001ffff);
NV_WRITE_CTX(0x00400e10, 0x00001000);
NV_WRITE_CTX(0x00400e14, 0x00001000);
NV_WRITE_CTX(0x00400e30, 0x00080008);
NV_WRITE_CTX(0x00400e34, 0x00080008);
if (dev_priv->chipset >= 0x17) {
/* is it really needed ??? */
NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4,
nv_rd32(dev, NV10_PGRAPH_DEBUG_4));
NV17_WRITE_CTX(0x004006b0, nv_rd32(dev, 0x004006b0));
NV17_WRITE_CTX(0x00400eac, 0x0fff0000);
NV17_WRITE_CTX(0x00400eb0, 0x0fff0000);
NV17_WRITE_CTX(0x00400ec0, 0x00000080);
NV17_WRITE_CTX(0x00400ed0, 0x00000080);
}
NV_WRITE_CTX(NV10_PGRAPH_CTX_USER, chan->id << 24);
nv10_graph_create_pipe(chan);
return 0;
}
void nv10_graph_destroy_context(struct nouveau_channel *chan)
{
struct graph_state *pgraph_ctx = chan->pgraph_ctx;
kfree(pgraph_ctx);
chan->pgraph_ctx = NULL;
}
int nv10_graph_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t tmp;
int i;
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
/* nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */
nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x25f92ad9);
nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0x55DE0830 |
(1<<29) |
(1<<31));
if (dev_priv->chipset >= 0x17) {
nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x1f000000);
nv_wr32(dev, 0x004006b0, 0x40000020);
} else
nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
/* copy tile info from PFB */
for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
nv_wr32(dev, NV10_PGRAPH_TILE(i),
nv_rd32(dev, NV10_PFB_TILE(i)));
nv_wr32(dev, NV10_PGRAPH_TLIMIT(i),
nv_rd32(dev, NV10_PFB_TLIMIT(i)));
nv_wr32(dev, NV10_PGRAPH_TSIZE(i),
nv_rd32(dev, NV10_PFB_TSIZE(i)));
nv_wr32(dev, NV10_PGRAPH_TSTATUS(i),
nv_rd32(dev, NV10_PFB_TSTATUS(i)));
}
nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000);
nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, 0x08000000);
return 0;
}
void nv10_graph_takedown(struct drm_device *dev)
{
}
struct nouveau_pgraph_object_class nv10_graph_grclass[] = {
{ 0x0030, false, NULL }, /* null */
{ 0x0039, false, NULL }, /* m2mf */
{ 0x004a, false, NULL }, /* gdirect */
{ 0x005f, false, NULL }, /* imageblit */
{ 0x009f, false, NULL }, /* imageblit (nv12) */
{ 0x008a, false, NULL }, /* ifc */
{ 0x0089, false, NULL }, /* sifm */
{ 0x0062, false, NULL }, /* surf2d */
{ 0x0043, false, NULL }, /* rop */
{ 0x0012, false, NULL }, /* beta1 */
{ 0x0072, false, NULL }, /* beta4 */
{ 0x0019, false, NULL }, /* cliprect */
{ 0x0044, false, NULL }, /* pattern */
{ 0x0052, false, NULL }, /* swzsurf */
{ 0x0093, false, NULL }, /* surf3d */
{ 0x0094, false, NULL }, /* tex_tri */
{ 0x0095, false, NULL }, /* multitex_tri */
{ 0x0056, false, NULL }, /* celcius (nv10) */
{ 0x0096, false, NULL }, /* celcius (nv11) */
{ 0x0099, false, NULL }, /* celcius (nv17) */
{}
};

View File

@ -0,0 +1,92 @@
/*
* Copyright (C) 2009 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 "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
static bool
get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift,
uint32_t *mask)
{
if (ent->line < 2) {
*reg = NV_PCRTC_GPIO;
*shift = ent->line * 16;
*mask = 0x11;
} else if (ent->line < 10) {
*reg = NV_PCRTC_GPIO_EXT;
*shift = (ent->line - 2) * 4;
*mask = 0x3;
} else if (ent->line < 14) {
*reg = NV_PCRTC_850;
*shift = (ent->line - 10) * 4;
*mask = 0x3;
} else {
return false;
}
return true;
}
int
nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
{
struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
uint32_t reg, shift, mask, value;
if (!ent)
return -ENODEV;
if (!get_gpio_location(ent, &reg, &shift, &mask))
return -ENODEV;
value = NVReadCRTC(dev, 0, reg) >> shift;
return (ent->invert ? 1 : 0) ^ (value & 1);
}
int
nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
{
struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
uint32_t reg, shift, mask, value;
if (!ent)
return -ENODEV;
if (!get_gpio_location(ent, &reg, &shift, &mask))
return -ENODEV;
value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift;
mask = ~(mask << shift);
NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask));
return 0;
}

View File

@ -0,0 +1,681 @@
/*
* Copyright (C) 2009 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 "drmP.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
#include "nv17_tv.h"
enum drm_connector_status nv17_tv_detect(struct drm_encoder *encoder,
struct drm_connector *connector,
uint32_t pin_mask)
{
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
tv_enc->pin_mask = pin_mask >> 28 & 0xe;
switch (tv_enc->pin_mask) {
case 0x2:
case 0x4:
tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
break;
case 0xc:
tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
break;
case 0xe:
if (nouveau_encoder(encoder)->dcb->tvconf.has_component_output)
tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
else
tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
break;
default:
tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
break;
}
drm_connector_property_set_value(connector,
encoder->dev->mode_config.tv_subconnector_property,
tv_enc->subconnector);
return tv_enc->subconnector ? connector_status_connected :
connector_status_disconnected;
}
static const struct {
int hdisplay;
int vdisplay;
} modes[] = {
{ 640, 400 },
{ 640, 480 },
{ 720, 480 },
{ 720, 576 },
{ 800, 600 },
{ 1024, 768 },
{ 1280, 720 },
{ 1280, 1024 },
{ 1920, 1080 }
};
static int nv17_tv_get_modes(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
struct drm_display_mode *mode;
struct drm_display_mode *output_mode;
int n = 0;
int i;
if (tv_norm->kind != CTV_ENC_MODE) {
struct drm_display_mode *tv_mode;
for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
mode = drm_mode_duplicate(encoder->dev, tv_mode);
mode->clock = tv_norm->tv_enc_mode.vrefresh *
mode->htotal / 1000 *
mode->vtotal / 1000;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
mode->clock *= 2;
if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
mode->type |= DRM_MODE_TYPE_PREFERRED;
drm_mode_probed_add(connector, mode);
n++;
}
return n;
}
/* tv_norm->kind == CTV_ENC_MODE */
output_mode = &tv_norm->ctv_enc_mode.mode;
for (i = 0; i < ARRAY_SIZE(modes); i++) {
if (modes[i].hdisplay > output_mode->hdisplay ||
modes[i].vdisplay > output_mode->vdisplay)
continue;
if (modes[i].hdisplay == output_mode->hdisplay &&
modes[i].vdisplay == output_mode->vdisplay) {
mode = drm_mode_duplicate(encoder->dev, output_mode);
mode->type |= DRM_MODE_TYPE_PREFERRED;
} else {
mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
modes[i].vdisplay, 60, false,
output_mode->flags & DRM_MODE_FLAG_INTERLACE,
false);
}
/* CVT modes are sometimes unsuitable... */
if (output_mode->hdisplay <= 720
|| output_mode->hdisplay >= 1920) {
mode->htotal = output_mode->htotal;
mode->hsync_start = (mode->hdisplay + (mode->htotal
- mode->hdisplay) * 9 / 10) & ~7;
mode->hsync_end = mode->hsync_start + 8;
}
if (output_mode->vdisplay >= 1024) {
mode->vtotal = output_mode->vtotal;
mode->vsync_start = output_mode->vsync_start;
mode->vsync_end = output_mode->vsync_end;
}
mode->type |= DRM_MODE_TYPE_DRIVER;
drm_mode_probed_add(connector, mode);
n++;
}
return n;
}
static int nv17_tv_mode_valid(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
if (tv_norm->kind == CTV_ENC_MODE) {
struct drm_display_mode *output_mode =
&tv_norm->ctv_enc_mode.mode;
if (mode->clock > 400000)
return MODE_CLOCK_HIGH;
if (mode->hdisplay > output_mode->hdisplay ||
mode->vdisplay > output_mode->vdisplay)
return MODE_BAD;
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !=
(output_mode->flags & DRM_MODE_FLAG_INTERLACE))
return MODE_NO_INTERLACE;
if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
return MODE_NO_DBLESCAN;
} else {
const int vsync_tolerance = 600;
if (mode->clock > 70000)
return MODE_CLOCK_HIGH;
if (abs(drm_mode_vrefresh(mode) * 1000 -
tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance)
return MODE_VSYNC;
/* The encoder takes care of the actual interlacing */
if (mode->flags & DRM_MODE_FLAG_INTERLACE)
return MODE_NO_INTERLACE;
}
return MODE_OK;
}
static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
if (tv_norm->kind == CTV_ENC_MODE)
adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
else
adjusted_mode->clock = 90000;
return true;
}
static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
if (nouveau_encoder(encoder)->last_dpms == mode)
return;
nouveau_encoder(encoder)->last_dpms = mode;
NV_TRACE(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
mode, nouveau_encoder(encoder)->dcb->index);
regs->ptv_200 &= ~1;
if (tv_norm->kind == CTV_ENC_MODE) {
nv04_dfp_update_fp_control(encoder, mode);
} else {
nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
if (mode == DRM_MODE_DPMS_ON)
regs->ptv_200 |= 1;
}
nv_load_ptv(dev, regs, 200);
nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
}
static void nv17_tv_prepare(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
int head = nouveau_crtc(encoder->crtc)->index;
uint8_t *cr_lcd = &dev_priv->mode_reg.crtc_reg[head].CRTC[
NV_CIO_CRE_LCD__INDEX];
uint32_t dacclk_off = NV_PRAMDAC_DACCLK +
nv04_dac_output_offset(encoder);
uint32_t dacclk;
helper->dpms(encoder, DRM_MODE_DPMS_OFF);
nv04_dfp_disable(dev, head);
/* Unbind any FP encoders from this head if we need the FP
* stuff enabled. */
if (tv_norm->kind == CTV_ENC_MODE) {
struct drm_encoder *enc;
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
struct dcb_entry *dcb = nouveau_encoder(enc)->dcb;
if ((dcb->type == OUTPUT_TMDS ||
dcb->type == OUTPUT_LVDS) &&
!enc->crtc &&
nv04_dfp_get_bound_head(dev, dcb) == head) {
nv04_dfp_bind_head(dev, dcb, head ^ 1,
dev_priv->VBIOS.fp.dual_link);
}
}
}
/* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
* at LCD__INDEX which we don't alter
*/
if (!(*cr_lcd & 0x44)) {
if (tv_norm->kind == CTV_ENC_MODE)
*cr_lcd = 0x1 | (head ? 0x0 : 0x8);
else
*cr_lcd = 0;
}
/* Set the DACCLK register */
dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
if (dev_priv->card_type == NV_40)
dacclk |= 0x1a << 16;
if (tv_norm->kind == CTV_ENC_MODE) {
dacclk |= 0x20;
if (head)
dacclk |= 0x100;
else
dacclk &= ~0x100;
} else {
dacclk |= 0x10;
}
NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
}
static void nv17_tv_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *drm_mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int head = nouveau_crtc(encoder->crtc)->index;
struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
int i;
regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
regs->tv_setup = 1;
regs->ramdac_8c0 = 0x0;
if (tv_norm->kind == TV_ENC_MODE) {
tv_regs->ptv_200 = 0x13111100;
if (head)
tv_regs->ptv_200 |= 0x10;
tv_regs->ptv_20c = 0x808010;
tv_regs->ptv_304 = 0x2d00000;
tv_regs->ptv_600 = 0x0;
tv_regs->ptv_60c = 0x0;
tv_regs->ptv_610 = 0x1e00000;
if (tv_norm->tv_enc_mode.vdisplay == 576) {
tv_regs->ptv_508 = 0x1200000;
tv_regs->ptv_614 = 0x33;
} else if (tv_norm->tv_enc_mode.vdisplay == 480) {
tv_regs->ptv_508 = 0xf00000;
tv_regs->ptv_614 = 0x13;
}
if (dev_priv->card_type >= NV_30) {
tv_regs->ptv_500 = 0xe8e0;
tv_regs->ptv_504 = 0x1710;
tv_regs->ptv_604 = 0x0;
tv_regs->ptv_608 = 0x0;
} else {
if (tv_norm->tv_enc_mode.vdisplay == 576) {
tv_regs->ptv_604 = 0x20;
tv_regs->ptv_608 = 0x10;
tv_regs->ptv_500 = 0x19710;
tv_regs->ptv_504 = 0x68f0;
} else if (tv_norm->tv_enc_mode.vdisplay == 480) {
tv_regs->ptv_604 = 0x10;
tv_regs->ptv_608 = 0x20;
tv_regs->ptv_500 = 0x4b90;
tv_regs->ptv_504 = 0x1b480;
}
}
for (i = 0; i < 0x40; i++)
tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
} else {
struct drm_display_mode *output_mode =
&tv_norm->ctv_enc_mode.mode;
/* The registers in PRAMDAC+0xc00 control some timings and CSC
* parameters for the CTV encoder (It's only used for "HD" TV
* modes, I don't think I have enough working to guess what
* they exactly mean...), it's probably connected at the
* output of the FP encoder, but it also needs the analog
* encoder in its OR enabled and routed to the head it's
* using. It's enabled with the DACCLK register, bits [5:4].
*/
for (i = 0; i < 38; i++)
regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
regs->fp_horiz_regs[FP_SYNC_START] =
output_mode->hsync_start - 1;
regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
max((output_mode->hdisplay-600)/40 - 1, 1);
regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
regs->fp_vert_regs[FP_SYNC_START] =
output_mode->vsync_start - 1;
regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
regs->fp_debug_2 = 0;
regs->fp_margin_color = 0x801080;
}
}
static void nv17_tv_commit(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_encoder_helper_funcs *helper = encoder->helper_private;
if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
nv17_tv_update_rescaler(encoder);
nv17_tv_update_properties(encoder);
} else {
nv17_ctv_update_rescaler(encoder);
}
nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
/* This could use refinement for flatpanels, but it should work */
if (dev_priv->chipset < 0x44)
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder),
0xf0000000);
else
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
nv04_dac_output_offset(encoder),
0x00100000);
helper->dpms(encoder, DRM_MODE_DPMS_ON);
NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
drm_get_connector_name(
&nouveau_encoder_connector_get(nv_encoder)->base),
nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
}
static void nv17_tv_save(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
nouveau_encoder(encoder)->restore.output =
NVReadRAMDAC(dev, 0,
NV_PRAMDAC_DACCLK +
nv04_dac_output_offset(encoder));
nv17_tv_state_save(dev, &tv_enc->saved_state);
tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
}
static void nv17_tv_restore(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
nv04_dac_output_offset(encoder),
nouveau_encoder(encoder)->restore.output);
nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
}
static int nv17_tv_create_resources(struct drm_encoder *encoder,
struct drm_connector *connector)
{
struct drm_device *dev = encoder->dev;
struct drm_mode_config *conf = &dev->mode_config;
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS :
NUM_LD_TV_NORMS;
int i;
if (nouveau_tv_norm) {
for (i = 0; i < num_tv_norms; i++) {
if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) {
tv_enc->tv_norm = i;
break;
}
}
if (i == num_tv_norms)
NV_WARN(dev, "Invalid TV norm setting \"%s\"\n",
nouveau_tv_norm);
}
drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names);
drm_connector_attach_property(connector,
conf->tv_select_subconnector_property,
tv_enc->select_subconnector);
drm_connector_attach_property(connector,
conf->tv_subconnector_property,
tv_enc->subconnector);
drm_connector_attach_property(connector,
conf->tv_mode_property,
tv_enc->tv_norm);
drm_connector_attach_property(connector,
conf->tv_flicker_reduction_property,
tv_enc->flicker);
drm_connector_attach_property(connector,
conf->tv_saturation_property,
tv_enc->saturation);
drm_connector_attach_property(connector,
conf->tv_hue_property,
tv_enc->hue);
drm_connector_attach_property(connector,
conf->tv_overscan_property,
tv_enc->overscan);
return 0;
}
static int nv17_tv_set_property(struct drm_encoder *encoder,
struct drm_connector *connector,
struct drm_property *property,
uint64_t val)
{
struct drm_mode_config *conf = &encoder->dev->mode_config;
struct drm_crtc *crtc = encoder->crtc;
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
bool modes_changed = false;
if (property == conf->tv_overscan_property) {
tv_enc->overscan = val;
if (encoder->crtc) {
if (tv_norm->kind == CTV_ENC_MODE)
nv17_ctv_update_rescaler(encoder);
else
nv17_tv_update_rescaler(encoder);
}
} else if (property == conf->tv_saturation_property) {
if (tv_norm->kind != TV_ENC_MODE)
return -EINVAL;
tv_enc->saturation = val;
nv17_tv_update_properties(encoder);
} else if (property == conf->tv_hue_property) {
if (tv_norm->kind != TV_ENC_MODE)
return -EINVAL;
tv_enc->hue = val;
nv17_tv_update_properties(encoder);
} else if (property == conf->tv_flicker_reduction_property) {
if (tv_norm->kind != TV_ENC_MODE)
return -EINVAL;
tv_enc->flicker = val;
if (encoder->crtc)
nv17_tv_update_rescaler(encoder);
} else if (property == conf->tv_mode_property) {
if (connector->dpms != DRM_MODE_DPMS_OFF)
return -EINVAL;
tv_enc->tv_norm = val;
modes_changed = true;
} else if (property == conf->tv_select_subconnector_property) {
if (tv_norm->kind != TV_ENC_MODE)
return -EINVAL;
tv_enc->select_subconnector = val;
nv17_tv_update_properties(encoder);
} else {
return -EINVAL;
}
if (modes_changed) {
drm_helper_probe_single_connector_modes(connector, 0, 0);
/* Disable the crtc to ensure a full modeset is
* performed whenever it's turned on again. */
if (crtc) {
struct drm_mode_set modeset = {
.crtc = crtc,
};
crtc->funcs->set_config(&modeset);
}
}
return 0;
}
static void nv17_tv_destroy(struct drm_encoder *encoder)
{
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
NV_DEBUG(encoder->dev, "\n");
drm_encoder_cleanup(encoder);
kfree(tv_enc);
}
static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
.dpms = nv17_tv_dpms,
.save = nv17_tv_save,
.restore = nv17_tv_restore,
.mode_fixup = nv17_tv_mode_fixup,
.prepare = nv17_tv_prepare,
.commit = nv17_tv_commit,
.mode_set = nv17_tv_mode_set,
.detect = nv17_dac_detect,
};
static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
.get_modes = nv17_tv_get_modes,
.mode_valid = nv17_tv_mode_valid,
.create_resources = nv17_tv_create_resources,
.set_property = nv17_tv_set_property,
};
static struct drm_encoder_funcs nv17_tv_funcs = {
.destroy = nv17_tv_destroy,
};
int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
{
struct drm_encoder *encoder;
struct nv17_tv_encoder *tv_enc = NULL;
tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
if (!tv_enc)
return -ENOMEM;
tv_enc->overscan = 50;
tv_enc->flicker = 50;
tv_enc->saturation = 50;
tv_enc->hue = 0;
tv_enc->tv_norm = TV_NORM_PAL;
tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
tv_enc->pin_mask = 0;
encoder = to_drm_encoder(&tv_enc->base);
tv_enc->base.dcb = entry;
tv_enc->base.or = ffs(entry->or) - 1;
drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
encoder->possible_crtcs = entry->heads;
encoder->possible_clones = 0;
return 0;
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2009 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.
*
*/
#ifndef __NV17_TV_H__
#define __NV17_TV_H__
struct nv17_tv_state {
uint8_t tv_enc[0x40];
uint32_t hfilter[4][7];
uint32_t hfilter2[4][7];
uint32_t vfilter[4][7];
uint32_t ptv_200;
uint32_t ptv_204;
uint32_t ptv_208;
uint32_t ptv_20c;
uint32_t ptv_304;
uint32_t ptv_500;
uint32_t ptv_504;
uint32_t ptv_508;
uint32_t ptv_600;
uint32_t ptv_604;
uint32_t ptv_608;
uint32_t ptv_60c;
uint32_t ptv_610;
uint32_t ptv_614;
};
enum nv17_tv_norm{
TV_NORM_PAL,
TV_NORM_PAL_M,
TV_NORM_PAL_N,
TV_NORM_PAL_NC,
TV_NORM_NTSC_M,
TV_NORM_NTSC_J,
NUM_LD_TV_NORMS,
TV_NORM_HD480I = NUM_LD_TV_NORMS,
TV_NORM_HD480P,
TV_NORM_HD576I,
TV_NORM_HD576P,
TV_NORM_HD720P,
TV_NORM_HD1080I,
NUM_TV_NORMS
};
struct nv17_tv_encoder {
struct nouveau_encoder base;
struct nv17_tv_state state;
struct nv17_tv_state saved_state;
int overscan;
int flicker;
int saturation;
int hue;
enum nv17_tv_norm tv_norm;
int subconnector;
int select_subconnector;
uint32_t pin_mask;
};
#define to_tv_enc(x) container_of(nouveau_encoder(x), \
struct nv17_tv_encoder, base)
extern char *nv17_tv_norm_names[NUM_TV_NORMS];
extern struct nv17_tv_norm_params {
enum {
TV_ENC_MODE,
CTV_ENC_MODE,
} kind;
union {
struct {
int hdisplay;
int vdisplay;
int vrefresh; /* mHz */
uint8_t tv_enc[0x40];
} tv_enc_mode;
struct {
struct drm_display_mode mode;
uint32_t ctv_regs[38];
} ctv_enc_mode;
};
} nv17_tv_norms[NUM_TV_NORMS];
#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm])
extern struct drm_display_mode nv17_tv_modes[];
static inline int interpolate(int y0, int y1, int y2, int x)
{
return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
}
void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state);
void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state);
void nv17_tv_update_properties(struct drm_encoder *encoder);
void nv17_tv_update_rescaler(struct drm_encoder *encoder);
void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
/* TV hardware access functions */
static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val)
{
nv_wr32(dev, reg, val);
}
static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
{
return nv_rd32(dev, reg);
}
static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t val)
{
nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
nv_write_ptv(dev, NV_PTV_TV_DATA, val);
}
static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg)
{
nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
return nv_read_ptv(dev, NV_PTV_TV_DATA);
}
#define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg)
#define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg)
#define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg])
#endif

View File

@ -0,0 +1,583 @@
/*
* Copyright (C) 2009 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 "drmP.h"
#include "drm_crtc_helper.h"
#include "nouveau_drv.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
#include "nv17_tv.h"
char *nv17_tv_norm_names[NUM_TV_NORMS] = {
[TV_NORM_PAL] = "PAL",
[TV_NORM_PAL_M] = "PAL-M",
[TV_NORM_PAL_N] = "PAL-N",
[TV_NORM_PAL_NC] = "PAL-Nc",
[TV_NORM_NTSC_M] = "NTSC-M",
[TV_NORM_NTSC_J] = "NTSC-J",
[TV_NORM_HD480I] = "hd480i",
[TV_NORM_HD480P] = "hd480p",
[TV_NORM_HD576I] = "hd576i",
[TV_NORM_HD576P] = "hd576p",
[TV_NORM_HD720P] = "hd720p",
[TV_NORM_HD1080I] = "hd1080i"
};
/* TV standard specific parameters */
struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = {
[TV_NORM_PAL] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 576, 50000, {
0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
} } } },
[TV_NORM_PAL_M] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 480, 59940, {
0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0,
0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c,
0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
} } } },
[TV_NORM_PAL_N] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 576, 50000, {
0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0,
0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
} } } },
[TV_NORM_PAL_NC] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 576, 50000, {
0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
} } } },
[TV_NORM_NTSC_M] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 480, 59940, {
0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0,
0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c,
0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
} } } },
[TV_NORM_NTSC_J] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 480, 59940, {
0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
} } } },
[TV_NORM_HD480I] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 480, 59940, {
0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
} } } },
[TV_NORM_HD576I] = { TV_ENC_MODE, {
.tv_enc_mode = { 720, 576, 50000, {
0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
} } } },
[TV_NORM_HD480P] = { CTV_ENC_MODE, {
.ctv_enc_mode = {
.mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000,
720, 735, 743, 858, 0, 480, 490, 494, 525, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
.ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
0x354003a, 0x40000, 0x6f0344, 0x18100000,
0x10160004, 0x10060005, 0x1006000c, 0x10060020,
0x10060021, 0x140e0022, 0x10060202, 0x1802020a,
0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff,
0x10000fff, 0x10000fff, 0x10000fff, 0x70,
0x3ff0000, 0x57, 0x2e001e, 0x258012c,
0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
} } } },
[TV_NORM_HD576P] = { CTV_ENC_MODE, {
.ctv_enc_mode = {
.mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000,
720, 730, 738, 864, 0, 576, 581, 585, 625, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
.ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
0x354003a, 0x40000, 0x6f0344, 0x18100000,
0x10060001, 0x10060009, 0x10060026, 0x10060027,
0x140e0028, 0x10060268, 0x1810026d, 0x10000fff,
0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff,
0x10000fff, 0x10000fff, 0x10000fff, 0x69,
0x3ff0000, 0x57, 0x2e001e, 0x258012c,
0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
} } } },
[TV_NORM_HD720P] = { CTV_ENC_MODE, {
.ctv_enc_mode = {
.mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250,
1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
.ctv_regs = { 0x1260394, 0x0, 0x0, 0x622,
0x66b0021, 0x6004a, 0x1210626, 0x8170000,
0x70004, 0x70016, 0x70017, 0x40f0018,
0x702e8, 0x81702ed, 0xfff, 0xfff,
0xfff, 0xfff, 0xfff, 0xfff,
0xfff, 0xfff, 0xfff, 0x0,
0x2e40001, 0x58, 0x2e001e, 0x258012c,
0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300,
0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0
} } } },
[TV_NORM_HD1080I] = { CTV_ENC_MODE, {
.ctv_enc_mode = {
.mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250,
1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
| DRM_MODE_FLAG_INTERLACE) },
.ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868,
0x8940028, 0x60054, 0xe80870, 0xbf70000,
0xbc70004, 0x70005, 0x70012, 0x70013,
0x40f0014, 0x70230, 0xbf70232, 0xbf70233,
0x1c70237, 0x70238, 0x70244, 0x70245,
0x40f0246, 0x70462, 0x1f70464, 0x0,
0x2e40001, 0x58, 0x2e001e, 0x258012c,
0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300,
0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0
} } } }
};
/*
* The following is some guesswork on how the TV encoder flicker
* filter/rescaler works:
*
* It seems to use some sort of resampling filter, it is controlled
* through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they
* control the horizontal and vertical stage respectively, there is
* also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER,
* but they seem to do nothing. A rough guess might be that they could
* be used to independently control the filtering of each interlaced
* field, but I don't know how they are enabled. The whole filtering
* process seems to be disabled with bits 26:27 of PTV_200, but we
* aren't doing that.
*
* The layout of both register sets is the same:
*
* A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40]
* B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c]
*
* Each coefficient is stored in bits [31],[15:9] in two's complement
* format. They seem to be some kind of weights used in a low-pass
* filter. Both A and B coefficients are applied to the 14 nearest
* samples on each side (Listed from nearest to furthermost. They
* roughly cover 2 framebuffer pixels on each side). They are
* probably multiplied with some more hardwired weights before being
* used: B-coefficients are applied the same on both sides,
* A-coefficients are inverted before being applied to the opposite
* side.
*
* After all the hassle, I got the following formula by empirical
* means...
*/
#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o)
#define id1 (1LL << 8)
#define id2 (1LL << 16)
#define id3 (1LL << 24)
#define id4 (1LL << 32)
#define id5 (1LL << 48)
static struct filter_params{
int64_t k1;
int64_t ki;
int64_t ki2;
int64_t ki3;
int64_t kr;
int64_t kir;
int64_t ki2r;
int64_t ki3r;
int64_t kf;
int64_t kif;
int64_t ki2f;
int64_t ki3f;
int64_t krf;
int64_t kirf;
int64_t ki2rf;
int64_t ki3rf;
} fparams[2][4] = {
/* Horizontal filter parameters */
{
{64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5,
0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4,
9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3,
-8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1},
{-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5,
29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4,
104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3,
-37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1},
{-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5,
33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4,
87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3,
17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1},
{51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5,
-41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4,
-154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3,
112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1},
},
/* Vertical filter parameters */
{
{67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5,
-3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4,
-9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3,
6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1},
{6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5,
8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4,
37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3,
-2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1},
{-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5,
39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4,
152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3,
-38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1},
{-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5,
20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4,
60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3,
-17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1},
}
};
static void tv_setup_filter(struct drm_encoder *encoder)
{
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
struct drm_display_mode *mode = &encoder->crtc->mode;
uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter,
&tv_enc->state.vfilter};
int i, j, k;
int32_t overscan = calc_overscan(tv_enc->overscan);
int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100);
uint64_t rs[] = {mode->hdisplay * id3,
mode->vdisplay * id3};
do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay);
do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay);
for (k = 0; k < 2; k++) {
rs[k] = max((int64_t)rs[k], id2);
for (j = 0; j < 4; j++) {
struct filter_params *p = &fparams[k][j];
for (i = 0; i < 7; i++) {
int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i)
+ (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
+ (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
+ (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k];
(*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9);
}
}
}
}
/* Hardware state saving/restoring */
static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
{
int i, j;
uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
for (i = 0; i < 4; i++) {
for (j = 0; j < 7; j++)
regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
}
}
static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
{
int i, j;
uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
for (i = 0; i < 4; i++) {
for (j = 0; j < 7; j++)
nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
}
}
void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
{
int i;
for (i = 0; i < 0x40; i++)
state->tv_enc[i] = nv_read_tv_enc(dev, i);
tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
nv_save_ptv(dev, state, 200);
nv_save_ptv(dev, state, 204);
nv_save_ptv(dev, state, 208);
nv_save_ptv(dev, state, 20c);
nv_save_ptv(dev, state, 304);
nv_save_ptv(dev, state, 500);
nv_save_ptv(dev, state, 504);
nv_save_ptv(dev, state, 508);
nv_save_ptv(dev, state, 600);
nv_save_ptv(dev, state, 604);
nv_save_ptv(dev, state, 608);
nv_save_ptv(dev, state, 60c);
nv_save_ptv(dev, state, 610);
nv_save_ptv(dev, state, 614);
}
void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
{
int i;
for (i = 0; i < 0x40; i++)
nv_write_tv_enc(dev, i, state->tv_enc[i]);
tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
nv_load_ptv(dev, state, 200);
nv_load_ptv(dev, state, 204);
nv_load_ptv(dev, state, 208);
nv_load_ptv(dev, state, 20c);
nv_load_ptv(dev, state, 304);
nv_load_ptv(dev, state, 500);
nv_load_ptv(dev, state, 504);
nv_load_ptv(dev, state, 508);
nv_load_ptv(dev, state, 600);
nv_load_ptv(dev, state, 604);
nv_load_ptv(dev, state, 608);
nv_load_ptv(dev, state, 60c);
nv_load_ptv(dev, state, 610);
nv_load_ptv(dev, state, 614);
/* This is required for some settings to kick in. */
nv_write_tv_enc(dev, 0x3e, 1);
nv_write_tv_enc(dev, 0x3e, 0);
}
/* Timings similar to the ones the blob sets */
struct drm_display_mode nv17_tv_modes[] = {
{ DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
| DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
{ DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
| DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
{ DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
| DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
{ DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
{ DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
{ DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
{ DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
{ DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
{}
};
void nv17_tv_update_properties(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
struct nv17_tv_state *regs = &tv_enc->state;
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
int subconnector = tv_enc->select_subconnector ?
tv_enc->select_subconnector :
tv_enc->subconnector;
switch (subconnector) {
case DRM_MODE_SUBCONNECTOR_Composite:
{
regs->ptv_204 = 0x2;
/* The composite connector may be found on either pin. */
if (tv_enc->pin_mask & 0x4)
regs->ptv_204 |= 0x010000;
else if (tv_enc->pin_mask & 0x2)
regs->ptv_204 |= 0x100000;
else
regs->ptv_204 |= 0x110000;
regs->tv_enc[0x7] = 0x10;
break;
}
case DRM_MODE_SUBCONNECTOR_SVIDEO:
regs->ptv_204 = 0x11012;
regs->tv_enc[0x7] = 0x18;
break;
case DRM_MODE_SUBCONNECTOR_Component:
regs->ptv_204 = 0x111333;
regs->tv_enc[0x7] = 0x14;
break;
case DRM_MODE_SUBCONNECTOR_SCART:
regs->ptv_204 = 0x111012;
regs->tv_enc[0x7] = 0x18;
break;
}
regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255,
tv_enc->saturation);
regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255,
tv_enc->saturation);
regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
nv_load_ptv(dev, regs, 204);
nv_load_tv_enc(dev, regs, 7);
nv_load_tv_enc(dev, regs, 20);
nv_load_tv_enc(dev, regs, 22);
nv_load_tv_enc(dev, regs, 25);
}
void nv17_tv_update_rescaler(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
struct nv17_tv_state *regs = &tv_enc->state;
regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
tv_setup_filter(encoder);
nv_load_ptv(dev, regs, 208);
tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
}
void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
int head = nouveau_crtc(encoder->crtc)->index;
struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode;
int overscan, hmargin, vmargin, hratio, vratio;
/* The rescaler doesn't do the right thing for interlaced modes. */
if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
overscan = 100;
else
overscan = tv_enc->overscan;
hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
overscan);
vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
overscan);
hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin);
vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3;
regs->fp_horiz_regs[FP_VALID_START] = hmargin;
regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
regs->fp_vert_regs[FP_VALID_START] = vmargin;
regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
regs->fp_horiz_regs[FP_VALID_START]);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
regs->fp_horiz_regs[FP_VALID_END]);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
regs->fp_vert_regs[FP_VALID_START]);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
regs->fp_vert_regs[FP_VALID_END]);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
}

View File

@ -0,0 +1,780 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
/*
* NV20
* -----
* There are 3 families :
* NV20 is 0x10de:0x020*
* NV25/28 is 0x10de:0x025* / 0x10de:0x028*
* NV2A is 0x10de:0x02A0
*
* NV30
* -----
* There are 3 families :
* NV30/31 is 0x10de:0x030* / 0x10de:0x031*
* NV34 is 0x10de:0x032*
* NV35/36 is 0x10de:0x033* / 0x10de:0x034*
*
* Not seen in the wild, no dumps (probably NV35) :
* NV37 is 0x10de:0x00fc, 0x10de:0x00fd
* NV38 is 0x10de:0x0333, 0x10de:0x00fe
*
*/
#define NV20_GRCTX_SIZE (3580*4)
#define NV25_GRCTX_SIZE (3529*4)
#define NV2A_GRCTX_SIZE (3500*4)
#define NV30_31_GRCTX_SIZE (24392)
#define NV34_GRCTX_SIZE (18140)
#define NV35_36_GRCTX_SIZE (22396)
static void
nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
for (i = 0x04d4; i <= 0x04e0; i += 4)
nv_wo32(dev, ctx, i/4, 0x00030303);
for (i = 0x04f4; i <= 0x0500; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080000);
for (i = 0x050c; i <= 0x0518; i += 4)
nv_wo32(dev, ctx, i/4, 0x01012000);
for (i = 0x051c; i <= 0x0528; i += 4)
nv_wo32(dev, ctx, i/4, 0x000105b8);
for (i = 0x052c; i <= 0x0538; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080008);
for (i = 0x055c; i <= 0x0598; i += 4)
nv_wo32(dev, ctx, i/4, 0x07ff0000);
nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
for (i = 0x1c1c; i <= 0x248c; i += 16) {
nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
}
nv_wo32(dev, ctx, 0x281c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2830/4, 0x3f800000);
nv_wo32(dev, ctx, 0x285c/4, 0x40000000);
nv_wo32(dev, ctx, 0x2860/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2864/4, 0x3f000000);
nv_wo32(dev, ctx, 0x286c/4, 0x40000000);
nv_wo32(dev, ctx, 0x2870/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2878/4, 0xbf800000);
nv_wo32(dev, ctx, 0x2880/4, 0xbf800000);
nv_wo32(dev, ctx, 0x34a4/4, 0x000fe000);
nv_wo32(dev, ctx, 0x3530/4, 0x000003f8);
nv_wo32(dev, ctx, 0x3540/4, 0x002fe000);
for (i = 0x355c; i <= 0x3578; i += 4)
nv_wo32(dev, ctx, i/4, 0x001c527c);
}
static void
nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
nv_wo32(dev, ctx, 0x035c/4, 0xffff0000);
nv_wo32(dev, ctx, 0x03c0/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x03c4/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x049c/4, 0x00000101);
nv_wo32(dev, ctx, 0x04b0/4, 0x00000111);
nv_wo32(dev, ctx, 0x04c8/4, 0x00000080);
nv_wo32(dev, ctx, 0x04cc/4, 0xffff0000);
nv_wo32(dev, ctx, 0x04d0/4, 0x00000001);
nv_wo32(dev, ctx, 0x04e4/4, 0x44400000);
nv_wo32(dev, ctx, 0x04fc/4, 0x4b800000);
for (i = 0x0510; i <= 0x051c; i += 4)
nv_wo32(dev, ctx, i/4, 0x00030303);
for (i = 0x0530; i <= 0x053c; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080000);
for (i = 0x0548; i <= 0x0554; i += 4)
nv_wo32(dev, ctx, i/4, 0x01012000);
for (i = 0x0558; i <= 0x0564; i += 4)
nv_wo32(dev, ctx, i/4, 0x000105b8);
for (i = 0x0568; i <= 0x0574; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080008);
for (i = 0x0598; i <= 0x05d4; i += 4)
nv_wo32(dev, ctx, i/4, 0x07ff0000);
nv_wo32(dev, ctx, 0x05e0/4, 0x4b7fffff);
nv_wo32(dev, ctx, 0x0620/4, 0x00000080);
nv_wo32(dev, ctx, 0x0624/4, 0x30201000);
nv_wo32(dev, ctx, 0x0628/4, 0x70605040);
nv_wo32(dev, ctx, 0x062c/4, 0xb0a09080);
nv_wo32(dev, ctx, 0x0630/4, 0xf0e0d0c0);
nv_wo32(dev, ctx, 0x0664/4, 0x00000001);
nv_wo32(dev, ctx, 0x066c/4, 0x00004000);
nv_wo32(dev, ctx, 0x0678/4, 0x00000001);
nv_wo32(dev, ctx, 0x0680/4, 0x00040000);
nv_wo32(dev, ctx, 0x0684/4, 0x00010000);
for (i = 0x1b04; i <= 0x2374; i += 16) {
nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
}
nv_wo32(dev, ctx, 0x2704/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2718/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2744/4, 0x40000000);
nv_wo32(dev, ctx, 0x2748/4, 0x3f800000);
nv_wo32(dev, ctx, 0x274c/4, 0x3f000000);
nv_wo32(dev, ctx, 0x2754/4, 0x40000000);
nv_wo32(dev, ctx, 0x2758/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2760/4, 0xbf800000);
nv_wo32(dev, ctx, 0x2768/4, 0xbf800000);
nv_wo32(dev, ctx, 0x308c/4, 0x000fe000);
nv_wo32(dev, ctx, 0x3108/4, 0x000003f8);
nv_wo32(dev, ctx, 0x3468/4, 0x002fe000);
for (i = 0x3484; i <= 0x34a0; i += 4)
nv_wo32(dev, ctx, i/4, 0x001c527c);
}
static void
nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
for (i = 0x04d4; i <= 0x04e0; i += 4)
nv_wo32(dev, ctx, i/4, 0x00030303);
for (i = 0x04f4; i <= 0x0500; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080000);
for (i = 0x050c; i <= 0x0518; i += 4)
nv_wo32(dev, ctx, i/4, 0x01012000);
for (i = 0x051c; i <= 0x0528; i += 4)
nv_wo32(dev, ctx, i/4, 0x000105b8);
for (i = 0x052c; i <= 0x0538; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080008);
for (i = 0x055c; i <= 0x0598; i += 4)
nv_wo32(dev, ctx, i/4, 0x07ff0000);
nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */
nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
}
nv_wo32(dev, ctx, 0x269c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x26b0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x26dc/4, 0x40000000);
nv_wo32(dev, ctx, 0x26e0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x26e4/4, 0x3f000000);
nv_wo32(dev, ctx, 0x26ec/4, 0x40000000);
nv_wo32(dev, ctx, 0x26f0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x26f8/4, 0xbf800000);
nv_wo32(dev, ctx, 0x2700/4, 0xbf800000);
nv_wo32(dev, ctx, 0x3024/4, 0x000fe000);
nv_wo32(dev, ctx, 0x30a0/4, 0x000003f8);
nv_wo32(dev, ctx, 0x33fc/4, 0x002fe000);
for (i = 0x341c; i <= 0x3438; i += 4)
nv_wo32(dev, ctx, i/4, 0x001c527c);
}
static void
nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
nv_wo32(dev, ctx, 0x0410/4, 0x00000101);
nv_wo32(dev, ctx, 0x0424/4, 0x00000111);
nv_wo32(dev, ctx, 0x0428/4, 0x00000060);
nv_wo32(dev, ctx, 0x0444/4, 0x00000080);
nv_wo32(dev, ctx, 0x0448/4, 0xffff0000);
nv_wo32(dev, ctx, 0x044c/4, 0x00000001);
nv_wo32(dev, ctx, 0x0460/4, 0x44400000);
nv_wo32(dev, ctx, 0x048c/4, 0xffff0000);
for (i = 0x04e0; i < 0x04e8; i += 4)
nv_wo32(dev, ctx, i/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x04ec/4, 0x00011100);
for (i = 0x0508; i < 0x0548; i += 4)
nv_wo32(dev, ctx, i/4, 0x07ff0000);
nv_wo32(dev, ctx, 0x0550/4, 0x4b7fffff);
nv_wo32(dev, ctx, 0x058c/4, 0x00000080);
nv_wo32(dev, ctx, 0x0590/4, 0x30201000);
nv_wo32(dev, ctx, 0x0594/4, 0x70605040);
nv_wo32(dev, ctx, 0x0598/4, 0xb8a89888);
nv_wo32(dev, ctx, 0x059c/4, 0xf8e8d8c8);
nv_wo32(dev, ctx, 0x05b0/4, 0xb0000000);
for (i = 0x0600; i < 0x0640; i += 4)
nv_wo32(dev, ctx, i/4, 0x00010588);
for (i = 0x0640; i < 0x0680; i += 4)
nv_wo32(dev, ctx, i/4, 0x00030303);
for (i = 0x06c0; i < 0x0700; i += 4)
nv_wo32(dev, ctx, i/4, 0x0008aae4);
for (i = 0x0700; i < 0x0740; i += 4)
nv_wo32(dev, ctx, i/4, 0x01012000);
for (i = 0x0740; i < 0x0780; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080008);
nv_wo32(dev, ctx, 0x085c/4, 0x00040000);
nv_wo32(dev, ctx, 0x0860/4, 0x00010000);
for (i = 0x0864; i < 0x0874; i += 4)
nv_wo32(dev, ctx, i/4, 0x00040004);
for (i = 0x1f18; i <= 0x3088 ; i += 16) {
nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
}
for (i = 0x30b8; i < 0x30c8; i += 4)
nv_wo32(dev, ctx, i/4, 0x0000ffff);
nv_wo32(dev, ctx, 0x344c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3808/4, 0x3f800000);
nv_wo32(dev, ctx, 0x381c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3848/4, 0x40000000);
nv_wo32(dev, ctx, 0x384c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3850/4, 0x3f000000);
nv_wo32(dev, ctx, 0x3858/4, 0x40000000);
nv_wo32(dev, ctx, 0x385c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3864/4, 0xbf800000);
nv_wo32(dev, ctx, 0x386c/4, 0xbf800000);
}
static void
nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
nv_wo32(dev, ctx, 0x040c/4, 0x01000101);
nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
nv_wo32(dev, ctx, 0x0480/4, 0xffff0000);
for (i = 0x04d4; i < 0x04dc; i += 4)
nv_wo32(dev, ctx, i/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x04e0/4, 0x00011100);
for (i = 0x04fc; i < 0x053c; i += 4)
nv_wo32(dev, ctx, i/4, 0x07ff0000);
nv_wo32(dev, ctx, 0x0544/4, 0x4b7fffff);
nv_wo32(dev, ctx, 0x057c/4, 0x00000080);
nv_wo32(dev, ctx, 0x0580/4, 0x30201000);
nv_wo32(dev, ctx, 0x0584/4, 0x70605040);
nv_wo32(dev, ctx, 0x0588/4, 0xb8a89888);
nv_wo32(dev, ctx, 0x058c/4, 0xf8e8d8c8);
nv_wo32(dev, ctx, 0x05a0/4, 0xb0000000);
for (i = 0x05f0; i < 0x0630; i += 4)
nv_wo32(dev, ctx, i/4, 0x00010588);
for (i = 0x0630; i < 0x0670; i += 4)
nv_wo32(dev, ctx, i/4, 0x00030303);
for (i = 0x06b0; i < 0x06f0; i += 4)
nv_wo32(dev, ctx, i/4, 0x0008aae4);
for (i = 0x06f0; i < 0x0730; i += 4)
nv_wo32(dev, ctx, i/4, 0x01012000);
for (i = 0x0730; i < 0x0770; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080008);
nv_wo32(dev, ctx, 0x0850/4, 0x00040000);
nv_wo32(dev, ctx, 0x0854/4, 0x00010000);
for (i = 0x0858; i < 0x0868; i += 4)
nv_wo32(dev, ctx, i/4, 0x00040004);
for (i = 0x15ac; i <= 0x271c ; i += 16) {
nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
}
for (i = 0x274c; i < 0x275c; i += 4)
nv_wo32(dev, ctx, i/4, 0x0000ffff);
nv_wo32(dev, ctx, 0x2ae0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2e9c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2eb0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2edc/4, 0x40000000);
nv_wo32(dev, ctx, 0x2ee0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2ee4/4, 0x3f000000);
nv_wo32(dev, ctx, 0x2eec/4, 0x40000000);
nv_wo32(dev, ctx, 0x2ef0/4, 0x3f800000);
nv_wo32(dev, ctx, 0x2ef8/4, 0xbf800000);
nv_wo32(dev, ctx, 0x2f00/4, 0xbf800000);
}
static void
nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
int i;
nv_wo32(dev, ctx, 0x040c/4, 0x00000101);
nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
nv_wo32(dev, ctx, 0x0488/4, 0xffff0000);
for (i = 0x04dc; i < 0x04e4; i += 4)
nv_wo32(dev, ctx, i/4, 0x0fff0000);
nv_wo32(dev, ctx, 0x04e8/4, 0x00011100);
for (i = 0x0504; i < 0x0544; i += 4)
nv_wo32(dev, ctx, i/4, 0x07ff0000);
nv_wo32(dev, ctx, 0x054c/4, 0x4b7fffff);
nv_wo32(dev, ctx, 0x0588/4, 0x00000080);
nv_wo32(dev, ctx, 0x058c/4, 0x30201000);
nv_wo32(dev, ctx, 0x0590/4, 0x70605040);
nv_wo32(dev, ctx, 0x0594/4, 0xb8a89888);
nv_wo32(dev, ctx, 0x0598/4, 0xf8e8d8c8);
nv_wo32(dev, ctx, 0x05ac/4, 0xb0000000);
for (i = 0x0604; i < 0x0644; i += 4)
nv_wo32(dev, ctx, i/4, 0x00010588);
for (i = 0x0644; i < 0x0684; i += 4)
nv_wo32(dev, ctx, i/4, 0x00030303);
for (i = 0x06c4; i < 0x0704; i += 4)
nv_wo32(dev, ctx, i/4, 0x0008aae4);
for (i = 0x0704; i < 0x0744; i += 4)
nv_wo32(dev, ctx, i/4, 0x01012000);
for (i = 0x0744; i < 0x0784; i += 4)
nv_wo32(dev, ctx, i/4, 0x00080008);
nv_wo32(dev, ctx, 0x0860/4, 0x00040000);
nv_wo32(dev, ctx, 0x0864/4, 0x00010000);
for (i = 0x0868; i < 0x0878; i += 4)
nv_wo32(dev, ctx, i/4, 0x00040004);
for (i = 0x1f1c; i <= 0x308c ; i += 16) {
nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
}
for (i = 0x30bc; i < 0x30cc; i += 4)
nv_wo32(dev, ctx, i/4, 0x0000ffff);
nv_wo32(dev, ctx, 0x3450/4, 0x3f800000);
nv_wo32(dev, ctx, 0x380c/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3820/4, 0x3f800000);
nv_wo32(dev, ctx, 0x384c/4, 0x40000000);
nv_wo32(dev, ctx, 0x3850/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3854/4, 0x3f000000);
nv_wo32(dev, ctx, 0x385c/4, 0x40000000);
nv_wo32(dev, ctx, 0x3860/4, 0x3f800000);
nv_wo32(dev, ctx, 0x3868/4, 0xbf800000);
nv_wo32(dev, ctx, 0x3870/4, 0xbf800000);
}
int
nv20_graph_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
unsigned int ctx_size;
unsigned int idoffs = 0x28/4;
int ret;
switch (dev_priv->chipset) {
case 0x20:
ctx_size = NV20_GRCTX_SIZE;
ctx_init = nv20_graph_context_init;
idoffs = 0;
break;
case 0x25:
case 0x28:
ctx_size = NV25_GRCTX_SIZE;
ctx_init = nv25_graph_context_init;
break;
case 0x2a:
ctx_size = NV2A_GRCTX_SIZE;
ctx_init = nv2a_graph_context_init;
idoffs = 0;
break;
case 0x30:
case 0x31:
ctx_size = NV30_31_GRCTX_SIZE;
ctx_init = nv30_31_graph_context_init;
break;
case 0x34:
ctx_size = NV34_GRCTX_SIZE;
ctx_init = nv34_graph_context_init;
break;
case 0x35:
case 0x36:
ctx_size = NV35_36_GRCTX_SIZE;
ctx_init = nv35_36_graph_context_init;
break;
default:
ctx_size = 0;
ctx_init = nv35_36_graph_context_init;
NV_ERROR(dev, "Please contact the devs if you want your NV%x"
" card to work\n", dev_priv->chipset);
return -ENOSYS;
break;
}
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16,
NVOBJ_FLAG_ZERO_ALLOC,
&chan->ramin_grctx);
if (ret)
return ret;
/* Initialise default context values */
dev_priv->engine.instmem.prepare_access(dev, true);
ctx_init(dev, chan->ramin_grctx->gpuobj);
/* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs,
(chan->id << 24) | 0x1); /* CTX_USER */
nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id,
chan->ramin_grctx->instance >> 4);
dev_priv->engine.instmem.finish_access(dev);
return 0;
}
void
nv20_graph_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (chan->ramin_grctx)
nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, 0);
dev_priv->engine.instmem.finish_access(dev);
}
int
nv20_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
uint32_t inst;
if (!chan->ramin_grctx)
return -EINVAL;
inst = chan->ramin_grctx->instance >> 4;
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD);
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
nouveau_wait_for_idle(dev);
return 0;
}
int
nv20_graph_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_channel *chan;
uint32_t inst, tmp;
chan = pgraph->channel(dev);
if (!chan)
return 0;
inst = chan->ramin_grctx->instance >> 4;
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
nouveau_wait_for_idle(dev);
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
tmp |= (pfifo->channels - 1) << 24;
nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
return 0;
}
static void
nv20_graph_rdi(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int i, writecount = 32;
uint32_t rdi_index = 0x2c80000;
if (dev_priv->chipset == 0x20) {
rdi_index = 0x3d0000;
writecount = 15;
}
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
for (i = 0; i < writecount; i++)
nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
nouveau_wait_for_idle(dev);
}
int
nv20_graph_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv =
(struct drm_nouveau_private *)dev->dev_private;
uint32_t tmp, vramsz;
int ret, i;
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
if (!dev_priv->ctx_table) {
/* Create Context Pointer Table */
dev_priv->ctx_table_size = 32 * 4;
ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
dev_priv->ctx_table_size, 16,
NVOBJ_FLAG_ZERO_ALLOC,
&dev_priv->ctx_table);
if (ret)
return ret;
}
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
dev_priv->ctx_table->instance >> 4);
nv20_graph_rdi(dev);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */
nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
nv_wr32(dev, 0x40009C , 0x00000040);
if (dev_priv->chipset >= 0x25) {
nv_wr32(dev, 0x400890, 0x00080000);
nv_wr32(dev, 0x400610, 0x304B1FB6);
nv_wr32(dev, 0x400B80, 0x18B82880);
nv_wr32(dev, 0x400B84, 0x44000000);
nv_wr32(dev, 0x400098, 0x40000080);
nv_wr32(dev, 0x400B88, 0x000000ff);
} else {
nv_wr32(dev, 0x400880, 0x00080000); /* 0x0008c7df */
nv_wr32(dev, 0x400094, 0x00000005);
nv_wr32(dev, 0x400B80, 0x45CAA208); /* 0x45eae20e */
nv_wr32(dev, 0x400B84, 0x24000000);
nv_wr32(dev, 0x400098, 0x00000040);
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00038);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E10038);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
}
/* copy tile info from PFB */
for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
nv_wr32(dev, 0x00400904 + i * 0x10,
nv_rd32(dev, NV10_PFB_TLIMIT(i)));
/* which is NV40_PGRAPH_TLIMIT0(i) ?? */
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + i * 4);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
nv_rd32(dev, NV10_PFB_TLIMIT(i)));
nv_wr32(dev, 0x00400908 + i * 0x10,
nv_rd32(dev, NV10_PFB_TSIZE(i)));
/* which is NV40_PGRAPH_TSIZE0(i) ?? */
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + i * 4);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
nv_rd32(dev, NV10_PFB_TSIZE(i)));
nv_wr32(dev, 0x00400900 + i * 0x10,
nv_rd32(dev, NV10_PFB_TILE(i)));
/* which is NV40_PGRAPH_TILE0(i) ?? */
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + i * 4);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
nv_rd32(dev, NV10_PFB_TILE(i)));
}
for (i = 0; i < 8; i++) {
nv_wr32(dev, 0x400980 + i * 4, nv_rd32(dev, 0x100300 + i * 4));
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0090 + i * 4);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
nv_rd32(dev, 0x100300 + i * 4));
}
nv_wr32(dev, 0x4009a0, nv_rd32(dev, 0x100324));
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA000C);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA, nv_rd32(dev, 0x100324));
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) & 0x0007ff00;
nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) | 0x00020100;
nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
/* begin RAM config */
vramsz = drm_get_resource_len(dev, 0) - 1;
nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG1));
nv_wr32(dev, 0x400820, 0);
nv_wr32(dev, 0x400824, 0);
nv_wr32(dev, 0x400864, vramsz - 1);
nv_wr32(dev, 0x400868, vramsz - 1);
/* interesting.. the below overwrites some of the tile setup above.. */
nv_wr32(dev, 0x400B20, 0x00000000);
nv_wr32(dev, 0x400B04, 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMIN, 0);
nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMIN, 0);
nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff);
nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff);
return 0;
}
void
nv20_graph_takedown(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table);
}
int
nv30_graph_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int ret, i;
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
if (!dev_priv->ctx_table) {
/* Create Context Pointer Table */
dev_priv->ctx_table_size = 32 * 4;
ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
dev_priv->ctx_table_size, 16,
NVOBJ_FLAG_ZERO_ALLOC,
&dev_priv->ctx_table);
if (ret)
return ret;
}
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
dev_priv->ctx_table->instance >> 4);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
nv_wr32(dev, 0x400890, 0x01b463ff);
nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf2de0475);
nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6);
nv_wr32(dev, 0x400B80, 0x1003d888);
nv_wr32(dev, 0x400B84, 0x0c000000);
nv_wr32(dev, 0x400098, 0x00000000);
nv_wr32(dev, 0x40009C, 0x0005ad00);
nv_wr32(dev, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */
nv_wr32(dev, 0x4000a0, 0x00000000);
nv_wr32(dev, 0x4000a4, 0x00000008);
nv_wr32(dev, 0x4008a8, 0xb784a400);
nv_wr32(dev, 0x400ba0, 0x002f8685);
nv_wr32(dev, 0x400ba4, 0x00231f3f);
nv_wr32(dev, 0x4008a4, 0x40000020);
if (dev_priv->chipset == 0x34) {
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00200201);
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0008);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000008);
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000032);
nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00004);
nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000002);
}
nv_wr32(dev, 0x4000c0, 0x00000016);
/* copy tile info from PFB */
for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
nv_wr32(dev, 0x00400904 + i * 0x10,
nv_rd32(dev, NV10_PFB_TLIMIT(i)));
/* which is NV40_PGRAPH_TLIMIT0(i) ?? */
nv_wr32(dev, 0x00400908 + i * 0x10,
nv_rd32(dev, NV10_PFB_TSIZE(i)));
/* which is NV40_PGRAPH_TSIZE0(i) ?? */
nv_wr32(dev, 0x00400900 + i * 0x10,
nv_rd32(dev, NV10_PFB_TILE(i)));
/* which is NV40_PGRAPH_TILE0(i) ?? */
}
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
nv_wr32(dev, 0x0040075c , 0x00000001);
/* begin RAM config */
/* vramsz = drm_get_resource_len(dev, 0) - 1; */
nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
if (dev_priv->chipset != 0x34) {
nv_wr32(dev, 0x400750, 0x00EA0000);
nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x400750, 0x00EA0004);
nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG1));
}
return 0;
}
struct nouveau_pgraph_object_class nv20_graph_grclass[] = {
{ 0x0030, false, NULL }, /* null */
{ 0x0039, false, NULL }, /* m2mf */
{ 0x004a, false, NULL }, /* gdirect */
{ 0x009f, false, NULL }, /* imageblit (nv12) */
{ 0x008a, false, NULL }, /* ifc */
{ 0x0089, false, NULL }, /* sifm */
{ 0x0062, false, NULL }, /* surf2d */
{ 0x0043, false, NULL }, /* rop */
{ 0x0012, false, NULL }, /* beta1 */
{ 0x0072, false, NULL }, /* beta4 */
{ 0x0019, false, NULL }, /* cliprect */
{ 0x0044, false, NULL }, /* pattern */
{ 0x009e, false, NULL }, /* swzsurf */
{ 0x0096, false, NULL }, /* celcius */
{ 0x0097, false, NULL }, /* kelvin (nv20) */
{ 0x0597, false, NULL }, /* kelvin (nv25) */
{}
};
struct nouveau_pgraph_object_class nv30_graph_grclass[] = {
{ 0x0030, false, NULL }, /* null */
{ 0x0039, false, NULL }, /* m2mf */
{ 0x004a, false, NULL }, /* gdirect */
{ 0x009f, false, NULL }, /* imageblit (nv12) */
{ 0x008a, false, NULL }, /* ifc */
{ 0x038a, false, NULL }, /* ifc (nv30) */
{ 0x0089, false, NULL }, /* sifm */
{ 0x0389, false, NULL }, /* sifm (nv30) */
{ 0x0062, false, NULL }, /* surf2d */
{ 0x0362, false, NULL }, /* surf2d (nv30) */
{ 0x0043, false, NULL }, /* rop */
{ 0x0012, false, NULL }, /* beta1 */
{ 0x0072, false, NULL }, /* beta4 */
{ 0x0019, false, NULL }, /* cliprect */
{ 0x0044, false, NULL }, /* pattern */
{ 0x039e, false, NULL }, /* swzsurf */
{ 0x0397, false, NULL }, /* rankine (nv30) */
{ 0x0497, false, NULL }, /* rankine (nv35) */
{ 0x0697, false, NULL }, /* rankine (nv34) */
{}
};

View File

@ -0,0 +1,62 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
int
nv40_fb_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t fb_bar_size, tmp;
int num_tiles;
int i;
/* This is strictly a NV4x register (don't know about NV5x). */
/* The blob sets these to all kinds of values, and they mess up our setup. */
/* I got value 0x52802 instead. For some cards the blob even sets it back to 0x1. */
/* Note: the blob doesn't read this value, so i'm pretty sure this is safe for all cards. */
/* Any idea what this is? */
nv_wr32(dev, NV40_PFB_UNK_800, 0x1);
switch (dev_priv->chipset) {
case 0x40:
case 0x45:
tmp = nv_rd32(dev, NV10_PFB_CLOSE_PAGE2);
nv_wr32(dev, NV10_PFB_CLOSE_PAGE2, tmp & ~(1 << 15));
num_tiles = NV10_PFB_TILE__SIZE;
break;
case 0x46: /* G72 */
case 0x47: /* G70 */
case 0x49: /* G71 */
case 0x4b: /* G73 */
case 0x4c: /* C51 (G7X version) */
num_tiles = NV40_PFB_TILE__SIZE_1;
break;
default:
num_tiles = NV40_PFB_TILE__SIZE_0;
break;
}
fb_bar_size = drm_get_resource_len(dev, 0) - 1;
switch (dev_priv->chipset) {
case 0x40:
for (i = 0; i < num_tiles; i++) {
nv_wr32(dev, NV10_PFB_TILE(i), 0);
nv_wr32(dev, NV10_PFB_TLIMIT(i), fb_bar_size);
}
break;
default:
for (i = 0; i < num_tiles; i++) {
nv_wr32(dev, NV40_PFB_TILE(i), 0);
nv_wr32(dev, NV40_PFB_TLIMIT(i), fb_bar_size);
}
break;
}
return 0;
}
void
nv40_fb_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,314 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE))
#define NV40_RAMFC__SIZE 128
int
nv40_fifo_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t fc = NV40_RAMFC(chan->id);
int ret;
ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
if (ret)
return ret;
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wi32(dev, fc + 0, chan->pushbuf_base);
nv_wi32(dev, fc + 4, chan->pushbuf_base);
nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
nv_wi32(dev, fc + 24, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
#ifdef __BIG_ENDIAN
NV_PFIFO_CACHE1_BIG_ENDIAN |
#endif
0x30000000 /* no idea.. */);
nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4);
nv_wi32(dev, fc + 60, 0x0001FFFF);
dev_priv->engine.instmem.finish_access(dev);
/* enable the fifo dma operation */
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
return 0;
}
void
nv40_fifo_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
nv_wr32(dev, NV04_PFIFO_MODE,
nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
if (chan->ramfc)
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
}
static void
nv40_fifo_do_load_context(struct drm_device *dev, int chid)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t fc = NV40_RAMFC(chid), tmp, tmp2;
dev_priv->engine.instmem.prepare_access(dev, false);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, nv_ri32(dev, fc + 12));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, nv_ri32(dev, fc + 16));
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 20));
/* No idea what 0x2058 is.. */
tmp = nv_ri32(dev, fc + 24);
tmp2 = nv_rd32(dev, 0x2058) & 0xFFF;
tmp2 |= (tmp & 0x30000000);
nv_wr32(dev, 0x2058, tmp2);
tmp &= ~0x30000000;
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, tmp);
nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 28));
nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 32));
nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 36));
tmp = nv_ri32(dev, fc + 40);
nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 44));
nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 48));
nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 52));
nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, nv_ri32(dev, fc + 56));
/* Don't clobber the TIMEOUT_ENABLED flag when restoring from RAMFC */
tmp = nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & ~0x1FFFF;
tmp |= nv_ri32(dev, fc + 60) & 0x1FFFF;
nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, tmp);
nv_wr32(dev, 0x32e4, nv_ri32(dev, fc + 64));
/* NVIDIA does this next line twice... */
nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68));
nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76));
nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80));
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
}
int
nv40_fifo_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
uint32_t tmp;
nv40_fifo_do_load_context(dev, chan->id);
/* Set channel active, and in DMA mode */
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
NV40_PFIFO_CACHE1_PUSH1_DMA | chan->id);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
/* Reset DMA_CTL_AT_INFO to INVALID */
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
return 0;
}
int
nv40_fifo_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
uint32_t fc, tmp;
int chid;
chid = pfifo->channel_id(dev);
if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
return 0;
fc = NV40_RAMFC(chid);
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
nv_wi32(dev, fc + 12, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE));
nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT));
nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH);
tmp |= nv_rd32(dev, 0x2058) & 0x30000000;
nv_wi32(dev, fc + 24, tmp);
nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
nv_wi32(dev, fc + 32, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
nv_wi32(dev, fc + 36, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
nv_wi32(dev, fc + 40, tmp);
nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
nv_wi32(dev, fc + 48, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
/* NVIDIA read 0x3228 first, then write DMA_GET here.. maybe something
* more involved depending on the value of 0x3228?
*/
nv_wi32(dev, fc + 52, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
nv_wi32(dev, fc + 56, nv_rd32(dev, NV40_PFIFO_GRCTX_INSTANCE));
nv_wi32(dev, fc + 60, nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & 0x1ffff);
/* No idea what the below is for exactly, ripped from a mmio-trace */
nv_wi32(dev, fc + 64, nv_rd32(dev, NV40_PFIFO_UNK32E4));
/* NVIDIA do this next line twice.. bug? */
nv_wi32(dev, fc + 68, nv_rd32(dev, 0x32e8));
nv_wi32(dev, fc + 76, nv_rd32(dev, 0x2088));
nv_wi32(dev, fc + 80, nv_rd32(dev, 0x3300));
#if 0 /* no real idea which is PUT/GET in UNK_48.. */
tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_GET);
tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16);
nv_wi32(dev, fc + 72, tmp);
#endif
dev_priv->engine.instmem.finish_access(dev);
nv40_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
NV40_PFIFO_CACHE1_PUSH1_DMA | (pfifo->channels - 1));
return 0;
}
static void
nv40_fifo_init_reset(struct drm_device *dev)
{
int i;
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
nv_wr32(dev, NV03_PMC_ENABLE,
nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
nv_wr32(dev, 0x003224, 0x000f0078);
nv_wr32(dev, 0x003210, 0x00000000);
nv_wr32(dev, 0x003270, 0x00000000);
nv_wr32(dev, 0x003240, 0x00000000);
nv_wr32(dev, 0x003244, 0x00000000);
nv_wr32(dev, 0x003258, 0x00000000);
nv_wr32(dev, 0x002504, 0x00000000);
for (i = 0; i < 16; i++)
nv_wr32(dev, 0x002510 + (i * 4), 0x00000000);
nv_wr32(dev, 0x00250c, 0x0000ffff);
nv_wr32(dev, 0x002048, 0x00000000);
nv_wr32(dev, 0x003228, 0x00000000);
nv_wr32(dev, 0x0032e8, 0x00000000);
nv_wr32(dev, 0x002410, 0x00000000);
nv_wr32(dev, 0x002420, 0x00000000);
nv_wr32(dev, 0x002058, 0x00000001);
nv_wr32(dev, 0x00221c, 0x00000000);
/* something with 0x2084, read/modify/write, no change */
nv_wr32(dev, 0x002040, 0x000000ff);
nv_wr32(dev, 0x002500, 0x00000000);
nv_wr32(dev, 0x003200, 0x00000000);
nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x2101ffff);
}
static void
nv40_fifo_init_ramxx(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
((dev_priv->ramht_bits - 9) << 16) |
(dev_priv->ramht_offset >> 8));
nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
switch (dev_priv->chipset) {
case 0x47:
case 0x49:
case 0x4b:
nv_wr32(dev, 0x2230, 1);
break;
default:
break;
}
switch (dev_priv->chipset) {
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x45:
case 0x47:
case 0x48:
case 0x49:
case 0x4b:
nv_wr32(dev, NV40_PFIFO_RAMFC, 0x30002);
break;
default:
nv_wr32(dev, 0x2230, 0);
nv_wr32(dev, NV40_PFIFO_RAMFC,
((nouveau_mem_fb_amount(dev) - 512 * 1024 +
dev_priv->ramfc_offset) >> 16) | (3 << 16));
break;
}
}
static void
nv40_fifo_init_intr(struct drm_device *dev)
{
nv_wr32(dev, 0x002100, 0xffffffff);
nv_wr32(dev, 0x002140, 0xffffffff);
}
int
nv40_fifo_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
int i;
nv40_fifo_init_reset(dev);
nv40_fifo_init_ramxx(dev);
nv40_fifo_do_load_context(dev, pfifo->channels - 1);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
nv40_fifo_init_intr(dev);
pfifo->enable(dev);
pfifo->reassign(dev, true);
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
if (dev_priv->fifos[i]) {
uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
}
}
return 0;
}

View File

@ -0,0 +1,560 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 <linux/firmware.h>
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
MODULE_FIRMWARE("nouveau/nv40.ctxprog");
MODULE_FIRMWARE("nouveau/nv40.ctxvals");
MODULE_FIRMWARE("nouveau/nv41.ctxprog");
MODULE_FIRMWARE("nouveau/nv41.ctxvals");
MODULE_FIRMWARE("nouveau/nv42.ctxprog");
MODULE_FIRMWARE("nouveau/nv42.ctxvals");
MODULE_FIRMWARE("nouveau/nv43.ctxprog");
MODULE_FIRMWARE("nouveau/nv43.ctxvals");
MODULE_FIRMWARE("nouveau/nv44.ctxprog");
MODULE_FIRMWARE("nouveau/nv44.ctxvals");
MODULE_FIRMWARE("nouveau/nv46.ctxprog");
MODULE_FIRMWARE("nouveau/nv46.ctxvals");
MODULE_FIRMWARE("nouveau/nv47.ctxprog");
MODULE_FIRMWARE("nouveau/nv47.ctxvals");
MODULE_FIRMWARE("nouveau/nv49.ctxprog");
MODULE_FIRMWARE("nouveau/nv49.ctxvals");
MODULE_FIRMWARE("nouveau/nv4a.ctxprog");
MODULE_FIRMWARE("nouveau/nv4a.ctxvals");
MODULE_FIRMWARE("nouveau/nv4b.ctxprog");
MODULE_FIRMWARE("nouveau/nv4b.ctxvals");
MODULE_FIRMWARE("nouveau/nv4c.ctxprog");
MODULE_FIRMWARE("nouveau/nv4c.ctxvals");
MODULE_FIRMWARE("nouveau/nv4e.ctxprog");
MODULE_FIRMWARE("nouveau/nv4e.ctxvals");
struct nouveau_channel *
nv40_graph_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t inst;
int i;
inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
return NULL;
inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
struct nouveau_channel *chan = dev_priv->fifos[i];
if (chan && chan->ramin_grctx &&
chan->ramin_grctx->instance == inst)
return chan;
}
return NULL;
}
int
nv40_graph_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ctx;
int ret;
/* Allocate a 175KiB block of PRAMIN to store the context. This
* is massive overkill for a lot of chipsets, but it should be safe
* until we're able to implement this properly (will happen at more
* or less the same time we're able to write our own context programs.
*/
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 175*1024, 16,
NVOBJ_FLAG_ZERO_ALLOC,
&chan->ramin_grctx);
if (ret)
return ret;
ctx = chan->ramin_grctx->gpuobj;
/* Initialise default context values */
dev_priv->engine.instmem.prepare_access(dev, true);
nv40_grctx_vals_load(dev, ctx);
nv_wo32(dev, ctx, 0, ctx->im_pramin->start);
dev_priv->engine.instmem.finish_access(dev);
return 0;
}
void
nv40_graph_destroy_context(struct nouveau_channel *chan)
{
nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx);
}
static int
nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
{
uint32_t old_cp, tv = 1000, tmp;
int i;
old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER);
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310);
tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE :
NV40_PGRAPH_CTXCTL_0310_XFER_LOAD;
nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp);
tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304);
tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX;
nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp);
nouveau_wait_for_idle(dev);
for (i = 0; i < tv; i++) {
if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0)
break;
}
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp);
if (i == tv) {
uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT);
NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save);
NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n",
ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT,
ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK);
NV_ERROR(dev, "0x40030C = 0x%08x\n",
nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C));
return -EBUSY;
}
return 0;
}
/* Restore the context for a specific channel into PGRAPH */
int
nv40_graph_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
uint32_t inst;
int ret;
if (!chan->ramin_grctx)
return -EINVAL;
inst = chan->ramin_grctx->instance >> 4;
ret = nv40_graph_transfer_context(dev, inst, 0);
if (ret)
return ret;
/* 0x40032C, no idea of it's exact function. Could simply be a
* record of the currently active PGRAPH context. It's currently
* unknown as to what bit 24 does. The nv ddx has it set, so we will
* set it here too.
*/
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR,
(inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) |
NV40_PGRAPH_CTXCTL_CUR_LOADED);
/* 0x32E0 records the instance address of the active FIFO's PGRAPH
* context. If at any time this doesn't match 0x40032C, you will
* recieve PGRAPH_INTR_CONTEXT_SWITCH
*/
nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst);
return 0;
}
int
nv40_graph_unload_context(struct drm_device *dev)
{
uint32_t inst;
int ret;
inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
return 0;
inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
ret = nv40_graph_transfer_context(dev, inst, 1);
nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
return ret;
}
struct nouveau_ctxprog {
uint32_t signature;
uint8_t version;
uint16_t length;
uint32_t data[];
} __attribute__ ((packed));
struct nouveau_ctxvals {
uint32_t signature;
uint8_t version;
uint32_t length;
struct {
uint32_t offset;
uint32_t value;
} data[];
} __attribute__ ((packed));
int
nv40_grctx_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
const int chipset = dev_priv->chipset;
const struct firmware *fw;
const struct nouveau_ctxprog *cp;
const struct nouveau_ctxvals *cv;
char name[32];
int ret, i;
pgraph->accel_blocked = true;
if (!pgraph->ctxprog) {
sprintf(name, "nouveau/nv%02x.ctxprog", chipset);
ret = request_firmware(&fw, name, &dev->pdev->dev);
if (ret) {
NV_ERROR(dev, "No ctxprog for NV%02x\n", chipset);
return ret;
}
pgraph->ctxprog = kmalloc(fw->size, GFP_KERNEL);
if (!pgraph->ctxprog) {
NV_ERROR(dev, "OOM copying ctxprog\n");
release_firmware(fw);
return -ENOMEM;
}
memcpy(pgraph->ctxprog, fw->data, fw->size);
cp = pgraph->ctxprog;
if (cp->signature != 0x5043564e || cp->version != 0 ||
cp->length != ((fw->size - 7) / 4)) {
NV_ERROR(dev, "ctxprog invalid\n");
release_firmware(fw);
nv40_grctx_fini(dev);
return -EINVAL;
}
release_firmware(fw);
}
if (!pgraph->ctxvals) {
sprintf(name, "nouveau/nv%02x.ctxvals", chipset);
ret = request_firmware(&fw, name, &dev->pdev->dev);
if (ret) {
NV_ERROR(dev, "No ctxvals for NV%02x\n", chipset);
nv40_grctx_fini(dev);
return ret;
}
pgraph->ctxvals = kmalloc(fw->size, GFP_KERNEL);
if (!pgraph->ctxprog) {
NV_ERROR(dev, "OOM copying ctxprog\n");
release_firmware(fw);
nv40_grctx_fini(dev);
return -ENOMEM;
}
memcpy(pgraph->ctxvals, fw->data, fw->size);
cv = (void *)pgraph->ctxvals;
if (cv->signature != 0x5643564e || cv->version != 0 ||
cv->length != ((fw->size - 9) / 8)) {
NV_ERROR(dev, "ctxvals invalid\n");
release_firmware(fw);
nv40_grctx_fini(dev);
return -EINVAL;
}
release_firmware(fw);
}
cp = pgraph->ctxprog;
nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
for (i = 0; i < cp->length; i++)
nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp->data[i]);
pgraph->accel_blocked = false;
return 0;
}
void
nv40_grctx_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
if (pgraph->ctxprog) {
kfree(pgraph->ctxprog);
pgraph->ctxprog = NULL;
}
if (pgraph->ctxvals) {
kfree(pgraph->ctxprog);
pgraph->ctxvals = NULL;
}
}
void
nv40_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
struct nouveau_ctxvals *cv = pgraph->ctxvals;
int i;
if (!cv)
return;
for (i = 0; i < cv->length; i++)
nv_wo32(dev, ctx, cv->data[i].offset, cv->data[i].value);
}
/*
* G70 0x47
* G71 0x49
* NV45 0x48
* G72[M] 0x46
* G73 0x4b
* C51_G7X 0x4c
* C51 0x4e
*/
int
nv40_graph_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv =
(struct drm_nouveau_private *)dev->dev_private;
uint32_t vramsz, tmp;
int i, j;
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
~NV_PMC_ENABLE_PGRAPH);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
NV_PMC_ENABLE_PGRAPH);
nv40_grctx_init(dev);
/* No context present currently */
nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000);
nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xe0de8055);
nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f);
nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
j = nv_rd32(dev, 0x1540) & 0xff;
if (j) {
for (i = 0; !(j & 1); j >>= 1, i++)
;
nv_wr32(dev, 0x405000, i);
}
if (dev_priv->chipset == 0x40) {
nv_wr32(dev, 0x4009b0, 0x83280fff);
nv_wr32(dev, 0x4009b4, 0x000000a0);
} else {
nv_wr32(dev, 0x400820, 0x83280eff);
nv_wr32(dev, 0x400824, 0x000000a0);
}
switch (dev_priv->chipset) {
case 0x40:
case 0x45:
nv_wr32(dev, 0x4009b8, 0x0078e366);
nv_wr32(dev, 0x4009bc, 0x0000014c);
break;
case 0x41:
case 0x42: /* pciid also 0x00Cx */
/* case 0x0120: XXX (pciid) */
nv_wr32(dev, 0x400828, 0x007596ff);
nv_wr32(dev, 0x40082c, 0x00000108);
break;
case 0x43:
nv_wr32(dev, 0x400828, 0x0072cb77);
nv_wr32(dev, 0x40082c, 0x00000108);
break;
case 0x44:
case 0x46: /* G72 */
case 0x4a:
case 0x4c: /* G7x-based C51 */
case 0x4e:
nv_wr32(dev, 0x400860, 0);
nv_wr32(dev, 0x400864, 0);
break;
case 0x47: /* G70 */
case 0x49: /* G71 */
case 0x4b: /* G73 */
nv_wr32(dev, 0x400828, 0x07830610);
nv_wr32(dev, 0x40082c, 0x0000016A);
break;
default:
break;
}
nv_wr32(dev, 0x400b38, 0x2ffff800);
nv_wr32(dev, 0x400b3c, 0x00006000);
/* copy tile info from PFB */
switch (dev_priv->chipset) {
case 0x40: /* vanilla NV40 */
for (i = 0; i < NV10_PFB_TILE__SIZE; i++) {
tmp = nv_rd32(dev, NV10_PFB_TILE(i));
nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp);
tmp = nv_rd32(dev, NV10_PFB_TLIMIT(i));
nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp);
tmp = nv_rd32(dev, NV10_PFB_TSIZE(i));
nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp);
tmp = nv_rd32(dev, NV10_PFB_TSTATUS(i));
nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp);
}
break;
case 0x44:
case 0x4a:
case 0x4e: /* NV44-based cores don't have 0x406900? */
for (i = 0; i < NV40_PFB_TILE__SIZE_0; i++) {
tmp = nv_rd32(dev, NV40_PFB_TILE(i));
nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i));
nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TSIZE(i));
nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i));
nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp);
}
break;
case 0x46:
case 0x47:
case 0x49:
case 0x4b: /* G7X-based cores */
for (i = 0; i < NV40_PFB_TILE__SIZE_1; i++) {
tmp = nv_rd32(dev, NV40_PFB_TILE(i));
nv_wr32(dev, NV47_PGRAPH_TILE0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i));
nv_wr32(dev, NV47_PGRAPH_TLIMIT0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TSIZE(i));
nv_wr32(dev, NV47_PGRAPH_TSIZE0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i));
nv_wr32(dev, NV47_PGRAPH_TSTATUS0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp);
}
break;
default: /* everything else */
for (i = 0; i < NV40_PFB_TILE__SIZE_0; i++) {
tmp = nv_rd32(dev, NV40_PFB_TILE(i));
nv_wr32(dev, NV40_PGRAPH_TILE0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TILE1(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TLIMIT(i));
nv_wr32(dev, NV40_PGRAPH_TLIMIT0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TSIZE(i));
nv_wr32(dev, NV40_PGRAPH_TSIZE0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), tmp);
tmp = nv_rd32(dev, NV40_PFB_TSTATUS(i));
nv_wr32(dev, NV40_PGRAPH_TSTATUS0(i), tmp);
nv_wr32(dev, NV40_PGRAPH_TSTATUS1(i), tmp);
}
break;
}
/* begin RAM config */
vramsz = drm_get_resource_len(dev, 0) - 1;
switch (dev_priv->chipset) {
case 0x40:
nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
nv_wr32(dev, 0x4069A4, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x4069A8, nv_rd32(dev, NV04_PFB_CFG1));
nv_wr32(dev, 0x400820, 0);
nv_wr32(dev, 0x400824, 0);
nv_wr32(dev, 0x400864, vramsz);
nv_wr32(dev, 0x400868, vramsz);
break;
default:
switch (dev_priv->chipset) {
case 0x46:
case 0x47:
case 0x49:
case 0x4b:
nv_wr32(dev, 0x400DF0, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x400DF4, nv_rd32(dev, NV04_PFB_CFG1));
break;
default:
nv_wr32(dev, 0x4009F0, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x4009F4, nv_rd32(dev, NV04_PFB_CFG1));
break;
}
nv_wr32(dev, 0x4069F0, nv_rd32(dev, NV04_PFB_CFG0));
nv_wr32(dev, 0x4069F4, nv_rd32(dev, NV04_PFB_CFG1));
nv_wr32(dev, 0x400840, 0);
nv_wr32(dev, 0x400844, 0);
nv_wr32(dev, 0x4008A0, vramsz);
nv_wr32(dev, 0x4008A4, vramsz);
break;
}
return 0;
}
void nv40_graph_takedown(struct drm_device *dev)
{
}
struct nouveau_pgraph_object_class nv40_graph_grclass[] = {
{ 0x0030, false, NULL }, /* null */
{ 0x0039, false, NULL }, /* m2mf */
{ 0x004a, false, NULL }, /* gdirect */
{ 0x009f, false, NULL }, /* imageblit (nv12) */
{ 0x008a, false, NULL }, /* ifc */
{ 0x0089, false, NULL }, /* sifm */
{ 0x3089, false, NULL }, /* sifm (nv40) */
{ 0x0062, false, NULL }, /* surf2d */
{ 0x3062, false, NULL }, /* surf2d (nv40) */
{ 0x0043, false, NULL }, /* rop */
{ 0x0012, false, NULL }, /* beta1 */
{ 0x0072, false, NULL }, /* beta4 */
{ 0x0019, false, NULL }, /* cliprect */
{ 0x0044, false, NULL }, /* pattern */
{ 0x309e, false, NULL }, /* swzsurf */
{ 0x4097, false, NULL }, /* curie (nv40) */
{ 0x4497, false, NULL }, /* curie (nv44) */
{}
};

View File

@ -0,0 +1,38 @@
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
int
nv40_mc_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t tmp;
/* Power up everything, resetting each individual unit will
* be done later if needed.
*/
nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
switch (dev_priv->chipset) {
case 0x44:
case 0x46: /* G72 */
case 0x4e:
case 0x4c: /* C51_G7X */
tmp = nv_rd32(dev, NV40_PFB_020C);
nv_wr32(dev, NV40_PMC_1700, tmp);
nv_wr32(dev, NV40_PMC_1704, 0);
nv_wr32(dev, NV40_PMC_1708, 0);
nv_wr32(dev, NV40_PMC_170C, tmp);
break;
default:
break;
}
return 0;
}
void
nv40_mc_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,769 @@
/*
* 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 "drmP.h"
#include "drm_mode.h"
#include "drm_crtc_helper.h"
#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
#include "nouveau_fb.h"
#include "nouveau_connector.h"
#include "nv50_display.h"
static void
nv50_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;
NV_DEBUG(crtc->dev, "\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 drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int index = nv_crtc->index, ret;
NV_DEBUG(dev, "index %d\n", nv_crtc->index);
NV_DEBUG(dev, "%s\n", blanked ? "blanked" : "unblanked");
if (blanked) {
nv_crtc->cursor.hide(nv_crtc, false);
ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 7 : 5);
if (ret) {
NV_ERROR(dev, "no space while blanking crtc\n");
return ret;
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK);
OUT_RING(evo, 0);
if (dev_priv->chipset != 0x50) {
BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE);
}
BEGIN_RING(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, dev_priv->chipset != 0x50 ? 10 : 8);
if (ret) {
NV_ERROR(dev, "no space while unblanking crtc\n");
return ret;
}
BEGIN_RING(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.mem.mm_node->start <<
PAGE_SHIFT) >> 8);
if (dev_priv->chipset != 0x50) {
BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
OUT_RING(evo, NvEvoVRAM);
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2);
OUT_RING(evo, nv_crtc->fb.offset >> 8);
OUT_RING(evo, 0);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
if (dev_priv->chipset != 0x50)
if (nv_crtc->fb.tile_flags == 0x7a00)
OUT_RING(evo, NvEvoFB32);
else
if (nv_crtc->fb.tile_flags == 0x7000)
OUT_RING(evo, NvEvoFB16);
else
OUT_RING(evo, NvEvoVRAM);
else
OUT_RING(evo, NvEvoVRAM);
}
nv_crtc->fb.blanked = blanked;
return 0;
}
static int
nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
{
struct drm_device *dev = nv_crtc->base.dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
NV_DEBUG(dev, "\n");
ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
if (ret) {
NV_ERROR(dev, "no space while setting dither\n");
return ret;
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1);
if (on)
OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON);
else
OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF);
if (update) {
BEGIN_RING(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, int scaling_mode, bool update)
{
struct nouveau_connector *nv_connector =
nouveau_crtc_connector_get(nv_crtc);
struct drm_device *dev = nv_crtc->base.dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct drm_display_mode *native_mode = NULL;
struct drm_display_mode *mode = &nv_crtc->base.mode;
uint32_t outX, outY, horiz, vert;
int ret;
NV_DEBUG(dev, "\n");
switch (scaling_mode) {
case DRM_MODE_SCALE_NONE:
break;
default:
if (!nv_connector || !nv_connector->native_mode) {
NV_ERROR(dev, "No native mode, forcing panel scaling\n");
scaling_mode = DRM_MODE_SCALE_NONE;
} else {
native_mode = nv_connector->native_mode;
}
break;
}
switch (scaling_mode) {
case DRM_MODE_SCALE_ASPECT:
horiz = (native_mode->hdisplay << 19) / mode->hdisplay;
vert = (native_mode->vdisplay << 19) / mode->vdisplay;
if (vert > horiz) {
outX = (mode->hdisplay * horiz) >> 19;
outY = (mode->vdisplay * horiz) >> 19;
} else {
outX = (mode->hdisplay * vert) >> 19;
outY = (mode->vdisplay * vert) >> 19;
}
break;
case DRM_MODE_SCALE_FULLSCREEN:
outX = native_mode->hdisplay;
outY = native_mode->vdisplay;
break;
case DRM_MODE_SCALE_CENTER:
case DRM_MODE_SCALE_NONE:
default:
outX = mode->hdisplay;
outY = mode->vdisplay;
break;
}
ret = RING_SPACE(evo, update ? 7 : 5);
if (ret)
return ret;
/* Got a better name for SCALER_ACTIVE? */
/* One day i've got to really figure out why this is needed. */
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ||
(mode->flags & DRM_MODE_FLAG_INTERLACE) ||
mode->hdisplay != outX || mode->vdisplay != outY) {
OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE);
} else {
OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE);
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
OUT_RING(evo, outY << 16 | outX);
OUT_RING(evo, outY << 16 | outX);
if (update) {
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
}
return 0;
}
int
nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
{
uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
struct nouveau_pll_vals pll;
struct pll_lims limits;
uint32_t reg1, reg2;
int ret;
ret = get_pll_limits(dev, pll_reg, &limits);
if (ret)
return ret;
ret = nouveau_calc_pll_mnp(dev, &limits, pclk, &pll);
if (ret <= 0)
return ret;
if (limits.vco2.maxfreq) {
reg1 = nv_rd32(dev, pll_reg + 4) & 0xff00ff00;
reg2 = nv_rd32(dev, pll_reg + 8) & 0x8000ff00;
nv_wr32(dev, pll_reg, 0x10000611);
nv_wr32(dev, pll_reg + 4, reg1 | (pll.M1 << 16) | pll.N1);
nv_wr32(dev, pll_reg + 8,
reg2 | (pll.log2P << 28) | (pll.M2 << 16) | pll.N2);
} else {
reg1 = nv_rd32(dev, pll_reg + 4) & 0xffc00000;
nv_wr32(dev, pll_reg, 0x50000610);
nv_wr32(dev, pll_reg + 4, reg1 |
(pll.log2P << 16) | (pll.M1 << 8) | pll.N1);
}
return 0;
}
static void
nv50_crtc_destroy(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
NV_DEBUG(dev, "\n");
if (!crtc)
return;
drm_crtc_cleanup(&nv_crtc->base);
nv50_cursor_fini(nv_crtc);
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
kfree(nv_crtc->mode);
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 drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nouveau_bo *cursor = NULL;
struct drm_gem_object *gem;
int ret = 0, i;
if (width != 64 || height != 64)
return -EINVAL;
if (!buffer_handle) {
nv_crtc->cursor.hide(nv_crtc, true);
return 0;
}
gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
if (!gem)
return -EINVAL;
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 -
dev_priv->vm_vram_base);
nv_crtc->cursor.show(nv_crtc, true);
out:
mutex_lock(&dev->struct_mutex);
drm_gem_object_unreference(gem);
mutex_unlock(&dev->struct_mutex);
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 size)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int i;
if (size != 256)
return;
for (i = 0; i < 256; 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)
{
NV_ERROR(crtc->dev, "!!\n");
}
static void
nv50_crtc_restore(struct drm_crtc *crtc)
{
NV_ERROR(crtc->dev, "!!\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,
.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 drm_encoder *encoder;
NV_DEBUG(dev, "index %d\n", nv_crtc->index);
/* Disconnect all unused encoders. */
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (drm_helper_encoder_in_use(encoder))
continue;
nv_encoder->disconnect(nv_encoder);
}
nv50_crtc_blank(nv_crtc, true);
}
static void
nv50_crtc_commit(struct drm_crtc *crtc)
{
struct drm_crtc *crtc2;
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
int ret;
NV_DEBUG(dev, "index %d\n", nv_crtc->index);
nv50_crtc_blank(nv_crtc, false);
/* Explicitly blank all unused crtc's. */
list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) {
if (!drm_helper_crtc_in_use(crtc2))
nv50_crtc_blank(nouveau_crtc(crtc2), true);
}
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while committing crtc\n");
return;
}
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
}
static bool
nv50_crtc_mode_fixup(struct drm_crtc *crtc, 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, int x, int y,
struct drm_framebuffer *old_fb, bool update)
{
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct drm_device *dev = nv_crtc->base.dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
int ret, format;
NV_DEBUG(dev, "index %d\n", nv_crtc->index);
switch (drm_fb->depth) {
case 8:
format = NV50_EVO_CRTC_FB_DEPTH_8;
break;
case 15:
format = NV50_EVO_CRTC_FB_DEPTH_15;
break;
case 16:
format = NV50_EVO_CRTC_FB_DEPTH_16;
break;
case 24:
case 32:
format = NV50_EVO_CRTC_FB_DEPTH_24;
break;
case 30:
format = NV50_EVO_CRTC_FB_DEPTH_30;
break;
default:
NV_ERROR(dev, "unknown depth %d\n", drm_fb->depth);
return -EINVAL;
}
ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
if (ret)
return ret;
if (old_fb) {
struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
nouveau_bo_unpin(ofb->nvbo);
}
nv_crtc->fb.offset = fb->nvbo->bo.offset - dev_priv->vm_vram_base;
nv_crtc->fb.tile_flags = fb->nvbo->tile_flags;
nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) {
ret = RING_SPACE(evo, 2);
if (ret)
return ret;
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1);
if (nv_crtc->fb.tile_flags == 0x7a00)
OUT_RING(evo, NvEvoFB32);
else
if (nv_crtc->fb.tile_flags == 0x7000)
OUT_RING(evo, NvEvoFB16);
else
OUT_RING(evo, NvEvoVRAM);
}
ret = RING_SPACE(evo, 12);
if (ret)
return ret;
BEGIN_RING(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);
if (!nv_crtc->fb.tile_flags) {
OUT_RING(evo, drm_fb->pitch | (1 << 20));
} else {
OUT_RING(evo, ((drm_fb->pitch / 4) << 4) |
fb->nvbo->tile_mode);
}
if (dev_priv->chipset == 0x50)
OUT_RING(evo, (fb->nvbo->tile_flags << 8) | format);
else
OUT_RING(evo, format);
BEGIN_RING(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_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
OUT_RING(evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR);
BEGIN_RING(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);
}
if (update) {
ret = RING_SPACE(evo, 2);
if (ret)
return ret;
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
}
return 0;
}
static int
nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nouveau_connector *nv_connector = NULL;
uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end;
uint32_t hunk1, vunk1, vunk2a, vunk2b;
int ret;
/* Find the connector attached to this CRTC */
nv_connector = nouveau_crtc_connector_get(nv_crtc);
*nv_crtc->mode = *adjusted_mode;
NV_DEBUG(dev, "index %d\n", nv_crtc->index);
hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start;
vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start;
/* I can't give this a proper name, anyone else can? */
hunk1 = adjusted_mode->htotal -
adjusted_mode->hsync_start + adjusted_mode->hdisplay;
vunk1 = adjusted_mode->vtotal -
adjusted_mode->vsync_start + adjusted_mode->vdisplay;
/* Another strange value, this time only for interlaced adjusted_modes. */
vunk2a = 2 * adjusted_mode->vtotal -
adjusted_mode->vsync_start + adjusted_mode->vdisplay;
vunk2b = adjusted_mode->vtotal -
adjusted_mode->vsync_start + adjusted_mode->vtotal;
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
vsync_dur /= 2;
vsync_start_to_end /= 2;
vunk1 /= 2;
vunk2a /= 2;
vunk2b /= 2;
/* magic */
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
vsync_start_to_end -= 1;
vunk1 -= 1;
vunk2a -= 1;
vunk2b -= 1;
}
}
ret = RING_SPACE(evo, 17);
if (ret)
return ret;
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2);
OUT_RING(evo, adjusted_mode->clock | 0x800000);
OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5);
OUT_RING(evo, 0);
OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal);
OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1));
OUT_RING(evo, (vsync_start_to_end - 1) << 16 |
(hsync_start_to_end - 1));
OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1));
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1);
OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1));
} else {
OUT_RING(evo, 0);
OUT_RING(evo, 0);
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1);
OUT_RING(evo, 0);
/* This is the actual resolution of the mode. */
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1);
OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1);
OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0));
nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, false);
}
static int
nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
struct drm_framebuffer *old_fb)
{
return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, true);
}
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,
.load_lut = nv50_crtc_lut_load,
};
int
nv50_crtc_create(struct drm_device *dev, int index)
{
struct nouveau_crtc *nv_crtc = NULL;
int ret, i;
NV_DEBUG(dev, "\n");
nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
if (!nv_crtc)
return -ENOMEM;
nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL);
if (!nv_crtc->mode) {
kfree(nv_crtc);
return -ENOMEM;
}
/* Default CLUT parameters, will be activated on the hw upon
* first mode set.
*/
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;
}
nv_crtc->lut.depth = 0;
ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM,
0, 0x0000, false, true, &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) {
kfree(nv_crtc->mode);
kfree(nv_crtc);
return ret;
}
nv_crtc->index = index;
/* set function pointers */
nv_crtc->set_dither = nv50_crtc_set_dither;
nv_crtc->set_scale = nv50_crtc_set_scale;
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, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
0, 0x0000, false, true, &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);
}
nv50_cursor_init(nv_crtc);
return 0;
}

View File

@ -0,0 +1,156 @@
/*
* 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 "drmP.h"
#include "drm_mode.h"
#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "nouveau_crtc.h"
#include "nv50_display.h"
static void
nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
{
struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct drm_device *dev = nv_crtc->base.dev;
int ret;
NV_DEBUG(dev, "\n");
if (update && nv_crtc->cursor.visible)
return;
ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
if (ret) {
NV_ERROR(dev, "no space while unhiding cursor\n");
return;
}
if (dev_priv->chipset != 0x50) {
BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
OUT_RING(evo, NvEvoVRAM);
}
BEGIN_RING(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_RING(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_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct drm_device *dev = nv_crtc->base.dev;
int ret;
NV_DEBUG(dev, "\n");
if (update && !nv_crtc->cursor.visible)
return;
ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
if (ret) {
NV_ERROR(dev, "no space while hiding cursor\n");
return;
}
BEGIN_RING(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 (dev_priv->chipset != 0x50) {
BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE);
}
if (update) {
BEGIN_RING(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 drm_device *dev = nv_crtc->base.dev;
nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index),
((y & 0xFFFF) << 16) | (x & 0xFFFF));
/* Needed to make the cursor move. */
nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0);
}
static void
nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
{
NV_DEBUG(nv_crtc->base.dev, "\n");
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;
}
void
nv50_cursor_fini(struct nouveau_crtc *nv_crtc)
{
struct drm_device *dev = nv_crtc->base.dev;
int idx = nv_crtc->index;
NV_DEBUG(dev, "\n");
nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx)));
}
}

View File

@ -0,0 +1,304 @@
/*
* 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 "drmP.h"
#include "drm_crtc_helper.h"
#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nv50_display.h"
static void
nv50_dac_disconnect(struct nouveau_encoder *nv_encoder)
{
struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
NV_DEBUG(dev, "Disconnecting DAC %d\n", nv_encoder->or);
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while disconnecting DAC\n");
return;
}
BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1);
OUT_RING(evo, 0);
}
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 drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
enum drm_connector_status status = connector_status_disconnected;
uint32_t dpms_state, load_pattern, load_state;
int or = nv_encoder->or;
nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001);
dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or));
nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
return status;
}
/* Use bios provided value if possible. */
if (dev_priv->vbios->dactestval) {
load_pattern = dev_priv->vbios->dactestval;
NV_DEBUG(dev, "Using bios provided load_pattern of %d\n",
load_pattern);
} else {
load_pattern = 340;
NV_DEBUG(dev, "Using default load_pattern of %d\n",
load_pattern);
}
nv_wr32(dev, 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(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or));
nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or), 0);
nv_wr32(dev, 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(dev, "Load was detected on output with or %d\n", or);
else
NV_DEBUG(dev, "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 drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
uint32_t val;
int or = nv_encoder->or;
NV_DEBUG(dev, "or %d mode %d\n", or, mode);
/* wait for it to be done */
if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
return;
}
val = nv_rd32(dev, 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(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val |
NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
}
static void
nv50_dac_save(struct drm_encoder *encoder)
{
NV_ERROR(encoder->dev, "!!\n");
}
static void
nv50_dac_restore(struct drm_encoder *encoder)
{
NV_ERROR(encoder->dev, "!!\n");
}
static bool
nv50_dac_mode_fixup(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_connector *connector;
NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or);
connector = nouveau_encoder_connector_get(nv_encoder);
if (!connector) {
NV_ERROR(encoder->dev, "Encoder has no connector\n");
return false;
}
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
connector->native_mode) {
int id = adjusted_mode->base.id;
*adjusted_mode = *connector->native_mode;
adjusted_mode->base.id = id;
}
return true;
}
static void
nv50_dac_prepare(struct drm_encoder *encoder)
{
}
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 drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
uint32_t mode_ctl = 0, mode_ctl2 = 0;
int ret;
NV_DEBUG(dev, "or %d\n", nv_encoder->or);
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 == OUTPUT_ANALOG)
mode_ctl |= 0x40;
else
if (nv_encoder->dcb->type == 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(dev, "no space while connecting DAC\n");
return;
}
BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2);
OUT_RING(evo, mode_ctl);
OUT_RING(evo, mode_ctl2);
}
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_prepare,
.commit = nv50_dac_commit,
.mode_set = nv50_dac_mode_set,
.detect = nv50_dac_detect
};
static void
nv50_dac_destroy(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (!encoder)
return;
NV_DEBUG(encoder->dev, "\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_device *dev, struct dcb_entry *entry)
{
struct nouveau_encoder *nv_encoder;
struct drm_encoder *encoder;
NV_DEBUG(dev, "\n");
NV_INFO(dev, "Detected a DAC output\n");
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->disconnect = nv50_dac_disconnect;
drm_encoder_init(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;
return 0;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
/*
* 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_DISPLAY_H__
#define __NV50_DISPLAY_H__
#include "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
#include "nouveau_reg.h"
#include "nouveau_crtc.h"
#include "nv50_evo.h"
void nv50_display_irq_handler(struct drm_device *dev);
void nv50_display_irq_handler_bh(struct work_struct *work);
int nv50_display_init(struct drm_device *dev);
int nv50_display_create(struct drm_device *dev);
int nv50_display_destroy(struct drm_device *dev);
int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
#endif /* __NV50_DISPLAY_H__ */

View File

@ -0,0 +1,113 @@
/*
* 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.
*
*/
#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_COLOR 0x00040000
#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

View File

@ -0,0 +1,273 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
#include "nouveau_fbcon.h"
static void
nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (!(info->flags & FBINFO_HWACCEL_DISABLED) &&
RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_fillrect(info, rect);
return;
}
if (rect->rop != ROP_COPY) {
BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
OUT_RING(chan, 1);
}
BEGIN_RING(chan, NvSub2D, 0x0588, 1);
OUT_RING(chan, rect->color);
BEGIN_RING(chan, NvSub2D, 0x0600, 4);
OUT_RING(chan, rect->dx);
OUT_RING(chan, rect->dy);
OUT_RING(chan, rect->dx + rect->width);
OUT_RING(chan, rect->dy + rect->height);
if (rect->rop != ROP_COPY) {
BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
OUT_RING(chan, 3);
}
FIRE_RING(chan);
}
static void
nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 12)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_copyarea(info, region);
return;
}
BEGIN_RING(chan, NvSub2D, 0x0110, 1);
OUT_RING(chan, 0);
BEGIN_RING(chan, NvSub2D, 0x08b0, 4);
OUT_RING(chan, region->dx);
OUT_RING(chan, region->dy);
OUT_RING(chan, region->width);
OUT_RING(chan, region->height);
BEGIN_RING(chan, NvSub2D, 0x08d0, 4);
OUT_RING(chan, 0);
OUT_RING(chan, region->sx);
OUT_RING(chan, 0);
OUT_RING(chan, region->sy);
FIRE_RING(chan);
}
static void
nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
uint32_t width, dwords, *data = (uint32_t *)image->data;
uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
uint32_t *palette = info->pseudo_palette;
if (info->state != FBINFO_STATE_RUNNING)
return;
if (image->depth != 1) {
cfb_imageblit(info, image);
return;
}
if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 11)) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
}
if (info->flags & FBINFO_HWACCEL_DISABLED) {
cfb_imageblit(info, image);
return;
}
width = (image->width + 31) & ~31;
dwords = (width * image->height) >> 5;
BEGIN_RING(chan, NvSub2D, 0x0814, 2);
if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
OUT_RING(chan, palette[image->bg_color] | mask);
OUT_RING(chan, palette[image->fg_color] | mask);
} else {
OUT_RING(chan, image->bg_color);
OUT_RING(chan, image->fg_color);
}
BEGIN_RING(chan, NvSub2D, 0x0838, 2);
OUT_RING(chan, image->width);
OUT_RING(chan, image->height);
BEGIN_RING(chan, NvSub2D, 0x0850, 4);
OUT_RING(chan, 0);
OUT_RING(chan, image->dx);
OUT_RING(chan, 0);
OUT_RING(chan, image->dy);
while (dwords) {
int push = dwords > 2047 ? 2047 : dwords;
if (RING_SPACE(chan, push + 1)) {
NV_ERROR(dev,
"GPU lockup - switching to software fbcon\n");
info->flags |= FBINFO_HWACCEL_DISABLED;
cfb_imageblit(info, image);
return;
}
dwords -= push;
BEGIN_RING(chan, NvSub2D, 0x40000860, push);
OUT_RINGp(chan, data, push);
data += push;
}
FIRE_RING(chan);
}
int
nv50_fbcon_accel_init(struct fb_info *info)
{
struct nouveau_fbcon_par *par = info->par;
struct drm_device *dev = par->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->channel;
struct nouveau_gpuobj *eng2d = NULL;
int ret, format;
switch (info->var.bits_per_pixel) {
case 8:
format = 0xf3;
break;
case 15:
format = 0xf8;
break;
case 16:
format = 0xe8;
break;
case 32:
switch (info->var.transp.length) {
case 0: /* depth 24 */
case 8: /* depth 32, just use 24.. */
format = 0xe6;
break;
case 2: /* depth 30 */
format = 0xd1;
break;
default:
return -EINVAL;
}
break;
default:
return -EINVAL;
}
ret = nouveau_gpuobj_gr_new(dev_priv->channel, 0x502d, &eng2d);
if (ret)
return ret;
ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL);
if (ret)
return ret;
ret = RING_SPACE(chan, 59);
if (ret) {
NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
return ret;
}
BEGIN_RING(chan, NvSub2D, 0x0000, 1);
OUT_RING(chan, Nv2D);
BEGIN_RING(chan, NvSub2D, 0x0180, 4);
OUT_RING(chan, NvNotify0);
OUT_RING(chan, chan->vram_handle);
OUT_RING(chan, chan->vram_handle);
OUT_RING(chan, chan->vram_handle);
BEGIN_RING(chan, NvSub2D, 0x0290, 1);
OUT_RING(chan, 0);
BEGIN_RING(chan, NvSub2D, 0x0888, 1);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
OUT_RING(chan, 3);
BEGIN_RING(chan, NvSub2D, 0x02a0, 1);
OUT_RING(chan, 0x55);
BEGIN_RING(chan, NvSub2D, 0x08c0, 4);
OUT_RING(chan, 0);
OUT_RING(chan, 1);
OUT_RING(chan, 0);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0580, 2);
OUT_RING(chan, 4);
OUT_RING(chan, format);
BEGIN_RING(chan, NvSub2D, 0x02e8, 2);
OUT_RING(chan, 2);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0804, 1);
OUT_RING(chan, format);
BEGIN_RING(chan, NvSub2D, 0x0800, 1);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0808, 3);
OUT_RING(chan, 0);
OUT_RING(chan, 0);
OUT_RING(chan, 0);
BEGIN_RING(chan, NvSub2D, 0x081c, 1);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0840, 4);
OUT_RING(chan, 0);
OUT_RING(chan, 1);
OUT_RING(chan, 0);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0200, 2);
OUT_RING(chan, format);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0214, 5);
OUT_RING(chan, info->fix.line_length);
OUT_RING(chan, info->var.xres_virtual);
OUT_RING(chan, info->var.yres_virtual);
OUT_RING(chan, 0);
OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
dev_priv->vm_vram_base);
BEGIN_RING(chan, NvSub2D, 0x0230, 2);
OUT_RING(chan, format);
OUT_RING(chan, 1);
BEGIN_RING(chan, NvSub2D, 0x0244, 5);
OUT_RING(chan, info->fix.line_length);
OUT_RING(chan, info->var.xres_virtual);
OUT_RING(chan, info->var.yres_virtual);
OUT_RING(chan, 0);
OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
dev_priv->vm_vram_base);
info->fbops->fb_fillrect = nv50_fbcon_fillrect;
info->fbops->fb_copyarea = nv50_fbcon_copyarea;
info->fbops->fb_imageblit = nv50_fbcon_imageblit;
return 0;
}

View File

@ -0,0 +1,494 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
struct nv50_fifo_priv {
struct nouveau_gpuobj_ref *thingo[2];
int cur_thingo;
};
#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
static void
nv50_fifo_init_thingo(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
struct nouveau_gpuobj_ref *cur;
int i, nr;
NV_DEBUG(dev, "\n");
cur = priv->thingo[priv->cur_thingo];
priv->cur_thingo = !priv->cur_thingo;
/* We never schedule channel 0 or 127 */
dev_priv->engine.instmem.prepare_access(dev, true);
for (i = 1, nr = 0; i < 127; i++) {
if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc)
nv_wo32(dev, cur->gpuobj, nr++, i);
}
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, 0x32f4, cur->instance >> 12);
nv_wr32(dev, 0x32ec, nr);
nv_wr32(dev, 0x2500, 0x101);
}
static int
nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->fifos[channel];
uint32_t inst;
NV_DEBUG(dev, "ch%d\n", channel);
if (!chan->ramfc)
return -EINVAL;
if (IS_G80)
inst = chan->ramfc->instance >> 12;
else
inst = chan->ramfc->instance >> 8;
nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel),
inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
if (!nt)
nv50_fifo_init_thingo(dev);
return 0;
}
static void
nv50_fifo_channel_disable(struct drm_device *dev, int channel, bool nt)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t inst;
NV_DEBUG(dev, "ch%d, nt=%d\n", channel, nt);
if (IS_G80)
inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80;
else
inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84;
nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst);
if (!nt)
nv50_fifo_init_thingo(dev);
}
static void
nv50_fifo_init_reset(struct drm_device *dev)
{
uint32_t pmc_e = NV_PMC_ENABLE_PFIFO;
NV_DEBUG(dev, "\n");
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
}
static void
nv50_fifo_init_intr(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF);
nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF);
}
static void
nv50_fifo_init_context_table(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int i;
NV_DEBUG(dev, "\n");
for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) {
if (dev_priv->fifos[i])
nv50_fifo_channel_enable(dev, i, true);
else
nv50_fifo_channel_disable(dev, i, true);
}
nv50_fifo_init_thingo(dev);
}
static void
nv50_fifo_init_regs__nv(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv_wr32(dev, 0x250c, 0x6f3cfc34);
}
static void
nv50_fifo_init_regs(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv_wr32(dev, 0x2500, 0);
nv_wr32(dev, 0x3250, 0);
nv_wr32(dev, 0x3220, 0);
nv_wr32(dev, 0x3204, 0);
nv_wr32(dev, 0x3210, 0);
nv_wr32(dev, 0x3270, 0);
/* Enable dummy channels setup by nv50_instmem.c */
nv50_fifo_channel_enable(dev, 0, true);
nv50_fifo_channel_enable(dev, 127, true);
}
int
nv50_fifo_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_fifo_priv *priv;
int ret;
NV_DEBUG(dev, "\n");
priv = dev_priv->engine.fifo.priv;
if (priv) {
priv->cur_thingo = !priv->cur_thingo;
goto just_reset;
}
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_priv->engine.fifo.priv = priv;
ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]);
if (ret) {
NV_ERROR(dev, "error creating thingo0: %d\n", ret);
return ret;
}
ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]);
if (ret) {
NV_ERROR(dev, "error creating thingo1: %d\n", ret);
return ret;
}
just_reset:
nv50_fifo_init_reset(dev);
nv50_fifo_init_intr(dev);
nv50_fifo_init_context_table(dev);
nv50_fifo_init_regs__nv(dev);
nv50_fifo_init_regs(dev);
dev_priv->engine.fifo.enable(dev);
dev_priv->engine.fifo.reassign(dev, true);
return 0;
}
void
nv50_fifo_takedown(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
NV_DEBUG(dev, "\n");
if (!priv)
return;
nouveau_gpuobj_ref_del(dev, &priv->thingo[0]);
nouveau_gpuobj_ref_del(dev, &priv->thingo[1]);
dev_priv->engine.fifo.priv = NULL;
kfree(priv);
}
int
nv50_fifo_channel_id(struct drm_device *dev)
{
return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
NV50_PFIFO_CACHE1_PUSH1_CHID_MASK;
}
int
nv50_fifo_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ramfc = NULL;
int ret;
NV_DEBUG(dev, "ch%d\n", chan->id);
if (IS_G80) {
uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start;
uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start;
ret = nouveau_gpuobj_new_fake(dev, ramin_poffset, ramin_voffset,
0x100, NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, &ramfc,
&chan->ramfc);
if (ret)
return ret;
ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400,
ramin_voffset + 0x0400, 4096,
0, NULL, &chan->cache);
if (ret)
return ret;
} else {
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 0x100, 256,
NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE,
&chan->ramfc);
if (ret)
return ret;
ramfc = chan->ramfc->gpuobj;
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 256,
0, &chan->cache);
if (ret)
return ret;
}
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base);
nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
nv_wo32(dev, ramfc, 0x3c/4, 0x00086078);
nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff);
if (!IS_G80) {
nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
nv_wo32(dev, chan->ramin->gpuobj, 1,
chan->ramfc->instance >> 8);
nv_wo32(dev, ramfc, 0x88/4, chan->cache->instance >> 10);
nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12);
}
dev_priv->engine.instmem.finish_access(dev);
ret = nv50_fifo_channel_enable(dev, chan->id, false);
if (ret) {
NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
return ret;
}
return 0;
}
void
nv50_fifo_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
NV_DEBUG(dev, "ch%d\n", chan->id);
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
nouveau_gpuobj_ref_del(dev, &chan->cache);
nv50_fifo_channel_disable(dev, chan->id, false);
/* Dummy channel, also used on ch 127 */
if (chan->id == 0)
nv50_fifo_channel_disable(dev, 127, false);
}
int
nv50_fifo_load_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj;
struct nouveau_gpuobj *cache = chan->cache->gpuobj;
int ptr, cnt;
NV_DEBUG(dev, "ch%d\n", chan->id);
dev_priv->engine.instmem.prepare_access(dev, false);
nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4));
nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4));
nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4));
nv_wr32(dev, 0x3320, nv_ro32(dev, ramfc, 0x0c/4));
nv_wr32(dev, 0x3244, nv_ro32(dev, ramfc, 0x10/4));
nv_wr32(dev, 0x3328, nv_ro32(dev, ramfc, 0x14/4));
nv_wr32(dev, 0x3368, nv_ro32(dev, ramfc, 0x18/4));
nv_wr32(dev, 0x336c, nv_ro32(dev, ramfc, 0x1c/4));
nv_wr32(dev, 0x3370, nv_ro32(dev, ramfc, 0x20/4));
nv_wr32(dev, 0x3374, nv_ro32(dev, ramfc, 0x24/4));
nv_wr32(dev, 0x3378, nv_ro32(dev, ramfc, 0x28/4));
nv_wr32(dev, 0x337c, nv_ro32(dev, ramfc, 0x2c/4));
nv_wr32(dev, 0x3228, nv_ro32(dev, ramfc, 0x30/4));
nv_wr32(dev, 0x3364, nv_ro32(dev, ramfc, 0x34/4));
nv_wr32(dev, 0x32a0, nv_ro32(dev, ramfc, 0x38/4));
nv_wr32(dev, 0x3224, nv_ro32(dev, ramfc, 0x3c/4));
nv_wr32(dev, 0x324c, nv_ro32(dev, ramfc, 0x40/4));
nv_wr32(dev, 0x2044, nv_ro32(dev, ramfc, 0x44/4));
nv_wr32(dev, 0x322c, nv_ro32(dev, ramfc, 0x48/4));
nv_wr32(dev, 0x3234, nv_ro32(dev, ramfc, 0x4c/4));
nv_wr32(dev, 0x3340, nv_ro32(dev, ramfc, 0x50/4));
nv_wr32(dev, 0x3344, nv_ro32(dev, ramfc, 0x54/4));
nv_wr32(dev, 0x3280, nv_ro32(dev, ramfc, 0x58/4));
nv_wr32(dev, 0x3254, nv_ro32(dev, ramfc, 0x5c/4));
nv_wr32(dev, 0x3260, nv_ro32(dev, ramfc, 0x60/4));
nv_wr32(dev, 0x3264, nv_ro32(dev, ramfc, 0x64/4));
nv_wr32(dev, 0x3268, nv_ro32(dev, ramfc, 0x68/4));
nv_wr32(dev, 0x326c, nv_ro32(dev, ramfc, 0x6c/4));
nv_wr32(dev, 0x32e4, nv_ro32(dev, ramfc, 0x70/4));
nv_wr32(dev, 0x3248, nv_ro32(dev, ramfc, 0x74/4));
nv_wr32(dev, 0x2088, nv_ro32(dev, ramfc, 0x78/4));
nv_wr32(dev, 0x2058, nv_ro32(dev, ramfc, 0x7c/4));
nv_wr32(dev, 0x2210, nv_ro32(dev, ramfc, 0x80/4));
cnt = nv_ro32(dev, ramfc, 0x84/4);
for (ptr = 0; ptr < cnt; ptr++) {
nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr),
nv_ro32(dev, cache, (ptr * 2) + 0));
nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr),
nv_ro32(dev, cache, (ptr * 2) + 1));
}
nv_wr32(dev, 0x3210, cnt << 2);
nv_wr32(dev, 0x3270, 0);
/* guessing that all the 0x34xx regs aren't on NV50 */
if (!IS_G80) {
nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4));
nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4));
nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4));
nv_wr32(dev, 0x3408, nv_ro32(dev, ramfc, 0x94/4));
nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4));
}
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
return 0;
}
int
nv50_fifo_unload_context(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
struct nouveau_gpuobj *ramfc, *cache;
struct nouveau_channel *chan = NULL;
int chid, get, put, ptr;
NV_DEBUG(dev, "\n");
chid = pfifo->channel_id(dev);
if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
return 0;
chan = dev_priv->fifos[chid];
if (!chan) {
NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
return -EINVAL;
}
NV_DEBUG(dev, "ch%d\n", chan->id);
ramfc = chan->ramfc->gpuobj;
cache = chan->cache->gpuobj;
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330));
nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334));
nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240));
nv_wo32(dev, ramfc, 0x0c/4, nv_rd32(dev, 0x3320));
nv_wo32(dev, ramfc, 0x10/4, nv_rd32(dev, 0x3244));
nv_wo32(dev, ramfc, 0x14/4, nv_rd32(dev, 0x3328));
nv_wo32(dev, ramfc, 0x18/4, nv_rd32(dev, 0x3368));
nv_wo32(dev, ramfc, 0x1c/4, nv_rd32(dev, 0x336c));
nv_wo32(dev, ramfc, 0x20/4, nv_rd32(dev, 0x3370));
nv_wo32(dev, ramfc, 0x24/4, nv_rd32(dev, 0x3374));
nv_wo32(dev, ramfc, 0x28/4, nv_rd32(dev, 0x3378));
nv_wo32(dev, ramfc, 0x2c/4, nv_rd32(dev, 0x337c));
nv_wo32(dev, ramfc, 0x30/4, nv_rd32(dev, 0x3228));
nv_wo32(dev, ramfc, 0x34/4, nv_rd32(dev, 0x3364));
nv_wo32(dev, ramfc, 0x38/4, nv_rd32(dev, 0x32a0));
nv_wo32(dev, ramfc, 0x3c/4, nv_rd32(dev, 0x3224));
nv_wo32(dev, ramfc, 0x40/4, nv_rd32(dev, 0x324c));
nv_wo32(dev, ramfc, 0x44/4, nv_rd32(dev, 0x2044));
nv_wo32(dev, ramfc, 0x48/4, nv_rd32(dev, 0x322c));
nv_wo32(dev, ramfc, 0x4c/4, nv_rd32(dev, 0x3234));
nv_wo32(dev, ramfc, 0x50/4, nv_rd32(dev, 0x3340));
nv_wo32(dev, ramfc, 0x54/4, nv_rd32(dev, 0x3344));
nv_wo32(dev, ramfc, 0x58/4, nv_rd32(dev, 0x3280));
nv_wo32(dev, ramfc, 0x5c/4, nv_rd32(dev, 0x3254));
nv_wo32(dev, ramfc, 0x60/4, nv_rd32(dev, 0x3260));
nv_wo32(dev, ramfc, 0x64/4, nv_rd32(dev, 0x3264));
nv_wo32(dev, ramfc, 0x68/4, nv_rd32(dev, 0x3268));
nv_wo32(dev, ramfc, 0x6c/4, nv_rd32(dev, 0x326c));
nv_wo32(dev, ramfc, 0x70/4, nv_rd32(dev, 0x32e4));
nv_wo32(dev, ramfc, 0x74/4, nv_rd32(dev, 0x3248));
nv_wo32(dev, ramfc, 0x78/4, nv_rd32(dev, 0x2088));
nv_wo32(dev, ramfc, 0x7c/4, nv_rd32(dev, 0x2058));
nv_wo32(dev, ramfc, 0x80/4, nv_rd32(dev, 0x2210));
put = (nv_rd32(dev, NV03_PFIFO_CACHE1_PUT) & 0x7ff) >> 2;
get = (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) & 0x7ff) >> 2;
ptr = 0;
while (put != get) {
nv_wo32(dev, cache, ptr++,
nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get)));
nv_wo32(dev, cache, ptr++,
nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get)));
get = (get + 1) & 0x1ff;
}
/* guessing that all the 0x34xx regs aren't on NV50 */
if (!IS_G80) {
nv_wo32(dev, ramfc, 0x84/4, ptr >> 1);
nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c));
nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400));
nv_wo32(dev, ramfc, 0x90/4, nv_rd32(dev, 0x3404));
nv_wo32(dev, ramfc, 0x94/4, nv_rd32(dev, 0x3408));
nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410));
}
dev_priv->engine.instmem.finish_access(dev);
/*XXX: probably reload ch127 (NULL) state back too */
nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127);
return 0;
}

View File

@ -0,0 +1,385 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
MODULE_FIRMWARE("nouveau/nv50.ctxprog");
MODULE_FIRMWARE("nouveau/nv50.ctxvals");
MODULE_FIRMWARE("nouveau/nv84.ctxprog");
MODULE_FIRMWARE("nouveau/nv84.ctxvals");
MODULE_FIRMWARE("nouveau/nv86.ctxprog");
MODULE_FIRMWARE("nouveau/nv86.ctxvals");
MODULE_FIRMWARE("nouveau/nv92.ctxprog");
MODULE_FIRMWARE("nouveau/nv92.ctxvals");
MODULE_FIRMWARE("nouveau/nv94.ctxprog");
MODULE_FIRMWARE("nouveau/nv94.ctxvals");
MODULE_FIRMWARE("nouveau/nv96.ctxprog");
MODULE_FIRMWARE("nouveau/nv96.ctxvals");
MODULE_FIRMWARE("nouveau/nv98.ctxprog");
MODULE_FIRMWARE("nouveau/nv98.ctxvals");
MODULE_FIRMWARE("nouveau/nva0.ctxprog");
MODULE_FIRMWARE("nouveau/nva0.ctxvals");
MODULE_FIRMWARE("nouveau/nva5.ctxprog");
MODULE_FIRMWARE("nouveau/nva5.ctxvals");
MODULE_FIRMWARE("nouveau/nva8.ctxprog");
MODULE_FIRMWARE("nouveau/nva8.ctxvals");
MODULE_FIRMWARE("nouveau/nvaa.ctxprog");
MODULE_FIRMWARE("nouveau/nvaa.ctxvals");
MODULE_FIRMWARE("nouveau/nvac.ctxprog");
MODULE_FIRMWARE("nouveau/nvac.ctxvals");
#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
static void
nv50_graph_init_reset(struct drm_device *dev)
{
uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21);
NV_DEBUG(dev, "\n");
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
}
static void
nv50_graph_init_intr(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff);
nv_wr32(dev, 0x400138, 0xffffffff);
nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff);
}
static void
nv50_graph_init_regs__nv(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv_wr32(dev, 0x400804, 0xc0000000);
nv_wr32(dev, 0x406800, 0xc0000000);
nv_wr32(dev, 0x400c04, 0xc0000000);
nv_wr32(dev, 0x401804, 0xc0000000);
nv_wr32(dev, 0x405018, 0xc0000000);
nv_wr32(dev, 0x402000, 0xc0000000);
nv_wr32(dev, 0x400108, 0xffffffff);
nv_wr32(dev, 0x400824, 0x00004000);
nv_wr32(dev, 0x400500, 0x00010001);
}
static void
nv50_graph_init_regs(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv_wr32(dev, NV04_PGRAPH_DEBUG_3,
(1 << 2) /* HW_CONTEXT_SWITCH_ENABLED */);
nv_wr32(dev, 0x402ca8, 0x800);
}
static int
nv50_graph_init_ctxctl(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv40_grctx_init(dev);
nv_wr32(dev, 0x400320, 4);
nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0);
return 0;
}
int
nv50_graph_init(struct drm_device *dev)
{
int ret;
NV_DEBUG(dev, "\n");
nv50_graph_init_reset(dev);
nv50_graph_init_regs__nv(dev);
nv50_graph_init_regs(dev);
nv50_graph_init_intr(dev);
ret = nv50_graph_init_ctxctl(dev);
if (ret)
return ret;
return 0;
}
void
nv50_graph_takedown(struct drm_device *dev)
{
NV_DEBUG(dev, "\n");
nv40_grctx_fini(dev);
}
void
nv50_graph_fifo_access(struct drm_device *dev, bool enabled)
{
const uint32_t mask = 0x00010001;
if (enabled)
nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask);
else
nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask);
}
struct nouveau_channel *
nv50_graph_channel(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
uint32_t inst;
int i;
inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
return NULL;
inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12;
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
struct nouveau_channel *chan = dev_priv->fifos[i];
if (chan && chan->ramin && chan->ramin->instance == inst)
return chan;
}
return NULL;
}
int
nv50_graph_create_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
struct nouveau_gpuobj *ctx;
uint32_t grctx_size = 0x70000;
int hdr, ret;
NV_DEBUG(dev, "ch%d\n", chan->id);
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000,
NVOBJ_FLAG_ZERO_ALLOC |
NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
if (ret)
return ret;
ctx = chan->ramin_grctx->gpuobj;
hdr = IS_G80 ? 0x200 : 0x20;
dev_priv->engine.instmem.prepare_access(dev, true);
nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
grctx_size - 1);
nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance);
nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000);
dev_priv->engine.instmem.finish_access(dev);
dev_priv->engine.instmem.prepare_access(dev, true);
nv40_grctx_vals_load(dev, ctx);
nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
if ((dev_priv->chipset & 0xf0) == 0xa0)
nv_wo32(dev, ctx, 0x00004/4, 0x00000000);
else
nv_wo32(dev, ctx, 0x0011c/4, 0x00000000);
dev_priv->engine.instmem.finish_access(dev);
return 0;
}
void
nv50_graph_destroy_context(struct nouveau_channel *chan)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
int i, hdr = IS_G80 ? 0x200 : 0x20;
NV_DEBUG(dev, "ch%d\n", chan->id);
if (!chan->ramin || !chan->ramin->gpuobj)
return;
dev_priv->engine.instmem.prepare_access(dev, true);
for (i = hdr; i < hdr + 24; i += 4)
nv_wo32(dev, chan->ramin->gpuobj, i/4, 0);
dev_priv->engine.instmem.finish_access(dev);
nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
}
static int
nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
{
uint32_t fifo = nv_rd32(dev, 0x400500);
nv_wr32(dev, 0x400500, fifo & ~1);
nv_wr32(dev, 0x400784, inst);
nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40);
nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11);
nv_wr32(dev, 0x400040, 0xffffffff);
(void)nv_rd32(dev, 0x400040);
nv_wr32(dev, 0x400040, 0x00000000);
nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1);
if (nouveau_wait_for_idle(dev))
nv_wr32(dev, 0x40032c, inst | (1<<31));
nv_wr32(dev, 0x400500, fifo);
return 0;
}
int
nv50_graph_load_context(struct nouveau_channel *chan)
{
uint32_t inst = chan->ramin->instance >> 12;
NV_DEBUG(chan->dev, "ch%d\n", chan->id);
return nv50_graph_do_load_context(chan->dev, inst);
}
int
nv50_graph_unload_context(struct drm_device *dev)
{
uint32_t inst, fifo = nv_rd32(dev, 0x400500);
inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
return 0;
inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE;
nv_wr32(dev, 0x400500, fifo & ~1);
nv_wr32(dev, 0x400784, inst);
nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20);
nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01);
nouveau_wait_for_idle(dev);
nv_wr32(dev, 0x400500, fifo);
nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst);
return 0;
}
void
nv50_graph_context_switch(struct drm_device *dev)
{
uint32_t inst;
nv50_graph_unload_context(dev);
inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_NEXT);
inst &= NV50_PGRAPH_CTXCTL_NEXT_INSTANCE;
nv50_graph_do_load_context(dev, inst);
nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
NV40_PGRAPH_INTR_EN) | NV_PGRAPH_INTR_CONTEXT_SWITCH);
}
static int
nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
struct nouveau_gpuobj_ref *ref = NULL;
if (nouveau_gpuobj_ref_find(chan, data, &ref))
return -ENOENT;
if (nouveau_notifier_offset(ref->gpuobj, NULL))
return -EINVAL;
chan->nvsw.vblsem = ref->gpuobj;
chan->nvsw.vblsem_offset = ~0;
return 0;
}
static int
nv50_graph_nvsw_vblsem_offset(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
if (nouveau_notifier_offset(chan->nvsw.vblsem, &data))
return -ERANGE;
chan->nvsw.vblsem_offset = data >> 2;
return 0;
}
static int
nv50_graph_nvsw_vblsem_release_val(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
chan->nvsw.vblsem_rval = data;
return 0;
}
static int
nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass,
int mthd, uint32_t data)
{
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (!chan->nvsw.vblsem || chan->nvsw.vblsem_offset == ~0 || data > 1)
return -EINVAL;
if (!(nv_rd32(dev, NV50_PDISPLAY_INTR_EN) &
NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data))) {
nv_wr32(dev, NV50_PDISPLAY_INTR_1,
NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(data));
nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
NV50_PDISPLAY_INTR_EN) |
NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data));
}
list_add(&chan->nvsw.vbl_wait, &dev_priv->vbl_waiting);
return 0;
}
static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = {
{ 0x018c, nv50_graph_nvsw_dma_vblsem },
{ 0x0400, nv50_graph_nvsw_vblsem_offset },
{ 0x0404, nv50_graph_nvsw_vblsem_release_val },
{ 0x0408, nv50_graph_nvsw_vblsem_release },
{}
};
struct nouveau_pgraph_object_class nv50_graph_grclass[] = {
{ 0x506e, true, nv50_graph_nvsw_methods }, /* nvsw */
{ 0x0030, false, NULL }, /* null */
{ 0x5039, false, NULL }, /* m2mf */
{ 0x502d, false, NULL }, /* 2d */
{ 0x50c0, false, NULL }, /* compute */
{ 0x5097, false, NULL }, /* tesla (nv50) */
{ 0x8297, false, NULL }, /* tesla (nv80/nv90) */
{ 0x8397, false, NULL }, /* tesla (nva0) */
{ 0x8597, false, NULL }, /* tesla (nva8) */
{}
};

View File

@ -0,0 +1,509 @@
/*
* Copyright (C) 2007 Ben Skeggs.
*
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
struct nv50_instmem_priv {
uint32_t save1700[5]; /* 0x1700->0x1710 */
struct nouveau_gpuobj_ref *pramin_pt;
struct nouveau_gpuobj_ref *pramin_bar;
struct nouveau_gpuobj_ref *fb_bar;
bool last_access_wr;
};
#define NV50_INSTMEM_PAGE_SHIFT 12
#define NV50_INSTMEM_PAGE_SIZE (1 << NV50_INSTMEM_PAGE_SHIFT)
#define NV50_INSTMEM_PT_SIZE(a) (((a) >> 12) << 3)
/*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN
*/
#define BAR0_WI32(g, o, v) do { \
uint32_t offset; \
if ((g)->im_backing) { \
offset = (g)->im_backing_start; \
} else { \
offset = chan->ramin->gpuobj->im_backing_start; \
offset += (g)->im_pramin->start; \
} \
offset += (o); \
nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v)); \
} while (0)
int
nv50_instmem_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan;
uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size;
struct nv50_instmem_priv *priv;
int ret, i;
uint32_t v, save_nv001700;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
dev_priv->engine.instmem.priv = priv;
/* Save state, will restore at takedown. */
for (i = 0x1700; i <= 0x1710; i += 4)
priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
/* Reserve the last MiB of VRAM, we should probably try to avoid
* setting up the below tables over the top of the VBIOS image at
* some point.
*/
dev_priv->ramin_rsvd_vram = 1 << 20;
c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram;
c_size = 128 << 10;
c_vmpd = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200;
c_ramfc = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20;
c_base = c_vmpd + 0x4000;
pt_size = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size);
NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset);
NV_DEBUG(dev, " VBIOS image: 0x%08x\n",
(nv_rd32(dev, 0x619f04) & ~0xff) << 8);
NV_DEBUG(dev, " Aperture size: %d MiB\n", dev_priv->ramin_size >> 20);
NV_DEBUG(dev, " PT size: %d KiB\n", pt_size >> 10);
/* Determine VM layout, we need to do this first to make sure
* we allocate enough memory for all the page tables.
*/
dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
dev_priv->vm_gart_size = NV50_VM_BLOCK;
dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev);
if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
dev_priv->vm_gart_base,
dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
dev_priv->vm_vram_base,
dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8);
/* Map BAR0 PRAMIN aperture over the memory we want to use */
save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN);
nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16));
/* Create a fake channel, and use it as our "dummy" channels 0/127.
* The main reason for creating a channel is so we can use the gpuobj
* code. However, it's probably worth noting that NVIDIA also setup
* their channels 0/127 with the same values they configure here.
* So, there may be some other reason for doing this.
*
* Have to create the entire channel manually, as the real channel
* creation code assumes we have PRAMIN access, and we don't until
* we're done here.
*/
chan = kzalloc(sizeof(*chan), GFP_KERNEL);
if (!chan)
return -ENOMEM;
chan->id = 0;
chan->dev = dev;
chan->file_priv = (struct drm_file *)-2;
dev_priv->fifos[0] = dev_priv->fifos[127] = chan;
/* Channel's PRAMIN object + heap */
ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0,
NULL, &chan->ramin);
if (ret)
return ret;
if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base))
return -ENOMEM;
/* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc,
0x4000, 0, NULL, &chan->ramfc);
if (ret)
return ret;
for (i = 0; i < c_vmpd; i += 4)
BAR0_WI32(chan->ramin->gpuobj, i, 0);
/* VM page directory */
ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd,
0x4000, 0, &chan->vm_pd, NULL);
if (ret)
return ret;
for (i = 0; i < 0x4000; i += 8) {
BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000);
BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000);
}
/* PRAMIN page table, cheat and map into VM at 0x0000000000.
* We map the entire fake channel into the start of the PRAMIN BAR
*/
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
0, &priv->pramin_pt);
if (ret)
return ret;
for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
if (v < (c_offset + c_size))
BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
else
BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
}
BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
BAR0_WI32(chan->vm_pd, 0x04, 0x00000000);
/* VRAM page table(s), mapped into VM at +1GiB */
for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0,
NV50_VM_BLOCK/65536*8, 0, 0,
&chan->vm_vram_pt[i]);
if (ret) {
NV_ERROR(dev, "Error creating VRAM page tables: %d\n",
ret);
dev_priv->vm_vram_pt_nr = i;
return ret;
}
dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]->gpuobj;
for (v = 0; v < dev_priv->vm_vram_pt[i]->im_pramin->size;
v += 4)
BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0);
BAR0_WI32(chan->vm_pd, 0x10 + (i*8),
chan->vm_vram_pt[i]->instance | 0x61);
BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0);
}
/* DMA object for PRAMIN BAR */
ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
&priv->pramin_bar);
if (ret)
return ret;
BAR0_WI32(priv->pramin_bar->gpuobj, 0x00, 0x7fc00000);
BAR0_WI32(priv->pramin_bar->gpuobj, 0x04, dev_priv->ramin_size - 1);
BAR0_WI32(priv->pramin_bar->gpuobj, 0x08, 0x00000000);
BAR0_WI32(priv->pramin_bar->gpuobj, 0x0c, 0x00000000);
BAR0_WI32(priv->pramin_bar->gpuobj, 0x10, 0x00000000);
BAR0_WI32(priv->pramin_bar->gpuobj, 0x14, 0x00000000);
/* DMA object for FB BAR */
ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
&priv->fb_bar);
if (ret)
return ret;
BAR0_WI32(priv->fb_bar->gpuobj, 0x00, 0x7fc00000);
BAR0_WI32(priv->fb_bar->gpuobj, 0x04, 0x40000000 +
drm_get_resource_len(dev, 1) - 1);
BAR0_WI32(priv->fb_bar->gpuobj, 0x08, 0x40000000);
BAR0_WI32(priv->fb_bar->gpuobj, 0x0c, 0x00000000);
BAR0_WI32(priv->fb_bar->gpuobj, 0x10, 0x00000000);
BAR0_WI32(priv->fb_bar->gpuobj, 0x14, 0x00000000);
/* Poke the relevant regs, and pray it works :) */
nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
nv_wr32(dev, NV50_PUNK_UNK1710, 0);
nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
NV50_PUNK_BAR_CFG_BASE_VALID);
nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
NV50_PUNK_BAR1_CTXDMA_VALID);
nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
NV50_PUNK_BAR3_CTXDMA_VALID);
for (i = 0; i < 8; i++)
nv_wr32(dev, 0x1900 + (i*4), 0);
/* Assume that praying isn't enough, check that we can re-read the
* entire fake channel back from the PRAMIN BAR */
dev_priv->engine.instmem.prepare_access(dev, false);
for (i = 0; i < c_size; i += 4) {
if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
i);
dev_priv->engine.instmem.finish_access(dev);
return -EINVAL;
}
}
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
/* Global PRAMIN heap */
if (nouveau_mem_init_heap(&dev_priv->ramin_heap,
c_size, dev_priv->ramin_size - c_size)) {
dev_priv->ramin_heap = NULL;
NV_ERROR(dev, "Failed to init RAMIN heap\n");
}
/*XXX: incorrect, but needed to make hash func "work" */
dev_priv->ramht_offset = 0x10000;
dev_priv->ramht_bits = 9;
dev_priv->ramht_size = (1 << dev_priv->ramht_bits);
return 0;
}
void
nv50_instmem_takedown(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
struct nouveau_channel *chan = dev_priv->fifos[0];
int i;
NV_DEBUG(dev, "\n");
if (!priv)
return;
/* Restore state from before init */
for (i = 0x1700; i <= 0x1710; i += 4)
nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]);
nouveau_gpuobj_ref_del(dev, &priv->fb_bar);
nouveau_gpuobj_ref_del(dev, &priv->pramin_bar);
nouveau_gpuobj_ref_del(dev, &priv->pramin_pt);
/* Destroy dummy channel */
if (chan) {
for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
dev_priv->vm_vram_pt[i] = NULL;
}
dev_priv->vm_vram_pt_nr = 0;
nouveau_gpuobj_del(dev, &chan->vm_pd);
nouveau_gpuobj_ref_del(dev, &chan->ramfc);
nouveau_gpuobj_ref_del(dev, &chan->ramin);
nouveau_mem_takedown(&chan->ramin_heap);
dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
kfree(chan);
}
dev_priv->engine.instmem.priv = NULL;
kfree(priv);
}
int
nv50_instmem_suspend(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *chan = dev_priv->fifos[0];
struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
int i;
ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size);
if (!ramin->im_backing_suspend)
return -ENOMEM;
for (i = 0; i < ramin->im_pramin->size; i += 4)
ramin->im_backing_suspend[i/4] = nv_ri32(dev, i);
return 0;
}
void
nv50_instmem_resume(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
struct nouveau_channel *chan = dev_priv->fifos[0];
struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
int i;
nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->im_backing_start >> 16));
for (i = 0; i < ramin->im_pramin->size; i += 4)
BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]);
vfree(ramin->im_backing_suspend);
ramin->im_backing_suspend = NULL;
/* Poke the relevant regs, and pray it works :) */
nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
nv_wr32(dev, NV50_PUNK_UNK1710, 0);
nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
NV50_PUNK_BAR_CFG_BASE_VALID);
nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
NV50_PUNK_BAR1_CTXDMA_VALID);
nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
NV50_PUNK_BAR3_CTXDMA_VALID);
for (i = 0; i < 8; i++)
nv_wr32(dev, 0x1900 + (i*4), 0);
}
int
nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
uint32_t *sz)
{
int ret;
if (gpuobj->im_backing)
return -EINVAL;
*sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1);
if (*sz == 0)
return -EINVAL;
ret = nouveau_bo_new(dev, NULL, *sz, 0, TTM_PL_FLAG_VRAM, 0, 0x0000,
true, false, &gpuobj->im_backing);
if (ret) {
NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
return ret;
}
ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM);
if (ret) {
NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret);
nouveau_bo_ref(NULL, &gpuobj->im_backing);
return ret;
}
gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
gpuobj->im_backing_start <<= PAGE_SHIFT;
return 0;
}
void
nv50_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (gpuobj && gpuobj->im_backing) {
if (gpuobj->im_bound)
dev_priv->engine.instmem.unbind(dev, gpuobj);
nouveau_bo_unpin(gpuobj->im_backing);
nouveau_bo_ref(NULL, &gpuobj->im_backing);
gpuobj->im_backing = NULL;
}
}
int
nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
uint32_t pte, pte_end, vram;
if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
return -EINVAL;
NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
gpuobj->im_pramin->start, gpuobj->im_pramin->size);
pte = (gpuobj->im_pramin->start >> 12) << 3;
pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
vram = gpuobj->im_backing_start;
NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
gpuobj->im_pramin->start, pte, pte_end);
NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
dev_priv->engine.instmem.prepare_access(dev, true);
while (pte < pte_end) {
nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
pte += 8;
vram += NV50_INSTMEM_PAGE_SIZE;
}
dev_priv->engine.instmem.finish_access(dev);
nv_wr32(dev, 0x100c80, 0x00040001);
if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n");
NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
return -EBUSY;
}
nv_wr32(dev, 0x100c80, 0x00060001);
if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
return -EBUSY;
}
gpuobj->im_bound = 1;
return 0;
}
int
nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
uint32_t pte, pte_end;
if (gpuobj->im_bound == 0)
return -EINVAL;
pte = (gpuobj->im_pramin->start >> 12) << 3;
pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
dev_priv->engine.instmem.prepare_access(dev, true);
while (pte < pte_end) {
nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
pte += 8;
}
dev_priv->engine.instmem.finish_access(dev);
gpuobj->im_bound = 0;
return 0;
}
void
nv50_instmem_prepare_access(struct drm_device *dev, bool write)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
priv->last_access_wr = write;
}
void
nv50_instmem_finish_access(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
if (priv->last_access_wr) {
nv_wr32(dev, 0x070000, 0x00000001);
if (!nv_wait(0x070000, 0x00000001, 0x00000000))
NV_ERROR(dev, "PRAMIN flush timeout\n");
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright (C) 2007 Ben Skeggs.
* 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 "drmP.h"
#include "drm.h"
#include "nouveau_drv.h"
int
nv50_mc_init(struct drm_device *dev)
{
nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
return 0;
}
void nv50_mc_takedown(struct drm_device *dev)
{
}

View File

@ -0,0 +1,309 @@
/*
* 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 "drmP.h"
#include "drm_crtc_helper.h"
#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
#include "nouveau_reg.h"
#include "nouveau_drv.h"
#include "nouveau_dma.h"
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nv50_display.h"
static void
nv50_sor_disconnect(struct nouveau_encoder *nv_encoder)
{
struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
int ret;
NV_DEBUG(dev, "Disconnecting SOR %d\n", nv_encoder->or);
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while disconnecting SOR\n");
return;
}
BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
OUT_RING(evo, 0);
}
static void
nv50_sor_dp_link_train(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct bit_displayport_encoder_table *dpe;
int dpe_headerlen;
dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
if (!dpe) {
NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
return;
}
if (dpe->script0) {
NV_DEBUG(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
nv_encoder->dcb);
}
if (!nouveau_dp_link_train(encoder))
NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or);
if (dpe->script1) {
NV_DEBUG(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
nv_encoder->dcb);
}
}
static void
nv50_sor_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
uint32_t val;
int or = nv_encoder->or;
NV_DEBUG(dev, "or %d mode %d\n", or, mode);
/* wait for it to be done */
if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or);
NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or,
nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)));
}
val = nv_rd32(dev, 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(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val |
NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING);
if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or),
NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or);
NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or,
nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or)));
}
if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON)
nv50_sor_dp_link_train(encoder);
}
static void
nv50_sor_save(struct drm_encoder *encoder)
{
NV_ERROR(encoder->dev, "!!\n");
}
static void
nv50_sor_restore(struct drm_encoder *encoder)
{
NV_ERROR(encoder->dev, "!!\n");
}
static bool
nv50_sor_mode_fixup(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_connector *connector;
NV_DEBUG(encoder->dev, "or %d\n", nv_encoder->or);
connector = nouveau_encoder_connector_get(nv_encoder);
if (!connector) {
NV_ERROR(encoder->dev, "Encoder has no connector\n");
return false;
}
if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
connector->native_mode) {
int id = adjusted_mode->base.id;
*adjusted_mode = *connector->native_mode;
adjusted_mode->base.id = id;
}
return true;
}
static void
nv50_sor_prepare(struct drm_encoder *encoder)
{
}
static void
nv50_sor_commit(struct drm_encoder *encoder)
{
}
static void
nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
{
struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
struct nouveau_channel *evo = dev_priv->evo;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
uint32_t mode_ctl = 0;
int ret;
NV_DEBUG(dev, "or %d\n", nv_encoder->or);
nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
switch (nv_encoder->dcb->type) {
case OUTPUT_TMDS:
if (nv_encoder->dcb->sorconf.link & 1) {
if (adjusted_mode->clock < 165000)
mode_ctl = 0x0100;
else
mode_ctl = 0x0500;
} else
mode_ctl = 0x0200;
break;
case OUTPUT_DP:
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 (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while connecting SOR\n");
return;
}
BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
OUT_RING(evo, mode_ctl);
}
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,
.detect = NULL
};
static void
nv50_sor_destroy(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
if (!encoder)
return;
NV_DEBUG(encoder->dev, "\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_device *dev, struct dcb_entry *entry)
{
struct nouveau_encoder *nv_encoder = NULL;
struct drm_encoder *encoder;
bool dum;
int type;
NV_DEBUG(dev, "\n");
switch (entry->type) {
case OUTPUT_TMDS:
NV_INFO(dev, "Detected a TMDS output\n");
type = DRM_MODE_ENCODER_TMDS;
break;
case OUTPUT_LVDS:
NV_INFO(dev, "Detected a LVDS output\n");
type = DRM_MODE_ENCODER_LVDS;
if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
NV_ERROR(dev, "Failed parsing LVDS table\n");
return -EINVAL;
}
break;
case OUTPUT_DP:
NV_INFO(dev, "Detected a DP output\n");
type = DRM_MODE_ENCODER_TMDS;
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->disconnect = nv50_sor_disconnect;
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;
return 0;
}

View File

@ -0,0 +1,535 @@
/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */
/*
* Copyright 1996-1997 David J. McKay
*
* 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
* DAVID J. MCKAY 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.
*/
/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */
#ifndef __NVREG_H_
#define __NVREG_H_
#define NV_PMC_OFFSET 0x00000000
#define NV_PMC_SIZE 0x00001000
#define NV_PBUS_OFFSET 0x00001000
#define NV_PBUS_SIZE 0x00001000
#define NV_PFIFO_OFFSET 0x00002000
#define NV_PFIFO_SIZE 0x00002000
#define NV_HDIAG_OFFSET 0x00005000
#define NV_HDIAG_SIZE 0x00001000
#define NV_PRAM_OFFSET 0x00006000
#define NV_PRAM_SIZE 0x00001000
#define NV_PVIDEO_OFFSET 0x00008000
#define NV_PVIDEO_SIZE 0x00001000
#define NV_PTIMER_OFFSET 0x00009000
#define NV_PTIMER_SIZE 0x00001000
#define NV_PPM_OFFSET 0x0000A000
#define NV_PPM_SIZE 0x00001000
#define NV_PTV_OFFSET 0x0000D000
#define NV_PTV_SIZE 0x00001000
#define NV_PRMVGA_OFFSET 0x000A0000
#define NV_PRMVGA_SIZE 0x00020000
#define NV_PRMVIO0_OFFSET 0x000C0000
#define NV_PRMVIO_SIZE 0x00002000
#define NV_PRMVIO1_OFFSET 0x000C2000
#define NV_PFB_OFFSET 0x00100000
#define NV_PFB_SIZE 0x00001000
#define NV_PEXTDEV_OFFSET 0x00101000
#define NV_PEXTDEV_SIZE 0x00001000
#define NV_PME_OFFSET 0x00200000
#define NV_PME_SIZE 0x00001000
#define NV_PROM_OFFSET 0x00300000
#define NV_PROM_SIZE 0x00010000
#define NV_PGRAPH_OFFSET 0x00400000
#define NV_PGRAPH_SIZE 0x00010000
#define NV_PCRTC0_OFFSET 0x00600000
#define NV_PCRTC0_SIZE 0x00002000 /* empirical */
#define NV_PRMCIO0_OFFSET 0x00601000
#define NV_PRMCIO_SIZE 0x00002000
#define NV_PRMCIO1_OFFSET 0x00603000
#define NV50_DISPLAY_OFFSET 0x00610000
#define NV50_DISPLAY_SIZE 0x0000FFFF
#define NV_PRAMDAC0_OFFSET 0x00680000
#define NV_PRAMDAC0_SIZE 0x00002000
#define NV_PRMDIO0_OFFSET 0x00681000
#define NV_PRMDIO_SIZE 0x00002000
#define NV_PRMDIO1_OFFSET 0x00683000
#define NV_PRAMIN_OFFSET 0x00700000
#define NV_PRAMIN_SIZE 0x00100000
#define NV_FIFO_OFFSET 0x00800000
#define NV_FIFO_SIZE 0x00800000
#define NV_PMC_BOOT_0 0x00000000
#define NV_PMC_ENABLE 0x00000200
#define NV_VIO_VSE2 0x000003c3
#define NV_VIO_SRX 0x000003c4
#define NV_CIO_CRX__COLOR 0x000003d4
#define NV_CIO_CR__COLOR 0x000003d5
#define NV_PBUS_DEBUG_1 0x00001084
#define NV_PBUS_DEBUG_4 0x00001098
#define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0
#define NV_PBUS_POWERCTRL_1 0x00001584
#define NV_PBUS_POWERCTRL_2 0x00001588
#define NV_PBUS_POWERCTRL_4 0x00001590
#define NV_PBUS_PCI_NV_19 0x0000184C
#define NV_PBUS_PCI_NV_20 0x00001850
# define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
# define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
#define NV_PFIFO_RAMHT 0x00002210
#define NV_PTV_TV_INDEX 0x0000d220
#define NV_PTV_TV_DATA 0x0000d224
#define NV_PTV_HFILTER 0x0000d310
#define NV_PTV_HFILTER2 0x0000d390
#define NV_PTV_VFILTER 0x0000d510
#define NV_PRMVIO_MISC__WRITE 0x000c03c2
#define NV_PRMVIO_SRX 0x000c03c4
#define NV_PRMVIO_SR 0x000c03c5
# define NV_VIO_SR_RESET_INDEX 0x00
# define NV_VIO_SR_CLOCK_INDEX 0x01
# define NV_VIO_SR_PLANE_MASK_INDEX 0x02
# define NV_VIO_SR_CHAR_MAP_INDEX 0x03
# define NV_VIO_SR_MEM_MODE_INDEX 0x04
#define NV_PRMVIO_MISC__READ 0x000c03cc
#define NV_PRMVIO_GRX 0x000c03ce
#define NV_PRMVIO_GX 0x000c03cf
# define NV_VIO_GX_SR_INDEX 0x00
# define NV_VIO_GX_SREN_INDEX 0x01
# define NV_VIO_GX_CCOMP_INDEX 0x02
# define NV_VIO_GX_ROP_INDEX 0x03
# define NV_VIO_GX_READ_MAP_INDEX 0x04
# define NV_VIO_GX_MODE_INDEX 0x05
# define NV_VIO_GX_MISC_INDEX 0x06
# define NV_VIO_GX_DONT_CARE_INDEX 0x07
# define NV_VIO_GX_BIT_MASK_INDEX 0x08
#define NV_PFB_BOOT_0 0x00100000
#define NV_PFB_CFG0 0x00100200
#define NV_PFB_CFG1 0x00100204
#define NV_PFB_CSTATUS 0x0010020C
#define NV_PFB_REFCTRL 0x00100210
# define NV_PFB_REFCTRL_VALID_1 (1 << 31)
#define NV_PFB_PAD 0x0010021C
# define NV_PFB_PAD_CKE_NORMAL (1 << 0)
#define NV_PFB_TILE_NV10 0x00100240
#define NV_PFB_TILE_SIZE_NV10 0x00100244
#define NV_PFB_REF 0x001002D0
# define NV_PFB_REF_CMD_REFRESH (1 << 0)
#define NV_PFB_PRE 0x001002D4
# define NV_PFB_PRE_CMD_PRECHARGE (1 << 0)
#define NV_PFB_CLOSE_PAGE2 0x0010033C
#define NV_PFB_TILE_NV40 0x00100600
#define NV_PFB_TILE_SIZE_NV40 0x00100604
#define NV_PEXTDEV_BOOT_0 0x00101000
# define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12)
#define NV_PEXTDEV_BOOT_3 0x0010100c
#define NV_PCRTC_INTR_0 0x00600100
# define NV_PCRTC_INTR_0_VBLANK (1 << 0)
#define NV_PCRTC_INTR_EN_0 0x00600140
#define NV_PCRTC_START 0x00600800
#define NV_PCRTC_CONFIG 0x00600804
# define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0)
# define NV_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0)
#define NV_PCRTC_CURSOR_CONFIG 0x00600810
# define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0)
# define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4)
# define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8)
# define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12)
# define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16)
# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24)
# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24)
# define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28)
/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */
#define NV_PCRTC_GPIO 0x00600818
#define NV_PCRTC_GPIO_EXT 0x0060081c
#define NV_PCRTC_830 0x00600830
#define NV_PCRTC_834 0x00600834
#define NV_PCRTC_850 0x00600850
#define NV_PCRTC_ENGINE_CTRL 0x00600860
# define NV_CRTC_FSEL_I2C (1 << 4)
# define NV_CRTC_FSEL_OVERLAY (1 << 12)
#define NV_PRMCIO_ARX 0x006013c0
#define NV_PRMCIO_AR__WRITE 0x006013c0
#define NV_PRMCIO_AR__READ 0x006013c1
# define NV_CIO_AR_MODE_INDEX 0x10
# define NV_CIO_AR_OSCAN_INDEX 0x11
# define NV_CIO_AR_PLANE_INDEX 0x12
# define NV_CIO_AR_HPP_INDEX 0x13
# define NV_CIO_AR_CSEL_INDEX 0x14
#define NV_PRMCIO_INP0 0x006013c2
#define NV_PRMCIO_CRX__COLOR 0x006013d4
#define NV_PRMCIO_CR__COLOR 0x006013d5
/* Standard VGA CRTC registers */
# define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */
# define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */
# define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */
# define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */
# define NV_CIO_CR_HBE_4_0 4:0
# define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */
# define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */
# define NV_CIO_CR_HRE_4_0 4:0
# define NV_CIO_CR_HRE_HBE_5 7:7
# define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */
# define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */
# define NV_CIO_CR_OVL_VDT_8 0:0
# define NV_CIO_CR_OVL_VDE_8 1:1
# define NV_CIO_CR_OVL_VRS_8 2:2
# define NV_CIO_CR_OVL_VBS_8 3:3
# define NV_CIO_CR_OVL_VDT_9 5:5
# define NV_CIO_CR_OVL_VDE_9 6:6
# define NV_CIO_CR_OVL_VRS_9 7:7
# define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */
# define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */
# define NV_CIO_CR_CELL_HT_VBS_9 5:5
# define NV_CIO_CR_CELL_HT_SCANDBL 7:7
# define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */
# define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */
# define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */
# define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */
# define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */
# define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */
# define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */
# define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */
# define NV_CIO_CR_VRE_3_0 3:0
# define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */
# define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */
# define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */
# define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */
# define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */
# define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */
# define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */
/* Extended VGA CRTC registers */
# define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */
# define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5
# define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */
# define NV_CIO_CRE_RPC1_LARGE 2:2
# define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */
# define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */
# define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */
# define NV_CIO_SR_UNLOCK_RW_VALUE 0x57
# define NV_CIO_SR_LOCK_VALUE 0x99
# define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */
# define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */
# define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */
# define NV_CIO_CRE_LSR_VDT_10 0:0
# define NV_CIO_CRE_LSR_VDE_10 1:1
# define NV_CIO_CRE_LSR_VRS_10 2:2
# define NV_CIO_CRE_LSR_VBS_10 3:3
# define NV_CIO_CRE_LSR_HBE_6 4:4
# define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */
# define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */
# define NV_CIO_CRE_PIXEL_INDEX 0x28
# define NV_CIO_CRE_PIXEL_FORMAT 1:0
# define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */
# define NV_CIO_CRE_HEB_HDT_8 0:0
# define NV_CIO_CRE_HEB_HDE_8 1:1
# define NV_CIO_CRE_HEB_HBS_8 2:2
# define NV_CIO_CRE_HEB_HRS_8 3:3
# define NV_CIO_CRE_HEB_ILC_8 4:4
# define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */
# define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */
# define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */
# define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0
# define NV_CIO_CRE_HCUR_ASI 7:7
# define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */
# define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0
# define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1
# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2
# define NV_CIO_CRE_LCD__INDEX 0x33
# define NV_CIO_CRE_LCD_LCD_SELECT 0:0
# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36
# define NV_CIO_CRE_DDC0_WR__INDEX 0x37
# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */
# define NV_CIO_CRE_SCRATCH3__INDEX 0x3b
# define NV_CIO_CRE_SCRATCH4__INDEX 0x3c
# define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e
# define NV_CIO_CRE_DDC_WR__INDEX 0x3f
# define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */
# define NV_CIO_CRE_EBR_VDT_11 0:0
# define NV_CIO_CRE_EBR_VDE_11 2:2
# define NV_CIO_CRE_EBR_VRS_11 4:4
# define NV_CIO_CRE_EBR_VBS_11 6:6
# define NV_CIO_CRE_43 0x43
# define NV_CIO_CRE_44 0x44 /* head control */
# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */
# define NV_CIO_CRE_RCR 0x46
# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7
# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */
# define NV_CIO_CRE_49 0x49
# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */
# define NV_CIO_CRE_TVOUT_LATENCY 0x52
# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */
# define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */
# define NV_CIO_CRE_57 0x57 /* index reg for cr58 */
# define NV_CIO_CRE_58 0x58 /* data reg for cr57 */
# define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */
# define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */
# define NV_CIO_CRE_85 0x85
# define NV_CIO_CRE_86 0x86
#define NV_PRMCIO_INP0__COLOR 0x006013da
#define NV_PRAMDAC_CU_START_POS 0x00680300
# define NV_PRAMDAC_CU_START_POS_X 15:0
# define NV_PRAMDAC_CU_START_POS_Y 31:16
#define NV_RAMDAC_NV10_CURSYNC 0x00680404
#define NV_PRAMDAC_NVPLL_COEFF 0x00680500
#define NV_PRAMDAC_MPLL_COEFF 0x00680504
#define NV_PRAMDAC_VPLL_COEFF 0x00680508
# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4)
#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c
# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16)
# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16)
# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16)
# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16)
# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20)
# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28)
# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28)
#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510
#define NV_RAMDAC_VPLL2 0x00680520
#define NV_PRAMDAC_SEL_CLK 0x00680524
#define NV_RAMDAC_DITHER_NV11 0x00680528
#define NV_PRAMDAC_DACCLK 0x0068052c
# define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0)
#define NV_RAMDAC_NVPLL_B 0x00680570
#define NV_RAMDAC_MPLL_B 0x00680574
#define NV_RAMDAC_VPLL_B 0x00680578
#define NV_RAMDAC_VPLL2_B 0x0068057c
# define NV31_RAMDAC_ENABLE_VCO2 (8 << 28)
#define NV_PRAMDAC_580 0x00680580
# define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8)
# define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28)
#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600
# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4)
# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8)
# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12)
# define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16)
# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20)
# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28)
#define NV_PRAMDAC_TEST_CONTROL 0x00680608
# define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12)
# define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16)
# define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28)
#define NV_PRAMDAC_TESTPOINT_DATA 0x00680610
# define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28)
#define NV_PRAMDAC_630 0x00680630
#define NV_PRAMDAC_634 0x00680634
#define NV_PRAMDAC_TV_SETUP 0x00680700
#define NV_PRAMDAC_TV_VTOTAL 0x00680720
#define NV_PRAMDAC_TV_VSKEW 0x00680724
#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728
#define NV_PRAMDAC_TV_HTOTAL 0x0068072c
#define NV_PRAMDAC_TV_HSKEW 0x00680730
#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734
#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738
#define NV_PRAMDAC_TV_SETUP 0x00680700
#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800
#define NV_PRAMDAC_FP_VTOTAL 0x00680804
#define NV_PRAMDAC_FP_VCRTC 0x00680808
#define NV_PRAMDAC_FP_VSYNC_START 0x0068080c
#define NV_PRAMDAC_FP_VSYNC_END 0x00680810
#define NV_PRAMDAC_FP_VVALID_START 0x00680814
#define NV_PRAMDAC_FP_VVALID_END 0x00680818
#define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820
#define NV_PRAMDAC_FP_HTOTAL 0x00680824
#define NV_PRAMDAC_FP_HCRTC 0x00680828
#define NV_PRAMDAC_FP_HSYNC_START 0x0068082c
#define NV_PRAMDAC_FP_HSYNC_END 0x00680830
#define NV_PRAMDAC_FP_HVALID_START 0x00680834
#define NV_PRAMDAC_FP_HVALID_END 0x00680838
#define NV_RAMDAC_FP_DITHER 0x0068083c
#define NV_PRAMDAC_FP_TG_CONTROL 0x00680848
# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0)
# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0)
# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4)
# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4)
# define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8)
# define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8)
# define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8)
# define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20)
# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24)
# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28)
# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28)
#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c
#define NV_PRAMDAC_850 0x00680850
#define NV_PRAMDAC_85C 0x0068085c
#define NV_PRAMDAC_FP_DEBUG_0 0x00680880
# define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0)
# define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4)
/* This doesn't seem to be essential for tmds, but still often set */
# define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4)
# define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8)
# define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12)
# define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20)
# define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24)
# define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28)
#define NV_PRAMDAC_FP_DEBUG_1 0x00680884
# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0
# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12)
# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16
# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28)
#define NV_PRAMDAC_FP_DEBUG_2 0x00680888
#define NV_PRAMDAC_FP_DEBUG_3 0x0068088C
/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */
#define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0
# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16)
#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4
#define NV_PRAMDAC_8C0 0x006808c0
/* Some kind of switch */
#define NV_PRAMDAC_900 0x00680900
#define NV_PRAMDAC_A20 0x00680A20
#define NV_PRAMDAC_A24 0x00680A24
#define NV_PRAMDAC_A34 0x00680A34
#define NV_PRAMDAC_CTV 0x00680c00
/* names fabricated from NV_USER_DAC info */
#define NV_PRMDIO_PIXEL_MASK 0x006813c6
# define NV_PRMDIO_PIXEL_MASK_MASK 0xff
#define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7
#define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8
#define NV_PRMDIO_PALETTE_DATA 0x006813c9
#define NV_PGRAPH_DEBUG_0 0x00400080
#define NV_PGRAPH_DEBUG_1 0x00400084
#define NV_PGRAPH_DEBUG_2_NV04 0x00400088
#define NV_PGRAPH_DEBUG_2 0x00400620
#define NV_PGRAPH_DEBUG_3 0x0040008c
#define NV_PGRAPH_DEBUG_4 0x00400090
#define NV_PGRAPH_INTR 0x00400100
#define NV_PGRAPH_INTR_EN 0x00400140
#define NV_PGRAPH_CTX_CONTROL 0x00400144
#define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170
#define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C
#define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540
#define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544
#define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548
#define NV_PGRAPH_BETA_AND 0x00400608
#define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610
#define NV_PGRAPH_BOFFSET0 0x00400640
#define NV_PGRAPH_BOFFSET1 0x00400644
#define NV_PGRAPH_BOFFSET2 0x00400648
#define NV_PGRAPH_BLIMIT0 0x00400684
#define NV_PGRAPH_BLIMIT1 0x00400688
#define NV_PGRAPH_BLIMIT2 0x0040068c
#define NV_PGRAPH_STATUS 0x00400700
#define NV_PGRAPH_SURFACE 0x00400710
#define NV_PGRAPH_STATE 0x00400714
#define NV_PGRAPH_FIFO 0x00400720
#define NV_PGRAPH_PATTERN_SHAPE 0x00400810
#define NV_PGRAPH_TILE 0x00400b00
#define NV_PVIDEO_INTR_EN 0x00008140
#define NV_PVIDEO_BUFFER 0x00008700
#define NV_PVIDEO_STOP 0x00008704
#define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4)
#define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4)
#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4)
#define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4)
#define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4)
#define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4)
#define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4)
#define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4)
#define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4)
#define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4)
#define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4)
#define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4)
#define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4)
#define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4)
#define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4)
# define NV_PVIDEO_FORMAT_PLANAR (1 << 0)
# define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16)
# define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20)
# define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24)
#define NV_PVIDEO_COLOR_KEY 0x00008B00
/* NV04 overlay defines from VIDIX & Haiku */
#define NV_PVIDEO_INTR_EN_0 0x00680140
#define NV_PVIDEO_STEP_SIZE 0x00680200
#define NV_PVIDEO_CONTROL_Y 0x00680204
#define NV_PVIDEO_CONTROL_X 0x00680208
#define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c
#define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214
#define NV_PVIDEO_BUFF0_OFFSET 0x0068021c
#define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210
#define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218
#define NV_PVIDEO_BUFF1_OFFSET 0x00680220
#define NV_PVIDEO_OE_STATE 0x00680224
#define NV_PVIDEO_SU_STATE 0x00680228
#define NV_PVIDEO_RM_STATE 0x0068022c
#define NV_PVIDEO_WINDOW_START 0x00680230
#define NV_PVIDEO_WINDOW_SIZE 0x00680234
#define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238
#define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c
#define NV_PVIDEO_KEY 0x00680240
#define NV_PVIDEO_OVERLAY 0x00680244
#define NV_PVIDEO_RED_CSC_OFFSET 0x00680280
#define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284
#define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288
#define NV_PVIDEO_CSC_ADJUST 0x0068028c
#endif

View File

@ -103,6 +103,8 @@ source "drivers/staging/line6/Kconfig"
source "drivers/gpu/drm/radeon/Kconfig"
source "drivers/gpu/drm/nouveau/Kconfig"
source "drivers/staging/octeon/Kconfig"
source "drivers/staging/serqt_usb2/Kconfig"

View File

@ -8,3 +8,4 @@ unifdef-y += radeon_drm.h
unifdef-y += sis_drm.h
unifdef-y += savage_drm.h
unifdef-y += via_drm.h
unifdef-y += nouveau_drm.h

86
include/drm/i2c/ch7006.h Normal file
View File

@ -0,0 +1,86 @@
/*
* Copyright (C) 2009 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.
*
*/
#ifndef __DRM_I2C_CH7006_H__
#define __DRM_I2C_CH7006_H__
/**
* struct ch7006_encoder_params
*
* Describes how the ch7006 is wired up with the GPU. It should be
* used as the @params parameter of its @set_config method.
*
* See "http://www.chrontel.com/pdf/7006.pdf" for their precise
* meaning.
*/
struct ch7006_encoder_params {
enum {
CH7006_FORMAT_RGB16 = 0,
CH7006_FORMAT_YCrCb24m16,
CH7006_FORMAT_RGB24m16,
CH7006_FORMAT_RGB15,
CH7006_FORMAT_RGB24m12C,
CH7006_FORMAT_RGB24m12I,
CH7006_FORMAT_RGB24m8,
CH7006_FORMAT_RGB16m8,
CH7006_FORMAT_RGB15m8,
CH7006_FORMAT_YCrCb24m8,
} input_format;
enum {
CH7006_CLOCK_SLAVE = 0,
CH7006_CLOCK_MASTER,
} clock_mode;
enum {
CH7006_CLOCK_EDGE_NEG = 0,
CH7006_CLOCK_EDGE_POS,
} clock_edge;
int xcm, pcm;
enum {
CH7006_SYNC_SLAVE = 0,
CH7006_SYNC_MASTER,
} sync_direction;
enum {
CH7006_SYNC_SEPARATED = 0,
CH7006_SYNC_EMBEDDED,
} sync_encoding;
enum {
CH7006_POUT_1_8V = 0,
CH7006_POUT_3_3V,
} pout_level;
enum {
CH7006_ACTIVE_HSYNC = 0,
CH7006_ACTIVE_DSTART,
} active_detect;
};
#endif

220
include/drm/nouveau_drm.h Normal file
View File

@ -0,0 +1,220 @@
/*
* Copyright 2005 Stephane Marchesin.
* 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
* VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
#ifndef __NOUVEAU_DRM_H__
#define __NOUVEAU_DRM_H__
#define NOUVEAU_DRM_HEADER_PATCHLEVEL 15
struct drm_nouveau_channel_alloc {
uint32_t fb_ctxdma_handle;
uint32_t tt_ctxdma_handle;
int channel;
/* Notifier memory */
uint32_t notifier_handle;
/* DRM-enforced subchannel assignments */
struct {
uint32_t handle;
uint32_t grclass;
} subchan[8];
uint32_t nr_subchan;
};
struct drm_nouveau_channel_free {
int channel;
};
struct drm_nouveau_grobj_alloc {
int channel;
uint32_t handle;
int class;
};
struct drm_nouveau_notifierobj_alloc {
uint32_t channel;
uint32_t handle;
uint32_t size;
uint32_t offset;
};
struct drm_nouveau_gpuobj_free {
int channel;
uint32_t handle;
};
/* FIXME : maybe unify {GET,SET}PARAMs */
#define NOUVEAU_GETPARAM_PCI_VENDOR 3
#define NOUVEAU_GETPARAM_PCI_DEVICE 4
#define NOUVEAU_GETPARAM_BUS_TYPE 5
#define NOUVEAU_GETPARAM_FB_PHYSICAL 6
#define NOUVEAU_GETPARAM_AGP_PHYSICAL 7
#define NOUVEAU_GETPARAM_FB_SIZE 8
#define NOUVEAU_GETPARAM_AGP_SIZE 9
#define NOUVEAU_GETPARAM_PCI_PHYSICAL 10
#define NOUVEAU_GETPARAM_CHIPSET_ID 11
#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12
struct drm_nouveau_getparam {
uint64_t param;
uint64_t value;
};
struct drm_nouveau_setparam {
uint64_t param;
uint64_t value;
};
#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0)
#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1)
#define NOUVEAU_GEM_DOMAIN_GART (1 << 2)
#define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3)
struct drm_nouveau_gem_info {
uint32_t handle;
uint32_t domain;
uint64_t size;
uint64_t offset;
uint64_t map_handle;
uint32_t tile_mode;
uint32_t tile_flags;
};
struct drm_nouveau_gem_new {
struct drm_nouveau_gem_info info;
uint32_t channel_hint;
uint32_t align;
};
struct drm_nouveau_gem_pushbuf_bo {
uint64_t user_priv;
uint32_t handle;
uint32_t read_domains;
uint32_t write_domains;
uint32_t valid_domains;
uint32_t presumed_ok;
uint32_t presumed_domain;
uint64_t presumed_offset;
};
#define NOUVEAU_GEM_RELOC_LOW (1 << 0)
#define NOUVEAU_GEM_RELOC_HIGH (1 << 1)
#define NOUVEAU_GEM_RELOC_OR (1 << 2)
struct drm_nouveau_gem_pushbuf_reloc {
uint32_t bo_index;
uint32_t reloc_index;
uint32_t flags;
uint32_t data;
uint32_t vor;
uint32_t tor;
};
#define NOUVEAU_GEM_MAX_BUFFERS 1024
#define NOUVEAU_GEM_MAX_RELOCS 1024
struct drm_nouveau_gem_pushbuf {
uint32_t channel;
uint32_t nr_dwords;
uint32_t nr_buffers;
uint32_t nr_relocs;
uint64_t dwords;
uint64_t buffers;
uint64_t relocs;
};
struct drm_nouveau_gem_pushbuf_call {
uint32_t channel;
uint32_t handle;
uint32_t offset;
uint32_t nr_buffers;
uint32_t nr_relocs;
uint32_t nr_dwords;
uint64_t buffers;
uint64_t relocs;
uint32_t suffix0;
uint32_t suffix1;
/* below only accessed for CALL2 */
uint64_t vram_available;
uint64_t gart_available;
};
struct drm_nouveau_gem_pin {
uint32_t handle;
uint32_t domain;
uint64_t offset;
};
struct drm_nouveau_gem_unpin {
uint32_t handle;
};
#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001
#define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002
#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004
struct drm_nouveau_gem_cpu_prep {
uint32_t handle;
uint32_t flags;
};
struct drm_nouveau_gem_cpu_fini {
uint32_t handle;
};
struct drm_nouveau_gem_tile {
uint32_t handle;
uint32_t offset;
uint32_t size;
uint32_t tile_mode;
uint32_t tile_flags;
};
enum nouveau_bus_type {
NV_AGP = 0,
NV_PCI = 1,
NV_PCIE = 2,
};
struct drm_nouveau_sarea {
};
#define DRM_NOUVEAU_CARD_INIT 0x00
#define DRM_NOUVEAU_GETPARAM 0x01
#define DRM_NOUVEAU_SETPARAM 0x02
#define DRM_NOUVEAU_CHANNEL_ALLOC 0x03
#define DRM_NOUVEAU_CHANNEL_FREE 0x04
#define DRM_NOUVEAU_GROBJ_ALLOC 0x05
#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x06
#define DRM_NOUVEAU_GPUOBJ_FREE 0x07
#define DRM_NOUVEAU_GEM_NEW 0x40
#define DRM_NOUVEAU_GEM_PUSHBUF 0x41
#define DRM_NOUVEAU_GEM_PUSHBUF_CALL 0x42
#define DRM_NOUVEAU_GEM_PIN 0x43 /* !KMS only */
#define DRM_NOUVEAU_GEM_UNPIN 0x44 /* !KMS only */
#define DRM_NOUVEAU_GEM_CPU_PREP 0x45
#define DRM_NOUVEAU_GEM_CPU_FINI 0x46
#define DRM_NOUVEAU_GEM_INFO 0x47
#define DRM_NOUVEAU_GEM_PUSHBUF_CALL2 0x48
#endif /* __NOUVEAU_DRM_H__ */