mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-10 02:12:00 -04:00
Merge branch 'drm-nouveau-next' of git://git.freedesktop.org/git/nouveau/linux-2.6 into drm-next
* 'drm-nouveau-next' of git://git.freedesktop.org/git/nouveau/linux-2.6: (353 commits) drm/nouveau: remove allocations from gart populate() hook drm/nvc0/fb: slightly improve PMFB intr handling, move out of nvc0_graph.c drm/nvc0/fifo: avoid touching missing subfifos drm/nvd9/disp: bail out of mode_set_base if no fb bound to crtc drm/nvd9/disp: stub some more api hooks so we don't oops on resume drm/nouveau: fix printk typo in ioremap failure path drm/nvc0/pm: minor clock readback fixes drm/nv40/pm: execute memory reset script from vbios drm/nv50/gr: refactor initialisation drm/nouveau: if requested, try harder at disabling sysmem pushbufs drm/nv50/gr: enable ctxprog xfer only when we need it to save power drm/nouveau/dp: add support for displayport table 0x30 drm/nouveau/dp: return master dp table pointer too when looking up encoder drm/nouveau/bios: simplify U/d table hash matching func to just match drm/nouveau/dp: preserve non-pattern bits in DP_TRAINING_PATTERN_SET drm/nvc0/gr: remove MODULE_FIRMWARE() lines drm/nouveau/dp: use alternate lane mask for nvaf drm/nouveau/dp: link rate scripts are selected with a comparison table drm/nv40/pm: write nv40-specific reclocking routines drm/nv40/pm: parse geometric delta clock from vbios ...
This commit is contained in:
@@ -256,7 +256,6 @@ int drm_fb_helper_panic(struct notifier_block *n, unsigned long ununsed,
|
||||
{
|
||||
printk(KERN_ERR "panic occurred, switching back to text console\n");
|
||||
return drm_fb_helper_force_kernel_mode();
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(drm_fb_helper_panic);
|
||||
|
||||
|
||||
@@ -21,16 +21,17 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
|
||||
nv40_grctx.o nv50_grctx.o nvc0_grctx.o \
|
||||
nv84_crypt.o \
|
||||
nva3_copy.o nvc0_copy.o \
|
||||
nv40_mpeg.o nv50_mpeg.o \
|
||||
nv31_mpeg.o nv50_mpeg.o \
|
||||
nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
|
||||
nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
|
||||
nv50_cursor.o nv50_display.o \
|
||||
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
|
||||
nv04_crtc.o nv04_display.o nv04_cursor.o \
|
||||
nv50_evo.o nv50_crtc.o nv50_dac.o nv50_sor.o \
|
||||
nv50_cursor.o nv50_display.o \
|
||||
nvd0_display.o \
|
||||
nv04_fbcon.o nv50_fbcon.o nvc0_fbcon.o \
|
||||
nv10_gpio.o nv50_gpio.o \
|
||||
nv50_calc.o \
|
||||
nv04_pm.o nv50_pm.o nva3_pm.o \
|
||||
nv04_pm.o nv40_pm.o nv50_pm.o nva3_pm.o nvc0_pm.o \
|
||||
nv50_vram.o nvc0_vram.o \
|
||||
nv50_vm.o nvc0_vm.o
|
||||
|
||||
|
||||
@@ -37,8 +37,10 @@
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_drm.h"
|
||||
#include "nouveau_reg.h"
|
||||
#include "nouveau_encoder.h"
|
||||
|
||||
static int nv40_get_intensity(struct backlight_device *bd)
|
||||
static int
|
||||
nv40_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(bd);
|
||||
int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
|
||||
@@ -47,7 +49,8 @@ static int nv40_get_intensity(struct backlight_device *bd)
|
||||
return val;
|
||||
}
|
||||
|
||||
static int nv40_set_intensity(struct backlight_device *bd)
|
||||
static int
|
||||
nv40_set_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct drm_device *dev = bl_get_data(bd);
|
||||
int val = bd->props.brightness;
|
||||
@@ -65,30 +68,8 @@ static const struct backlight_ops nv40_bl_ops = {
|
||||
.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 const struct backlight_ops nv50_bl_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.get_brightness = nv50_get_intensity,
|
||||
.update_status = nv50_set_intensity,
|
||||
};
|
||||
|
||||
static int nouveau_nv40_backlight_init(struct drm_connector *connector)
|
||||
static int
|
||||
nv40_backlight_init(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
@@ -113,34 +94,129 @@ static int nouveau_nv40_backlight_init(struct drm_connector *connector)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int nouveau_nv50_backlight_init(struct drm_connector *connector)
|
||||
static int
|
||||
nv50_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
|
||||
struct drm_device *dev = nv_encoder->base.base.dev;
|
||||
int or = nv_encoder->or;
|
||||
u32 div = 1025;
|
||||
u32 val;
|
||||
|
||||
val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or));
|
||||
val &= NV50_PDISP_SOR_PWM_CTL_VAL;
|
||||
return ((val * 100) + (div / 2)) / div;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_set_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
|
||||
struct drm_device *dev = nv_encoder->base.base.dev;
|
||||
int or = nv_encoder->or;
|
||||
u32 div = 1025;
|
||||
u32 val = (bd->props.brightness * div) / 100;
|
||||
|
||||
nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or),
|
||||
NV50_PDISP_SOR_PWM_CTL_NEW | val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct backlight_ops nv50_bl_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.get_brightness = nv50_get_intensity,
|
||||
.update_status = nv50_set_intensity,
|
||||
};
|
||||
|
||||
static int
|
||||
nva3_get_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
|
||||
struct drm_device *dev = nv_encoder->base.base.dev;
|
||||
int or = nv_encoder->or;
|
||||
u32 div, val;
|
||||
|
||||
div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or));
|
||||
val = nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(or));
|
||||
val &= NVA3_PDISP_SOR_PWM_CTL_VAL;
|
||||
if (div && div >= val)
|
||||
return ((val * 100) + (div / 2)) / div;
|
||||
|
||||
return 100;
|
||||
}
|
||||
|
||||
static int
|
||||
nva3_set_intensity(struct backlight_device *bd)
|
||||
{
|
||||
struct nouveau_encoder *nv_encoder = bl_get_data(bd);
|
||||
struct drm_device *dev = nv_encoder->base.base.dev;
|
||||
int or = nv_encoder->or;
|
||||
u32 div, val;
|
||||
|
||||
div = nv_rd32(dev, NV50_PDISP_SOR_PWM_DIV(or));
|
||||
val = (bd->props.brightness * div) / 100;
|
||||
if (div) {
|
||||
nv_wr32(dev, NV50_PDISP_SOR_PWM_CTL(or), val |
|
||||
NV50_PDISP_SOR_PWM_CTL_NEW |
|
||||
NVA3_PDISP_SOR_PWM_CTL_UNK);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct backlight_ops nva3_bl_ops = {
|
||||
.options = BL_CORE_SUSPENDRESUME,
|
||||
.get_brightness = nva3_get_intensity,
|
||||
.update_status = nva3_set_intensity,
|
||||
};
|
||||
|
||||
static int
|
||||
nv50_backlight_init(struct drm_connector *connector)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_encoder *nv_encoder;
|
||||
struct backlight_properties props;
|
||||
struct backlight_device *bd;
|
||||
const struct backlight_ops *ops;
|
||||
|
||||
if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
|
||||
nv_encoder = find_encoder(connector, OUTPUT_LVDS);
|
||||
if (!nv_encoder) {
|
||||
nv_encoder = find_encoder(connector, OUTPUT_DP);
|
||||
if (!nv_encoder)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (!nv_rd32(dev, NV50_PDISP_SOR_PWM_CTL(nv_encoder->or)))
|
||||
return 0;
|
||||
|
||||
if (dev_priv->chipset <= 0xa0 ||
|
||||
dev_priv->chipset == 0xaa ||
|
||||
dev_priv->chipset == 0xac)
|
||||
ops = &nv50_bl_ops;
|
||||
else
|
||||
ops = &nva3_bl_ops;
|
||||
|
||||
memset(&props, 0, sizeof(struct backlight_properties));
|
||||
props.type = BACKLIGHT_RAW;
|
||||
props.max_brightness = 1025;
|
||||
bd = backlight_device_register("nv_backlight", &connector->kdev, dev,
|
||||
&nv50_bl_ops, &props);
|
||||
props.max_brightness = 100;
|
||||
bd = backlight_device_register("nv_backlight", &connector->kdev,
|
||||
nv_encoder, ops, &props);
|
||||
if (IS_ERR(bd))
|
||||
return PTR_ERR(bd);
|
||||
|
||||
dev_priv->backlight = bd;
|
||||
bd->props.brightness = nv50_get_intensity(bd);
|
||||
bd->props.brightness = bd->ops->get_brightness(bd);
|
||||
backlight_update_status(bd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int nouveau_backlight_init(struct drm_connector *connector)
|
||||
int
|
||||
nouveau_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct drm_connector *connector;
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
if (acpi_video_backlight_support()) {
|
||||
@@ -150,21 +226,28 @@ int nouveau_backlight_init(struct drm_connector *connector)
|
||||
}
|
||||
#endif
|
||||
|
||||
switch (dev_priv->card_type) {
|
||||
case NV_40:
|
||||
return nouveau_nv40_backlight_init(connector);
|
||||
case NV_50:
|
||||
return nouveau_nv50_backlight_init(connector);
|
||||
default:
|
||||
break;
|
||||
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
|
||||
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
|
||||
connector->connector_type != DRM_MODE_CONNECTOR_eDP)
|
||||
continue;
|
||||
|
||||
switch (dev_priv->card_type) {
|
||||
case NV_40:
|
||||
return nv40_backlight_init(connector);
|
||||
case NV_50:
|
||||
return nv50_backlight_init(connector);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void nouveau_backlight_exit(struct drm_connector *connector)
|
||||
void
|
||||
nouveau_backlight_exit(struct drm_device *dev)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
|
||||
if (dev_priv->backlight) {
|
||||
|
||||
@@ -296,6 +296,11 @@ munge_reg(struct nvbios *bios, uint32_t reg)
|
||||
if (dev_priv->card_type < NV_50)
|
||||
return reg;
|
||||
|
||||
if (reg & 0x80000000) {
|
||||
BUG_ON(bios->display.crtc < 0);
|
||||
reg += bios->display.crtc * 0x800;
|
||||
}
|
||||
|
||||
if (reg & 0x40000000) {
|
||||
BUG_ON(!dcbent);
|
||||
|
||||
@@ -304,7 +309,7 @@ munge_reg(struct nvbios *bios, uint32_t reg)
|
||||
reg += 0x00000080;
|
||||
}
|
||||
|
||||
reg &= ~0x60000000;
|
||||
reg &= ~0xe0000000;
|
||||
return reg;
|
||||
}
|
||||
|
||||
@@ -1174,22 +1179,19 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
*
|
||||
*/
|
||||
|
||||
struct bit_displayport_encoder_table *dpe = NULL;
|
||||
struct dcb_entry *dcb = bios->display.output;
|
||||
struct drm_device *dev = bios->dev;
|
||||
uint8_t cond = bios->data[offset + 1];
|
||||
int dummy;
|
||||
uint8_t *table, *entry;
|
||||
|
||||
BIOSLOG(bios, "0x%04X: subop 0x%02X\n", offset, cond);
|
||||
|
||||
if (!iexec->execute)
|
||||
return 3;
|
||||
|
||||
dpe = nouveau_bios_dp_table(dev, dcb, &dummy);
|
||||
if (!dpe) {
|
||||
NV_ERROR(dev, "0x%04X: INIT_3A: no encoder table!!\n", offset);
|
||||
table = nouveau_dp_bios_data(dev, dcb, &entry);
|
||||
if (!table)
|
||||
return 3;
|
||||
}
|
||||
|
||||
switch (cond) {
|
||||
case 0:
|
||||
@@ -1203,7 +1205,7 @@ init_dp_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
if (!(dpe->unknown & cond))
|
||||
if (!(entry[5] & cond))
|
||||
iexec->execute = false;
|
||||
break;
|
||||
case 5:
|
||||
@@ -3221,6 +3223,49 @@ init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void
|
||||
init_gpio_unknv50(struct nvbios *bios, struct dcb_gpio_entry *gpio)
|
||||
{
|
||||
const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
|
||||
u32 r, s, v;
|
||||
|
||||
/* Not a clue, needs de-magicing */
|
||||
r = nv50_gpio_ctl[gpio->line >> 4];
|
||||
s = (gpio->line & 0x0f);
|
||||
v = bios_rd32(bios, r) & ~(0x00010001 << s);
|
||||
switch ((gpio->entry & 0x06000000) >> 25) {
|
||||
case 1:
|
||||
v |= (0x00000001 << s);
|
||||
break;
|
||||
case 2:
|
||||
v |= (0x00010000 << s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
bios_wr32(bios, r, v);
|
||||
}
|
||||
|
||||
static void
|
||||
init_gpio_unknvd0(struct nvbios *bios, struct dcb_gpio_entry *gpio)
|
||||
{
|
||||
u32 v, i;
|
||||
|
||||
v = bios_rd32(bios, 0x00d610 + (gpio->line * 4));
|
||||
v &= 0xffffff00;
|
||||
v |= (gpio->entry & 0x00ff0000) >> 16;
|
||||
bios_wr32(bios, 0x00d610 + (gpio->line * 4), v);
|
||||
|
||||
i = (gpio->entry & 0x1f000000) >> 24;
|
||||
if (i) {
|
||||
v = bios_rd32(bios, 0x00d640 + ((i - 1) * 4));
|
||||
v &= 0xffffff00;
|
||||
v |= gpio->line;
|
||||
bios_wr32(bios, 0x00d640 + ((i - 1) * 4), v);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
{
|
||||
@@ -3235,7 +3280,6 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
|
||||
struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
|
||||
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
|
||||
const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
|
||||
int i;
|
||||
|
||||
if (dev_priv->card_type < NV_50) {
|
||||
@@ -3248,33 +3292,20 @@ init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
|
||||
for (i = 0; i < bios->dcb.gpio.entries; i++) {
|
||||
struct dcb_gpio_entry *gpio = &bios->dcb.gpio.entry[i];
|
||||
uint32_t r, s, v;
|
||||
|
||||
BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, gpio->entry);
|
||||
|
||||
BIOSLOG(bios, "0x%04X: set gpio 0x%02x, state %d\n",
|
||||
offset, gpio->tag, gpio->state_default);
|
||||
if (bios->execute)
|
||||
pgpio->set(bios->dev, gpio->tag, gpio->state_default);
|
||||
|
||||
/* The NVIDIA binary driver doesn't appear to actually do
|
||||
* any of this, my VBIOS does however.
|
||||
*/
|
||||
/* Not a clue, needs de-magicing */
|
||||
r = nv50_gpio_ctl[gpio->line >> 4];
|
||||
s = (gpio->line & 0x0f);
|
||||
v = bios_rd32(bios, r) & ~(0x00010001 << s);
|
||||
switch ((gpio->entry & 0x06000000) >> 25) {
|
||||
case 1:
|
||||
v |= (0x00000001 << s);
|
||||
break;
|
||||
case 2:
|
||||
v |= (0x00010000 << s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
bios_wr32(bios, r, v);
|
||||
if (!bios->execute)
|
||||
continue;
|
||||
|
||||
pgpio->set(bios->dev, gpio->tag, gpio->state_default);
|
||||
if (dev_priv->card_type < NV_D0)
|
||||
init_gpio_unknv50(bios, gpio);
|
||||
else
|
||||
init_gpio_unknvd0(bios, gpio);
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -3737,6 +3768,10 @@ parse_init_table(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
|
||||
int count = 0, i, ret;
|
||||
uint8_t id;
|
||||
|
||||
/* catch NULL script pointers */
|
||||
if (offset == 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Loop until INIT_DONE causes us to break out of the loop
|
||||
* (or until offset > bios length just in case... )
|
||||
@@ -4389,86 +4424,37 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t *
|
||||
bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
uint16_t record, int record_len, int record_nr,
|
||||
bool match_link)
|
||||
/* BIT 'U'/'d' table encoder subtables have hashes matching them to
|
||||
* a particular set of encoders.
|
||||
*
|
||||
* This function returns true if a particular DCB entry matches.
|
||||
*/
|
||||
bool
|
||||
bios_encoder_match(struct dcb_entry *dcb, u32 hash)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
uint32_t entry;
|
||||
uint16_t table;
|
||||
int i, v;
|
||||
if ((hash & 0x000000f0) != (dcb->location << 4))
|
||||
return false;
|
||||
if ((hash & 0x0000000f) != dcb->type)
|
||||
return false;
|
||||
if (!(hash & (dcb->or << 16)))
|
||||
return false;
|
||||
|
||||
switch (dcbent->type) {
|
||||
switch (dcb->type) {
|
||||
case OUTPUT_TMDS:
|
||||
case OUTPUT_LVDS:
|
||||
case OUTPUT_DP:
|
||||
break;
|
||||
default:
|
||||
match_link = false;
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < record_nr; i++, record += record_len) {
|
||||
table = ROM16(bios->data[record]);
|
||||
if (!table)
|
||||
continue;
|
||||
entry = ROM32(bios->data[table]);
|
||||
|
||||
if (match_link) {
|
||||
v = (entry & 0x00c00000) >> 22;
|
||||
if (!(v & dcbent->sorconf.link))
|
||||
continue;
|
||||
if (hash & 0x00c00000) {
|
||||
if (!(hash & (dcb->sorconf.link << 22)))
|
||||
return false;
|
||||
}
|
||||
|
||||
v = (entry & 0x000f0000) >> 16;
|
||||
if (!(v & dcbent->or))
|
||||
continue;
|
||||
|
||||
v = (entry & 0x000000f0) >> 4;
|
||||
if (v != dcbent->location)
|
||||
continue;
|
||||
|
||||
v = (entry & 0x0000000f);
|
||||
if (v != dcbent->type)
|
||||
continue;
|
||||
|
||||
return &bios->data[table];
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void *
|
||||
nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
int *length)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
uint8_t *table;
|
||||
|
||||
if (!bios->display.dp_table_ptr) {
|
||||
NV_ERROR(dev, "No pointer to DisplayPort table\n");
|
||||
return NULL;
|
||||
}
|
||||
table = &bios->data[bios->display.dp_table_ptr];
|
||||
|
||||
if (table[0] != 0x20 && table[0] != 0x21) {
|
||||
NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
|
||||
table[0]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
*length = table[4];
|
||||
return bios_output_config_match(dev, dcbent,
|
||||
bios->display.dp_table_ptr + table[1],
|
||||
table[2], table[3], table[0] >= 0x21);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
uint32_t sub, int pxclk)
|
||||
nouveau_bios_run_display_table(struct drm_device *dev, u16 type, int pclk,
|
||||
struct dcb_entry *dcbent, int crtc)
|
||||
{
|
||||
/*
|
||||
* The display script table is located by the BIT 'U' table.
|
||||
@@ -4498,7 +4484,7 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
uint8_t *table = &bios->data[bios->display.script_table_ptr];
|
||||
uint8_t *otable = NULL;
|
||||
uint16_t script;
|
||||
int i = 0;
|
||||
int i;
|
||||
|
||||
if (!bios->display.script_table_ptr) {
|
||||
NV_ERROR(dev, "No pointer to output script table\n");
|
||||
@@ -4550,30 +4536,33 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
|
||||
NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n",
|
||||
dcbent->type, dcbent->location, dcbent->or);
|
||||
otable = bios_output_config_match(dev, dcbent, table[1] +
|
||||
bios->display.script_table_ptr,
|
||||
table[2], table[3], table[0] >= 0x21);
|
||||
for (i = 0; i < table[3]; i++) {
|
||||
otable = ROMPTR(bios, table[table[1] + (i * table[2])]);
|
||||
if (otable && bios_encoder_match(dcbent, ROM32(otable[0])))
|
||||
break;
|
||||
}
|
||||
|
||||
if (!otable) {
|
||||
NV_DEBUG_KMS(dev, "failed to match any output table\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (pxclk < -2 || pxclk > 0) {
|
||||
if (pclk < -2 || pclk > 0) {
|
||||
/* Try to find matching script table entry */
|
||||
for (i = 0; i < otable[5]; i++) {
|
||||
if (ROM16(otable[table[4] + i*6]) == sub)
|
||||
if (ROM16(otable[table[4] + i*6]) == type)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == otable[5]) {
|
||||
NV_ERROR(dev, "Table 0x%04x not found for %d/%d, "
|
||||
"using first\n",
|
||||
sub, dcbent->type, dcbent->or);
|
||||
type, dcbent->type, dcbent->or);
|
||||
i = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (pxclk == 0) {
|
||||
if (pclk == 0) {
|
||||
script = ROM16(otable[6]);
|
||||
if (!script) {
|
||||
NV_DEBUG_KMS(dev, "output script 0 not found\n");
|
||||
@@ -4581,9 +4570,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
}
|
||||
|
||||
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 0\n", script);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent, crtc);
|
||||
} else
|
||||
if (pxclk == -1) {
|
||||
if (pclk == -1) {
|
||||
script = ROM16(otable[8]);
|
||||
if (!script) {
|
||||
NV_DEBUG_KMS(dev, "output script 1 not found\n");
|
||||
@@ -4591,9 +4580,9 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
}
|
||||
|
||||
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 1\n", script);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent, crtc);
|
||||
} else
|
||||
if (pxclk == -2) {
|
||||
if (pclk == -2) {
|
||||
if (table[4] >= 12)
|
||||
script = ROM16(otable[10]);
|
||||
else
|
||||
@@ -4604,31 +4593,31 @@ nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
|
||||
}
|
||||
|
||||
NV_DEBUG_KMS(dev, "0x%04X: parsing output script 2\n", script);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent, crtc);
|
||||
} else
|
||||
if (pxclk > 0) {
|
||||
if (pclk > 0) {
|
||||
script = ROM16(otable[table[4] + i*6 + 2]);
|
||||
if (script)
|
||||
script = clkcmptable(bios, script, pxclk);
|
||||
script = clkcmptable(bios, script, pclk);
|
||||
if (!script) {
|
||||
NV_DEBUG_KMS(dev, "clock script 0 not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 0\n", script);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent, crtc);
|
||||
} else
|
||||
if (pxclk < 0) {
|
||||
if (pclk < 0) {
|
||||
script = ROM16(otable[table[4] + i*6 + 4]);
|
||||
if (script)
|
||||
script = clkcmptable(bios, script, -pxclk);
|
||||
script = clkcmptable(bios, script, -pclk);
|
||||
if (!script) {
|
||||
NV_DEBUG_KMS(dev, "clock script 1 not found\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
NV_DEBUG_KMS(dev, "0x%04X: parsing clock script 1\n", script);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent);
|
||||
nouveau_bios_run_init_table(dev, script, dcbent, crtc);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -5478,14 +5467,6 @@ parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios,
|
||||
struct bit_entry *bitentry)
|
||||
{
|
||||
bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bit_table {
|
||||
const char id;
|
||||
int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *);
|
||||
@@ -5559,7 +5540,6 @@ parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
|
||||
parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds));
|
||||
parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds));
|
||||
parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U));
|
||||
parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport));
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -5884,9 +5864,15 @@ parse_dcb_gpio_table(struct nvbios *bios)
|
||||
}
|
||||
|
||||
e->line = (e->entry & 0x0000001f) >> 0;
|
||||
e->state_default = (e->entry & 0x01000000) >> 24;
|
||||
e->state[0] = (e->entry & 0x18000000) >> 27;
|
||||
e->state[1] = (e->entry & 0x60000000) >> 29;
|
||||
if (gpio[0] == 0x40) {
|
||||
e->state_default = (e->entry & 0x01000000) >> 24;
|
||||
e->state[0] = (e->entry & 0x18000000) >> 27;
|
||||
e->state[1] = (e->entry & 0x60000000) >> 29;
|
||||
} else {
|
||||
e->state_default = (e->entry & 0x00000080) >> 7;
|
||||
e->state[0] = (entry[4] >> 4) & 3;
|
||||
e->state[1] = (entry[4] >> 6) & 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6156,7 +6142,14 @@ parse_dcb20_entry(struct drm_device *dev, struct dcb_table *dcb,
|
||||
}
|
||||
case OUTPUT_DP:
|
||||
entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
|
||||
entry->dpconf.link_bw = (conf & 0x00e00000) >> 21;
|
||||
switch ((conf & 0x00e00000) >> 21) {
|
||||
case 0:
|
||||
entry->dpconf.link_bw = 162000;
|
||||
break;
|
||||
default:
|
||||
entry->dpconf.link_bw = 270000;
|
||||
break;
|
||||
}
|
||||
switch ((conf & 0x0f000000) >> 24) {
|
||||
case 0xf:
|
||||
entry->dpconf.link_nr = 4;
|
||||
@@ -6769,7 +6762,7 @@ uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
|
||||
|
||||
void
|
||||
nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
|
||||
struct dcb_entry *dcbent)
|
||||
struct dcb_entry *dcbent, int crtc)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
@@ -6777,11 +6770,22 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
|
||||
|
||||
spin_lock_bh(&bios->lock);
|
||||
bios->display.output = dcbent;
|
||||
bios->display.crtc = crtc;
|
||||
parse_init_table(bios, table, &iexec);
|
||||
bios->display.output = NULL;
|
||||
spin_unlock_bh(&bios->lock);
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_bios_init_exec(struct drm_device *dev, uint16_t table)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
struct init_exec iexec = { true, false };
|
||||
|
||||
parse_init_table(bios, table, &iexec);
|
||||
}
|
||||
|
||||
static bool NVInitVBIOS(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
@@ -6863,9 +6867,8 @@ nouveau_run_vbios_init(struct drm_device *dev)
|
||||
|
||||
if (dev_priv->card_type >= NV_50) {
|
||||
for (i = 0; i < bios->dcb.entries; i++) {
|
||||
nouveau_bios_run_display_table(dev,
|
||||
&bios->dcb.entry[i],
|
||||
0, 0);
|
||||
nouveau_bios_run_display_table(dev, 0, 0,
|
||||
&bios->dcb.entry[i], -1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -289,8 +289,8 @@ struct nvbios {
|
||||
|
||||
struct {
|
||||
struct dcb_entry *output;
|
||||
int crtc;
|
||||
uint16_t script_table_ptr;
|
||||
uint16_t dp_table_ptr;
|
||||
} display;
|
||||
|
||||
struct {
|
||||
|
||||
@@ -956,7 +956,7 @@ nouveau_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem)
|
||||
break;
|
||||
}
|
||||
|
||||
if (dev_priv->card_type == NV_C0)
|
||||
if (dev_priv->card_type >= NV_C0)
|
||||
page_shift = node->page_shift;
|
||||
else
|
||||
page_shift = 12;
|
||||
|
||||
@@ -411,13 +411,17 @@ nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
|
||||
return ret;
|
||||
init->channel = chan->id;
|
||||
|
||||
if (chan->dma.ib_max)
|
||||
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
|
||||
NOUVEAU_GEM_DOMAIN_GART;
|
||||
else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM)
|
||||
if (nouveau_vram_pushbuf == 0) {
|
||||
if (chan->dma.ib_max)
|
||||
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM |
|
||||
NOUVEAU_GEM_DOMAIN_GART;
|
||||
else if (chan->pushbuf_bo->bo.mem.mem_type == TTM_PL_VRAM)
|
||||
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
|
||||
else
|
||||
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
|
||||
} else {
|
||||
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_VRAM;
|
||||
else
|
||||
init->pushbuf_domains = NOUVEAU_GEM_DOMAIN_GART;
|
||||
}
|
||||
|
||||
if (dev_priv->card_type < NV_C0) {
|
||||
init->subchan[0].handle = NvM2MF;
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
|
||||
static void nouveau_connector_hotplug(void *, int);
|
||||
|
||||
static struct nouveau_encoder *
|
||||
struct nouveau_encoder *
|
||||
find_encoder(struct drm_connector *connector, int type)
|
||||
{
|
||||
struct drm_device *dev = connector->dev;
|
||||
@@ -116,10 +116,6 @@ nouveau_connector_destroy(struct drm_connector *connector)
|
||||
nouveau_connector_hotplug, connector);
|
||||
}
|
||||
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
|
||||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
|
||||
nouveau_backlight_exit(connector);
|
||||
|
||||
kfree(nv_connector->edid);
|
||||
drm_sysfs_connector_remove(connector);
|
||||
drm_connector_cleanup(connector);
|
||||
@@ -712,11 +708,8 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
|
||||
case OUTPUT_TV:
|
||||
return get_slave_funcs(encoder)->mode_valid(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;
|
||||
|
||||
max_clock = nv_encoder->dp.link_nr;
|
||||
max_clock *= nv_encoder->dp.link_bw;
|
||||
clock = clock * nouveau_connector_bpp(connector) / 8;
|
||||
break;
|
||||
default:
|
||||
@@ -871,7 +864,6 @@ nouveau_connector_create(struct drm_device *dev, int index)
|
||||
dev->mode_config.scaling_mode_property,
|
||||
nv_connector->scaling_mode);
|
||||
}
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
/* fall-through */
|
||||
case DCB_CONNECTOR_TV_0:
|
||||
case DCB_CONNECTOR_TV_1:
|
||||
@@ -888,27 +880,20 @@ nouveau_connector_create(struct drm_device *dev, int index)
|
||||
dev->mode_config.dithering_mode_property,
|
||||
nv_connector->use_dithering ?
|
||||
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
|
||||
|
||||
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS) {
|
||||
if (dev_priv->card_type >= NV_50)
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
else
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (pgpio->irq_register) {
|
||||
if (nv_connector->dcb->gpio_tag != 0xff && pgpio->irq_register) {
|
||||
pgpio->irq_register(dev, nv_connector->dcb->gpio_tag,
|
||||
nouveau_connector_hotplug, connector);
|
||||
|
||||
connector->polled = DRM_CONNECTOR_POLL_HPD;
|
||||
} else {
|
||||
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
|
||||
}
|
||||
|
||||
drm_sysfs_connector_add(connector);
|
||||
|
||||
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS ||
|
||||
connector->connector_type == DRM_MODE_CONNECTOR_eDP)
|
||||
nouveau_backlight_init(connector);
|
||||
|
||||
dcb->drm = connector;
|
||||
return dcb->drm;
|
||||
|
||||
@@ -925,22 +910,13 @@ nouveau_connector_hotplug(void *data, int plugged)
|
||||
struct drm_connector *connector = data;
|
||||
struct drm_device *dev = connector->dev;
|
||||
|
||||
NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
|
||||
drm_get_connector_name(connector));
|
||||
NV_DEBUG(dev, "%splugged %s\n", plugged ? "" : "un",
|
||||
drm_get_connector_name(connector));
|
||||
|
||||
if (connector->encoder && connector->encoder->crtc &&
|
||||
connector->encoder->crtc->enabled) {
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(connector->encoder);
|
||||
struct drm_encoder_helper_funcs *helper =
|
||||
connector->encoder->helper_private;
|
||||
|
||||
if (nv_encoder->dcb->type == OUTPUT_DP) {
|
||||
if (plugged)
|
||||
helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
|
||||
else
|
||||
helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
|
||||
}
|
||||
}
|
||||
if (plugged)
|
||||
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);
|
||||
else
|
||||
drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);
|
||||
|
||||
drm_helper_hpd_irq_event(dev);
|
||||
}
|
||||
|
||||
@@ -82,14 +82,13 @@ static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
|
||||
}
|
||||
|
||||
int nv50_crtc_create(struct drm_device *dev, int index);
|
||||
int nv50_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 *);
|
||||
int nv50_cursor_init(struct nouveau_crtc *);
|
||||
|
||||
struct nouveau_connector *
|
||||
nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
|
||||
|
||||
@@ -105,9 +105,12 @@ nouveau_framebuffer_init(struct drm_device *dev,
|
||||
if (dev_priv->chipset == 0x50)
|
||||
nv_fb->r_format |= (tile_flags << 8);
|
||||
|
||||
if (!tile_flags)
|
||||
nv_fb->r_pitch = 0x00100000 | fb->pitch;
|
||||
else {
|
||||
if (!tile_flags) {
|
||||
if (dev_priv->card_type < NV_D0)
|
||||
nv_fb->r_pitch = 0x00100000 | fb->pitch;
|
||||
else
|
||||
nv_fb->r_pitch = 0x01000000 | fb->pitch;
|
||||
} else {
|
||||
u32 mode = nvbo->tile_mode;
|
||||
if (dev_priv->card_type >= NV_C0)
|
||||
mode >>= 4;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -41,7 +41,7 @@ int nouveau_agpmode = -1;
|
||||
module_param_named(agpmode, nouveau_agpmode, int, 0400);
|
||||
|
||||
MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
|
||||
static int nouveau_modeset = -1; /* kms */
|
||||
int nouveau_modeset = -1;
|
||||
module_param_named(modeset, nouveau_modeset, int, 0400);
|
||||
|
||||
MODULE_PARM_DESC(vbios, "Override default VBIOS location");
|
||||
|
||||
@@ -414,12 +414,13 @@ struct nouveau_gpio_engine {
|
||||
};
|
||||
|
||||
struct nouveau_pm_voltage_level {
|
||||
u8 voltage;
|
||||
u8 vid;
|
||||
u32 voltage; /* microvolts */
|
||||
u8 vid;
|
||||
};
|
||||
|
||||
struct nouveau_pm_voltage {
|
||||
bool supported;
|
||||
u8 version;
|
||||
u8 vid_mask;
|
||||
|
||||
struct nouveau_pm_voltage_level *level;
|
||||
@@ -428,17 +429,48 @@ struct nouveau_pm_voltage {
|
||||
|
||||
struct nouveau_pm_memtiming {
|
||||
int id;
|
||||
u32 reg_100220;
|
||||
u32 reg_100224;
|
||||
u32 reg_100228;
|
||||
u32 reg_10022c;
|
||||
u32 reg_100230;
|
||||
u32 reg_100234;
|
||||
u32 reg_100238;
|
||||
u32 reg_10023c;
|
||||
u32 reg_100240;
|
||||
u32 reg_0; /* 0x10f290 on Fermi, 0x100220 for older */
|
||||
u32 reg_1;
|
||||
u32 reg_2;
|
||||
u32 reg_3;
|
||||
u32 reg_4;
|
||||
u32 reg_5;
|
||||
u32 reg_6;
|
||||
u32 reg_7;
|
||||
u32 reg_8;
|
||||
/* To be written to 0x1002c0 */
|
||||
u8 CL;
|
||||
u8 WR;
|
||||
};
|
||||
|
||||
struct nouveau_pm_tbl_header{
|
||||
u8 version;
|
||||
u8 header_len;
|
||||
u8 entry_cnt;
|
||||
u8 entry_len;
|
||||
};
|
||||
|
||||
struct nouveau_pm_tbl_entry{
|
||||
u8 tWR;
|
||||
u8 tUNK_1;
|
||||
u8 tCL;
|
||||
u8 tRP; /* Byte 3 */
|
||||
u8 empty_4;
|
||||
u8 tRAS; /* Byte 5 */
|
||||
u8 empty_6;
|
||||
u8 tRFC; /* Byte 7 */
|
||||
u8 empty_8;
|
||||
u8 tRC; /* Byte 9 */
|
||||
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
|
||||
u8 empty_15,empty_16,empty_17;
|
||||
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
|
||||
};
|
||||
|
||||
/* nouveau_mem.c */
|
||||
void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
|
||||
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
|
||||
struct nouveau_pm_memtiming *timing);
|
||||
|
||||
#define NOUVEAU_PM_MAX_LEVEL 8
|
||||
struct nouveau_pm_level {
|
||||
struct device_attribute dev_attr;
|
||||
@@ -448,11 +480,19 @@ struct nouveau_pm_level {
|
||||
u32 core;
|
||||
u32 memory;
|
||||
u32 shader;
|
||||
u32 unk05;
|
||||
u32 unk0a;
|
||||
u32 rop;
|
||||
u32 copy;
|
||||
u32 daemon;
|
||||
u32 vdec;
|
||||
u32 unk05; /* nv50:nva3, roughly.. */
|
||||
u32 unka0; /* nva3:nvc0 */
|
||||
u32 hub01; /* nvc0- */
|
||||
u32 hub06; /* nvc0- */
|
||||
u32 hub07; /* nvc0- */
|
||||
|
||||
u8 voltage;
|
||||
u8 fanspeed;
|
||||
u32 volt_min; /* microvolts */
|
||||
u32 volt_max;
|
||||
u8 fanspeed;
|
||||
|
||||
u16 memscript;
|
||||
struct nouveau_pm_memtiming *timing;
|
||||
@@ -496,6 +536,11 @@ struct nouveau_pm_engine {
|
||||
void *(*clock_pre)(struct drm_device *, struct nouveau_pm_level *,
|
||||
u32 id, int khz);
|
||||
void (*clock_set)(struct drm_device *, void *);
|
||||
|
||||
int (*clocks_get)(struct drm_device *, struct nouveau_pm_level *);
|
||||
void *(*clocks_pre)(struct drm_device *, struct nouveau_pm_level *);
|
||||
void (*clocks_set)(struct drm_device *, void *);
|
||||
|
||||
int (*voltage_get)(struct drm_device *);
|
||||
int (*voltage_set)(struct drm_device *, int voltage);
|
||||
int (*fanspeed_get)(struct drm_device *);
|
||||
@@ -504,7 +549,7 @@ struct nouveau_pm_engine {
|
||||
};
|
||||
|
||||
struct nouveau_vram_engine {
|
||||
struct nouveau_mm *mm;
|
||||
struct nouveau_mm mm;
|
||||
|
||||
int (*init)(struct drm_device *);
|
||||
void (*takedown)(struct drm_device *dev);
|
||||
@@ -623,6 +668,7 @@ enum nouveau_card_type {
|
||||
NV_40 = 0x40,
|
||||
NV_50 = 0x50,
|
||||
NV_C0 = 0xc0,
|
||||
NV_D0 = 0xd0
|
||||
};
|
||||
|
||||
struct drm_nouveau_private {
|
||||
@@ -633,8 +679,8 @@ struct drm_nouveau_private {
|
||||
enum nouveau_card_type card_type;
|
||||
/* exact chipset, derived from NV_PMC_BOOT_0 */
|
||||
int chipset;
|
||||
int stepping;
|
||||
int flags;
|
||||
u32 crystal;
|
||||
|
||||
void __iomem *mmio;
|
||||
|
||||
@@ -721,7 +767,6 @@ struct drm_nouveau_private {
|
||||
uint64_t vram_size;
|
||||
uint64_t vram_sys_base;
|
||||
|
||||
uint64_t fb_phys;
|
||||
uint64_t fb_available_size;
|
||||
uint64_t fb_mappable_pages;
|
||||
uint64_t fb_aper_free;
|
||||
@@ -784,6 +829,7 @@ nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
|
||||
}
|
||||
|
||||
/* nouveau_drv.c */
|
||||
extern int nouveau_modeset;
|
||||
extern int nouveau_agpmode;
|
||||
extern int nouveau_duallink;
|
||||
extern int nouveau_uscript_lvds;
|
||||
@@ -824,6 +870,8 @@ extern bool nouveau_wait_eq(struct drm_device *, uint64_t timeout,
|
||||
uint32_t reg, uint32_t mask, uint32_t val);
|
||||
extern bool nouveau_wait_ne(struct drm_device *, uint64_t timeout,
|
||||
uint32_t reg, uint32_t mask, uint32_t val);
|
||||
extern bool nouveau_wait_cb(struct drm_device *, u64 timeout,
|
||||
bool (*cond)(void *), void *);
|
||||
extern bool nouveau_wait_for_idle(struct drm_device *);
|
||||
extern int nouveau_card_init(struct drm_device *);
|
||||
|
||||
@@ -1006,15 +1054,15 @@ static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector
|
||||
|
||||
/* nouveau_backlight.c */
|
||||
#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
|
||||
extern int nouveau_backlight_init(struct drm_connector *);
|
||||
extern void nouveau_backlight_exit(struct drm_connector *);
|
||||
extern int nouveau_backlight_init(struct drm_device *);
|
||||
extern void nouveau_backlight_exit(struct drm_device *);
|
||||
#else
|
||||
static inline int nouveau_backlight_init(struct drm_connector *dev)
|
||||
static inline int nouveau_backlight_init(struct drm_device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void nouveau_backlight_exit(struct drm_connector *dev) { }
|
||||
static inline void nouveau_backlight_exit(struct drm_device *dev) { }
|
||||
#endif
|
||||
|
||||
/* nouveau_bios.c */
|
||||
@@ -1022,7 +1070,8 @@ extern int nouveau_bios_init(struct drm_device *);
|
||||
extern void nouveau_bios_takedown(struct drm_device *dev);
|
||||
extern int nouveau_run_vbios_init(struct drm_device *);
|
||||
extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
|
||||
struct dcb_entry *);
|
||||
struct dcb_entry *, int crtc);
|
||||
extern void nouveau_bios_init_exec(struct drm_device *, uint16_t table);
|
||||
extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
|
||||
enum dcb_gpio_tag);
|
||||
extern struct dcb_connector_table_entry *
|
||||
@@ -1030,11 +1079,8 @@ nouveau_bios_connector_entry(struct drm_device *, int index);
|
||||
extern u32 get_pll_register(struct drm_device *, enum pll_types);
|
||||
extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
|
||||
struct pll_lims *);
|
||||
extern int nouveau_bios_run_display_table(struct drm_device *,
|
||||
struct dcb_entry *,
|
||||
uint32_t script, int pxclk);
|
||||
extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *,
|
||||
int *length);
|
||||
extern int nouveau_bios_run_display_table(struct drm_device *, u16 id, int clk,
|
||||
struct dcb_entry *, int crtc);
|
||||
extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
|
||||
extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *);
|
||||
extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk,
|
||||
@@ -1043,6 +1089,7 @@ extern int run_tmds_table(struct drm_device *, struct dcb_entry *,
|
||||
int head, int pxclk);
|
||||
extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head,
|
||||
enum LVDS_script, int pxclk);
|
||||
bool bios_encoder_match(struct dcb_entry *, u32 hash);
|
||||
|
||||
/* nouveau_ttm.c */
|
||||
int nouveau_ttm_global_init(struct drm_nouveau_private *);
|
||||
@@ -1053,7 +1100,9 @@ int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
|
||||
int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
|
||||
uint8_t *data, int data_nr);
|
||||
bool nouveau_dp_detect(struct drm_encoder *);
|
||||
bool nouveau_dp_link_train(struct drm_encoder *);
|
||||
bool nouveau_dp_link_train(struct drm_encoder *, u32 datarate);
|
||||
void nouveau_dp_tu_update(struct drm_device *, int, int, u32, u32);
|
||||
u8 *nouveau_dp_bios_data(struct drm_device *, struct dcb_entry *, u8 **);
|
||||
|
||||
/* nv04_fb.c */
|
||||
extern int nv04_fb_init(struct drm_device *);
|
||||
@@ -1179,8 +1228,8 @@ extern int nva3_copy_create(struct drm_device *dev);
|
||||
/* nvc0_copy.c */
|
||||
extern int nvc0_copy_create(struct drm_device *dev, int engine);
|
||||
|
||||
/* nv40_mpeg.c */
|
||||
extern int nv40_mpeg_create(struct drm_device *dev);
|
||||
/* nv31_mpeg.c */
|
||||
extern int nv31_mpeg_create(struct drm_device *dev);
|
||||
|
||||
/* nv50_mpeg.c */
|
||||
extern int nv50_mpeg_create(struct drm_device *dev);
|
||||
@@ -1265,6 +1314,11 @@ extern int nv04_display_create(struct drm_device *);
|
||||
extern int nv04_display_init(struct drm_device *);
|
||||
extern void nv04_display_destroy(struct drm_device *);
|
||||
|
||||
/* nvd0_display.c */
|
||||
extern int nvd0_display_create(struct drm_device *);
|
||||
extern int nvd0_display_init(struct drm_device *);
|
||||
extern void nvd0_display_destroy(struct drm_device *);
|
||||
|
||||
/* nv04_crtc.c */
|
||||
extern int nv04_crtc_create(struct drm_device *, int index);
|
||||
|
||||
@@ -1374,6 +1428,8 @@ int nv50_gpio_init(struct drm_device *dev);
|
||||
void nv50_gpio_fini(struct drm_device *dev);
|
||||
int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
|
||||
int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
|
||||
int nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
|
||||
int nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
|
||||
int nv50_gpio_irq_register(struct drm_device *, enum dcb_gpio_tag,
|
||||
void (*)(void *, int), void *);
|
||||
void nv50_gpio_irq_unregister(struct drm_device *, enum dcb_gpio_tag,
|
||||
@@ -1448,6 +1504,8 @@ static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
|
||||
nouveau_wait_eq(dev, 2000000000ULL, (reg), (mask), (val))
|
||||
#define nv_wait_ne(dev, reg, mask, val) \
|
||||
nouveau_wait_ne(dev, 2000000000ULL, (reg), (mask), (val))
|
||||
#define nv_wait_cb(dev, func, data) \
|
||||
nouveau_wait_cb(dev, 2000000000ULL, (func), (data))
|
||||
|
||||
/* PRAMIN access */
|
||||
static inline u32 nv_ri32(struct drm_device *dev, unsigned offset)
|
||||
@@ -1514,6 +1572,7 @@ enum {
|
||||
NOUVEAU_REG_DEBUG_RMVIO = 0x80,
|
||||
NOUVEAU_REG_DEBUG_VGAATTR = 0x100,
|
||||
NOUVEAU_REG_DEBUG_EVO = 0x200,
|
||||
NOUVEAU_REG_DEBUG_AUXCH = 0x400
|
||||
};
|
||||
|
||||
#define NV_REG_DEBUG(type, dev, fmt, arg...) do { \
|
||||
|
||||
@@ -49,17 +49,17 @@ struct nouveau_encoder {
|
||||
|
||||
union {
|
||||
struct {
|
||||
int mc_unknown;
|
||||
uint32_t unk0;
|
||||
uint32_t unk1;
|
||||
int dpcd_version;
|
||||
u8 dpcd[8];
|
||||
int link_nr;
|
||||
int link_bw;
|
||||
bool enhanced_frame;
|
||||
u32 datarate;
|
||||
} dp;
|
||||
};
|
||||
};
|
||||
|
||||
struct nouveau_encoder *
|
||||
find_encoder(struct drm_connector *connector, int type);
|
||||
|
||||
static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
|
||||
{
|
||||
struct drm_encoder_slave *slave = to_encoder_slave(enc);
|
||||
@@ -83,21 +83,4 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
|
||||
int nv50_sor_create(struct drm_connector *, struct dcb_entry *);
|
||||
int nv50_dac_create(struct drm_connector *, struct dcb_entry *);
|
||||
|
||||
struct bit_displayport_encoder_table {
|
||||
uint32_t match;
|
||||
uint8_t record_nr;
|
||||
uint8_t unknown;
|
||||
uint16_t script0;
|
||||
uint16_t script1;
|
||||
uint16_t unknown_table;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct bit_displayport_encoder_table_entry {
|
||||
uint8_t vs_level;
|
||||
uint8_t pre_level;
|
||||
uint8_t reg0;
|
||||
uint8_t reg1;
|
||||
uint8_t reg2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#endif /* __NOUVEAU_ENCODER_H__ */
|
||||
|
||||
@@ -519,7 +519,7 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
|
||||
if (USE_SEMA(dev) && dev_priv->chipset < 0x84) {
|
||||
struct ttm_mem_reg *mem = &dev_priv->fence.bo->bo.mem;
|
||||
|
||||
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
|
||||
ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_FROM_MEMORY,
|
||||
mem->start << PAGE_SHIFT,
|
||||
mem->size, NV_MEM_ACCESS_RW,
|
||||
NV_MEM_TARGET_VRAM, &obj);
|
||||
@@ -530,7 +530,8 @@ nouveau_fence_channel_init(struct nouveau_channel *chan)
|
||||
nouveau_gpuobj_ref(NULL, &obj);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
} else
|
||||
if (USE_SEMA(dev)) {
|
||||
/* map fence bo into channel's vm */
|
||||
ret = nouveau_bo_vma_add(dev_priv->fence.bo, chan->vm,
|
||||
&chan->fence.vma);
|
||||
|
||||
@@ -107,6 +107,13 @@ nv4e_i2c_getsda(void *data)
|
||||
return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
|
||||
}
|
||||
|
||||
static const uint32_t nv50_i2c_port[] = {
|
||||
0x00e138, 0x00e150, 0x00e168, 0x00e180,
|
||||
0x00e254, 0x00e274, 0x00e764, 0x00e780,
|
||||
0x00e79c, 0x00e7b8
|
||||
};
|
||||
#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
|
||||
|
||||
static int
|
||||
nv50_i2c_getscl(void *data)
|
||||
{
|
||||
@@ -130,28 +137,32 @@ 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));
|
||||
nv_wr32(i2c->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));
|
||||
nv_mask(i2c->dev, i2c->wr, 0x00000006, 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)
|
||||
static int
|
||||
nvd0_i2c_getscl(void *data)
|
||||
{
|
||||
struct nouveau_i2c_chan *i2c = data;
|
||||
return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10);
|
||||
}
|
||||
|
||||
static int
|
||||
nvd0_i2c_getsda(void *data)
|
||||
{
|
||||
struct nouveau_i2c_chan *i2c = data;
|
||||
return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20);
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
|
||||
@@ -163,7 +174,8 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
|
||||
if (entry->chan)
|
||||
return -EEXIST;
|
||||
|
||||
if (dev_priv->card_type >= NV_50 && entry->read >= NV50_I2C_PORTS) {
|
||||
if (dev_priv->card_type >= NV_50 &&
|
||||
dev_priv->card_type <= NV_C0 && entry->read >= NV50_I2C_PORTS) {
|
||||
NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
|
||||
return -EINVAL;
|
||||
}
|
||||
@@ -192,10 +204,17 @@ nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
|
||||
case 5:
|
||||
i2c->bit.setsda = nv50_i2c_setsda;
|
||||
i2c->bit.setscl = nv50_i2c_setscl;
|
||||
i2c->bit.getsda = nv50_i2c_getsda;
|
||||
i2c->bit.getscl = nv50_i2c_getscl;
|
||||
i2c->rd = nv50_i2c_port[entry->read];
|
||||
i2c->wr = i2c->rd;
|
||||
if (dev_priv->card_type < NV_D0) {
|
||||
i2c->bit.getsda = nv50_i2c_getsda;
|
||||
i2c->bit.getscl = nv50_i2c_getscl;
|
||||
i2c->rd = nv50_i2c_port[entry->read];
|
||||
i2c->wr = i2c->rd;
|
||||
} else {
|
||||
i2c->bit.getsda = nvd0_i2c_getsda;
|
||||
i2c->bit.getscl = nvd0_i2c_getscl;
|
||||
i2c->rd = 0x00d014 + (entry->read * 0x20);
|
||||
i2c->wr = i2c->rd;
|
||||
}
|
||||
break;
|
||||
case 6:
|
||||
i2c->rd = entry->read;
|
||||
@@ -267,7 +286,10 @@ nouveau_i2c_find(struct drm_device *dev, int index)
|
||||
val = 0xe001;
|
||||
}
|
||||
|
||||
nv_wr32(dev, reg, (nv_rd32(dev, reg) & ~0xf003) | val);
|
||||
/* nfi, but neither auxch or i2c work if it's 1 */
|
||||
nv_mask(dev, reg + 0x0c, 0x00000001, 0x00000000);
|
||||
/* nfi, but switches auxch vs normal i2c */
|
||||
nv_mask(dev, reg + 0x00, 0x0000f003, val);
|
||||
}
|
||||
|
||||
if (!i2c->chan && nouveau_i2c_init(dev, i2c, index))
|
||||
|
||||
@@ -408,8 +408,6 @@ nouveau_mem_vram_init(struct drm_device *dev)
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_priv->fb_phys = pci_resource_start(dev->pdev, 1);
|
||||
|
||||
ret = nouveau_ttm_global_init(dev_priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -504,35 +502,146 @@ nouveau_mem_gart_init(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* XXX: For now a dummy. More samples required, possibly even a card
|
||||
* Called from nouveau_perf.c */
|
||||
void nv30_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
|
||||
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
|
||||
struct nouveau_pm_memtiming *timing) {
|
||||
|
||||
NV_DEBUG(dev,"Timing entry format unknown, please contact nouveau developers");
|
||||
}
|
||||
|
||||
void nv40_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
|
||||
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,
|
||||
struct nouveau_pm_memtiming *timing) {
|
||||
|
||||
timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
|
||||
|
||||
/* XXX: I don't trust the -1's and +1's... they must come
|
||||
* from somewhere! */
|
||||
timing->reg_1 = (e->tWR + 2 + magic_number) << 24 |
|
||||
1 << 16 |
|
||||
(e->tUNK_1 + 2 + magic_number) << 8 |
|
||||
(e->tCL + 2 - magic_number);
|
||||
timing->reg_2 = (magic_number << 24 | e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
|
||||
timing->reg_2 |= 0x20200000;
|
||||
|
||||
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x\n", timing->id,
|
||||
timing->reg_0, timing->reg_1,timing->reg_2);
|
||||
}
|
||||
|
||||
void nv50_mem_timing_entry(struct drm_device *dev, struct bit_entry *P, struct nouveau_pm_tbl_header *hdr,
|
||||
struct nouveau_pm_tbl_entry *e, uint8_t magic_number,struct nouveau_pm_memtiming *timing) {
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
|
||||
uint8_t unk18 = 1,
|
||||
unk19 = 1,
|
||||
unk20 = 0,
|
||||
unk21 = 0;
|
||||
|
||||
switch (min(hdr->entry_len, (u8) 22)) {
|
||||
case 22:
|
||||
unk21 = e->tUNK_21;
|
||||
case 21:
|
||||
unk20 = e->tUNK_20;
|
||||
case 20:
|
||||
unk19 = e->tUNK_19;
|
||||
case 19:
|
||||
unk18 = e->tUNK_18;
|
||||
break;
|
||||
}
|
||||
|
||||
timing->reg_0 = (e->tRC << 24 | e->tRFC << 16 | e->tRAS << 8 | e->tRP);
|
||||
|
||||
/* XXX: I don't trust the -1's and +1's... they must come
|
||||
* from somewhere! */
|
||||
timing->reg_1 = (e->tWR + unk19 + 1 + magic_number) << 24 |
|
||||
max(unk18, (u8) 1) << 16 |
|
||||
(e->tUNK_1 + unk19 + 1 + magic_number) << 8;
|
||||
if (dev_priv->chipset == 0xa8) {
|
||||
timing->reg_1 |= (e->tCL - 1);
|
||||
} else {
|
||||
timing->reg_1 |= (e->tCL + 2 - magic_number);
|
||||
}
|
||||
timing->reg_2 = (e->tUNK_12 << 16 | e->tUNK_11 << 8 | e->tUNK_10);
|
||||
|
||||
timing->reg_5 = (e->tRAS << 24 | e->tRC);
|
||||
timing->reg_5 += max(e->tUNK_10, e->tUNK_11) << 16;
|
||||
|
||||
if (P->version == 1) {
|
||||
timing->reg_2 |= magic_number << 24;
|
||||
timing->reg_3 = (0x14 + e->tCL) << 24 |
|
||||
0x16 << 16 |
|
||||
(e->tCL - 1) << 8 |
|
||||
(e->tCL - 1);
|
||||
timing->reg_4 = (nv_rd32(dev,0x10022c) & 0xffff0000) | e->tUNK_13 << 8 | e->tUNK_13;
|
||||
timing->reg_5 |= (e->tCL + 2) << 8;
|
||||
timing->reg_7 = 0x4000202 | (e->tCL - 1) << 16;
|
||||
} else {
|
||||
timing->reg_2 |= (unk19 - 1) << 24;
|
||||
/* XXX: reg_10022c for recentish cards pretty much unknown*/
|
||||
timing->reg_3 = e->tCL - 1;
|
||||
timing->reg_4 = (unk20 << 24 | unk21 << 16 |
|
||||
e->tUNK_13 << 8 | e->tUNK_13);
|
||||
/* XXX: +6? */
|
||||
timing->reg_5 |= (unk19 + 6) << 8;
|
||||
|
||||
/* XXX: reg_10023c currently unknown
|
||||
* 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
|
||||
timing->reg_7 = 0x202;
|
||||
}
|
||||
|
||||
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", timing->id,
|
||||
timing->reg_0, timing->reg_1,
|
||||
timing->reg_2, timing->reg_3);
|
||||
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
|
||||
timing->reg_4, timing->reg_5,
|
||||
timing->reg_6, timing->reg_7);
|
||||
NV_DEBUG(dev, " 240: %08x\n", timing->reg_8);
|
||||
}
|
||||
|
||||
void nvc0_mem_timing_entry(struct drm_device *dev, struct nouveau_pm_tbl_header *hdr,
|
||||
struct nouveau_pm_tbl_entry *e, struct nouveau_pm_memtiming *timing) {
|
||||
timing->reg_0 = (e->tRC << 24 | (e->tRFC & 0x7f) << 17 | e->tRAS << 8 | e->tRP);
|
||||
timing->reg_1 = (nv_rd32(dev,0x10f294) & 0xff000000) | (e->tUNK_11&0x0f) << 20 | (e->tUNK_19 << 7) | (e->tCL & 0x0f);
|
||||
timing->reg_2 = (nv_rd32(dev,0x10f298) & 0xff0000ff) | e->tWR << 16 | e->tUNK_1 << 8;
|
||||
timing->reg_3 = e->tUNK_20 << 9 | e->tUNK_13;
|
||||
timing->reg_4 = (nv_rd32(dev,0x10f2a0) & 0xfff000ff) | e->tUNK_12 << 15;
|
||||
NV_DEBUG(dev, "Entry %d: 290: %08x %08x %08x %08x\n", timing->id,
|
||||
timing->reg_0, timing->reg_1,
|
||||
timing->reg_2, timing->reg_3);
|
||||
NV_DEBUG(dev, " 2a0: %08x %08x %08x %08x\n",
|
||||
timing->reg_4, timing->reg_5,
|
||||
timing->reg_6, timing->reg_7);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the Memory Timing BIOS table, stores generated
|
||||
* register values
|
||||
* @pre init scripts were run, memtiming regs are initialized
|
||||
*/
|
||||
void
|
||||
nouveau_mem_timing_init(struct drm_device *dev)
|
||||
{
|
||||
/* cards < NVC0 only */
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
|
||||
struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
struct bit_entry P;
|
||||
u8 tUNK_0, tUNK_1, tUNK_2;
|
||||
u8 tRP; /* Byte 3 */
|
||||
u8 tRAS; /* Byte 5 */
|
||||
u8 tRFC; /* Byte 7 */
|
||||
u8 tRC; /* Byte 9 */
|
||||
u8 tUNK_10, tUNK_11, tUNK_12, tUNK_13, tUNK_14;
|
||||
u8 tUNK_18, tUNK_19, tUNK_20, tUNK_21;
|
||||
u8 magic_number = 0; /* Yeah... sorry*/
|
||||
u8 *mem = NULL, *entry;
|
||||
int i, recordlen, entries;
|
||||
struct nouveau_pm_tbl_header *hdr = NULL;
|
||||
uint8_t magic_number;
|
||||
u8 *entry;
|
||||
int i;
|
||||
|
||||
if (bios->type == NVBIOS_BIT) {
|
||||
if (bit_table(dev, 'P', &P))
|
||||
return;
|
||||
|
||||
if (P.version == 1)
|
||||
mem = ROMPTR(bios, P.data[4]);
|
||||
hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]);
|
||||
else
|
||||
if (P.version == 2)
|
||||
mem = ROMPTR(bios, P.data[8]);
|
||||
hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]);
|
||||
else {
|
||||
NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
|
||||
}
|
||||
@@ -541,150 +650,56 @@ nouveau_mem_timing_init(struct drm_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mem) {
|
||||
if (!hdr) {
|
||||
NV_DEBUG(dev, "memory timing table pointer invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (mem[0] != 0x10) {
|
||||
NV_WARN(dev, "memory timing table 0x%02x unknown\n", mem[0]);
|
||||
if (hdr->version != 0x10) {
|
||||
NV_WARN(dev, "memory timing table 0x%02x unknown\n", hdr->version);
|
||||
return;
|
||||
}
|
||||
|
||||
/* validate record length */
|
||||
entries = mem[2];
|
||||
recordlen = mem[3];
|
||||
if (recordlen < 15) {
|
||||
NV_ERROR(dev, "mem timing table length unknown: %d\n", mem[3]);
|
||||
if (hdr->entry_len < 15) {
|
||||
NV_ERROR(dev, "mem timing table length unknown: %d\n", hdr->entry_len);
|
||||
return;
|
||||
}
|
||||
|
||||
/* parse vbios entries into common format */
|
||||
memtimings->timing =
|
||||
kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
|
||||
kcalloc(hdr->entry_cnt, sizeof(*memtimings->timing), GFP_KERNEL);
|
||||
if (!memtimings->timing)
|
||||
return;
|
||||
|
||||
/* Get "some number" from the timing reg for NV_40 and NV_50
|
||||
* Used in calculations later */
|
||||
if (dev_priv->card_type >= NV_40 && dev_priv->chipset < 0x98) {
|
||||
* Used in calculations later... source unknown */
|
||||
magic_number = 0;
|
||||
if (P.version == 1) {
|
||||
magic_number = (nv_rd32(dev, 0x100228) & 0x0f000000) >> 24;
|
||||
}
|
||||
|
||||
entry = mem + mem[1];
|
||||
for (i = 0; i < entries; i++, entry += recordlen) {
|
||||
entry = (u8*) hdr + hdr->header_len;
|
||||
for (i = 0; i < hdr->entry_cnt; i++, entry += hdr->entry_len) {
|
||||
struct nouveau_pm_memtiming *timing = &pm->memtimings.timing[i];
|
||||
if (entry[0] == 0)
|
||||
continue;
|
||||
|
||||
tUNK_18 = 1;
|
||||
tUNK_19 = 1;
|
||||
tUNK_20 = 0;
|
||||
tUNK_21 = 0;
|
||||
switch (min(recordlen, 22)) {
|
||||
case 22:
|
||||
tUNK_21 = entry[21];
|
||||
case 21:
|
||||
tUNK_20 = entry[20];
|
||||
case 20:
|
||||
tUNK_19 = entry[19];
|
||||
case 19:
|
||||
tUNK_18 = entry[18];
|
||||
default:
|
||||
tUNK_0 = entry[0];
|
||||
tUNK_1 = entry[1];
|
||||
tUNK_2 = entry[2];
|
||||
tRP = entry[3];
|
||||
tRAS = entry[5];
|
||||
tRFC = entry[7];
|
||||
tRC = entry[9];
|
||||
tUNK_10 = entry[10];
|
||||
tUNK_11 = entry[11];
|
||||
tUNK_12 = entry[12];
|
||||
tUNK_13 = entry[13];
|
||||
tUNK_14 = entry[14];
|
||||
break;
|
||||
}
|
||||
|
||||
timing->reg_100220 = (tRC << 24 | tRFC << 16 | tRAS << 8 | tRP);
|
||||
|
||||
/* XXX: I don't trust the -1's and +1's... they must come
|
||||
* from somewhere! */
|
||||
timing->reg_100224 = (tUNK_0 + tUNK_19 + 1 + magic_number) << 24 |
|
||||
max(tUNK_18, (u8) 1) << 16 |
|
||||
(tUNK_1 + tUNK_19 + 1 + magic_number) << 8;
|
||||
if (dev_priv->chipset == 0xa8) {
|
||||
timing->reg_100224 |= (tUNK_2 - 1);
|
||||
} else {
|
||||
timing->reg_100224 |= (tUNK_2 + 2 - magic_number);
|
||||
}
|
||||
|
||||
timing->reg_100228 = (tUNK_12 << 16 | tUNK_11 << 8 | tUNK_10);
|
||||
if (dev_priv->chipset >= 0xa3 && dev_priv->chipset < 0xaa)
|
||||
timing->reg_100228 |= (tUNK_19 - 1) << 24;
|
||||
else
|
||||
timing->reg_100228 |= magic_number << 24;
|
||||
|
||||
if (dev_priv->card_type == NV_40) {
|
||||
/* NV40: don't know what the rest of the regs are..
|
||||
* And don't need to know either */
|
||||
timing->reg_100228 |= 0x20200000;
|
||||
} else if (dev_priv->card_type >= NV_50) {
|
||||
if (dev_priv->chipset < 0x98 ||
|
||||
(dev_priv->chipset == 0x98 &&
|
||||
dev_priv->stepping <= 0xa1)) {
|
||||
timing->reg_10022c = (0x14 + tUNK_2) << 24 |
|
||||
0x16 << 16 |
|
||||
(tUNK_2 - 1) << 8 |
|
||||
(tUNK_2 - 1);
|
||||
} else {
|
||||
/* XXX: reg_10022c for recentish cards */
|
||||
timing->reg_10022c = tUNK_2 - 1;
|
||||
}
|
||||
|
||||
timing->reg_100230 = (tUNK_20 << 24 | tUNK_21 << 16 |
|
||||
tUNK_13 << 8 | tUNK_13);
|
||||
|
||||
timing->reg_100234 = (tRAS << 24 | tRC);
|
||||
timing->reg_100234 += max(tUNK_10, tUNK_11) << 16;
|
||||
|
||||
if (dev_priv->chipset < 0x98 ||
|
||||
(dev_priv->chipset == 0x98 &&
|
||||
dev_priv->stepping <= 0xa1)) {
|
||||
timing->reg_100234 |= (tUNK_2 + 2) << 8;
|
||||
} else {
|
||||
/* XXX: +6? */
|
||||
timing->reg_100234 |= (tUNK_19 + 6) << 8;
|
||||
}
|
||||
|
||||
/* XXX; reg_100238
|
||||
* reg_100238: 0x00?????? */
|
||||
timing->reg_10023c = 0x202;
|
||||
if (dev_priv->chipset < 0x98 ||
|
||||
(dev_priv->chipset == 0x98 &&
|
||||
dev_priv->stepping <= 0xa1)) {
|
||||
timing->reg_10023c |= 0x4000000 | (tUNK_2 - 1) << 16;
|
||||
} else {
|
||||
/* XXX: reg_10023c
|
||||
* currently unknown
|
||||
* 10023c seen as 06xxxxxx, 0bxxxxxx or 0fxxxxxx */
|
||||
}
|
||||
|
||||
/* XXX: reg_100240? */
|
||||
}
|
||||
timing->id = i;
|
||||
timing->WR = entry[0];
|
||||
timing->CL = entry[2];
|
||||
|
||||
NV_DEBUG(dev, "Entry %d: 220: %08x %08x %08x %08x\n", i,
|
||||
timing->reg_100220, timing->reg_100224,
|
||||
timing->reg_100228, timing->reg_10022c);
|
||||
NV_DEBUG(dev, " 230: %08x %08x %08x %08x\n",
|
||||
timing->reg_100230, timing->reg_100234,
|
||||
timing->reg_100238, timing->reg_10023c);
|
||||
NV_DEBUG(dev, " 240: %08x\n", timing->reg_100240);
|
||||
if(dev_priv->card_type <= NV_40) {
|
||||
nv40_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
|
||||
} else if(dev_priv->card_type == NV_50){
|
||||
nv50_mem_timing_entry(dev,&P,hdr,(struct nouveau_pm_tbl_entry*) entry,magic_number,&pm->memtimings.timing[i]);
|
||||
} else if(dev_priv->card_type == NV_C0) {
|
||||
nvc0_mem_timing_entry(dev,hdr,(struct nouveau_pm_tbl_entry*) entry,&pm->memtimings.timing[i]);
|
||||
}
|
||||
}
|
||||
|
||||
memtimings->nr_timing = entries;
|
||||
memtimings->supported = (dev_priv->chipset <= 0x98);
|
||||
memtimings->nr_timing = hdr->entry_cnt;
|
||||
memtimings->supported = P.version == 1;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -693,7 +708,10 @@ nouveau_mem_timing_fini(struct drm_device *dev)
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pm_memtimings *mem = &dev_priv->engine.pm.memtimings;
|
||||
|
||||
kfree(mem->timing);
|
||||
if(mem->timing) {
|
||||
kfree(mem->timing);
|
||||
mem->timing = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "nouveau_mm.h"
|
||||
|
||||
static inline void
|
||||
region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)
|
||||
region_put(struct nouveau_mm *mm, struct nouveau_mm_node *a)
|
||||
{
|
||||
list_del(&a->nl_entry);
|
||||
list_del(&a->fl_entry);
|
||||
@@ -35,7 +35,7 @@ region_put(struct nouveau_mm *rmm, struct nouveau_mm_node *a)
|
||||
}
|
||||
|
||||
static struct nouveau_mm_node *
|
||||
region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)
|
||||
region_split(struct nouveau_mm *mm, struct nouveau_mm_node *a, u32 size)
|
||||
{
|
||||
struct nouveau_mm_node *b;
|
||||
|
||||
@@ -57,33 +57,33 @@ region_split(struct nouveau_mm *rmm, struct nouveau_mm_node *a, u32 size)
|
||||
return b;
|
||||
}
|
||||
|
||||
#define node(root, dir) ((root)->nl_entry.dir == &rmm->nodes) ? NULL : \
|
||||
#define node(root, dir) ((root)->nl_entry.dir == &mm->nodes) ? NULL : \
|
||||
list_entry((root)->nl_entry.dir, struct nouveau_mm_node, nl_entry)
|
||||
|
||||
void
|
||||
nouveau_mm_put(struct nouveau_mm *rmm, struct nouveau_mm_node *this)
|
||||
nouveau_mm_put(struct nouveau_mm *mm, struct nouveau_mm_node *this)
|
||||
{
|
||||
struct nouveau_mm_node *prev = node(this, prev);
|
||||
struct nouveau_mm_node *next = node(this, next);
|
||||
|
||||
list_add(&this->fl_entry, &rmm->free);
|
||||
list_add(&this->fl_entry, &mm->free);
|
||||
this->type = 0;
|
||||
|
||||
if (prev && prev->type == 0) {
|
||||
prev->length += this->length;
|
||||
region_put(rmm, this);
|
||||
region_put(mm, this);
|
||||
this = prev;
|
||||
}
|
||||
|
||||
if (next && next->type == 0) {
|
||||
next->offset = this->offset;
|
||||
next->length += this->length;
|
||||
region_put(rmm, this);
|
||||
region_put(mm, this);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
|
||||
nouveau_mm_get(struct nouveau_mm *mm, int type, u32 size, u32 size_nc,
|
||||
u32 align, struct nouveau_mm_node **pnode)
|
||||
{
|
||||
struct nouveau_mm_node *prev, *this, *next;
|
||||
@@ -92,17 +92,17 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
|
||||
u32 splitoff;
|
||||
u32 s, e;
|
||||
|
||||
list_for_each_entry(this, &rmm->free, fl_entry) {
|
||||
list_for_each_entry(this, &mm->free, fl_entry) {
|
||||
e = this->offset + this->length;
|
||||
s = this->offset;
|
||||
|
||||
prev = node(this, prev);
|
||||
if (prev && prev->type != type)
|
||||
s = roundup(s, rmm->block_size);
|
||||
s = roundup(s, mm->block_size);
|
||||
|
||||
next = node(this, next);
|
||||
if (next && next->type != type)
|
||||
e = rounddown(e, rmm->block_size);
|
||||
e = rounddown(e, mm->block_size);
|
||||
|
||||
s = (s + align_mask) & ~align_mask;
|
||||
e &= ~align_mask;
|
||||
@@ -110,10 +110,10 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
|
||||
continue;
|
||||
|
||||
splitoff = s - this->offset;
|
||||
if (splitoff && !region_split(rmm, this, splitoff))
|
||||
if (splitoff && !region_split(mm, this, splitoff))
|
||||
return -ENOMEM;
|
||||
|
||||
this = region_split(rmm, this, min(size, e - s));
|
||||
this = region_split(mm, this, min(size, e - s));
|
||||
if (!this)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -127,52 +127,49 @@ nouveau_mm_get(struct nouveau_mm *rmm, int type, u32 size, u32 size_nc,
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_mm_init(struct nouveau_mm **prmm, u32 offset, u32 length, u32 block)
|
||||
nouveau_mm_init(struct nouveau_mm *mm, u32 offset, u32 length, u32 block)
|
||||
{
|
||||
struct nouveau_mm *rmm;
|
||||
struct nouveau_mm_node *heap;
|
||||
struct nouveau_mm_node *node;
|
||||
|
||||
heap = kzalloc(sizeof(*heap), GFP_KERNEL);
|
||||
if (!heap)
|
||||
return -ENOMEM;
|
||||
heap->offset = roundup(offset, block);
|
||||
heap->length = rounddown(offset + length, block) - heap->offset;
|
||||
|
||||
rmm = kzalloc(sizeof(*rmm), GFP_KERNEL);
|
||||
if (!rmm) {
|
||||
kfree(heap);
|
||||
return -ENOMEM;
|
||||
if (block) {
|
||||
mutex_init(&mm->mutex);
|
||||
INIT_LIST_HEAD(&mm->nodes);
|
||||
INIT_LIST_HEAD(&mm->free);
|
||||
mm->block_size = block;
|
||||
mm->heap_nodes = 0;
|
||||
}
|
||||
rmm->block_size = block;
|
||||
mutex_init(&rmm->mutex);
|
||||
INIT_LIST_HEAD(&rmm->nodes);
|
||||
INIT_LIST_HEAD(&rmm->free);
|
||||
list_add(&heap->nl_entry, &rmm->nodes);
|
||||
list_add(&heap->fl_entry, &rmm->free);
|
||||
|
||||
*prmm = rmm;
|
||||
node = kzalloc(sizeof(*node), GFP_KERNEL);
|
||||
if (!node)
|
||||
return -ENOMEM;
|
||||
node->offset = roundup(offset, mm->block_size);
|
||||
node->length = rounddown(offset + length, mm->block_size) - node->offset;
|
||||
|
||||
list_add_tail(&node->nl_entry, &mm->nodes);
|
||||
list_add_tail(&node->fl_entry, &mm->free);
|
||||
mm->heap_nodes++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nouveau_mm_fini(struct nouveau_mm **prmm)
|
||||
nouveau_mm_fini(struct nouveau_mm *mm)
|
||||
{
|
||||
struct nouveau_mm *rmm = *prmm;
|
||||
struct nouveau_mm_node *node, *heap =
|
||||
list_first_entry(&rmm->nodes, struct nouveau_mm_node, nl_entry);
|
||||
list_first_entry(&mm->nodes, struct nouveau_mm_node, nl_entry);
|
||||
int nodes = 0;
|
||||
|
||||
if (!list_is_singular(&rmm->nodes)) {
|
||||
printk(KERN_ERR "nouveau_mm not empty at destroy time!\n");
|
||||
list_for_each_entry(node, &rmm->nodes, nl_entry) {
|
||||
printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
|
||||
node->type, node->offset, node->length);
|
||||
list_for_each_entry(node, &mm->nodes, nl_entry) {
|
||||
if (nodes++ == mm->heap_nodes) {
|
||||
printk(KERN_ERR "nouveau_mm in use at destroy time!\n");
|
||||
list_for_each_entry(node, &mm->nodes, nl_entry) {
|
||||
printk(KERN_ERR "0x%02x: 0x%08x 0x%08x\n",
|
||||
node->type, node->offset, node->length);
|
||||
}
|
||||
WARN_ON(1);
|
||||
return -EBUSY;
|
||||
}
|
||||
WARN_ON(1);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
kfree(heap);
|
||||
kfree(rmm);
|
||||
*prmm = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -42,10 +42,11 @@ struct nouveau_mm {
|
||||
struct mutex mutex;
|
||||
|
||||
u32 block_size;
|
||||
int heap_nodes;
|
||||
};
|
||||
|
||||
int nouveau_mm_init(struct nouveau_mm **, u32 offset, u32 length, u32 block);
|
||||
int nouveau_mm_fini(struct nouveau_mm **);
|
||||
int nouveau_mm_init(struct nouveau_mm *, u32 offset, u32 length, u32 block);
|
||||
int nouveau_mm_fini(struct nouveau_mm *);
|
||||
int nouveau_mm_pre(struct nouveau_mm *);
|
||||
int nouveau_mm_get(struct nouveau_mm *, int type, u32 size, u32 size_nc,
|
||||
u32 align, struct nouveau_mm_node **);
|
||||
|
||||
@@ -693,6 +693,7 @@ nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
|
||||
static int
|
||||
nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
|
||||
struct drm_device *dev = chan->dev;
|
||||
struct nouveau_gpuobj *pgd = NULL;
|
||||
struct nouveau_vm_pgd *vpgd;
|
||||
@@ -722,6 +723,9 @@ nvc0_gpuobj_channel_init(struct nouveau_channel *chan, struct nouveau_vm *vm)
|
||||
nv_wo32(chan->ramin, 0x020c, 0x000000ff);
|
||||
|
||||
/* map display semaphore buffers into channel's vm */
|
||||
if (dev_priv->card_type >= NV_D0)
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i];
|
||||
|
||||
@@ -746,7 +750,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
|
||||
int ret, i;
|
||||
|
||||
NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
|
||||
if (dev_priv->card_type == NV_C0)
|
||||
if (dev_priv->card_type >= NV_C0)
|
||||
return nvc0_gpuobj_channel_init(chan, vm);
|
||||
|
||||
/* Allocate a chunk of memory for per-channel object storage */
|
||||
@@ -793,7 +797,7 @@ nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
|
||||
return ret;
|
||||
|
||||
/* dma objects for display sync channel semaphore blocks */
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
||||
struct nouveau_gpuobj *sem = NULL;
|
||||
struct nv50_display_crtc *dispc =
|
||||
&nv50_display(dev)->crtc[i];
|
||||
@@ -875,18 +879,18 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
|
||||
|
||||
NV_DEBUG(dev, "ch%d\n", chan->id);
|
||||
|
||||
if (dev_priv->card_type >= NV_50) {
|
||||
if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) {
|
||||
struct nv50_display *disp = nv50_display(dev);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
for (i = 0; i < dev->mode_config.num_crtc; i++) {
|
||||
struct nv50_display_crtc *dispc = &disp->crtc[i];
|
||||
nouveau_bo_vma_del(dispc->sem.bo, &chan->dispc_vma[i]);
|
||||
}
|
||||
|
||||
nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd);
|
||||
nouveau_gpuobj_ref(NULL, &chan->vm_pd);
|
||||
}
|
||||
|
||||
nouveau_vm_ref(NULL, &chan->vm, chan->vm_pd);
|
||||
nouveau_gpuobj_ref(NULL, &chan->vm_pd);
|
||||
|
||||
if (drm_mm_initialized(&chan->ramin_heap))
|
||||
drm_mm_takedown(&chan->ramin_heap);
|
||||
nouveau_gpuobj_ref(NULL, &chan->ramin);
|
||||
|
||||
@@ -127,13 +127,57 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
|
||||
|
||||
entry += ramcfg * recordlen;
|
||||
if (entry[1] >= pm->memtimings.nr_timing) {
|
||||
NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
|
||||
if (entry[1] != 0xff)
|
||||
NV_WARN(dev, "timingset %d does not exist\n", entry[1]);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &pm->memtimings.timing[entry[1]];
|
||||
}
|
||||
|
||||
static void
|
||||
nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
|
||||
struct nouveau_pm_level *perflvl)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
u8 *vmap;
|
||||
int id;
|
||||
|
||||
id = perflvl->volt_min;
|
||||
perflvl->volt_min = 0;
|
||||
|
||||
/* boards using voltage table version <0x40 store the voltage
|
||||
* level directly in the perflvl entry as a multiple of 10mV
|
||||
*/
|
||||
if (dev_priv->engine.pm.voltage.version < 0x40) {
|
||||
perflvl->volt_min = id * 10000;
|
||||
perflvl->volt_max = perflvl->volt_min;
|
||||
return;
|
||||
}
|
||||
|
||||
/* on newer ones, the perflvl stores an index into yet another
|
||||
* vbios table containing a min/max voltage value for the perflvl
|
||||
*/
|
||||
if (P->version != 2 || P->length < 34) {
|
||||
NV_DEBUG(dev, "where's our volt map table ptr? %d %d\n",
|
||||
P->version, P->length);
|
||||
return;
|
||||
}
|
||||
|
||||
vmap = ROMPTR(bios, P->data[32]);
|
||||
if (!vmap) {
|
||||
NV_DEBUG(dev, "volt map table pointer invalid\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (id < vmap[3]) {
|
||||
vmap += vmap[1] + (vmap[2] * id);
|
||||
perflvl->volt_min = ROM32(vmap[0]);
|
||||
perflvl->volt_max = ROM32(vmap[4]);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nouveau_perf_init(struct drm_device *dev)
|
||||
{
|
||||
@@ -141,6 +185,8 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
|
||||
struct nvbios *bios = &dev_priv->vbios;
|
||||
struct bit_entry P;
|
||||
struct nouveau_pm_memtimings *memtimings = &pm->memtimings;
|
||||
struct nouveau_pm_tbl_header mt_hdr;
|
||||
u8 version, headerlen, recordlen, entries;
|
||||
u8 *perf, *entry;
|
||||
int vid, i;
|
||||
@@ -188,6 +234,22 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
}
|
||||
|
||||
entry = perf + headerlen;
|
||||
|
||||
/* For version 0x15, initialize memtiming table */
|
||||
if(version == 0x15) {
|
||||
memtimings->timing =
|
||||
kcalloc(entries, sizeof(*memtimings->timing), GFP_KERNEL);
|
||||
if(!memtimings) {
|
||||
NV_WARN(dev,"Could not allocate memtiming table\n");
|
||||
return;
|
||||
}
|
||||
|
||||
mt_hdr.entry_cnt = entries;
|
||||
mt_hdr.entry_len = 14;
|
||||
mt_hdr.version = version;
|
||||
mt_hdr.header_len = 4;
|
||||
}
|
||||
|
||||
for (i = 0; i < entries; i++) {
|
||||
struct nouveau_pm_level *perflvl = &pm->perflvl[pm->nr_perflvl];
|
||||
|
||||
@@ -203,7 +265,8 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
case 0x13:
|
||||
case 0x15:
|
||||
perflvl->fanspeed = entry[55];
|
||||
perflvl->voltage = (recordlen > 56) ? entry[56] : 0;
|
||||
if (recordlen > 56)
|
||||
perflvl->volt_min = entry[56];
|
||||
perflvl->core = ROM32(entry[1]) * 10;
|
||||
perflvl->memory = ROM32(entry[5]) * 20;
|
||||
break;
|
||||
@@ -211,9 +274,10 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
perflvl->fanspeed = entry[4];
|
||||
perflvl->voltage = entry[5];
|
||||
perflvl->core = ROM16(entry[6]) * 1000;
|
||||
|
||||
perflvl->volt_min = entry[5];
|
||||
perflvl->shader = ROM16(entry[6]) * 1000;
|
||||
perflvl->core = perflvl->shader;
|
||||
perflvl->core += (signed char)entry[8] * 1000;
|
||||
if (dev_priv->chipset == 0x49 ||
|
||||
dev_priv->chipset == 0x4b)
|
||||
perflvl->memory = ROM16(entry[11]) * 1000;
|
||||
@@ -223,7 +287,7 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
break;
|
||||
case 0x25:
|
||||
perflvl->fanspeed = entry[4];
|
||||
perflvl->voltage = entry[5];
|
||||
perflvl->volt_min = entry[5];
|
||||
perflvl->core = ROM16(entry[6]) * 1000;
|
||||
perflvl->shader = ROM16(entry[10]) * 1000;
|
||||
perflvl->memory = ROM16(entry[12]) * 1000;
|
||||
@@ -232,7 +296,7 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
perflvl->memscript = ROM16(entry[2]);
|
||||
case 0x35:
|
||||
perflvl->fanspeed = entry[6];
|
||||
perflvl->voltage = entry[7];
|
||||
perflvl->volt_min = entry[7];
|
||||
perflvl->core = ROM16(entry[8]) * 1000;
|
||||
perflvl->shader = ROM16(entry[10]) * 1000;
|
||||
perflvl->memory = ROM16(entry[12]) * 1000;
|
||||
@@ -240,30 +304,34 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
perflvl->unk05 = ROM16(entry[16]) * 1000;
|
||||
break;
|
||||
case 0x40:
|
||||
#define subent(n) entry[perf[2] + ((n) * perf[3])]
|
||||
#define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000
|
||||
perflvl->fanspeed = 0; /*XXX*/
|
||||
perflvl->voltage = entry[2];
|
||||
perflvl->volt_min = entry[2];
|
||||
if (dev_priv->card_type == NV_50) {
|
||||
perflvl->core = ROM16(subent(0)) & 0xfff;
|
||||
perflvl->shader = ROM16(subent(1)) & 0xfff;
|
||||
perflvl->memory = ROM16(subent(2)) & 0xfff;
|
||||
perflvl->core = subent(0);
|
||||
perflvl->shader = subent(1);
|
||||
perflvl->memory = subent(2);
|
||||
perflvl->vdec = subent(3);
|
||||
perflvl->unka0 = subent(4);
|
||||
} else {
|
||||
perflvl->shader = ROM16(subent(3)) & 0xfff;
|
||||
perflvl->hub06 = subent(0);
|
||||
perflvl->hub01 = subent(1);
|
||||
perflvl->copy = subent(2);
|
||||
perflvl->shader = subent(3);
|
||||
perflvl->rop = subent(4);
|
||||
perflvl->memory = subent(5);
|
||||
perflvl->vdec = subent(6);
|
||||
perflvl->daemon = subent(10);
|
||||
perflvl->hub07 = subent(11);
|
||||
perflvl->core = perflvl->shader / 2;
|
||||
perflvl->unk0a = ROM16(subent(4)) & 0xfff;
|
||||
perflvl->memory = ROM16(subent(5)) & 0xfff;
|
||||
}
|
||||
|
||||
perflvl->core *= 1000;
|
||||
perflvl->shader *= 1000;
|
||||
perflvl->memory *= 1000;
|
||||
perflvl->unk0a *= 1000;
|
||||
break;
|
||||
}
|
||||
|
||||
/* make sure vid is valid */
|
||||
if (pm->voltage.supported && perflvl->voltage) {
|
||||
vid = nouveau_volt_vid_lookup(dev, perflvl->voltage);
|
||||
nouveau_perf_voltage(dev, &P, perflvl);
|
||||
if (pm->voltage.supported && perflvl->volt_min) {
|
||||
vid = nouveau_volt_vid_lookup(dev, perflvl->volt_min);
|
||||
if (vid < 0) {
|
||||
NV_DEBUG(dev, "drop perflvl %d, bad vid\n", i);
|
||||
entry += recordlen;
|
||||
@@ -272,7 +340,11 @@ nouveau_perf_init(struct drm_device *dev)
|
||||
}
|
||||
|
||||
/* get the corresponding memory timings */
|
||||
if (version > 0x15) {
|
||||
if (version == 0x15) {
|
||||
memtimings->timing[i].id = i;
|
||||
nv30_mem_timing_entry(dev,&mt_hdr,(struct nouveau_pm_tbl_entry*) &entry[41],0,&memtimings->timing[i]);
|
||||
perflvl->timing = &memtimings->timing[i];
|
||||
} else if (version > 0x15) {
|
||||
/* last 3 args are for < 0x40, ignored for >= 0x40 */
|
||||
perflvl->timing =
|
||||
nouveau_perf_timing(dev, &P,
|
||||
|
||||
@@ -64,18 +64,26 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
if (perflvl == pm->cur)
|
||||
return 0;
|
||||
|
||||
if (pm->voltage.supported && pm->voltage_set && perflvl->voltage) {
|
||||
ret = pm->voltage_set(dev, perflvl->voltage);
|
||||
if (pm->voltage.supported && pm->voltage_set && perflvl->volt_min) {
|
||||
ret = pm->voltage_set(dev, perflvl->volt_min);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "voltage_set %d failed: %d\n",
|
||||
perflvl->voltage, ret);
|
||||
perflvl->volt_min, ret);
|
||||
}
|
||||
}
|
||||
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
|
||||
if (pm->clocks_pre) {
|
||||
void *state = pm->clocks_pre(dev, perflvl);
|
||||
if (IS_ERR(state))
|
||||
return PTR_ERR(state);
|
||||
pm->clocks_set(dev, state);
|
||||
} else
|
||||
if (pm->clock_set) {
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_CORE, perflvl->core);
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_SHADER, perflvl->shader);
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_MEMORY, perflvl->memory);
|
||||
nouveau_pm_clock_set(dev, perflvl, PLL_UNK05, perflvl->unk05);
|
||||
}
|
||||
|
||||
pm->cur = perflvl;
|
||||
return 0;
|
||||
@@ -92,9 +100,6 @@ nouveau_pm_profile_set(struct drm_device *dev, const char *profile)
|
||||
if (nouveau_perflvl_wr != 7777)
|
||||
return -EPERM;
|
||||
|
||||
if (!pm->clock_set)
|
||||
return -EINVAL;
|
||||
|
||||
if (!strncmp(profile, "boot", 4))
|
||||
perflvl = &pm->boot;
|
||||
else {
|
||||
@@ -123,31 +128,37 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
|
||||
int ret;
|
||||
|
||||
if (!pm->clock_get)
|
||||
return -EINVAL;
|
||||
|
||||
memset(perflvl, 0, sizeof(*perflvl));
|
||||
|
||||
ret = pm->clock_get(dev, PLL_CORE);
|
||||
if (ret > 0)
|
||||
perflvl->core = ret;
|
||||
if (pm->clocks_get) {
|
||||
ret = pm->clocks_get(dev, perflvl);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else
|
||||
if (pm->clock_get) {
|
||||
ret = pm->clock_get(dev, PLL_CORE);
|
||||
if (ret > 0)
|
||||
perflvl->core = ret;
|
||||
|
||||
ret = pm->clock_get(dev, PLL_MEMORY);
|
||||
if (ret > 0)
|
||||
perflvl->memory = ret;
|
||||
ret = pm->clock_get(dev, PLL_MEMORY);
|
||||
if (ret > 0)
|
||||
perflvl->memory = ret;
|
||||
|
||||
ret = pm->clock_get(dev, PLL_SHADER);
|
||||
if (ret > 0)
|
||||
perflvl->shader = ret;
|
||||
ret = pm->clock_get(dev, PLL_SHADER);
|
||||
if (ret > 0)
|
||||
perflvl->shader = ret;
|
||||
|
||||
ret = pm->clock_get(dev, PLL_UNK05);
|
||||
if (ret > 0)
|
||||
perflvl->unk05 = ret;
|
||||
ret = pm->clock_get(dev, PLL_UNK05);
|
||||
if (ret > 0)
|
||||
perflvl->unk05 = ret;
|
||||
}
|
||||
|
||||
if (pm->voltage.supported && pm->voltage_get) {
|
||||
ret = pm->voltage_get(dev);
|
||||
if (ret > 0)
|
||||
perflvl->voltage = ret;
|
||||
if (ret > 0) {
|
||||
perflvl->volt_min = ret;
|
||||
perflvl->volt_max = ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -156,7 +167,7 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
static void
|
||||
nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
|
||||
{
|
||||
char c[16], s[16], v[16], f[16], t[16];
|
||||
char c[16], s[16], v[32], f[16], t[16], m[16];
|
||||
|
||||
c[0] = '\0';
|
||||
if (perflvl->core)
|
||||
@@ -166,9 +177,19 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
|
||||
if (perflvl->shader)
|
||||
snprintf(s, sizeof(s), " shader %dMHz", perflvl->shader / 1000);
|
||||
|
||||
m[0] = '\0';
|
||||
if (perflvl->memory)
|
||||
snprintf(m, sizeof(m), " memory %dMHz", perflvl->memory / 1000);
|
||||
|
||||
v[0] = '\0';
|
||||
if (perflvl->voltage)
|
||||
snprintf(v, sizeof(v), " voltage %dmV", perflvl->voltage * 10);
|
||||
if (perflvl->volt_min && perflvl->volt_min != perflvl->volt_max) {
|
||||
snprintf(v, sizeof(v), " voltage %dmV-%dmV",
|
||||
perflvl->volt_min / 1000, perflvl->volt_max / 1000);
|
||||
} else
|
||||
if (perflvl->volt_min) {
|
||||
snprintf(v, sizeof(v), " voltage %dmV",
|
||||
perflvl->volt_min / 1000);
|
||||
}
|
||||
|
||||
f[0] = '\0';
|
||||
if (perflvl->fanspeed)
|
||||
@@ -178,8 +199,7 @@ nouveau_pm_perflvl_info(struct nouveau_pm_level *perflvl, char *ptr, int len)
|
||||
if (perflvl->timing)
|
||||
snprintf(t, sizeof(t), " timing %d", perflvl->timing->id);
|
||||
|
||||
snprintf(ptr, len, "memory %dMHz%s%s%s%s%s\n", perflvl->memory / 1000,
|
||||
c, s, v, f, t);
|
||||
snprintf(ptr, len, "%s%s%s%s%s%s\n", c, s, m, t, v, f);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
@@ -190,7 +210,7 @@ nouveau_pm_get_perflvl_info(struct device *d,
|
||||
char *ptr = buf;
|
||||
int len = PAGE_SIZE;
|
||||
|
||||
snprintf(ptr, len, "%d: ", perflvl->id);
|
||||
snprintf(ptr, len, "%d:", perflvl->id);
|
||||
ptr += strlen(buf);
|
||||
len -= strlen(buf);
|
||||
|
||||
@@ -211,9 +231,9 @@ nouveau_pm_get_perflvl(struct device *d, struct device_attribute *a, char *buf)
|
||||
if (!pm->cur)
|
||||
snprintf(ptr, len, "setting: boot\n");
|
||||
else if (pm->cur == &pm->boot)
|
||||
snprintf(ptr, len, "setting: boot\nc: ");
|
||||
snprintf(ptr, len, "setting: boot\nc:");
|
||||
else
|
||||
snprintf(ptr, len, "setting: static %d\nc: ", pm->cur->id);
|
||||
snprintf(ptr, len, "setting: static %d\nc:", pm->cur->id);
|
||||
ptr += strlen(buf);
|
||||
len -= strlen(buf);
|
||||
|
||||
@@ -292,7 +312,7 @@ nouveau_sysfs_fini(struct drm_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef CONFIG_HWMON
|
||||
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
|
||||
static ssize_t
|
||||
nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)
|
||||
{
|
||||
@@ -409,7 +429,7 @@ static const struct attribute_group hwmon_attrgroup = {
|
||||
static int
|
||||
nouveau_hwmon_init(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_HWMON
|
||||
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
|
||||
struct device *hwmon_dev;
|
||||
@@ -442,7 +462,7 @@ nouveau_hwmon_init(struct drm_device *dev)
|
||||
static void
|
||||
nouveau_hwmon_fini(struct drm_device *dev)
|
||||
{
|
||||
#ifdef CONFIG_HWMON
|
||||
#if defined(CONFIG_HWMON) || (defined(MODULE) && defined(CONFIG_HWMON_MODULE))
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
|
||||
|
||||
@@ -488,7 +508,7 @@ nouveau_pm_init(struct drm_device *dev)
|
||||
NV_INFO(dev, "%d available performance level(s)\n", pm->nr_perflvl);
|
||||
for (i = 0; i < pm->nr_perflvl; i++) {
|
||||
nouveau_pm_perflvl_info(&pm->perflvl[i], info, sizeof(info));
|
||||
NV_INFO(dev, "%d: %s", pm->perflvl[i].id, info);
|
||||
NV_INFO(dev, "%d:%s", pm->perflvl[i].id, info);
|
||||
}
|
||||
|
||||
/* determine current ("boot") performance level */
|
||||
@@ -498,7 +518,7 @@ nouveau_pm_init(struct drm_device *dev)
|
||||
pm->cur = &pm->boot;
|
||||
|
||||
nouveau_pm_perflvl_info(&pm->boot, info, sizeof(info));
|
||||
NV_INFO(dev, "c: %s", info);
|
||||
NV_INFO(dev, "c:%s", info);
|
||||
}
|
||||
|
||||
/* switch performance levels now if requested */
|
||||
|
||||
@@ -52,6 +52,11 @@ void *nv04_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
|
||||
u32 id, int khz);
|
||||
void nv04_pm_clock_set(struct drm_device *, void *);
|
||||
|
||||
/* nv40_pm.c */
|
||||
int nv40_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
|
||||
void *nv40_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
|
||||
void nv40_pm_clocks_set(struct drm_device *, void *);
|
||||
|
||||
/* nv50_pm.c */
|
||||
int nv50_pm_clock_get(struct drm_device *, u32 id);
|
||||
void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
|
||||
@@ -59,10 +64,12 @@ void *nv50_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
|
||||
void nv50_pm_clock_set(struct drm_device *, void *);
|
||||
|
||||
/* nva3_pm.c */
|
||||
int nva3_pm_clock_get(struct drm_device *, u32 id);
|
||||
void *nva3_pm_clock_pre(struct drm_device *, struct nouveau_pm_level *,
|
||||
u32 id, int khz);
|
||||
void nva3_pm_clock_set(struct drm_device *, void *);
|
||||
int nva3_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
|
||||
void *nva3_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
|
||||
void nva3_pm_clocks_set(struct drm_device *, void *);
|
||||
|
||||
/* nvc0_pm.c */
|
||||
int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
|
||||
|
||||
/* nouveau_temp.c */
|
||||
void nouveau_temp_init(struct drm_device *dev);
|
||||
|
||||
@@ -826,9 +826,12 @@
|
||||
#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_PDISP_SOR_PWM_DIV(i) (0x0061c080 + (i) * 0x800)
|
||||
#define NV50_PDISP_SOR_PWM_CTL(i) (0x0061c084 + (i) * 0x800)
|
||||
#define NV50_PDISP_SOR_PWM_CTL_NEW 0x80000000
|
||||
#define NVA3_PDISP_SOR_PWM_CTL_UNK 0x40000000
|
||||
#define NV50_PDISP_SOR_PWM_CTL_VAL 0x000007ff
|
||||
#define NVA3_PDISP_SOR_PWM_CTL_VAL 0x00ffffff
|
||||
#define NV50_SOR_DP_CTRL(i, l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
|
||||
#define NV50_SOR_DP_CTRL_ENABLED 0x00000001
|
||||
#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
|
||||
@@ -843,7 +846,7 @@
|
||||
#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
|
||||
#define NV50_SOR_DP_UNK118(i, l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
|
||||
#define NV50_SOR_DP_UNK120(i, l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
|
||||
#define NV50_SOR_DP_UNK128(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
|
||||
#define NV50_SOR_DP_SCFG(i, l) (0x0061c128 + (i) * 0x800 + (l) * 0x80)
|
||||
#define NV50_SOR_DP_UNK130(i, l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
|
||||
|
||||
#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
|
||||
|
||||
@@ -12,8 +12,8 @@ struct nouveau_sgdma_be {
|
||||
struct drm_device *dev;
|
||||
|
||||
dma_addr_t *pages;
|
||||
bool *ttm_alloced;
|
||||
unsigned nr_pages;
|
||||
bool unmap_pages;
|
||||
|
||||
u64 offset;
|
||||
bool bound;
|
||||
@@ -26,40 +26,28 @@ nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
|
||||
{
|
||||
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
||||
struct drm_device *dev = nvbe->dev;
|
||||
int i;
|
||||
|
||||
NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
|
||||
|
||||
if (nvbe->pages)
|
||||
return -EINVAL;
|
||||
nvbe->pages = dma_addrs;
|
||||
nvbe->nr_pages = num_pages;
|
||||
nvbe->unmap_pages = true;
|
||||
|
||||
nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);
|
||||
if (!nvbe->pages)
|
||||
return -ENOMEM;
|
||||
/* this code path isn't called and is incorrect anyways */
|
||||
if (0) { /* dma_addrs[0] != DMA_ERROR_CODE) { */
|
||||
nvbe->unmap_pages = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
nvbe->ttm_alloced = kmalloc(sizeof(bool) * num_pages, GFP_KERNEL);
|
||||
if (!nvbe->ttm_alloced)
|
||||
return -ENOMEM;
|
||||
|
||||
nvbe->nr_pages = 0;
|
||||
while (num_pages--) {
|
||||
/* this code path isn't called and is incorrect anyways */
|
||||
if (0) { /*dma_addrs[nvbe->nr_pages] != DMA_ERROR_CODE)*/
|
||||
nvbe->pages[nvbe->nr_pages] =
|
||||
dma_addrs[nvbe->nr_pages];
|
||||
nvbe->ttm_alloced[nvbe->nr_pages] = true;
|
||||
} else {
|
||||
nvbe->pages[nvbe->nr_pages] =
|
||||
pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
|
||||
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
||||
if (pci_dma_mapping_error(dev->pdev,
|
||||
nvbe->pages[nvbe->nr_pages])) {
|
||||
be->func->clear(be);
|
||||
return -EFAULT;
|
||||
}
|
||||
nvbe->ttm_alloced[nvbe->nr_pages] = false;
|
||||
for (i = 0; i < num_pages; i++) {
|
||||
nvbe->pages[i] = pci_map_page(dev->pdev, pages[i], 0,
|
||||
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
||||
if (pci_dma_mapping_error(dev->pdev, nvbe->pages[i])) {
|
||||
nvbe->nr_pages = --i;
|
||||
be->func->clear(be);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
nvbe->nr_pages++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -69,25 +57,16 @@ static void
|
||||
nouveau_sgdma_clear(struct ttm_backend *be)
|
||||
{
|
||||
struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
|
||||
struct drm_device *dev;
|
||||
struct drm_device *dev = nvbe->dev;
|
||||
|
||||
if (nvbe && nvbe->pages) {
|
||||
dev = nvbe->dev;
|
||||
NV_DEBUG(dev, "\n");
|
||||
|
||||
if (nvbe->bound)
|
||||
be->func->unbind(be);
|
||||
if (nvbe->bound)
|
||||
be->func->unbind(be);
|
||||
|
||||
if (nvbe->unmap_pages) {
|
||||
while (nvbe->nr_pages--) {
|
||||
if (!nvbe->ttm_alloced[nvbe->nr_pages])
|
||||
pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
|
||||
pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
|
||||
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
|
||||
}
|
||||
kfree(nvbe->pages);
|
||||
kfree(nvbe->ttm_alloced);
|
||||
nvbe->pages = NULL;
|
||||
nvbe->ttm_alloced = NULL;
|
||||
nvbe->nr_pages = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +105,7 @@ nv04_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
|
||||
|
||||
for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++, pte++) {
|
||||
nv_wo32(gpuobj, (pte * 4) + 0, offset_l | 3);
|
||||
dma_offset += NV_CTXDMA_PAGE_SIZE;
|
||||
offset_l += NV_CTXDMA_PAGE_SIZE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -286,9 +286,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
|
||||
engine->gpio.get = nv10_gpio_get;
|
||||
engine->gpio.set = nv10_gpio_set;
|
||||
engine->gpio.irq_enable = NULL;
|
||||
engine->pm.clock_get = nv04_pm_clock_get;
|
||||
engine->pm.clock_pre = nv04_pm_clock_pre;
|
||||
engine->pm.clock_set = nv04_pm_clock_set;
|
||||
engine->pm.clocks_get = nv40_pm_clocks_get;
|
||||
engine->pm.clocks_pre = nv40_pm_clocks_pre;
|
||||
engine->pm.clocks_set = nv40_pm_clocks_set;
|
||||
engine->pm.voltage_get = nouveau_voltage_gpio_get;
|
||||
engine->pm.voltage_set = nouveau_voltage_gpio_set;
|
||||
engine->pm.temp_get = nv40_temp_get;
|
||||
@@ -299,7 +299,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
|
||||
case 0x50:
|
||||
case 0x80: /* gotta love NVIDIA's consistency.. */
|
||||
case 0x90:
|
||||
case 0xA0:
|
||||
case 0xa0:
|
||||
engine->instmem.init = nv50_instmem_init;
|
||||
engine->instmem.takedown = nv50_instmem_takedown;
|
||||
engine->instmem.suspend = nv50_instmem_suspend;
|
||||
@@ -359,9 +359,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
|
||||
engine->pm.clock_set = nv50_pm_clock_set;
|
||||
break;
|
||||
default:
|
||||
engine->pm.clock_get = nva3_pm_clock_get;
|
||||
engine->pm.clock_pre = nva3_pm_clock_pre;
|
||||
engine->pm.clock_set = nva3_pm_clock_set;
|
||||
engine->pm.clocks_get = nva3_pm_clocks_get;
|
||||
engine->pm.clocks_pre = nva3_pm_clocks_pre;
|
||||
engine->pm.clocks_set = nva3_pm_clocks_set;
|
||||
break;
|
||||
}
|
||||
engine->pm.voltage_get = nouveau_voltage_gpio_get;
|
||||
@@ -376,7 +376,7 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
|
||||
engine->vram.put = nv50_vram_del;
|
||||
engine->vram.flags_valid = nv50_vram_flags_valid;
|
||||
break;
|
||||
case 0xC0:
|
||||
case 0xc0:
|
||||
engine->instmem.init = nvc0_instmem_init;
|
||||
engine->instmem.takedown = nvc0_instmem_takedown;
|
||||
engine->instmem.suspend = nvc0_instmem_suspend;
|
||||
@@ -422,12 +422,73 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
|
||||
engine->vram.put = nv50_vram_del;
|
||||
engine->vram.flags_valid = nvc0_vram_flags_valid;
|
||||
engine->pm.temp_get = nv84_temp_get;
|
||||
engine->pm.clocks_get = nvc0_pm_clocks_get;
|
||||
engine->pm.voltage_get = nouveau_voltage_gpio_get;
|
||||
engine->pm.voltage_set = nouveau_voltage_gpio_set;
|
||||
break;
|
||||
case 0xd0:
|
||||
engine->instmem.init = nvc0_instmem_init;
|
||||
engine->instmem.takedown = nvc0_instmem_takedown;
|
||||
engine->instmem.suspend = nvc0_instmem_suspend;
|
||||
engine->instmem.resume = nvc0_instmem_resume;
|
||||
engine->instmem.get = nv50_instmem_get;
|
||||
engine->instmem.put = nv50_instmem_put;
|
||||
engine->instmem.map = nv50_instmem_map;
|
||||
engine->instmem.unmap = nv50_instmem_unmap;
|
||||
engine->instmem.flush = nv84_instmem_flush;
|
||||
engine->mc.init = nv50_mc_init;
|
||||
engine->mc.takedown = nv50_mc_takedown;
|
||||
engine->timer.init = nv04_timer_init;
|
||||
engine->timer.read = nv04_timer_read;
|
||||
engine->timer.takedown = nv04_timer_takedown;
|
||||
engine->fb.init = nvc0_fb_init;
|
||||
engine->fb.takedown = nvc0_fb_takedown;
|
||||
engine->fifo.channels = 128;
|
||||
engine->fifo.init = nvc0_fifo_init;
|
||||
engine->fifo.takedown = nvc0_fifo_takedown;
|
||||
engine->fifo.disable = nvc0_fifo_disable;
|
||||
engine->fifo.enable = nvc0_fifo_enable;
|
||||
engine->fifo.reassign = nvc0_fifo_reassign;
|
||||
engine->fifo.channel_id = nvc0_fifo_channel_id;
|
||||
engine->fifo.create_context = nvc0_fifo_create_context;
|
||||
engine->fifo.destroy_context = nvc0_fifo_destroy_context;
|
||||
engine->fifo.load_context = nvc0_fifo_load_context;
|
||||
engine->fifo.unload_context = nvc0_fifo_unload_context;
|
||||
engine->display.early_init = nouveau_stub_init;
|
||||
engine->display.late_takedown = nouveau_stub_takedown;
|
||||
engine->display.create = nvd0_display_create;
|
||||
engine->display.init = nvd0_display_init;
|
||||
engine->display.destroy = nvd0_display_destroy;
|
||||
engine->gpio.init = nv50_gpio_init;
|
||||
engine->gpio.takedown = nouveau_stub_takedown;
|
||||
engine->gpio.get = nvd0_gpio_get;
|
||||
engine->gpio.set = nvd0_gpio_set;
|
||||
engine->gpio.irq_register = nv50_gpio_irq_register;
|
||||
engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
|
||||
engine->gpio.irq_enable = nv50_gpio_irq_enable;
|
||||
engine->vram.init = nvc0_vram_init;
|
||||
engine->vram.takedown = nv50_vram_fini;
|
||||
engine->vram.get = nvc0_vram_new;
|
||||
engine->vram.put = nv50_vram_del;
|
||||
engine->vram.flags_valid = nvc0_vram_flags_valid;
|
||||
engine->pm.clocks_get = nvc0_pm_clocks_get;
|
||||
engine->pm.voltage_get = nouveau_voltage_gpio_get;
|
||||
engine->pm.voltage_set = nouveau_voltage_gpio_set;
|
||||
break;
|
||||
default:
|
||||
NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* headless mode */
|
||||
if (nouveau_modeset == 2) {
|
||||
engine->display.early_init = nouveau_stub_init;
|
||||
engine->display.late_takedown = nouveau_stub_takedown;
|
||||
engine->display.create = nouveau_stub_init;
|
||||
engine->display.init = nouveau_stub_init;
|
||||
engine->display.destroy = nouveau_stub_takedown;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -449,21 +510,6 @@ nouveau_vga_set_decode(void *priv, bool state)
|
||||
return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
|
||||
}
|
||||
|
||||
static int
|
||||
nouveau_card_init_channel(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
int ret;
|
||||
|
||||
ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
|
||||
NvDmaFB, NvDmaTT);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
mutex_unlock(&dev_priv->channel->mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
|
||||
enum vga_switcheroo_state state)
|
||||
{
|
||||
@@ -630,8 +676,11 @@ nouveau_card_init(struct drm_device *dev)
|
||||
break;
|
||||
}
|
||||
|
||||
if (dev_priv->card_type == NV_40)
|
||||
nv40_mpeg_create(dev);
|
||||
if (dev_priv->card_type == NV_40 ||
|
||||
dev_priv->chipset == 0x31 ||
|
||||
dev_priv->chipset == 0x34 ||
|
||||
dev_priv->chipset == 0x36)
|
||||
nv31_mpeg_create(dev);
|
||||
else
|
||||
if (dev_priv->card_type == NV_50 &&
|
||||
(dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0))
|
||||
@@ -651,41 +700,69 @@ nouveau_card_init(struct drm_device *dev)
|
||||
goto out_engine;
|
||||
}
|
||||
|
||||
ret = engine->display.create(dev);
|
||||
ret = nouveau_irq_init(dev);
|
||||
if (ret)
|
||||
goto out_fifo;
|
||||
|
||||
ret = drm_vblank_init(dev, nv_two_heads(dev) ? 2 : 1);
|
||||
if (ret)
|
||||
goto out_vblank;
|
||||
/* initialise general modesetting */
|
||||
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.fb_base = pci_resource_start(dev->pdev, 1);
|
||||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
if (dev_priv->card_type < NV_10) {
|
||||
dev->mode_config.max_width = 2048;
|
||||
dev->mode_config.max_height = 2048;
|
||||
} else
|
||||
if (dev_priv->card_type < NV_50) {
|
||||
dev->mode_config.max_width = 4096;
|
||||
dev->mode_config.max_height = 4096;
|
||||
} else {
|
||||
dev->mode_config.max_width = 8192;
|
||||
dev->mode_config.max_height = 8192;
|
||||
}
|
||||
|
||||
ret = nouveau_irq_init(dev);
|
||||
ret = engine->display.create(dev);
|
||||
if (ret)
|
||||
goto out_vblank;
|
||||
goto out_irq;
|
||||
|
||||
/* what about PVIDEO/PCRTC/PRAMDAC etc? */
|
||||
nouveau_backlight_init(dev);
|
||||
|
||||
if (dev_priv->eng[NVOBJ_ENGINE_GR]) {
|
||||
ret = nouveau_fence_init(dev);
|
||||
if (ret)
|
||||
goto out_irq;
|
||||
goto out_disp;
|
||||
|
||||
ret = nouveau_card_init_channel(dev);
|
||||
ret = nouveau_channel_alloc(dev, &dev_priv->channel, NULL,
|
||||
NvDmaFB, NvDmaTT);
|
||||
if (ret)
|
||||
goto out_fence;
|
||||
|
||||
mutex_unlock(&dev_priv->channel->mutex);
|
||||
}
|
||||
|
||||
if (dev->mode_config.num_crtc) {
|
||||
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
|
||||
if (ret)
|
||||
goto out_chan;
|
||||
|
||||
nouveau_fbcon_init(dev);
|
||||
drm_kms_helper_poll_init(dev);
|
||||
}
|
||||
|
||||
nouveau_fbcon_init(dev);
|
||||
drm_kms_helper_poll_init(dev);
|
||||
return 0;
|
||||
|
||||
out_chan:
|
||||
nouveau_channel_put_unlocked(&dev_priv->channel);
|
||||
out_fence:
|
||||
nouveau_fence_fini(dev);
|
||||
out_disp:
|
||||
nouveau_backlight_exit(dev);
|
||||
engine->display.destroy(dev);
|
||||
out_irq:
|
||||
nouveau_irq_fini(dev);
|
||||
out_vblank:
|
||||
drm_vblank_cleanup(dev);
|
||||
engine->display.destroy(dev);
|
||||
out_fifo:
|
||||
if (!dev_priv->noaccel)
|
||||
engine->fifo.takedown(dev);
|
||||
@@ -732,15 +809,20 @@ static void nouveau_card_takedown(struct drm_device *dev)
|
||||
struct nouveau_engine *engine = &dev_priv->engine;
|
||||
int e;
|
||||
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
nouveau_fbcon_fini(dev);
|
||||
if (dev->mode_config.num_crtc) {
|
||||
drm_kms_helper_poll_fini(dev);
|
||||
nouveau_fbcon_fini(dev);
|
||||
drm_vblank_cleanup(dev);
|
||||
}
|
||||
|
||||
if (dev_priv->channel) {
|
||||
nouveau_channel_put_unlocked(&dev_priv->channel);
|
||||
nouveau_fence_fini(dev);
|
||||
}
|
||||
|
||||
nouveau_backlight_exit(dev);
|
||||
engine->display.destroy(dev);
|
||||
drm_mode_config_cleanup(dev);
|
||||
|
||||
if (!dev_priv->noaccel) {
|
||||
engine->fifo.takedown(dev);
|
||||
@@ -774,7 +856,6 @@ static void nouveau_card_takedown(struct drm_device *dev)
|
||||
engine->vram.takedown(dev);
|
||||
|
||||
nouveau_irq_fini(dev);
|
||||
drm_vblank_cleanup(dev);
|
||||
|
||||
nouveau_pm_fini(dev);
|
||||
nouveau_bios_takedown(dev);
|
||||
@@ -907,7 +988,7 @@ static int nouveau_remove_conflicting_drivers(struct drm_device *dev)
|
||||
int nouveau_load(struct drm_device *dev, unsigned long flags)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv;
|
||||
uint32_t reg0;
|
||||
uint32_t reg0, strap;
|
||||
resource_size_t mmio_start_offs;
|
||||
int ret;
|
||||
|
||||
@@ -951,13 +1032,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
|
||||
|
||||
/* Time to determine the card architecture */
|
||||
reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
|
||||
dev_priv->stepping = 0; /* XXX: add stepping for pre-NV10? */
|
||||
|
||||
/* We're dealing with >=NV10 */
|
||||
if ((reg0 & 0x0f000000) > 0) {
|
||||
/* Bit 27-20 contain the architecture in hex */
|
||||
dev_priv->chipset = (reg0 & 0xff00000) >> 20;
|
||||
dev_priv->stepping = (reg0 & 0xff);
|
||||
/* NV04 or NV05 */
|
||||
} else if ((reg0 & 0xff00fff0) == 0x20004000) {
|
||||
if (reg0 & 0x00f00000)
|
||||
@@ -987,6 +1066,9 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
|
||||
case 0xc0:
|
||||
dev_priv->card_type = NV_C0;
|
||||
break;
|
||||
case 0xd0:
|
||||
dev_priv->card_type = NV_D0;
|
||||
break;
|
||||
default:
|
||||
NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
|
||||
ret = -EINVAL;
|
||||
@@ -996,6 +1078,23 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
|
||||
NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
|
||||
dev_priv->card_type, reg0);
|
||||
|
||||
/* determine frequency of timing crystal */
|
||||
strap = nv_rd32(dev, 0x101000);
|
||||
if ( dev_priv->chipset < 0x17 ||
|
||||
(dev_priv->chipset >= 0x20 && dev_priv->chipset <= 0x25))
|
||||
strap &= 0x00000040;
|
||||
else
|
||||
strap &= 0x00400040;
|
||||
|
||||
switch (strap) {
|
||||
case 0x00000000: dev_priv->crystal = 13500; break;
|
||||
case 0x00000040: dev_priv->crystal = 14318; break;
|
||||
case 0x00400000: dev_priv->crystal = 27000; break;
|
||||
case 0x00400040: dev_priv->crystal = 25000; break;
|
||||
}
|
||||
|
||||
NV_DEBUG(dev, "crystal freq: %dKHz\n", dev_priv->crystal);
|
||||
|
||||
/* Determine whether we'll attempt acceleration or not, some
|
||||
* cards are disabled by default here due to them being known
|
||||
* non-functional, or never been tested due to lack of hw.
|
||||
@@ -1030,7 +1129,7 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
|
||||
ioremap(pci_resource_start(dev->pdev, ramin_bar),
|
||||
dev_priv->ramin_size);
|
||||
if (!dev_priv->ramin) {
|
||||
NV_ERROR(dev, "Failed to PRAMIN BAR");
|
||||
NV_ERROR(dev, "Failed to map PRAMIN BAR\n");
|
||||
ret = -ENOMEM;
|
||||
goto err_mmio;
|
||||
}
|
||||
@@ -1130,7 +1229,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
|
||||
getparam->value = 1;
|
||||
break;
|
||||
case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
|
||||
getparam->value = 1;
|
||||
getparam->value = dev_priv->card_type < NV_D0;
|
||||
break;
|
||||
case NOUVEAU_GETPARAM_GRAPH_UNITS:
|
||||
/* NV40 and NV50 versions are quite different, but register
|
||||
@@ -1198,6 +1297,23 @@ nouveau_wait_ne(struct drm_device *dev, uint64_t timeout,
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Wait until cond(data) == true, up until timeout has hit */
|
||||
bool
|
||||
nouveau_wait_cb(struct drm_device *dev, u64 timeout,
|
||||
bool (*cond)(void *), void *data)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
|
||||
u64 start = ptimer->read(dev);
|
||||
|
||||
do {
|
||||
if (cond(data) == true)
|
||||
return true;
|
||||
} while (ptimer->read(dev) - start < timeout);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Waits for PGRAPH to go completely idle */
|
||||
bool nouveau_wait_for_idle(struct drm_device *dev)
|
||||
{
|
||||
|
||||
@@ -172,9 +172,9 @@ nouveau_vm_unmap_pgt(struct nouveau_vm *vm, int big, u32 fpde, u32 lpde)
|
||||
vm->map_pgt(vpgd->obj, pde, vpgt->obj);
|
||||
}
|
||||
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
nouveau_gpuobj_ref(NULL, &pgt);
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,18 +191,18 @@ nouveau_vm_map_pgt(struct nouveau_vm *vm, u32 pde, u32 type)
|
||||
pgt_size = (1 << (vm->pgt_bits + 12)) >> type;
|
||||
pgt_size *= 8;
|
||||
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
ret = nouveau_gpuobj_new(vm->dev, NULL, pgt_size, 0x1000,
|
||||
NVOBJ_FLAG_ZERO_ALLOC, &pgt);
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
if (unlikely(ret))
|
||||
return ret;
|
||||
|
||||
/* someone beat us to filling the PDE while we didn't have the lock */
|
||||
if (unlikely(vpgt->refcount[big]++)) {
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
nouveau_gpuobj_ref(NULL, &pgt);
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -223,10 +223,10 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
|
||||
u32 fpde, lpde, pde;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
ret = nouveau_mm_get(vm->mm, page_shift, msize, 0, align, &vma->node);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
ret = nouveau_mm_get(&vm->mm, page_shift, msize, 0, align, &vma->node);
|
||||
if (unlikely(ret != 0)) {
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -245,13 +245,13 @@ nouveau_vm_get(struct nouveau_vm *vm, u64 size, u32 page_shift,
|
||||
if (ret) {
|
||||
if (pde != fpde)
|
||||
nouveau_vm_unmap_pgt(vm, big, fpde, pde - 1);
|
||||
nouveau_mm_put(vm->mm, vma->node);
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
nouveau_mm_put(&vm->mm, vma->node);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
vma->node = NULL;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
|
||||
vma->vm = vm;
|
||||
vma->offset = (u64)vma->node->offset << 12;
|
||||
@@ -270,11 +270,11 @@ nouveau_vm_put(struct nouveau_vma *vma)
|
||||
fpde = (vma->node->offset >> vm->pgt_bits);
|
||||
lpde = (vma->node->offset + vma->node->length - 1) >> vm->pgt_bits;
|
||||
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
nouveau_vm_unmap_pgt(vm, vma->node->type != vm->spg_shift, fpde, lpde);
|
||||
nouveau_mm_put(vm->mm, vma->node);
|
||||
nouveau_mm_put(&vm->mm, vma->node);
|
||||
vma->node = NULL;
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -306,7 +306,7 @@ nouveau_vm_new(struct drm_device *dev, u64 offset, u64 length, u64 mm_offset,
|
||||
block = length;
|
||||
|
||||
} else
|
||||
if (dev_priv->card_type == NV_C0) {
|
||||
if (dev_priv->card_type >= NV_C0) {
|
||||
vm->map_pgt = nvc0_vm_map_pgt;
|
||||
vm->map = nvc0_vm_map;
|
||||
vm->map_sg = nvc0_vm_map_sg;
|
||||
@@ -360,11 +360,11 @@ nouveau_vm_link(struct nouveau_vm *vm, struct nouveau_gpuobj *pgd)
|
||||
|
||||
nouveau_gpuobj_ref(pgd, &vpgd->obj);
|
||||
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
for (i = vm->fpde; i <= vm->lpde; i++)
|
||||
vm->map_pgt(pgd, i, vm->pgt[i - vm->fpde].obj);
|
||||
list_add(&vpgd->head, &vm->pgd_list);
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
|
||||
if (!mpgd)
|
||||
return;
|
||||
|
||||
mutex_lock(&vm->mm->mutex);
|
||||
mutex_lock(&vm->mm.mutex);
|
||||
list_for_each_entry_safe(vpgd, tmp, &vm->pgd_list, head) {
|
||||
if (vpgd->obj == mpgd) {
|
||||
pgd = vpgd->obj;
|
||||
@@ -386,7 +386,7 @@ nouveau_vm_unlink(struct nouveau_vm *vm, struct nouveau_gpuobj *mpgd)
|
||||
break;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&vm->mm->mutex);
|
||||
mutex_unlock(&vm->mm.mutex);
|
||||
|
||||
nouveau_gpuobj_ref(NULL, &pgd);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ struct nouveau_vma {
|
||||
|
||||
struct nouveau_vm {
|
||||
struct drm_device *dev;
|
||||
struct nouveau_mm *mm;
|
||||
struct nouveau_mm mm;
|
||||
int refcount;
|
||||
|
||||
struct list_head pgd_list;
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_pm.h"
|
||||
|
||||
static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a };
|
||||
static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 };
|
||||
static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);
|
||||
|
||||
int
|
||||
@@ -170,6 +170,13 @@ nouveau_volt_init(struct drm_device *dev)
|
||||
*/
|
||||
vidshift = 2;
|
||||
break;
|
||||
case 0x40:
|
||||
headerlen = volt[1];
|
||||
recordlen = volt[2];
|
||||
entries = volt[3]; /* not a clue what the entries are for.. */
|
||||
vidmask = volt[11]; /* guess.. */
|
||||
vidshift = 0;
|
||||
break;
|
||||
default:
|
||||
NV_WARN(dev, "voltage table 0x%02x unknown\n", volt[0]);
|
||||
return;
|
||||
@@ -197,16 +204,37 @@ nouveau_volt_init(struct drm_device *dev)
|
||||
}
|
||||
|
||||
/* parse vbios entries into common format */
|
||||
voltage->level = kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
|
||||
if (!voltage->level)
|
||||
return;
|
||||
voltage->version = volt[0];
|
||||
if (voltage->version < 0x40) {
|
||||
voltage->nr_level = entries;
|
||||
voltage->level =
|
||||
kcalloc(entries, sizeof(*voltage->level), GFP_KERNEL);
|
||||
if (!voltage->level)
|
||||
return;
|
||||
|
||||
entry = volt + headerlen;
|
||||
for (i = 0; i < entries; i++, entry += recordlen) {
|
||||
voltage->level[i].voltage = entry[0];
|
||||
voltage->level[i].vid = entry[1] >> vidshift;
|
||||
entry = volt + headerlen;
|
||||
for (i = 0; i < entries; i++, entry += recordlen) {
|
||||
voltage->level[i].voltage = entry[0] * 10000;
|
||||
voltage->level[i].vid = entry[1] >> vidshift;
|
||||
}
|
||||
} else {
|
||||
u32 volt_uv = ROM32(volt[4]);
|
||||
s16 step_uv = ROM16(volt[8]);
|
||||
u8 vid;
|
||||
|
||||
voltage->nr_level = voltage->vid_mask + 1;
|
||||
voltage->level = kcalloc(voltage->nr_level,
|
||||
sizeof(*voltage->level), GFP_KERNEL);
|
||||
if (!voltage->level)
|
||||
return;
|
||||
|
||||
for (vid = 0; vid <= voltage->vid_mask; vid++) {
|
||||
voltage->level[vid].voltage = volt_uv;
|
||||
voltage->level[vid].vid = vid;
|
||||
volt_uv += step_uv;
|
||||
}
|
||||
}
|
||||
voltage->nr_level = entries;
|
||||
|
||||
voltage->supported = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -781,11 +781,20 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
|
||||
struct drm_device *dev = crtc->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_framebuffer *drm_fb = nv_crtc->base.fb;
|
||||
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
||||
struct drm_framebuffer *drm_fb;
|
||||
struct nouveau_framebuffer *fb;
|
||||
int arb_burst, arb_lwm;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
||||
|
||||
/* no fb bound */
|
||||
if (!atomic && !crtc->fb) {
|
||||
NV_DEBUG_KMS(dev, "No FB bound\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* If atomic, we want to switch to the fb we were passed, so
|
||||
* now we update pointers to do that. (We don't pin; just
|
||||
* assume we're already pinned and update the base address.)
|
||||
@@ -794,6 +803,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
|
||||
drm_fb = passed_fb;
|
||||
fb = nouveau_framebuffer(passed_fb);
|
||||
} else {
|
||||
drm_fb = crtc->fb;
|
||||
fb = nouveau_framebuffer(crtc->fb);
|
||||
/* If not atomic, we can go ahead and pin, and unpin the
|
||||
* old fb we were passed.
|
||||
*/
|
||||
|
||||
@@ -126,27 +126,6 @@ nv04_display_create(struct drm_device *dev)
|
||||
|
||||
nouveau_hw_save_vga_fonts(dev, 1);
|
||||
|
||||
drm_mode_config_init(dev);
|
||||
drm_mode_create_scaling_mode_property(dev);
|
||||
drm_mode_create_dithering_property(dev);
|
||||
|
||||
dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
|
||||
|
||||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
switch (dev_priv->card_type) {
|
||||
case NV_04:
|
||||
dev->mode_config.max_width = 2048;
|
||||
dev->mode_config.max_height = 2048;
|
||||
break;
|
||||
default:
|
||||
dev->mode_config.max_width = 4096;
|
||||
dev->mode_config.max_height = 4096;
|
||||
break;
|
||||
}
|
||||
|
||||
dev->mode_config.fb_base = dev_priv->fb_phys;
|
||||
|
||||
nv04_crtc_create(dev, 0);
|
||||
if (nv_two_heads(dev))
|
||||
nv04_crtc_create(dev, 1);
|
||||
@@ -235,8 +214,6 @@ nv04_display_destroy(struct drm_device *dev)
|
||||
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
|
||||
crtc->funcs->restore(crtc);
|
||||
|
||||
drm_mode_config_cleanup(dev);
|
||||
|
||||
nouveau_hw_save_vga_fonts(dev, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ void
|
||||
nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
|
||||
struct nv04_pm_state *state = pre_state;
|
||||
u32 reg = state->pll.reg;
|
||||
|
||||
@@ -85,6 +86,9 @@ nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
|
||||
nv_mask(dev, 0x1002c0, 0, 1 << 8);
|
||||
}
|
||||
|
||||
if (reg == NV_PRAMDAC_NVPLL_COEFF)
|
||||
ptimer->init(dev);
|
||||
|
||||
kfree(state);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,43 +6,75 @@
|
||||
int
|
||||
nv04_timer_init(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
u32 m, n, d;
|
||||
|
||||
nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
|
||||
nv_wr32(dev, NV04_PTIMER_INTR_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);
|
||||
/* aim for 31.25MHz, which gives us nanosecond timestamps */
|
||||
d = 1000000 / 32;
|
||||
|
||||
/* determine base clock for timer source */
|
||||
if (dev_priv->chipset < 0x40) {
|
||||
n = dev_priv->engine.pm.clock_get(dev, PLL_CORE);
|
||||
} else
|
||||
if (dev_priv->chipset == 0x40) {
|
||||
/*XXX: figure this out */
|
||||
n = 0;
|
||||
} else {
|
||||
n = dev_priv->crystal;
|
||||
m = 1;
|
||||
while (n < (d * 2)) {
|
||||
n += (n / m);
|
||||
m++;
|
||||
}
|
||||
|
||||
nv_wr32(dev, 0x009220, m - 1);
|
||||
}
|
||||
|
||||
if (!n) {
|
||||
NV_WARN(dev, "PTIMER: unknown input clock freq\n");
|
||||
if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
|
||||
!nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
|
||||
nv_wr32(dev, NV04_PTIMER_NUMERATOR, 1);
|
||||
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* reduce ratio to acceptable values */
|
||||
while (((n % 5) == 0) && ((d % 5) == 0)) {
|
||||
n /= 5;
|
||||
d /= 5;
|
||||
}
|
||||
|
||||
while (((n % 2) == 0) && ((d % 2) == 0)) {
|
||||
n /= 2;
|
||||
d /= 2;
|
||||
}
|
||||
|
||||
while (n > 0xffff || d > 0xffff) {
|
||||
n >>= 1;
|
||||
d >>= 1;
|
||||
}
|
||||
|
||||
nv_wr32(dev, NV04_PTIMER_NUMERATOR, n);
|
||||
nv_wr32(dev, NV04_PTIMER_DENOMINATOR, d);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
u64
|
||||
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;
|
||||
u32 hi, lo;
|
||||
|
||||
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;
|
||||
hi = nv_rd32(dev, NV04_PTIMER_TIME_1);
|
||||
lo = nv_rd32(dev, NV04_PTIMER_TIME_0);
|
||||
} while (hi != nv_rd32(dev, NV04_PTIMER_TIME_1));
|
||||
|
||||
return ((u64)hi << 32 | lo);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -26,10 +26,32 @@
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_ramht.h"
|
||||
|
||||
struct nv40_mpeg_engine {
|
||||
struct nv31_mpeg_engine {
|
||||
struct nouveau_exec_engine base;
|
||||
atomic_t refcount;
|
||||
};
|
||||
|
||||
|
||||
static int
|
||||
nv31_mpeg_context_new(struct nouveau_channel *chan, int engine)
|
||||
{
|
||||
struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine);
|
||||
|
||||
if (!atomic_add_unless(&pmpeg->refcount, 1, 1))
|
||||
return -EBUSY;
|
||||
|
||||
chan->engctx[engine] = (void *)0xdeadcafe;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
nv31_mpeg_context_del(struct nouveau_channel *chan, int engine)
|
||||
{
|
||||
struct nv31_mpeg_engine *pmpeg = nv_engine(chan->dev, engine);
|
||||
atomic_dec(&pmpeg->refcount);
|
||||
chan->engctx[engine] = NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_mpeg_context_new(struct nouveau_channel *chan, int engine)
|
||||
{
|
||||
@@ -81,7 +103,7 @@ nv40_mpeg_context_del(struct nouveau_channel *chan, int engine)
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_mpeg_object_new(struct nouveau_channel *chan, int engine,
|
||||
nv31_mpeg_object_new(struct nouveau_channel *chan, int engine,
|
||||
u32 handle, u16 class)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
@@ -103,10 +125,10 @@ nv40_mpeg_object_new(struct nouveau_channel *chan, int engine,
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_mpeg_init(struct drm_device *dev, int engine)
|
||||
nv31_mpeg_init(struct drm_device *dev, int engine)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine);
|
||||
struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
|
||||
int i;
|
||||
|
||||
/* VPE init */
|
||||
@@ -121,7 +143,7 @@ nv40_mpeg_init(struct drm_device *dev, int engine)
|
||||
/* PMPEG init */
|
||||
nv_wr32(dev, 0x00b32c, 0x00000000);
|
||||
nv_wr32(dev, 0x00b314, 0x00000100);
|
||||
nv_wr32(dev, 0x00b220, 0x00000044);
|
||||
nv_wr32(dev, 0x00b220, nv44_graph_class(dev) ? 0x00000044 : 0x00000031);
|
||||
nv_wr32(dev, 0x00b300, 0x02001ec1);
|
||||
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000001);
|
||||
|
||||
@@ -137,7 +159,7 @@ nv40_mpeg_init(struct drm_device *dev, int engine)
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
|
||||
nv31_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
|
||||
{
|
||||
/*XXX: context save? */
|
||||
nv_mask(dev, 0x00b32c, 0x00000001, 0x00000000);
|
||||
@@ -146,7 +168,7 @@ nv40_mpeg_fini(struct drm_device *dev, int engine, bool suspend)
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
|
||||
nv31_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
|
||||
{
|
||||
struct drm_device *dev = chan->dev;
|
||||
u32 inst = data << 4;
|
||||
@@ -184,13 +206,17 @@ nv40_mpeg_mthd_dma(struct nouveau_channel *chan, u32 class, u32 mthd, u32 data)
|
||||
}
|
||||
|
||||
static int
|
||||
nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst)
|
||||
nv31_mpeg_isr_chid(struct drm_device *dev, u32 inst)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_gpuobj *ctx;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
/* hardcode drm channel id on nv3x, so swmthd lookup works */
|
||||
if (dev_priv->card_type < NV_40)
|
||||
return 0;
|
||||
|
||||
spin_lock_irqsave(&dev_priv->channels.lock, flags);
|
||||
for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
|
||||
if (!dev_priv->channels.ptr[i])
|
||||
@@ -205,7 +231,7 @@ nv40_mpeg_isr_chid(struct drm_device *dev, u32 inst)
|
||||
}
|
||||
|
||||
static void
|
||||
nv40_vpe_set_tile_region(struct drm_device *dev, int i)
|
||||
nv31_vpe_set_tile_region(struct drm_device *dev, int i)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
|
||||
@@ -216,10 +242,10 @@ nv40_vpe_set_tile_region(struct drm_device *dev, int i)
|
||||
}
|
||||
|
||||
static void
|
||||
nv40_mpeg_isr(struct drm_device *dev)
|
||||
nv31_mpeg_isr(struct drm_device *dev)
|
||||
{
|
||||
u32 inst = (nv_rd32(dev, 0x00b318) & 0x000fffff) << 4;
|
||||
u32 chid = nv40_mpeg_isr_chid(dev, inst);
|
||||
u32 chid = nv31_mpeg_isr_chid(dev, inst);
|
||||
u32 stat = nv_rd32(dev, 0x00b100);
|
||||
u32 type = nv_rd32(dev, 0x00b230);
|
||||
u32 mthd = nv_rd32(dev, 0x00b234);
|
||||
@@ -249,10 +275,10 @@ nv40_mpeg_isr(struct drm_device *dev)
|
||||
}
|
||||
|
||||
static void
|
||||
nv40_vpe_isr(struct drm_device *dev)
|
||||
nv31_vpe_isr(struct drm_device *dev)
|
||||
{
|
||||
if (nv_rd32(dev, 0x00b100))
|
||||
nv40_mpeg_isr(dev);
|
||||
nv31_mpeg_isr(dev);
|
||||
|
||||
if (nv_rd32(dev, 0x00b800)) {
|
||||
u32 stat = nv_rd32(dev, 0x00b800);
|
||||
@@ -262,9 +288,9 @@ nv40_vpe_isr(struct drm_device *dev)
|
||||
}
|
||||
|
||||
static void
|
||||
nv40_mpeg_destroy(struct drm_device *dev, int engine)
|
||||
nv31_mpeg_destroy(struct drm_device *dev, int engine)
|
||||
{
|
||||
struct nv40_mpeg_engine *pmpeg = nv_engine(dev, engine);
|
||||
struct nv31_mpeg_engine *pmpeg = nv_engine(dev, engine);
|
||||
|
||||
nouveau_irq_unregister(dev, 0);
|
||||
|
||||
@@ -273,34 +299,41 @@ nv40_mpeg_destroy(struct drm_device *dev, int engine)
|
||||
}
|
||||
|
||||
int
|
||||
nv40_mpeg_create(struct drm_device *dev)
|
||||
nv31_mpeg_create(struct drm_device *dev)
|
||||
{
|
||||
struct nv40_mpeg_engine *pmpeg;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nv31_mpeg_engine *pmpeg;
|
||||
|
||||
pmpeg = kzalloc(sizeof(*pmpeg), GFP_KERNEL);
|
||||
if (!pmpeg)
|
||||
return -ENOMEM;
|
||||
atomic_set(&pmpeg->refcount, 0);
|
||||
|
||||
pmpeg->base.destroy = nv40_mpeg_destroy;
|
||||
pmpeg->base.init = nv40_mpeg_init;
|
||||
pmpeg->base.fini = nv40_mpeg_fini;
|
||||
pmpeg->base.context_new = nv40_mpeg_context_new;
|
||||
pmpeg->base.context_del = nv40_mpeg_context_del;
|
||||
pmpeg->base.object_new = nv40_mpeg_object_new;
|
||||
pmpeg->base.destroy = nv31_mpeg_destroy;
|
||||
pmpeg->base.init = nv31_mpeg_init;
|
||||
pmpeg->base.fini = nv31_mpeg_fini;
|
||||
if (dev_priv->card_type < NV_40) {
|
||||
pmpeg->base.context_new = nv31_mpeg_context_new;
|
||||
pmpeg->base.context_del = nv31_mpeg_context_del;
|
||||
} else {
|
||||
pmpeg->base.context_new = nv40_mpeg_context_new;
|
||||
pmpeg->base.context_del = nv40_mpeg_context_del;
|
||||
}
|
||||
pmpeg->base.object_new = nv31_mpeg_object_new;
|
||||
|
||||
/* ISR vector, PMC_ENABLE bit, and TILE regs are shared between
|
||||
* all VPE engines, for this driver's purposes the PMPEG engine
|
||||
* will be treated as the "master" and handle the global VPE
|
||||
* bits too
|
||||
*/
|
||||
pmpeg->base.set_tile_region = nv40_vpe_set_tile_region;
|
||||
nouveau_irq_register(dev, 0, nv40_vpe_isr);
|
||||
pmpeg->base.set_tile_region = nv31_vpe_set_tile_region;
|
||||
nouveau_irq_register(dev, 0, nv31_vpe_isr);
|
||||
|
||||
NVOBJ_ENGINE_ADD(dev, MPEG, &pmpeg->base);
|
||||
NVOBJ_CLASS(dev, 0x3174, MPEG);
|
||||
NVOBJ_MTHD (dev, 0x3174, 0x0190, nv40_mpeg_mthd_dma);
|
||||
NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv40_mpeg_mthd_dma);
|
||||
NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv40_mpeg_mthd_dma);
|
||||
NVOBJ_MTHD (dev, 0x3174, 0x0190, nv31_mpeg_mthd_dma);
|
||||
NVOBJ_MTHD (dev, 0x3174, 0x01a0, nv31_mpeg_mthd_dma);
|
||||
NVOBJ_MTHD (dev, 0x3174, 0x01b0, nv31_mpeg_mthd_dma);
|
||||
|
||||
#if 0
|
||||
NVOBJ_ENGINE_ADD(dev, ME, &pme->base);
|
||||
338
drivers/gpu/drm/nouveau/nv40_pm.c
Normal file
338
drivers/gpu/drm/nouveau/nv40_pm.c
Normal file
@@ -0,0 +1,338 @@
|
||||
/*
|
||||
* Copyright 2011 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_bios.h"
|
||||
#include "nouveau_pm.h"
|
||||
#include "nouveau_hw.h"
|
||||
|
||||
#define min2(a,b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
static u32
|
||||
read_pll_1(struct drm_device *dev, u32 reg)
|
||||
{
|
||||
u32 ctrl = nv_rd32(dev, reg + 0x00);
|
||||
int P = (ctrl & 0x00070000) >> 16;
|
||||
int N = (ctrl & 0x0000ff00) >> 8;
|
||||
int M = (ctrl & 0x000000ff) >> 0;
|
||||
u32 ref = 27000, clk = 0;
|
||||
|
||||
if (ctrl & 0x80000000)
|
||||
clk = ref * N / M;
|
||||
|
||||
return clk >> P;
|
||||
}
|
||||
|
||||
static u32
|
||||
read_pll_2(struct drm_device *dev, u32 reg)
|
||||
{
|
||||
u32 ctrl = nv_rd32(dev, reg + 0x00);
|
||||
u32 coef = nv_rd32(dev, reg + 0x04);
|
||||
int N2 = (coef & 0xff000000) >> 24;
|
||||
int M2 = (coef & 0x00ff0000) >> 16;
|
||||
int N1 = (coef & 0x0000ff00) >> 8;
|
||||
int M1 = (coef & 0x000000ff) >> 0;
|
||||
int P = (ctrl & 0x00070000) >> 16;
|
||||
u32 ref = 27000, clk = 0;
|
||||
|
||||
if (ctrl & 0x80000000)
|
||||
clk = ref * N1 / M1;
|
||||
|
||||
if (!(ctrl & 0x00000100)) {
|
||||
if (ctrl & 0x40000000)
|
||||
clk = clk * N2 / M2;
|
||||
}
|
||||
|
||||
return clk >> P;
|
||||
}
|
||||
|
||||
static u32
|
||||
read_clk(struct drm_device *dev, u32 src)
|
||||
{
|
||||
switch (src) {
|
||||
case 3:
|
||||
return read_pll_2(dev, 0x004000);
|
||||
case 2:
|
||||
return read_pll_1(dev, 0x004008);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv40_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
{
|
||||
u32 ctrl = nv_rd32(dev, 0x00c040);
|
||||
|
||||
perflvl->core = read_clk(dev, (ctrl & 0x00000003) >> 0);
|
||||
perflvl->shader = read_clk(dev, (ctrl & 0x00000030) >> 4);
|
||||
perflvl->memory = read_pll_2(dev, 0x4020);
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nv40_pm_state {
|
||||
u32 ctrl;
|
||||
u32 npll_ctrl;
|
||||
u32 npll_coef;
|
||||
u32 spll;
|
||||
u32 mpll_ctrl;
|
||||
u32 mpll_coef;
|
||||
};
|
||||
|
||||
static int
|
||||
nv40_calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
|
||||
u32 clk, int *N1, int *M1, int *N2, int *M2, int *log2P)
|
||||
{
|
||||
struct nouveau_pll_vals coef;
|
||||
int ret;
|
||||
|
||||
ret = get_pll_limits(dev, reg, pll);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (clk < pll->vco1.maxfreq)
|
||||
pll->vco2.maxfreq = 0;
|
||||
|
||||
ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef);
|
||||
if (ret == 0)
|
||||
return -ERANGE;
|
||||
|
||||
*N1 = coef.N1;
|
||||
*M1 = coef.M1;
|
||||
if (N2 && M2) {
|
||||
if (pll->vco2.maxfreq) {
|
||||
*N2 = coef.N2;
|
||||
*M2 = coef.M2;
|
||||
} else {
|
||||
*N2 = 1;
|
||||
*M2 = 1;
|
||||
}
|
||||
}
|
||||
*log2P = coef.log2P;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *
|
||||
nv40_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
{
|
||||
struct nv40_pm_state *info;
|
||||
struct pll_lims pll;
|
||||
int N1, N2, M1, M2, log2P;
|
||||
int ret;
|
||||
|
||||
info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
/* core/geometric clock */
|
||||
ret = nv40_calc_pll(dev, 0x004000, &pll, perflvl->core,
|
||||
&N1, &M1, &N2, &M2, &log2P);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (N2 == M2) {
|
||||
info->npll_ctrl = 0x80000100 | (log2P << 16);
|
||||
info->npll_coef = (N1 << 8) | M1;
|
||||
} else {
|
||||
info->npll_ctrl = 0xc0000000 | (log2P << 16);
|
||||
info->npll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
|
||||
}
|
||||
|
||||
/* use the second PLL for shader/rop clock, if it differs from core */
|
||||
if (perflvl->shader && perflvl->shader != perflvl->core) {
|
||||
ret = nv40_calc_pll(dev, 0x004008, &pll, perflvl->shader,
|
||||
&N1, &M1, NULL, NULL, &log2P);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
info->spll = 0xc0000000 | (log2P << 16) | (N1 << 8) | M1;
|
||||
info->ctrl = 0x00000223;
|
||||
} else {
|
||||
info->spll = 0x00000000;
|
||||
info->ctrl = 0x00000333;
|
||||
}
|
||||
|
||||
/* memory clock */
|
||||
ret = nv40_calc_pll(dev, 0x004020, &pll, perflvl->memory,
|
||||
&N1, &M1, &N2, &M2, &log2P);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
info->mpll_ctrl = 0x80000000 | (log2P << 16);
|
||||
info->mpll_ctrl |= min2(pll.log2p_bias + log2P, pll.max_log2p) << 20;
|
||||
if (N2 == M2) {
|
||||
info->mpll_ctrl |= 0x00000100;
|
||||
info->mpll_coef = (N1 << 8) | M1;
|
||||
} else {
|
||||
info->mpll_ctrl |= 0x40000000;
|
||||
info->mpll_coef = (N2 << 24) | (M2 << 16) | (N1 << 8) | M1;
|
||||
}
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
kfree(info);
|
||||
info = ERR_PTR(ret);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool
|
||||
nv40_pm_gr_idle(void *data)
|
||||
{
|
||||
struct drm_device *dev = data;
|
||||
|
||||
if ((nv_rd32(dev, 0x400760) & 0x000000f0) >> 4 !=
|
||||
(nv_rd32(dev, 0x400760) & 0x0000000f))
|
||||
return false;
|
||||
|
||||
if (nv_rd32(dev, 0x400700))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nv40_pm_state *info = pre_state;
|
||||
unsigned long flags;
|
||||
struct bit_entry M;
|
||||
u32 crtc_mask = 0;
|
||||
u8 sr1[2];
|
||||
int i;
|
||||
|
||||
/* determine which CRTCs are active, fetch VGA_SR1 for each */
|
||||
for (i = 0; i < 2; i++) {
|
||||
u32 vbl = nv_rd32(dev, 0x600808 + (i * 0x2000));
|
||||
u32 cnt = 0;
|
||||
do {
|
||||
if (vbl != nv_rd32(dev, 0x600808 + (i * 0x2000))) {
|
||||
nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
|
||||
sr1[i] = nv_rd08(dev, 0x0c03c5 + (i * 0x2000));
|
||||
if (!(sr1[i] & 0x20))
|
||||
crtc_mask |= (1 << i);
|
||||
break;
|
||||
}
|
||||
udelay(1);
|
||||
} while (cnt++ < 32);
|
||||
}
|
||||
|
||||
/* halt and idle engines */
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
nv_mask(dev, 0x002500, 0x00000001, 0x00000000);
|
||||
if (!nv_wait(dev, 0x002500, 0x00000010, 0x00000000))
|
||||
goto resume;
|
||||
nv_mask(dev, 0x003220, 0x00000001, 0x00000000);
|
||||
if (!nv_wait(dev, 0x003220, 0x00000010, 0x00000000))
|
||||
goto resume;
|
||||
nv_mask(dev, 0x003200, 0x00000001, 0x00000000);
|
||||
nv04_fifo_cache_pull(dev, false);
|
||||
|
||||
if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev))
|
||||
goto resume;
|
||||
|
||||
/* set engine clocks */
|
||||
nv_mask(dev, 0x00c040, 0x00000333, 0x00000000);
|
||||
nv_wr32(dev, 0x004004, info->npll_coef);
|
||||
nv_mask(dev, 0x004000, 0xc0070100, info->npll_ctrl);
|
||||
nv_mask(dev, 0x004008, 0xc007ffff, info->spll);
|
||||
mdelay(5);
|
||||
nv_mask(dev, 0x00c040, 0x00000333, info->ctrl);
|
||||
|
||||
/* wait for vblank start on active crtcs, disable memory access */
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!(crtc_mask & (1 << i)))
|
||||
continue;
|
||||
nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00000000);
|
||||
nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
|
||||
nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
|
||||
nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i] | 0x20);
|
||||
}
|
||||
|
||||
/* prepare ram for reclocking */
|
||||
nv_wr32(dev, 0x1002d4, 0x00000001); /* precharge */
|
||||
nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */
|
||||
nv_wr32(dev, 0x1002d0, 0x00000001); /* refresh */
|
||||
nv_mask(dev, 0x100210, 0x80000000, 0x00000000); /* no auto refresh */
|
||||
nv_wr32(dev, 0x1002dc, 0x00000001); /* enable self-refresh */
|
||||
|
||||
/* change the PLL of each memory partition */
|
||||
nv_mask(dev, 0x00c040, 0x0000c000, 0x00000000);
|
||||
switch (dev_priv->chipset) {
|
||||
case 0x40:
|
||||
case 0x45:
|
||||
case 0x41:
|
||||
case 0x42:
|
||||
case 0x47:
|
||||
nv_mask(dev, 0x004044, 0xc0771100, info->mpll_ctrl);
|
||||
nv_mask(dev, 0x00402c, 0xc0771100, info->mpll_ctrl);
|
||||
nv_wr32(dev, 0x004048, info->mpll_coef);
|
||||
nv_wr32(dev, 0x004030, info->mpll_coef);
|
||||
case 0x43:
|
||||
case 0x49:
|
||||
case 0x4b:
|
||||
nv_mask(dev, 0x004038, 0xc0771100, info->mpll_ctrl);
|
||||
nv_wr32(dev, 0x00403c, info->mpll_coef);
|
||||
default:
|
||||
nv_mask(dev, 0x004020, 0xc0771100, info->mpll_ctrl);
|
||||
nv_wr32(dev, 0x004024, info->mpll_coef);
|
||||
break;
|
||||
}
|
||||
udelay(100);
|
||||
nv_mask(dev, 0x00c040, 0x0000c000, 0x0000c000);
|
||||
|
||||
/* re-enable normal operation of memory controller */
|
||||
nv_wr32(dev, 0x1002dc, 0x00000000);
|
||||
nv_mask(dev, 0x100210, 0x80000000, 0x80000000);
|
||||
udelay(100);
|
||||
|
||||
/* execute memory reset script from vbios */
|
||||
if (!bit_table(dev, 'M', &M))
|
||||
nouveau_bios_init_exec(dev, ROM16(M.data[0]));
|
||||
|
||||
/* make sure we're in vblank (hopefully the same one as before), and
|
||||
* then re-enable crtc memory access
|
||||
*/
|
||||
for (i = 0; i < 2; i++) {
|
||||
if (!(crtc_mask & (1 << i)))
|
||||
continue;
|
||||
nv_wait(dev, 0x600808 + (i * 0x2000), 0x00010000, 0x00010000);
|
||||
nv_wr08(dev, 0x0c03c4 + (i * 0x2000), 0x01);
|
||||
nv_wr08(dev, 0x0c03c5 + (i * 0x2000), sr1[i]);
|
||||
}
|
||||
|
||||
/* resume engines */
|
||||
resume:
|
||||
nv_wr32(dev, 0x003250, 0x00000001);
|
||||
nv_mask(dev, 0x003220, 0x00000001, 0x00000001);
|
||||
nv_wr32(dev, 0x003200, 0x00000001);
|
||||
nv_wr32(dev, 0x002500, 0x00000001);
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
|
||||
kfree(info);
|
||||
}
|
||||
@@ -329,8 +329,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
|
||||
|
||||
drm_crtc_cleanup(&nv_crtc->base);
|
||||
|
||||
nv50_cursor_fini(nv_crtc);
|
||||
|
||||
nouveau_bo_unmap(nv_crtc->lut.nvbo);
|
||||
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
|
||||
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
|
||||
@@ -519,12 +517,18 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
|
||||
struct drm_device *dev = nv_crtc->base.dev;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_channel *evo = nv50_display(dev)->master;
|
||||
struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
|
||||
struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
|
||||
struct drm_framebuffer *drm_fb;
|
||||
struct nouveau_framebuffer *fb;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
||||
|
||||
/* no fb bound */
|
||||
if (!atomic && !crtc->fb) {
|
||||
NV_DEBUG_KMS(dev, "No FB bound\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If atomic, we want to switch to the fb we were passed, so
|
||||
* now we update pointers to do that. (We don't pin; just
|
||||
* assume we're already pinned and update the base address.)
|
||||
@@ -533,6 +537,8 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
|
||||
drm_fb = passed_fb;
|
||||
fb = nouveau_framebuffer(passed_fb);
|
||||
} else {
|
||||
drm_fb = crtc->fb;
|
||||
fb = nouveau_framebuffer(crtc->fb);
|
||||
/* If not atomic, we can go ahead and pin, and unpin the
|
||||
* old fb we were passed.
|
||||
*/
|
||||
|
||||
@@ -137,21 +137,3 @@ nv50_cursor_init(struct nouveau_crtc *nv_crtc)
|
||||
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_KMS(dev, "\n");
|
||||
|
||||
nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
|
||||
if (!nv_wait(dev, 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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -247,6 +247,16 @@ static int nv50_display_disable(struct drm_device *dev)
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0);
|
||||
if (!nv_wait(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
|
||||
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(i)));
|
||||
}
|
||||
}
|
||||
|
||||
nv50_evo_fini(dev);
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
@@ -286,23 +296,6 @@ int nv50_display_create(struct drm_device *dev)
|
||||
return -ENOMEM;
|
||||
dev_priv->engine.display.priv = priv;
|
||||
|
||||
/* init basic kernel modesetting */
|
||||
drm_mode_config_init(dev);
|
||||
|
||||
/* Initialise some optional connector properties. */
|
||||
drm_mode_create_scaling_mode_property(dev);
|
||||
drm_mode_create_dithering_property(dev);
|
||||
|
||||
dev->mode_config.min_width = 0;
|
||||
dev->mode_config.min_height = 0;
|
||||
|
||||
dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
|
||||
|
||||
dev->mode_config.max_width = 8192;
|
||||
dev->mode_config.max_height = 8192;
|
||||
|
||||
dev->mode_config.fb_base = dev_priv->fb_phys;
|
||||
|
||||
/* Create CRTC objects */
|
||||
for (i = 0; i < 2; i++)
|
||||
nv50_crtc_create(dev, i);
|
||||
@@ -364,8 +357,6 @@ nv50_display_destroy(struct drm_device *dev)
|
||||
|
||||
NV_DEBUG_KMS(dev, "\n");
|
||||
|
||||
drm_mode_config_cleanup(dev);
|
||||
|
||||
nv50_display_disable(dev);
|
||||
nouveau_irq_unregister(dev, 26);
|
||||
kfree(disp);
|
||||
@@ -698,7 +689,7 @@ nv50_display_unk10_handler(struct drm_device *dev)
|
||||
struct dcb_entry *dcb = &dev_priv->vbios.dcb.entry[i];
|
||||
|
||||
if (dcb->type == type && (dcb->or & (1 << or))) {
|
||||
nouveau_bios_run_display_table(dev, dcb, 0, -1);
|
||||
nouveau_bios_run_display_table(dev, 0, -1, dcb, -1);
|
||||
disp->irq.dcb = dcb;
|
||||
goto ack;
|
||||
}
|
||||
@@ -710,37 +701,6 @@ nv50_display_unk10_handler(struct drm_device *dev)
|
||||
nv_wr32(dev, 0x610030, 0x80000000);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_unk20_dp_hack(struct drm_device *dev, struct dcb_entry *dcb)
|
||||
{
|
||||
int or = ffs(dcb->or) - 1, link = !(dcb->dpconf.sor.link & 1);
|
||||
struct drm_encoder *encoder;
|
||||
uint32_t tmp, unk0 = 0, unk1 = 0;
|
||||
|
||||
if (dcb->type != OUTPUT_DP)
|
||||
return;
|
||||
|
||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
|
||||
if (nv_encoder->dcb == dcb) {
|
||||
unk0 = nv_encoder->dp.unk0;
|
||||
unk1 = nv_encoder->dp.unk1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (unk0 || unk1) {
|
||||
tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
|
||||
tmp &= 0xfffffe03;
|
||||
nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp | unk0);
|
||||
|
||||
tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
|
||||
tmp &= 0xfef080c0;
|
||||
nv_wr32(dev, NV50_SOR_DP_UNK128(or, link), tmp | unk1);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_display_unk20_handler(struct drm_device *dev)
|
||||
{
|
||||
@@ -753,7 +713,7 @@ nv50_display_unk20_handler(struct drm_device *dev)
|
||||
NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
|
||||
dcb = disp->irq.dcb;
|
||||
if (dcb) {
|
||||
nouveau_bios_run_display_table(dev, dcb, 0, -2);
|
||||
nouveau_bios_run_display_table(dev, 0, -2, dcb, -1);
|
||||
disp->irq.dcb = NULL;
|
||||
}
|
||||
|
||||
@@ -837,9 +797,15 @@ nv50_display_unk20_handler(struct drm_device *dev)
|
||||
}
|
||||
|
||||
script = nv50_display_script_select(dev, dcb, mc, pclk);
|
||||
nouveau_bios_run_display_table(dev, dcb, script, pclk);
|
||||
nouveau_bios_run_display_table(dev, script, pclk, dcb, -1);
|
||||
|
||||
nv50_display_unk20_dp_hack(dev, dcb);
|
||||
if (type == OUTPUT_DP) {
|
||||
int link = !(dcb->dpconf.sor.link & 1);
|
||||
if ((mc & 0x000f0000) == 0x00020000)
|
||||
nouveau_dp_tu_update(dev, or, link, pclk, 18);
|
||||
else
|
||||
nouveau_dp_tu_update(dev, or, link, pclk, 24);
|
||||
}
|
||||
|
||||
if (dcb->type != OUTPUT_ANALOG) {
|
||||
tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
|
||||
@@ -904,7 +870,7 @@ nv50_display_unk40_handler(struct drm_device *dev)
|
||||
if (!dcb)
|
||||
goto ack;
|
||||
|
||||
nouveau_bios_run_display_table(dev, dcb, script, -pclk);
|
||||
nouveau_bios_run_display_table(dev, script, -pclk, dcb, -1);
|
||||
nv50_display_unk40_dp_set_tmds(dev, dcb);
|
||||
|
||||
ack:
|
||||
|
||||
@@ -97,6 +97,37 @@ nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nvd0_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
|
||||
{
|
||||
struct dcb_gpio_entry *gpio;
|
||||
u32 v;
|
||||
|
||||
gpio = nouveau_bios_gpio_entry(dev, tag);
|
||||
if (!gpio)
|
||||
return -ENOENT;
|
||||
|
||||
v = nv_rd32(dev, 0x00d610 + (gpio->line * 4));
|
||||
v &= 0x00004000;
|
||||
return (!!v == (gpio->state[1] & 1));
|
||||
}
|
||||
|
||||
int
|
||||
nvd0_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
|
||||
{
|
||||
struct dcb_gpio_entry *gpio;
|
||||
u32 v;
|
||||
|
||||
gpio = nouveau_bios_gpio_entry(dev, tag);
|
||||
if (!gpio)
|
||||
return -ENOENT;
|
||||
|
||||
v = gpio->state[state] ^ 2;
|
||||
|
||||
nv_mask(dev, 0x00d610 + (gpio->line * 4), 0x00003000, v << 12);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nv50_gpio_irq_register(struct drm_device *dev, enum dcb_gpio_tag tag,
|
||||
void (*handler)(void *, int), void *data)
|
||||
|
||||
@@ -120,70 +120,62 @@ nv50_graph_unload_context(struct drm_device *dev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)
|
||||
static int
|
||||
nv50_graph_init(struct drm_device *dev, int engine)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
uint32_t units = nv_rd32(dev, 0x1540);
|
||||
struct nv50_graph_engine *pgraph = nv_engine(dev, engine);
|
||||
u32 units = nv_rd32(dev, 0x001540);
|
||||
int i;
|
||||
|
||||
NV_DEBUG(dev, "\n");
|
||||
|
||||
/* master reset */
|
||||
nv_mask(dev, 0x000200, 0x00200100, 0x00000000);
|
||||
nv_mask(dev, 0x000200, 0x00200100, 0x00200100);
|
||||
nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */
|
||||
|
||||
/* reset/enable traps and interrupts */
|
||||
nv_wr32(dev, 0x400804, 0xc0000000);
|
||||
nv_wr32(dev, 0x406800, 0xc0000000);
|
||||
nv_wr32(dev, 0x400c04, 0xc0000000);
|
||||
nv_wr32(dev, 0x401800, 0xc0000000);
|
||||
nv_wr32(dev, 0x405018, 0xc0000000);
|
||||
nv_wr32(dev, 0x402000, 0xc0000000);
|
||||
|
||||
for (i = 0; i < 16; i++) {
|
||||
if (units & 1 << i) {
|
||||
if (dev_priv->chipset < 0xa0) {
|
||||
nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000);
|
||||
nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000);
|
||||
nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000);
|
||||
} else {
|
||||
nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000);
|
||||
nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000);
|
||||
nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000);
|
||||
}
|
||||
if (!(units & (1 << i)))
|
||||
continue;
|
||||
|
||||
if (dev_priv->chipset < 0xa0) {
|
||||
nv_wr32(dev, 0x408900 + (i << 12), 0xc0000000);
|
||||
nv_wr32(dev, 0x408e08 + (i << 12), 0xc0000000);
|
||||
nv_wr32(dev, 0x408314 + (i << 12), 0xc0000000);
|
||||
} else {
|
||||
nv_wr32(dev, 0x408600 + (i << 11), 0xc0000000);
|
||||
nv_wr32(dev, 0x408708 + (i << 11), 0xc0000000);
|
||||
nv_wr32(dev, 0x40831c + (i << 11), 0xc0000000);
|
||||
}
|
||||
}
|
||||
|
||||
nv_wr32(dev, 0x400108, 0xffffffff);
|
||||
|
||||
nv_wr32(dev, 0x400824, 0x00004000);
|
||||
nv_wr32(dev, 0x400138, 0xffffffff);
|
||||
nv_wr32(dev, 0x400100, 0xffffffff);
|
||||
nv_wr32(dev, 0x40013c, 0xffffffff);
|
||||
nv_wr32(dev, 0x400500, 0x00010001);
|
||||
}
|
||||
|
||||
static void
|
||||
nv50_graph_init_zcull(struct drm_device *dev)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
int i;
|
||||
|
||||
NV_DEBUG(dev, "\n");
|
||||
/* upload context program, initialise ctxctl defaults */
|
||||
nv_wr32(dev, 0x400324, 0x00000000);
|
||||
for (i = 0; i < pgraph->ctxprog_size; i++)
|
||||
nv_wr32(dev, 0x400328, pgraph->ctxprog[i]);
|
||||
nv_wr32(dev, 0x400824, 0x00000000);
|
||||
nv_wr32(dev, 0x400828, 0x00000000);
|
||||
nv_wr32(dev, 0x40082c, 0x00000000);
|
||||
nv_wr32(dev, 0x400830, 0x00000000);
|
||||
nv_wr32(dev, 0x400724, 0x00000000);
|
||||
nv_wr32(dev, 0x40032c, 0x00000000);
|
||||
nv_wr32(dev, 0x400320, 4); /* CTXCTL_CMD = NEWCTXDMA */
|
||||
|
||||
/* some unknown zcull magic */
|
||||
switch (dev_priv->chipset & 0xf0) {
|
||||
case 0x50:
|
||||
case 0x80:
|
||||
@@ -212,43 +204,7 @@ nv50_graph_init_zcull(struct drm_device *dev)
|
||||
nv_wr32(dev, 0x402c28 + (i * 8), 0x00000000);
|
||||
nv_wr32(dev, 0x402c2c + (i * 8), 0x00000000);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_graph_init_ctxctl(struct drm_device *dev)
|
||||
{
|
||||
struct nv50_graph_engine *pgraph = nv_engine(dev, NVOBJ_ENGINE_GR);
|
||||
int i;
|
||||
|
||||
NV_DEBUG(dev, "\n");
|
||||
|
||||
nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
|
||||
for (i = 0; i < pgraph->ctxprog_size; i++)
|
||||
nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, pgraph->ctxprog[i]);
|
||||
|
||||
nv_wr32(dev, 0x40008c, 0x00000004); /* HW_CTX_SWITCH_ENABLED */
|
||||
nv_wr32(dev, 0x400320, 4);
|
||||
nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
|
||||
nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
nv50_graph_init(struct drm_device *dev, int engine)
|
||||
{
|
||||
int ret;
|
||||
|
||||
NV_DEBUG(dev, "\n");
|
||||
|
||||
nv50_graph_init_reset(dev);
|
||||
nv50_graph_init_regs__nv(dev);
|
||||
nv50_graph_init_zcull(dev);
|
||||
|
||||
ret = nv50_graph_init_ctxctl(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
nv50_graph_init_intr(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
#define CP_FLAG_UNK0B ((0 * 32) + 0xb)
|
||||
#define CP_FLAG_UNK0B_CLEAR 0
|
||||
#define CP_FLAG_UNK0B_SET 1
|
||||
#define CP_FLAG_XFER_SWITCH ((0 * 32) + 0xe)
|
||||
#define CP_FLAG_XFER_SWITCH_DISABLE 0
|
||||
#define CP_FLAG_XFER_SWITCH_ENABLE 1
|
||||
#define CP_FLAG_STATE ((0 * 32) + 0x1c)
|
||||
#define CP_FLAG_STATE_STOPPED 0
|
||||
#define CP_FLAG_STATE_RUNNING 1
|
||||
#define CP_FLAG_UNK1D ((0 * 32) + 0x1d)
|
||||
#define CP_FLAG_UNK1D_CLEAR 0
|
||||
#define CP_FLAG_UNK1D_SET 1
|
||||
@@ -194,6 +200,9 @@ nv50_grctx_init(struct nouveau_grctx *ctx)
|
||||
"the devs.\n");
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
cp_set (ctx, STATE, RUNNING);
|
||||
cp_set (ctx, XFER_SWITCH, ENABLE);
|
||||
/* decide whether we're loading/unloading the context */
|
||||
cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save);
|
||||
cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save);
|
||||
@@ -260,6 +269,8 @@ nv50_grctx_init(struct nouveau_grctx *ctx)
|
||||
cp_name(ctx, cp_exit);
|
||||
cp_set (ctx, USER_SAVE, NOT_PENDING);
|
||||
cp_set (ctx, USER_LOAD, NOT_PENDING);
|
||||
cp_set (ctx, XFER_SWITCH, DISABLE);
|
||||
cp_set (ctx, STATE, STOPPED);
|
||||
cp_out (ctx, CP_END);
|
||||
ctx->ctxvals_pos += 0x400; /* padding... no idea why you need it */
|
||||
|
||||
|
||||
@@ -115,15 +115,15 @@ nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
|
||||
BIT_M.version == 1 && BIT_M.length >= 0x0b) {
|
||||
script = ROM16(BIT_M.data[0x05]);
|
||||
if (script)
|
||||
nouveau_bios_run_init_table(dev, script, NULL);
|
||||
nouveau_bios_run_init_table(dev, script, NULL, -1);
|
||||
script = ROM16(BIT_M.data[0x07]);
|
||||
if (script)
|
||||
nouveau_bios_run_init_table(dev, script, NULL);
|
||||
nouveau_bios_run_init_table(dev, script, NULL, -1);
|
||||
script = ROM16(BIT_M.data[0x09]);
|
||||
if (script)
|
||||
nouveau_bios_run_init_table(dev, script, NULL);
|
||||
nouveau_bios_run_init_table(dev, script, NULL, -1);
|
||||
|
||||
nouveau_bios_run_init_table(dev, perflvl->memscript, NULL);
|
||||
nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1);
|
||||
}
|
||||
|
||||
if (state->type == PLL_MEMORY) {
|
||||
|
||||
@@ -124,7 +124,7 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
|
||||
if (mode == DRM_MODE_DPMS_ON) {
|
||||
u8 status = DP_SET_POWER_D0;
|
||||
nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
|
||||
nouveau_dp_link_train(encoder);
|
||||
nouveau_dp_link_train(encoder, nv_encoder->dp.datarate);
|
||||
} else {
|
||||
u8 status = DP_SET_POWER_D3;
|
||||
nouveau_dp_auxch(auxch, 8, DP_SET_POWER, &status, 1);
|
||||
@@ -187,14 +187,13 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||
struct drm_device *dev = encoder->dev;
|
||||
struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
|
||||
struct nouveau_connector *nv_connector;
|
||||
uint32_t mode_ctl = 0;
|
||||
int ret;
|
||||
|
||||
NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n",
|
||||
nv_encoder->or, nv_encoder->dcb->type, crtc->index);
|
||||
|
||||
nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
switch (nv_encoder->dcb->type) {
|
||||
case OUTPUT_TMDS:
|
||||
if (nv_encoder->dcb->sorconf.link & 1) {
|
||||
@@ -206,7 +205,15 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
||||
mode_ctl = 0x0200;
|
||||
break;
|
||||
case OUTPUT_DP:
|
||||
mode_ctl |= (nv_encoder->dp.mc_unknown << 16);
|
||||
nv_connector = nouveau_encoder_connector_get(nv_encoder);
|
||||
if (nv_connector && nv_connector->base.display_info.bpc == 6) {
|
||||
nv_encoder->dp.datarate = crtc->mode->clock * 18 / 8;
|
||||
mode_ctl |= 0x00020000;
|
||||
} else {
|
||||
nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8;
|
||||
mode_ctl |= 0x00050000;
|
||||
}
|
||||
|
||||
if (nv_encoder->dcb->sorconf.link & 1)
|
||||
mode_ctl |= 0x00000800;
|
||||
else
|
||||
@@ -227,6 +234,8 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
|
||||
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
|
||||
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
|
||||
|
||||
nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
|
||||
|
||||
ret = RING_SPACE(evo, 2);
|
||||
if (ret) {
|
||||
NV_ERROR(dev, "no space while connecting SOR\n");
|
||||
@@ -313,31 +322,6 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_entry *entry)
|
||||
encoder->possible_crtcs = entry->heads;
|
||||
encoder->possible_clones = 0;
|
||||
|
||||
if (nv_encoder->dcb->type == OUTPUT_DP) {
|
||||
int or = nv_encoder->or, link = !(entry->dpconf.sor.link & 1);
|
||||
uint32_t tmp;
|
||||
|
||||
tmp = nv_rd32(dev, 0x61c700 + (or * 0x800));
|
||||
if (!tmp)
|
||||
tmp = nv_rd32(dev, 0x610798 + (or * 8));
|
||||
|
||||
switch ((tmp & 0x00000f00) >> 8) {
|
||||
case 8:
|
||||
case 9:
|
||||
nv_encoder->dp.mc_unknown = (tmp & 0x000f0000) >> 16;
|
||||
tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
|
||||
nv_encoder->dp.unk0 = tmp & 0x000001fc;
|
||||
tmp = nv_rd32(dev, NV50_SOR_DP_UNK128(or, link));
|
||||
nv_encoder->dp.unk1 = tmp & 0x010f7f3f;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!nv_encoder->dp.mc_unknown)
|
||||
nv_encoder->dp.mc_unknown = 5;
|
||||
}
|
||||
|
||||
drm_mode_connector_attach_encoder(connector, encoder);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ void
|
||||
nv50_vram_del(struct drm_device *dev, struct nouveau_mem **pmem)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_mm *mm = dev_priv->engine.vram.mm;
|
||||
struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
|
||||
struct nouveau_mm_node *this;
|
||||
struct nouveau_mem *mem;
|
||||
|
||||
@@ -82,7 +82,7 @@ nv50_vram_new(struct drm_device *dev, u64 size, u32 align, u32 size_nc,
|
||||
u32 memtype, struct nouveau_mem **pmem)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_mm *mm = dev_priv->engine.vram.mm;
|
||||
struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
|
||||
struct nouveau_mm_node *r;
|
||||
struct nouveau_mem *mem;
|
||||
int comp = (memtype & 0x300) >> 8;
|
||||
|
||||
@@ -27,178 +27,316 @@
|
||||
#include "nouveau_bios.h"
|
||||
#include "nouveau_pm.h"
|
||||
|
||||
/* This is actually a lot more complex than it appears here, but hopefully
|
||||
* this should be able to deal with what the VBIOS leaves for us..
|
||||
*
|
||||
* If not, well, I'll jump off that bridge when I come to it.
|
||||
*/
|
||||
static u32 read_clk(struct drm_device *, int, bool);
|
||||
static u32 read_pll(struct drm_device *, int, u32);
|
||||
|
||||
struct nva3_pm_state {
|
||||
enum pll_types type;
|
||||
u32 src0;
|
||||
u32 src1;
|
||||
u32 ctrl;
|
||||
u32 coef;
|
||||
u32 old_pnm;
|
||||
u32 new_pnm;
|
||||
u32 new_div;
|
||||
static u32
|
||||
read_vco(struct drm_device *dev, int clk)
|
||||
{
|
||||
u32 sctl = nv_rd32(dev, 0x4120 + (clk * 4));
|
||||
if ((sctl & 0x00000030) != 0x00000030)
|
||||
return read_pll(dev, 0x41, 0x00e820);
|
||||
return read_pll(dev, 0x42, 0x00e8a0);
|
||||
}
|
||||
|
||||
static u32
|
||||
read_clk(struct drm_device *dev, int clk, bool ignore_en)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
u32 sctl, sdiv, sclk;
|
||||
|
||||
/* refclk for the 0xe8xx plls is a fixed frequency */
|
||||
if (clk >= 0x40) {
|
||||
if (dev_priv->chipset == 0xaf) {
|
||||
/* no joke.. seriously.. sigh.. */
|
||||
return nv_rd32(dev, 0x00471c) * 1000;
|
||||
}
|
||||
|
||||
return dev_priv->crystal;
|
||||
}
|
||||
|
||||
sctl = nv_rd32(dev, 0x4120 + (clk * 4));
|
||||
if (!ignore_en && !(sctl & 0x00000100))
|
||||
return 0;
|
||||
|
||||
switch (sctl & 0x00003000) {
|
||||
case 0x00000000:
|
||||
return dev_priv->crystal;
|
||||
case 0x00002000:
|
||||
if (sctl & 0x00000040)
|
||||
return 108000;
|
||||
return 100000;
|
||||
case 0x00003000:
|
||||
sclk = read_vco(dev, clk);
|
||||
sdiv = ((sctl & 0x003f0000) >> 16) + 2;
|
||||
return (sclk * 2) / sdiv;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u32
|
||||
read_pll(struct drm_device *dev, int clk, u32 pll)
|
||||
{
|
||||
u32 ctrl = nv_rd32(dev, pll + 0);
|
||||
u32 sclk = 0, P = 1, N = 1, M = 1;
|
||||
|
||||
if (!(ctrl & 0x00000008)) {
|
||||
if (ctrl & 0x00000001) {
|
||||
u32 coef = nv_rd32(dev, pll + 4);
|
||||
M = (coef & 0x000000ff) >> 0;
|
||||
N = (coef & 0x0000ff00) >> 8;
|
||||
P = (coef & 0x003f0000) >> 16;
|
||||
|
||||
/* no post-divider on these.. */
|
||||
if ((pll & 0x00ff00) == 0x00e800)
|
||||
P = 1;
|
||||
|
||||
sclk = read_clk(dev, 0x00 + clk, false);
|
||||
}
|
||||
} else {
|
||||
sclk = read_clk(dev, 0x10 + clk, false);
|
||||
}
|
||||
|
||||
return sclk * N / (M * P);
|
||||
}
|
||||
|
||||
struct creg {
|
||||
u32 clk;
|
||||
u32 pll;
|
||||
};
|
||||
|
||||
static int
|
||||
nva3_pm_pll_offset(u32 id)
|
||||
calc_clk(struct drm_device *dev, int clk, u32 pll, u32 khz, struct creg *reg)
|
||||
{
|
||||
static const u32 pll_map[] = {
|
||||
0x00, PLL_CORE,
|
||||
0x01, PLL_SHADER,
|
||||
0x02, PLL_MEMORY,
|
||||
0x00, 0x00
|
||||
};
|
||||
const u32 *map = pll_map;
|
||||
struct pll_lims limits;
|
||||
u32 oclk, sclk, sdiv;
|
||||
int P, N, M, diff;
|
||||
int ret;
|
||||
|
||||
while (map[1]) {
|
||||
if (id == map[1])
|
||||
return map[0];
|
||||
map += 2;
|
||||
reg->pll = 0;
|
||||
reg->clk = 0;
|
||||
if (!khz) {
|
||||
NV_DEBUG(dev, "no clock for 0x%04x/0x%02x\n", pll, clk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
switch (khz) {
|
||||
case 27000:
|
||||
reg->clk = 0x00000100;
|
||||
return khz;
|
||||
case 100000:
|
||||
reg->clk = 0x00002100;
|
||||
return khz;
|
||||
case 108000:
|
||||
reg->clk = 0x00002140;
|
||||
return khz;
|
||||
default:
|
||||
sclk = read_vco(dev, clk);
|
||||
sdiv = min((sclk * 2) / (khz - 2999), (u32)65);
|
||||
/* if the clock has a PLL attached, and we can get a within
|
||||
* [-2, 3) MHz of a divider, we'll disable the PLL and use
|
||||
* the divider instead.
|
||||
*
|
||||
* divider can go as low as 2, limited here because NVIDIA
|
||||
* and the VBIOS on my NVA8 seem to prefer using the PLL
|
||||
* for 810MHz - is there a good reason?
|
||||
*/
|
||||
if (sdiv > 4) {
|
||||
oclk = (sclk * 2) / sdiv;
|
||||
diff = khz - oclk;
|
||||
if (!pll || (diff >= -2000 && diff < 3000)) {
|
||||
reg->clk = (((sdiv - 2) << 16) | 0x00003100);
|
||||
return oclk;
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
nva3_pm_clock_get(struct drm_device *dev, u32 id)
|
||||
{
|
||||
u32 src0, src1, ctrl, coef;
|
||||
struct pll_lims pll;
|
||||
int ret, off;
|
||||
int P, N, M;
|
||||
if (!pll) {
|
||||
NV_ERROR(dev, "bad freq %02x: %d %d\n", clk, khz, sclk);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
ret = get_pll_limits(dev, id, &pll);
|
||||
break;
|
||||
}
|
||||
|
||||
ret = get_pll_limits(dev, pll, &limits);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
off = nva3_pm_pll_offset(id);
|
||||
if (off < 0)
|
||||
return off;
|
||||
limits.refclk = read_clk(dev, clk - 0x10, true);
|
||||
if (!limits.refclk)
|
||||
return -EINVAL;
|
||||
|
||||
src0 = nv_rd32(dev, 0x4120 + (off * 4));
|
||||
src1 = nv_rd32(dev, 0x4160 + (off * 4));
|
||||
ctrl = nv_rd32(dev, pll.reg + 0);
|
||||
coef = nv_rd32(dev, pll.reg + 4);
|
||||
NV_DEBUG(dev, "PLL %02x: 0x%08x 0x%08x 0x%08x 0x%08x\n",
|
||||
id, src0, src1, ctrl, coef);
|
||||
|
||||
if (ctrl & 0x00000008) {
|
||||
u32 div = ((src1 & 0x003c0000) >> 18) + 1;
|
||||
return (pll.refclk * 2) / div;
|
||||
ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
|
||||
if (ret >= 0) {
|
||||
reg->clk = nv_rd32(dev, 0x4120 + (clk * 4));
|
||||
reg->pll = (P << 16) | (N << 8) | M;
|
||||
}
|
||||
|
||||
P = (coef & 0x003f0000) >> 16;
|
||||
N = (coef & 0x0000ff00) >> 8;
|
||||
M = (coef & 0x000000ff);
|
||||
return pll.refclk * N / M / P;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *
|
||||
nva3_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
|
||||
u32 id, int khz)
|
||||
static void
|
||||
prog_pll(struct drm_device *dev, int clk, u32 pll, struct creg *reg)
|
||||
{
|
||||
struct nva3_pm_state *pll;
|
||||
struct pll_lims limits;
|
||||
int N, M, P, diff;
|
||||
int ret, off;
|
||||
const u32 src0 = 0x004120 + (clk * 4);
|
||||
const u32 src1 = 0x004160 + (clk * 4);
|
||||
const u32 ctrl = pll + 0;
|
||||
const u32 coef = pll + 4;
|
||||
u32 cntl;
|
||||
|
||||
ret = get_pll_limits(dev, id, &limits);
|
||||
if (ret < 0)
|
||||
return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
|
||||
|
||||
off = nva3_pm_pll_offset(id);
|
||||
if (id < 0)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
|
||||
pll = kzalloc(sizeof(*pll), GFP_KERNEL);
|
||||
if (!pll)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
pll->type = id;
|
||||
pll->src0 = 0x004120 + (off * 4);
|
||||
pll->src1 = 0x004160 + (off * 4);
|
||||
pll->ctrl = limits.reg + 0;
|
||||
pll->coef = limits.reg + 4;
|
||||
|
||||
/* If target clock is within [-2, 3) MHz of a divisor, we'll
|
||||
* use that instead of calculating MNP values
|
||||
*/
|
||||
pll->new_div = min((limits.refclk * 2) / (khz - 2999), 16);
|
||||
if (pll->new_div) {
|
||||
diff = khz - ((limits.refclk * 2) / pll->new_div);
|
||||
if (diff < -2000 || diff >= 3000)
|
||||
pll->new_div = 0;
|
||||
if (!reg->clk && !reg->pll) {
|
||||
NV_DEBUG(dev, "no clock for %02x\n", clk);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pll->new_div) {
|
||||
ret = nva3_calc_pll(dev, &limits, khz, &N, NULL, &M, &P);
|
||||
if (ret < 0)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
pll->new_pnm = (P << 16) | (N << 8) | M;
|
||||
pll->new_div = 2 - 1;
|
||||
cntl = nv_rd32(dev, ctrl) & 0xfffffff2;
|
||||
if (reg->pll) {
|
||||
nv_mask(dev, src0, 0x00000101, 0x00000101);
|
||||
nv_wr32(dev, coef, reg->pll);
|
||||
nv_wr32(dev, ctrl, cntl | 0x00000015);
|
||||
nv_mask(dev, src1, 0x00000100, 0x00000000);
|
||||
nv_mask(dev, src1, 0x00000001, 0x00000000);
|
||||
} else {
|
||||
pll->new_pnm = 0;
|
||||
pll->new_div--;
|
||||
nv_mask(dev, src1, 0x003f3141, 0x00000101 | reg->clk);
|
||||
nv_wr32(dev, ctrl, cntl | 0x0000001d);
|
||||
nv_mask(dev, ctrl, 0x00000001, 0x00000000);
|
||||
nv_mask(dev, src0, 0x00000100, 0x00000000);
|
||||
nv_mask(dev, src0, 0x00000001, 0x00000000);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
prog_clk(struct drm_device *dev, int clk, struct creg *reg)
|
||||
{
|
||||
if (!reg->clk) {
|
||||
NV_DEBUG(dev, "no clock for %02x\n", clk);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((nv_rd32(dev, pll->src1) & 0x00000101) != 0x00000101)
|
||||
pll->old_pnm = nv_rd32(dev, pll->coef);
|
||||
return pll;
|
||||
nv_mask(dev, 0x004120 + (clk * 4), 0x003f3141, 0x00000101 | reg->clk);
|
||||
}
|
||||
|
||||
int
|
||||
nva3_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
{
|
||||
perflvl->core = read_pll(dev, 0x00, 0x4200);
|
||||
perflvl->shader = read_pll(dev, 0x01, 0x4220);
|
||||
perflvl->memory = read_pll(dev, 0x02, 0x4000);
|
||||
perflvl->unka0 = read_clk(dev, 0x20, false);
|
||||
perflvl->vdec = read_clk(dev, 0x21, false);
|
||||
perflvl->daemon = read_clk(dev, 0x25, false);
|
||||
perflvl->copy = perflvl->core;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct nva3_pm_state {
|
||||
struct creg nclk;
|
||||
struct creg sclk;
|
||||
struct creg mclk;
|
||||
struct creg vdec;
|
||||
struct creg unka0;
|
||||
};
|
||||
|
||||
void *
|
||||
nva3_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
{
|
||||
struct nva3_pm_state *info;
|
||||
int ret;
|
||||
|
||||
info = kzalloc(sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = calc_clk(dev, 0x10, 0x4200, perflvl->core, &info->nclk);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = calc_clk(dev, 0x11, 0x4220, perflvl->shader, &info->sclk);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = calc_clk(dev, 0x12, 0x4000, perflvl->memory, &info->mclk);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = calc_clk(dev, 0x20, 0x0000, perflvl->unka0, &info->unka0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = calc_clk(dev, 0x21, 0x0000, perflvl->vdec, &info->vdec);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (ret < 0) {
|
||||
kfree(info);
|
||||
info = ERR_PTR(ret);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
static bool
|
||||
nva3_pm_grcp_idle(void *data)
|
||||
{
|
||||
struct drm_device *dev = data;
|
||||
|
||||
if (!(nv_rd32(dev, 0x400304) & 0x00000001))
|
||||
return true;
|
||||
if (nv_rd32(dev, 0x400308) == 0x0050001c)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
nva3_pm_clock_set(struct drm_device *dev, void *pre_state)
|
||||
nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)
|
||||
{
|
||||
struct nva3_pm_state *pll = pre_state;
|
||||
u32 ctrl = 0;
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nva3_pm_state *info = pre_state;
|
||||
unsigned long flags;
|
||||
|
||||
/* For the memory clock, NVIDIA will build a "script" describing
|
||||
* the reclocking process and ask PDAEMON to execute it.
|
||||
*/
|
||||
if (pll->type == PLL_MEMORY) {
|
||||
/* prevent any new grctx switches from starting */
|
||||
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
|
||||
nv_wr32(dev, 0x400324, 0x00000000);
|
||||
nv_wr32(dev, 0x400328, 0x0050001c); /* wait flag 0x1c */
|
||||
/* wait for any pending grctx switches to complete */
|
||||
if (!nv_wait_cb(dev, nva3_pm_grcp_idle, dev)) {
|
||||
NV_ERROR(dev, "pm: ctxprog didn't go idle\n");
|
||||
goto cleanup;
|
||||
}
|
||||
/* freeze PFIFO */
|
||||
nv_mask(dev, 0x002504, 0x00000001, 0x00000001);
|
||||
if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010)) {
|
||||
NV_ERROR(dev, "pm: fifo didn't go idle\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
prog_pll(dev, 0x00, 0x004200, &info->nclk);
|
||||
prog_pll(dev, 0x01, 0x004220, &info->sclk);
|
||||
prog_clk(dev, 0x20, &info->unka0);
|
||||
prog_clk(dev, 0x21, &info->vdec);
|
||||
|
||||
if (info->mclk.clk || info->mclk.pll) {
|
||||
nv_wr32(dev, 0x100210, 0);
|
||||
nv_wr32(dev, 0x1002dc, 1);
|
||||
nv_wr32(dev, 0x004018, 0x00001000);
|
||||
ctrl = 0x18000100;
|
||||
}
|
||||
|
||||
if (pll->old_pnm || !pll->new_pnm) {
|
||||
nv_mask(dev, pll->src1, 0x003c0101, 0x00000101 |
|
||||
(pll->new_div << 18));
|
||||
nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
|
||||
nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
|
||||
}
|
||||
|
||||
if (pll->new_pnm) {
|
||||
nv_mask(dev, pll->src0, 0x00000101, 0x00000101);
|
||||
nv_wr32(dev, pll->coef, pll->new_pnm);
|
||||
nv_wr32(dev, pll->ctrl, 0x0001001d | ctrl);
|
||||
nv_mask(dev, pll->ctrl, 0x00000010, 0x00000000);
|
||||
nv_mask(dev, pll->ctrl, 0x00020010, 0x00020010);
|
||||
nv_wr32(dev, pll->ctrl, 0x00010015 | ctrl);
|
||||
nv_mask(dev, pll->src1, 0x00000100, 0x00000000);
|
||||
nv_mask(dev, pll->src1, 0x00000001, 0x00000000);
|
||||
if (pll->type == PLL_MEMORY)
|
||||
nv_wr32(dev, 0x4018, 0x10005000);
|
||||
} else {
|
||||
nv_mask(dev, pll->ctrl, 0x00000001, 0x00000000);
|
||||
nv_mask(dev, pll->src0, 0x00000100, 0x00000000);
|
||||
nv_mask(dev, pll->src0, 0x00000001, 0x00000000);
|
||||
if (pll->type == PLL_MEMORY)
|
||||
nv_wr32(dev, 0x4018, 0x1000d000);
|
||||
}
|
||||
|
||||
if (pll->type == PLL_MEMORY) {
|
||||
prog_pll(dev, 0x02, 0x004000, &info->mclk);
|
||||
if (nv_rd32(dev, 0x4000) & 0x00000008)
|
||||
nv_wr32(dev, 0x004018, 0x1000d000);
|
||||
else
|
||||
nv_wr32(dev, 0x004018, 0x10005000);
|
||||
nv_wr32(dev, 0x1002dc, 0);
|
||||
nv_wr32(dev, 0x100210, 0x80000000);
|
||||
}
|
||||
|
||||
kfree(pll);
|
||||
cleanup:
|
||||
/* unfreeze PFIFO */
|
||||
nv_mask(dev, 0x002504, 0x00000001, 0x00000000);
|
||||
/* restore ctxprog to normal */
|
||||
nv_wr32(dev, 0x400324, 0x00000000);
|
||||
nv_wr32(dev, 0x400328, 0x0070009c); /* set flag 0x1c */
|
||||
/* unblock it if necessary */
|
||||
if (nv_rd32(dev, 0x400308) == 0x0050001c)
|
||||
nv_mask(dev, 0x400824, 0x10000000, 0x10000000);
|
||||
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,30 @@ struct nvc0_fb_priv {
|
||||
dma_addr_t r100c10;
|
||||
};
|
||||
|
||||
static inline void
|
||||
nvc0_mfb_subp_isr(struct drm_device *dev, int unit, int subp)
|
||||
{
|
||||
u32 subp_base = 0x141000 + (unit * 0x2000) + (subp * 0x400);
|
||||
u32 stat = nv_rd32(dev, subp_base + 0x020);
|
||||
|
||||
if (stat) {
|
||||
NV_INFO(dev, "PMFB%d_SUBP%d: 0x%08x\n", unit, subp, stat);
|
||||
nv_wr32(dev, subp_base + 0x020, stat);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nvc0_mfb_isr(struct drm_device *dev)
|
||||
{
|
||||
u32 units = nv_rd32(dev, 0x00017c);
|
||||
while (units) {
|
||||
u32 subp, unit = ffs(units) - 1;
|
||||
for (subp = 0; subp < 2; subp++)
|
||||
nvc0_mfb_subp_isr(dev, unit, subp);
|
||||
units &= ~(1 << unit);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
nvc0_fb_destroy(struct drm_device *dev)
|
||||
{
|
||||
@@ -39,6 +63,8 @@ nvc0_fb_destroy(struct drm_device *dev)
|
||||
struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
|
||||
struct nvc0_fb_priv *priv = pfb->priv;
|
||||
|
||||
nouveau_irq_unregister(dev, 25);
|
||||
|
||||
if (priv->r100c10_page) {
|
||||
pci_unmap_page(dev->pdev, priv->r100c10, PAGE_SIZE,
|
||||
PCI_DMA_BIDIRECTIONAL);
|
||||
@@ -74,6 +100,7 @@ nvc0_fb_create(struct drm_device *dev)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
nouveau_irq_register(dev, 25, nvc0_mfb_isr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -322,7 +322,7 @@ nvc0_fifo_init(struct drm_device *dev)
|
||||
}
|
||||
|
||||
/* PSUBFIFO[n] */
|
||||
for (i = 0; i < 3; i++) {
|
||||
for (i = 0; i < priv->spoon_nr; i++) {
|
||||
nv_mask(dev, 0x04013c + (i * 0x2000), 0x10000100, 0x00000000);
|
||||
nv_wr32(dev, 0x040108 + (i * 0x2000), 0xffffffff); /* INTR */
|
||||
nv_wr32(dev, 0x04010c + (i * 0x2000), 0xfffffeff); /* INTR_EN */
|
||||
|
||||
@@ -390,7 +390,7 @@ nvc0_graph_init_gpc_0(struct drm_device *dev)
|
||||
}
|
||||
|
||||
nv_wr32(dev, GPC_BCAST(0x1bd4), magicgpc918);
|
||||
nv_wr32(dev, GPC_BCAST(0x08ac), priv->rop_nr);
|
||||
nv_wr32(dev, GPC_BCAST(0x08ac), nv_rd32(dev, 0x100800));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -700,22 +700,6 @@ nvc0_graph_isr(struct drm_device *dev)
|
||||
nv_wr32(dev, 0x400500, 0x00010001);
|
||||
}
|
||||
|
||||
static void
|
||||
nvc0_runk140_isr(struct drm_device *dev)
|
||||
{
|
||||
u32 units = nv_rd32(dev, 0x00017c) & 0x1f;
|
||||
|
||||
while (units) {
|
||||
u32 unit = ffs(units) - 1;
|
||||
u32 reg = 0x140000 + unit * 0x2000;
|
||||
u32 st0 = nv_mask(dev, reg + 0x1020, 0, 0);
|
||||
u32 st1 = nv_mask(dev, reg + 0x1420, 0, 0);
|
||||
|
||||
NV_DEBUG(dev, "PRUNK140: %d 0x%08x 0x%08x\n", unit, st0, st1);
|
||||
units &= ~(1 << unit);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
nvc0_graph_create_fw(struct drm_device *dev, const char *fwname,
|
||||
struct nvc0_graph_fuc *fuc)
|
||||
@@ -764,7 +748,6 @@ nvc0_graph_destroy(struct drm_device *dev, int engine)
|
||||
}
|
||||
|
||||
nouveau_irq_unregister(dev, 12);
|
||||
nouveau_irq_unregister(dev, 25);
|
||||
|
||||
nouveau_gpuobj_ref(NULL, &priv->unk4188b8);
|
||||
nouveau_gpuobj_ref(NULL, &priv->unk4188b4);
|
||||
@@ -803,7 +786,6 @@ nvc0_graph_create(struct drm_device *dev)
|
||||
|
||||
NVOBJ_ENGINE_ADD(dev, GR, &priv->base);
|
||||
nouveau_irq_register(dev, 12, nvc0_graph_isr);
|
||||
nouveau_irq_register(dev, 25, nvc0_runk140_isr);
|
||||
|
||||
if (nouveau_ctxfw) {
|
||||
NV_INFO(dev, "PGRAPH: using external firmware\n");
|
||||
@@ -864,6 +846,9 @@ nvc0_graph_create(struct drm_device *dev)
|
||||
case 0xce: /* 4/4/0/0, 4 */
|
||||
priv->magic_not_rop_nr = 0x03;
|
||||
break;
|
||||
case 0xcf: /* 4/0/0/0, 3 */
|
||||
priv->magic_not_rop_nr = 0x03;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!priv->magic_not_rop_nr) {
|
||||
@@ -889,20 +874,3 @@ nvc0_graph_create(struct drm_device *dev)
|
||||
nvc0_graph_destroy(dev, NVOBJ_ENGINE_GR);
|
||||
return ret;
|
||||
}
|
||||
|
||||
MODULE_FIRMWARE("nouveau/nvc0_fuc409c");
|
||||
MODULE_FIRMWARE("nouveau/nvc0_fuc409d");
|
||||
MODULE_FIRMWARE("nouveau/nvc0_fuc41ac");
|
||||
MODULE_FIRMWARE("nouveau/nvc0_fuc41ad");
|
||||
MODULE_FIRMWARE("nouveau/nvc3_fuc409c");
|
||||
MODULE_FIRMWARE("nouveau/nvc3_fuc409d");
|
||||
MODULE_FIRMWARE("nouveau/nvc3_fuc41ac");
|
||||
MODULE_FIRMWARE("nouveau/nvc3_fuc41ad");
|
||||
MODULE_FIRMWARE("nouveau/nvc4_fuc409c");
|
||||
MODULE_FIRMWARE("nouveau/nvc4_fuc409d");
|
||||
MODULE_FIRMWARE("nouveau/nvc4_fuc41ac");
|
||||
MODULE_FIRMWARE("nouveau/nvc4_fuc41ad");
|
||||
MODULE_FIRMWARE("nouveau/fuc409c");
|
||||
MODULE_FIRMWARE("nouveau/fuc409d");
|
||||
MODULE_FIRMWARE("nouveau/fuc41ac");
|
||||
MODULE_FIRMWARE("nouveau/fuc41ad");
|
||||
|
||||
@@ -82,6 +82,7 @@ nvc0_graph_class(struct drm_device *dev)
|
||||
case 0xc3:
|
||||
case 0xc4:
|
||||
case 0xce: /* guess, mmio trace shows only 0x9097 state */
|
||||
case 0xcf: /* guess, mmio trace shows only 0x9097 state */
|
||||
return 0x9097;
|
||||
case 0xc1:
|
||||
return 0x9197;
|
||||
|
||||
@@ -1678,7 +1678,10 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
|
||||
nv_wr32(dev, 0x419c04, 0x00000006);
|
||||
nv_wr32(dev, 0x419c08, 0x00000002);
|
||||
nv_wr32(dev, 0x419c20, 0x00000000);
|
||||
nv_wr32(dev, 0x419cb0, 0x00060048); //XXX: 0xce 0x00020048
|
||||
if (chipset == 0xce || chipset == 0xcf)
|
||||
nv_wr32(dev, 0x419cb0, 0x00020048);
|
||||
else
|
||||
nv_wr32(dev, 0x419cb0, 0x00060048);
|
||||
nv_wr32(dev, 0x419ce8, 0x00000000);
|
||||
nv_wr32(dev, 0x419cf4, 0x00000183);
|
||||
nv_wr32(dev, 0x419d20, chipset != 0xc1 ? 0x02180000 : 0x12180000);
|
||||
@@ -1783,11 +1786,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
|
||||
nv_wr32(dev, 0x40587c, 0x00000000);
|
||||
|
||||
if (1) {
|
||||
const u8 chipset_tp_max[] = { 16, 4, 0, 4, 8, 0, 0, 0,
|
||||
16, 0, 0, 0, 0, 0, 8, 0 };
|
||||
u8 max = chipset_tp_max[dev_priv->chipset & 0x0f];
|
||||
u8 tpnr[GPC_MAX];
|
||||
u8 data[TP_MAX];
|
||||
u8 tpnr[GPC_MAX], data[TP_MAX];
|
||||
|
||||
memcpy(tpnr, priv->tp_nr, sizeof(priv->tp_nr));
|
||||
memset(data, 0x1f, sizeof(data));
|
||||
@@ -1801,7 +1800,7 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
|
||||
data[tp] = gpc;
|
||||
}
|
||||
|
||||
for (i = 0; i < max / 4; i++)
|
||||
for (i = 0; i < 4; i++)
|
||||
nv_wr32(dev, 0x4060a8 + (i * 4), ((u32 *)data)[i]);
|
||||
}
|
||||
|
||||
|
||||
@@ -77,6 +77,11 @@ chipsets:
|
||||
.b16 nvc0_gpc_mmio_tail
|
||||
.b16 nvc0_tpc_mmio_head
|
||||
.b16 nvc3_tpc_mmio_tail
|
||||
.b8 0xcf 0 0 0
|
||||
.b16 nvc0_gpc_mmio_head
|
||||
.b16 nvc0_gpc_mmio_tail
|
||||
.b16 nvc0_tpc_mmio_head
|
||||
.b16 nvcf_tpc_mmio_tail
|
||||
.b8 0 0 0 0
|
||||
|
||||
// GPC mmio lists
|
||||
@@ -134,8 +139,9 @@ mmctx_data(0x000750, 2)
|
||||
nvc0_tpc_mmio_tail:
|
||||
mmctx_data(0x000758, 1)
|
||||
mmctx_data(0x0002c4, 1)
|
||||
mmctx_data(0x0004bc, 1)
|
||||
mmctx_data(0x0006e0, 1)
|
||||
nvcf_tpc_mmio_tail:
|
||||
mmctx_data(0x0004bc, 1)
|
||||
nvc3_tpc_mmio_tail:
|
||||
mmctx_data(0x000544, 1)
|
||||
nvc1_tpc_mmio_tail:
|
||||
|
||||
@@ -25,23 +25,26 @@ uint32_t nvc0_grgpc_data[] = {
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x000000c0,
|
||||
0x011000b0,
|
||||
0x01640114,
|
||||
0x011c00bc,
|
||||
0x01700120,
|
||||
0x000000c1,
|
||||
0x011400b0,
|
||||
0x01780114,
|
||||
0x012000bc,
|
||||
0x01840120,
|
||||
0x000000c3,
|
||||
0x011000b0,
|
||||
0x01740114,
|
||||
0x011c00bc,
|
||||
0x01800120,
|
||||
0x000000c4,
|
||||
0x011000b0,
|
||||
0x01740114,
|
||||
0x011c00bc,
|
||||
0x01800120,
|
||||
0x000000c8,
|
||||
0x011000b0,
|
||||
0x01640114,
|
||||
0x011c00bc,
|
||||
0x01700120,
|
||||
0x000000ce,
|
||||
0x011000b0,
|
||||
0x01740114,
|
||||
0x011c00bc,
|
||||
0x01800120,
|
||||
0x000000cf,
|
||||
0x011c00bc,
|
||||
0x017c0120,
|
||||
0x00000000,
|
||||
0x00000380,
|
||||
0x14000400,
|
||||
@@ -90,8 +93,8 @@ uint32_t nvc0_grgpc_data[] = {
|
||||
0x04000750,
|
||||
0x00000758,
|
||||
0x000002c4,
|
||||
0x000004bc,
|
||||
0x000006e0,
|
||||
0x000004bc,
|
||||
0x00000544,
|
||||
};
|
||||
|
||||
|
||||
@@ -56,6 +56,9 @@ chipsets:
|
||||
.b8 0xce 0 0 0
|
||||
.b16 nvc0_hub_mmio_head
|
||||
.b16 nvc0_hub_mmio_tail
|
||||
.b8 0xcf 0 0 0
|
||||
.b16 nvc0_hub_mmio_head
|
||||
.b16 nvc0_hub_mmio_tail
|
||||
.b8 0 0 0 0
|
||||
|
||||
nvc0_hub_mmio_head:
|
||||
|
||||
@@ -23,17 +23,19 @@ uint32_t nvc0_grhub_data[] = {
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x000000c0,
|
||||
0x012c0090,
|
||||
0x01340098,
|
||||
0x000000c1,
|
||||
0x01300090,
|
||||
0x01380098,
|
||||
0x000000c3,
|
||||
0x012c0090,
|
||||
0x01340098,
|
||||
0x000000c4,
|
||||
0x012c0090,
|
||||
0x01340098,
|
||||
0x000000c8,
|
||||
0x012c0090,
|
||||
0x01340098,
|
||||
0x000000ce,
|
||||
0x012c0090,
|
||||
0x01340098,
|
||||
0x000000cf,
|
||||
0x01340098,
|
||||
0x00000000,
|
||||
0x0417e91c,
|
||||
0x04400204,
|
||||
@@ -190,8 +192,6 @@ uint32_t nvc0_grhub_data[] = {
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
0x00000000,
|
||||
};
|
||||
|
||||
uint32_t nvc0_grhub_code[] = {
|
||||
|
||||
155
drivers/gpu/drm/nouveau/nvc0_pm.c
Normal file
155
drivers/gpu/drm/nouveau/nvc0_pm.c
Normal file
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2011 Red Hat Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a
|
||||
* copy of this software and associated documentation files (the "Software"),
|
||||
* to deal in the Software without restriction, including without limitation
|
||||
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
* and/or sell copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
* Authors: Ben Skeggs
|
||||
*/
|
||||
|
||||
#include "drmP.h"
|
||||
#include "nouveau_drv.h"
|
||||
#include "nouveau_bios.h"
|
||||
#include "nouveau_pm.h"
|
||||
|
||||
static u32 read_div(struct drm_device *, int, u32, u32);
|
||||
static u32 read_pll(struct drm_device *, u32);
|
||||
|
||||
static u32
|
||||
read_vco(struct drm_device *dev, u32 dsrc)
|
||||
{
|
||||
u32 ssrc = nv_rd32(dev, dsrc);
|
||||
if (!(ssrc & 0x00000100))
|
||||
return read_pll(dev, 0x00e800);
|
||||
return read_pll(dev, 0x00e820);
|
||||
}
|
||||
|
||||
static u32
|
||||
read_pll(struct drm_device *dev, u32 pll)
|
||||
{
|
||||
u32 ctrl = nv_rd32(dev, pll + 0);
|
||||
u32 coef = nv_rd32(dev, pll + 4);
|
||||
u32 P = (coef & 0x003f0000) >> 16;
|
||||
u32 N = (coef & 0x0000ff00) >> 8;
|
||||
u32 M = (coef & 0x000000ff) >> 0;
|
||||
u32 sclk, doff;
|
||||
|
||||
if (!(ctrl & 0x00000001))
|
||||
return 0;
|
||||
|
||||
switch (pll & 0xfff000) {
|
||||
case 0x00e000:
|
||||
sclk = 27000;
|
||||
P = 1;
|
||||
break;
|
||||
case 0x137000:
|
||||
doff = (pll - 0x137000) / 0x20;
|
||||
sclk = read_div(dev, doff, 0x137120, 0x137140);
|
||||
break;
|
||||
case 0x132000:
|
||||
switch (pll) {
|
||||
case 0x132000:
|
||||
sclk = read_pll(dev, 0x132020);
|
||||
break;
|
||||
case 0x132020:
|
||||
sclk = read_div(dev, 0, 0x137320, 0x137330);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sclk * N / M / P;
|
||||
}
|
||||
|
||||
static u32
|
||||
read_div(struct drm_device *dev, int doff, u32 dsrc, u32 dctl)
|
||||
{
|
||||
u32 ssrc = nv_rd32(dev, dsrc + (doff * 4));
|
||||
u32 sctl = nv_rd32(dev, dctl + (doff * 4));
|
||||
|
||||
switch (ssrc & 0x00000003) {
|
||||
case 0:
|
||||
if ((ssrc & 0x00030000) != 0x00030000)
|
||||
return 27000;
|
||||
return 108000;
|
||||
case 2:
|
||||
return 100000;
|
||||
case 3:
|
||||
if (sctl & 0x80000000) {
|
||||
u32 sclk = read_vco(dev, dsrc + (doff * 4));
|
||||
u32 sdiv = (sctl & 0x0000003f) + 2;
|
||||
return (sclk * 2) / sdiv;
|
||||
}
|
||||
|
||||
return read_vco(dev, dsrc + (doff * 4));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static u32
|
||||
read_mem(struct drm_device *dev)
|
||||
{
|
||||
u32 ssel = nv_rd32(dev, 0x1373f0);
|
||||
if (ssel & 0x00000001)
|
||||
return read_div(dev, 0, 0x137300, 0x137310);
|
||||
return read_pll(dev, 0x132000);
|
||||
}
|
||||
|
||||
static u32
|
||||
read_clk(struct drm_device *dev, int clk)
|
||||
{
|
||||
u32 sctl = nv_rd32(dev, 0x137250 + (clk * 4));
|
||||
u32 ssel = nv_rd32(dev, 0x137100);
|
||||
u32 sclk, sdiv;
|
||||
|
||||
if (ssel & (1 << clk)) {
|
||||
if (clk < 7)
|
||||
sclk = read_pll(dev, 0x137000 + (clk * 0x20));
|
||||
else
|
||||
sclk = read_pll(dev, 0x1370e0);
|
||||
sdiv = ((sctl & 0x00003f00) >> 8) + 2;
|
||||
} else {
|
||||
sclk = read_div(dev, clk, 0x137160, 0x1371d0);
|
||||
sdiv = ((sctl & 0x0000003f) >> 0) + 2;
|
||||
}
|
||||
|
||||
if (sctl & 0x80000000)
|
||||
return (sclk * 2) / sdiv;
|
||||
return sclk;
|
||||
}
|
||||
|
||||
int
|
||||
nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
|
||||
{
|
||||
perflvl->shader = read_clk(dev, 0x00);
|
||||
perflvl->core = perflvl->shader / 2;
|
||||
perflvl->memory = read_mem(dev);
|
||||
perflvl->rop = read_clk(dev, 0x01);
|
||||
perflvl->hub07 = read_clk(dev, 0x02);
|
||||
perflvl->hub06 = read_clk(dev, 0x07);
|
||||
perflvl->hub01 = read_clk(dev, 0x08);
|
||||
perflvl->copy = read_clk(dev, 0x09);
|
||||
perflvl->daemon = read_clk(dev, 0x0c);
|
||||
perflvl->vdec = read_clk(dev, 0x0e);
|
||||
return 0;
|
||||
}
|
||||
@@ -61,7 +61,7 @@ nvc0_vram_new(struct drm_device *dev, u64 size, u32 align, u32 ncmin,
|
||||
u32 type, struct nouveau_mem **pmem)
|
||||
{
|
||||
struct drm_nouveau_private *dev_priv = dev->dev_private;
|
||||
struct nouveau_mm *mm = dev_priv->engine.vram.mm;
|
||||
struct nouveau_mm *mm = &dev_priv->engine.vram.mm;
|
||||
struct nouveau_mm_node *r;
|
||||
struct nouveau_mem *mem;
|
||||
int ret;
|
||||
@@ -106,12 +106,50 @@ nvc0_vram_init(struct drm_device *dev)
|
||||
struct nouveau_vram_engine *vram = &dev_priv->engine.vram;
|
||||
const u32 rsvd_head = ( 256 * 1024) >> 12; /* vga memory */
|
||||
const u32 rsvd_tail = (1024 * 1024) >> 12; /* vbios etc */
|
||||
u32 length;
|
||||
u32 parts = nv_rd32(dev, 0x121c74);
|
||||
u32 bsize = nv_rd32(dev, 0x10f20c);
|
||||
u32 offset, length;
|
||||
bool uniform = true;
|
||||
int ret, i;
|
||||
|
||||
dev_priv->vram_size = nv_rd32(dev, 0x10f20c) << 20;
|
||||
dev_priv->vram_size *= nv_rd32(dev, 0x121c74);
|
||||
NV_DEBUG(dev, "0x100800: 0x%08x\n", nv_rd32(dev, 0x100800));
|
||||
NV_DEBUG(dev, "parts 0x%08x bcast_mem_amount 0x%08x\n", parts, bsize);
|
||||
|
||||
length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
|
||||
/* read amount of vram attached to each memory controller */
|
||||
for (i = 0; i < parts; i++) {
|
||||
u32 psize = nv_rd32(dev, 0x11020c + (i * 0x1000));
|
||||
if (psize != bsize) {
|
||||
if (psize < bsize)
|
||||
bsize = psize;
|
||||
uniform = false;
|
||||
}
|
||||
|
||||
return nouveau_mm_init(&vram->mm, rsvd_head, length, 1);
|
||||
NV_DEBUG(dev, "%d: mem_amount 0x%08x\n", i, psize);
|
||||
|
||||
dev_priv->vram_size += (u64)psize << 20;
|
||||
}
|
||||
|
||||
/* if all controllers have the same amount attached, there's no holes */
|
||||
if (uniform) {
|
||||
offset = rsvd_head;
|
||||
length = (dev_priv->vram_size >> 12) - rsvd_head - rsvd_tail;
|
||||
return nouveau_mm_init(&vram->mm, offset, length, 1);
|
||||
}
|
||||
|
||||
/* otherwise, address lowest common amount from 0GiB */
|
||||
ret = nouveau_mm_init(&vram->mm, rsvd_head, (bsize << 8) * parts, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* and the rest starting from (8GiB + common_size) */
|
||||
offset = (0x0200000000ULL >> 12) + (bsize << 8);
|
||||
length = (dev_priv->vram_size >> 12) - (bsize << 8) - rsvd_tail;
|
||||
|
||||
ret = nouveau_mm_init(&vram->mm, offset, length, 0);
|
||||
if (ret) {
|
||||
nouveau_mm_fini(&vram->mm);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
1473
drivers/gpu/drm/nouveau/nvd0_display.c
Normal file
1473
drivers/gpu/drm/nouveau/nvd0_display.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -41,6 +41,31 @@ static void evergreen_gpu_init(struct radeon_device *rdev);
|
||||
void evergreen_fini(struct radeon_device *rdev);
|
||||
static void evergreen_pcie_gen2_enable(struct radeon_device *rdev);
|
||||
|
||||
void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev)
|
||||
{
|
||||
u16 ctl, v;
|
||||
int cap, err;
|
||||
|
||||
cap = pci_pcie_cap(rdev->pdev);
|
||||
if (!cap)
|
||||
return;
|
||||
|
||||
err = pci_read_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, &ctl);
|
||||
if (err)
|
||||
return;
|
||||
|
||||
v = (ctl & PCI_EXP_DEVCTL_READRQ) >> 12;
|
||||
|
||||
/* if bios or OS sets MAX_READ_REQUEST_SIZE to an invalid value, fix it
|
||||
* to avoid hangs or perfomance issues
|
||||
*/
|
||||
if ((v == 0) || (v == 6) || (v == 7)) {
|
||||
ctl &= ~PCI_EXP_DEVCTL_READRQ;
|
||||
ctl |= (2 << 12);
|
||||
pci_write_config_word(rdev->pdev, cap + PCI_EXP_DEVCTL, ctl);
|
||||
}
|
||||
}
|
||||
|
||||
void evergreen_pre_page_flip(struct radeon_device *rdev, int crtc)
|
||||
{
|
||||
/* enable the pflip int */
|
||||
@@ -1866,6 +1891,8 @@ static void evergreen_gpu_init(struct radeon_device *rdev)
|
||||
|
||||
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
|
||||
|
||||
evergreen_fix_pci_max_read_req_size(rdev);
|
||||
|
||||
cc_gc_shader_pipe_config = RREG32(CC_GC_SHADER_PIPE_CONFIG) & ~2;
|
||||
|
||||
cc_gc_shader_pipe_config |=
|
||||
|
||||
@@ -39,6 +39,7 @@ extern int evergreen_mc_wait_for_idle(struct radeon_device *rdev);
|
||||
extern void evergreen_mc_program(struct radeon_device *rdev);
|
||||
extern void evergreen_irq_suspend(struct radeon_device *rdev);
|
||||
extern int evergreen_mc_init(struct radeon_device *rdev);
|
||||
extern void evergreen_fix_pci_max_read_req_size(struct radeon_device *rdev);
|
||||
|
||||
#define EVERGREEN_PFP_UCODE_SIZE 1120
|
||||
#define EVERGREEN_PM4_UCODE_SIZE 1376
|
||||
@@ -669,6 +670,8 @@ static void cayman_gpu_init(struct radeon_device *rdev)
|
||||
|
||||
WREG32(GRBM_CNTL, GRBM_READ_TIMEOUT(0xff));
|
||||
|
||||
evergreen_fix_pci_max_read_req_size(rdev);
|
||||
|
||||
mc_shared_chmap = RREG32(MC_SHARED_CHMAP);
|
||||
mc_arb_ramcfg = RREG32(MC_ARB_RAMCFG);
|
||||
|
||||
|
||||
@@ -219,6 +219,9 @@ void radeon_get_clock_info(struct drm_device *dev)
|
||||
} else {
|
||||
DRM_INFO("Using generic clock info\n");
|
||||
|
||||
/* may need to be per card */
|
||||
rdev->clock.max_pixel_clock = 35000;
|
||||
|
||||
if (rdev->flags & RADEON_IS_IGP) {
|
||||
p1pll->reference_freq = 1432;
|
||||
p2pll->reference_freq = 1432;
|
||||
|
||||
@@ -1297,12 +1297,33 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
|
||||
if (!radeon_dig_connector->edp_on)
|
||||
atombios_set_edp_panel_power(connector,
|
||||
ATOM_TRANSMITTER_ACTION_POWER_OFF);
|
||||
} else {
|
||||
/* need to setup ddc on the bridge */
|
||||
if (radeon_connector_encoder_is_dp_bridge(connector)) {
|
||||
} else if (radeon_connector_encoder_is_dp_bridge(connector)) {
|
||||
/* DP bridges are always DP */
|
||||
radeon_dig_connector->dp_sink_type = CONNECTOR_OBJECT_ID_DISPLAYPORT;
|
||||
/* get the DPCD from the bridge */
|
||||
radeon_dp_getdpcd(radeon_connector);
|
||||
|
||||
if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd))
|
||||
ret = connector_status_connected;
|
||||
else {
|
||||
/* need to setup ddc on the bridge */
|
||||
if (encoder)
|
||||
radeon_atom_ext_encoder_setup_ddc(encoder);
|
||||
if (radeon_ddc_probe(radeon_connector,
|
||||
radeon_connector->requires_extended_probe))
|
||||
ret = connector_status_connected;
|
||||
}
|
||||
|
||||
if ((ret == connector_status_disconnected) &&
|
||||
radeon_connector->dac_load_detect) {
|
||||
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
|
||||
struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
if (encoder) {
|
||||
encoder_funcs = encoder->helper_private;
|
||||
ret = encoder_funcs->detect(encoder, connector);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
radeon_dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector);
|
||||
if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) {
|
||||
ret = connector_status_connected;
|
||||
@@ -1318,16 +1339,6 @@ radeon_dp_detect(struct drm_connector *connector, bool force)
|
||||
ret = connector_status_connected;
|
||||
}
|
||||
}
|
||||
|
||||
if ((ret == connector_status_disconnected) &&
|
||||
radeon_connector->dac_load_detect) {
|
||||
struct drm_encoder *encoder = radeon_best_single_encoder(connector);
|
||||
struct drm_encoder_helper_funcs *encoder_funcs;
|
||||
if (encoder) {
|
||||
encoder_funcs = encoder->helper_private;
|
||||
ret = encoder_funcs->detect(encoder, connector);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
radeon_connector_update_scratch_regs(connector, ret);
|
||||
|
||||
@@ -707,16 +707,21 @@ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
|
||||
radeon_router_select_ddc_port(radeon_connector);
|
||||
|
||||
if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
|
||||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
|
||||
(radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP) ||
|
||||
radeon_connector_encoder_is_dp_bridge(&radeon_connector->base)) {
|
||||
struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
|
||||
|
||||
if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
|
||||
dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus)
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter);
|
||||
}
|
||||
if (!radeon_connector->ddc_bus)
|
||||
return -1;
|
||||
if (!radeon_connector->edid) {
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
||||
&dig->dp_i2c_bus->adapter);
|
||||
else if (radeon_connector->ddc_bus && !radeon_connector->edid)
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
||||
&radeon_connector->ddc_bus->adapter);
|
||||
} else {
|
||||
if (radeon_connector->ddc_bus && !radeon_connector->edid)
|
||||
radeon_connector->edid = drm_get_edid(&radeon_connector->base,
|
||||
&radeon_connector->ddc_bus->adapter);
|
||||
}
|
||||
|
||||
if (!radeon_connector->edid) {
|
||||
|
||||
Reference in New Issue
Block a user