mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-11-24 14:10:49 +07:00
Merge branch 'drm-nouveau-pony' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6
* 'drm-nouveau-pony' of git://git.kernel.org/pub/scm/linux/kernel/git/airlied/drm-2.6: drm/nouveau: Add DRM driver for NVIDIA GPUs
This commit is contained in:
commit
9764757932
@ -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/
|
||||
|
4
drivers/gpu/drm/i2c/Makefile
Normal file
4
drivers/gpu/drm/i2c/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
ccflags-y := -Iinclude/drm
|
||||
|
||||
ch7006-y := ch7006_drv.o ch7006_mode.o
|
||||
obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
|
531
drivers/gpu/drm/i2c/ch7006_drv.c
Normal file
531
drivers/gpu/drm/i2c/ch7006_drv.c
Normal 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);
|
473
drivers/gpu/drm/i2c/ch7006_mode.c
Normal file
473
drivers/gpu/drm/i2c/ch7006_mode.c
Normal 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;
|
||||
}
|
344
drivers/gpu/drm/i2c/ch7006_priv.h
Normal file
344
drivers/gpu/drm/i2c/ch7006_priv.h
Normal 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
|
44
drivers/gpu/drm/nouveau/Kconfig
Normal file
44
drivers/gpu/drm/nouveau/Kconfig
Normal 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
|
31
drivers/gpu/drm/nouveau/Makefile
Normal file
31
drivers/gpu/drm/nouveau/Makefile
Normal 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
|
125
drivers/gpu/drm/nouveau/nouveau_acpi.c
Normal file
125
drivers/gpu/drm/nouveau/nouveau_acpi.c
Normal 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;
|
||||
}
|
155
drivers/gpu/drm/nouveau/nouveau_backlight.c
Normal file
155
drivers/gpu/drm/nouveau/nouveau_backlight.c
Normal 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;
|
||||
}
|
||||
}
|
6095
drivers/gpu/drm/nouveau/nouveau_bios.c
Normal file
6095
drivers/gpu/drm/nouveau/nouveau_bios.c
Normal file
File diff suppressed because it is too large
Load Diff
289
drivers/gpu/drm/nouveau/nouveau_bios.h
Normal file
289
drivers/gpu/drm/nouveau/nouveau_bios.h
Normal 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
|
671
drivers/gpu/drm/nouveau/nouveau_bo.c
Normal file
671
drivers/gpu/drm/nouveau/nouveau_bo.c
Normal 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,
|
||||
};
|
||||
|
478
drivers/gpu/drm/nouveau/nouveau_calc.c
Normal file
478
drivers/gpu/drm/nouveau/nouveau_calc.c
Normal 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;
|
||||
}
|
468
drivers/gpu/drm/nouveau/nouveau_channel.c
Normal file
468
drivers/gpu/drm/nouveau/nouveau_channel.c
Normal 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);
|
824
drivers/gpu/drm/nouveau/nouveau_connector.c
Normal file
824
drivers/gpu/drm/nouveau/nouveau_connector.c
Normal 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;
|
||||
}
|
54
drivers/gpu/drm/nouveau/nouveau_connector.h
Normal file
54
drivers/gpu/drm/nouveau/nouveau_connector.h
Normal 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__ */
|
95
drivers/gpu/drm/nouveau/nouveau_crtc.h
Normal file
95
drivers/gpu/drm/nouveau/nouveau_crtc.h
Normal 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__ */
|
155
drivers/gpu/drm/nouveau/nouveau_debugfs.c
Normal file
155
drivers/gpu/drm/nouveau/nouveau_debugfs.c
Normal 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);
|
||||
}
|
115
drivers/gpu/drm/nouveau/nouveau_display.c
Normal file
115
drivers/gpu/drm/nouveau/nouveau_display.c
Normal 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,
|
||||
};
|
||||
|
206
drivers/gpu/drm/nouveau/nouveau_dma.c
Normal file
206
drivers/gpu/drm/nouveau/nouveau_dma.c
Normal 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;
|
||||
}
|
||||
|
157
drivers/gpu/drm/nouveau/nouveau_dma.h
Normal file
157
drivers/gpu/drm/nouveau/nouveau_dma.h
Normal 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
|
569
drivers/gpu/drm/nouveau/nouveau_dp.c
Normal file
569
drivers/gpu/drm/nouveau/nouveau_dp.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
405
drivers/gpu/drm/nouveau/nouveau_drv.c
Normal file
405
drivers/gpu/drm/nouveau/nouveau_drv.c
Normal 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");
|
1286
drivers/gpu/drm/nouveau/nouveau_drv.h
Normal file
1286
drivers/gpu/drm/nouveau/nouveau_drv.h
Normal file
File diff suppressed because it is too large
Load Diff
91
drivers/gpu/drm/nouveau/nouveau_encoder.h
Normal file
91
drivers/gpu/drm/nouveau/nouveau_encoder.h
Normal 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__ */
|
47
drivers/gpu/drm/nouveau/nouveau_fb.h
Normal file
47
drivers/gpu/drm/nouveau/nouveau_fb.h
Normal 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__ */
|
380
drivers/gpu/drm/nouveau/nouveau_fbcon.c
Normal file
380
drivers/gpu/drm/nouveau/nouveau_fbcon.c
Normal 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;
|
||||
}
|
47
drivers/gpu/drm/nouveau/nouveau_fbcon.h
Normal file
47
drivers/gpu/drm/nouveau/nouveau_fbcon.h
Normal 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__ */
|
||||
|
262
drivers/gpu/drm/nouveau/nouveau_fence.c
Normal file
262
drivers/gpu/drm/nouveau/nouveau_fence.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
992
drivers/gpu/drm/nouveau/nouveau_gem.c
Normal file
992
drivers/gpu/drm/nouveau/nouveau_gem.c
Normal 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;
|
||||
}
|
||||
|
1080
drivers/gpu/drm/nouveau/nouveau_hw.c
Normal file
1080
drivers/gpu/drm/nouveau/nouveau_hw.c
Normal file
File diff suppressed because it is too large
Load Diff
455
drivers/gpu/drm/nouveau/nouveau_hw.h
Normal file
455
drivers/gpu/drm/nouveau/nouveau_hw.h
Normal 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__ */
|
269
drivers/gpu/drm/nouveau/nouveau_i2c.c
Normal file
269
drivers/gpu/drm/nouveau/nouveau_i2c.c
Normal 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;
|
||||
}
|
||||
|
52
drivers/gpu/drm/nouveau/nouveau_i2c.h
Normal file
52
drivers/gpu/drm/nouveau/nouveau_i2c.h
Normal 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__ */
|
72
drivers/gpu/drm/nouveau/nouveau_ioc32.c
Normal file
72
drivers/gpu/drm/nouveau/nouveau_ioc32.c
Normal 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;
|
||||
}
|
702
drivers/gpu/drm/nouveau/nouveau_irq.c
Normal file
702
drivers/gpu/drm/nouveau/nouveau_irq.c
Normal 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;
|
||||
}
|
568
drivers/gpu/drm/nouveau/nouveau_mem.c
Normal file
568
drivers/gpu/drm/nouveau/nouveau_mem.c
Normal 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;
|
||||
}
|
||||
|
||||
|
196
drivers/gpu/drm/nouveau/nouveau_notifier.c
Normal file
196
drivers/gpu/drm/nouveau/nouveau_notifier.c
Normal 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;
|
||||
}
|
1294
drivers/gpu/drm/nouveau/nouveau_object.c
Normal file
1294
drivers/gpu/drm/nouveau/nouveau_object.c
Normal file
File diff suppressed because it is too large
Load Diff
836
drivers/gpu/drm/nouveau/nouveau_reg.h
Normal file
836
drivers/gpu/drm/nouveau/nouveau_reg.h
Normal 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)
|
321
drivers/gpu/drm/nouveau/nouveau_sgdma.c
Normal file
321
drivers/gpu/drm/nouveau/nouveau_sgdma.c
Normal 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;
|
||||
}
|
811
drivers/gpu/drm/nouveau/nouveau_state.c
Normal file
811
drivers/gpu/drm/nouveau/nouveau_state.c
Normal 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;
|
||||
}
|
||||
|
131
drivers/gpu/drm/nouveau/nouveau_ttm.c
Normal file
131
drivers/gpu/drm/nouveau/nouveau_ttm.c
Normal 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;
|
||||
}
|
||||
|
1002
drivers/gpu/drm/nouveau/nv04_crtc.c
Normal file
1002
drivers/gpu/drm/nouveau/nv04_crtc.c
Normal file
File diff suppressed because it is too large
Load Diff
70
drivers/gpu/drm/nouveau/nv04_cursor.c
Normal file
70
drivers/gpu/drm/nouveau/nv04_cursor.c
Normal 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;
|
||||
}
|
||||
|
528
drivers/gpu/drm/nouveau/nv04_dac.c
Normal file
528
drivers/gpu/drm/nouveau/nv04_dac.c
Normal 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;
|
||||
}
|
621
drivers/gpu/drm/nouveau/nv04_dfp.c
Normal file
621
drivers/gpu/drm/nouveau/nv04_dfp.c
Normal 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;
|
||||
}
|
288
drivers/gpu/drm/nouveau/nv04_display.c
Normal file
288
drivers/gpu/drm/nouveau/nv04_display.c
Normal 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);
|
||||
}
|
||||
|
21
drivers/gpu/drm/nouveau/nv04_fb.c
Normal file
21
drivers/gpu/drm/nouveau/nv04_fb.c
Normal 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)
|
||||
{
|
||||
}
|
316
drivers/gpu/drm/nouveau/nv04_fbcon.c
Normal file
316
drivers/gpu/drm/nouveau/nv04_fbcon.c
Normal 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;
|
||||
}
|
||||
|
271
drivers/gpu/drm/nouveau/nv04_fifo.c
Normal file
271
drivers/gpu/drm/nouveau/nv04_fifo.c
Normal 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;
|
||||
}
|
||||
|
579
drivers/gpu/drm/nouveau/nv04_graph.c
Normal file
579
drivers/gpu/drm/nouveau/nv04_graph.c
Normal 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 */
|
||||
{}
|
||||
};
|
||||
|
208
drivers/gpu/drm/nouveau/nv04_instmem.c
Normal file
208
drivers/gpu/drm/nouveau/nv04_instmem.c
Normal 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)
|
||||
{
|
||||
}
|
||||
|
20
drivers/gpu/drm/nouveau/nv04_mc.c
Normal file
20
drivers/gpu/drm/nouveau/nv04_mc.c
Normal 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)
|
||||
{
|
||||
}
|
51
drivers/gpu/drm/nouveau/nv04_timer.c
Normal file
51
drivers/gpu/drm/nouveau/nv04_timer.c
Normal 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)
|
||||
{
|
||||
}
|
305
drivers/gpu/drm/nouveau/nv04_tv.c
Normal file
305
drivers/gpu/drm/nouveau/nv04_tv.c
Normal 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;
|
||||
}
|
24
drivers/gpu/drm/nouveau/nv10_fb.c
Normal file
24
drivers/gpu/drm/nouveau/nv10_fb.c
Normal 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)
|
||||
{
|
||||
}
|
260
drivers/gpu/drm/nouveau/nv10_fifo.c
Normal file
260
drivers/gpu/drm/nouveau/nv10_fifo.c
Normal 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;
|
||||
}
|
892
drivers/gpu/drm/nouveau/nv10_graph.c
Normal file
892
drivers/gpu/drm/nouveau/nv10_graph.c
Normal 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) */
|
||||
{}
|
||||
};
|
92
drivers/gpu/drm/nouveau/nv17_gpio.c
Normal file
92
drivers/gpu/drm/nouveau/nv17_gpio.c
Normal 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, ®, &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, ®, &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;
|
||||
}
|
681
drivers/gpu/drm/nouveau/nv17_tv.c
Normal file
681
drivers/gpu/drm/nouveau/nv17_tv.c
Normal 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;
|
||||
}
|
156
drivers/gpu/drm/nouveau/nv17_tv.h
Normal file
156
drivers/gpu/drm/nouveau/nv17_tv.h
Normal 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
|
583
drivers/gpu/drm/nouveau/nv17_tv_modes.c
Normal file
583
drivers/gpu/drm/nouveau/nv17_tv_modes.c
Normal 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);
|
||||
}
|
780
drivers/gpu/drm/nouveau/nv20_graph.c
Normal file
780
drivers/gpu/drm/nouveau/nv20_graph.c
Normal 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) */
|
||||
{}
|
||||
};
|
||||
|
62
drivers/gpu/drm/nouveau/nv40_fb.c
Normal file
62
drivers/gpu/drm/nouveau/nv40_fb.c
Normal 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)
|
||||
{
|
||||
}
|
314
drivers/gpu/drm/nouveau/nv40_fifo.c
Normal file
314
drivers/gpu/drm/nouveau/nv40_fifo.c
Normal 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;
|
||||
}
|
560
drivers/gpu/drm/nouveau/nv40_graph.c
Normal file
560
drivers/gpu/drm/nouveau/nv40_graph.c
Normal 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) */
|
||||
{}
|
||||
};
|
||||
|
38
drivers/gpu/drm/nouveau/nv40_mc.c
Normal file
38
drivers/gpu/drm/nouveau/nv40_mc.c
Normal 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)
|
||||
{
|
||||
}
|
769
drivers/gpu/drm/nouveau/nv50_crtc.c
Normal file
769
drivers/gpu/drm/nouveau/nv50_crtc.c
Normal 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;
|
||||
}
|
156
drivers/gpu/drm/nouveau/nv50_cursor.c
Normal file
156
drivers/gpu/drm/nouveau/nv50_cursor.c
Normal 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)));
|
||||
}
|
||||
}
|
||||
|
304
drivers/gpu/drm/nouveau/nv50_dac.c
Normal file
304
drivers/gpu/drm/nouveau/nv50_dac.c
Normal 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;
|
||||
}
|
||||
|
1015
drivers/gpu/drm/nouveau/nv50_display.c
Normal file
1015
drivers/gpu/drm/nouveau/nv50_display.c
Normal file
File diff suppressed because it is too large
Load Diff
46
drivers/gpu/drm/nouveau/nv50_display.h
Normal file
46
drivers/gpu/drm/nouveau/nv50_display.h
Normal 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__ */
|
113
drivers/gpu/drm/nouveau/nv50_evo.h
Normal file
113
drivers/gpu/drm/nouveau/nv50_evo.h
Normal 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
|
||||
|
273
drivers/gpu/drm/nouveau/nv50_fbcon.c
Normal file
273
drivers/gpu/drm/nouveau/nv50_fbcon.c
Normal 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;
|
||||
}
|
||||
|
494
drivers/gpu/drm/nouveau/nv50_fifo.c
Normal file
494
drivers/gpu/drm/nouveau/nv50_fifo.c
Normal 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;
|
||||
}
|
||||
|
385
drivers/gpu/drm/nouveau/nv50_graph.c
Normal file
385
drivers/gpu/drm/nouveau/nv50_graph.c
Normal 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) */
|
||||
{}
|
||||
};
|
509
drivers/gpu/drm/nouveau/nv50_instmem.c
Normal file
509
drivers/gpu/drm/nouveau/nv50_instmem.c
Normal 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");
|
||||
}
|
||||
}
|
||||
|
40
drivers/gpu/drm/nouveau/nv50_mc.c
Normal file
40
drivers/gpu/drm/nouveau/nv50_mc.c
Normal 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)
|
||||
{
|
||||
}
|
309
drivers/gpu/drm/nouveau/nv50_sor.c
Normal file
309
drivers/gpu/drm/nouveau/nv50_sor.c
Normal 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;
|
||||
}
|
535
drivers/gpu/drm/nouveau/nvreg.h
Normal file
535
drivers/gpu/drm/nouveau/nvreg.h
Normal 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
|
@ -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"
|
||||
|
@ -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
86
include/drm/i2c/ch7006.h
Normal 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
220
include/drm/nouveau_drm.h
Normal 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__ */
|
Loading…
Reference in New Issue
Block a user