Merge branch 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6 into drm-core-next

* 'drm-nouveau-next' of git://anongit.freedesktop.org/git/nouveau/linux-2.6: (102 commits)
  drm/nouveau/ttm: fix crash as a result of a recent ttm change
  drm/nouveau: Fix notifier blocks over the 4GB mark.
  drm/nouveau: Fix pushbufs over the 4GB mark.
  drm/nvc0/pm: initial engine reclocking
  drm/nouveau: move hpd enable/disable to common code
  drm/nv40/disp: implement support for hotplug irq
  drm/nouveau/gpio: reimplement as nouveau_gpio.c, fixing a number of issues
  drm/nouveau: just pass gpio line to pwm_*, not entire gpio struct
  drm/nouveau/hwsq: remove some magic, give proper opcode names
  drm/nv50/pm: introduce hwsq-based memory reclocking
  drm/nv04/disp: handle dual-link spwg panels without needing quirks
  drm/nouveau/dp: remove broken display depth function, use the improved one
  drm/nouveau/mxm: implement ROM shadow method
  drm/nouveau/mxm: implement _DSM shadow method
  drm/nouveau/mxm: implement wmi shadow method
  drm/nouveau/mxm: initial implementation of dcb sanitisation
  drm/nouveau/disp: parse connector info directly in nouveau_connector.c
  drm/nouveau/i2c: handle bit-banging ourselves
  drm/nouveau/i2c: fix debug message
  drm/nouveau/i2c: tidy up bit-bang helpers, also fixing nv50 setsda bug
  ...
This commit is contained in:
Dave Airlie 2011-12-22 11:02:06 +00:00
commit 5d56fe5fd7
71 changed files with 6496 additions and 2753 deletions

View File

@ -9,9 +9,9 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
nouveau_dp.o nouveau_ramht.o \
nouveau_hdmi.o nouveau_dp.o nouveau_ramht.o \
nouveau_pm.o nouveau_volt.o nouveau_perf.o nouveau_temp.o \
nouveau_mm.o nouveau_vm.o \
nouveau_mm.o nouveau_vm.o nouveau_mxm.o nouveau_gpio.o \
nv04_timer.o \
nv04_mc.o nv40_mc.o nv50_mc.o \
nv04_fb.o nv10_fb.o nv30_fb.o nv40_fb.o nv50_fb.o nvc0_fb.o \
@ -19,9 +19,12 @@ nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
nv04_graph.o nv10_graph.o nv20_graph.o \
nv40_graph.o nv50_graph.o nvc0_graph.o \
nv40_grctx.o nv50_grctx.o nvc0_grctx.o \
nv84_crypt.o \
nv84_crypt.o nv98_crypt.o \
nva3_copy.o nvc0_copy.o \
nv31_mpeg.o nv50_mpeg.o \
nv84_bsp.o \
nv84_vp.o \
nv98_ppp.o \
nv04_instmem.o nv50_instmem.o nvc0_instmem.o \
nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
nv04_crtc.o nv04_display.o nv04_cursor.o \

File diff suppressed because it is too large Load Diff

View File

@ -34,9 +34,14 @@
#define DCB_LOC_ON_CHIP 0
#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
#define ROMPTR(bios, x) (ROM16(x) ? &(bios)->data[ROM16(x)] : NULL)
#define ROM16(x) le16_to_cpu(*(u16 *)&(x))
#define ROM32(x) le32_to_cpu(*(u32 *)&(x))
#define ROM48(x) ({ u8 *p = &(x); (u64)ROM16(p[4]) << 32 | ROM32(p[0]); })
#define ROM64(x) le64_to_cpu(*(u64 *)&(x))
#define ROMPTR(d,x) ({ \
struct drm_nouveau_private *dev_priv = (d)->dev_private; \
ROM16(x) ? &dev_priv->vbios.data[ROM16(x)] : NULL; \
})
struct bit_entry {
uint8_t id;
@ -48,30 +53,12 @@ struct bit_entry {
int bit_table(struct drm_device *, u8 id, struct bit_entry *);
struct dcb_i2c_entry {
uint32_t entry;
uint8_t port_type;
uint8_t read, write;
struct nouveau_i2c_chan *chan;
};
enum dcb_gpio_tag {
DCB_GPIO_TVDAC0 = 0xc,
DCB_GPIO_TVDAC1 = 0x2d,
};
struct dcb_gpio_entry {
enum dcb_gpio_tag tag;
int line;
bool invert;
uint32_t entry;
uint8_t state_default;
uint8_t state[2];
};
struct dcb_gpio_table {
int entries;
struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
DCB_GPIO_PWM_FAN = 0x9,
DCB_GPIO_FAN_SENSE = 0x3d,
DCB_GPIO_UNUSED = 0xff
};
enum dcb_connector_type {
@ -90,20 +77,6 @@ enum dcb_connector_type {
DCB_CONNECTOR_NONE = 0xff
};
struct dcb_connector_table_entry {
uint8_t index;
uint32_t entry;
enum dcb_connector_type type;
uint8_t index2;
uint8_t gpio_tag;
void *drm;
};
struct dcb_connector_table {
int entries;
struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
};
enum dcb_type {
OUTPUT_ANALOG = 0,
OUTPUT_TV = 1,
@ -111,6 +84,7 @@ enum dcb_type {
OUTPUT_LVDS = 3,
OUTPUT_DP = 6,
OUTPUT_EOL = 14, /* DCB 4.0+, appears to be end-of-list */
OUTPUT_UNUSED = 15,
OUTPUT_ANY = -1
};
@ -155,18 +129,8 @@ struct dcb_entry {
struct dcb_table {
uint8_t version;
int entries;
struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
uint8_t *i2c_table;
uint8_t i2c_default_indices;
struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
uint16_t gpio_table_ptr;
struct dcb_gpio_table gpio;
uint16_t connector_table_ptr;
struct dcb_connector_table connector;
};
enum nouveau_or {
@ -195,7 +159,7 @@ enum pll_types {
PLL_SHADER = 0x02,
PLL_UNK03 = 0x03,
PLL_MEMORY = 0x04,
PLL_UNK05 = 0x05,
PLL_VDEC = 0x05,
PLL_UNK40 = 0x40,
PLL_UNK41 = 0x41,
PLL_UNK42 = 0x42,
@ -333,4 +297,11 @@ struct nvbios {
} legacy;
};
void *dcb_table(struct drm_device *);
void *dcb_outp(struct drm_device *, u8 idx);
int dcb_outp_foreach(struct drm_device *, void *data,
int (*)(struct drm_device *, void *, int idx, u8 *outp));
u8 *dcb_conntab(struct drm_device *);
u8 *dcb_conn(struct drm_device *, u8 idx);
#endif

View File

@ -682,8 +682,7 @@ nouveau_vma_getmap(struct nouveau_channel *chan, struct nouveau_bo *nvbo,
if (mem->mem_type == TTM_PL_VRAM)
nouveau_vm_map(vma, node);
else
nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT,
node, node->pages);
nouveau_vm_map_sg(vma, 0, mem->num_pages << PAGE_SHIFT, node);
return 0;
}
@ -810,7 +809,6 @@ nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
static void
nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem)
{
struct nouveau_mem *node = new_mem->mm_node;
struct nouveau_bo *nvbo = nouveau_bo(bo);
struct nouveau_vma *vma;
@ -822,7 +820,7 @@ nouveau_bo_move_ntfy(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem)
nvbo->page_shift == vma->vm->spg_shift) {
nouveau_vm_map_sg(vma, 0, new_mem->
num_pages << PAGE_SHIFT,
node, node->pages);
new_mem->mm_node);
} else {
nouveau_vm_unmap(vma);
}
@ -1173,7 +1171,7 @@ nouveau_bo_vma_add(struct nouveau_bo *nvbo, struct nouveau_vm *vm,
nouveau_vm_map(vma, nvbo->bo.mem.mm_node);
else
if (nvbo->bo.mem.mem_type == TTM_PL_TT)
nouveau_vm_map_sg(vma, 0, size, node, node->pages);
nouveau_vm_map_sg(vma, 0, size, node);
list_add_tail(&vma->head, &nvbo->vma_list);
vma->refcount = 1;

View File

@ -187,6 +187,8 @@ nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
nouveau_dma_pre_init(chan);
chan->user_put = 0x40;
chan->user_get = 0x44;
if (dev_priv->card_type >= NV_50)
chan->user_get_hi = 0x60;
/* disable the fifo caches */
pfifo->reassign(dev, false);

View File

@ -35,6 +35,7 @@
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
#include "nouveau_connector.h"
#include "nouveau_gpio.h"
#include "nouveau_hw.h"
static void nouveau_connector_hotplug(void *, int);
@ -78,29 +79,11 @@ nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
return NULL;
}
/*TODO: This could use improvement, and learn to handle the fixed
* BIOS tables etc. It's fine currently, for its only user.
*/
int
nouveau_connector_bpp(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
if (nv_connector->edid && nv_connector->edid->revision >= 4) {
u8 bpc = ((nv_connector->edid->input & 0x70) >> 3) + 4;
if (bpc > 4)
return bpc;
}
return 18;
}
static void
nouveau_connector_destroy(struct drm_connector *connector)
{
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct drm_nouveau_private *dev_priv;
struct nouveau_gpio_engine *pgpio;
struct drm_device *dev;
if (!nv_connector)
@ -110,10 +93,9 @@ nouveau_connector_destroy(struct drm_connector *connector)
dev_priv = dev->dev_private;
NV_DEBUG_KMS(dev, "\n");
pgpio = &dev_priv->engine.gpio;
if (pgpio->irq_unregister) {
pgpio->irq_unregister(dev, nv_connector->dcb->gpio_tag,
nouveau_connector_hotplug, connector);
if (nv_connector->hpd != DCB_GPIO_UNUSED) {
nouveau_gpio_isr_del(dev, 0, nv_connector->hpd, 0xff,
nouveau_connector_hotplug, connector);
}
kfree(nv_connector->edid);
@ -198,6 +180,10 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
return;
nv_connector->detected_encoder = nv_encoder;
if (dev_priv->card_type >= NV_50) {
connector->interlace_allowed = true;
connector->doublescan_allowed = true;
} else
if (nv_encoder->dcb->type == OUTPUT_LVDS ||
nv_encoder->dcb->type == OUTPUT_TMDS) {
connector->doublescan_allowed = false;
@ -214,7 +200,7 @@ nouveau_connector_set_encoder(struct drm_connector *connector,
connector->interlace_allowed = true;
}
if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
drm_connector_property_set_value(connector,
dev->mode_config.dvi_i_subconnector_property,
nv_encoder->dcb->type == OUTPUT_TMDS ?
@ -397,7 +383,7 @@ nouveau_connector_force(struct drm_connector *connector)
struct nouveau_encoder *nv_encoder;
int type;
if (nv_connector->dcb->type == DCB_CONNECTOR_DVI_I) {
if (nv_connector->type == DCB_CONNECTOR_DVI_I) {
if (connector->force == DRM_FORCE_ON_DIGITAL)
type = OUTPUT_TMDS;
else
@ -420,15 +406,21 @@ static int
nouveau_connector_set_property(struct drm_connector *connector,
struct drm_property *property, uint64_t value)
{
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct nouveau_display_engine *disp = &dev_priv->engine.display;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
struct drm_device *dev = connector->dev;
struct nouveau_crtc *nv_crtc;
int ret;
nv_crtc = NULL;
if (connector->encoder && connector->encoder->crtc)
nv_crtc = nouveau_crtc(connector->encoder->crtc);
/* Scaling mode */
if (property == dev->mode_config.scaling_mode_property) {
struct nouveau_crtc *nv_crtc = NULL;
bool modeset = false;
switch (value) {
@ -454,8 +446,6 @@ nouveau_connector_set_property(struct drm_connector *connector,
modeset = true;
nv_connector->scaling_mode = value;
if (connector->encoder && connector->encoder->crtc)
nv_crtc = nouveau_crtc(connector->encoder->crtc);
if (!nv_crtc)
return 0;
@ -467,7 +457,7 @@ nouveau_connector_set_property(struct drm_connector *connector,
if (!ret)
return -EINVAL;
} else {
ret = nv_crtc->set_scale(nv_crtc, value, true);
ret = nv_crtc->set_scale(nv_crtc, true);
if (ret)
return ret;
}
@ -475,23 +465,58 @@ nouveau_connector_set_property(struct drm_connector *connector,
return 0;
}
/* Underscan */
if (property == disp->underscan_property) {
if (nv_connector->underscan != value) {
nv_connector->underscan = value;
if (!nv_crtc || !nv_crtc->set_scale)
return 0;
return nv_crtc->set_scale(nv_crtc, true);
}
return 0;
}
if (property == disp->underscan_hborder_property) {
if (nv_connector->underscan_hborder != value) {
nv_connector->underscan_hborder = value;
if (!nv_crtc || !nv_crtc->set_scale)
return 0;
return nv_crtc->set_scale(nv_crtc, true);
}
return 0;
}
if (property == disp->underscan_vborder_property) {
if (nv_connector->underscan_vborder != value) {
nv_connector->underscan_vborder = value;
if (!nv_crtc || !nv_crtc->set_scale)
return 0;
return nv_crtc->set_scale(nv_crtc, true);
}
return 0;
}
/* Dithering */
if (property == dev->mode_config.dithering_mode_property) {
struct nouveau_crtc *nv_crtc = NULL;
if (value == DRM_MODE_DITHERING_ON)
nv_connector->use_dithering = true;
else
nv_connector->use_dithering = false;
if (connector->encoder && connector->encoder->crtc)
nv_crtc = nouveau_crtc(connector->encoder->crtc);
if (property == disp->dithering_mode) {
nv_connector->dithering_mode = value;
if (!nv_crtc || !nv_crtc->set_dither)
return 0;
return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering,
true);
return nv_crtc->set_dither(nv_crtc, true);
}
if (property == disp->dithering_depth) {
nv_connector->dithering_depth = value;
if (!nv_crtc || !nv_crtc->set_dither)
return 0;
return nv_crtc->set_dither(nv_crtc, true);
}
if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
@ -602,6 +627,46 @@ nouveau_connector_scaler_modes_add(struct drm_connector *connector)
return modes;
}
static void
nouveau_connector_detect_depth(struct drm_connector *connector)
{
struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
struct nouveau_connector *nv_connector = nouveau_connector(connector);
struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
struct nvbios *bios = &dev_priv->vbios;
struct drm_display_mode *mode = nv_connector->native_mode;
bool duallink;
/* if the edid is feeling nice enough to provide this info, use it */
if (nv_connector->edid && connector->display_info.bpc)
return;
/* if not, we're out of options unless we're LVDS, default to 6bpc */
connector->display_info.bpc = 6;
if (nv_encoder->dcb->type != OUTPUT_LVDS)
return;
/* LVDS: panel straps */
if (bios->fp_no_ddc) {
if (bios->fp.if_is_24bit)
connector->display_info.bpc = 8;
return;
}
/* LVDS: DDC panel, need to first determine the number of links to
* know which if_is_24bit flag to check...
*/
if (nv_connector->edid &&
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG)
duallink = ((u8 *)nv_connector->edid)[121] == 2;
else
duallink = mode->clock >= bios->fp.duallink_transition_clk;
if ((!duallink && (bios->fp.strapless_is_24bit & 1)) ||
( duallink && (bios->fp.strapless_is_24bit & 2)))
connector->display_info.bpc = 8;
}
static int
nouveau_connector_get_modes(struct drm_connector *connector)
{
@ -631,6 +696,12 @@ nouveau_connector_get_modes(struct drm_connector *connector)
nv_connector->native_mode = drm_mode_duplicate(dev, &mode);
}
/* Determine display colour depth for everything except LVDS now,
* DP requires this before mode_valid() is called.
*/
if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS)
nouveau_connector_detect_depth(connector);
/* Find the native mode if this is a digital panel, if we didn't
* find any modes through DDC previously add the native mode to
* the list of modes.
@ -646,12 +717,19 @@ nouveau_connector_get_modes(struct drm_connector *connector)
ret = 1;
}
/* Determine LVDS colour depth, must happen after determining
* "native" mode as some VBIOS tables require us to use the
* pixel clock as part of the lookup...
*/
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
nouveau_connector_detect_depth(connector);
if (nv_encoder->dcb->type == OUTPUT_TV)
ret = get_slave_funcs(encoder)->get_modes(encoder, connector);
if (nv_connector->dcb->type == DCB_CONNECTOR_LVDS ||
nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG ||
nv_connector->dcb->type == DCB_CONNECTOR_eDP)
if (nv_connector->type == DCB_CONNECTOR_LVDS ||
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG ||
nv_connector->type == DCB_CONNECTOR_eDP)
ret += nouveau_connector_scaler_modes_add(connector);
return ret;
@ -710,7 +788,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector,
case OUTPUT_DP:
max_clock = nv_encoder->dp.link_nr;
max_clock *= nv_encoder->dp.link_bw;
clock = clock * nouveau_connector_bpp(connector) / 10;
clock = clock * (connector->display_info.bpc * 3) / 10;
break;
default:
BUG_ON(1);
@ -768,66 +846,144 @@ nouveau_connector_funcs_lvds = {
.force = nouveau_connector_force
};
static int
drm_conntype_from_dcb(enum dcb_connector_type dcb)
{
switch (dcb) {
case DCB_CONNECTOR_VGA : return DRM_MODE_CONNECTOR_VGA;
case DCB_CONNECTOR_TV_0 :
case DCB_CONNECTOR_TV_1 :
case DCB_CONNECTOR_TV_3 : return DRM_MODE_CONNECTOR_TV;
case DCB_CONNECTOR_DVI_I : return DRM_MODE_CONNECTOR_DVII;
case DCB_CONNECTOR_DVI_D : return DRM_MODE_CONNECTOR_DVID;
case DCB_CONNECTOR_LVDS :
case DCB_CONNECTOR_LVDS_SPWG: return DRM_MODE_CONNECTOR_LVDS;
case DCB_CONNECTOR_DP : return DRM_MODE_CONNECTOR_DisplayPort;
case DCB_CONNECTOR_eDP : return DRM_MODE_CONNECTOR_eDP;
case DCB_CONNECTOR_HDMI_0 :
case DCB_CONNECTOR_HDMI_1 : return DRM_MODE_CONNECTOR_HDMIA;
default:
break;
}
return DRM_MODE_CONNECTOR_Unknown;
}
struct drm_connector *
nouveau_connector_create(struct drm_device *dev, int index)
{
const struct drm_connector_funcs *funcs = &nouveau_connector_funcs;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nouveau_display_engine *disp = &dev_priv->engine.display;
struct nouveau_connector *nv_connector = NULL;
struct dcb_connector_table_entry *dcb = NULL;
struct drm_connector *connector;
int type, ret = 0;
bool dummy;
NV_DEBUG_KMS(dev, "\n");
if (index >= dev_priv->vbios.dcb.connector.entries)
return ERR_PTR(-EINVAL);
dcb = &dev_priv->vbios.dcb.connector.entry[index];
if (dcb->drm)
return dcb->drm;
switch (dcb->type) {
case DCB_CONNECTOR_VGA:
type = DRM_MODE_CONNECTOR_VGA;
break;
case DCB_CONNECTOR_TV_0:
case DCB_CONNECTOR_TV_1:
case DCB_CONNECTOR_TV_3:
type = DRM_MODE_CONNECTOR_TV;
break;
case DCB_CONNECTOR_DVI_I:
type = DRM_MODE_CONNECTOR_DVII;
break;
case DCB_CONNECTOR_DVI_D:
type = DRM_MODE_CONNECTOR_DVID;
break;
case DCB_CONNECTOR_HDMI_0:
case DCB_CONNECTOR_HDMI_1:
type = DRM_MODE_CONNECTOR_HDMIA;
break;
case DCB_CONNECTOR_LVDS:
case DCB_CONNECTOR_LVDS_SPWG:
type = DRM_MODE_CONNECTOR_LVDS;
funcs = &nouveau_connector_funcs_lvds;
break;
case DCB_CONNECTOR_DP:
type = DRM_MODE_CONNECTOR_DisplayPort;
break;
case DCB_CONNECTOR_eDP:
type = DRM_MODE_CONNECTOR_eDP;
break;
default:
NV_ERROR(dev, "unknown connector type: 0x%02x!!\n", dcb->type);
return ERR_PTR(-EINVAL);
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
nv_connector = nouveau_connector(connector);
if (nv_connector->index == index)
return connector;
}
nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
if (!nv_connector)
return ERR_PTR(-ENOMEM);
nv_connector->dcb = dcb;
connector = &nv_connector->base;
nv_connector->index = index;
/* attempt to parse vbios connector type and hotplug gpio */
nv_connector->dcb = dcb_conn(dev, index);
if (nv_connector->dcb) {
static const u8 hpd[16] = {
0xff, 0x07, 0x08, 0xff, 0xff, 0x51, 0x52, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0x5e, 0x5f, 0x60,
};
u32 entry = ROM16(nv_connector->dcb[0]);
if (dcb_conntab(dev)[3] >= 4)
entry |= (u32)ROM16(nv_connector->dcb[2]) << 16;
nv_connector->hpd = ffs((entry & 0x07033000) >> 12);
nv_connector->hpd = hpd[nv_connector->hpd];
nv_connector->type = nv_connector->dcb[0];
if (drm_conntype_from_dcb(nv_connector->type) ==
DRM_MODE_CONNECTOR_Unknown) {
NV_WARN(dev, "unknown connector type %02x\n",
nv_connector->type);
nv_connector->type = DCB_CONNECTOR_NONE;
}
/* Gigabyte NX85T */
if (nv_match_device(dev, 0x0421, 0x1458, 0x344c)) {
if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
nv_connector->type = DCB_CONNECTOR_DVI_I;
}
/* Gigabyte GV-NX86T512H */
if (nv_match_device(dev, 0x0402, 0x1458, 0x3455)) {
if (nv_connector->type == DCB_CONNECTOR_HDMI_1)
nv_connector->type = DCB_CONNECTOR_DVI_I;
}
} else {
nv_connector->type = DCB_CONNECTOR_NONE;
nv_connector->hpd = DCB_GPIO_UNUSED;
}
/* no vbios data, or an unknown dcb connector type - attempt to
* figure out something suitable ourselves
*/
if (nv_connector->type == DCB_CONNECTOR_NONE) {
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct dcb_table *dcbt = &dev_priv->vbios.dcb;
u32 encoders = 0;
int i;
for (i = 0; i < dcbt->entries; i++) {
if (dcbt->entry[i].connector == nv_connector->index)
encoders |= (1 << dcbt->entry[i].type);
}
if (encoders & (1 << OUTPUT_DP)) {
if (encoders & (1 << OUTPUT_TMDS))
nv_connector->type = DCB_CONNECTOR_DP;
else
nv_connector->type = DCB_CONNECTOR_eDP;
} else
if (encoders & (1 << OUTPUT_TMDS)) {
if (encoders & (1 << OUTPUT_ANALOG))
nv_connector->type = DCB_CONNECTOR_DVI_I;
else
nv_connector->type = DCB_CONNECTOR_DVI_D;
} else
if (encoders & (1 << OUTPUT_ANALOG)) {
nv_connector->type = DCB_CONNECTOR_VGA;
} else
if (encoders & (1 << OUTPUT_LVDS)) {
nv_connector->type = DCB_CONNECTOR_LVDS;
} else
if (encoders & (1 << OUTPUT_TV)) {
nv_connector->type = DCB_CONNECTOR_TV_0;
}
}
type = drm_conntype_from_dcb(nv_connector->type);
if (type == DRM_MODE_CONNECTOR_LVDS) {
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &dummy);
if (ret) {
NV_ERROR(dev, "Error parsing LVDS table, disabling\n");
kfree(nv_connector);
return ERR_PTR(ret);
}
funcs = &nouveau_connector_funcs_lvds;
} else {
funcs = &nouveau_connector_funcs;
}
/* defaults, will get overridden in detect() */
connector->interlace_allowed = false;
@ -836,28 +992,29 @@ nouveau_connector_create(struct drm_device *dev, int index)
drm_connector_init(dev, connector, funcs, type);
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
/* Check if we need dithering enabled */
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
bool dummy, is_24bit = false;
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit);
if (ret) {
NV_ERROR(dev, "Error parsing LVDS table, disabling "
"LVDS\n");
goto fail;
}
nv_connector->use_dithering = !is_24bit;
}
/* Init DVI-I specific properties */
if (dcb->type == DCB_CONNECTOR_DVI_I) {
drm_mode_create_dvi_i_properties(dev);
if (nv_connector->type == DCB_CONNECTOR_DVI_I)
drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
/* Add overscan compensation options to digital outputs */
if (disp->underscan_property &&
(nv_connector->type == DCB_CONNECTOR_DVI_D ||
nv_connector->type == DCB_CONNECTOR_DVI_I ||
nv_connector->type == DCB_CONNECTOR_HDMI_0 ||
nv_connector->type == DCB_CONNECTOR_HDMI_1 ||
nv_connector->type == DCB_CONNECTOR_DP)) {
drm_connector_attach_property(connector,
disp->underscan_property,
UNDERSCAN_OFF);
drm_connector_attach_property(connector,
disp->underscan_hborder_property,
0);
drm_connector_attach_property(connector,
disp->underscan_vborder_property,
0);
}
switch (dcb->type) {
switch (nv_connector->type) {
case DCB_CONNECTOR_VGA:
if (dev_priv->card_type >= NV_50) {
drm_connector_attach_property(connector,
@ -876,32 +1033,32 @@ nouveau_connector_create(struct drm_device *dev, int index)
drm_connector_attach_property(connector,
dev->mode_config.scaling_mode_property,
nv_connector->scaling_mode);
drm_connector_attach_property(connector,
dev->mode_config.dithering_mode_property,
nv_connector->use_dithering ?
DRM_MODE_DITHERING_ON : DRM_MODE_DITHERING_OFF);
if (disp->dithering_mode) {
nv_connector->dithering_mode = DITHERING_MODE_AUTO;
drm_connector_attach_property(connector,
disp->dithering_mode,
nv_connector->dithering_mode);
}
if (disp->dithering_depth) {
nv_connector->dithering_depth = DITHERING_DEPTH_AUTO;
drm_connector_attach_property(connector,
disp->dithering_depth,
nv_connector->dithering_depth);
}
break;
}
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;
connector->polled = DRM_CONNECTOR_POLL_CONNECT;
if (nv_connector->hpd != DCB_GPIO_UNUSED) {
ret = nouveau_gpio_isr_add(dev, 0, nv_connector->hpd, 0xff,
nouveau_connector_hotplug,
connector);
if (ret == 0)
connector->polled = DRM_CONNECTOR_POLL_HPD;
}
drm_sysfs_connector_add(connector);
dcb->drm = connector;
return dcb->drm;
fail:
drm_connector_cleanup(connector);
kfree(connector);
return ERR_PTR(ret);
return connector;
}
static void

View File

@ -30,13 +30,43 @@
#include "drm_edid.h"
#include "nouveau_i2c.h"
enum nouveau_underscan_type {
UNDERSCAN_OFF,
UNDERSCAN_ON,
UNDERSCAN_AUTO,
};
/* the enum values specifically defined here match nv50/nvd0 hw values, and
* the code relies on this
*/
enum nouveau_dithering_mode {
DITHERING_MODE_OFF = 0x00,
DITHERING_MODE_ON = 0x01,
DITHERING_MODE_DYNAMIC2X2 = 0x10 | DITHERING_MODE_ON,
DITHERING_MODE_STATIC2X2 = 0x18 | DITHERING_MODE_ON,
DITHERING_MODE_TEMPORAL = 0x20 | DITHERING_MODE_ON,
DITHERING_MODE_AUTO
};
enum nouveau_dithering_depth {
DITHERING_DEPTH_6BPC = 0x00,
DITHERING_DEPTH_8BPC = 0x02,
DITHERING_DEPTH_AUTO
};
struct nouveau_connector {
struct drm_connector base;
enum dcb_connector_type type;
u8 index;
u8 *dcb;
u8 hpd;
struct dcb_connector_table_entry *dcb;
int dithering_mode;
int dithering_depth;
int scaling_mode;
bool use_dithering;
enum nouveau_underscan_type underscan;
u32 underscan_hborder;
u32 underscan_vborder;
struct nouveau_encoder *detected_encoder;
struct edid *edid;

View File

@ -32,8 +32,6 @@ struct nouveau_crtc {
int index;
struct drm_display_mode *mode;
uint32_t dpms_saved_fp_control;
uint32_t fp_users;
int saturation;
@ -67,8 +65,8 @@ struct nouveau_crtc {
int depth;
} lut;
int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
int (*set_dither)(struct nouveau_crtc *crtc, bool update);
int (*set_scale)(struct nouveau_crtc *crtc, bool update);
};
static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)

View File

@ -44,7 +44,7 @@ nouveau_debugfs_channel_info(struct seq_file *m, void *data)
seq_printf(m, "channel id : %d\n", chan->id);
seq_printf(m, "cpu fifo state:\n");
seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base);
seq_printf(m, " base: 0x%10llx\n", chan->pushbuf_base);
seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2);
seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2);
seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2);

View File

@ -32,6 +32,8 @@
#include "nouveau_hw.h"
#include "nouveau_crtc.h"
#include "nouveau_dma.h"
#include "nouveau_connector.h"
#include "nouveau_gpio.h"
#include "nv50_display.h"
static void
@ -147,11 +149,186 @@ nouveau_user_framebuffer_create(struct drm_device *dev,
return &nouveau_fb->base;
}
const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
static const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
.fb_create = nouveau_user_framebuffer_create,
.output_poll_changed = nouveau_fbcon_output_poll_changed,
};
struct drm_prop_enum_list {
u8 gen_mask;
int type;
char *name;
};
static struct drm_prop_enum_list underscan[] = {
{ 6, UNDERSCAN_AUTO, "auto" },
{ 6, UNDERSCAN_OFF, "off" },
{ 6, UNDERSCAN_ON, "on" },
{}
};
static struct drm_prop_enum_list dither_mode[] = {
{ 7, DITHERING_MODE_AUTO, "auto" },
{ 7, DITHERING_MODE_OFF, "off" },
{ 1, DITHERING_MODE_ON, "on" },
{ 6, DITHERING_MODE_STATIC2X2, "static 2x2" },
{ 6, DITHERING_MODE_DYNAMIC2X2, "dynamic 2x2" },
{ 4, DITHERING_MODE_TEMPORAL, "temporal" },
{}
};
static struct drm_prop_enum_list dither_depth[] = {
{ 6, DITHERING_DEPTH_AUTO, "auto" },
{ 6, DITHERING_DEPTH_6BPC, "6 bpc" },
{ 6, DITHERING_DEPTH_8BPC, "8 bpc" },
{}
};
#define PROP_ENUM(p,gen,n,list) do { \
struct drm_prop_enum_list *l = (list); \
int c = 0; \
while (l->gen_mask) { \
if (l->gen_mask & (1 << (gen))) \
c++; \
l++; \
} \
if (c) { \
p = drm_property_create(dev, DRM_MODE_PROP_ENUM, n, c); \
l = (list); \
c = 0; \
while (p && l->gen_mask) { \
if (l->gen_mask & (1 << (gen))) { \
drm_property_add_enum(p, c, l->type, l->name); \
c++; \
} \
l++; \
} \
} \
} while(0)
int
nouveau_display_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_display_engine *disp = &dev_priv->engine.display;
struct drm_connector *connector;
int ret;
ret = disp->init(dev);
if (ret)
return ret;
drm_kms_helper_poll_enable(dev);
/* enable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, true);
}
return ret;
}
void
nouveau_display_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_display_engine *disp = &dev_priv->engine.display;
struct drm_connector *connector;
/* disable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
nouveau_gpio_irq(dev, 0, conn->hpd, 0xff, false);
}
drm_kms_helper_poll_disable(dev);
disp->fini(dev);
}
int
nouveau_display_create(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_display_engine *disp = &dev_priv->engine.display;
int ret, gen;
drm_mode_config_init(dev);
drm_mode_create_scaling_mode_property(dev);
drm_mode_create_dvi_i_properties(dev);
if (dev_priv->card_type < NV_50)
gen = 0;
else
if (dev_priv->card_type < NV_D0)
gen = 1;
else
gen = 2;
PROP_ENUM(disp->dithering_mode, gen, "dithering mode", dither_mode);
PROP_ENUM(disp->dithering_depth, gen, "dithering depth", dither_depth);
PROP_ENUM(disp->underscan_property, gen, "underscan", underscan);
disp->underscan_hborder_property =
drm_property_create(dev, DRM_MODE_PROP_RANGE,
"underscan hborder", 2);
disp->underscan_hborder_property->values[0] = 0;
disp->underscan_hborder_property->values[1] = 128;
disp->underscan_vborder_property =
drm_property_create(dev, DRM_MODE_PROP_RANGE,
"underscan vborder", 2);
disp->underscan_vborder_property->values[0] = 0;
disp->underscan_vborder_property->values[1] = 128;
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;
}
drm_kms_helper_poll_init(dev);
drm_kms_helper_poll_disable(dev);
ret = disp->create(dev);
if (ret)
return ret;
if (dev->mode_config.num_crtc) {
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
if (ret)
return ret;
}
return ret;
}
void
nouveau_display_destroy(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_display_engine *disp = &dev_priv->engine.display;
drm_vblank_cleanup(dev);
disp->destroy(dev);
drm_kms_helper_poll_fini(dev);
drm_mode_config_cleanup(dev);
}
int
nouveau_vblank_enable(struct drm_device *dev, int crtc)
{
@ -305,7 +482,10 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
/* Emit a page flip */
if (dev_priv->card_type >= NV_50) {
ret = nv50_display_flip_next(crtc, fb, chan);
if (dev_priv->card_type >= NV_D0)
ret = nvd0_display_flip_next(crtc, fb, chan, 0);
else
ret = nv50_display_flip_next(crtc, fb, chan);
if (ret) {
nouveau_channel_put(&chan);
goto fail_unreserve;

View File

@ -134,11 +134,13 @@ OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
* -EBUSY if timeout exceeded
*/
static inline int
READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)
READ_GET(struct nouveau_channel *chan, uint64_t *prev_get, int *timeout)
{
uint32_t val;
uint64_t val;
val = nvchan_rd32(chan, chan->user_get);
if (chan->user_get_hi)
val |= (uint64_t)nvchan_rd32(chan, chan->user_get_hi) << 32;
/* reset counter as long as GET is still advancing, this is
* to avoid misdetecting a GPU lockup if the GPU happens to
@ -218,8 +220,8 @@ nv50_dma_push_wait(struct nouveau_channel *chan, int count)
static int
nv50_dma_wait(struct nouveau_channel *chan, int slots, int count)
{
uint32_t cnt = 0, prev_get = 0;
int ret;
uint64_t prev_get = 0;
int ret, cnt = 0;
ret = nv50_dma_push_wait(chan, slots + 1);
if (unlikely(ret))
@ -261,8 +263,8 @@ nv50_dma_wait(struct nouveau_channel *chan, int slots, int count)
int
nouveau_dma_wait(struct nouveau_channel *chan, int slots, int size)
{
uint32_t prev_get = 0, cnt = 0;
int get;
uint64_t prev_get = 0;
int cnt = 0, get;
if (chan->dma.ib_max)
return nv50_dma_wait(chan, slots, size);

View File

@ -29,6 +29,7 @@
#include "nouveau_connector.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
#include "nouveau_gpio.h"
/******************************************************************************
* aux channel util functions
@ -273,8 +274,6 @@ nouveau_dp_tu_update(struct drm_device *dev, int or, int link, u32 clk, u32 bpp)
u8 *
nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
struct bit_entry d;
u8 *table;
int i;
@ -289,7 +288,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
return NULL;
}
table = ROMPTR(bios, d.data[0]);
table = ROMPTR(dev, d.data[0]);
if (!table) {
NV_ERROR(dev, "displayport table pointer invalid\n");
return NULL;
@ -306,7 +305,7 @@ nouveau_dp_bios_data(struct drm_device *dev, struct dcb_entry *dcb, u8 **entry)
}
for (i = 0; i < table[3]; i++) {
*entry = ROMPTR(bios, table[table[1] + (i * table[2])]);
*entry = ROMPTR(dev, table[table[1] + (i * table[2])]);
if (*entry && bios_encoder_match(dcb, ROM32((*entry)[0])))
return table;
}
@ -336,7 +335,6 @@ struct dp_state {
static void
dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int or = dp->or, link = dp->link;
u8 *entry, sink[2];
u32 dp_ctrl;
@ -360,7 +358,7 @@ dp_set_link_config(struct drm_device *dev, struct dp_state *dp)
* table, that has (among other things) pointers to more scripts that
* need to be executed, this time depending on link speed.
*/
entry = ROMPTR(&dev_priv->vbios, dp->entry[10]);
entry = ROMPTR(dev, dp->entry[10]);
if (entry) {
if (dp->table[0] < 0x30) {
while (dp->link_bw < (ROM16(entry[0]) * 10))
@ -559,8 +557,6 @@ dp_link_train_eq(struct drm_device *dev, struct dp_state *dp)
bool
nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
{
struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
struct nouveau_connector *nv_connector =
@ -581,7 +577,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
dp.dcb = nv_encoder->dcb;
dp.crtc = nv_crtc->index;
dp.auxch = auxch->rd;
dp.auxch = auxch->drive;
dp.or = nv_encoder->or;
dp.link = !(nv_encoder->dcb->sorconf.link & 1);
dp.dpcd = nv_encoder->dp.dpcd;
@ -590,7 +586,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
* we take during link training (DP_SET_POWER is one), we need
* to ignore them for the moment to avoid races.
*/
pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, false);
nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, false);
/* enable down-spreading, if possible */
if (dp.table[1] >= 16) {
@ -639,7 +635,7 @@ nouveau_dp_link_train(struct drm_encoder *encoder, u32 datarate)
nouveau_bios_run_init_table(dev, ROM16(dp.entry[8]), dp.dcb, dp.crtc);
/* re-enable hotplug detect */
pgpio->irq_enable(dev, nv_connector->dcb->gpio_tag, true);
nouveau_gpio_irq(dev, 0, nv_connector->hpd, 0xff, true);
return true;
}
@ -656,7 +652,7 @@ nouveau_dp_detect(struct drm_encoder *encoder)
if (!auxch)
return false;
ret = auxch_tx(dev, auxch->rd, 9, DP_DPCD_REV, dpcd, 8);
ret = auxch_tx(dev, auxch->drive, 9, DP_DPCD_REV, dpcd, 8);
if (ret)
return false;
@ -684,7 +680,7 @@ int
nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr)
{
return auxch_tx(auxch->dev, auxch->rd, cmd, addr, data, data_nr);
return auxch_tx(auxch->dev, auxch->drive, cmd, addr, data, data_nr);
}
static int

View File

@ -124,6 +124,10 @@ MODULE_PARM_DESC(ctxfw, "Use external HUB/GPC ucode (fermi)\n");
int nouveau_ctxfw;
module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
MODULE_PARM_DESC(ctxfw, "Santise DCB table according to MXM-SIS\n");
int nouveau_mxmdcb = 1;
module_param_named(mxmdcb, nouveau_mxmdcb, int, 0400);
int nouveau_fbpercrtc;
#if 0
module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
@ -178,8 +182,11 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
NV_INFO(dev, "Disabling fbcon acceleration...\n");
nouveau_fbcon_save_disable_accel(dev);
NV_INFO(dev, "Disabling display...\n");
nouveau_display_fini(dev);
NV_INFO(dev, "Disabling fbcon...\n");
nouveau_fbcon_set_suspend(dev, 1);
NV_INFO(dev, "Unpinning framebuffer(s)...\n");
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@ -220,7 +227,7 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
ret = dev_priv->eng[e]->fini(dev, e, true);
if (ret) {
NV_ERROR(dev, "... engine %d failed: %d\n", i, ret);
NV_ERROR(dev, "... engine %d failed: %d\n", e, ret);
goto out_abort;
}
}
@ -246,10 +253,6 @@ nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
pci_set_power_state(pdev, PCI_D3hot);
}
console_lock();
nouveau_fbcon_set_suspend(dev, 1);
console_unlock();
nouveau_fbcon_restore_accel(dev);
return 0;
out_abort:
@ -275,8 +278,6 @@ nouveau_pci_resume(struct pci_dev *pdev)
if (dev->switch_power_state == DRM_SWITCH_POWER_OFF)
return 0;
nouveau_fbcon_save_disable_accel(dev);
NV_INFO(dev, "We're back, enabling device...\n");
pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
@ -296,8 +297,6 @@ nouveau_pci_resume(struct pci_dev *pdev)
if (ret)
return ret;
nouveau_pm_resume(dev);
if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
ret = nouveau_mem_init_agp(dev);
if (ret) {
@ -337,6 +336,8 @@ nouveau_pci_resume(struct pci_dev *pdev)
}
}
nouveau_pm_resume(dev);
NV_INFO(dev, "Restoring mode...\n");
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_framebuffer *nouveau_fb;
@ -358,7 +359,19 @@ nouveau_pci_resume(struct pci_dev *pdev)
NV_ERROR(dev, "Could not pin/map cursor.\n");
}
engine->display.init(dev);
nouveau_fbcon_set_suspend(dev, 0);
nouveau_fbcon_zfill_all(dev);
nouveau_display_init(dev);
/* Force CLUT to get re-loaded during modeset */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
nv_crtc->lut.depth = 0;
}
drm_helper_resume_force_mode(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
@ -369,22 +382,6 @@ nouveau_pci_resume(struct pci_dev *pdev)
nv_crtc->cursor_saved_y);
}
/* Force CLUT to get re-loaded during modeset */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
nv_crtc->lut.depth = 0;
}
console_lock();
nouveau_fbcon_set_suspend(dev, 0);
console_unlock();
nouveau_fbcon_zfill_all(dev);
drm_helper_resume_force_mode(dev);
nouveau_fbcon_restore_accel(dev);
return 0;
}

View File

@ -163,6 +163,9 @@ enum nouveau_flags {
#define NVOBJ_ENGINE_COPY0 3
#define NVOBJ_ENGINE_COPY1 4
#define NVOBJ_ENGINE_MPEG 5
#define NVOBJ_ENGINE_PPP NVOBJ_ENGINE_MPEG
#define NVOBJ_ENGINE_BSP 6
#define NVOBJ_ENGINE_VP 7
#define NVOBJ_ENGINE_DISPLAY 15
#define NVOBJ_ENGINE_NR 16
@ -229,6 +232,7 @@ struct nouveau_channel {
/* mapping of the regs controlling the fifo */
void __iomem *user;
uint32_t user_get;
uint32_t user_get_hi;
uint32_t user_put;
/* Fencing */
@ -246,7 +250,7 @@ struct nouveau_channel {
struct nouveau_gpuobj *pushbuf;
struct nouveau_bo *pushbuf_bo;
struct nouveau_vma pushbuf_vma;
uint32_t pushbuf_base;
uint64_t pushbuf_base;
/* Notifier memory */
struct nouveau_bo *notifier_bo;
@ -393,24 +397,25 @@ struct nouveau_display_engine {
int (*early_init)(struct drm_device *);
void (*late_takedown)(struct drm_device *);
int (*create)(struct drm_device *);
int (*init)(struct drm_device *);
void (*destroy)(struct drm_device *);
int (*init)(struct drm_device *);
void (*fini)(struct drm_device *);
struct drm_property *dithering_mode;
struct drm_property *dithering_depth;
struct drm_property *underscan_property;
struct drm_property *underscan_hborder_property;
struct drm_property *underscan_vborder_property;
};
struct nouveau_gpio_engine {
void *priv;
int (*init)(struct drm_device *);
void (*takedown)(struct drm_device *);
int (*get)(struct drm_device *, enum dcb_gpio_tag);
int (*set)(struct drm_device *, enum dcb_gpio_tag, int state);
int (*irq_register)(struct drm_device *, enum dcb_gpio_tag,
void (*)(void *, int), void *);
void (*irq_unregister)(struct drm_device *, enum dcb_gpio_tag,
void (*)(void *, int), void *);
bool (*irq_enable)(struct drm_device *, enum dcb_gpio_tag, bool on);
spinlock_t lock;
struct list_head isr;
int (*init)(struct drm_device *);
void (*fini)(struct drm_device *);
int (*drive)(struct drm_device *, int line, int dir, int out);
int (*sense)(struct drm_device *, int line);
void (*irq_enable)(struct drm_device *, int line, bool);
};
struct nouveau_pm_voltage_level {
@ -484,7 +489,7 @@ struct nouveau_pm_level {
u32 copy;
u32 daemon;
u32 vdec;
u32 unk05; /* nv50:nva3, roughly.. */
u32 dom6;
u32 unka0; /* nva3:nvc0 */
u32 hub01; /* nvc0- */
u32 hub06; /* nvc0- */
@ -518,6 +523,12 @@ struct nouveau_pm_memtimings {
int nr_timing;
};
struct nouveau_pm_fan {
u32 min_duty;
u32 max_duty;
u32 pwm_freq;
};
struct nouveau_pm_engine {
struct nouveau_pm_voltage voltage;
struct nouveau_pm_level perflvl[NOUVEAU_PM_MAX_LEVEL];
@ -525,6 +536,8 @@ struct nouveau_pm_engine {
struct nouveau_pm_memtimings memtimings;
struct nouveau_pm_temp_sensor_constants sensor_constants;
struct nouveau_pm_threshold_temp threshold_temp;
struct nouveau_pm_fan fan;
u32 pwm_divisor;
struct nouveau_pm_level boot;
struct nouveau_pm_level *cur;
@ -532,19 +545,14 @@ struct nouveau_pm_engine {
struct device *hwmon;
struct notifier_block acpi_nb;
int (*clock_get)(struct drm_device *, u32 id);
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 (*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 *);
int (*fanspeed_set)(struct drm_device *, int fanspeed);
int (*pwm_get)(struct drm_device *, int line, u32*, u32*);
int (*pwm_set)(struct drm_device *, int line, u32, u32);
int (*temp_get)(struct drm_device *);
};
@ -780,6 +788,8 @@ struct drm_nouveau_private {
struct nouveau_vm *chan_vm;
struct nvbios vbios;
u8 *mxms;
struct list_head i2c_ports;
struct nv04_mode_state mode_reg;
struct nv04_mode_state saved_reg;
@ -850,6 +860,7 @@ extern char *nouveau_perflvl;
extern int nouveau_perflvl_wr;
extern int nouveau_msi;
extern int nouveau_ctxfw;
extern int nouveau_mxmdcb;
extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state);
extern int nouveau_pci_resume(struct pci_dev *pdev);
@ -1075,8 +1086,6 @@ 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 *, 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 *
nouveau_bios_connector_entry(struct drm_device *, int index);
extern u32 get_pll_register(struct drm_device *, enum pll_types);
@ -1094,11 +1103,18 @@ 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_mxm.c */
int nouveau_mxm_init(struct drm_device *dev);
void nouveau_mxm_fini(struct drm_device *dev);
/* nouveau_ttm.c */
int nouveau_ttm_global_init(struct drm_nouveau_private *);
void nouveau_ttm_global_release(struct drm_nouveau_private *);
int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
/* nouveau_hdmi.c */
void nouveau_hdmi_mode_set(struct drm_encoder *, struct drm_display_mode *);
/* nouveau_dp.c */
int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
uint8_t *data, int data_nr);
@ -1225,6 +1241,9 @@ extern int nvc0_graph_isr_chid(struct drm_device *dev, u64 inst);
/* nv84_crypt.c */
extern int nv84_crypt_create(struct drm_device *);
/* nv98_crypt.c */
extern int nv98_crypt_create(struct drm_device *dev);
/* nva3_copy.c */
extern int nva3_copy_create(struct drm_device *dev);
@ -1237,6 +1256,17 @@ extern int nv31_mpeg_create(struct drm_device *dev);
/* nv50_mpeg.c */
extern int nv50_mpeg_create(struct drm_device *dev);
/* nv84_bsp.c */
/* nv98_bsp.c */
extern int nv84_bsp_create(struct drm_device *dev);
/* nv84_vp.c */
/* nv98_vp.c */
extern int nv84_vp_create(struct drm_device *dev);
/* nv98_ppp.c */
extern int nv98_ppp_create(struct drm_device *dev);
/* nv04_instmem.c */
extern int nv04_instmem_init(struct drm_device *);
extern void nv04_instmem_takedown(struct drm_device *);
@ -1314,13 +1344,19 @@ extern int nv17_tv_create(struct drm_connector *, struct dcb_entry *);
extern int nv04_display_early_init(struct drm_device *);
extern void nv04_display_late_takedown(struct drm_device *);
extern int nv04_display_create(struct drm_device *);
extern int nv04_display_init(struct drm_device *);
extern void nv04_display_destroy(struct drm_device *);
extern int nv04_display_init(struct drm_device *);
extern void nv04_display_fini(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 *);
extern int nvd0_display_init(struct drm_device *);
extern void nvd0_display_fini(struct drm_device *);
struct nouveau_bo *nvd0_display_crtc_sema(struct drm_device *, int crtc);
void nvd0_display_flip_stop(struct drm_crtc *);
int nvd0_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
struct nouveau_channel *, u32 swap_interval);
/* nv04_crtc.c */
extern int nv04_crtc_create(struct drm_device *, int index);
@ -1415,6 +1451,10 @@ extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
struct drm_file *);
/* nouveau_display.c */
int nouveau_display_create(struct drm_device *dev);
void nouveau_display_destroy(struct drm_device *dev);
int nouveau_display_init(struct drm_device *dev);
void nouveau_display_fini(struct drm_device *dev);
int nouveau_vblank_enable(struct drm_device *dev, int crtc);
void nouveau_vblank_disable(struct drm_device *dev, int crtc);
int nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
@ -1429,23 +1469,22 @@ int nouveau_display_dumb_destroy(struct drm_file *, struct drm_device *,
uint32_t handle);
/* nv10_gpio.c */
int nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
int nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
int nv10_gpio_init(struct drm_device *dev);
void nv10_gpio_fini(struct drm_device *dev);
int nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out);
int nv10_gpio_sense(struct drm_device *dev, int line);
void nv10_gpio_irq_enable(struct drm_device *, int line, bool on);
/* nv50_gpio.c */
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,
void (*)(void *, int), void *);
bool nv50_gpio_irq_enable(struct drm_device *, enum dcb_gpio_tag, bool on);
int nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out);
int nv50_gpio_sense(struct drm_device *dev, int line);
void nv50_gpio_irq_enable(struct drm_device *, int line, bool on);
int nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out);
int nvd0_gpio_sense(struct drm_device *dev, int line);
/* nv50_calc. */
/* nv50_calc.c */
int nv50_calc_pll(struct drm_device *, struct pll_lims *, int clk,
int *N1, int *M1, int *N2, int *M2, int *P);
int nva3_calc_pll(struct drm_device *, struct pll_lims *,
@ -1568,6 +1607,13 @@ extern void nv_wo32(struct nouveau_gpuobj *, u32 offset, u32 val);
#define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg)
#define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
#define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg)
#define NV_WARNONCE(d, fmt, arg...) do { \
static int _warned = 0; \
if (!_warned) { \
NV_WARN(d, fmt, ##arg); \
_warned = 1; \
} \
} while(0)
/* nouveau_reg_debug bitmask */
enum {

View File

@ -42,8 +42,6 @@ nouveau_framebuffer(struct drm_framebuffer *fb)
return container_of(fb, struct nouveau_framebuffer, base);
}
extern const struct drm_mode_config_funcs nouveau_mode_config_funcs;
int nouveau_framebuffer_init(struct drm_device *dev, struct nouveau_framebuffer *nouveau_fb,
struct drm_mode_fb_cmd2 *mode_cmd, struct nouveau_bo *nvbo);
#endif /* __NOUVEAU_FB_H__ */

View File

@ -36,6 +36,7 @@
#include <linux/init.h>
#include <linux/screen_info.h>
#include <linux/vga_switcheroo.h>
#include <linux/console.h>
#include "drmP.h"
#include "drm.h"
@ -548,7 +549,13 @@ void nouveau_fbcon_restore_accel(struct drm_device *dev)
void nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
console_lock();
if (state == 0)
nouveau_fbcon_save_disable_accel(dev);
fb_set_suspend(dev_priv->nfbdev->helper.fbdev, state);
if (state == 1)
nouveau_fbcon_restore_accel(dev);
console_unlock();
}
void nouveau_fbcon_zfill_all(struct drm_device *dev)

View File

@ -0,0 +1,400 @@
/*
* 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_i2c.h"
#include "nouveau_gpio.h"
static u8 *
dcb_gpio_table(struct drm_device *dev)
{
u8 *dcb = dcb_table(dev);
if (dcb) {
if (dcb[0] >= 0x30 && dcb[1] >= 0x0c)
return ROMPTR(dev, dcb[0x0a]);
if (dcb[0] >= 0x22 && dcb[-1] >= 0x13)
return ROMPTR(dev, dcb[-15]);
}
return NULL;
}
static u8 *
dcb_gpio_entry(struct drm_device *dev, int idx, int ent, u8 *version)
{
u8 *table = dcb_gpio_table(dev);
if (table) {
*version = table[0];
if (*version < 0x30 && ent < table[2])
return table + 3 + (ent * table[1]);
else if (ent < table[2])
return table + table[1] + (ent * table[3]);
}
return NULL;
}
int
nouveau_gpio_drive(struct drm_device *dev, int idx, int line, int dir, int out)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
return pgpio->drive ? pgpio->drive(dev, line, dir, out) : -ENODEV;
}
int
nouveau_gpio_sense(struct drm_device *dev, int idx, int line)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
return pgpio->sense ? pgpio->sense(dev, line) : -ENODEV;
}
int
nouveau_gpio_find(struct drm_device *dev, int idx, u8 func, u8 line,
struct gpio_func *gpio)
{
u8 *table, *entry, version;
int i = -1;
if (line == 0xff && func == 0xff)
return -EINVAL;
while ((entry = dcb_gpio_entry(dev, idx, ++i, &version))) {
if (version < 0x40) {
u16 data = ROM16(entry[0]);
*gpio = (struct gpio_func) {
.line = (data & 0x001f) >> 0,
.func = (data & 0x07e0) >> 5,
.log[0] = (data & 0x1800) >> 11,
.log[1] = (data & 0x6000) >> 13,
};
} else
if (version < 0x41) {
*gpio = (struct gpio_func) {
.line = entry[0] & 0x1f,
.func = entry[1],
.log[0] = (entry[3] & 0x18) >> 3,
.log[1] = (entry[3] & 0x60) >> 5,
};
} else {
*gpio = (struct gpio_func) {
.line = entry[0] & 0x3f,
.func = entry[1],
.log[0] = (entry[4] & 0x30) >> 4,
.log[1] = (entry[4] & 0xc0) >> 6,
};
}
if ((line == 0xff || line == gpio->line) &&
(func == 0xff || func == gpio->func))
return 0;
}
/* DCB 2.2, fixed TVDAC GPIO data */
if ((table = dcb_table(dev)) && table[0] >= 0x22) {
if (func == DCB_GPIO_TVDAC0) {
*gpio = (struct gpio_func) {
.func = DCB_GPIO_TVDAC0,
.line = table[-4] >> 4,
.log[0] = !!(table[-5] & 2),
.log[1] = !(table[-5] & 2),
};
return 0;
}
}
/* Apple iMac G4 NV18 */
if (nv_match_device(dev, 0x0189, 0x10de, 0x0010)) {
if (func == DCB_GPIO_TVDAC0) {
*gpio = (struct gpio_func) {
.func = DCB_GPIO_TVDAC0,
.line = 4,
.log[0] = 0,
.log[1] = 1,
};
return 0;
}
}
return -EINVAL;
}
int
nouveau_gpio_set(struct drm_device *dev, int idx, u8 tag, u8 line, int state)
{
struct gpio_func gpio;
int ret;
ret = nouveau_gpio_find(dev, idx, tag, line, &gpio);
if (ret == 0) {
int dir = !!(gpio.log[state] & 0x02);
int out = !!(gpio.log[state] & 0x01);
ret = nouveau_gpio_drive(dev, idx, gpio.line, dir, out);
}
return ret;
}
int
nouveau_gpio_get(struct drm_device *dev, int idx, u8 tag, u8 line)
{
struct gpio_func gpio;
int ret;
ret = nouveau_gpio_find(dev, idx, tag, line, &gpio);
if (ret == 0) {
ret = nouveau_gpio_sense(dev, idx, gpio.line);
if (ret >= 0)
ret = (ret == (gpio.log[1] & 1));
}
return ret;
}
int
nouveau_gpio_irq(struct drm_device *dev, int idx, u8 tag, u8 line, bool on)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct gpio_func gpio;
int ret;
ret = nouveau_gpio_find(dev, idx, tag, line, &gpio);
if (ret == 0) {
if (idx == 0 && pgpio->irq_enable)
pgpio->irq_enable(dev, gpio.line, on);
else
ret = -ENODEV;
}
return ret;
}
struct gpio_isr {
struct drm_device *dev;
struct list_head head;
struct work_struct work;
int idx;
struct gpio_func func;
void (*handler)(void *, int);
void *data;
bool inhibit;
};
static void
nouveau_gpio_isr_bh(struct work_struct *work)
{
struct gpio_isr *isr = container_of(work, struct gpio_isr, work);
struct drm_device *dev = isr->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
unsigned long flags;
int state;
state = nouveau_gpio_get(dev, isr->idx, isr->func.func, isr->func.line);
if (state >= 0)
isr->handler(isr->data, state);
spin_lock_irqsave(&pgpio->lock, flags);
isr->inhibit = false;
spin_unlock_irqrestore(&pgpio->lock, flags);
}
void
nouveau_gpio_isr(struct drm_device *dev, int idx, u32 line_mask)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct gpio_isr *isr;
if (idx != 0)
return;
spin_lock(&pgpio->lock);
list_for_each_entry(isr, &pgpio->isr, head) {
if (line_mask & (1 << isr->func.line)) {
if (isr->inhibit)
continue;
isr->inhibit = true;
schedule_work(&isr->work);
}
}
spin_unlock(&pgpio->lock);
}
int
nouveau_gpio_isr_add(struct drm_device *dev, int idx, u8 tag, u8 line,
void (*handler)(void *, int), void *data)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct gpio_isr *isr;
unsigned long flags;
int ret;
isr = kzalloc(sizeof(*isr), GFP_KERNEL);
if (!isr)
return -ENOMEM;
ret = nouveau_gpio_find(dev, idx, tag, line, &isr->func);
if (ret) {
kfree(isr);
return ret;
}
INIT_WORK(&isr->work, nouveau_gpio_isr_bh);
isr->dev = dev;
isr->handler = handler;
isr->data = data;
isr->idx = idx;
spin_lock_irqsave(&pgpio->lock, flags);
list_add(&isr->head, &pgpio->isr);
spin_unlock_irqrestore(&pgpio->lock, flags);
return 0;
}
void
nouveau_gpio_isr_del(struct drm_device *dev, int idx, u8 tag, u8 line,
void (*handler)(void *, int), void *data)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct gpio_isr *isr, *tmp;
struct gpio_func func;
unsigned long flags;
LIST_HEAD(tofree);
int ret;
ret = nouveau_gpio_find(dev, idx, tag, line, &func);
if (ret == 0) {
spin_lock_irqsave(&pgpio->lock, flags);
list_for_each_entry_safe(isr, tmp, &pgpio->isr, head) {
if (memcmp(&isr->func, &func, sizeof(func)) ||
isr->idx != idx ||
isr->handler != handler || isr->data != data)
continue;
list_move(&isr->head, &tofree);
}
spin_unlock_irqrestore(&pgpio->lock, flags);
list_for_each_entry_safe(isr, tmp, &tofree, head) {
flush_work_sync(&isr->work);
kfree(isr);
}
}
}
int
nouveau_gpio_create(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
INIT_LIST_HEAD(&pgpio->isr);
spin_lock_init(&pgpio->lock);
return nouveau_gpio_init(dev);
}
void
nouveau_gpio_destroy(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
nouveau_gpio_fini(dev);
BUG_ON(!list_empty(&pgpio->isr));
}
int
nouveau_gpio_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
int ret = 0;
if (pgpio->init)
ret = pgpio->init(dev);
return ret;
}
void
nouveau_gpio_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
if (pgpio->fini)
pgpio->fini(dev);
}
void
nouveau_gpio_reset(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u8 *entry, version;
int ent = -1;
while ((entry = dcb_gpio_entry(dev, 0, ++ent, &version))) {
u8 func = 0xff, line, defs, unk0, unk1;
if (version >= 0x41) {
defs = !!(entry[0] & 0x80);
line = entry[0] & 0x3f;
func = entry[1];
unk0 = entry[2];
unk1 = entry[3] & 0x1f;
} else
if (version >= 0x40) {
line = entry[0] & 0x1f;
func = entry[1];
defs = !!(entry[3] & 0x01);
unk0 = !!(entry[3] & 0x02);
unk1 = !!(entry[3] & 0x04);
} else {
break;
}
if (func == 0xff)
continue;
nouveau_gpio_func_set(dev, func, defs);
if (dev_priv->card_type >= NV_D0) {
nv_mask(dev, 0x00d610 + (line * 4), 0xff, unk0);
if (unk1--)
nv_mask(dev, 0x00d640 + (unk1 * 4), 0xff, line);
} else
if (dev_priv->card_type >= NV_50) {
static const u32 regs[] = { 0xe100, 0xe28c };
u32 val = (unk1 << 16) | unk0;
u32 reg = regs[line >> 4]; line &= 0x0f;
nv_mask(dev, reg, 0x00010001 << line, val << line);
}
}
}

View File

@ -0,0 +1,71 @@
/*
* 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.
*/
#ifndef __NOUVEAU_GPIO_H__
#define __NOUVEAU_GPIO_H__
struct gpio_func {
u8 func;
u8 line;
u8 log[2];
};
/* nouveau_gpio.c */
int nouveau_gpio_create(struct drm_device *);
void nouveau_gpio_destroy(struct drm_device *);
int nouveau_gpio_init(struct drm_device *);
void nouveau_gpio_fini(struct drm_device *);
void nouveau_gpio_reset(struct drm_device *);
int nouveau_gpio_drive(struct drm_device *, int idx, int line,
int dir, int out);
int nouveau_gpio_sense(struct drm_device *, int idx, int line);
int nouveau_gpio_find(struct drm_device *, int idx, u8 tag, u8 line,
struct gpio_func *);
int nouveau_gpio_set(struct drm_device *, int idx, u8 tag, u8 line, int state);
int nouveau_gpio_get(struct drm_device *, int idx, u8 tag, u8 line);
int nouveau_gpio_irq(struct drm_device *, int idx, u8 tag, u8 line, bool on);
void nouveau_gpio_isr(struct drm_device *, int idx, u32 mask);
int nouveau_gpio_isr_add(struct drm_device *, int idx, u8 tag, u8 line,
void (*)(void *, int state), void *data);
void nouveau_gpio_isr_del(struct drm_device *, int idx, u8 tag, u8 line,
void (*)(void *, int state), void *data);
static inline bool
nouveau_gpio_func_valid(struct drm_device *dev, u8 tag)
{
struct gpio_func func;
return (nouveau_gpio_find(dev, 0, tag, 0xff, &func)) == 0;
}
static inline int
nouveau_gpio_func_set(struct drm_device *dev, u8 tag, int state)
{
return nouveau_gpio_set(dev, 0, tag, 0xff, state);
}
static inline int
nouveau_gpio_func_get(struct drm_device *dev, u8 tag)
{
return nouveau_gpio_get(dev, 0, tag, 0xff);
}
#endif

View File

@ -0,0 +1,258 @@
/*
* 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_connector.h"
#include "nouveau_encoder.h"
#include "nouveau_crtc.h"
static bool
hdmi_sor(struct drm_encoder *encoder)
{
struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
if (dev_priv->chipset < 0xa3)
return false;
return true;
}
static inline u32
hdmi_base(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_crtc *nv_crtc = nouveau_crtc(nv_encoder->crtc);
if (!hdmi_sor(encoder))
return 0x616500 + (nv_crtc->index * 0x800);
return 0x61c500 + (nv_encoder->or * 0x800);
}
static void
hdmi_wr32(struct drm_encoder *encoder, u32 reg, u32 val)
{
nv_wr32(encoder->dev, hdmi_base(encoder) + reg, val);
}
static u32
hdmi_rd32(struct drm_encoder *encoder, u32 reg)
{
return nv_rd32(encoder->dev, hdmi_base(encoder) + reg);
}
static u32
hdmi_mask(struct drm_encoder *encoder, u32 reg, u32 mask, u32 val)
{
u32 tmp = hdmi_rd32(encoder, reg);
hdmi_wr32(encoder, reg, (tmp & ~mask) | val);
return tmp;
}
static void
nouveau_audio_disconnect(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_device *dev = encoder->dev;
u32 or = nv_encoder->or * 0x800;
if (hdmi_sor(encoder)) {
nv_mask(dev, 0x61c448 + or, 0x00000003, 0x00000000);
}
}
static void
nouveau_audio_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_connector *nv_connector;
struct drm_device *dev = encoder->dev;
u32 or = nv_encoder->or * 0x800;
int i;
nv_connector = nouveau_encoder_connector_get(nv_encoder);
if (!drm_detect_monitor_audio(nv_connector->edid)) {
nouveau_audio_disconnect(encoder);
return;
}
if (hdmi_sor(encoder)) {
nv_mask(dev, 0x61c448 + or, 0x00000001, 0x00000001);
drm_edid_to_eld(&nv_connector->base, nv_connector->edid);
if (nv_connector->base.eld[0]) {
u8 *eld = nv_connector->base.eld;
for (i = 0; i < eld[2] * 4; i++)
nv_wr32(dev, 0x61c440 + or, (i << 8) | eld[i]);
for (i = eld[2] * 4; i < 0x60; i++)
nv_wr32(dev, 0x61c440 + or, (i << 8) | 0x00);
nv_mask(dev, 0x61c448 + or, 0x00000002, 0x00000002);
}
}
}
static void
nouveau_hdmi_infoframe(struct drm_encoder *encoder, u32 ctrl, u8 *frame)
{
/* calculate checksum for the infoframe */
u8 sum = 0, i;
for (i = 0; i < frame[2]; i++)
sum += frame[i];
frame[3] = 256 - sum;
/* disable infoframe, and write header */
hdmi_mask(encoder, ctrl + 0x00, 0x00000001, 0x00000000);
hdmi_wr32(encoder, ctrl + 0x08, *(u32 *)frame & 0xffffff);
/* register scans tell me the audio infoframe has only one set of
* subpack regs, according to tegra (gee nvidia, it'd be nice if we
* could get those docs too!), the hdmi block pads out the rest of
* the packet on its own.
*/
if (ctrl == 0x020)
frame[2] = 6;
/* write out checksum and data, weird weird 7 byte register pairs */
for (i = 0; i < frame[2] + 1; i += 7) {
u32 rsubpack = ctrl + 0x0c + ((i / 7) * 8);
u32 *subpack = (u32 *)&frame[3 + i];
hdmi_wr32(encoder, rsubpack + 0, subpack[0]);
hdmi_wr32(encoder, rsubpack + 4, subpack[1] & 0xffffff);
}
/* enable the infoframe */
hdmi_mask(encoder, ctrl, 0x00000001, 0x00000001);
}
static void
nouveau_hdmi_video_infoframe(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
const u8 Y = 0, A = 0, B = 0, S = 0, C = 0, M = 0, R = 0;
const u8 ITC = 0, EC = 0, Q = 0, SC = 0, VIC = 0, PR = 0;
const u8 bar_top = 0, bar_bottom = 0, bar_left = 0, bar_right = 0;
u8 frame[20];
frame[0x00] = 0x82; /* AVI infoframe */
frame[0x01] = 0x02; /* version */
frame[0x02] = 0x0d; /* length */
frame[0x03] = 0x00;
frame[0x04] = (Y << 5) | (A << 4) | (B << 2) | S;
frame[0x05] = (C << 6) | (M << 4) | R;
frame[0x06] = (ITC << 7) | (EC << 4) | (Q << 2) | SC;
frame[0x07] = VIC;
frame[0x08] = PR;
frame[0x09] = bar_top & 0xff;
frame[0x0a] = bar_top >> 8;
frame[0x0b] = bar_bottom & 0xff;
frame[0x0c] = bar_bottom >> 8;
frame[0x0d] = bar_left & 0xff;
frame[0x0e] = bar_left >> 8;
frame[0x0f] = bar_right & 0xff;
frame[0x10] = bar_right >> 8;
frame[0x11] = 0x00;
frame[0x12] = 0x00;
frame[0x13] = 0x00;
nouveau_hdmi_infoframe(encoder, 0x020, frame);
}
static void
nouveau_hdmi_audio_infoframe(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
const u8 CT = 0x00, CC = 0x01, ceaSS = 0x00, SF = 0x00, FMT = 0x00;
const u8 CA = 0x00, DM_INH = 0, LSV = 0x00;
u8 frame[12];
frame[0x00] = 0x84; /* Audio infoframe */
frame[0x01] = 0x01; /* version */
frame[0x02] = 0x0a; /* length */
frame[0x03] = 0x00;
frame[0x04] = (CT << 4) | CC;
frame[0x05] = (SF << 2) | ceaSS;
frame[0x06] = FMT;
frame[0x07] = CA;
frame[0x08] = (DM_INH << 7) | (LSV << 3);
frame[0x09] = 0x00;
frame[0x0a] = 0x00;
frame[0x0b] = 0x00;
nouveau_hdmi_infoframe(encoder, 0x000, frame);
}
static void
nouveau_hdmi_disconnect(struct drm_encoder *encoder)
{
nouveau_audio_disconnect(encoder);
/* disable audio and avi infoframes */
hdmi_mask(encoder, 0x000, 0x00000001, 0x00000000);
hdmi_mask(encoder, 0x020, 0x00000001, 0x00000000);
/* disable hdmi */
hdmi_mask(encoder, 0x0a4, 0x40000000, 0x00000000);
}
void
nouveau_hdmi_mode_set(struct drm_encoder *encoder,
struct drm_display_mode *mode)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct nouveau_connector *nv_connector;
struct drm_device *dev = encoder->dev;
u32 max_ac_packet, rekey;
nv_connector = nouveau_encoder_connector_get(nv_encoder);
if (!mode || !nv_connector || !nv_connector->edid ||
!drm_detect_hdmi_monitor(nv_connector->edid)) {
nouveau_hdmi_disconnect(encoder);
return;
}
nouveau_hdmi_video_infoframe(encoder, mode);
nouveau_hdmi_audio_infoframe(encoder, mode);
hdmi_mask(encoder, 0x0d0, 0x00070001, 0x00010001); /* SPARE, HW_CTS */
hdmi_mask(encoder, 0x068, 0x00010101, 0x00000000); /* ACR_CTRL, ?? */
hdmi_mask(encoder, 0x078, 0x80000000, 0x80000000); /* ACR_0441_ENABLE */
nv_mask(dev, 0x61733c, 0x00100000, 0x00100000); /* RESETF */
nv_mask(dev, 0x61733c, 0x10000000, 0x10000000); /* LOOKUP_EN */
nv_mask(dev, 0x61733c, 0x00100000, 0x00000000); /* !RESETF */
/* value matches nvidia binary driver, and tegra constant */
rekey = 56;
max_ac_packet = mode->htotal - mode->hdisplay;
max_ac_packet -= rekey;
max_ac_packet -= 18; /* constant from tegra */
max_ac_packet /= 32;
/* enable hdmi */
hdmi_mask(encoder, 0x0a4, 0x5f1f003f, 0x40000000 | /* enable */
0x1f000000 | /* unknown */
max_ac_packet << 16 |
rekey);
nouveau_audio_mode_set(encoder, mode);
}

View File

@ -0,0 +1,115 @@
/*
* Copyright 2010 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#ifndef __NOUVEAU_HWSQ_H__
#define __NOUVEAU_HWSQ_H__
struct hwsq_ucode {
u8 data[0x200];
union {
u8 *u08;
u16 *u16;
u32 *u32;
} ptr;
u16 len;
u32 reg;
u32 val;
};
static inline void
hwsq_init(struct hwsq_ucode *hwsq)
{
hwsq->ptr.u08 = hwsq->data;
hwsq->reg = 0xffffffff;
hwsq->val = 0xffffffff;
}
static inline void
hwsq_fini(struct hwsq_ucode *hwsq)
{
do {
*hwsq->ptr.u08++ = 0x7f;
hwsq->len = hwsq->ptr.u08 - hwsq->data;
} while (hwsq->len & 3);
hwsq->ptr.u08 = hwsq->data;
}
static inline void
hwsq_usec(struct hwsq_ucode *hwsq, u8 usec)
{
u32 shift = 0;
while (usec & ~3) {
usec >>= 2;
shift++;
}
*hwsq->ptr.u08++ = (shift << 2) | usec;
}
static inline void
hwsq_setf(struct hwsq_ucode *hwsq, u8 flag, int val)
{
flag += 0x80;
if (val >= 0)
flag += 0x20;
if (val >= 1)
flag += 0x20;
*hwsq->ptr.u08++ = flag;
}
static inline void
hwsq_op5f(struct hwsq_ucode *hwsq, u8 v0, u8 v1)
{
*hwsq->ptr.u08++ = 0x5f;
*hwsq->ptr.u08++ = v0;
*hwsq->ptr.u08++ = v1;
}
static inline void
hwsq_wr32(struct hwsq_ucode *hwsq, u32 reg, u32 val)
{
if (val != hwsq->val) {
if ((val & 0xffff0000) == (hwsq->val & 0xffff0000)) {
*hwsq->ptr.u08++ = 0x42;
*hwsq->ptr.u16++ = (val & 0x0000ffff);
} else {
*hwsq->ptr.u08++ = 0xe2;
*hwsq->ptr.u32++ = val;
}
hwsq->val = val;
}
if ((reg & 0xffff0000) == (hwsq->reg & 0xffff0000)) {
*hwsq->ptr.u08++ = 0x40;
*hwsq->ptr.u16++ = (reg & 0x0000ffff);
} else {
*hwsq->ptr.u08++ = 0xe0;
*hwsq->ptr.u32++ = reg;
}
hwsq->reg = reg;
}
#endif

View File

@ -29,262 +29,465 @@
#include "nouveau_i2c.h"
#include "nouveau_hw.h"
static void
nv04_i2c_setscl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
#define T_TIMEOUT 2200000
#define T_RISEFALL 1000
#define T_HOLD 5000
val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
static void
i2c_drive_scl(void *data, int state)
{
struct nouveau_i2c_chan *port = data;
if (port->type == 0) {
u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);
if (state) val |= 0x20;
else val &= 0xdf;
NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
} else
if (port->type == 4) {
nv_mask(port->dev, port->drive, 0x2f, state ? 0x21 : 0x01);
} else
if (port->type == 5) {
if (state) port->state |= 0x01;
else port->state &= 0xfe;
nv_wr32(port->dev, port->drive, 4 | port->state);
}
}
static void
nv04_i2c_setsda(void *data, int state)
i2c_drive_sda(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
struct nouveau_i2c_chan *port = data;
if (port->type == 0) {
u8 val = NVReadVgaCrtc(port->dev, 0, port->drive);
if (state) val |= 0x10;
else val &= 0xef;
NVWriteVgaCrtc(port->dev, 0, port->drive, val | 0x01);
} else
if (port->type == 4) {
nv_mask(port->dev, port->drive, 0x1f, state ? 0x11 : 0x01);
} else
if (port->type == 5) {
if (state) port->state |= 0x02;
else port->state &= 0xfd;
nv_wr32(port->dev, port->drive, 4 | port->state);
}
}
static int
nv04_i2c_getscl(void *data)
i2c_sense_scl(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4);
struct nouveau_i2c_chan *port = data;
struct drm_nouveau_private *dev_priv = port->dev->dev_private;
if (port->type == 0) {
return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x04);
} else
if (port->type == 4) {
return !!(nv_rd32(port->dev, port->sense) & 0x00040000);
} else
if (port->type == 5) {
if (dev_priv->card_type < NV_D0)
return !!(nv_rd32(port->dev, port->sense) & 0x01);
else
return !!(nv_rd32(port->dev, port->sense) & 0x10);
}
return 0;
}
static int
nv04_i2c_getsda(void *data)
i2c_sense_sda(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8);
struct nouveau_i2c_chan *port = data;
struct drm_nouveau_private *dev_priv = port->dev->dev_private;
if (port->type == 0) {
return !!(NVReadVgaCrtc(port->dev, 0, port->sense) & 0x08);
} else
if (port->type == 4) {
return !!(nv_rd32(port->dev, port->sense) & 0x00080000);
} else
if (port->type == 5) {
if (dev_priv->card_type < NV_D0)
return !!(nv_rd32(port->dev, port->sense) & 0x02);
else
return !!(nv_rd32(port->dev, port->sense) & 0x20);
}
return 0;
}
static void
nv4e_i2c_setscl(void *data, int state)
i2c_delay(struct nouveau_i2c_chan *port, u32 nsec)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
udelay((nsec + 500) / 1000);
}
val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
nv_wr32(dev, i2c->wr, val | 0x01);
static bool
i2c_raise_scl(struct nouveau_i2c_chan *port)
{
u32 timeout = T_TIMEOUT / T_RISEFALL;
i2c_drive_scl(port, 1);
do {
i2c_delay(port, T_RISEFALL);
} while (!i2c_sense_scl(port) && --timeout);
return timeout != 0;
}
static int
i2c_start(struct nouveau_i2c_chan *port)
{
int ret = 0;
port->state = i2c_sense_scl(port);
port->state |= i2c_sense_sda(port) << 1;
if (port->state != 3) {
i2c_drive_scl(port, 0);
i2c_drive_sda(port, 1);
if (!i2c_raise_scl(port))
ret = -EBUSY;
}
i2c_drive_sda(port, 0);
i2c_delay(port, T_HOLD);
i2c_drive_scl(port, 0);
i2c_delay(port, T_HOLD);
return ret;
}
static void
nv4e_i2c_setsda(void *data, int state)
i2c_stop(struct nouveau_i2c_chan *port)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
i2c_drive_scl(port, 0);
i2c_drive_sda(port, 0);
i2c_delay(port, T_RISEFALL);
val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
nv_wr32(dev, i2c->wr, val | 0x01);
i2c_drive_scl(port, 1);
i2c_delay(port, T_HOLD);
i2c_drive_sda(port, 1);
i2c_delay(port, T_HOLD);
}
static int
nv4e_i2c_getscl(void *data)
i2c_bitw(struct nouveau_i2c_chan *port, int sda)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
i2c_drive_sda(port, sda);
i2c_delay(port, T_RISEFALL);
return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
if (!i2c_raise_scl(port))
return -ETIMEDOUT;
i2c_delay(port, T_HOLD);
i2c_drive_scl(port, 0);
i2c_delay(port, T_HOLD);
return 0;
}
static int
nv4e_i2c_getsda(void *data)
i2c_bitr(struct nouveau_i2c_chan *port)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
int sda;
return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
i2c_drive_sda(port, 1);
i2c_delay(port, T_RISEFALL);
if (!i2c_raise_scl(port))
return -ETIMEDOUT;
i2c_delay(port, T_HOLD);
sda = i2c_sense_sda(port);
i2c_drive_scl(port, 0);
i2c_delay(port, T_HOLD);
return sda;
}
static int
i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last)
{
int i, bit;
*byte = 0;
for (i = 7; i >= 0; i--) {
bit = i2c_bitr(port);
if (bit < 0)
return bit;
*byte |= bit << i;
}
return i2c_bitw(port, last ? 1 : 0);
}
static int
i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte)
{
int i, ret;
for (i = 7; i >= 0; i--) {
ret = i2c_bitw(port, !!(byte & (1 << i)));
if (ret < 0)
return ret;
}
ret = i2c_bitr(port);
if (ret == 1) /* nack */
ret = -EIO;
return ret;
}
static int
i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg)
{
u32 addr = msg->addr << 1;
if (msg->flags & I2C_M_RD)
addr |= 1;
return i2c_put_byte(port, addr);
}
static int
i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nouveau_i2c_chan *port = (struct nouveau_i2c_chan *)adap;
struct i2c_msg *msg = msgs;
int ret = 0, mcnt = num;
while (!ret && mcnt--) {
u8 remaining = msg->len;
u8 *ptr = msg->buf;
ret = i2c_start(port);
if (ret == 0)
ret = i2c_addr(port, msg);
if (msg->flags & I2C_M_RD) {
while (!ret && remaining--)
ret = i2c_get_byte(port, ptr++, !remaining);
} else {
while (!ret && remaining--)
ret = i2c_put_byte(port, *ptr++);
}
msg++;
}
i2c_stop(port);
return (ret < 0) ? ret : num;
}
static u32
i2c_bit_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = i2c_bit_xfer,
.functionality = i2c_bit_func
};
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)
static u8 *
i2c_table(struct drm_device *dev, u8 *version)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
u8 *dcb = dcb_table(dev), *i2c = NULL;
if (dcb) {
if (dcb[0] >= 0x15)
i2c = ROMPTR(dev, dcb[2]);
if (dcb[0] >= 0x30)
i2c = ROMPTR(dev, dcb[4]);
}
return !!(nv_rd32(dev, i2c->rd) & 1);
}
/* early revisions had no version number, use dcb version */
if (i2c) {
*version = dcb[0];
if (*version >= 0x30)
*version = i2c[0];
}
static int
nv50_i2c_getsda(void *data)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!(nv_rd32(dev, i2c->rd) & 2);
}
static void
nv50_i2c_setscl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
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;
nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0));
i2c->data = state;
}
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);
return i2c;
}
int
nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
nouveau_i2c_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *i2c;
int ret;
struct nvbios *bios = &dev_priv->vbios;
struct nouveau_i2c_chan *port;
u8 *i2c, *entry, legacy[2][4] = {};
u8 version, entries, recordlen;
int ret, i;
if (entry->chan)
return -EEXIST;
INIT_LIST_HEAD(&dev_priv->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;
i2c = i2c_table(dev, &version);
if (!i2c) {
u8 *bmp = &bios->data[bios->offset];
if (bios->type != NVBIOS_BMP)
return -ENODEV;
legacy[0][0] = NV_CIO_CRE_DDC_WR__INDEX;
legacy[0][1] = NV_CIO_CRE_DDC_STATUS__INDEX;
legacy[1][0] = NV_CIO_CRE_DDC0_WR__INDEX;
legacy[1][1] = NV_CIO_CRE_DDC0_STATUS__INDEX;
/* BMP (from v4.0) has i2c info in the structure, it's in a
* fixed location on earlier VBIOS
*/
if (bmp[5] < 4)
i2c = &bios->data[0x48];
else
i2c = &bmp[0x36];
if (i2c[4]) legacy[0][0] = i2c[4];
if (i2c[5]) legacy[0][1] = i2c[5];
if (i2c[6]) legacy[1][0] = i2c[6];
if (i2c[7]) legacy[1][1] = i2c[7];
}
i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
if (i2c == NULL)
return -ENOMEM;
switch (entry->port_type) {
case 0:
i2c->bit.setsda = nv04_i2c_setsda;
i2c->bit.setscl = nv04_i2c_setscl;
i2c->bit.getsda = nv04_i2c_getsda;
i2c->bit.getscl = nv04_i2c_getscl;
i2c->rd = entry->read;
i2c->wr = entry->write;
break;
case 4:
i2c->bit.setsda = nv4e_i2c_setsda;
i2c->bit.setscl = nv4e_i2c_setscl;
i2c->bit.getsda = nv4e_i2c_getsda;
i2c->bit.getscl = nv4e_i2c_getscl;
i2c->rd = 0x600800 + entry->read;
i2c->wr = 0x600800 + entry->write;
break;
case 5:
i2c->bit.setsda = nv50_i2c_setsda;
i2c->bit.setscl = nv50_i2c_setscl;
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;
i2c->wr = entry->write;
break;
default:
NV_ERROR(dev, "DCB I2C port type %d unknown\n",
entry->port_type);
kfree(i2c);
return -EINVAL;
}
snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
"nouveau-%s-%d", pci_name(dev->pdev), index);
i2c->adapter.owner = THIS_MODULE;
i2c->adapter.dev.parent = &dev->pdev->dev;
i2c->dev = dev;
i2c_set_adapdata(&i2c->adapter, i2c);
if (entry->port_type < 6) {
i2c->adapter.algo_data = &i2c->bit;
i2c->bit.udelay = 40;
i2c->bit.timeout = usecs_to_jiffies(5000);
i2c->bit.data = i2c;
ret = i2c_bit_add_bus(&i2c->adapter);
if (i2c && version >= 0x30) {
entry = i2c[1] + i2c;
entries = i2c[2];
recordlen = i2c[3];
} else
if (i2c) {
entry = i2c;
entries = 16;
recordlen = 4;
} else {
i2c->adapter.algo = &nouveau_dp_i2c_algo;
ret = i2c_add_adapter(&i2c->adapter);
entry = legacy[0];
entries = 2;
recordlen = 4;
}
if (ret) {
NV_ERROR(dev, "Failed to register i2c %d\n", index);
kfree(i2c);
return ret;
for (i = 0; i < entries; i++, entry += recordlen) {
port = kzalloc(sizeof(*port), GFP_KERNEL);
if (port == NULL) {
nouveau_i2c_fini(dev);
return -ENOMEM;
}
port->type = entry[3];
if (version < 0x30) {
port->type &= 0x07;
if (port->type == 0x07)
port->type = 0xff;
}
if (port->type == 0xff) {
kfree(port);
continue;
}
switch (port->type) {
case 0: /* NV04:NV50 */
port->drive = entry[0];
port->sense = entry[1];
port->adapter.algo = &i2c_bit_algo;
break;
case 4: /* NV4E */
port->drive = 0x600800 + entry[1];
port->sense = port->drive;
port->adapter.algo = &i2c_bit_algo;
break;
case 5: /* NV50- */
port->drive = entry[0] & 0x0f;
if (dev_priv->card_type < NV_D0) {
if (port->drive >= ARRAY_SIZE(nv50_i2c_port))
break;
port->drive = nv50_i2c_port[port->drive];
port->sense = port->drive;
} else {
port->drive = 0x00d014 + (port->drive * 0x20);
port->sense = port->drive;
}
port->adapter.algo = &i2c_bit_algo;
break;
case 6: /* NV50- DP AUX */
port->drive = entry[0];
port->sense = port->drive;
port->adapter.algo = &nouveau_dp_i2c_algo;
break;
default:
break;
}
if (!port->adapter.algo) {
NV_ERROR(dev, "I2C%d: type %d index %x/%x unknown\n",
i, port->type, port->drive, port->sense);
kfree(port);
continue;
}
snprintf(port->adapter.name, sizeof(port->adapter.name),
"nouveau-%s-%d", pci_name(dev->pdev), i);
port->adapter.owner = THIS_MODULE;
port->adapter.dev.parent = &dev->pdev->dev;
port->dev = dev;
port->index = i;
port->dcb = ROM32(entry[0]);
i2c_set_adapdata(&port->adapter, i2c);
ret = i2c_add_adapter(&port->adapter);
if (ret) {
NV_ERROR(dev, "I2C%d: failed register: %d\n", i, ret);
kfree(port);
continue;
}
list_add_tail(&port->head, &dev_priv->i2c_ports);
}
entry->chan = i2c;
return 0;
}
void
nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry)
nouveau_i2c_fini(struct drm_device *dev)
{
if (!entry->chan)
return;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *port, *tmp;
i2c_del_adapter(&entry->chan->adapter);
kfree(entry->chan);
entry->chan = NULL;
list_for_each_entry_safe(port, tmp, &dev_priv->i2c_ports, head) {
i2c_del_adapter(&port->adapter);
kfree(port);
}
}
struct nouveau_i2c_chan *
nouveau_i2c_find(struct drm_device *dev, int index)
nouveau_i2c_find(struct drm_device *dev, u8 index)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct dcb_i2c_entry *i2c = &dev_priv->vbios.dcb.i2c[index];
struct nouveau_i2c_chan *port;
if (index >= DCB_MAX_NUM_I2C_ENTRIES)
if (index == NV_I2C_DEFAULT(0) ||
index == NV_I2C_DEFAULT(1)) {
u8 version, *i2c = i2c_table(dev, &version);
if (i2c && version >= 0x30) {
if (index == NV_I2C_DEFAULT(0))
index = (i2c[4] & 0x0f);
else
index = (i2c[4] & 0xf0) >> 4;
} else {
index = 2;
}
}
list_for_each_entry(port, &dev_priv->i2c_ports, head) {
if (port->index == index)
break;
}
if (&port->head == &dev_priv->i2c_ports)
return NULL;
if (dev_priv->card_type >= NV_50 && (i2c->entry & 0x00000100)) {
uint32_t reg = 0xe500, val;
if (i2c->port_type == 6) {
reg += i2c->read * 0x50;
if (dev_priv->card_type >= NV_50 && (port->dcb & 0x00000100)) {
u32 reg = 0x00e500, val;
if (port->type == 6) {
reg += port->drive * 0x50;
val = 0x2002;
} else {
reg += ((i2c->entry & 0x1e00) >> 9) * 0x50;
reg += ((port->dcb & 0x1e00) >> 9) * 0x50;
val = 0xe001;
}
@ -294,9 +497,7 @@ nouveau_i2c_find(struct drm_device *dev, int index)
nv_mask(dev, reg + 0x00, 0x0000f003, val);
}
if (!i2c->chan && nouveau_i2c_init(dev, i2c, index))
return NULL;
return i2c->chan;
return port;
}
bool
@ -331,9 +532,13 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what,
struct nouveau_i2c_chan *i2c = nouveau_i2c_find(dev, index);
int i;
NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, index);
if (!i2c) {
NV_DEBUG(dev, "No bus when probing %s on %d\n", what, index);
return -ENODEV;
}
for (i = 0; i2c && info[i].addr; i++) {
NV_DEBUG(dev, "Probing %ss on I2C bus: %d\n", what, i2c->index);
for (i = 0; info[i].addr; i++) {
if (nouveau_probe_i2c_addr(i2c, info[i].addr) &&
(!match || match(i2c, &info[i]))) {
NV_INFO(dev, "Detected %s: %s\n", what, info[i].type);
@ -342,6 +547,5 @@ nouveau_i2c_identify(struct drm_device *dev, const char *what,
}
NV_DEBUG(dev, "No devices found.\n");
return -ENODEV;
}

View File

@ -27,20 +27,25 @@
#include <linux/i2c-algo-bit.h>
#include "drm_dp_helper.h"
struct dcb_i2c_entry;
#define NV_I2C_PORT(n) (0x00 + (n))
#define NV_I2C_PORT_NUM 0x10
#define NV_I2C_DEFAULT(n) (0x80 + (n))
struct nouveau_i2c_chan {
struct i2c_adapter adapter;
struct drm_device *dev;
struct i2c_algo_bit_data bit;
unsigned rd;
unsigned wr;
unsigned data;
struct list_head head;
u8 index;
u8 type;
u32 dcb;
u32 drive;
u32 sense;
u32 state;
};
int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
int nouveau_i2c_init(struct drm_device *);
void nouveau_i2c_fini(struct drm_device *);
struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, u8 index);
bool nouveau_probe_i2c_addr(struct nouveau_i2c_chan *i2c, int addr);
int nouveau_i2c_identify(struct drm_device *dev, const char *what,
struct i2c_board_info *info,

View File

@ -644,10 +644,10 @@ nouveau_mem_timing_init(struct drm_device *dev)
return;
if (P.version == 1)
hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[4]);
hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[4]);
else
if (P.version == 2)
hdr = (struct nouveau_pm_tbl_header *) ROMPTR(bios, P.data[8]);
hdr = (struct nouveau_pm_tbl_header *) ROMPTR(dev, P.data[8]);
else {
NV_WARN(dev, "unknown mem for BIT P %d\n", P.version);
}

View File

@ -0,0 +1,677 @@
/*
* Copyright 2011 Red Hat Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* Authors: Ben Skeggs
*/
#include <linux/acpi.h>
#include "drmP.h"
#include "nouveau_drv.h"
#define MXM_DBG(dev, fmt, args...) NV_DEBUG((dev), "MXM: " fmt, ##args)
#define MXM_MSG(dev, fmt, args...) NV_INFO((dev), "MXM: " fmt, ##args)
static u8 *
mxms_data(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
return dev_priv->mxms;
}
static u16
mxms_version(struct drm_device *dev)
{
u8 *mxms = mxms_data(dev);
u16 version = (mxms[4] << 8) | mxms[5];
switch (version ) {
case 0x0200:
case 0x0201:
case 0x0300:
return version;
default:
break;
}
MXM_DBG(dev, "unknown version %d.%d\n", mxms[4], mxms[5]);
return 0x0000;
}
static u16
mxms_headerlen(struct drm_device *dev)
{
return 8;
}
static u16
mxms_structlen(struct drm_device *dev)
{
return *(u16 *)&mxms_data(dev)[6];
}
static bool
mxms_checksum(struct drm_device *dev)
{
u16 size = mxms_headerlen(dev) + mxms_structlen(dev);
u8 *mxms = mxms_data(dev), sum = 0;
while (size--)
sum += *mxms++;
if (sum) {
MXM_DBG(dev, "checksum invalid\n");
return false;
}
return true;
}
static bool
mxms_valid(struct drm_device *dev)
{
u8 *mxms = mxms_data(dev);
if (*(u32 *)mxms != 0x5f4d584d) {
MXM_DBG(dev, "signature invalid\n");
return false;
}
if (!mxms_version(dev) || !mxms_checksum(dev))
return false;
return true;
}
static bool
mxms_foreach(struct drm_device *dev, u8 types,
bool (*exec)(struct drm_device *, u8 *, void *), void *info)
{
u8 *mxms = mxms_data(dev);
u8 *desc = mxms + mxms_headerlen(dev);
u8 *fini = desc + mxms_structlen(dev) - 1;
while (desc < fini) {
u8 type = desc[0] & 0x0f;
u8 headerlen = 0;
u8 recordlen = 0;
u8 entries = 0;
switch (type) {
case 0: /* Output Device Structure */
if (mxms_version(dev) >= 0x0300)
headerlen = 8;
else
headerlen = 6;
break;
case 1: /* System Cooling Capability Structure */
case 2: /* Thermal Structure */
case 3: /* Input Power Structure */
headerlen = 4;
break;
case 4: /* GPIO Device Structure */
headerlen = 4;
recordlen = 2;
entries = (ROM32(desc[0]) & 0x01f00000) >> 20;
break;
case 5: /* Vendor Specific Structure */
headerlen = 8;
break;
case 6: /* Backlight Control Structure */
if (mxms_version(dev) >= 0x0300) {
headerlen = 4;
recordlen = 8;
entries = (desc[1] & 0xf0) >> 4;
} else {
headerlen = 8;
}
break;
case 7: /* Fan Control Structure */
headerlen = 8;
recordlen = 4;
entries = desc[1] & 0x07;
break;
default:
MXM_DBG(dev, "unknown descriptor type %d\n", type);
return false;
}
if ((drm_debug & DRM_UT_DRIVER) && (exec == NULL)) {
static const char * mxms_desc_name[] = {
"ODS", "SCCS", "TS", "IPS",
"GSD", "VSS", "BCS", "FCS",
};
u8 *dump = desc;
int i, j;
MXM_DBG(dev, "%4s: ", mxms_desc_name[type]);
for (j = headerlen - 1; j >= 0; j--)
printk("%02x", dump[j]);
printk("\n");
dump += headerlen;
for (i = 0; i < entries; i++, dump += recordlen) {
MXM_DBG(dev, " ");
for (j = recordlen - 1; j >= 0; j--)
printk("%02x", dump[j]);
printk("\n");
}
}
if (types & (1 << type)) {
if (!exec(dev, desc, info))
return false;
}
desc += headerlen + (entries * recordlen);
}
return true;
}
static u8 *
mxm_table(struct drm_device *dev, u8 *size)
{
struct bit_entry x;
if (bit_table(dev, 'x', &x)) {
MXM_DBG(dev, "BIT 'x' table not present\n");
return NULL;
}
if (x.version != 1 || x.length < 3) {
MXM_MSG(dev, "BIT x table %d/%d unknown\n",
x.version, x.length);
return NULL;
}
*size = x.length;
return x.data;
}
/* These map MXM v2.x digital connection values to the appropriate SOR/link,
* hopefully they're correct for all boards within the same chipset...
*
* MXM v3.x VBIOS are nicer and provide pointers to these tables.
*/
static u8 nv84_sor_map[16] = {
0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 nv92_sor_map[16] = {
0x00, 0x12, 0x22, 0x11, 0x32, 0x31, 0x11, 0x31,
0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 nv94_sor_map[16] = {
0x00, 0x14, 0x24, 0x11, 0x34, 0x31, 0x11, 0x31,
0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 nv96_sor_map[16] = {
0x00, 0x14, 0x24, 0x00, 0x34, 0x00, 0x11, 0x31,
0x11, 0x31, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8 nv98_sor_map[16] = {
0x00, 0x14, 0x12, 0x11, 0x00, 0x31, 0x11, 0x31,
0x11, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static u8
mxm_sor_map(struct drm_device *dev, u8 conn)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u8 len, *mxm = mxm_table(dev, &len);
if (mxm && len >= 6) {
u8 *map = ROMPTR(dev, mxm[4]);
if (map) {
if (map[0] == 0x10) {
if (conn < map[3])
return map[map[1] + conn];
return 0x00;
}
MXM_MSG(dev, "unknown sor map 0x%02x\n", map[0]);
}
}
if (dev_priv->chipset == 0x84 || dev_priv->chipset == 0x86)
return nv84_sor_map[conn];
if (dev_priv->chipset == 0x92)
return nv92_sor_map[conn];
if (dev_priv->chipset == 0x94)
return nv94_sor_map[conn];
if (dev_priv->chipset == 0x96)
return nv96_sor_map[conn];
if (dev_priv->chipset == 0x98)
return nv98_sor_map[conn];
MXM_MSG(dev, "missing sor map\n");
return 0x00;
}
static u8
mxm_ddc_map(struct drm_device *dev, u8 port)
{
u8 len, *mxm = mxm_table(dev, &len);
if (mxm && len >= 8) {
u8 *map = ROMPTR(dev, mxm[6]);
if (map) {
if (map[0] == 0x10) {
if (port < map[3])
return map[map[1] + port];
return 0x00;
}
MXM_MSG(dev, "unknown ddc map 0x%02x\n", map[0]);
}
}
/* v2.x: directly write port as dcb i2cidx */
return (port << 4) | port;
}
struct mxms_odev {
u8 outp_type;
u8 conn_type;
u8 ddc_port;
u8 dig_conn;
};
static void
mxms_output_device(struct drm_device *dev, u8 *pdata, struct mxms_odev *desc)
{
u64 data = ROM32(pdata[0]);
if (mxms_version(dev) >= 0x0300)
data |= (u64)ROM16(pdata[4]) << 32;
desc->outp_type = (data & 0x00000000000000f0ULL) >> 4;
desc->ddc_port = (data & 0x0000000000000f00ULL) >> 8;
desc->conn_type = (data & 0x000000000001f000ULL) >> 12;
desc->dig_conn = (data & 0x0000000000780000ULL) >> 19;
}
struct context {
u32 *outp;
struct mxms_odev desc;
};
static bool
mxm_match_tmds_partner(struct drm_device *dev, u8 *data, void *info)
{
struct context *ctx = info;
struct mxms_odev desc;
mxms_output_device(dev, data, &desc);
if (desc.outp_type == 2 &&
desc.dig_conn == ctx->desc.dig_conn)
return false;
return true;
}
static bool
mxm_match_dcb(struct drm_device *dev, u8 *data, void *info)
{
struct context *ctx = info;
u64 desc = *(u64 *)data;
mxms_output_device(dev, data, &ctx->desc);
/* match dcb encoder type to mxm-ods device type */
if ((ctx->outp[0] & 0x0000000f) != ctx->desc.outp_type)
return true;
/* digital output, have some extra stuff to match here, there's a
* table in the vbios that provides a mapping from the mxm digital
* connection enum values to SOR/link
*/
if ((desc & 0x00000000000000f0) >= 0x20) {
/* check against sor index */
u8 link = mxm_sor_map(dev, ctx->desc.dig_conn);
if ((ctx->outp[0] & 0x0f000000) != (link & 0x0f) << 24)
return true;
/* check dcb entry has a compatible link field */
link = (link & 0x30) >> 4;
if ((link & ((ctx->outp[1] & 0x00000030) >> 4)) != link)
return true;
}
/* mark this descriptor accounted for by setting invalid device type,
* except of course some manufactures don't follow specs properly and
* we need to avoid killing off the TMDS function on DP connectors
* if MXM-SIS is missing an entry for it.
*/
data[0] &= ~0xf0;
if (ctx->desc.outp_type == 6 && ctx->desc.conn_type == 6 &&
mxms_foreach(dev, 0x01, mxm_match_tmds_partner, ctx)) {
data[0] |= 0x20; /* modify descriptor to match TMDS now */
} else {
data[0] |= 0xf0;
}
return false;
}
static int
mxm_dcb_sanitise_entry(struct drm_device *dev, void *data, int idx, u8 *dcbe)
{
struct context ctx = { .outp = (u32 *)dcbe };
u8 type, i2cidx, link;
u8 *conn;
/* look for an output device structure that matches this dcb entry.
* if one isn't found, disable it.
*/
if (mxms_foreach(dev, 0x01, mxm_match_dcb, &ctx)) {
MXM_DBG(dev, "disable %d: 0x%08x 0x%08x\n",
idx, ctx.outp[0], ctx.outp[1]);
ctx.outp[0] |= 0x0000000f;
return 0;
}
/* modify the output's ddc/aux port, there's a pointer to a table
* with the mapping from mxm ddc/aux port to dcb i2c_index in the
* vbios mxm table
*/
i2cidx = mxm_ddc_map(dev, ctx.desc.ddc_port);
if ((ctx.outp[0] & 0x0000000f) != OUTPUT_DP)
i2cidx = (i2cidx & 0x0f) << 4;
else
i2cidx = (i2cidx & 0xf0);
if (i2cidx != 0xf0) {
ctx.outp[0] &= ~0x000000f0;
ctx.outp[0] |= i2cidx;
}
/* override dcb sorconf.link, based on what mxm data says */
switch (ctx.desc.outp_type) {
case 0x00: /* Analog CRT */
case 0x01: /* Analog TV/HDTV */
break;
default:
link = mxm_sor_map(dev, ctx.desc.dig_conn) & 0x30;
ctx.outp[1] &= ~0x00000030;
ctx.outp[1] |= link;
break;
}
/* we may need to fixup various other vbios tables based on what
* the descriptor says the connector type should be.
*
* in a lot of cases, the vbios tables will claim DVI-I is possible,
* and the mxm data says the connector is really HDMI. another
* common example is DP->eDP.
*/
conn = dcb_conn(dev, (ctx.outp[0] & 0x0000f000) >> 12);
type = conn[0];
switch (ctx.desc.conn_type) {
case 0x01: /* LVDS */
ctx.outp[1] |= 0x00000004; /* use_power_scripts */
/* XXX: modify default link width in LVDS table */
break;
case 0x02: /* HDMI */
type = DCB_CONNECTOR_HDMI_1;
break;
case 0x03: /* DVI-D */
type = DCB_CONNECTOR_DVI_D;
break;
case 0x0e: /* eDP, falls through to DPint */
ctx.outp[1] |= 0x00010000;
case 0x07: /* DP internal, wtf is this?? HP8670w */
ctx.outp[1] |= 0x00000004; /* use_power_scripts? */
type = DCB_CONNECTOR_eDP;
break;
default:
break;
}
if (mxms_version(dev) >= 0x0300)
conn[0] = type;
return 0;
}
static bool
mxm_show_unmatched(struct drm_device *dev, u8 *data, void *info)
{
u64 desc = *(u64 *)data;
if ((desc & 0xf0) != 0xf0)
MXM_MSG(dev, "unmatched output device 0x%016llx\n", desc);
return true;
}
static void
mxm_dcb_sanitise(struct drm_device *dev)
{
u8 *dcb = dcb_table(dev);
if (!dcb || dcb[0] != 0x40) {
MXM_DBG(dev, "unsupported DCB version\n");
return;
}
dcb_outp_foreach(dev, NULL, mxm_dcb_sanitise_entry);
mxms_foreach(dev, 0x01, mxm_show_unmatched, NULL);
}
static bool
mxm_shadow_rom_fetch(struct nouveau_i2c_chan *i2c, u8 addr,
u8 offset, u8 size, u8 *data)
{
struct i2c_msg msgs[] = {
{ .addr = addr, .flags = 0, .len = 1, .buf = &offset },
{ .addr = addr, .flags = I2C_M_RD, .len = size, .buf = data, },
};
return i2c_transfer(&i2c->adapter, msgs, 2) == 2;
}
static bool
mxm_shadow_rom(struct drm_device *dev, u8 version)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *i2c = NULL;
u8 i2cidx, mxms[6], addr, size;
i2cidx = mxm_ddc_map(dev, 1 /* LVDS_DDC */) & 0x0f;
if (i2cidx < 0x0f)
i2c = nouveau_i2c_find(dev, i2cidx);
if (!i2c)
return false;
addr = 0x54;
if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms)) {
addr = 0x56;
if (!mxm_shadow_rom_fetch(i2c, addr, 0, 6, mxms))
return false;
}
dev_priv->mxms = mxms;
size = mxms_headerlen(dev) + mxms_structlen(dev);
dev_priv->mxms = kmalloc(size, GFP_KERNEL);
if (dev_priv->mxms &&
mxm_shadow_rom_fetch(i2c, addr, 0, size, dev_priv->mxms))
return true;
kfree(dev_priv->mxms);
dev_priv->mxms = NULL;
return false;
}
#if defined(CONFIG_ACPI)
static bool
mxm_shadow_dsm(struct drm_device *dev, u8 version)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
static char muid[] = {
0x00, 0xA4, 0x04, 0x40, 0x7D, 0x91, 0xF2, 0x4C,
0xB8, 0x9C, 0x79, 0xB6, 0x2F, 0xD5, 0x56, 0x65
};
u32 mxms_args[] = { 0x00000000 };
union acpi_object args[4] = {
/* _DSM MUID */
{ .buffer.type = 3,
.buffer.length = sizeof(muid),
.buffer.pointer = muid,
},
/* spec says this can be zero to mean "highest revision", but
* of course there's at least one bios out there which fails
* unless you pass in exactly the version it supports..
*/
{ .integer.type = ACPI_TYPE_INTEGER,
.integer.value = (version & 0xf0) << 4 | (version & 0x0f),
},
/* MXMS function */
{ .integer.type = ACPI_TYPE_INTEGER,
.integer.value = 0x00000010,
},
/* Pointer to MXMS arguments */
{ .buffer.type = ACPI_TYPE_BUFFER,
.buffer.length = sizeof(mxms_args),
.buffer.pointer = (char *)mxms_args,
},
};
struct acpi_object_list list = { ARRAY_SIZE(args), args };
struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_handle handle;
int ret;
handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
if (!handle)
return false;
ret = acpi_evaluate_object(handle, "_DSM", &list, &retn);
if (ret) {
MXM_DBG(dev, "DSM MXMS failed: %d\n", ret);
return false;
}
obj = retn.pointer;
if (obj->type == ACPI_TYPE_BUFFER) {
dev_priv->mxms = kmemdup(obj->buffer.pointer,
obj->buffer.length, GFP_KERNEL);
} else
if (obj->type == ACPI_TYPE_INTEGER) {
MXM_DBG(dev, "DSM MXMS returned 0x%llx\n", obj->integer.value);
}
kfree(obj);
return dev_priv->mxms != NULL;
}
#endif
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
#define WMI_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0"
static bool
mxm_shadow_wmi(struct drm_device *dev, u8 version)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 mxms_args[] = { 0x534D584D /* MXMS */, version, 0 };
struct acpi_buffer args = { sizeof(mxms_args), mxms_args };
struct acpi_buffer retn = { ACPI_ALLOCATE_BUFFER, NULL };
union acpi_object *obj;
acpi_status status;
if (!wmi_has_guid(WMI_WMMX_GUID))
return false;
status = wmi_evaluate_method(WMI_WMMX_GUID, 0, 0, &args, &retn);
if (ACPI_FAILURE(status)) {
MXM_DBG(dev, "WMMX MXMS returned %d\n", status);
return false;
}
obj = retn.pointer;
if (obj->type == ACPI_TYPE_BUFFER) {
dev_priv->mxms = kmemdup(obj->buffer.pointer,
obj->buffer.length, GFP_KERNEL);
}
kfree(obj);
return dev_priv->mxms != NULL;
}
#endif
struct mxm_shadow_h {
const char *name;
bool (*exec)(struct drm_device *, u8 version);
} _mxm_shadow[] = {
{ "ROM", mxm_shadow_rom },
#if defined(CONFIG_ACPI)
{ "DSM", mxm_shadow_dsm },
#endif
#if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE)
{ "WMI", mxm_shadow_wmi },
#endif
{}
};
static int
mxm_shadow(struct drm_device *dev, u8 version)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct mxm_shadow_h *shadow = _mxm_shadow;
do {
MXM_DBG(dev, "checking %s\n", shadow->name);
if (shadow->exec(dev, version)) {
if (mxms_valid(dev))
return 0;
kfree(dev_priv->mxms);
dev_priv->mxms = NULL;
}
} while ((++shadow)->name);
return -ENOENT;
}
int
nouveau_mxm_init(struct drm_device *dev)
{
u8 mxm_size, *mxm = mxm_table(dev, &mxm_size);
if (!mxm || !mxm[0]) {
MXM_MSG(dev, "no VBIOS data, nothing to do\n");
return 0;
}
MXM_MSG(dev, "BIOS version %d.%d\n", mxm[0] >> 4, mxm[0] & 0x0f);
if (mxm_shadow(dev, mxm[0])) {
MXM_MSG(dev, "failed to locate valid SIS\n");
return -EINVAL;
}
MXM_MSG(dev, "MXMS Version %d.%d\n",
mxms_version(dev) >> 8, mxms_version(dev) & 0xff);
mxms_foreach(dev, 0, NULL, NULL);
if (nouveau_mxmdcb)
mxm_dcb_sanitise(dev);
return 0;
}
void
nouveau_mxm_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
kfree(dev_priv->mxms);
dev_priv->mxms = NULL;
}

View File

@ -115,7 +115,7 @@ nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *nobj = NULL;
struct drm_mm_node *mem;
uint32_t offset;
uint64_t offset;
int target, ret;
mem = drm_mm_search_free_in_range(&chan->notifier_heap, size, 0,

View File

@ -723,14 +723,14 @@ 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 < dev->mode_config.num_crtc; i++) {
struct nouveau_bo *bo;
if (dev_priv->card_type >= NV_D0)
bo = nvd0_display_crtc_sema(dev, i);
else
bo = nv50_display(dev)->crtc[i].sem.bo;
for (i = 0; i < 2; i++) {
struct nv50_display_crtc *dispc = &nv50_display(dev)->crtc[i];
ret = nouveau_bo_vma_add(dispc->sem.bo, chan->vm,
&chan->dispc_vma[i]);
ret = nouveau_bo_vma_add(bo, chan->vm, &chan->dispc_vma[i]);
if (ret)
return ret;
}
@ -879,9 +879,14 @@ nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
NV_DEBUG(dev, "ch%d\n", chan->id);
if (dev_priv->card_type >= NV_50 && dev_priv->card_type <= NV_C0) {
if (dev_priv->card_type >= NV_D0) {
for (i = 0; i < dev->mode_config.num_crtc; i++) {
struct nouveau_bo *bo = nvd0_display_crtc_sema(dev, i);
nouveau_bo_vma_del(bo, &chan->dispc_vma[i]);
}
} else
if (dev_priv->card_type >= NV_50) {
struct nv50_display *disp = nv50_display(dev);
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]);

View File

@ -41,7 +41,7 @@ legacy_perf_init(struct drm_device *dev)
return;
}
perf = ROMPTR(bios, bmp[0x73]);
perf = ROMPTR(dev, bmp[0x73]);
if (!perf) {
NV_DEBUG(dev, "No memclock table pointer found.\n");
return;
@ -87,7 +87,7 @@ nouveau_perf_timing(struct drm_device *dev, struct bit_entry *P,
* ramcfg to select the correct subentry
*/
if (P->version == 2) {
u8 *tmap = ROMPTR(bios, P->data[4]);
u8 *tmap = ROMPTR(dev, P->data[4]);
if (!tmap) {
NV_DEBUG(dev, "no timing map pointer\n");
return NULL;
@ -140,7 +140,6 @@ 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;
@ -165,7 +164,7 @@ nouveau_perf_voltage(struct drm_device *dev, struct bit_entry *P,
return;
}
vmap = ROMPTR(bios, P->data[32]);
vmap = ROMPTR(dev, P->data[32]);
if (!vmap) {
NV_DEBUG(dev, "volt map table pointer invalid\n");
return;
@ -200,12 +199,14 @@ nouveau_perf_init(struct drm_device *dev)
return;
}
perf = ROMPTR(bios, P.data[0]);
perf = ROMPTR(dev, P.data[0]);
version = perf[0];
headerlen = perf[1];
if (version < 0x40) {
recordlen = perf[3] + (perf[4] * perf[5]);
entries = perf[2];
pm->pwm_divisor = ROM16(perf[6]);
} else {
recordlen = perf[2] + (perf[3] * perf[4]);
entries = perf[5];
@ -216,7 +217,7 @@ nouveau_perf_init(struct drm_device *dev)
return;
}
perf = ROMPTR(bios, bios->data[bios->offset + 0x94]);
perf = ROMPTR(dev, bios->data[bios->offset + 0x94]);
if (!perf) {
NV_DEBUG(dev, "perf table pointer invalid\n");
return;
@ -283,7 +284,6 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->memory = ROM16(entry[11]) * 1000;
else
perflvl->memory = ROM16(entry[11]) * 2000;
break;
case 0x25:
perflvl->fanspeed = entry[4];
@ -300,8 +300,8 @@ nouveau_perf_init(struct drm_device *dev)
perflvl->core = ROM16(entry[8]) * 1000;
perflvl->shader = ROM16(entry[10]) * 1000;
perflvl->memory = ROM16(entry[12]) * 1000;
/*XXX: confirm on 0x35 */
perflvl->unk05 = ROM16(entry[16]) * 1000;
perflvl->vdec = ROM16(entry[16]) * 1000;
perflvl->dom6 = ROM16(entry[20]) * 1000;
break;
case 0x40:
#define subent(n) (ROM16(entry[perf[2] + ((n) * perf[3])]) & 0xfff) * 1000

View File

@ -26,6 +26,7 @@
#include "nouveau_drv.h"
#include "nouveau_pm.h"
#include "nouveau_gpio.h"
#ifdef CONFIG_ACPI
#include <linux/acpi.h>
@ -35,22 +36,95 @@
#include <linux/hwmon-sysfs.h>
static int
nouveau_pm_clock_set(struct drm_device *dev, struct nouveau_pm_level *perflvl,
u8 id, u32 khz)
nouveau_pwmfan_get(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
void *pre_state;
struct gpio_func gpio;
u32 divs, duty;
int ret;
if (khz == 0)
return 0;
if (!pm->pwm_get)
return -ENODEV;
pre_state = pm->clock_pre(dev, perflvl, id, khz);
if (IS_ERR(pre_state))
return PTR_ERR(pre_state);
ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
if (ret == 0) {
ret = pm->pwm_get(dev, gpio.line, &divs, &duty);
if (ret == 0) {
divs = max(divs, duty);
if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
duty = divs - duty;
return (duty * 100) / divs;
}
return nouveau_gpio_func_get(dev, gpio.func) * 100;
}
return -ENODEV;
}
static int
nouveau_pwmfan_set(struct drm_device *dev, int percent)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct gpio_func gpio;
u32 divs, duty;
int ret;
if (!pm->pwm_set)
return -ENODEV;
ret = nouveau_gpio_find(dev, 0, DCB_GPIO_PWM_FAN, 0xff, &gpio);
if (ret == 0) {
divs = pm->pwm_divisor;
if (pm->fan.pwm_freq) {
/*XXX: PNVIO clock more than likely... */
divs = 135000 / pm->fan.pwm_freq;
if (dev_priv->chipset < 0xa3)
divs /= 4;
}
duty = ((divs * percent) + 99) / 100;
if (dev_priv->card_type <= NV_40 || (gpio.log[0] & 1))
duty = divs - duty;
return pm->pwm_set(dev, gpio.line, divs, duty);
}
return -ENODEV;
}
static int
nouveau_pm_perflvl_aux(struct drm_device *dev, struct nouveau_pm_level *perflvl,
struct nouveau_pm_level *a, struct nouveau_pm_level *b)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
int ret;
/*XXX: not on all boards, we should control based on temperature
* on recent boards.. or maybe on some other factor we don't
* know about?
*/
if (a->fanspeed && b->fanspeed && b->fanspeed > a->fanspeed) {
ret = nouveau_pwmfan_set(dev, perflvl->fanspeed);
if (ret && ret != -ENODEV) {
NV_ERROR(dev, "fanspeed set failed: %d\n", ret);
return ret;
}
}
if (pm->voltage.supported && pm->voltage_set) {
if (perflvl->volt_min && b->volt_min > a->volt_min) {
ret = pm->voltage_set(dev, perflvl->volt_min);
if (ret) {
NV_ERROR(dev, "voltage set failed: %d\n", ret);
return ret;
}
}
}
if (pre_state)
pm->clock_set(dev, pre_state);
return 0;
}
@ -59,31 +133,24 @@ nouveau_pm_perflvl_set(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
void *state;
int ret;
if (perflvl == pm->cur)
return 0;
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->volt_min, ret);
}
}
ret = nouveau_pm_perflvl_aux(dev, perflvl, pm->cur, perflvl);
if (ret)
return ret;
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);
}
state = pm->clocks_pre(dev, perflvl);
if (IS_ERR(state))
return PTR_ERR(state);
pm->clocks_set(dev, state);
ret = nouveau_pm_perflvl_aux(dev, perflvl, perflvl, pm->cur);
if (ret)
return ret;
pm->cur = perflvl;
return 0;
@ -130,28 +197,9 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
memset(perflvl, 0, sizeof(*perflvl));
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_SHADER);
if (ret > 0)
perflvl->shader = ret;
ret = pm->clock_get(dev, PLL_UNK05);
if (ret > 0)
perflvl->unk05 = ret;
}
ret = pm->clocks_get(dev, perflvl);
if (ret)
return ret;
if (pm->voltage.supported && pm->voltage_get) {
ret = pm->voltage_get(dev);
@ -161,6 +209,10 @@ nouveau_pm_perflvl_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
}
}
ret = nouveau_pwmfan_get(dev);
if (ret > 0)
perflvl->fanspeed = ret;
return 0;
}
@ -412,6 +464,172 @@ static SENSOR_DEVICE_ATTR(update_rate, S_IRUGO,
nouveau_hwmon_show_update_rate,
NULL, 0);
static ssize_t
nouveau_hwmon_show_fan0_input(struct device *d, struct device_attribute *attr,
char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
struct gpio_func gpio;
u32 cycles, cur, prev;
u64 start;
int ret;
ret = nouveau_gpio_find(dev, 0, DCB_GPIO_FAN_SENSE, 0xff, &gpio);
if (ret)
return ret;
/* Monitor the GPIO input 0x3b for 250ms.
* When the fan spins, it changes the value of GPIO FAN_SENSE.
* We get 4 changes (0 -> 1 -> 0 -> 1 -> [...]) per complete rotation.
*/
start = ptimer->read(dev);
prev = nouveau_gpio_sense(dev, 0, gpio.line);
cycles = 0;
do {
cur = nouveau_gpio_sense(dev, 0, gpio.line);
if (prev != cur) {
cycles++;
prev = cur;
}
usleep_range(500, 1000); /* supports 0 < rpm < 7500 */
} while (ptimer->read(dev) - start < 250000000);
/* interpolate to get rpm */
return sprintf(buf, "%i\n", cycles / 4 * 4 * 60);
}
static SENSOR_DEVICE_ATTR(fan0_input, S_IRUGO, nouveau_hwmon_show_fan0_input,
NULL, 0);
static ssize_t
nouveau_hwmon_get_pwm0(struct device *d, struct device_attribute *a, char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
int ret;
ret = nouveau_pwmfan_get(dev);
if (ret < 0)
return ret;
return sprintf(buf, "%i\n", ret);
}
static ssize_t
nouveau_hwmon_set_pwm0(struct device *d, struct device_attribute *a,
const char *buf, size_t count)
{
struct drm_device *dev = dev_get_drvdata(d);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
int ret = -ENODEV;
long value;
if (nouveau_perflvl_wr != 7777)
return -EPERM;
if (strict_strtol(buf, 10, &value) == -EINVAL)
return -EINVAL;
if (value < pm->fan.min_duty)
value = pm->fan.min_duty;
if (value > pm->fan.max_duty)
value = pm->fan.max_duty;
ret = nouveau_pwmfan_set(dev, value);
if (ret)
return ret;
return count;
}
static SENSOR_DEVICE_ATTR(pwm0, S_IRUGO | S_IWUSR,
nouveau_hwmon_get_pwm0,
nouveau_hwmon_set_pwm0, 0);
static ssize_t
nouveau_hwmon_get_pwm0_min(struct device *d,
struct device_attribute *a, char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
return sprintf(buf, "%i\n", pm->fan.min_duty);
}
static ssize_t
nouveau_hwmon_set_pwm0_min(struct device *d, struct device_attribute *a,
const char *buf, size_t count)
{
struct drm_device *dev = dev_get_drvdata(d);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
long value;
if (strict_strtol(buf, 10, &value) == -EINVAL)
return -EINVAL;
if (value < 0)
value = 0;
if (pm->fan.max_duty - value < 10)
value = pm->fan.max_duty - 10;
if (value < 10)
pm->fan.min_duty = 10;
else
pm->fan.min_duty = value;
return count;
}
static SENSOR_DEVICE_ATTR(pwm0_min, S_IRUGO | S_IWUSR,
nouveau_hwmon_get_pwm0_min,
nouveau_hwmon_set_pwm0_min, 0);
static ssize_t
nouveau_hwmon_get_pwm0_max(struct device *d,
struct device_attribute *a, char *buf)
{
struct drm_device *dev = dev_get_drvdata(d);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
return sprintf(buf, "%i\n", pm->fan.max_duty);
}
static ssize_t
nouveau_hwmon_set_pwm0_max(struct device *d, struct device_attribute *a,
const char *buf, size_t count)
{
struct drm_device *dev = dev_get_drvdata(d);
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
long value;
if (strict_strtol(buf, 10, &value) == -EINVAL)
return -EINVAL;
if (value < 0)
value = 0;
if (value - pm->fan.min_duty < 10)
value = pm->fan.min_duty + 10;
if (value > 100)
pm->fan.max_duty = 100;
else
pm->fan.max_duty = value;
return count;
}
static SENSOR_DEVICE_ATTR(pwm0_max, S_IRUGO | S_IWUSR,
nouveau_hwmon_get_pwm0_max,
nouveau_hwmon_set_pwm0_max, 0);
static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr,
&sensor_dev_attr_temp1_max.dev_attr.attr,
@ -420,10 +638,26 @@ static struct attribute *hwmon_attributes[] = {
&sensor_dev_attr_update_rate.dev_attr.attr,
NULL
};
static struct attribute *hwmon_fan_rpm_attributes[] = {
&sensor_dev_attr_fan0_input.dev_attr.attr,
NULL
};
static struct attribute *hwmon_pwm_fan_attributes[] = {
&sensor_dev_attr_pwm0.dev_attr.attr,
&sensor_dev_attr_pwm0_min.dev_attr.attr,
&sensor_dev_attr_pwm0_max.dev_attr.attr,
NULL
};
static const struct attribute_group hwmon_attrgroup = {
.attrs = hwmon_attributes,
};
static const struct attribute_group hwmon_fan_rpm_attrgroup = {
.attrs = hwmon_fan_rpm_attributes,
};
static const struct attribute_group hwmon_pwm_fan_attrgroup = {
.attrs = hwmon_pwm_fan_attributes,
};
#endif
static int
@ -433,7 +667,7 @@ nouveau_hwmon_init(struct drm_device *dev)
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_pm_engine *pm = &dev_priv->engine.pm;
struct device *hwmon_dev;
int ret;
int ret = 0;
if (!pm->temp_get)
return -ENODEV;
@ -446,17 +680,46 @@ nouveau_hwmon_init(struct drm_device *dev)
return ret;
}
dev_set_drvdata(hwmon_dev, dev);
/* default sysfs entries */
ret = sysfs_create_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
if (ret) {
NV_ERROR(dev,
"Unable to create hwmon sysfs file: %d\n", ret);
hwmon_device_unregister(hwmon_dev);
return ret;
if (ret)
goto error;
}
/* if the card has a pwm fan */
/*XXX: incorrect, need better detection for this, some boards have
* the gpio entries for pwm fan control even when there's no
* actual fan connected to it... therm table? */
if (nouveau_pwmfan_get(dev) >= 0) {
ret = sysfs_create_group(&dev->pdev->dev.kobj,
&hwmon_pwm_fan_attrgroup);
if (ret)
goto error;
}
/* if the card can read the fan rpm */
if (nouveau_gpio_func_valid(dev, DCB_GPIO_FAN_SENSE)) {
ret = sysfs_create_group(&dev->pdev->dev.kobj,
&hwmon_fan_rpm_attrgroup);
if (ret)
goto error;
}
pm->hwmon = hwmon_dev;
#endif
return 0;
error:
NV_ERROR(dev, "Unable to create some hwmon sysfs files: %d\n", ret);
hwmon_device_unregister(hwmon_dev);
pm->hwmon = NULL;
return ret;
#else
pm->hwmon = NULL;
return 0;
#endif
}
static void
@ -468,6 +731,9 @@ nouveau_hwmon_fini(struct drm_device *dev)
if (pm->hwmon) {
sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_attrgroup);
sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_pwm_fan_attrgroup);
sysfs_remove_group(&dev->pdev->dev.kobj, &hwmon_fan_rpm_attrgroup);
hwmon_device_unregister(pm->hwmon);
}
#endif

View File

@ -47,29 +47,33 @@ void nouveau_mem_timing_init(struct drm_device *);
void nouveau_mem_timing_fini(struct drm_device *);
/* nv04_pm.c */
int nv04_pm_clock_get(struct drm_device *, u32 id);
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 *);
int nv04_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
void *nv04_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
int nv04_pm_clocks_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 *);
int nv40_pm_clocks_set(struct drm_device *, void *);
int nv40_pm_pwm_get(struct drm_device *, int, u32 *, u32 *);
int nv40_pm_pwm_set(struct drm_device *, int, u32, u32);
/* 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 *,
u32 id, int khz);
void nv50_pm_clock_set(struct drm_device *, void *);
int nv50_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
void *nv50_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
int nv50_pm_clocks_set(struct drm_device *, void *);
int nv50_pm_pwm_get(struct drm_device *, int, u32 *, u32 *);
int nv50_pm_pwm_set(struct drm_device *, int, u32, u32);
/* nva3_pm.c */
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 *);
int nva3_pm_clocks_set(struct drm_device *, void *);
/* nvc0_pm.c */
int nvc0_pm_clocks_get(struct drm_device *, struct nouveau_pm_level *);
void *nvc0_pm_clocks_pre(struct drm_device *, struct nouveau_pm_level *);
int nvc0_pm_clocks_set(struct drm_device *, void *);
/* nouveau_temp.c */
void nouveau_temp_init(struct drm_device *dev);

View File

@ -36,6 +36,7 @@
#include "nouveau_drm.h"
#include "nouveau_fbcon.h"
#include "nouveau_ramht.h"
#include "nouveau_gpio.h"
#include "nouveau_pm.h"
#include "nv50_display.h"
@ -80,16 +81,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
engine->display.init = nv04_display_init;
engine->display.destroy = nv04_display_destroy;
engine->gpio.init = nouveau_stub_init;
engine->gpio.takedown = nouveau_stub_takedown;
engine->gpio.get = NULL;
engine->gpio.set = NULL;
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->display.init = nv04_display_init;
engine->display.fini = nv04_display_fini;
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
engine->vram.init = nouveau_mem_detect;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
@ -129,16 +126,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
engine->display.init = nv04_display_init;
engine->display.destroy = nv04_display_destroy;
engine->gpio.init = nouveau_stub_init;
engine->gpio.takedown = nouveau_stub_takedown;
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->display.init = nv04_display_init;
engine->display.fini = nv04_display_fini;
engine->gpio.drive = nv10_gpio_drive;
engine->gpio.sense = nv10_gpio_sense;
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
engine->vram.init = nouveau_mem_detect;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
@ -178,16 +173,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
engine->display.init = nv04_display_init;
engine->display.destroy = nv04_display_destroy;
engine->gpio.init = nouveau_stub_init;
engine->gpio.takedown = nouveau_stub_takedown;
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->display.init = nv04_display_init;
engine->display.fini = nv04_display_fini;
engine->gpio.drive = nv10_gpio_drive;
engine->gpio.sense = nv10_gpio_sense;
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
engine->vram.init = nouveau_mem_detect;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
@ -227,16 +220,14 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
engine->display.init = nv04_display_init;
engine->display.destroy = nv04_display_destroy;
engine->gpio.init = nouveau_stub_init;
engine->gpio.takedown = nouveau_stub_takedown;
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->display.init = nv04_display_init;
engine->display.fini = nv04_display_fini;
engine->gpio.drive = nv10_gpio_drive;
engine->gpio.sense = nv10_gpio_sense;
engine->pm.clocks_get = nv04_pm_clocks_get;
engine->pm.clocks_pre = nv04_pm_clocks_pre;
engine->pm.clocks_set = nv04_pm_clocks_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
engine->vram.init = nouveau_mem_detect;
@ -279,19 +270,22 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv04_display_early_init;
engine->display.late_takedown = nv04_display_late_takedown;
engine->display.create = nv04_display_create;
engine->display.init = nv04_display_init;
engine->display.destroy = nv04_display_destroy;
engine->gpio.init = nouveau_stub_init;
engine->gpio.takedown = nouveau_stub_takedown;
engine->gpio.get = nv10_gpio_get;
engine->gpio.set = nv10_gpio_set;
engine->gpio.irq_enable = NULL;
engine->display.init = nv04_display_init;
engine->display.fini = nv04_display_fini;
engine->gpio.init = nv10_gpio_init;
engine->gpio.fini = nv10_gpio_fini;
engine->gpio.drive = nv10_gpio_drive;
engine->gpio.sense = nv10_gpio_sense;
engine->gpio.irq_enable = nv10_gpio_irq_enable;
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;
engine->pm.pwm_get = nv40_pm_pwm_get;
engine->pm.pwm_set = nv40_pm_pwm_set;
engine->vram.init = nouveau_mem_detect;
engine->vram.takedown = nouveau_stub_takedown;
engine->vram.flags_valid = nouveau_mem_flags_valid;
@ -334,14 +328,13 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv50_display_early_init;
engine->display.late_takedown = nv50_display_late_takedown;
engine->display.create = nv50_display_create;
engine->display.init = nv50_display_init;
engine->display.destroy = nv50_display_destroy;
engine->display.init = nv50_display_init;
engine->display.fini = nv50_display_fini;
engine->gpio.init = nv50_gpio_init;
engine->gpio.takedown = nv50_gpio_fini;
engine->gpio.get = nv50_gpio_get;
engine->gpio.set = nv50_gpio_set;
engine->gpio.irq_register = nv50_gpio_irq_register;
engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
engine->gpio.fini = nv50_gpio_fini;
engine->gpio.drive = nv50_gpio_drive;
engine->gpio.sense = nv50_gpio_sense;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
switch (dev_priv->chipset) {
case 0x84:
@ -354,9 +347,9 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
case 0xaa:
case 0xac:
case 0x50:
engine->pm.clock_get = nv50_pm_clock_get;
engine->pm.clock_pre = nv50_pm_clock_pre;
engine->pm.clock_set = nv50_pm_clock_set;
engine->pm.clocks_get = nv50_pm_clocks_get;
engine->pm.clocks_pre = nv50_pm_clocks_pre;
engine->pm.clocks_set = nv50_pm_clocks_set;
break;
default:
engine->pm.clocks_get = nva3_pm_clocks_get;
@ -370,6 +363,8 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->pm.temp_get = nv84_temp_get;
else
engine->pm.temp_get = nv40_temp_get;
engine->pm.pwm_get = nv50_pm_pwm_get;
engine->pm.pwm_set = nv50_pm_pwm_set;
engine->vram.init = nv50_vram_init;
engine->vram.takedown = nv50_vram_fini;
engine->vram.get = nv50_vram_new;
@ -407,14 +402,13 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
engine->display.early_init = nv50_display_early_init;
engine->display.late_takedown = nv50_display_late_takedown;
engine->display.create = nv50_display_create;
engine->display.init = nv50_display_init;
engine->display.destroy = nv50_display_destroy;
engine->display.init = nv50_display_init;
engine->display.fini = nv50_display_fini;
engine->gpio.init = nv50_gpio_init;
engine->gpio.takedown = nouveau_stub_takedown;
engine->gpio.get = nv50_gpio_get;
engine->gpio.set = nv50_gpio_set;
engine->gpio.irq_register = nv50_gpio_irq_register;
engine->gpio.irq_unregister = nv50_gpio_irq_unregister;
engine->gpio.fini = nv50_gpio_fini;
engine->gpio.drive = nv50_gpio_drive;
engine->gpio.sense = nv50_gpio_sense;
engine->gpio.irq_enable = nv50_gpio_irq_enable;
engine->vram.init = nvc0_vram_init;
engine->vram.takedown = nv50_vram_fini;
@ -423,8 +417,12 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
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.clocks_pre = nvc0_pm_clocks_pre;
engine->pm.clocks_set = nvc0_pm_clocks_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
engine->pm.pwm_get = nv50_pm_pwm_get;
engine->pm.pwm_set = nv50_pm_pwm_set;
break;
case 0xd0:
engine->instmem.init = nvc0_instmem_init;
@ -457,21 +455,23 @@ static int nouveau_init_engine_ptrs(struct drm_device *dev)
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->display.init = nvd0_display_init;
engine->display.fini = nvd0_display_fini;
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.fini = nv50_gpio_fini;
engine->gpio.drive = nvd0_gpio_drive;
engine->gpio.sense = nvd0_gpio_sense;
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.temp_get = nv84_temp_get;
engine->pm.clocks_get = nvc0_pm_clocks_get;
engine->pm.clocks_pre = nvc0_pm_clocks_pre;
engine->pm.clocks_set = nvc0_pm_clocks_set;
engine->pm.voltage_get = nouveau_voltage_gpio_get;
engine->pm.voltage_set = nouveau_voltage_gpio_set;
break;
@ -615,7 +615,7 @@ nouveau_card_init(struct drm_device *dev)
goto out_gart;
/* PGPIO */
ret = engine->gpio.init(dev);
ret = nouveau_gpio_create(dev);
if (ret)
goto out_mc;
@ -648,6 +648,7 @@ nouveau_card_init(struct drm_device *dev)
nv50_graph_create(dev);
break;
case NV_C0:
case NV_D0:
nvc0_graph_create(dev);
break;
default:
@ -663,6 +664,11 @@ nouveau_card_init(struct drm_device *dev)
case 0xa0:
nv84_crypt_create(dev);
break;
case 0x98:
case 0xaa:
case 0xac:
nv98_crypt_create(dev);
break;
}
switch (dev_priv->card_type) {
@ -684,15 +690,25 @@ nouveau_card_init(struct drm_device *dev)
break;
}
if (dev_priv->chipset >= 0xa3 || dev_priv->chipset == 0x98) {
nv84_bsp_create(dev);
nv84_vp_create(dev);
nv98_ppp_create(dev);
} else
if (dev_priv->chipset >= 0x84) {
nv50_mpeg_create(dev);
nv84_bsp_create(dev);
nv84_vp_create(dev);
} else
if (dev_priv->chipset >= 0x50) {
nv50_mpeg_create(dev);
} else
if (dev_priv->card_type == NV_40 ||
dev_priv->chipset == 0x31 ||
dev_priv->chipset == 0x34 ||
dev_priv->chipset == 0x36)
dev_priv->chipset == 0x36) {
nv31_mpeg_create(dev);
else
if (dev_priv->card_type == NV_50 &&
(dev_priv->chipset < 0x98 || dev_priv->chipset == 0xa0))
nv50_mpeg_create(dev);
}
for (e = 0; e < NVOBJ_ENGINE_NR; e++) {
if (dev_priv->eng[e]) {
@ -712,27 +728,7 @@ nouveau_card_init(struct drm_device *dev)
if (ret)
goto out_fifo;
/* 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 = engine->display.create(dev);
ret = nouveau_display_create(dev);
if (ret)
goto out_irq;
@ -752,12 +748,11 @@ nouveau_card_init(struct drm_device *dev)
}
if (dev->mode_config.num_crtc) {
ret = drm_vblank_init(dev, dev->mode_config.num_crtc);
ret = nouveau_display_init(dev);
if (ret)
goto out_chan;
nouveau_fbcon_init(dev);
drm_kms_helper_poll_init(dev);
}
return 0;
@ -768,7 +763,7 @@ nouveau_card_init(struct drm_device *dev)
nouveau_fence_fini(dev);
out_disp:
nouveau_backlight_exit(dev);
engine->display.destroy(dev);
nouveau_display_destroy(dev);
out_irq:
nouveau_irq_fini(dev);
out_fifo:
@ -788,7 +783,7 @@ nouveau_card_init(struct drm_device *dev)
out_timer:
engine->timer.takedown(dev);
out_gpio:
engine->gpio.takedown(dev);
nouveau_gpio_destroy(dev);
out_mc:
engine->mc.takedown(dev);
out_gart:
@ -818,9 +813,8 @@ static void nouveau_card_takedown(struct drm_device *dev)
int e;
if (dev->mode_config.num_crtc) {
drm_kms_helper_poll_fini(dev);
nouveau_fbcon_fini(dev);
drm_vblank_cleanup(dev);
nouveau_display_fini(dev);
}
if (dev_priv->channel) {
@ -829,8 +823,7 @@ static void nouveau_card_takedown(struct drm_device *dev)
}
nouveau_backlight_exit(dev);
engine->display.destroy(dev);
drm_mode_config_cleanup(dev);
nouveau_display_destroy(dev);
if (!dev_priv->noaccel) {
engine->fifo.takedown(dev);
@ -843,7 +836,7 @@ static void nouveau_card_takedown(struct drm_device *dev)
}
engine->fb.takedown(dev);
engine->timer.takedown(dev);
engine->gpio.takedown(dev);
nouveau_gpio_destroy(dev);
engine->mc.takedown(dev);
engine->display.late_takedown(dev);
@ -1110,13 +1103,11 @@ int nouveau_load(struct drm_device *dev, unsigned long flags)
dev_priv->noaccel = !!nouveau_noaccel;
if (nouveau_noaccel == -1) {
switch (dev_priv->chipset) {
#if 0
case 0xXX: /* known broken */
case 0xd9: /* known broken */
NV_INFO(dev, "acceleration disabled by default, pass "
"noaccel=0 to force enable\n");
dev_priv->noaccel = true;
break;
#endif
default:
dev_priv->noaccel = false;
break;
@ -1238,7 +1229,7 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
getparam->value = 1;
break;
case NOUVEAU_GETPARAM_HAS_PAGEFLIP:
getparam->value = dev_priv->card_type < NV_D0;
getparam->value = 1;
break;
case NOUVEAU_GETPARAM_GRAPH_UNITS:
/* NV40 and NV50 versions are quite different, but register

View File

@ -55,6 +55,10 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp)
temps->down_clock = 100;
temps->fan_boost = 90;
/* Set the default range for the pwm fan */
pm->fan.min_duty = 30;
pm->fan.max_duty = 100;
/* Set the known default values to setup the temperature sensor */
if (dev_priv->card_type >= NV_40) {
switch (dev_priv->chipset) {
@ -156,11 +160,26 @@ nouveau_temp_vbios_parse(struct drm_device *dev, u8 *temp)
case 0x13:
sensor->slope_div = value;
break;
case 0x22:
pm->fan.min_duty = value & 0xff;
pm->fan.max_duty = (value & 0xff00) >> 8;
break;
case 0x26:
pm->fan.pwm_freq = value;
break;
}
temp += recordlen;
}
nouveau_temp_safety_checks(dev);
/* check the fan min/max settings */
if (pm->fan.min_duty < 10)
pm->fan.min_duty = 10;
if (pm->fan.max_duty > 100)
pm->fan.max_duty = 100;
if (pm->fan.max_duty < pm->fan.min_duty)
pm->fan.max_duty = pm->fan.min_duty;
}
static int
@ -267,8 +286,6 @@ probe_monitoring_device(struct nouveau_i2c_chan *i2c,
static void
nouveau_temp_probe_i2c(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct dcb_table *dcb = &dev_priv->vbios.dcb;
struct i2c_board_info info[] = {
{ I2C_BOARD_INFO("w83l785ts", 0x2d) },
{ I2C_BOARD_INFO("w83781d", 0x2d) },
@ -277,11 +294,9 @@ nouveau_temp_probe_i2c(struct drm_device *dev)
{ I2C_BOARD_INFO("lm99", 0x4c) },
{ }
};
int idx = (dcb->version >= 0x40 ?
dcb->i2c_default_indices & 0xf : 2);
nouveau_i2c_identify(dev, "monitoring device", info,
probe_monitoring_device, idx);
probe_monitoring_device, NV_I2C_DEFAULT(0));
}
void
@ -297,9 +312,9 @@ nouveau_temp_init(struct drm_device *dev)
return;
if (P.version == 1)
temp = ROMPTR(bios, P.data[12]);
temp = ROMPTR(dev, P.data[12]);
else if (P.version == 2)
temp = ROMPTR(bios, P.data[16]);
temp = ROMPTR(dev, P.data[16]);
else
NV_WARN(dev, "unknown temp for BIT P %d\n", P.version);

View File

@ -78,9 +78,10 @@ nouveau_vm_map(struct nouveau_vma *vma, struct nouveau_mem *node)
void
nouveau_vm_map_sg(struct nouveau_vma *vma, u64 delta, u64 length,
struct nouveau_mem *mem, dma_addr_t *list)
struct nouveau_mem *mem)
{
struct nouveau_vm *vm = vma->vm;
dma_addr_t *list = mem->pages;
int big = vma->node->type != vm->spg_shift;
u32 offset = vma->node->offset + (delta >> 12);
u32 bits = vma->node->type - 12;

View File

@ -89,7 +89,7 @@ void nouveau_vm_map_at(struct nouveau_vma *, u64 offset, struct nouveau_mem *);
void nouveau_vm_unmap(struct nouveau_vma *);
void nouveau_vm_unmap_at(struct nouveau_vma *, u64 offset, u64 length);
void nouveau_vm_map_sg(struct nouveau_vma *, u64 offset, u64 length,
struct nouveau_mem *, dma_addr_t *);
struct nouveau_mem *);
/* nv50_vm.c */
void nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde,

View File

@ -26,6 +26,7 @@
#include "nouveau_drv.h"
#include "nouveau_pm.h"
#include "nouveau_gpio.h"
static const enum dcb_gpio_tag vidtag[] = { 0x04, 0x05, 0x06, 0x1a, 0x73 };
static int nr_vidtag = sizeof(vidtag) / sizeof(vidtag[0]);
@ -34,7 +35,6 @@ int
nouveau_voltage_gpio_get(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
u8 vid = 0;
int i;
@ -43,7 +43,7 @@ nouveau_voltage_gpio_get(struct drm_device *dev)
if (!(volt->vid_mask & (1 << i)))
continue;
vid |= gpio->get(dev, vidtag[i]) << i;
vid |= nouveau_gpio_func_get(dev, vidtag[i]) << i;
}
return nouveau_volt_lvl_lookup(dev, vid);
@ -53,7 +53,6 @@ int
nouveau_voltage_gpio_set(struct drm_device *dev, int voltage)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
struct nouveau_pm_voltage *volt = &dev_priv->engine.pm.voltage;
int vid, i;
@ -65,7 +64,7 @@ nouveau_voltage_gpio_set(struct drm_device *dev, int voltage)
if (!(volt->vid_mask & (1 << i)))
continue;
gpio->set(dev, vidtag[i], !!(vid & (1 << i)));
nouveau_gpio_func_set(dev, vidtag[i], !!(vid & (1 << i)));
}
return 0;
@ -117,10 +116,10 @@ nouveau_volt_init(struct drm_device *dev)
return;
if (P.version == 1)
volt = ROMPTR(bios, P.data[16]);
volt = ROMPTR(dev, P.data[16]);
else
if (P.version == 2)
volt = ROMPTR(bios, P.data[12]);
volt = ROMPTR(dev, P.data[12]);
else {
NV_WARN(dev, "unknown volt for BIT P %d\n", P.version);
}
@ -130,7 +129,7 @@ nouveau_volt_init(struct drm_device *dev)
return;
}
volt = ROMPTR(bios, bios->data[bios->offset + 0x98]);
volt = ROMPTR(dev, bios->data[bios->offset + 0x98]);
}
if (!volt) {
@ -194,7 +193,7 @@ nouveau_volt_init(struct drm_device *dev)
return;
}
if (!nouveau_bios_gpio_entry(dev, vidtag[i])) {
if (!nouveau_gpio_func_valid(dev, vidtag[i])) {
NV_DEBUG(dev, "vid bit %d has no gpio tag\n", i);
return;
}

View File

@ -32,6 +32,7 @@
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nouveau_hw.h"
#include "nouveau_gpio.h"
#include "nvreg.h"
int nv04_dac_output_offset(struct drm_encoder *encoder)
@ -220,7 +221,6 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder);
uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
@ -252,11 +252,11 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
}
saved_gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1);
saved_gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0);
saved_gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1);
saved_gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0);
gpio->set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV);
gpio->set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV);
msleep(4);
@ -306,8 +306,8 @@ uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
gpio->set(dev, DCB_GPIO_TVDAC1, saved_gpio1);
gpio->set(dev, DCB_GPIO_TVDAC0, saved_gpio0);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, saved_gpio1);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, saved_gpio0);
return sample;
}

View File

@ -289,6 +289,7 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
struct drm_display_mode *output_mode = &nv_encoder->mode;
struct drm_connector *connector = &nv_connector->base;
uint32_t mode_ratio, panel_ratio;
NV_DEBUG_KMS(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
@ -340,10 +341,15 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
output_mode->clock > 165000)
regp->fp_control |= (2 << 24);
if (nv_encoder->dcb->type == OUTPUT_LVDS) {
bool duallink, dummy;
bool duallink = false, dummy;
if (nv_connector->edid &&
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
duallink = (((u8 *)nv_connector->edid)[121] == 2);
} else {
nouveau_bios_parse_lvds_table(dev, output_mode->clock,
&duallink, &dummy);
}
nouveau_bios_parse_lvds_table(dev, output_mode->clock,
&duallink, &dummy);
if (duallink)
regp->fp_control |= (8 << 28);
} else
@ -407,7 +413,9 @@ static void nv04_dfp_mode_set(struct drm_encoder *encoder,
}
/* Output property. */
if (nv_connector->use_dithering) {
if ((nv_connector->dithering_mode == DITHERING_MODE_ON) ||
(nv_connector->dithering_mode == DITHERING_MODE_AUTO &&
encoder->crtc->fb->depth > connector->display_info.bpc * 3)) {
if (dev_priv->chipset == 0x11)
regp->dither = savep->dither | 0x00010000;
else {

View File

@ -243,6 +243,11 @@ nv04_display_init(struct drm_device *dev)
return 0;
}
void
nv04_display_fini(struct drm_device *dev)
{
}
static void
nv04_vblank_crtc0_isr(struct drm_device *dev)
{

View File

@ -27,68 +27,111 @@
#include "nouveau_hw.h"
#include "nouveau_pm.h"
struct nv04_pm_state {
int
nv04_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
int ret;
ret = nouveau_hw_get_clock(dev, PLL_CORE);
if (ret < 0)
return ret;
perflvl->core = ret;
ret = nouveau_hw_get_clock(dev, PLL_MEMORY);
if (ret < 0)
return ret;
perflvl->memory = ret;
return 0;
}
struct nv04_pm_clock {
struct pll_lims pll;
struct nouveau_pll_vals calc;
};
int
nv04_pm_clock_get(struct drm_device *dev, u32 id)
struct nv04_pm_state {
struct nv04_pm_clock core;
struct nv04_pm_clock memory;
};
static int
calc_pll(struct drm_device *dev, u32 id, int khz, struct nv04_pm_clock *clk)
{
return nouveau_hw_get_clock(dev, id);
int ret;
ret = get_pll_limits(dev, id, &clk->pll);
if (ret)
return ret;
ret = nouveau_calc_pll_mnp(dev, &clk->pll, khz, &clk->calc);
if (!ret)
return -EINVAL;
return 0;
}
void *
nv04_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
u32 id, int khz)
nv04_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct nv04_pm_state *state;
struct nv04_pm_state *info;
int ret;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
ret = get_pll_limits(dev, id, &state->pll);
if (ret) {
kfree(state);
return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
ret = calc_pll(dev, PLL_CORE, perflvl->core, &info->core);
if (ret)
goto error;
if (perflvl->memory) {
ret = calc_pll(dev, PLL_MEMORY, perflvl->memory, &info->memory);
if (ret)
goto error;
}
ret = nouveau_calc_pll_mnp(dev, &state->pll, khz, &state->calc);
if (!ret) {
kfree(state);
return ERR_PTR(-EINVAL);
}
return state;
return info;
error:
kfree(info);
return ERR_PTR(ret);
}
void
nv04_pm_clock_set(struct drm_device *dev, void *pre_state)
static void
prog_pll(struct drm_device *dev, struct nv04_pm_clock *clk)
{
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;
u32 reg = clk->pll.reg;
/* thank the insane nouveau_hw_setpll() interface for this */
if (dev_priv->card_type >= NV_40)
reg += 4;
nouveau_hw_setpll(dev, reg, &state->calc);
if (dev_priv->card_type < NV_30 && reg == NV_PRAMDAC_MPLL_COEFF) {
if (dev_priv->card_type == NV_20)
nv_mask(dev, 0x1002c4, 0, 1 << 20);
/* Reset the DLLs */
nv_mask(dev, 0x1002c0, 0, 1 << 8);
}
if (reg == NV_PRAMDAC_NVPLL_COEFF)
ptimer->init(dev);
kfree(state);
nouveau_hw_setpll(dev, reg, &clk->calc);
}
int
nv04_pm_clocks_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;
prog_pll(dev, &state->core);
if (state->memory.pll.reg) {
prog_pll(dev, &state->memory);
if (dev_priv->card_type < NV_30) {
if (dev_priv->card_type == NV_20)
nv_mask(dev, 0x1002c4, 0, 1 << 20);
/* Reset the DLLs */
nv_mask(dev, 0x1002c0, 0, 1 << 8);
}
}
ptimer->init(dev);
kfree(state);
return 0;
}

View File

@ -2,6 +2,7 @@
#include "drm.h"
#include "nouveau_drv.h"
#include "nouveau_drm.h"
#include "nouveau_hw.h"
int
nv04_timer_init(struct drm_device *dev)
@ -17,7 +18,7 @@ nv04_timer_init(struct drm_device *dev)
/* determine base clock for timer source */
if (dev_priv->chipset < 0x40) {
n = dev_priv->engine.pm.clock_get(dev, PLL_CORE);
n = nouveau_hw_get_clock(dev, PLL_CORE);
} else
if (dev_priv->chipset == 0x40) {
/*XXX: figure this out */

View File

@ -27,66 +27,97 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include "nouveau_gpio.h"
static bool
get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift,
uint32_t *mask)
int
nv10_gpio_sense(struct drm_device *dev, int line)
{
if (ent->line < 2) {
*reg = NV_PCRTC_GPIO;
*shift = ent->line * 16;
*mask = 0x11;
} else if (ent->line < 10) {
*reg = NV_PCRTC_GPIO_EXT;
*shift = (ent->line - 2) * 4;
*mask = 0x3;
} else if (ent->line < 14) {
*reg = NV_PCRTC_850;
*shift = (ent->line - 10) * 4;
*mask = 0x3;
} else {
return false;
if (line < 2) {
line = line * 16;
line = NVReadCRTC(dev, 0, NV_PCRTC_GPIO) >> line;
return !!(line & 0x0100);
} else
if (line < 10) {
line = (line - 2) * 4;
line = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT) >> line;
return !!(line & 0x04);
} else
if (line < 14) {
line = (line - 10) * 4;
line = NVReadCRTC(dev, 0, NV_PCRTC_850) >> line;
return !!(line & 0x04);
}
return true;
return -EINVAL;
}
int
nv10_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
nv10_gpio_drive(struct drm_device *dev, int line, int dir, int out)
{
struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
uint32_t reg, shift, mask, value;
u32 reg, mask, data;
if (!ent)
return -ENODEV;
if (!get_gpio_location(ent, &reg, &shift, &mask))
return -ENODEV;
value = NVReadCRTC(dev, 0, reg) >> shift;
return (ent->invert ? 1 : 0) ^ (value & 1);
}
int
nv10_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
{
struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
uint32_t reg, shift, mask, value;
if (!ent)
return -ENODEV;
if (!get_gpio_location(ent, &reg, &shift, &mask))
return -ENODEV;
value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift;
mask = ~(mask << shift);
NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask));
if (line < 2) {
line = line * 16;
reg = NV_PCRTC_GPIO;
mask = 0x00000011;
data = (dir << 4) | out;
} else
if (line < 10) {
line = (line - 2) * 4;
reg = NV_PCRTC_GPIO_EXT;
mask = 0x00000003 << ((line - 2) * 4);
data = (dir << 1) | out;
} else
if (line < 14) {
line = (line - 10) * 4;
reg = NV_PCRTC_850;
mask = 0x00000003;
data = (dir << 1) | out;
} else {
return -EINVAL;
}
mask = NVReadCRTC(dev, 0, reg) & ~(mask << line);
NVWriteCRTC(dev, 0, reg, mask | (data << line));
return 0;
}
void
nv10_gpio_irq_enable(struct drm_device *dev, int line, bool on)
{
u32 mask = 0x00010001 << line;
nv_wr32(dev, 0x001104, mask);
nv_mask(dev, 0x001144, mask, on ? mask : 0);
}
static void
nv10_gpio_isr(struct drm_device *dev)
{
u32 intr = nv_rd32(dev, 0x1104);
u32 hi = (intr & 0x0000ffff) >> 0;
u32 lo = (intr & 0xffff0000) >> 16;
nouveau_gpio_isr(dev, 0, hi | lo);
nv_wr32(dev, 0x001104, intr);
}
int
nv10_gpio_init(struct drm_device *dev)
{
nv_wr32(dev, 0x001140, 0x00000000);
nv_wr32(dev, 0x001100, 0xffffffff);
nv_wr32(dev, 0x001144, 0x00000000);
nv_wr32(dev, 0x001104, 0xffffffff);
nouveau_irq_register(dev, 28, nv10_gpio_isr); /* PBUS */
return 0;
}
void
nv10_gpio_fini(struct drm_device *dev)
{
nv_wr32(dev, 0x001140, 0x00000000);
nv_wr32(dev, 0x001144, 0x00000000);
nouveau_irq_unregister(dev, 28);
}

View File

@ -30,6 +30,7 @@
#include "nouveau_encoder.h"
#include "nouveau_connector.h"
#include "nouveau_crtc.h"
#include "nouveau_gpio.h"
#include "nouveau_hw.h"
#include "nv17_tv.h"
@ -37,7 +38,6 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end,
fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c;
@ -53,8 +53,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
head = (dacclk & 0x100) >> 8;
/* Save the previous state. */
gpio1 = gpio->get(dev, DCB_GPIO_TVDAC1);
gpio0 = gpio->get(dev, DCB_GPIO_TVDAC0);
gpio1 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC1);
gpio0 = nouveau_gpio_func_get(dev, DCB_GPIO_TVDAC0);
fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL);
fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START);
fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END);
@ -65,8 +65,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c);
/* Prepare the DAC for load detection. */
gpio->set(dev, DCB_GPIO_TVDAC1, true);
gpio->set(dev, DCB_GPIO_TVDAC0, true);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, true);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, true);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047);
@ -111,8 +111,8 @@ static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start);
NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal);
gpio->set(dev, DCB_GPIO_TVDAC1, gpio1);
gpio->set(dev, DCB_GPIO_TVDAC0, gpio0);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, gpio1);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, gpio0);
return sample;
}
@ -357,8 +357,6 @@ static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
{
struct drm_device *dev = encoder->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *gpio = &dev_priv->engine.gpio;
struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
@ -383,8 +381,8 @@ static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
nv_load_ptv(dev, regs, 200);
gpio->set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
gpio->set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
nouveau_gpio_func_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
}

View File

@ -222,7 +222,7 @@ nv40_pm_gr_idle(void *data)
return true;
}
void
int
nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@ -231,7 +231,7 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
struct bit_entry M;
u32 crtc_mask = 0;
u8 sr1[2];
int i;
int i, ret = -EAGAIN;
/* determine which CRTCs are active, fetch VGA_SR1 for each */
for (i = 0; i < 2; i++) {
@ -263,6 +263,8 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
if (!nv_wait_cb(dev, nv40_pm_gr_idle, dev))
goto resume;
ret = 0;
/* set engine clocks */
nv_mask(dev, 0x00c040, 0x00000333, 0x00000000);
nv_wr32(dev, 0x004004, info->npll_coef);
@ -345,4 +347,48 @@ nv40_pm_clocks_set(struct drm_device *dev, void *pre_state)
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
kfree(info);
return ret;
}
int
nv40_pm_pwm_get(struct drm_device *dev, int line, u32 *divs, u32 *duty)
{
if (line == 2) {
u32 reg = nv_rd32(dev, 0x0010f0);
if (reg & 0x80000000) {
*duty = (reg & 0x7fff0000) >> 16;
*divs = (reg & 0x00007fff);
return 0;
}
} else
if (line == 9) {
u32 reg = nv_rd32(dev, 0x0015f4);
if (reg & 0x80000000) {
*divs = nv_rd32(dev, 0x0015f8);
*duty = (reg & 0x7fffffff);
return 0;
}
} else {
NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", line);
return -ENODEV;
}
return -EINVAL;
}
int
nv40_pm_pwm_set(struct drm_device *dev, int line, u32 divs, u32 duty)
{
if (line == 2) {
nv_wr32(dev, 0x0010f0, 0x80000000 | (duty << 16) | divs);
} else
if (line == 9) {
nv_wr32(dev, 0x0015f8, divs);
nv_wr32(dev, 0x0015f4, duty | 0x80000000);
} else {
NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", line);
return -ENODEV;
}
return 0;
}

View File

@ -132,33 +132,42 @@ nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
}
static int
nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool update)
{
struct drm_device *dev = nv_crtc->base.dev;
struct nouveau_channel *evo = nv50_display(dev)->master;
int ret;
struct nouveau_channel *evo = nv50_display(nv_crtc->base.dev)->master;
struct nouveau_connector *nv_connector;
struct drm_connector *connector;
int head = nv_crtc->index, ret;
u32 mode = 0x00;
NV_DEBUG_KMS(dev, "\n");
nv_connector = nouveau_crtc_connector_get(nv_crtc);
connector = &nv_connector->base;
if (nv_connector->dithering_mode == DITHERING_MODE_AUTO) {
if (nv_crtc->base.fb->depth > connector->display_info.bpc * 3)
mode = DITHERING_MODE_DYNAMIC2X2;
} else {
mode = nv_connector->dithering_mode;
}
if (nv_connector->dithering_depth == DITHERING_DEPTH_AUTO) {
if (connector->display_info.bpc >= 8)
mode |= DITHERING_DEPTH_8BPC;
} else {
mode |= nv_connector->dithering_depth;
}
ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
if (ret) {
NV_ERROR(dev, "no space while setting dither\n");
return ret;
if (ret == 0) {
BEGIN_RING(evo, 0, NV50_EVO_CRTC(head, DITHER_CTRL), 1);
OUT_RING (evo, mode);
if (update) {
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING (evo, 0);
FIRE_RING (evo);
}
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1);
if (on)
OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON);
else
OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF);
if (update) {
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
}
return 0;
return ret;
}
struct nouveau_connector *
@ -180,80 +189,103 @@ nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
}
static int
nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, bool update)
{
struct nouveau_connector *nv_connector =
nouveau_crtc_connector_get(nv_crtc);
struct drm_device *dev = nv_crtc->base.dev;
struct nouveau_connector *nv_connector;
struct drm_crtc *crtc = &nv_crtc->base;
struct drm_device *dev = crtc->dev;
struct nouveau_channel *evo = nv50_display(dev)->master;
struct drm_display_mode *native_mode = NULL;
struct drm_display_mode *mode = &nv_crtc->base.mode;
uint32_t outX, outY, horiz, vert;
int ret;
struct drm_display_mode *umode = &crtc->mode;
struct drm_display_mode *omode;
int scaling_mode, ret;
u32 ctrl = 0, oX, oY;
NV_DEBUG_KMS(dev, "\n");
switch (scaling_mode) {
case DRM_MODE_SCALE_NONE:
break;
default:
if (!nv_connector || !nv_connector->native_mode) {
NV_ERROR(dev, "No native mode, forcing panel scaling\n");
scaling_mode = DRM_MODE_SCALE_NONE;
} else {
native_mode = nv_connector->native_mode;
}
break;
nv_connector = nouveau_crtc_connector_get(nv_crtc);
if (!nv_connector || !nv_connector->native_mode) {
NV_ERROR(dev, "no native mode, forcing panel scaling\n");
scaling_mode = DRM_MODE_SCALE_NONE;
} else {
scaling_mode = nv_connector->scaling_mode;
}
switch (scaling_mode) {
case DRM_MODE_SCALE_ASPECT:
horiz = (native_mode->hdisplay << 19) / mode->hdisplay;
vert = (native_mode->vdisplay << 19) / mode->vdisplay;
/* start off at the resolution we programmed the crtc for, this
* effectively handles NONE/FULL scaling
*/
if (scaling_mode != DRM_MODE_SCALE_NONE)
omode = nv_connector->native_mode;
else
omode = umode;
if (vert > horiz) {
outX = (mode->hdisplay * horiz) >> 19;
outY = (mode->vdisplay * horiz) >> 19;
oX = omode->hdisplay;
oY = omode->vdisplay;
if (omode->flags & DRM_MODE_FLAG_DBLSCAN)
oY *= 2;
/* add overscan compensation if necessary, will keep the aspect
* ratio the same as the backend mode unless overridden by the
* user setting both hborder and vborder properties.
*/
if (nv_connector && ( nv_connector->underscan == UNDERSCAN_ON ||
(nv_connector->underscan == UNDERSCAN_AUTO &&
nv_connector->edid &&
drm_detect_hdmi_monitor(nv_connector->edid)))) {
u32 bX = nv_connector->underscan_hborder;
u32 bY = nv_connector->underscan_vborder;
u32 aspect = (oY << 19) / oX;
if (bX) {
oX -= (bX * 2);
if (bY) oY -= (bY * 2);
else oY = ((oX * aspect) + (aspect / 2)) >> 19;
} else {
outX = (mode->hdisplay * vert) >> 19;
outY = (mode->vdisplay * vert) >> 19;
oX -= (oX >> 4) + 32;
if (bY) oY -= (bY * 2);
else oY = ((oX * aspect) + (aspect / 2)) >> 19;
}
break;
case DRM_MODE_SCALE_FULLSCREEN:
outX = native_mode->hdisplay;
outY = native_mode->vdisplay;
break;
}
/* handle CENTER/ASPECT scaling, taking into account the areas
* removed already for overscan compensation
*/
switch (scaling_mode) {
case DRM_MODE_SCALE_CENTER:
case DRM_MODE_SCALE_NONE:
oX = min((u32)umode->hdisplay, oX);
oY = min((u32)umode->vdisplay, oY);
/* fall-through */
case DRM_MODE_SCALE_ASPECT:
if (oY < oX) {
u32 aspect = (umode->hdisplay << 19) / umode->vdisplay;
oX = ((oY * aspect) + (aspect / 2)) >> 19;
} else {
u32 aspect = (umode->vdisplay << 19) / umode->hdisplay;
oY = ((oX * aspect) + (aspect / 2)) >> 19;
}
break;
default:
outX = mode->hdisplay;
outY = mode->vdisplay;
break;
}
ret = RING_SPACE(evo, update ? 7 : 5);
if (umode->hdisplay != oX || umode->vdisplay != oY ||
umode->flags & DRM_MODE_FLAG_INTERLACE ||
umode->flags & DRM_MODE_FLAG_DBLSCAN)
ctrl |= NV50_EVO_CRTC_SCALE_CTRL_ACTIVE;
ret = RING_SPACE(evo, 5);
if (ret)
return ret;
/* Got a better name for SCALER_ACTIVE? */
/* One day i've got to really figure out why this is needed. */
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ||
(mode->flags & DRM_MODE_FLAG_INTERLACE) ||
mode->hdisplay != outX || mode->vdisplay != outY) {
OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE);
} else {
OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE);
}
OUT_RING (evo, ctrl);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
OUT_RING(evo, outY << 16 | outX);
OUT_RING(evo, outY << 16 | outX);
OUT_RING (evo, oY << 16 | oX);
OUT_RING (evo, oY << 16 | oX);
if (update) {
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING(evo, 0);
FIRE_RING(evo);
nv50_display_flip_stop(crtc);
nv50_display_sync(dev);
nv50_display_flip_next(crtc, crtc->fb, NULL);
}
return 0;
@ -333,7 +365,6 @@ nv50_crtc_destroy(struct drm_crtc *crtc)
nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
nouveau_bo_unmap(nv_crtc->cursor.nvbo);
nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
kfree(nv_crtc->mode);
kfree(nv_crtc);
}
@ -441,39 +472,6 @@ nv50_crtc_dpms(struct drm_crtc *crtc, int mode)
{
}
static int
nv50_crtc_wait_complete(struct drm_crtc *crtc)
{
struct drm_device *dev = crtc->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
struct nv50_display *disp = nv50_display(dev);
struct nouveau_channel *evo = disp->master;
u64 start;
int ret;
ret = RING_SPACE(evo, 6);
if (ret)
return ret;
BEGIN_RING(evo, 0, 0x0084, 1);
OUT_RING (evo, 0x80000000);
BEGIN_RING(evo, 0, 0x0080, 1);
OUT_RING (evo, 0);
BEGIN_RING(evo, 0, 0x0084, 1);
OUT_RING (evo, 0x00000000);
nv_wo32(disp->ntfy, 0x000, 0x00000000);
FIRE_RING (evo);
start = ptimer->read(dev);
do {
if (nv_ro32(disp->ntfy, 0x000))
return 0;
} while (ptimer->read(dev) - start < 2000000000ULL);
return -EBUSY;
}
static void
nv50_crtc_prepare(struct drm_crtc *crtc)
{
@ -497,7 +495,7 @@ nv50_crtc_commit(struct drm_crtc *crtc)
nv50_crtc_blank(nv_crtc, false);
drm_vblank_post_modeset(dev, nv_crtc->index);
nv50_crtc_wait_complete(crtc);
nv50_display_sync(dev);
nv50_display_flip_next(crtc, crtc->fb, NULL);
}
@ -593,90 +591,76 @@ nv50_crtc_do_mode_set_base(struct drm_crtc *crtc,
}
static int
nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode, int x, int y,
nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *umode,
struct drm_display_mode *mode, int x, int y,
struct drm_framebuffer *old_fb)
{
struct drm_device *dev = crtc->dev;
struct nouveau_channel *evo = nv50_display(dev)->master;
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
struct nouveau_connector *nv_connector = NULL;
uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end;
uint32_t hunk1, vunk1, vunk2a, vunk2b;
u32 head = nv_crtc->index * 0x400;
u32 ilace = (mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 1;
u32 vscan = (mode->flags & DRM_MODE_FLAG_DBLSCAN) ? 2 : 1;
u32 hactive, hsynce, hbackp, hfrontp, hblanke, hblanks;
u32 vactive, vsynce, vbackp, vfrontp, vblanke, vblanks;
u32 vblan2e = 0, vblan2s = 1;
int ret;
/* Find the connector attached to this CRTC */
nv_connector = nouveau_crtc_connector_get(nv_crtc);
/* hw timing description looks like this:
*
* <sync> <back porch> <---------display---------> <front porch>
* ______
* |____________|---------------------------|____________|
*
* ^ synce ^ blanke ^ blanks ^ active
*
* interlaced modes also have 2 additional values pointing at the end
* and start of the next field's blanking period.
*/
*nv_crtc->mode = *adjusted_mode;
hactive = mode->htotal;
hsynce = mode->hsync_end - mode->hsync_start - 1;
hbackp = mode->htotal - mode->hsync_end;
hblanke = hsynce + hbackp;
hfrontp = mode->hsync_start - mode->hdisplay;
hblanks = mode->htotal - hfrontp - 1;
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start;
vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start;
/* I can't give this a proper name, anyone else can? */
hunk1 = adjusted_mode->htotal -
adjusted_mode->hsync_start + adjusted_mode->hdisplay;
vunk1 = adjusted_mode->vtotal -
adjusted_mode->vsync_start + adjusted_mode->vdisplay;
/* Another strange value, this time only for interlaced adjusted_modes. */
vunk2a = 2 * adjusted_mode->vtotal -
adjusted_mode->vsync_start + adjusted_mode->vdisplay;
vunk2b = adjusted_mode->vtotal -
adjusted_mode->vsync_start + adjusted_mode->vtotal;
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
vsync_dur /= 2;
vsync_start_to_end /= 2;
vunk1 /= 2;
vunk2a /= 2;
vunk2b /= 2;
/* magic */
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
vsync_start_to_end -= 1;
vunk1 -= 1;
vunk2a -= 1;
vunk2b -= 1;
}
vactive = mode->vtotal * vscan / ilace;
vsynce = ((mode->vsync_end - mode->vsync_start) * vscan / ilace) - 1;
vbackp = (mode->vtotal - mode->vsync_end) * vscan / ilace;
vblanke = vsynce + vbackp;
vfrontp = (mode->vsync_start - mode->vdisplay) * vscan / ilace;
vblanks = vactive - vfrontp - 1;
if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
vblan2e = vactive + vsynce + vbackp;
vblan2s = vblan2e + (mode->vdisplay * vscan / ilace);
vactive = (vactive * 2) + 1;
}
ret = RING_SPACE(evo, 17);
if (ret)
return ret;
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2);
OUT_RING(evo, adjusted_mode->clock | 0x800000);
OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5);
OUT_RING(evo, 0);
OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal);
OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1));
OUT_RING(evo, (vsync_start_to_end - 1) << 16 |
(hsync_start_to_end - 1));
OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1));
if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1);
OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1));
} else {
OUT_RING(evo, 0);
OUT_RING(evo, 0);
ret = RING_SPACE(evo, 18);
if (ret == 0) {
BEGIN_RING(evo, 0, 0x0804 + head, 2);
OUT_RING (evo, 0x00800000 | mode->clock);
OUT_RING (evo, (ilace == 2) ? 2 : 0);
BEGIN_RING(evo, 0, 0x0810 + head, 6);
OUT_RING (evo, 0x00000000); /* border colour */
OUT_RING (evo, (vactive << 16) | hactive);
OUT_RING (evo, ( vsynce << 16) | hsynce);
OUT_RING (evo, (vblanke << 16) | hblanke);
OUT_RING (evo, (vblanks << 16) | hblanks);
OUT_RING (evo, (vblan2e << 16) | vblan2s);
BEGIN_RING(evo, 0, 0x082c + head, 1);
OUT_RING (evo, 0x00000000);
BEGIN_RING(evo, 0, 0x0900 + head, 1);
OUT_RING (evo, 0x00000311); /* makes sync channel work */
BEGIN_RING(evo, 0, 0x08c8 + head, 1);
OUT_RING (evo, (umode->vdisplay << 16) | umode->hdisplay);
BEGIN_RING(evo, 0, 0x08d4 + head, 1);
OUT_RING (evo, 0x00000000); /* screen position */
}
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1);
OUT_RING(evo, 0);
/* This is the actual resolution of the mode. */
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1);
OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1);
OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0));
nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
nv_crtc->set_dither(nv_crtc, false);
nv_crtc->set_scale(nv_crtc, false);
return nv50_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
}
@ -692,7 +676,7 @@ nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
if (ret)
return ret;
ret = nv50_crtc_wait_complete(crtc);
ret = nv50_display_sync(crtc->dev);
if (ret)
return ret;
@ -711,7 +695,7 @@ nv50_crtc_mode_set_base_atomic(struct drm_crtc *crtc,
if (ret)
return ret;
return nv50_crtc_wait_complete(crtc);
return nv50_display_sync(crtc->dev);
}
static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
@ -737,12 +721,6 @@ nv50_crtc_create(struct drm_device *dev, int index)
if (!nv_crtc)
return -ENOMEM;
nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL);
if (!nv_crtc->mode) {
kfree(nv_crtc);
return -ENOMEM;
}
/* Default CLUT parameters, will be activated on the hw upon
* first mode set.
*/
@ -764,7 +742,6 @@ nv50_crtc_create(struct drm_device *dev, int index)
}
if (ret) {
kfree(nv_crtc->mode);
kfree(nv_crtc);
return ret;
}

View File

@ -199,11 +199,6 @@ nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
return true;
}
static void
nv50_dac_prepare(struct drm_encoder *encoder)
{
}
static void
nv50_dac_commit(struct drm_encoder *encoder)
{
@ -266,7 +261,7 @@ static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
.save = nv50_dac_save,
.restore = nv50_dac_restore,
.mode_fixup = nv50_dac_mode_fixup,
.prepare = nv50_dac_prepare,
.prepare = nv50_dac_disconnect,
.commit = nv50_dac_commit,
.mode_set = nv50_dac_mode_set,
.get_crtc = nv50_dac_crtc_get,

View File

@ -50,9 +50,53 @@ nv50_sor_nr(struct drm_device *dev)
return 4;
}
static int
evo_icmd(struct drm_device *dev, int ch, u32 mthd, u32 data)
{
int ret = 0;
nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000001);
nv_wr32(dev, 0x610304 + (ch * 0x08), data);
nv_wr32(dev, 0x610300 + (ch * 0x08), 0x80000001 | mthd);
if (!nv_wait(dev, 0x610300 + (ch * 0x08), 0x80000000, 0x00000000))
ret = -EBUSY;
if (ret || (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO))
NV_INFO(dev, "EvoPIO: %d 0x%04x 0x%08x\n", ch, mthd, data);
nv_mask(dev, 0x610300 + (ch * 0x08), 0x00000001, 0x00000000);
return ret;
}
int
nv50_display_early_init(struct drm_device *dev)
{
u32 ctrl = nv_rd32(dev, 0x610200);
int i;
/* check if master evo channel is already active, a good a sign as any
* that the display engine is in a weird state (hibernate/kexec), if
* it is, do our best to reset the display engine...
*/
if ((ctrl & 0x00000003) == 0x00000003) {
NV_INFO(dev, "PDISP: EVO(0) 0x%08x, resetting...\n", ctrl);
/* deactivate both heads first, PDISP will disappear forever
* (well, until you power cycle) on some boards as soon as
* PMC_ENABLE is hit unless they are..
*/
for (i = 0; i < 2; i++) {
evo_icmd(dev, 0, 0x0880 + (i * 0x400), 0x05000000);
evo_icmd(dev, 0, 0x089c + (i * 0x400), 0);
evo_icmd(dev, 0, 0x0840 + (i * 0x400), 0);
evo_icmd(dev, 0, 0x0844 + (i * 0x400), 0);
evo_icmd(dev, 0, 0x085c + (i * 0x400), 0);
evo_icmd(dev, 0, 0x0874 + (i * 0x400), 0);
}
evo_icmd(dev, 0, 0x0080, 0);
/* reset PDISP */
nv_mask(dev, 0x000200, 0x40000000, 0x00000000);
nv_mask(dev, 0x000200, 0x40000000, 0x40000000);
}
return 0;
}
@ -62,11 +106,40 @@ nv50_display_late_takedown(struct drm_device *dev)
}
int
nv50_display_init(struct drm_device *dev)
nv50_display_sync(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct drm_connector *connector;
struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
struct nv50_display *disp = nv50_display(dev);
struct nouveau_channel *evo = disp->master;
u64 start;
int ret;
ret = RING_SPACE(evo, 6);
if (ret == 0) {
BEGIN_RING(evo, 0, 0x0084, 1);
OUT_RING (evo, 0x80000000);
BEGIN_RING(evo, 0, 0x0080, 1);
OUT_RING (evo, 0);
BEGIN_RING(evo, 0, 0x0084, 1);
OUT_RING (evo, 0x00000000);
nv_wo32(disp->ntfy, 0x000, 0x00000000);
FIRE_RING (evo);
start = ptimer->read(dev);
do {
if (nv_ro32(disp->ntfy, 0x000))
return 0;
} while (ptimer->read(dev) - start < 2000000000ULL);
}
return -EBUSY;
}
int
nv50_display_init(struct drm_device *dev)
{
struct nouveau_channel *evo;
int ret, i;
u32 val;
@ -161,16 +234,6 @@ nv50_display_init(struct drm_device *dev)
NV50_PDISPLAY_INTR_EN_1_CLK_UNK20 |
NV50_PDISPLAY_INTR_EN_1_CLK_UNK40);
/* enable hotplug interrupts */
list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
struct nouveau_connector *conn = nouveau_connector(connector);
if (conn->dcb->gpio_tag == 0xff)
continue;
pgpio->irq_enable(dev, conn->dcb->gpio_tag, true);
}
ret = nv50_evo_init(dev);
if (ret)
return ret;
@ -178,36 +241,19 @@ nv50_display_init(struct drm_device *dev)
nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->vinst >> 8) | 9);
ret = RING_SPACE(evo, 15);
ret = RING_SPACE(evo, 3);
if (ret)
return ret;
BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2);
OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
OUT_RING(evo, NvEvoSync);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1);
OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1);
OUT_RING(evo, 0);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1);
OUT_RING(evo, 0);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1);
OUT_RING(evo, 0);
/* required to make display sync channels not hate life */
BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK900), 1);
OUT_RING (evo, 0x00000311);
BEGIN_RING(evo, 0, NV50_EVO_CRTC(1, UNK900), 1);
OUT_RING (evo, 0x00000311);
FIRE_RING(evo);
if (!nv_wait(dev, 0x640004, 0xffffffff, evo->dma.put << 2))
NV_ERROR(dev, "evo pushbuf stalled\n");
OUT_RING (evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
OUT_RING (evo, NvEvoSync);
return 0;
return nv50_display_sync(dev);
}
static int nv50_display_disable(struct drm_device *dev)
void
nv50_display_fini(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_display *disp = nv50_display(dev);
struct nouveau_channel *evo = disp->master;
struct drm_crtc *drm_crtc;
@ -270,18 +316,10 @@ static int nv50_display_disable(struct drm_device *dev)
/* disable interrupts. */
nv_wr32(dev, NV50_PDISPLAY_INTR_EN_1, 0x00000000);
/* disable hotplug interrupts */
nv_wr32(dev, 0xe054, 0xffffffff);
nv_wr32(dev, 0xe050, 0x00000000);
if (dev_priv->chipset >= 0x90) {
nv_wr32(dev, 0xe074, 0xffffffff);
nv_wr32(dev, 0xe070, 0x00000000);
}
return 0;
}
int nv50_display_create(struct drm_device *dev)
int
nv50_display_create(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct dcb_table *dcb = &dev_priv->vbios.dcb;
@ -341,7 +379,7 @@ int nv50_display_create(struct drm_device *dev)
tasklet_init(&priv->tasklet, nv50_display_bh, (unsigned long)dev);
nouveau_irq_register(dev, 26, nv50_display_isr);
ret = nv50_display_init(dev);
ret = nv50_evo_create(dev);
if (ret) {
nv50_display_destroy(dev);
return ret;
@ -357,7 +395,7 @@ nv50_display_destroy(struct drm_device *dev)
NV_DEBUG_KMS(dev, "\n");
nv50_display_disable(dev);
nv50_evo_destroy(dev);
nouveau_irq_unregister(dev, 26);
kfree(disp);
}
@ -521,7 +559,7 @@ nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcb,
} else {
/* determine number of lvds links */
if (nv_connector && nv_connector->edid &&
nv_connector->dcb->type == DCB_CONNECTOR_LVDS_SPWG) {
nv_connector->type == DCB_CONNECTOR_LVDS_SPWG) {
/* http://www.spwg.org */
if (((u8 *)nv_connector->edid)[121] == 2)
script |= 0x0100;
@ -722,8 +760,8 @@ nv50_display_unk20_handler(struct drm_device *dev)
if (crtc >= 0) {
pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(crtc, CLOCK));
pclk &= 0x003fffff;
nv50_crtc_set_clock(dev, crtc, pclk);
if (pclk)
nv50_crtc_set_clock(dev, crtc, pclk);
tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(crtc));
tmp &= ~0x000000f;

View File

@ -69,14 +69,18 @@ int nv50_display_early_init(struct drm_device *dev);
void nv50_display_late_takedown(struct drm_device *dev);
int nv50_display_create(struct drm_device *dev);
int nv50_display_init(struct drm_device *dev);
void nv50_display_fini(struct drm_device *dev);
void nv50_display_destroy(struct drm_device *dev);
int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
int nv50_display_sync(struct drm_device *);
int nv50_display_flip_next(struct drm_crtc *, struct drm_framebuffer *,
struct nouveau_channel *chan);
void nv50_display_flip_stop(struct drm_crtc *);
int nv50_evo_create(struct drm_device *dev);
void nv50_evo_destroy(struct drm_device *dev);
int nv50_evo_init(struct drm_device *dev);
void nv50_evo_fini(struct drm_device *dev);
void nv50_evo_dmaobj_init(struct nouveau_gpuobj *, u32 memtype, u64 base,

View File

@ -218,7 +218,7 @@ nv50_evo_channel_fini(struct nouveau_channel *evo)
}
}
static void
void
nv50_evo_destroy(struct drm_device *dev)
{
struct nv50_display *disp = nv50_display(dev);
@ -235,7 +235,7 @@ nv50_evo_destroy(struct drm_device *dev)
nv50_evo_channel_del(&disp->master);
}
static int
int
nv50_evo_create(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
@ -388,12 +388,6 @@ nv50_evo_init(struct drm_device *dev)
struct nv50_display *disp = nv50_display(dev);
int ret, i;
if (!disp->master) {
ret = nv50_evo_create(dev);
if (ret)
return ret;
}
ret = nv50_evo_channel_init(disp->master);
if (ret)
return ret;
@ -420,6 +414,4 @@ nv50_evo_fini(struct drm_device *dev)
if (disp->master)
nv50_evo_channel_fini(disp->master);
nv50_evo_destroy(dev);
}

View File

@ -230,6 +230,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
struct drm_device *dev = chan->dev;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpuobj *ramfc = NULL;
uint64_t ib_offset = chan->pushbuf_base + chan->dma.ib_base * 4;
unsigned long flags;
int ret;
@ -280,8 +281,9 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
nv_wo32(ramfc, 0x7c, 0x30000001);
nv_wo32(ramfc, 0x78, 0x00000000);
nv_wo32(ramfc, 0x3c, 0x403f6078);
nv_wo32(ramfc, 0x50, chan->pushbuf_base + chan->dma.ib_base * 4);
nv_wo32(ramfc, 0x54, drm_order(chan->dma.ib_max + 1) << 16);
nv_wo32(ramfc, 0x50, lower_32_bits(ib_offset));
nv_wo32(ramfc, 0x54, upper_32_bits(ib_offset) |
drm_order(chan->dma.ib_max + 1) << 16);
if (dev_priv->chipset != 0x50) {
nv_wo32(chan->ramin, 0, chan->id);

View File

@ -25,229 +25,95 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_hw.h"
#include "nouveau_gpio.h"
#include "nv50_display.h"
static void nv50_gpio_isr(struct drm_device *dev);
static void nv50_gpio_isr_bh(struct work_struct *work);
struct nv50_gpio_priv {
struct list_head handlers;
spinlock_t lock;
};
struct nv50_gpio_handler {
struct drm_device *dev;
struct list_head head;
struct work_struct work;
bool inhibit;
struct dcb_gpio_entry *gpio;
void (*handler)(void *data, int state);
void *data;
};
static int
nv50_gpio_location(struct dcb_gpio_entry *gpio, uint32_t *reg, uint32_t *shift)
nv50_gpio_location(int line, u32 *reg, u32 *shift)
{
const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
if (gpio->line >= 32)
if (line >= 32)
return -EINVAL;
*reg = nv50_gpio_reg[gpio->line >> 3];
*shift = (gpio->line & 7) << 2;
*reg = nv50_gpio_reg[line >> 3];
*shift = (line & 7) << 2;
return 0;
}
int
nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
nv50_gpio_drive(struct drm_device *dev, int line, int dir, int out)
{
struct dcb_gpio_entry *gpio;
uint32_t r, s, v;
u32 reg, shift;
gpio = nouveau_bios_gpio_entry(dev, tag);
if (!gpio)
return -ENOENT;
if (nv50_gpio_location(gpio, &r, &s))
if (nv50_gpio_location(line, &reg, &shift))
return -EINVAL;
v = nv_rd32(dev, r) >> (s + 2);
return ((v & 1) == (gpio->state[1] & 1));
nv_mask(dev, reg, 7 << shift, (((dir ^ 1) << 1) | out) << shift);
return 0;
}
int
nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
nv50_gpio_sense(struct drm_device *dev, int line)
{
struct dcb_gpio_entry *gpio;
uint32_t r, s, v;
u32 reg, shift;
gpio = nouveau_bios_gpio_entry(dev, tag);
if (!gpio)
return -ENOENT;
if (nv50_gpio_location(gpio, &r, &s))
if (nv50_gpio_location(line, &reg, &shift))
return -EINVAL;
v = nv_rd32(dev, r) & ~(0x3 << s);
v |= (gpio->state[state] ^ 2) << s;
nv_wr32(dev, r, v);
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)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nv50_gpio_priv *priv = pgpio->priv;
struct nv50_gpio_handler *gpioh;
struct dcb_gpio_entry *gpio;
unsigned long flags;
gpio = nouveau_bios_gpio_entry(dev, tag);
if (!gpio)
return -ENOENT;
gpioh = kzalloc(sizeof(*gpioh), GFP_KERNEL);
if (!gpioh)
return -ENOMEM;
INIT_WORK(&gpioh->work, nv50_gpio_isr_bh);
gpioh->dev = dev;
gpioh->gpio = gpio;
gpioh->handler = handler;
gpioh->data = data;
spin_lock_irqsave(&priv->lock, flags);
list_add(&gpioh->head, &priv->handlers);
spin_unlock_irqrestore(&priv->lock, flags);
return 0;
return !!(nv_rd32(dev, reg) & (4 << shift));
}
void
nv50_gpio_irq_unregister(struct drm_device *dev, enum dcb_gpio_tag tag,
void (*handler)(void *, int), void *data)
nv50_gpio_irq_enable(struct drm_device *dev, int line, bool on)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nv50_gpio_priv *priv = pgpio->priv;
struct nv50_gpio_handler *gpioh, *tmp;
struct dcb_gpio_entry *gpio;
LIST_HEAD(tofree);
unsigned long flags;
gpio = nouveau_bios_gpio_entry(dev, tag);
if (!gpio)
return;
spin_lock_irqsave(&priv->lock, flags);
list_for_each_entry_safe(gpioh, tmp, &priv->handlers, head) {
if (gpioh->gpio != gpio ||
gpioh->handler != handler ||
gpioh->data != data)
continue;
list_move(&gpioh->head, &tofree);
}
spin_unlock_irqrestore(&priv->lock, flags);
list_for_each_entry_safe(gpioh, tmp, &tofree, head) {
flush_work_sync(&gpioh->work);
kfree(gpioh);
}
}
bool
nv50_gpio_irq_enable(struct drm_device *dev, enum dcb_gpio_tag tag, bool on)
{
struct dcb_gpio_entry *gpio;
u32 reg, mask;
gpio = nouveau_bios_gpio_entry(dev, tag);
if (!gpio)
return false;
reg = gpio->line < 16 ? 0xe050 : 0xe070;
mask = 0x00010001 << (gpio->line & 0xf);
u32 reg = line < 16 ? 0xe050 : 0xe070;
u32 mask = 0x00010001 << (line & 0xf);
nv_wr32(dev, reg + 4, mask);
reg = nv_mask(dev, reg + 0, mask, on ? mask : 0);
return (reg & mask) == mask;
nv_mask(dev, reg + 0, mask, on ? mask : 0);
}
static int
nv50_gpio_create(struct drm_device *dev)
int
nvd0_gpio_drive(struct drm_device *dev, int line, int dir, int out)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nv50_gpio_priv *priv;
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
INIT_LIST_HEAD(&priv->handlers);
spin_lock_init(&priv->lock);
pgpio->priv = priv;
u32 data = ((dir ^ 1) << 13) | (out << 12);
nv_mask(dev, 0x00d610 + (line * 4), 0x00003000, data);
nv_mask(dev, 0x00d604, 0x00000001, 0x00000001); /* update? */
return 0;
}
int
nvd0_gpio_sense(struct drm_device *dev, int line)
{
return !!(nv_rd32(dev, 0x00d610 + (line * 4)) & 0x00004000);
}
static void
nv50_gpio_destroy(struct drm_device *dev)
nv50_gpio_isr(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
u32 intr0, intr1 = 0;
u32 hi, lo;
kfree(pgpio->priv);
pgpio->priv = NULL;
intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
if (dev_priv->chipset >= 0x90)
intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
hi = (intr0 & 0x0000ffff) | (intr1 << 16);
lo = (intr0 >> 16) | (intr1 & 0xffff0000);
nouveau_gpio_isr(dev, 0, hi | lo);
nv_wr32(dev, 0xe054, intr0);
if (dev_priv->chipset >= 0x90)
nv_wr32(dev, 0xe074, intr1);
}
int
nv50_gpio_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
int ret;
if (!pgpio->priv) {
ret = nv50_gpio_create(dev);
if (ret)
return ret;
}
/* disable, and ack any pending gpio interrupts */
nv_wr32(dev, 0xe050, 0x00000000);
@ -270,64 +136,4 @@ nv50_gpio_fini(struct drm_device *dev)
if (dev_priv->chipset >= 0x90)
nv_wr32(dev, 0xe070, 0x00000000);
nouveau_irq_unregister(dev, 21);
nv50_gpio_destroy(dev);
}
static void
nv50_gpio_isr_bh(struct work_struct *work)
{
struct nv50_gpio_handler *gpioh =
container_of(work, struct nv50_gpio_handler, work);
struct drm_nouveau_private *dev_priv = gpioh->dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nv50_gpio_priv *priv = pgpio->priv;
unsigned long flags;
int state;
state = pgpio->get(gpioh->dev, gpioh->gpio->tag);
if (state < 0)
return;
gpioh->handler(gpioh->data, state);
spin_lock_irqsave(&priv->lock, flags);
gpioh->inhibit = false;
spin_unlock_irqrestore(&priv->lock, flags);
}
static void
nv50_gpio_isr(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_gpio_engine *pgpio = &dev_priv->engine.gpio;
struct nv50_gpio_priv *priv = pgpio->priv;
struct nv50_gpio_handler *gpioh;
u32 intr0, intr1 = 0;
u32 hi, lo, ch;
intr0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
if (dev_priv->chipset >= 0x90)
intr1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
hi = (intr0 & 0x0000ffff) | (intr1 << 16);
lo = (intr0 >> 16) | (intr1 & 0xffff0000);
ch = hi | lo;
nv_wr32(dev, 0xe054, intr0);
if (dev_priv->chipset >= 0x90)
nv_wr32(dev, 0xe074, intr1);
spin_lock(&priv->lock);
list_for_each_entry(gpioh, &priv->handlers, head) {
if (!(ch & (1 << gpioh->gpio->line)))
continue;
if (gpioh->inhibit)
continue;
gpioh->inhibit = true;
schedule_work(&gpioh->work);
}
spin_unlock(&priv->lock);
}

View File

@ -616,9 +616,9 @@ nv50_pgraph_tp_trap(struct drm_device *dev, int type, uint32_t ustatus_old,
}
break;
case 7: /* MP error */
if (ustatus & 0x00010000) {
if (ustatus & 0x04030000) {
nv50_pgraph_mp_trap(dev, i, display);
ustatus &= ~0x00010000;
ustatus &= ~0x04030000;
}
break;
case 8: /* TPDMA error */

View File

@ -25,122 +25,745 @@
#include "drmP.h"
#include "nouveau_drv.h"
#include "nouveau_bios.h"
#include "nouveau_hw.h"
#include "nouveau_pm.h"
#include "nouveau_hwsq.h"
struct nv50_pm_state {
struct nouveau_pm_level *perflvl;
struct pll_lims pll;
enum pll_types type;
int N, M, P;
enum clk_src {
clk_src_crystal,
clk_src_href,
clk_src_hclk,
clk_src_hclkm3,
clk_src_hclkm3d2,
clk_src_host,
clk_src_nvclk,
clk_src_sclk,
clk_src_mclk,
clk_src_vdec,
clk_src_dom6
};
int
nv50_pm_clock_get(struct drm_device *dev, u32 id)
static u32 read_clk(struct drm_device *, enum clk_src);
static u32
read_div(struct drm_device *dev)
{
struct pll_lims pll;
int P, N, M, ret;
u32 reg0, reg1;
struct drm_nouveau_private *dev_priv = dev->dev_private;
ret = get_pll_limits(dev, id, &pll);
if (ret)
return ret;
switch (dev_priv->chipset) {
case 0x50: /* it exists, but only has bit 31, not the dividers.. */
case 0x84:
case 0x86:
case 0x98:
case 0xa0:
return nv_rd32(dev, 0x004700);
case 0x92:
case 0x94:
case 0x96:
return nv_rd32(dev, 0x004800);
default:
return 0x00000000;
}
}
reg0 = nv_rd32(dev, pll.reg + 0);
reg1 = nv_rd32(dev, pll.reg + 4);
static u32
read_pll_src(struct drm_device *dev, u32 base)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 coef, ref = read_clk(dev, clk_src_crystal);
u32 rsel = nv_rd32(dev, 0x00e18c);
int P, N, M, id;
if ((reg0 & 0x80000000) == 0) {
if (id == PLL_SHADER) {
NV_DEBUG(dev, "Shader PLL is disabled. "
"Shader clock is twice the core\n");
ret = nv50_pm_clock_get(dev, PLL_CORE);
if (ret > 0)
return ret << 1;
} else if (id == PLL_MEMORY) {
NV_DEBUG(dev, "Memory PLL is disabled. "
"Memory clock is equal to the ref_clk\n");
return pll.refclk;
switch (dev_priv->chipset) {
case 0x50:
case 0xa0:
switch (base) {
case 0x4020:
case 0x4028: id = !!(rsel & 0x00000004); break;
case 0x4008: id = !!(rsel & 0x00000008); break;
case 0x4030: id = 0; break;
default:
NV_ERROR(dev, "ref: bad pll 0x%06x\n", base);
return 0;
}
coef = nv_rd32(dev, 0x00e81c + (id * 0x0c));
ref *= (coef & 0x01000000) ? 2 : 4;
P = (coef & 0x00070000) >> 16;
N = ((coef & 0x0000ff00) >> 8) + 1;
M = ((coef & 0x000000ff) >> 0) + 1;
break;
case 0x84:
case 0x86:
case 0x92:
coef = nv_rd32(dev, 0x00e81c);
P = (coef & 0x00070000) >> 16;
N = (coef & 0x0000ff00) >> 8;
M = (coef & 0x000000ff) >> 0;
break;
case 0x94:
case 0x96:
case 0x98:
rsel = nv_rd32(dev, 0x00c050);
switch (base) {
case 0x4020: rsel = (rsel & 0x00000003) >> 0; break;
case 0x4008: rsel = (rsel & 0x0000000c) >> 2; break;
case 0x4028: rsel = (rsel & 0x00001800) >> 11; break;
case 0x4030: rsel = 3; break;
default:
NV_ERROR(dev, "ref: bad pll 0x%06x\n", base);
return 0;
}
switch (rsel) {
case 0: id = 1; break;
case 1: return read_clk(dev, clk_src_crystal);
case 2: return read_clk(dev, clk_src_href);
case 3: id = 0; break;
}
coef = nv_rd32(dev, 0x00e81c + (id * 0x28));
P = (nv_rd32(dev, 0x00e824 + (id * 0x28)) >> 16) & 7;
P += (coef & 0x00070000) >> 16;
N = (coef & 0x0000ff00) >> 8;
M = (coef & 0x000000ff) >> 0;
break;
default:
BUG_ON(1);
}
if (M)
return (ref * N / M) >> P;
return 0;
}
static u32
read_pll_ref(struct drm_device *dev, u32 base)
{
u32 src, mast = nv_rd32(dev, 0x00c040);
switch (base) {
case 0x004028:
src = !!(mast & 0x00200000);
break;
case 0x004020:
src = !!(mast & 0x00400000);
break;
case 0x004008:
src = !!(mast & 0x00010000);
break;
case 0x004030:
src = !!(mast & 0x02000000);
break;
case 0x00e810:
return read_clk(dev, clk_src_crystal);
default:
NV_ERROR(dev, "bad pll 0x%06x\n", base);
return 0;
}
if (src)
return read_clk(dev, clk_src_href);
return read_pll_src(dev, base);
}
static u32
read_pll(struct drm_device *dev, u32 base)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 mast = nv_rd32(dev, 0x00c040);
u32 ctrl = nv_rd32(dev, base + 0);
u32 coef = nv_rd32(dev, base + 4);
u32 ref = read_pll_ref(dev, base);
u32 clk = 0;
int N1, N2, M1, M2;
if (base == 0x004028 && (mast & 0x00100000)) {
/* wtf, appears to only disable post-divider on nva0 */
if (dev_priv->chipset != 0xa0)
return read_clk(dev, clk_src_dom6);
}
N2 = (coef & 0xff000000) >> 24;
M2 = (coef & 0x00ff0000) >> 16;
N1 = (coef & 0x0000ff00) >> 8;
M1 = (coef & 0x000000ff);
if ((ctrl & 0x80000000) && M1) {
clk = ref * N1 / M1;
if ((ctrl & 0x40000100) == 0x40000000) {
if (M2)
clk = clk * N2 / M2;
else
clk = 0;
}
}
P = (reg0 & 0x00070000) >> 16;
N = (reg1 & 0x0000ff00) >> 8;
M = (reg1 & 0x000000ff);
return clk;
}
return ((pll.refclk * N / M) >> P);
static u32
read_clk(struct drm_device *dev, enum clk_src src)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 mast = nv_rd32(dev, 0x00c040);
u32 P = 0;
switch (src) {
case clk_src_crystal:
return dev_priv->crystal;
case clk_src_href:
return 100000; /* PCIE reference clock */
case clk_src_hclk:
return read_clk(dev, clk_src_href) * 27778 / 10000;
case clk_src_hclkm3:
return read_clk(dev, clk_src_hclk) * 3;
case clk_src_hclkm3d2:
return read_clk(dev, clk_src_hclk) * 3 / 2;
case clk_src_host:
switch (mast & 0x30000000) {
case 0x00000000: return read_clk(dev, clk_src_href);
case 0x10000000: break;
case 0x20000000: /* !0x50 */
case 0x30000000: return read_clk(dev, clk_src_hclk);
}
break;
case clk_src_nvclk:
if (!(mast & 0x00100000))
P = (nv_rd32(dev, 0x004028) & 0x00070000) >> 16;
switch (mast & 0x00000003) {
case 0x00000000: return read_clk(dev, clk_src_crystal) >> P;
case 0x00000001: return read_clk(dev, clk_src_dom6);
case 0x00000002: return read_pll(dev, 0x004020) >> P;
case 0x00000003: return read_pll(dev, 0x004028) >> P;
}
break;
case clk_src_sclk:
P = (nv_rd32(dev, 0x004020) & 0x00070000) >> 16;
switch (mast & 0x00000030) {
case 0x00000000:
if (mast & 0x00000080)
return read_clk(dev, clk_src_host) >> P;
return read_clk(dev, clk_src_crystal) >> P;
case 0x00000010: break;
case 0x00000020: return read_pll(dev, 0x004028) >> P;
case 0x00000030: return read_pll(dev, 0x004020) >> P;
}
break;
case clk_src_mclk:
P = (nv_rd32(dev, 0x004008) & 0x00070000) >> 16;
if (nv_rd32(dev, 0x004008) & 0x00000200) {
switch (mast & 0x0000c000) {
case 0x00000000:
return read_clk(dev, clk_src_crystal) >> P;
case 0x00008000:
case 0x0000c000:
return read_clk(dev, clk_src_href) >> P;
}
} else {
return read_pll(dev, 0x004008) >> P;
}
break;
case clk_src_vdec:
P = (read_div(dev) & 0x00000700) >> 8;
switch (dev_priv->chipset) {
case 0x84:
case 0x86:
case 0x92:
case 0x94:
case 0x96:
case 0xa0:
switch (mast & 0x00000c00) {
case 0x00000000:
if (dev_priv->chipset == 0xa0) /* wtf?? */
return read_clk(dev, clk_src_nvclk) >> P;
return read_clk(dev, clk_src_crystal) >> P;
case 0x00000400:
return 0;
case 0x00000800:
if (mast & 0x01000000)
return read_pll(dev, 0x004028) >> P;
return read_pll(dev, 0x004030) >> P;
case 0x00000c00:
return read_clk(dev, clk_src_nvclk) >> P;
}
break;
case 0x98:
switch (mast & 0x00000c00) {
case 0x00000000:
return read_clk(dev, clk_src_nvclk) >> P;
case 0x00000400:
return 0;
case 0x00000800:
return read_clk(dev, clk_src_hclkm3d2) >> P;
case 0x00000c00:
return read_clk(dev, clk_src_mclk) >> P;
}
break;
}
break;
case clk_src_dom6:
switch (dev_priv->chipset) {
case 0x50:
case 0xa0:
return read_pll(dev, 0x00e810) >> 2;
case 0x84:
case 0x86:
case 0x92:
case 0x94:
case 0x96:
case 0x98:
P = (read_div(dev) & 0x00000007) >> 0;
switch (mast & 0x0c000000) {
case 0x00000000: return read_clk(dev, clk_src_href);
case 0x04000000: break;
case 0x08000000: return read_clk(dev, clk_src_hclk);
case 0x0c000000:
return read_clk(dev, clk_src_hclkm3) >> P;
}
break;
default:
break;
}
default:
break;
}
NV_DEBUG(dev, "unknown clock source %d 0x%08x\n", src, mast);
return 0;
}
int
nv50_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->chipset == 0xaa ||
dev_priv->chipset == 0xac)
return 0;
perflvl->core = read_clk(dev, clk_src_nvclk);
perflvl->shader = read_clk(dev, clk_src_sclk);
perflvl->memory = read_clk(dev, clk_src_mclk);
if (dev_priv->chipset != 0x50) {
perflvl->vdec = read_clk(dev, clk_src_vdec);
perflvl->dom6 = read_clk(dev, clk_src_dom6);
}
return 0;
}
struct nv50_pm_state {
struct hwsq_ucode mclk_hwsq;
u32 mscript;
u32 emast;
u32 nctrl;
u32 ncoef;
u32 sctrl;
u32 scoef;
u32 amast;
u32 pdivs;
};
static u32
calc_pll(struct drm_device *dev, u32 reg, struct pll_lims *pll,
u32 clk, int *N1, int *M1, int *log2P)
{
struct nouveau_pll_vals coef;
int ret;
ret = get_pll_limits(dev, reg, pll);
if (ret)
return 0;
pll->vco2.maxfreq = 0;
pll->refclk = read_pll_ref(dev, reg);
if (!pll->refclk)
return 0;
ret = nouveau_calc_pll_mnp(dev, pll, clk, &coef);
if (ret == 0)
return 0;
*N1 = coef.N1;
*M1 = coef.M1;
*log2P = coef.log2P;
return ret;
}
static inline u32
calc_div(u32 src, u32 target, int *div)
{
u32 clk0 = src, clk1 = src;
for (*div = 0; *div <= 7; (*div)++) {
if (clk0 <= target) {
clk1 = clk0 << (*div ? 1 : 0);
break;
}
clk0 >>= 1;
}
if (target - clk0 <= clk1 - target)
return clk0;
(*div)--;
return clk1;
}
static inline u32
clk_same(u32 a, u32 b)
{
return ((a / 1000) == (b / 1000));
}
static int
calc_mclk(struct drm_device *dev, u32 freq, struct hwsq_ucode *hwsq)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct pll_lims pll;
u32 mast = nv_rd32(dev, 0x00c040);
u32 ctrl = nv_rd32(dev, 0x004008);
u32 coef = nv_rd32(dev, 0x00400c);
u32 orig = ctrl;
u32 crtc_mask = 0;
int N, M, P;
int ret, i;
/* use pcie refclock if possible, otherwise use mpll */
ctrl &= ~0x81ff0200;
if (clk_same(freq, read_clk(dev, clk_src_href))) {
ctrl |= 0x00000200 | (pll.log2p_bias << 19);
} else {
ret = calc_pll(dev, 0x4008, &pll, freq, &N, &M, &P);
if (ret == 0)
return -EINVAL;
ctrl |= 0x80000000 | (P << 22) | (P << 16);
ctrl |= pll.log2p_bias << 19;
coef = (N << 8) | M;
}
mast &= ~0xc0000000; /* get MCLK_2 from HREF */
mast |= 0x0000c000; /* use MCLK_2 as MPLL_BYPASS clock */
/* determine active crtcs */
for (i = 0; i < 2; i++) {
if (nv_rd32(dev, NV50_PDISPLAY_CRTC_C(i, CLOCK)))
crtc_mask |= (1 << i);
}
/* build the ucode which will reclock the memory for us */
hwsq_init(hwsq);
if (crtc_mask) {
hwsq_op5f(hwsq, crtc_mask, 0x00); /* wait for scanout */
hwsq_op5f(hwsq, crtc_mask, 0x01); /* wait for vblank */
}
if (dev_priv->chipset >= 0x92)
hwsq_wr32(hwsq, 0x611200, 0x00003300); /* disable scanout */
hwsq_setf(hwsq, 0x10, 0); /* disable bus access */
hwsq_op5f(hwsq, 0x00, 0x01); /* no idea :s */
/* prepare memory controller */
hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */
hwsq_wr32(hwsq, 0x1002d0, 0x00000001); /* force refresh */
hwsq_wr32(hwsq, 0x100210, 0x00000000); /* stop the automatic refresh */
hwsq_wr32(hwsq, 0x1002dc, 0x00000001); /* start self refresh mode */
/* reclock memory */
hwsq_wr32(hwsq, 0xc040, mast);
hwsq_wr32(hwsq, 0x4008, orig | 0x00000200); /* bypass MPLL */
hwsq_wr32(hwsq, 0x400c, coef);
hwsq_wr32(hwsq, 0x4008, ctrl);
/* restart memory controller */
hwsq_wr32(hwsq, 0x1002d4, 0x00000001); /* precharge banks and idle */
hwsq_wr32(hwsq, 0x1002dc, 0x00000000); /* stop self refresh mode */
hwsq_wr32(hwsq, 0x100210, 0x80000000); /* restart automatic refresh */
hwsq_usec(hwsq, 12); /* wait for the PLL to stabilize */
hwsq_usec(hwsq, 48); /* may be unnecessary: causes flickering */
hwsq_setf(hwsq, 0x10, 1); /* enable bus access */
hwsq_op5f(hwsq, 0x00, 0x00); /* no idea, reverse of 0x00, 0x01? */
if (dev_priv->chipset >= 0x92)
hwsq_wr32(hwsq, 0x611200, 0x00003330); /* enable scanout */
hwsq_fini(hwsq);
return 0;
}
void *
nv50_pm_clock_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl,
u32 id, int khz)
nv50_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct nv50_pm_state *state;
int dummy, ret;
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_pm_state *info;
struct pll_lims pll;
int ret = -EINVAL;
int N, M, P1, P2;
u32 clk, out;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
if (dev_priv->chipset == 0xaa ||
dev_priv->chipset == 0xac)
return ERR_PTR(-ENODEV);
info = kmalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
state->type = id;
state->perflvl = perflvl;
ret = get_pll_limits(dev, id, &state->pll);
if (ret < 0) {
kfree(state);
return (ret == -ENOENT) ? NULL : ERR_PTR(ret);
/* core: for the moment at least, always use nvpll */
clk = calc_pll(dev, 0x4028, &pll, perflvl->core, &N, &M, &P1);
if (clk == 0)
goto error;
info->emast = 0x00000003;
info->nctrl = 0x80000000 | (P1 << 19) | (P1 << 16);
info->ncoef = (N << 8) | M;
/* shader: tie to nvclk if possible, otherwise use spll. have to be
* very careful that the shader clock is at least twice the core, or
* some chipsets will be very unhappy. i expect most or all of these
* cases will be handled by tying to nvclk, but it's possible there's
* corners
*/
if (P1-- && perflvl->shader == (perflvl->core << 1)) {
info->emast |= 0x00000020;
info->sctrl = 0x00000000 | (P1 << 19) | (P1 << 16);
info->scoef = nv_rd32(dev, 0x004024);
} else {
clk = calc_pll(dev, 0x4020, &pll, perflvl->shader, &N, &M, &P1);
if (clk == 0)
goto error;
info->emast |= 0x00000030;
info->sctrl = 0x80000000 | (P1 << 19) | (P1 << 16);
info->scoef = (N << 8) | M;
}
ret = nv50_calc_pll(dev, &state->pll, khz, &state->N, &state->M,
&dummy, &dummy, &state->P);
if (ret < 0) {
kfree(state);
return ERR_PTR(ret);
/* memory: build hwsq ucode which we'll use to reclock memory */
info->mclk_hwsq.len = 0;
if (perflvl->memory) {
clk = calc_mclk(dev, perflvl->memory, &info->mclk_hwsq);
if (clk < 0) {
ret = clk;
goto error;
}
info->mscript = perflvl->memscript;
}
return state;
/* vdec: avoid modifying xpll until we know exactly how the other
* clock domains work, i suspect at least some of them can also be
* tied to xpll...
*/
info->amast = nv_rd32(dev, 0x00c040);
info->pdivs = read_div(dev);
if (perflvl->vdec) {
/* see how close we can get using nvclk as a source */
clk = calc_div(perflvl->core, perflvl->vdec, &P1);
/* see how close we can get using xpll/hclk as a source */
if (dev_priv->chipset != 0x98)
out = read_pll(dev, 0x004030);
else
out = read_clk(dev, clk_src_hclkm3d2);
out = calc_div(out, perflvl->vdec, &P2);
/* select whichever gets us closest */
info->amast &= ~0x00000c00;
info->pdivs &= ~0x00000700;
if (abs((int)perflvl->vdec - clk) <=
abs((int)perflvl->vdec - out)) {
if (dev_priv->chipset != 0x98)
info->amast |= 0x00000c00;
info->pdivs |= P1 << 8;
} else {
info->amast |= 0x00000800;
info->pdivs |= P2 << 8;
}
}
/* dom6: nfi what this is, but we're limited to various combinations
* of the host clock frequency
*/
if (perflvl->dom6) {
info->amast &= ~0x0c000000;
if (clk_same(perflvl->dom6, read_clk(dev, clk_src_href))) {
info->amast |= 0x00000000;
} else
if (clk_same(perflvl->dom6, read_clk(dev, clk_src_hclk))) {
info->amast |= 0x08000000;
} else {
clk = read_clk(dev, clk_src_hclk) * 3;
clk = calc_div(clk, perflvl->dom6, &P1);
info->amast |= 0x0c000000;
info->pdivs = (info->pdivs & ~0x00000007) | P1;
}
}
return info;
error:
kfree(info);
return ERR_PTR(ret);
}
void
nv50_pm_clock_set(struct drm_device *dev, void *pre_state)
static int
prog_mclk(struct drm_device *dev, struct hwsq_ucode *hwsq)
{
struct nv50_pm_state *state = pre_state;
struct nouveau_pm_level *perflvl = state->perflvl;
u32 reg = state->pll.reg, tmp;
struct bit_entry BIT_M;
u16 script;
int N = state->N;
int M = state->M;
int P = state->P;
struct drm_nouveau_private *dev_priv = dev->dev_private;
u32 hwsq_data, hwsq_kick;
int i;
if (state->type == PLL_MEMORY && perflvl->memscript &&
bit_table(dev, 'M', &BIT_M) == 0 &&
BIT_M.version == 1 && BIT_M.length >= 0x0b) {
script = ROM16(BIT_M.data[0x05]);
if (script)
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, -1);
script = ROM16(BIT_M.data[0x09]);
if (script)
nouveau_bios_run_init_table(dev, script, NULL, -1);
nouveau_bios_run_init_table(dev, perflvl->memscript, NULL, -1);
if (dev_priv->chipset < 0x90) {
hwsq_data = 0x001400;
hwsq_kick = 0x00000003;
} else {
hwsq_data = 0x080000;
hwsq_kick = 0x00000001;
}
if (state->type == PLL_MEMORY) {
nv_wr32(dev, 0x100210, 0);
nv_wr32(dev, 0x1002dc, 1);
/* upload hwsq ucode */
nv_mask(dev, 0x001098, 0x00000008, 0x00000000);
nv_wr32(dev, 0x001304, 0x00000000);
for (i = 0; i < hwsq->len / 4; i++)
nv_wr32(dev, hwsq_data + (i * 4), hwsq->ptr.u32[i]);
nv_mask(dev, 0x001098, 0x00000018, 0x00000018);
/* launch, and wait for completion */
nv_wr32(dev, 0x00130c, hwsq_kick);
if (!nv_wait(dev, 0x001308, 0x00000100, 0x00000000)) {
NV_ERROR(dev, "hwsq ucode exec timed out\n");
NV_ERROR(dev, "0x001308: 0x%08x\n", nv_rd32(dev, 0x001308));
for (i = 0; i < hwsq->len / 4; i++) {
NV_ERROR(dev, "0x%06x: 0x%08x\n", 0x1400 + (i * 4),
nv_rd32(dev, 0x001400 + (i * 4)));
}
return -EIO;
}
tmp = nv_rd32(dev, reg + 0) & 0xfff8ffff;
tmp |= 0x80000000 | (P << 16);
nv_wr32(dev, reg + 0, tmp);
nv_wr32(dev, reg + 4, (N << 8) | M);
if (state->type == PLL_MEMORY) {
nv_wr32(dev, 0x1002dc, 0);
nv_wr32(dev, 0x100210, 0x80000000);
}
kfree(state);
return 0;
}
int
nv50_pm_clocks_set(struct drm_device *dev, void *data)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nv50_pm_state *info = data;
struct bit_entry M;
int ret = 0;
/* halt and idle execution engines */
nv_mask(dev, 0x002504, 0x00000001, 0x00000001);
if (!nv_wait(dev, 0x002504, 0x00000010, 0x00000010))
goto error;
/* memory: it is *very* important we change this first, the ucode
* we build in pre() now has hardcoded 0xc040 values, which can't
* change before we execute it or the engine clocks may end up
* messed up.
*/
if (info->mclk_hwsq.len) {
/* execute some scripts that do ??? from the vbios.. */
if (!bit_table(dev, 'M', &M) && M.version == 1) {
if (M.length >= 6)
nouveau_bios_init_exec(dev, ROM16(M.data[5]));
if (M.length >= 8)
nouveau_bios_init_exec(dev, ROM16(M.data[7]));
if (M.length >= 10)
nouveau_bios_init_exec(dev, ROM16(M.data[9]));
nouveau_bios_init_exec(dev, info->mscript);
}
ret = prog_mclk(dev, &info->mclk_hwsq);
if (ret)
goto resume;
}
/* reclock vdec/dom6 */
nv_mask(dev, 0x00c040, 0x00000c00, 0x00000000);
switch (dev_priv->chipset) {
case 0x92:
case 0x94:
case 0x96:
nv_mask(dev, 0x004800, 0x00000707, info->pdivs);
break;
default:
nv_mask(dev, 0x004700, 0x00000707, info->pdivs);
break;
}
nv_mask(dev, 0x00c040, 0x0c000c00, info->amast);
/* core/shader: make sure sclk/nvclk are disconnected from their
* plls (nvclk to dom6, sclk to hclk), modify the plls, and
* reconnect sclk/nvclk to their new clock source
*/
if (dev_priv->chipset < 0x92)
nv_mask(dev, 0x00c040, 0x001000b0, 0x00100080); /* grrr! */
else
nv_mask(dev, 0x00c040, 0x000000b3, 0x00000081);
nv_mask(dev, 0x004020, 0xc03f0100, info->sctrl);
nv_wr32(dev, 0x004024, info->scoef);
nv_mask(dev, 0x004028, 0xc03f0100, info->nctrl);
nv_wr32(dev, 0x00402c, info->ncoef);
nv_mask(dev, 0x00c040, 0x00100033, info->emast);
goto resume;
error:
ret = -EBUSY;
resume:
nv_mask(dev, 0x002504, 0x00000001, 0x00000000);
kfree(info);
return ret;
}
static int
pwm_info(struct drm_device *dev, int *line, int *ctrl, int *indx)
{
if (*line == 0x04) {
*ctrl = 0x00e100;
*line = 4;
*indx = 0;
} else
if (*line == 0x09) {
*ctrl = 0x00e100;
*line = 9;
*indx = 1;
} else
if (*line == 0x10) {
*ctrl = 0x00e28c;
*line = 0;
*indx = 0;
} else {
NV_ERROR(dev, "unknown pwm ctrl for gpio %d\n", *line);
return -ENODEV;
}
return 0;
}
int
nv50_pm_pwm_get(struct drm_device *dev, int line, u32 *divs, u32 *duty)
{
int ctrl, id, ret = pwm_info(dev, &line, &ctrl, &id);
if (ret)
return ret;
if (nv_rd32(dev, ctrl) & (1 << line)) {
*divs = nv_rd32(dev, 0x00e114 + (id * 8));
*duty = nv_rd32(dev, 0x00e118 + (id * 8));
return 0;
}
return -EINVAL;
}
int
nv50_pm_pwm_set(struct drm_device *dev, int line, u32 divs, u32 duty)
{
int ctrl, id, ret = pwm_info(dev, &line, &ctrl, &id);
if (ret)
return ret;
nv_mask(dev, ctrl, 0x00010001 << line, 0x00000001 << line);
nv_wr32(dev, 0x00e114 + (id * 8), divs);
nv_wr32(dev, 0x00e118 + (id * 8), duty | 0x80000000);
return 0;
}

View File

@ -60,6 +60,8 @@ nv50_sor_disconnect(struct drm_encoder *encoder)
BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
OUT_RING (evo, 0);
nouveau_hdmi_mode_set(encoder, NULL);
nv_encoder->crtc = NULL;
nv_encoder->last_dpms = DRM_MODE_DPMS_OFF;
}
@ -172,6 +174,12 @@ nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
static void
nv50_sor_prepare(struct drm_encoder *encoder)
{
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
nv50_sor_disconnect(encoder);
if (nv_encoder->dcb->type == OUTPUT_DP) {
/* avoid race between link training and supervisor intr */
nv50_display_sync(encoder->dev);
}
}
static void
@ -180,8 +188,8 @@ nv50_sor_commit(struct drm_encoder *encoder)
}
static void
nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode)
nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *umode,
struct drm_display_mode *mode)
{
struct nouveau_channel *evo = nv50_display(encoder->dev)->master;
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
@ -193,24 +201,27 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
NV_DEBUG_KMS(dev, "or %d type %d -> crtc %d\n",
nv_encoder->or, nv_encoder->dcb->type, crtc->index);
nv_encoder->crtc = encoder->crtc;
switch (nv_encoder->dcb->type) {
case OUTPUT_TMDS:
if (nv_encoder->dcb->sorconf.link & 1) {
if (adjusted_mode->clock < 165000)
if (mode->clock < 165000)
mode_ctl = 0x0100;
else
mode_ctl = 0x0500;
} else
mode_ctl = 0x0200;
nouveau_hdmi_mode_set(encoder, mode);
break;
case OUTPUT_DP:
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;
nv_encoder->dp.datarate = mode->clock * 18 / 8;
mode_ctl |= 0x00020000;
} else {
nv_encoder->dp.datarate = crtc->mode->clock * 24 / 8;
nv_encoder->dp.datarate = mode->clock * 24 / 8;
mode_ctl |= 0x00050000;
}
@ -228,10 +239,10 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
else
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0;
if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
if (mode->flags & DRM_MODE_FLAG_NHSYNC)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
if (mode->flags & DRM_MODE_FLAG_NVSYNC)
mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
@ -239,12 +250,11 @@ nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
ret = RING_SPACE(evo, 2);
if (ret) {
NV_ERROR(dev, "no space while connecting SOR\n");
nv_encoder->crtc = NULL;
return;
}
BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
OUT_RING(evo, mode_ctl);
nv_encoder->crtc = encoder->crtc;
}
static struct drm_crtc *

View File

@ -48,7 +48,7 @@ nv50_vm_map_pgt(struct nouveau_gpuobj *pgd, u32 pde,
phys |= 0x60;
else if (coverage <= 64 * 1024 * 1024)
phys |= 0x40;
else if (coverage < 128 * 1024 * 1024)
else if (coverage <= 128 * 1024 * 1024)
phys |= 0x20;
}

View File

@ -0,0 +1,83 @@
/*
* 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_util.h"
#include "nouveau_vm.h"
#include "nouveau_ramht.h"
/*XXX: This stub is currently used on NV98+ also, as soon as this becomes
* more than just an enable/disable stub this needs to be split out to
* nv98_bsp.c...
*/
struct nv84_bsp_engine {
struct nouveau_exec_engine base;
};
static int
nv84_bsp_fini(struct drm_device *dev, int engine, bool suspend)
{
if (!(nv_rd32(dev, 0x000200) & 0x00008000))
return 0;
nv_mask(dev, 0x000200, 0x00008000, 0x00000000);
return 0;
}
static int
nv84_bsp_init(struct drm_device *dev, int engine)
{
nv_mask(dev, 0x000200, 0x00008000, 0x00000000);
nv_mask(dev, 0x000200, 0x00008000, 0x00008000);
return 0;
}
static void
nv84_bsp_destroy(struct drm_device *dev, int engine)
{
struct nv84_bsp_engine *pbsp = nv_engine(dev, engine);
NVOBJ_ENGINE_DEL(dev, BSP);
kfree(pbsp);
}
int
nv84_bsp_create(struct drm_device *dev)
{
struct nv84_bsp_engine *pbsp;
pbsp = kzalloc(sizeof(*pbsp), GFP_KERNEL);
if (!pbsp)
return -ENOMEM;
pbsp->base.destroy = nv84_bsp_destroy;
pbsp->base.init = nv84_bsp_init;
pbsp->base.fini = nv84_bsp_fini;
NVOBJ_ENGINE_ADD(dev, BSP, &pbsp->base);
return 0;
}

View File

@ -0,0 +1,83 @@
/*
* 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_util.h"
#include "nouveau_vm.h"
#include "nouveau_ramht.h"
/*XXX: This stub is currently used on NV98+ also, as soon as this becomes
* more than just an enable/disable stub this needs to be split out to
* nv98_vp.c...
*/
struct nv84_vp_engine {
struct nouveau_exec_engine base;
};
static int
nv84_vp_fini(struct drm_device *dev, int engine, bool suspend)
{
if (!(nv_rd32(dev, 0x000200) & 0x00020000))
return 0;
nv_mask(dev, 0x000200, 0x00020000, 0x00000000);
return 0;
}
static int
nv84_vp_init(struct drm_device *dev, int engine)
{
nv_mask(dev, 0x000200, 0x00020000, 0x00000000);
nv_mask(dev, 0x000200, 0x00020000, 0x00020000);
return 0;
}
static void
nv84_vp_destroy(struct drm_device *dev, int engine)
{
struct nv84_vp_engine *pvp = nv_engine(dev, engine);
NVOBJ_ENGINE_DEL(dev, VP);
kfree(pvp);
}
int
nv84_vp_create(struct drm_device *dev)
{
struct nv84_vp_engine *pvp;
pvp = kzalloc(sizeof(*pvp), GFP_KERNEL);
if (!pvp)
return -ENOMEM;
pvp->base.destroy = nv84_vp_destroy;
pvp->base.init = nv84_vp_init;
pvp->base.fini = nv84_vp_fini;
NVOBJ_ENGINE_ADD(dev, VP, &pvp->base);
return 0;
}

View File

@ -0,0 +1,78 @@
/*
* 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_util.h"
#include "nouveau_vm.h"
#include "nouveau_ramht.h"
struct nv98_crypt_engine {
struct nouveau_exec_engine base;
};
static int
nv98_crypt_fini(struct drm_device *dev, int engine, bool suspend)
{
if (!(nv_rd32(dev, 0x000200) & 0x00004000))
return 0;
nv_mask(dev, 0x000200, 0x00004000, 0x00000000);
return 0;
}
static int
nv98_crypt_init(struct drm_device *dev, int engine)
{
nv_mask(dev, 0x000200, 0x00004000, 0x00000000);
nv_mask(dev, 0x000200, 0x00004000, 0x00004000);
return 0;
}
static void
nv98_crypt_destroy(struct drm_device *dev, int engine)
{
struct nv98_crypt_engine *pcrypt = nv_engine(dev, engine);
NVOBJ_ENGINE_DEL(dev, CRYPT);
kfree(pcrypt);
}
int
nv98_crypt_create(struct drm_device *dev)
{
struct nv98_crypt_engine *pcrypt;
pcrypt = kzalloc(sizeof(*pcrypt), GFP_KERNEL);
if (!pcrypt)
return -ENOMEM;
pcrypt->base.destroy = nv98_crypt_destroy;
pcrypt->base.init = nv98_crypt_init;
pcrypt->base.fini = nv98_crypt_fini;
NVOBJ_ENGINE_ADD(dev, CRYPT, &pcrypt->base);
return 0;
}

View File

@ -0,0 +1,78 @@
/*
* 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_util.h"
#include "nouveau_vm.h"
#include "nouveau_ramht.h"
struct nv98_ppp_engine {
struct nouveau_exec_engine base;
};
static int
nv98_ppp_fini(struct drm_device *dev, int engine, bool suspend)
{
if (!(nv_rd32(dev, 0x000200) & 0x00000002))
return 0;
nv_mask(dev, 0x000200, 0x00000002, 0x00000000);
return 0;
}
static int
nv98_ppp_init(struct drm_device *dev, int engine)
{
nv_mask(dev, 0x000200, 0x00000002, 0x00000000);
nv_mask(dev, 0x000200, 0x00000002, 0x00000002);
return 0;
}
static void
nv98_ppp_destroy(struct drm_device *dev, int engine)
{
struct nv98_ppp_engine *pppp = nv_engine(dev, engine);
NVOBJ_ENGINE_DEL(dev, PPP);
kfree(pppp);
}
int
nv98_ppp_create(struct drm_device *dev)
{
struct nv98_ppp_engine *pppp;
pppp = kzalloc(sizeof(*pppp), GFP_KERNEL);
if (!pppp)
return -ENOMEM;
pppp->base.destroy = nv98_ppp_destroy;
pppp->base.init = nv98_ppp_init;
pppp->base.fini = nv98_ppp_fini;
NVOBJ_ENGINE_ADD(dev, PPP, &pppp->base);
return 0;
}

View File

@ -31,8 +31,9 @@
*/
ifdef(`NVA3',
.section nva3_pcopy_data,
.section nvc0_pcopy_data
.section #nva3_pcopy_data
,
.section #nvc0_pcopy_data
)
ctx_object: .b32 0
@ -42,7 +43,7 @@ ctx_dma_query: .b32 0
ctx_dma_src: .b32 0
ctx_dma_dst: .b32 0
,)
.equ ctx_dma_count 3
.equ #ctx_dma_count 3
ctx_query_address_high: .b32 0
ctx_query_address_low: .b32 0
ctx_query_counter: .b32 0
@ -78,64 +79,65 @@ ctx_ycnt: .b32 0
dispatch_table:
// mthd 0x0000, NAME
.b16 0x000 1
.b32 ctx_object ~0xffffffff
.b32 #ctx_object ~0xffffffff
// mthd 0x0100, NOP
.b16 0x040 1
.b32 0x00010000 + cmd_nop ~0xffffffff
.b32 0x00010000 + #cmd_nop ~0xffffffff
// mthd 0x0140, PM_TRIGGER
.b16 0x050 1
.b32 0x00010000 + cmd_pm_trigger ~0xffffffff
.b32 0x00010000 + #cmd_pm_trigger ~0xffffffff
ifdef(`NVA3', `
// mthd 0x0180-0x018c, DMA_
.b16 0x060 ctx_dma_count
.b16 0x060 #ctx_dma_count
dispatch_dma:
.b32 0x00010000 + cmd_dma ~0xffffffff
.b32 0x00010000 + cmd_dma ~0xffffffff
.b32 0x00010000 + cmd_dma ~0xffffffff
.b32 0x00010000 + #cmd_dma ~0xffffffff
.b32 0x00010000 + #cmd_dma ~0xffffffff
.b32 0x00010000 + #cmd_dma ~0xffffffff
',)
// mthd 0x0200-0x0218, SRC_TILE
.b16 0x80 7
.b32 ctx_src_tile_mode ~0x00000fff
.b32 ctx_src_xsize ~0x0007ffff
.b32 ctx_src_ysize ~0x00001fff
.b32 ctx_src_zsize ~0x000007ff
.b32 ctx_src_zoff ~0x00000fff
.b32 ctx_src_xoff ~0x0007ffff
.b32 ctx_src_yoff ~0x00001fff
.b32 #ctx_src_tile_mode ~0x00000fff
.b32 #ctx_src_xsize ~0x0007ffff
.b32 #ctx_src_ysize ~0x00001fff
.b32 #ctx_src_zsize ~0x000007ff
.b32 #ctx_src_zoff ~0x00000fff
.b32 #ctx_src_xoff ~0x0007ffff
.b32 #ctx_src_yoff ~0x00001fff
// mthd 0x0220-0x0238, DST_TILE
.b16 0x88 7
.b32 ctx_dst_tile_mode ~0x00000fff
.b32 ctx_dst_xsize ~0x0007ffff
.b32 ctx_dst_ysize ~0x00001fff
.b32 ctx_dst_zsize ~0x000007ff
.b32 ctx_dst_zoff ~0x00000fff
.b32 ctx_dst_xoff ~0x0007ffff
.b32 ctx_dst_yoff ~0x00001fff
.b32 #ctx_dst_tile_mode ~0x00000fff
.b32 #ctx_dst_xsize ~0x0007ffff
.b32 #ctx_dst_ysize ~0x00001fff
.b32 #ctx_dst_zsize ~0x000007ff
.b32 #ctx_dst_zoff ~0x00000fff
.b32 #ctx_dst_xoff ~0x0007ffff
.b32 #ctx_dst_yoff ~0x00001fff
// mthd 0x0300-0x0304, EXEC, WRCACHE_FLUSH
.b16 0xc0 2
.b32 0x00010000 + cmd_exec ~0xffffffff
.b32 0x00010000 + cmd_wrcache_flush ~0xffffffff
.b32 0x00010000 + #cmd_exec ~0xffffffff
.b32 0x00010000 + #cmd_wrcache_flush ~0xffffffff
// mthd 0x030c-0x0340, various stuff
.b16 0xc3 14
.b32 ctx_src_address_high ~0x000000ff
.b32 ctx_src_address_low ~0xfffffff0
.b32 ctx_dst_address_high ~0x000000ff
.b32 ctx_dst_address_low ~0xfffffff0
.b32 ctx_src_pitch ~0x0007ffff
.b32 ctx_dst_pitch ~0x0007ffff
.b32 ctx_xcnt ~0x0000ffff
.b32 ctx_ycnt ~0x00001fff
.b32 ctx_format ~0x0333ffff
.b32 ctx_swz_const0 ~0xffffffff
.b32 ctx_swz_const1 ~0xffffffff
.b32 ctx_query_address_high ~0x000000ff
.b32 ctx_query_address_low ~0xffffffff
.b32 ctx_query_counter ~0xffffffff
.b32 #ctx_src_address_high ~0x000000ff
.b32 #ctx_src_address_low ~0xfffffff0
.b32 #ctx_dst_address_high ~0x000000ff
.b32 #ctx_dst_address_low ~0xfffffff0
.b32 #ctx_src_pitch ~0x0007ffff
.b32 #ctx_dst_pitch ~0x0007ffff
.b32 #ctx_xcnt ~0x0000ffff
.b32 #ctx_ycnt ~0x00001fff
.b32 #ctx_format ~0x0333ffff
.b32 #ctx_swz_const0 ~0xffffffff
.b32 #ctx_swz_const1 ~0xffffffff
.b32 #ctx_query_address_high ~0x000000ff
.b32 #ctx_query_address_low ~0xffffffff
.b32 #ctx_query_counter ~0xffffffff
.b16 0x800 0
ifdef(`NVA3',
.section nva3_pcopy_code,
.section nvc0_pcopy_code
.section #nva3_pcopy_code
,
.section #nvc0_pcopy_code
)
main:
@ -143,12 +145,12 @@ main:
mov $sp $r0
// setup i0 handler and route fifo and ctxswitch to it
mov $r1 ih
mov $r1 #ih
mov $iv0 $r1
mov $r1 0x400
movw $r2 0xfff3
sethi $r2 0
iowr I[$r2 + 0x300] $r2
iowr I[$r1 + 0x300] $r2
// enable interrupts
or $r2 0xc
@ -164,19 +166,19 @@ main:
bset $flags $p0
spin:
sleep $p0
bra spin
bra #spin
// i0 handler
ih:
iord $r1 I[$r0 + 0x200]
and $r2 $r1 0x00000008
bra e ih_no_chsw
call chsw
bra e #ih_no_chsw
call #chsw
ih_no_chsw:
and $r2 $r1 0x00000004
bra e ih_no_cmd
call dispatch
bra e #ih_no_cmd
call #dispatch
ih_no_cmd:
and $r1 $r1 0x0000000c
@ -235,9 +237,9 @@ ifdef(`NVA3', `
sethi $r4 0x60000
// swap!
bra $p1 swctx_load
bra $p1 #swctx_load
xdst $r0 $r4
bra swctx_done
bra #swctx_done
swctx_load:
xdld $r0 $r4
swctx_done:
@ -251,9 +253,9 @@ chsw:
// if it's active, unload it and return
xbit $r15 $r3 0x1e
bra e chsw_no_unload
bra e #chsw_no_unload
bclr $flags $p1
call swctx
call #swctx
bclr $r3 0x1e
iowr I[$r2] $r3
mov $r4 1
@ -266,20 +268,20 @@ chsw:
// is there a channel waiting to be loaded?
xbit $r13 $r3 0x1e
bra e chsw_finish_load
bra e #chsw_finish_load
bset $flags $p1
call swctx
call #swctx
ifdef(`NVA3',
// load dma objects back into TARGET regs
mov $r5 ctx_dma
mov $r6 ctx_dma_count
mov $r5 #ctx_dma
mov $r6 #ctx_dma_count
chsw_load_ctx_dma:
ld b32 $r7 D[$r5 + $r6 * 4]
add b32 $r8 $r6 0x180
shl b32 $r8 8
iowr I[$r8] $r7
sub b32 $r6 1
bra nc chsw_load_ctx_dma
bra nc #chsw_load_ctx_dma
,)
chsw_finish_load:
@ -297,7 +299,7 @@ dispatch:
shl b32 $r2 0x10
// lookup method in the dispatch table, ILLEGAL_MTHD if not found
mov $r5 dispatch_table
mov $r5 #dispatch_table
clear b32 $r6
clear b32 $r7
dispatch_loop:
@ -305,14 +307,14 @@ dispatch:
ld b16 $r7 D[$r5 + 2]
add b32 $r5 4
cmpu b32 $r4 $r6
bra c dispatch_illegal_mthd
bra c #dispatch_illegal_mthd
add b32 $r7 $r6
cmpu b32 $r4 $r7
bra c dispatch_valid_mthd
bra c #dispatch_valid_mthd
sub b32 $r7 $r6
shl b32 $r7 3
add b32 $r5 $r7
bra dispatch_loop
bra #dispatch_loop
// ensure no bits set in reserved fields, INVALID_BITFIELD
dispatch_valid_mthd:
@ -322,20 +324,20 @@ dispatch:
ld b32 $r5 D[$r4 + 4]
and $r5 $r3
cmpu b32 $r5 0
bra ne dispatch_invalid_bitfield
bra ne #dispatch_invalid_bitfield
// depending on dispatch flags: execute method, or save data as state
ld b16 $r5 D[$r4 + 0]
ld b16 $r6 D[$r4 + 2]
cmpu b32 $r6 0
bra ne dispatch_cmd
bra ne #dispatch_cmd
st b32 D[$r5] $r3
bra dispatch_done
bra #dispatch_done
dispatch_cmd:
bclr $flags $p1
call $r5
bra $p1 dispatch_error
bra dispatch_done
bra $p1 #dispatch_error
bra #dispatch_done
dispatch_invalid_bitfield:
or $r2 2
@ -353,7 +355,7 @@ dispatch:
iord $r2 I[$r0 + 0x200]
and $r2 0x40
cmpu b32 $r2 0
bra ne hostirq_wait
bra ne #hostirq_wait
dispatch_done:
mov $r2 0x1d00
@ -409,10 +411,10 @@ ifdef(`NVA3',
// $r2: hostirq state
// $r3: data
cmd_dma:
sub b32 $r4 dispatch_dma
sub b32 $r4 #dispatch_dma
shr b32 $r4 1
bset $r3 0x1e
st b32 D[$r4 + ctx_dma] $r3
st b32 D[$r4 + #ctx_dma] $r3
add b32 $r4 0x600
shl b32 $r4 6
iowr I[$r4] $r3
@ -430,7 +432,7 @@ cmd_exec_set_format:
st b32 D[$sp + 0x0c] $r0
// extract cpp, src_ncomp and dst_ncomp from FORMAT
ld b32 $r4 D[$r0 + ctx_format]
ld b32 $r4 D[$r0 + #ctx_format]
extr $r5 $r4 16:17
add b32 $r5 1
extr $r6 $r4 20:21
@ -448,22 +450,22 @@ cmd_exec_set_format:
clear b32 $r11
bpc_loop:
cmpu b8 $r10 4
bra nc cmp_c0
bra nc #cmp_c0
mulu $r12 $r10 $r5
add b32 $r12 $r11
bset $flags $p2
bra bpc_next
bra #bpc_next
cmp_c0:
bra ne cmp_c1
bra ne #cmp_c1
mov $r12 0x10
add b32 $r12 $r11
bra bpc_next
bra #bpc_next
cmp_c1:
cmpu b8 $r10 6
bra nc cmp_zero
bra nc #cmp_zero
mov $r12 0x14
add b32 $r12 $r11
bra bpc_next
bra #bpc_next
cmp_zero:
mov $r12 0x80
bpc_next:
@ -471,22 +473,22 @@ cmd_exec_set_format:
add b32 $r8 1
add b32 $r11 1
cmpu b32 $r11 $r5
bra c bpc_loop
bra c #bpc_loop
add b32 $r9 1
cmpu b32 $r9 $r7
bra c ncomp_loop
bra c #ncomp_loop
// SRC_XCNT = (xcnt * src_cpp), or 0 if no src ref in swz (hw will hang)
mulu $r6 $r5
st b32 D[$r0 + ctx_src_cpp] $r6
ld b32 $r8 D[$r0 + ctx_xcnt]
st b32 D[$r0 + #ctx_src_cpp] $r6
ld b32 $r8 D[$r0 + #ctx_xcnt]
mulu $r6 $r8
bra $p2 dst_xcnt
bra $p2 #dst_xcnt
clear b32 $r6
dst_xcnt:
mulu $r7 $r5
st b32 D[$r0 + ctx_dst_cpp] $r7
st b32 D[$r0 + #ctx_dst_cpp] $r7
mulu $r7 $r8
mov $r5 0x810
@ -494,10 +496,10 @@ cmd_exec_set_format:
iowr I[$r5 + 0x000] $r6
iowr I[$r5 + 0x100] $r7
add b32 $r5 0x800
ld b32 $r6 D[$r0 + ctx_dst_cpp]
ld b32 $r6 D[$r0 + #ctx_dst_cpp]
sub b32 $r6 1
shl b32 $r6 8
ld b32 $r7 D[$r0 + ctx_src_cpp]
ld b32 $r7 D[$r0 + #ctx_src_cpp]
sub b32 $r7 1
or $r6 $r7
iowr I[$r5 + 0x000] $r6
@ -511,9 +513,9 @@ cmd_exec_set_format:
ld b32 $r6 D[$sp + 0x0c]
iowr I[$r5 + 0x300] $r6
add b32 $r5 0x400
ld b32 $r6 D[$r0 + ctx_swz_const0]
ld b32 $r6 D[$r0 + #ctx_swz_const0]
iowr I[$r5 + 0x000] $r6
ld b32 $r6 D[$r0 + ctx_swz_const1]
ld b32 $r6 D[$r0 + #ctx_swz_const1]
iowr I[$r5 + 0x100] $r6
add $sp 0x10
ret
@ -543,7 +545,7 @@ cmd_exec_set_format:
//
cmd_exec_set_surface_tiled:
// translate TILE_MODE into Tp, Th, Td shift values
ld b32 $r7 D[$r5 + ctx_src_tile_mode]
ld b32 $r7 D[$r5 + #ctx_src_tile_mode]
extr $r9 $r7 8:11
extr $r8 $r7 4:7
ifdef(`NVA3',
@ -553,9 +555,9 @@ ifdef(`NVA3',
)
extr $r7 $r7 0:3
cmp b32 $r7 0xe
bra ne xtile64
bra ne #xtile64
mov $r7 4
bra xtileok
bra #xtileok
xtile64:
xbit $r7 $flags $p2
add b32 $r7 17
@ -565,8 +567,8 @@ ifdef(`NVA3',
// Op = (x * cpp) & ((1 << Tp) - 1)
// Tx = (x * cpp) >> Tp
ld b32 $r10 D[$r5 + ctx_src_xoff]
ld b32 $r11 D[$r5 + ctx_src_cpp]
ld b32 $r10 D[$r5 + #ctx_src_xoff]
ld b32 $r11 D[$r5 + #ctx_src_cpp]
mulu $r10 $r11
mov $r11 1
shl b32 $r11 $r7
@ -576,7 +578,7 @@ ifdef(`NVA3',
// Tyo = y & ((1 << Th) - 1)
// Ty = y >> Th
ld b32 $r13 D[$r5 + ctx_src_yoff]
ld b32 $r13 D[$r5 + #ctx_src_yoff]
mov $r14 1
shl b32 $r14 $r8
sub b32 $r14 1
@ -598,8 +600,8 @@ ifdef(`NVA3',
add b32 $r12 $r11
// nTx = ((w * cpp) + ((1 << Tp) - 1) >> Tp)
ld b32 $r15 D[$r5 + ctx_src_xsize]
ld b32 $r11 D[$r5 + ctx_src_cpp]
ld b32 $r15 D[$r5 + #ctx_src_xsize]
ld b32 $r11 D[$r5 + #ctx_src_cpp]
mulu $r15 $r11
mov $r11 1
shl b32 $r11 $r7
@ -609,7 +611,7 @@ ifdef(`NVA3',
push $r15
// nTy = (h + ((1 << Th) - 1)) >> Th
ld b32 $r15 D[$r5 + ctx_src_ysize]
ld b32 $r15 D[$r5 + #ctx_src_ysize]
mov $r11 1
shl b32 $r11 $r8
sub b32 $r11 1
@ -629,7 +631,7 @@ ifdef(`NVA3',
// Tz = z >> Td
// Op += Tzo << Tys
// Ts = Tys + Td
ld b32 $r8 D[$r5 + ctx_src_zoff]
ld b32 $r8 D[$r5 + #ctx_src_zoff]
mov $r14 1
shl b32 $r14 $r9
sub b32 $r14 1
@ -656,8 +658,8 @@ ifdef(`NVA3',
// SRC_ADDRESS_LOW = (Ot + Op) & 0xffffffff
// CFG_ADDRESS_HIGH |= ((Ot + Op) >> 32) << 16
ld b32 $r7 D[$r5 + ctx_src_address_low]
ld b32 $r8 D[$r5 + ctx_src_address_high]
ld b32 $r7 D[$r5 + #ctx_src_address_low]
ld b32 $r8 D[$r5 + #ctx_src_address_high]
add b32 $r10 $r12
add b32 $r7 $r10
adc b32 $r8 0
@ -677,14 +679,14 @@ cmd_exec_set_surface_linear:
xbit $r6 $flags $p2
add b32 $r6 0x202
shl b32 $r6 8
ld b32 $r7 D[$r5 + ctx_src_address_low]
ld b32 $r7 D[$r5 + #ctx_src_address_low]
iowr I[$r6 + 0x000] $r7
add b32 $r6 0x400
ld b32 $r7 D[$r5 + ctx_src_address_high]
ld b32 $r7 D[$r5 + #ctx_src_address_high]
shl b32 $r7 16
iowr I[$r6 + 0x000] $r7
add b32 $r6 0x400
ld b32 $r7 D[$r5 + ctx_src_pitch]
ld b32 $r7 D[$r5 + #ctx_src_pitch]
iowr I[$r6 + 0x000] $r7
ret
@ -697,7 +699,7 @@ cmd_exec_wait:
loop:
iord $r1 I[$r0]
and $r1 1
bra ne loop
bra ne #loop
pop $r1
pop $r0
ret
@ -705,18 +707,18 @@ cmd_exec_wait:
cmd_exec_query:
// if QUERY_SHORT not set, write out { -, 0, TIME_LO, TIME_HI }
xbit $r4 $r3 13
bra ne query_counter
call cmd_exec_wait
bra ne #query_counter
call #cmd_exec_wait
mov $r4 0x80c
shl b32 $r4 6
ld b32 $r5 D[$r0 + ctx_query_address_low]
ld b32 $r5 D[$r0 + #ctx_query_address_low]
add b32 $r5 4
iowr I[$r4 + 0x000] $r5
iowr I[$r4 + 0x100] $r0
mov $r5 0xc
iowr I[$r4 + 0x200] $r5
add b32 $r4 0x400
ld b32 $r5 D[$r0 + ctx_query_address_high]
ld b32 $r5 D[$r0 + #ctx_query_address_high]
shl b32 $r5 16
iowr I[$r4 + 0x000] $r5
add b32 $r4 0x500
@ -741,16 +743,16 @@ cmd_exec_query:
// write COUNTER
query_counter:
call cmd_exec_wait
call #cmd_exec_wait
mov $r4 0x80c
shl b32 $r4 6
ld b32 $r5 D[$r0 + ctx_query_address_low]
ld b32 $r5 D[$r0 + #ctx_query_address_low]
iowr I[$r4 + 0x000] $r5
iowr I[$r4 + 0x100] $r0
mov $r5 0x4
iowr I[$r4 + 0x200] $r5
add b32 $r4 0x400
ld b32 $r5 D[$r0 + ctx_query_address_high]
ld b32 $r5 D[$r0 + #ctx_query_address_high]
shl b32 $r5 16
iowr I[$r4 + 0x000] $r5
add b32 $r4 0x500
@ -759,7 +761,7 @@ cmd_exec_query:
mov $r5 0x00001110
sethi $r5 0x13120000
iowr I[$r4 + 0x100] $r5
ld b32 $r5 D[$r0 + ctx_query_counter]
ld b32 $r5 D[$r0 + #ctx_query_counter]
add b32 $r4 0x500
iowr I[$r4 + 0x000] $r5
mov $r5 0x00002601
@ -787,22 +789,22 @@ cmd_exec_query:
// $r2: hostirq state
// $r3: data
cmd_exec:
call cmd_exec_wait
call #cmd_exec_wait
// if format requested, call function to calculate it, otherwise
// fill in cpp/xcnt for both surfaces as if (cpp == 1)
xbit $r15 $r3 0
bra e cmd_exec_no_format
call cmd_exec_set_format
bra e #cmd_exec_no_format
call #cmd_exec_set_format
mov $r4 0x200
bra cmd_exec_init_src_surface
bra #cmd_exec_init_src_surface
cmd_exec_no_format:
mov $r6 0x810
shl b32 $r6 6
mov $r7 1
st b32 D[$r0 + ctx_src_cpp] $r7
st b32 D[$r0 + ctx_dst_cpp] $r7
ld b32 $r7 D[$r0 + ctx_xcnt]
st b32 D[$r0 + #ctx_src_cpp] $r7
st b32 D[$r0 + #ctx_dst_cpp] $r7
ld b32 $r7 D[$r0 + #ctx_xcnt]
iowr I[$r6 + 0x000] $r7
iowr I[$r6 + 0x100] $r7
clear b32 $r4
@ -811,28 +813,28 @@ cmd_exec:
bclr $flags $p2
clear b32 $r5
xbit $r15 $r3 4
bra e src_tiled
call cmd_exec_set_surface_linear
bra cmd_exec_init_dst_surface
bra e #src_tiled
call #cmd_exec_set_surface_linear
bra #cmd_exec_init_dst_surface
src_tiled:
call cmd_exec_set_surface_tiled
call #cmd_exec_set_surface_tiled
bset $r4 7
cmd_exec_init_dst_surface:
bset $flags $p2
mov $r5 ctx_dst_address_high - ctx_src_address_high
mov $r5 #ctx_dst_address_high - #ctx_src_address_high
xbit $r15 $r3 8
bra e dst_tiled
call cmd_exec_set_surface_linear
bra cmd_exec_kick
bra e #dst_tiled
call #cmd_exec_set_surface_linear
bra #cmd_exec_kick
dst_tiled:
call cmd_exec_set_surface_tiled
call #cmd_exec_set_surface_tiled
bset $r4 8
cmd_exec_kick:
mov $r5 0x800
shl b32 $r5 6
ld b32 $r6 D[$r0 + ctx_ycnt]
ld b32 $r6 D[$r0 + #ctx_ycnt]
iowr I[$r5 + 0x100] $r6
mov $r6 0x0041
// SRC_TARGET = 1, DST_TARGET = 2
@ -842,8 +844,8 @@ cmd_exec:
// if requested, queue up a QUERY write after the copy has completed
xbit $r15 $r3 12
bra e cmd_exec_done
call cmd_exec_query
bra e #cmd_exec_done
call #cmd_exec_query
cmd_exec_done:
ret

View File

@ -152,7 +152,7 @@ uint32_t nva3_pcopy_code[] = {
0xf10010fe,
0xf1040017,
0xf0fff327,
0x22d00023,
0x12d00023,
0x0c25f0c0,
0xf40012d0,
0x17f11031,

View File

@ -287,12 +287,13 @@ nva3_pm_grcp_idle(void *data)
return false;
}
void
int
nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nva3_pm_state *info = pre_state;
unsigned long flags;
int ret = -EAGAIN;
/* prevent any new grctx switches from starting */
spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
@ -328,6 +329,8 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)
nv_wr32(dev, 0x100210, 0x80000000);
}
ret = 0;
cleanup:
/* unfreeze PFIFO */
nv_mask(dev, 0x002504, 0x00000001, 0x00000000);
@ -339,4 +342,5 @@ nva3_pm_clocks_set(struct drm_device *dev, void *pre_state)
nv_mask(dev, 0x400824, 0x10000000, 0x10000000);
spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
kfree(info);
return ret;
}

View File

@ -145,7 +145,7 @@ uint32_t nvc0_pcopy_code[] = {
0xf10010fe,
0xf1040017,
0xf0fff327,
0x22d00023,
0x12d00023,
0x0c25f0c0,
0xf40012d0,
0x17f11031,

View File

@ -875,14 +875,16 @@ nvc0_graph_create(struct drm_device *dev)
case 0xcf: /* 4/0/0/0, 3 */
priv->magic_not_rop_nr = 0x03;
break;
case 0xd9: /* 1/0/0/0, 1 */
priv->magic_not_rop_nr = 0x01;
break;
}
if (!priv->magic_not_rop_nr) {
NV_ERROR(dev, "PGRAPH: unknown config: %d/%d/%d/%d, %d\n",
priv->tp_nr[0], priv->tp_nr[1], priv->tp_nr[2],
priv->tp_nr[3], priv->rop_nr);
/* use 0xc3's values... */
priv->magic_not_rop_nr = 0x03;
priv->magic_not_rop_nr = 0x00;
}
NVOBJ_CLASS(dev, 0x902d, GR); /* 2D */

View File

@ -71,9 +71,9 @@ queue_put:
ld b32 $r9 D[$r13 + 0x4] // PUT
xor $r8 8
cmpu b32 $r8 $r9
bra ne queue_put_next
bra ne #queue_put_next
mov $r15 E_CMD_OVERFLOW
call error
call #error
ret
// store cmd/data on queue
@ -104,7 +104,7 @@ queue_get:
ld b32 $r8 D[$r13 + 0x0] // GET
ld b32 $r9 D[$r13 + 0x4] // PUT
cmpu b32 $r8 $r9
bra e queue_get_done
bra e #queue_get_done
// fetch first cmd/data pair
and $r9 $r8 7
shl b32 $r9 3
@ -135,9 +135,9 @@ nv_rd32:
nv_rd32_wait:
iord $r12 I[$r11 + 0x000]
xbit $r12 $r12 31
bra ne nv_rd32_wait
bra ne #nv_rd32_wait
mov $r10 6 // DONE_MMIO_RD
call wait_doneo
call #wait_doneo
iord $r15 I[$r11 + 0x100] // MMIO_RDVAL
ret
@ -157,7 +157,7 @@ nv_wr32:
nv_wr32_wait:
iord $r12 I[$r11 + 0x000]
xbit $r12 $r12 31
bra ne nv_wr32_wait
bra ne #nv_wr32_wait
ret
// (re)set watchdog timer
@ -193,7 +193,7 @@ $1:
shl b32 $r8 6
iord $r8 I[$r8 + 0x000] // DONE
xbit $r8 $r8 $r10
bra $2 wait_done_$1
bra $2 #wait_done_$1
trace_clr(T_WAIT)
ret
')
@ -216,7 +216,7 @@ mmctx_size:
add b32 $r9 $r8
add b32 $r14 4
cmpu b32 $r14 $r15
bra ne nv_mmctx_size_loop
bra ne #nv_mmctx_size_loop
mov b32 $r15 $r9
ret
@ -238,12 +238,12 @@ mmctx_xfer:
shl b32 $r8 6
clear b32 $r9
or $r11 $r11
bra e mmctx_base_disabled
bra e #mmctx_base_disabled
iowr I[$r8 + 0x000] $r11 // MMCTX_BASE
bset $r9 0 // BASE_EN
mmctx_base_disabled:
or $r14 $r14
bra e mmctx_multi_disabled
bra e #mmctx_multi_disabled
iowr I[$r8 + 0x200] $r14 // MMCTX_MULTI_STRIDE
iowr I[$r8 + 0x300] $r15 // MMCTX_MULTI_MASK
bset $r9 1 // MULTI_EN
@ -264,7 +264,7 @@ mmctx_xfer:
mmctx_wait_free:
iord $r14 I[$r8 + 0x000] // MMCTX_CTRL
and $r14 0x1f
bra e mmctx_wait_free
bra e #mmctx_wait_free
// queue up an entry
ld b32 $r14 D[$r12]
@ -272,19 +272,19 @@ mmctx_xfer:
iowr I[$r8 + 0x300] $r14
add b32 $r12 4
cmpu b32 $r12 $r13
bra ne mmctx_exec_loop
bra ne #mmctx_exec_loop
xbit $r11 $r10 2
bra ne mmctx_stop
bra ne #mmctx_stop
// wait for queue to empty
mmctx_fini_wait:
iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
and $r11 0x1f
cmpu b32 $r11 0x10
bra ne mmctx_fini_wait
bra ne #mmctx_fini_wait
mov $r10 2 // DONE_MMCTX
call wait_donez
bra mmctx_done
call #wait_donez
bra #mmctx_done
mmctx_stop:
xbit $r11 $r10 0
shl b32 $r11 16 // DIR
@ -295,7 +295,7 @@ mmctx_xfer:
// wait for STOP_TRIGGER to clear
iord $r11 I[$r8 + 0x000] // MMCTX_CTRL
xbit $r11 $r11 18
bra ne mmctx_stop_wait
bra ne #mmctx_stop_wait
mmctx_done:
trace_clr(T_MMCTX)
ret
@ -305,7 +305,7 @@ mmctx_xfer:
strand_wait:
push $r10
mov $r10 2
call wait_donez
call #wait_donez
pop $r10
ret
@ -316,7 +316,7 @@ strand_pre:
sethi $r8 0x20000
mov $r9 0xc
iowr I[$r8] $r9
call strand_wait
call #strand_wait
ret
// unknown - call after issuing strand commands
@ -326,7 +326,7 @@ strand_post:
sethi $r8 0x20000
mov $r9 0xd
iowr I[$r8] $r9
call strand_wait
call #strand_wait
ret
// Selects strand set?!
@ -341,11 +341,11 @@ strand_set:
iowr I[$r10 + 0x000] $r12 // 0x93c = 0xf
mov $r12 0xb
iowr I[$r11 + 0x000] $r12 // 0x928 = 0xb
call strand_wait
call #strand_wait
iowr I[$r10 + 0x000] $r14 // 0x93c = <id>
mov $r12 0xa
iowr I[$r11 + 0x000] $r12 // 0x928 = 0xa
call strand_wait
call #strand_wait
ret
// Initialise strand context data
@ -357,22 +357,22 @@ strand_set:
//
strand_ctx_init:
trace_set(T_STRINIT)
call strand_pre
call #strand_pre
mov $r14 3
call strand_set
call #strand_set
mov $r10 0x46fc
sethi $r10 0x20000
add b32 $r11 $r10 0x400
iowr I[$r10 + 0x100] $r0 // STRAND_FIRST_GENE = 0
mov $r12 1
iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_FIRST_GENE
call strand_wait
call #strand_wait
sub b32 $r12 $r0 1
iowr I[$r10 + 0x000] $r12 // STRAND_GENE_CNT = 0xffffffff
mov $r12 2
iowr I[$r11 + 0x000] $r12 // STRAND_CMD = LATCH_GENE_CNT
call strand_wait
call strand_post
call #strand_wait
call #strand_post
// read the size of each strand, poke the context offset of
// each into STRAND_{SAVE,LOAD}_SWBASE now, no need to worry
@ -391,7 +391,7 @@ strand_ctx_init:
add b32 $r14 $r10
add b32 $r8 4
sub b32 $r9 1
bra ne ctx_init_strand_loop
bra ne #ctx_init_strand_loop
shl b32 $r14 8
sub b32 $r15 $r14 $r15

View File

@ -87,6 +87,7 @@ nvc0_graph_class(struct drm_device *dev)
case 0xc1:
return 0x9197;
case 0xc8:
case 0xd9:
return 0x9297;
default:
return 0;

View File

@ -1268,6 +1268,17 @@ nvc0_grctx_generate_9039(struct drm_device *dev)
static void
nvc0_grctx_generate_90c0(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
int i;
for (i = 0; dev_priv->chipset == 0xd9 && i < 4; i++) {
nv_mthd(dev, 0x90c0, 0x2700 + (i * 0x40), 0x00000000);
nv_mthd(dev, 0x90c0, 0x2720 + (i * 0x40), 0x00000000);
nv_mthd(dev, 0x90c0, 0x2704 + (i * 0x40), 0x00000000);
nv_mthd(dev, 0x90c0, 0x2724 + (i * 0x40), 0x00000000);
nv_mthd(dev, 0x90c0, 0x2708 + (i * 0x40), 0x00000000);
nv_mthd(dev, 0x90c0, 0x2728 + (i * 0x40), 0x00000000);
}
nv_mthd(dev, 0x90c0, 0x270c, 0x00000000);
nv_mthd(dev, 0x90c0, 0x272c, 0x00000000);
nv_mthd(dev, 0x90c0, 0x274c, 0x00000000);
@ -1276,6 +1287,12 @@ nvc0_grctx_generate_90c0(struct drm_device *dev)
nv_mthd(dev, 0x90c0, 0x27ac, 0x00000000);
nv_mthd(dev, 0x90c0, 0x27cc, 0x00000000);
nv_mthd(dev, 0x90c0, 0x27ec, 0x00000000);
for (i = 0; dev_priv->chipset == 0xd9 && i < 4; i++) {
nv_mthd(dev, 0x90c0, 0x2710 + (i * 0x40), 0x00014000);
nv_mthd(dev, 0x90c0, 0x2730 + (i * 0x40), 0x00014000);
nv_mthd(dev, 0x90c0, 0x2714 + (i * 0x40), 0x00000040);
nv_mthd(dev, 0x90c0, 0x2734 + (i * 0x40), 0x00000040);
}
nv_mthd(dev, 0x90c0, 0x030c, 0x00000001);
nv_mthd(dev, 0x90c0, 0x1944, 0x00000000);
nv_mthd(dev, 0x90c0, 0x0758, 0x00000100);
@ -1471,14 +1488,20 @@ nvc0_grctx_generate_shaders(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
if (dev_priv->chipset != 0xc1) {
nv_wr32(dev, 0x405800, 0x078000bf);
nv_wr32(dev, 0x405830, 0x02180000);
} else {
if (dev_priv->chipset == 0xd9) {
nv_wr32(dev, 0x405800, 0x0f8000bf);
nv_wr32(dev, 0x405830, 0x02180218);
nv_wr32(dev, 0x405834, 0x08000000);
} else
if (dev_priv->chipset == 0xc1) {
nv_wr32(dev, 0x405800, 0x0f8000bf);
nv_wr32(dev, 0x405830, 0x02180218);
nv_wr32(dev, 0x405834, 0x00000000);
} else {
nv_wr32(dev, 0x405800, 0x078000bf);
nv_wr32(dev, 0x405830, 0x02180000);
nv_wr32(dev, 0x405834, 0x00000000);
}
nv_wr32(dev, 0x405834, 0x00000000);
nv_wr32(dev, 0x405838, 0x00000000);
nv_wr32(dev, 0x405854, 0x00000000);
nv_wr32(dev, 0x405870, 0x00000001);
@ -1509,7 +1532,10 @@ nvc0_grctx_generate_unk64xx(struct drm_device *dev)
nv_wr32(dev, 0x4064ac, 0x00003fff);
nv_wr32(dev, 0x4064b4, 0x00000000);
nv_wr32(dev, 0x4064b8, 0x00000000);
if (dev_priv->chipset == 0xc1) {
if (dev_priv->chipset == 0xd9)
nv_wr32(dev, 0x4064bc, 0x00000000);
if (dev_priv->chipset == 0xc1 ||
dev_priv->chipset == 0xd9) {
nv_wr32(dev, 0x4064c0, 0x80140078);
nv_wr32(dev, 0x4064c4, 0x0086ffff);
}
@ -1550,10 +1576,23 @@ nvc0_grctx_generate_rop(struct drm_device *dev)
/* ROPC_BROADCAST */
nv_wr32(dev, 0x408800, 0x02802a3c);
nv_wr32(dev, 0x408804, 0x00000040);
nv_wr32(dev, 0x408808, chipset != 0xc1 ? 0x0003e00d : 0x1003e005);
nv_wr32(dev, 0x408900, 0x3080b801);
nv_wr32(dev, 0x408904, chipset != 0xc1 ? 0x02000001 : 0x62000001);
nv_wr32(dev, 0x408908, 0x00c80929);
if (chipset == 0xd9) {
nv_wr32(dev, 0x408808, 0x1043e005);
nv_wr32(dev, 0x408900, 0x3080b801);
nv_wr32(dev, 0x408904, 0x1043e005);
nv_wr32(dev, 0x408908, 0x00c8102f);
} else
if (chipset == 0xc1) {
nv_wr32(dev, 0x408808, 0x1003e005);
nv_wr32(dev, 0x408900, 0x3080b801);
nv_wr32(dev, 0x408904, 0x62000001);
nv_wr32(dev, 0x408908, 0x00c80929);
} else {
nv_wr32(dev, 0x408808, 0x0003e00d);
nv_wr32(dev, 0x408900, 0x3080b801);
nv_wr32(dev, 0x408904, 0x02000001);
nv_wr32(dev, 0x408908, 0x00c80929);
}
nv_wr32(dev, 0x40890c, 0x00000000);
nv_wr32(dev, 0x408980, 0x0000011d);
}
@ -1572,7 +1611,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x418408, 0x00000000);
nv_wr32(dev, 0x41840c, 0x00001008);
nv_wr32(dev, 0x418410, 0x0fff0fff);
nv_wr32(dev, 0x418414, 0x00200fff);
nv_wr32(dev, 0x418414, chipset != 0xd9 ? 0x00200fff : 0x02200fff);
nv_wr32(dev, 0x418450, 0x00000000);
nv_wr32(dev, 0x418454, 0x00000000);
nv_wr32(dev, 0x418458, 0x00000000);
@ -1587,14 +1626,17 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x418700, 0x00000002);
nv_wr32(dev, 0x418704, 0x00000080);
nv_wr32(dev, 0x418708, 0x00000000);
nv_wr32(dev, 0x41870c, 0x07c80000);
nv_wr32(dev, 0x41870c, chipset != 0xd9 ? 0x07c80000 : 0x00000000);
nv_wr32(dev, 0x418710, 0x00000000);
nv_wr32(dev, 0x418800, 0x0006860a);
nv_wr32(dev, 0x418800, chipset != 0xd9 ? 0x0006860a : 0x7006860a);
nv_wr32(dev, 0x418808, 0x00000000);
nv_wr32(dev, 0x41880c, 0x00000000);
nv_wr32(dev, 0x418810, 0x00000000);
nv_wr32(dev, 0x418828, 0x00008442);
nv_wr32(dev, 0x418830, chipset != 0xc1 ? 0x00000001 : 0x10000001);
if (chipset == 0xc1 || chipset == 0xd9)
nv_wr32(dev, 0x418830, 0x10000001);
else
nv_wr32(dev, 0x418830, 0x00000001);
nv_wr32(dev, 0x4188d8, 0x00000008);
nv_wr32(dev, 0x4188e0, 0x01000000);
nv_wr32(dev, 0x4188e8, 0x00000000);
@ -1602,7 +1644,12 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x4188f0, 0x00000000);
nv_wr32(dev, 0x4188f4, 0x00000000);
nv_wr32(dev, 0x4188f8, 0x00000000);
nv_wr32(dev, 0x4188fc, chipset != 0xc1 ? 0x00100000 : 0x00100018);
if (chipset == 0xd9)
nv_wr32(dev, 0x4188fc, 0x20100008);
else if (chipset == 0xc1)
nv_wr32(dev, 0x4188fc, 0x00100018);
else
nv_wr32(dev, 0x4188fc, 0x00100000);
nv_wr32(dev, 0x41891c, 0x00ff00ff);
nv_wr32(dev, 0x418924, 0x00000000);
nv_wr32(dev, 0x418928, 0x00ffff00);
@ -1616,7 +1663,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x418a14 + (i * 0x20), 0x00000000);
nv_wr32(dev, 0x418a18 + (i * 0x20), 0x00000000);
}
nv_wr32(dev, 0x418b00, 0x00000000);
nv_wr32(dev, 0x418b00, chipset != 0xd9 ? 0x00000000 : 0x00000006);
nv_wr32(dev, 0x418b08, 0x0a418820);
nv_wr32(dev, 0x418b0c, 0x062080e6);
nv_wr32(dev, 0x418b10, 0x020398a4);
@ -1633,7 +1680,7 @@ nvc0_grctx_generate_gpc(struct drm_device *dev)
nv_wr32(dev, 0x418c24, 0x00000000);
nv_wr32(dev, 0x418c28, 0x00000000);
nv_wr32(dev, 0x418c2c, 0x00000000);
if (chipset == 0xc1)
if (chipset == 0xc1 || chipset == 0xd9)
nv_wr32(dev, 0x418c6c, 0x00000001);
nv_wr32(dev, 0x418c80, 0x20200004);
nv_wr32(dev, 0x418c8c, 0x00000001);
@ -1653,7 +1700,10 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419818, 0x00000000);
nv_wr32(dev, 0x41983c, 0x00038bc7);
nv_wr32(dev, 0x419848, 0x00000000);
nv_wr32(dev, 0x419864, chipset != 0xc1 ? 0x0000012a : 0x00000129);
if (chipset == 0xc1 || chipset == 0xd9)
nv_wr32(dev, 0x419864, 0x00000129);
else
nv_wr32(dev, 0x419864, 0x0000012a);
nv_wr32(dev, 0x419888, 0x00000000);
nv_wr32(dev, 0x419a00, 0x000001f0);
nv_wr32(dev, 0x419a04, 0x00000001);
@ -1663,7 +1713,9 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419a14, 0x00000200);
nv_wr32(dev, 0x419a1c, 0x00000000);
nv_wr32(dev, 0x419a20, 0x00000800);
if (chipset != 0xc0 && chipset != 0xc8)
if (chipset == 0xd9)
nv_wr32(dev, 0x00419ac4, 0x0017f440);
else if (chipset != 0xc0 && chipset != 0xc8)
nv_wr32(dev, 0x00419ac4, 0x0007f440);
nv_wr32(dev, 0x419b00, 0x0a418820);
nv_wr32(dev, 0x419b04, 0x062080e6);
@ -1672,21 +1724,33 @@ nvc0_grctx_generate_tp(struct drm_device *dev)
nv_wr32(dev, 0x419b10, 0x0a418820);
nv_wr32(dev, 0x419b14, 0x000000e6);
nv_wr32(dev, 0x419bd0, 0x00900103);
nv_wr32(dev, 0x419be0, chipset != 0xc1 ? 0x00000001 : 0x00400001);
if (chipset == 0xc1 || chipset == 0xd9)
nv_wr32(dev, 0x419be0, 0x00400001);
else
nv_wr32(dev, 0x419be0, 0x00000001);
nv_wr32(dev, 0x419be4, 0x00000000);
nv_wr32(dev, 0x419c00, 0x00000002);
nv_wr32(dev, 0x419c00, chipset != 0xd9 ? 0x00000002 : 0x0000000a);
nv_wr32(dev, 0x419c04, 0x00000006);
nv_wr32(dev, 0x419c08, 0x00000002);
nv_wr32(dev, 0x419c20, 0x00000000);
if (chipset == 0xce || chipset == 0xcf)
if (dev_priv->chipset == 0xd9) {
nv_wr32(dev, 0x419c24, 0x00084210);
nv_wr32(dev, 0x419c28, 0x3cf3cf3c);
nv_wr32(dev, 0x419cb0, 0x00020048);
else
} else
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);
if (chipset == 0xc1 || chipset == 0xd9)
nv_wr32(dev, 0x419d20, 0x12180000);
else
nv_wr32(dev, 0x419d20, 0x02180000);
nv_wr32(dev, 0x419d24, 0x00001fff);
if (chipset == 0xc1)
if (chipset == 0xc1 || chipset == 0xd9)
nv_wr32(dev, 0x419d44, 0x02180218);
nv_wr32(dev, 0x419e04, 0x00000000);
nv_wr32(dev, 0x419e08, 0x00000000);
@ -1986,6 +2050,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_icmd(dev, 0x00000215, 0x00000040);
nv_icmd(dev, 0x00000216, 0x00000040);
nv_icmd(dev, 0x00000217, 0x00000040);
if (dev_priv->chipset == 0xd9) {
for (i = 0x0400; i <= 0x0417; i++)
nv_icmd(dev, i, 0x00000040);
}
nv_icmd(dev, 0x00000218, 0x0000c080);
nv_icmd(dev, 0x00000219, 0x0000c080);
nv_icmd(dev, 0x0000021a, 0x0000c080);
@ -1994,6 +2062,10 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_icmd(dev, 0x0000021d, 0x0000c080);
nv_icmd(dev, 0x0000021e, 0x0000c080);
nv_icmd(dev, 0x0000021f, 0x0000c080);
if (dev_priv->chipset == 0xd9) {
for (i = 0x0440; i <= 0x0457; i++)
nv_icmd(dev, i, 0x0000c080);
}
nv_icmd(dev, 0x000000ad, 0x0000013e);
nv_icmd(dev, 0x000000e1, 0x00000010);
nv_icmd(dev, 0x00000290, 0x00000000);
@ -2556,7 +2628,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_icmd(dev, 0x0000053f, 0xffff0000);
nv_icmd(dev, 0x00000585, 0x0000003f);
nv_icmd(dev, 0x00000576, 0x00000003);
if (dev_priv->chipset == 0xc1)
if (dev_priv->chipset == 0xc1 ||
dev_priv->chipset == 0xd9)
nv_icmd(dev, 0x0000057b, 0x00000059);
nv_icmd(dev, 0x00000586, 0x00000040);
nv_icmd(dev, 0x00000582, 0x00000080);
@ -2658,6 +2731,8 @@ nvc0_grctx_generate(struct nouveau_channel *chan)
nv_icmd(dev, 0x00000957, 0x00000003);
nv_icmd(dev, 0x0000095e, 0x20164010);
nv_icmd(dev, 0x0000095f, 0x00000020);
if (dev_priv->chipset == 0xd9)
nv_icmd(dev, 0x0000097d, 0x00000020);
nv_icmd(dev, 0x00000683, 0x00000006);
nv_icmd(dev, 0x00000685, 0x003fffff);
nv_icmd(dev, 0x00000687, 0x00000c48);

View File

@ -32,7 +32,7 @@
* - watchdog timer around ctx operations
*/
.section nvc0_grgpc_data
.section #nvc0_grgpc_data
include(`nvc0_graph.fuc')
gpc_id: .b32 0
gpc_mmio_list_head: .b32 0
@ -48,40 +48,45 @@ cmd_queue: queue_init
// chipset descriptions
chipsets:
.b8 0xc0 0 0 0
.b16 nvc0_gpc_mmio_head
.b16 nvc0_gpc_mmio_tail
.b16 nvc0_tpc_mmio_head
.b16 nvc0_tpc_mmio_tail
.b16 #nvc0_gpc_mmio_head
.b16 #nvc0_gpc_mmio_tail
.b16 #nvc0_tpc_mmio_head
.b16 #nvc0_tpc_mmio_tail
.b8 0xc1 0 0 0
.b16 nvc0_gpc_mmio_head
.b16 nvc1_gpc_mmio_tail
.b16 nvc0_tpc_mmio_head
.b16 nvc1_tpc_mmio_tail
.b16 #nvc0_gpc_mmio_head
.b16 #nvc1_gpc_mmio_tail
.b16 #nvc0_tpc_mmio_head
.b16 #nvc1_tpc_mmio_tail
.b8 0xc3 0 0 0
.b16 nvc0_gpc_mmio_head
.b16 nvc0_gpc_mmio_tail
.b16 nvc0_tpc_mmio_head
.b16 nvc3_tpc_mmio_tail
.b16 #nvc0_gpc_mmio_head
.b16 #nvc0_gpc_mmio_tail
.b16 #nvc0_tpc_mmio_head
.b16 #nvc3_tpc_mmio_tail
.b8 0xc4 0 0 0
.b16 nvc0_gpc_mmio_head
.b16 nvc0_gpc_mmio_tail
.b16 nvc0_tpc_mmio_head
.b16 nvc3_tpc_mmio_tail
.b16 #nvc0_gpc_mmio_head
.b16 #nvc0_gpc_mmio_tail
.b16 #nvc0_tpc_mmio_head
.b16 #nvc3_tpc_mmio_tail
.b8 0xc8 0 0 0
.b16 nvc0_gpc_mmio_head
.b16 nvc0_gpc_mmio_tail
.b16 nvc0_tpc_mmio_head
.b16 nvc0_tpc_mmio_tail
.b16 #nvc0_gpc_mmio_head
.b16 #nvc0_gpc_mmio_tail
.b16 #nvc0_tpc_mmio_head
.b16 #nvc0_tpc_mmio_tail
.b8 0xce 0 0 0
.b16 nvc0_gpc_mmio_head
.b16 nvc0_gpc_mmio_tail
.b16 nvc0_tpc_mmio_head
.b16 nvc3_tpc_mmio_tail
.b16 #nvc0_gpc_mmio_head
.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
.b16 #nvc0_gpc_mmio_head
.b16 #nvc0_gpc_mmio_tail
.b16 #nvc0_tpc_mmio_head
.b16 #nvcf_tpc_mmio_tail
.b8 0xd9 0 0 0
.b16 #nvd9_gpc_mmio_head
.b16 #nvd9_gpc_mmio_tail
.b16 #nvd9_tpc_mmio_head
.b16 #nvd9_tpc_mmio_tail
.b8 0 0 0 0
// GPC mmio lists
@ -114,6 +119,35 @@ nvc0_gpc_mmio_tail:
mmctx_data(0x000c6c, 1);
nvc1_gpc_mmio_tail:
nvd9_gpc_mmio_head:
mmctx_data(0x000380, 1)
mmctx_data(0x000400, 2)
mmctx_data(0x00040c, 3)
mmctx_data(0x000450, 9)
mmctx_data(0x000600, 1)
mmctx_data(0x000684, 1)
mmctx_data(0x000700, 5)
mmctx_data(0x000800, 1)
mmctx_data(0x000808, 3)
mmctx_data(0x000828, 1)
mmctx_data(0x000830, 1)
mmctx_data(0x0008d8, 1)
mmctx_data(0x0008e0, 1)
mmctx_data(0x0008e8, 6)
mmctx_data(0x00091c, 1)
mmctx_data(0x000924, 3)
mmctx_data(0x000b00, 1)
mmctx_data(0x000b08, 6)
mmctx_data(0x000bb8, 1)
mmctx_data(0x000c08, 1)
mmctx_data(0x000c10, 8)
mmctx_data(0x000c6c, 1)
mmctx_data(0x000c80, 1)
mmctx_data(0x000c8c, 1)
mmctx_data(0x001000, 3)
mmctx_data(0x001014, 1)
nvd9_gpc_mmio_tail:
// TPC mmio lists
nvc0_tpc_mmio_head:
mmctx_data(0x000018, 1)
@ -146,9 +180,34 @@ nvc3_tpc_mmio_tail:
mmctx_data(0x000544, 1)
nvc1_tpc_mmio_tail:
nvd9_tpc_mmio_head:
mmctx_data(0x000018, 1)
mmctx_data(0x00003c, 1)
mmctx_data(0x000048, 1)
mmctx_data(0x000064, 1)
mmctx_data(0x000088, 1)
mmctx_data(0x000200, 6)
mmctx_data(0x00021c, 2)
mmctx_data(0x0002c4, 1)
mmctx_data(0x000300, 6)
mmctx_data(0x0003d0, 1)
mmctx_data(0x0003e0, 2)
mmctx_data(0x000400, 3)
mmctx_data(0x000420, 3)
mmctx_data(0x0004b0, 1)
mmctx_data(0x0004e8, 1)
mmctx_data(0x0004f4, 1)
mmctx_data(0x000520, 2)
mmctx_data(0x000544, 1)
mmctx_data(0x000604, 4)
mmctx_data(0x000644, 20)
mmctx_data(0x000698, 1)
mmctx_data(0x0006e0, 1)
mmctx_data(0x000750, 3)
nvd9_tpc_mmio_tail:
.section nvc0_grgpc_code
bra init
.section #nvc0_grgpc_code
bra #init
define(`include_code')
include(`nvc0_graph.fuc')
@ -160,10 +219,10 @@ error:
push $r14
mov $r14 -0x67ec // 0x9814
sethi $r14 0x400000
call nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code
call #nv_wr32 // HUB_CTXCTL_CC_SCRATCH[5] = error code
add b32 $r14 0x41c
mov $r15 1
call nv_wr32 // HUB_CTXCTL_INTR_UP_SET
call #nv_wr32 // HUB_CTXCTL_INTR_UP_SET
pop $r14
ret
@ -190,7 +249,7 @@ init:
iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
// setup i0 handler, and route all interrupts to it
mov $r1 ih
mov $r1 #ih
mov $iv0 $r1
mov $r1 0x400
iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
@ -210,24 +269,24 @@ init:
and $r2 0x1f
shl b32 $r3 $r2
sub b32 $r3 1
st b32 D[$r0 + tpc_count] $r2
st b32 D[$r0 + tpc_mask] $r3
st b32 D[$r0 + #tpc_count] $r2
st b32 D[$r0 + #tpc_mask] $r3
add b32 $r1 0x400
iord $r2 I[$r1 + 0x000] // MYINDEX
st b32 D[$r0 + gpc_id] $r2
st b32 D[$r0 + #gpc_id] $r2
// find context data for this chipset
mov $r2 0x800
shl b32 $r2 6
iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0]
mov $r1 chipsets - 12
mov $r1 #chipsets - 12
init_find_chipset:
add b32 $r1 12
ld b32 $r3 D[$r1 + 0x00]
cmpu b32 $r3 $r2
bra e init_context
bra e #init_context
cmpu b32 $r3 0
bra ne init_find_chipset
bra ne #init_find_chipset
// unknown chipset
ret
@ -253,19 +312,19 @@ init:
clear b32 $r15
ld b16 $r14 D[$r1 + 4]
ld b16 $r15 D[$r1 + 6]
st b16 D[$r0 + gpc_mmio_list_head] $r14
st b16 D[$r0 + gpc_mmio_list_tail] $r15
call mmctx_size
st b16 D[$r0 + #gpc_mmio_list_head] $r14
st b16 D[$r0 + #gpc_mmio_list_tail] $r15
call #mmctx_size
add b32 $r2 $r15
add b32 $r3 $r15
// calculate per-TPC mmio context size, store the list pointers
ld b16 $r14 D[$r1 + 8]
ld b16 $r15 D[$r1 + 10]
st b16 D[$r0 + tpc_mmio_list_head] $r14
st b16 D[$r0 + tpc_mmio_list_tail] $r15
call mmctx_size
ld b32 $r14 D[$r0 + tpc_count]
st b16 D[$r0 + #tpc_mmio_list_head] $r14
st b16 D[$r0 + #tpc_mmio_list_tail] $r15
call #mmctx_size
ld b32 $r14 D[$r0 + #tpc_count]
mulu $r14 $r15
add b32 $r2 $r14
add b32 $r3 $r14
@ -283,7 +342,7 @@ init:
// calculate size of strand context data
mov b32 $r15 $r2
call strand_ctx_init
call #strand_ctx_init
add b32 $r3 $r15
// save context size, and tell HUB we're done
@ -301,13 +360,13 @@ init:
main:
bset $flags $p0
sleep $p0
mov $r13 cmd_queue
call queue_get
bra $p1 main
mov $r13 #cmd_queue
call #queue_get
bra $p1 #main
// 0x0000-0x0003 are all context transfers
cmpu b32 $r14 0x04
bra nc main_not_ctx_xfer
bra nc #main_not_ctx_xfer
// fetch $flags and mask off $p1/$p2
mov $r1 $flags
mov $r2 0x0006
@ -318,14 +377,14 @@ main:
or $r1 $r14
mov $flags $r1
// transfer context data
call ctx_xfer
bra main
call #ctx_xfer
bra #main
main_not_ctx_xfer:
shl b32 $r15 $r14 16
or $r15 E_BAD_COMMAND
call error
bra main
call #error
bra #main
// interrupt handler
ih:
@ -342,13 +401,13 @@ ih:
// incoming fifo command?
iord $r10 I[$r0 + 0x200] // INTR
and $r11 $r10 0x00000004
bra e ih_no_fifo
bra e #ih_no_fifo
// queue incoming fifo command for later processing
mov $r11 0x1900
mov $r13 cmd_queue
mov $r13 #cmd_queue
iord $r14 I[$r11 + 0x100] // FIFO_CMD
iord $r15 I[$r11 + 0x000] // FIFO_DATA
call queue_put
call #queue_put
add b32 $r11 0x400
mov $r14 1
iowr I[$r11 + 0x000] $r14 // FIFO_ACK
@ -374,11 +433,11 @@ ih:
//
hub_barrier_done:
mov $r15 1
ld b32 $r14 D[$r0 + gpc_id]
ld b32 $r14 D[$r0 + #gpc_id]
shl b32 $r15 $r14
mov $r14 -0x6be8 // 0x409418 - HUB_BAR_SET
sethi $r14 0x400000
call nv_wr32
call #nv_wr32
ret
// Disables various things, waits a bit, and re-enables them..
@ -395,7 +454,7 @@ ctx_redswitch:
mov $r15 8
ctx_redswitch_delay:
sub b32 $r15 1
bra ne ctx_redswitch_delay
bra ne #ctx_redswitch_delay
mov $r15 0xa20
iowr I[$r14] $r15 // GPC_RED_SWITCH = UNK11, ENABLE, POWER
ret
@ -413,8 +472,8 @@ ctx_xfer:
mov $r1 0xa04
shl b32 $r1 6
iowr I[$r1 + 0x000] $r15// MEM_BASE
bra not $p1 ctx_xfer_not_load
call ctx_redswitch
bra not $p1 #ctx_xfer_not_load
call #ctx_redswitch
ctx_xfer_not_load:
// strands
@ -422,7 +481,7 @@ ctx_xfer:
sethi $r1 0x20000
mov $r2 0xc
iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c
call strand_wait
call #strand_wait
mov $r2 0x47fc
sethi $r2 0x20000
iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00
@ -435,46 +494,46 @@ ctx_xfer:
or $r10 2 // first
mov $r11 0x0000
sethi $r11 0x500000
ld b32 $r12 D[$r0 + gpc_id]
ld b32 $r12 D[$r0 + #gpc_id]
shl b32 $r12 15
add b32 $r11 $r12 // base = NV_PGRAPH_GPCn
ld b32 $r12 D[$r0 + gpc_mmio_list_head]
ld b32 $r13 D[$r0 + gpc_mmio_list_tail]
ld b32 $r12 D[$r0 + #gpc_mmio_list_head]
ld b32 $r13 D[$r0 + #gpc_mmio_list_tail]
mov $r14 0 // not multi
call mmctx_xfer
call #mmctx_xfer
// per-TPC mmio context
xbit $r10 $flags $p1 // direction
or $r10 4 // last
mov $r11 0x4000
sethi $r11 0x500000 // base = NV_PGRAPH_GPC0_TPC0
ld b32 $r12 D[$r0 + gpc_id]
ld b32 $r12 D[$r0 + #gpc_id]
shl b32 $r12 15
add b32 $r11 $r12 // base = NV_PGRAPH_GPCn_TPC0
ld b32 $r12 D[$r0 + tpc_mmio_list_head]
ld b32 $r13 D[$r0 + tpc_mmio_list_tail]
ld b32 $r15 D[$r0 + tpc_mask]
ld b32 $r12 D[$r0 + #tpc_mmio_list_head]
ld b32 $r13 D[$r0 + #tpc_mmio_list_tail]
ld b32 $r15 D[$r0 + #tpc_mask]
mov $r14 0x800 // stride = 0x800
call mmctx_xfer
call #mmctx_xfer
// wait for strands to finish
call strand_wait
call #strand_wait
// if load, or a save without a load following, do some
// unknown stuff that's done after finishing a block of
// strand commands
bra $p1 ctx_xfer_post
bra not $p2 ctx_xfer_done
bra $p1 #ctx_xfer_post
bra not $p2 #ctx_xfer_done
ctx_xfer_post:
mov $r1 0x4afc
sethi $r1 0x20000
mov $r2 0xd
iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0d
call strand_wait
call #strand_wait
// mark completion in HUB's barrier
ctx_xfer_done:
call hub_barrier_done
call #hub_barrier_done
ret
.align 256

View File

@ -25,26 +25,29 @@ uint32_t nvc0_grgpc_data[] = {
0x00000000,
0x00000000,
0x000000c0,
0x011c00bc,
0x01700120,
0x012800c8,
0x01e40194,
0x000000c1,
0x012000bc,
0x01840120,
0x012c00c8,
0x01f80194,
0x000000c3,
0x011c00bc,
0x01800120,
0x012800c8,
0x01f40194,
0x000000c4,
0x011c00bc,
0x01800120,
0x012800c8,
0x01f40194,
0x000000c8,
0x011c00bc,
0x01700120,
0x012800c8,
0x01e40194,
0x000000ce,
0x011c00bc,
0x01800120,
0x012800c8,
0x01f40194,
0x000000cf,
0x011c00bc,
0x017c0120,
0x012800c8,
0x01f00194,
0x000000d9,
0x0194012c,
0x025401f8,
0x00000000,
0x00000380,
0x14000400,
@ -71,6 +74,32 @@ uint32_t nvc0_grgpc_data[] = {
0x08001000,
0x00001014,
0x00000c6c,
0x00000380,
0x04000400,
0x0800040c,
0x20000450,
0x00000600,
0x00000684,
0x10000700,
0x00000800,
0x08000808,
0x00000828,
0x00000830,
0x000008d8,
0x000008e0,
0x140008e8,
0x0000091c,
0x08000924,
0x00000b00,
0x14000b08,
0x00000bb8,
0x00000c08,
0x1c000c10,
0x00000c6c,
0x00000c80,
0x00000c8c,
0x08001000,
0x00001014,
0x00000018,
0x0000003c,
0x00000048,
@ -96,6 +125,29 @@ uint32_t nvc0_grgpc_data[] = {
0x000006e0,
0x000004bc,
0x00000544,
0x00000018,
0x0000003c,
0x00000048,
0x00000064,
0x00000088,
0x14000200,
0x0400021c,
0x000002c4,
0x14000300,
0x000003d0,
0x040003e0,
0x08000400,
0x08000420,
0x000004b0,
0x000004e8,
0x000004f4,
0x04000520,
0x00000544,
0x0c000604,
0x4c000644,
0x00000698,
0x000006e0,
0x08000750,
};
uint32_t nvc0_grgpc_code[] = {

View File

@ -27,7 +27,7 @@
* m4 nvc0_grhub.fuc | envyas -a -w -m fuc -V nva3 -o nvc0_grhub.fuc.h
*/
.section nvc0_grhub_data
.section #nvc0_grhub_data
include(`nvc0_graph.fuc')
gpc_count: .b32 0
rop_count: .b32 0
@ -39,26 +39,29 @@ ctx_current: .b32 0
chipsets:
.b8 0xc0 0 0 0
.b16 nvc0_hub_mmio_head
.b16 nvc0_hub_mmio_tail
.b16 #nvc0_hub_mmio_head
.b16 #nvc0_hub_mmio_tail
.b8 0xc1 0 0 0
.b16 nvc0_hub_mmio_head
.b16 nvc1_hub_mmio_tail
.b16 #nvc0_hub_mmio_head
.b16 #nvc1_hub_mmio_tail
.b8 0xc3 0 0 0
.b16 nvc0_hub_mmio_head
.b16 nvc0_hub_mmio_tail
.b16 #nvc0_hub_mmio_head
.b16 #nvc0_hub_mmio_tail
.b8 0xc4 0 0 0
.b16 nvc0_hub_mmio_head
.b16 nvc0_hub_mmio_tail
.b16 #nvc0_hub_mmio_head
.b16 #nvc0_hub_mmio_tail
.b8 0xc8 0 0 0
.b16 nvc0_hub_mmio_head
.b16 nvc0_hub_mmio_tail
.b16 #nvc0_hub_mmio_head
.b16 #nvc0_hub_mmio_tail
.b8 0xce 0 0 0
.b16 nvc0_hub_mmio_head
.b16 nvc0_hub_mmio_tail
.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
.b16 #nvc0_hub_mmio_head
.b16 #nvc0_hub_mmio_tail
.b8 0xd9 0 0 0
.b16 #nvd9_hub_mmio_head
.b16 #nvd9_hub_mmio_tail
.b8 0 0 0 0
nvc0_hub_mmio_head:
@ -105,6 +108,48 @@ nvc0_hub_mmio_tail:
mmctx_data(0x4064c0, 2)
nvc1_hub_mmio_tail:
nvd9_hub_mmio_head:
mmctx_data(0x17e91c, 2)
mmctx_data(0x400204, 2)
mmctx_data(0x404004, 10)
mmctx_data(0x404044, 1)
mmctx_data(0x404094, 14)
mmctx_data(0x4040d0, 7)
mmctx_data(0x4040f8, 1)
mmctx_data(0x404130, 3)
mmctx_data(0x404150, 3)
mmctx_data(0x404164, 2)
mmctx_data(0x404178, 2)
mmctx_data(0x404200, 8)
mmctx_data(0x404404, 14)
mmctx_data(0x404460, 4)
mmctx_data(0x404480, 1)
mmctx_data(0x404498, 1)
mmctx_data(0x404604, 4)
mmctx_data(0x404618, 32)
mmctx_data(0x404698, 21)
mmctx_data(0x4046f0, 2)
mmctx_data(0x404700, 22)
mmctx_data(0x405800, 1)
mmctx_data(0x405830, 3)
mmctx_data(0x405854, 1)
mmctx_data(0x405870, 4)
mmctx_data(0x405a00, 2)
mmctx_data(0x405a18, 1)
mmctx_data(0x406020, 1)
mmctx_data(0x406028, 4)
mmctx_data(0x4064a8, 2)
mmctx_data(0x4064b4, 5)
mmctx_data(0x407804, 1)
mmctx_data(0x40780c, 6)
mmctx_data(0x4078bc, 1)
mmctx_data(0x408000, 7)
mmctx_data(0x408064, 1)
mmctx_data(0x408800, 3)
mmctx_data(0x408900, 4)
mmctx_data(0x408980, 1)
nvd9_hub_mmio_tail:
.align 256
chan_data:
chan_mmio_count: .b32 0
@ -113,8 +158,8 @@ chan_mmio_address: .b32 0
.align 256
xfer_data: .b32 0
.section nvc0_grhub_code
bra init
.section #nvc0_grhub_code
bra #init
define(`include_code')
include(`nvc0_graph.fuc')
@ -157,7 +202,7 @@ init:
iowr I[$r1 + 0x000] $r2 // FIFO_ENABLE
// setup i0 handler, and route all interrupts to it
mov $r1 ih
mov $r1 #ih
mov $iv0 $r1
mov $r1 0x400
iowr I[$r1 + 0x300] $r0 // INTR_DISPATCH
@ -201,11 +246,11 @@ init:
// fetch enabled GPC/ROP counts
mov $r14 -0x69fc // 0x409604
sethi $r14 0x400000
call nv_rd32
call #nv_rd32
extr $r1 $r15 16:20
st b32 D[$r0 + rop_count] $r1
st b32 D[$r0 + #rop_count] $r1
and $r15 0x1f
st b32 D[$r0 + gpc_count] $r15
st b32 D[$r0 + #gpc_count] $r15
// set BAR_REQMASK to GPC mask
mov $r1 1
@ -220,14 +265,14 @@ init:
mov $r2 0x800
shl b32 $r2 6
iord $r2 I[$r2 + 0x000] // CC_SCRATCH[0]
mov $r15 chipsets - 8
mov $r15 #chipsets - 8
init_find_chipset:
add b32 $r15 8
ld b32 $r3 D[$r15 + 0x00]
cmpu b32 $r3 $r2
bra e init_context
bra e #init_context
cmpu b32 $r3 0
bra ne init_find_chipset
bra ne #init_find_chipset
// unknown chipset
ret
@ -239,9 +284,9 @@ init:
ld b16 $r14 D[$r15 + 4]
ld b16 $r15 D[$r15 + 6]
sethi $r14 0
st b32 D[$r0 + hub_mmio_list_head] $r14
st b32 D[$r0 + hub_mmio_list_tail] $r15
call mmctx_size
st b32 D[$r0 + #hub_mmio_list_head] $r14
st b32 D[$r0 + #hub_mmio_list_tail] $r15
call #mmctx_size
// set mmctx base addresses now so we don't have to do it later,
// they don't (currently) ever change
@ -260,7 +305,7 @@ init:
add b32 $r1 1
shl b32 $r1 8
mov b32 $r15 $r1
call strand_ctx_init
call #strand_ctx_init
add b32 $r1 $r15
// initialise each GPC in sequence by passing in the offset of its
@ -271,40 +316,40 @@ init:
// when it has completed, and return the size of its context data
// in GPCn_CC_SCRATCH[1]
//
ld b32 $r3 D[$r0 + gpc_count]
ld b32 $r3 D[$r0 + #gpc_count]
mov $r4 0x2000
sethi $r4 0x500000
init_gpc:
// setup, and start GPC ucode running
add b32 $r14 $r4 0x804
mov b32 $r15 $r1
call nv_wr32 // CC_SCRATCH[1] = ctx offset
call #nv_wr32 // CC_SCRATCH[1] = ctx offset
add b32 $r14 $r4 0x800
mov b32 $r15 $r2
call nv_wr32 // CC_SCRATCH[0] = chipset
call #nv_wr32 // CC_SCRATCH[0] = chipset
add b32 $r14 $r4 0x10c
clear b32 $r15
call nv_wr32
call #nv_wr32
add b32 $r14 $r4 0x104
call nv_wr32 // ENTRY
call #nv_wr32 // ENTRY
add b32 $r14 $r4 0x100
mov $r15 2 // CTRL_START_TRIGGER
call nv_wr32 // CTRL
call #nv_wr32 // CTRL
// wait for it to complete, and adjust context size
add b32 $r14 $r4 0x800
init_gpc_wait:
call nv_rd32
call #nv_rd32
xbit $r15 $r15 31
bra e init_gpc_wait
bra e #init_gpc_wait
add b32 $r14 $r4 0x804
call nv_rd32
call #nv_rd32
add b32 $r1 $r15
// next!
add b32 $r4 0x8000
sub b32 $r3 1
bra ne init_gpc
bra ne #init_gpc
// save context size, and tell host we're ready
mov $r2 0x800
@ -322,13 +367,13 @@ main:
// sleep until we have something to do
bset $flags $p0
sleep $p0
mov $r13 cmd_queue
call queue_get
bra $p1 main
mov $r13 #cmd_queue
call #queue_get
bra $p1 #main
// context switch, requested by GPU?
cmpu b32 $r14 0x4001
bra ne main_not_ctx_switch
bra ne #main_not_ctx_switch
trace_set(T_AUTO)
mov $r1 0xb00
shl b32 $r1 6
@ -336,39 +381,39 @@ main:
iord $r1 I[$r1 + 0x000] // CHAN_CUR
xbit $r3 $r1 31
bra e chsw_no_prev
bra e #chsw_no_prev
xbit $r3 $r2 31
bra e chsw_prev_no_next
bra e #chsw_prev_no_next
push $r2
mov b32 $r2 $r1
trace_set(T_SAVE)
bclr $flags $p1
bset $flags $p2
call ctx_xfer
call #ctx_xfer
trace_clr(T_SAVE);
pop $r2
trace_set(T_LOAD);
bset $flags $p1
call ctx_xfer
call #ctx_xfer
trace_clr(T_LOAD);
bra chsw_done
bra #chsw_done
chsw_prev_no_next:
push $r2
mov b32 $r2 $r1
bclr $flags $p1
bclr $flags $p2
call ctx_xfer
call #ctx_xfer
pop $r2
mov $r1 0xb00
shl b32 $r1 6
iowr I[$r1] $r2
bra chsw_done
bra #chsw_done
chsw_no_prev:
xbit $r3 $r2 31
bra e chsw_done
bra e #chsw_done
bset $flags $p1
bclr $flags $p2
call ctx_xfer
call #ctx_xfer
// ack the context switch request
chsw_done:
@ -377,32 +422,32 @@ main:
mov $r2 1
iowr I[$r1 + 0x000] $r2 // 0x409b0c
trace_clr(T_AUTO)
bra main
bra #main
// request to set current channel? (*not* a context switch)
main_not_ctx_switch:
cmpu b32 $r14 0x0001
bra ne main_not_ctx_chan
bra ne #main_not_ctx_chan
mov b32 $r2 $r15
call ctx_chan
bra main_done
call #ctx_chan
bra #main_done
// request to store current channel context?
main_not_ctx_chan:
cmpu b32 $r14 0x0002
bra ne main_not_ctx_save
bra ne #main_not_ctx_save
trace_set(T_SAVE)
bclr $flags $p1
bclr $flags $p2
call ctx_xfer
call #ctx_xfer
trace_clr(T_SAVE)
bra main_done
bra #main_done
main_not_ctx_save:
shl b32 $r15 $r14 16
or $r15 E_BAD_COMMAND
call error
bra main
call #error
bra #main
main_done:
mov $r1 0x820
@ -410,7 +455,7 @@ main:
clear b32 $r2
bset $r2 31
iowr I[$r1 + 0x000] $r2 // CC_SCRATCH[0] |= 0x80000000
bra main
bra #main
// interrupt handler
ih:
@ -427,13 +472,13 @@ ih:
// incoming fifo command?
iord $r10 I[$r0 + 0x200] // INTR
and $r11 $r10 0x00000004
bra e ih_no_fifo
bra e #ih_no_fifo
// queue incoming fifo command for later processing
mov $r11 0x1900
mov $r13 cmd_queue
mov $r13 #cmd_queue
iord $r14 I[$r11 + 0x100] // FIFO_CMD
iord $r15 I[$r11 + 0x000] // FIFO_DATA
call queue_put
call #queue_put
add b32 $r11 0x400
mov $r14 1
iowr I[$r11 + 0x000] $r14 // FIFO_ACK
@ -441,18 +486,18 @@ ih:
// context switch request?
ih_no_fifo:
and $r11 $r10 0x00000100
bra e ih_no_ctxsw
bra e #ih_no_ctxsw
// enqueue a context switch for later processing
mov $r13 cmd_queue
mov $r13 #cmd_queue
mov $r14 0x4001
call queue_put
call #queue_put
// anything we didn't handle, bring it to the host's attention
ih_no_ctxsw:
mov $r11 0x104
not b32 $r11
and $r11 $r10 $r11
bra e ih_no_other
bra e #ih_no_other
mov $r10 0xc1c
shl b32 $r10 6
iowr I[$r10] $r11 // INTR_UP_SET
@ -478,11 +523,11 @@ ctx_4160s:
mov $r14 0x4160
sethi $r14 0x400000
mov $r15 1
call nv_wr32
call #nv_wr32
ctx_4160s_wait:
call nv_rd32
call #nv_rd32
xbit $r15 $r15 4
bra e ctx_4160s_wait
bra e #ctx_4160s_wait
ret
// Without clearing again at end of xfer, some things cause PGRAPH
@ -492,7 +537,7 @@ ctx_4160c:
mov $r14 0x4160
sethi $r14 0x400000
clear b32 $r15
call nv_wr32
call #nv_wr32
ret
// Again, not real sure
@ -503,7 +548,7 @@ ctx_4170s:
mov $r14 0x4170
sethi $r14 0x400000
or $r15 0x10
call nv_wr32
call #nv_wr32
ret
// Waits for a ctx_4170s() call to complete
@ -511,9 +556,9 @@ ctx_4170s:
ctx_4170w:
mov $r14 0x4170
sethi $r14 0x400000
call nv_rd32
call #nv_rd32
and $r15 0x10
bra ne ctx_4170w
bra ne #ctx_4170w
ret
// Disables various things, waits a bit, and re-enables them..
@ -530,7 +575,7 @@ ctx_redswitch:
mov $r15 8
ctx_redswitch_delay:
sub b32 $r15 1
bra ne ctx_redswitch_delay
bra ne #ctx_redswitch_delay
mov $r15 0x770
iowr I[$r14] $r15 // HUB_RED_SWITCH = ENABLE_ALL, POWER_ALL
ret
@ -546,10 +591,10 @@ ctx_86c:
iowr I[$r14] $r15 // HUB(0x86c) = val
mov $r14 -0x75ec
sethi $r14 0x400000
call nv_wr32 // ROP(0xa14) = val
call #nv_wr32 // ROP(0xa14) = val
mov $r14 -0x5794
sethi $r14 0x410000
call nv_wr32 // GPC(0x86c) = val
call #nv_wr32 // GPC(0x86c) = val
ret
// ctx_load - load's a channel's ctxctl data, and selects its vm
@ -561,7 +606,7 @@ ctx_load:
// switch to channel, somewhat magic in parts..
mov $r10 12 // DONE_UNK12
call wait_donez
call #wait_donez
mov $r1 0xa24
shl b32 $r1 6
iowr I[$r1 + 0x000] $r0 // 0x409a24
@ -576,7 +621,7 @@ ctx_load:
ctx_chan_wait_0:
iord $r4 I[$r1 + 0x100]
and $r4 0x1f
bra ne ctx_chan_wait_0
bra ne #ctx_chan_wait_0
iowr I[$r3 + 0x000] $r2 // CHAN_CUR
// load channel header, fetch PGRAPH context pointer
@ -595,19 +640,19 @@ ctx_load:
sethi $r2 0x80000000
iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vram
mov $r1 0x10 // chan + 0x0210
mov $r2 xfer_data
mov $r2 #xfer_data
sethi $r2 0x00020000 // 16 bytes
xdld $r1 $r2
xdwait
trace_clr(T_LCHAN)
// update current context
ld b32 $r1 D[$r0 + xfer_data + 4]
ld b32 $r1 D[$r0 + #xfer_data + 4]
shl b32 $r1 24
ld b32 $r2 D[$r0 + xfer_data + 0]
ld b32 $r2 D[$r0 + #xfer_data + 0]
shr b32 $r2 8
or $r1 $r2
st b32 D[$r0 + ctx_current] $r1
st b32 D[$r0 + #ctx_current] $r1
// set transfer base to start of context, and fetch context header
trace_set(T_LCTXH)
@ -618,7 +663,7 @@ ctx_load:
mov $r1 0xa20
shl b32 $r1 6
iowr I[$r1 + 0x000] $r2 // MEM_TARGET = vm
mov $r1 chan_data
mov $r1 #chan_data
sethi $r1 0x00060000 // 256 bytes
xdld $r0 $r1
xdwait
@ -635,10 +680,10 @@ ctx_load:
// In: $r2 channel address
//
ctx_chan:
call ctx_4160s
call ctx_load
call #ctx_4160s
call #ctx_load
mov $r10 12 // DONE_UNK12
call wait_donez
call #wait_donez
mov $r1 0xa10
shl b32 $r1 6
mov $r2 5
@ -646,8 +691,8 @@ ctx_chan:
ctx_chan_wait:
iord $r2 I[$r1 + 0x000]
or $r2 $r2
bra ne ctx_chan_wait
call ctx_4160c
bra ne #ctx_chan_wait
call #ctx_4160c
ret
// Execute per-context state overrides list
@ -661,7 +706,7 @@ ctx_chan:
//
ctx_mmio_exec:
// set transfer base to be the mmio list
ld b32 $r3 D[$r0 + chan_mmio_address]
ld b32 $r3 D[$r0 + #chan_mmio_address]
mov $r2 0xa04
shl b32 $r2 6
iowr I[$r2 + 0x000] $r3 // MEM_BASE
@ -670,31 +715,31 @@ ctx_mmio_exec:
ctx_mmio_loop:
// fetch next 256 bytes of mmio list if necessary
and $r4 $r3 0xff
bra ne ctx_mmio_pull
mov $r5 xfer_data
bra ne #ctx_mmio_pull
mov $r5 #xfer_data
sethi $r5 0x00060000 // 256 bytes
xdld $r3 $r5
xdwait
// execute a single list entry
ctx_mmio_pull:
ld b32 $r14 D[$r4 + xfer_data + 0x00]
ld b32 $r15 D[$r4 + xfer_data + 0x04]
call nv_wr32
ld b32 $r14 D[$r4 + #xfer_data + 0x00]
ld b32 $r15 D[$r4 + #xfer_data + 0x04]
call #nv_wr32
// next!
add b32 $r3 8
sub b32 $r1 1
bra ne ctx_mmio_loop
bra ne #ctx_mmio_loop
// set transfer base back to the current context
ctx_mmio_done:
ld b32 $r3 D[$r0 + ctx_current]
ld b32 $r3 D[$r0 + #ctx_current]
iowr I[$r2 + 0x000] $r3 // MEM_BASE
// disable the mmio list now, we don't need/want to execute it again
st b32 D[$r0 + chan_mmio_count] $r0
mov $r1 chan_data
st b32 D[$r0 + #chan_mmio_count] $r0
mov $r1 #chan_data
sethi $r1 0x00060000 // 256 bytes
xdst $r0 $r1
xdwait
@ -709,46 +754,46 @@ ctx_mmio_exec:
// on load it means: "a save preceeded this load"
//
ctx_xfer:
bra not $p1 ctx_xfer_pre
bra $p2 ctx_xfer_pre_load
bra not $p1 #ctx_xfer_pre
bra $p2 #ctx_xfer_pre_load
ctx_xfer_pre:
mov $r15 0x10
call ctx_86c
call ctx_4160s
bra not $p1 ctx_xfer_exec
call #ctx_86c
call #ctx_4160s
bra not $p1 #ctx_xfer_exec
ctx_xfer_pre_load:
mov $r15 2
call ctx_4170s
call ctx_4170w
call ctx_redswitch
call #ctx_4170s
call #ctx_4170w
call #ctx_redswitch
clear b32 $r15
call ctx_4170s
call ctx_load
call #ctx_4170s
call #ctx_load
// fetch context pointer, and initiate xfer on all GPCs
ctx_xfer_exec:
ld b32 $r1 D[$r0 + ctx_current]
ld b32 $r1 D[$r0 + #ctx_current]
mov $r2 0x414
shl b32 $r2 6
iowr I[$r2 + 0x000] $r0 // BAR_STATUS = reset
mov $r14 -0x5b00
sethi $r14 0x410000
mov b32 $r15 $r1
call nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer
call #nv_wr32 // GPC_BCAST_WRCMD_DATA = ctx pointer
add b32 $r14 4
xbit $r15 $flags $p1
xbit $r2 $flags $p2
shl b32 $r2 1
or $r15 $r2
call nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
call #nv_wr32 // GPC_BCAST_WRCMD_CMD = GPC_XFER(type)
// strands
mov $r1 0x4afc
sethi $r1 0x20000
mov $r2 0xc
iowr I[$r1] $r2 // STRAND_CMD(0x3f) = 0x0c
call strand_wait
call #strand_wait
mov $r2 0x47fc
sethi $r2 0x20000
iowr I[$r2] $r0 // STRAND_FIRST_GENE(0x3f) = 0x00
@ -760,22 +805,22 @@ ctx_xfer:
xbit $r10 $flags $p1 // direction
or $r10 6 // first, last
mov $r11 0 // base = 0
ld b32 $r12 D[$r0 + hub_mmio_list_head]
ld b32 $r13 D[$r0 + hub_mmio_list_tail]
ld b32 $r12 D[$r0 + #hub_mmio_list_head]
ld b32 $r13 D[$r0 + #hub_mmio_list_tail]
mov $r14 0 // not multi
call mmctx_xfer
call #mmctx_xfer
// wait for GPCs to all complete
mov $r10 8 // DONE_BAR
call wait_doneo
call #wait_doneo
// wait for strand xfer to complete
call strand_wait
call #strand_wait
// post-op
bra $p1 ctx_xfer_post
bra $p1 #ctx_xfer_post
mov $r10 12 // DONE_UNK12
call wait_donez
call #wait_donez
mov $r1 0xa10
shl b32 $r1 6
mov $r2 5
@ -783,27 +828,27 @@ ctx_xfer:
ctx_xfer_post_save_wait:
iord $r2 I[$r1]
or $r2 $r2
bra ne ctx_xfer_post_save_wait
bra ne #ctx_xfer_post_save_wait
bra $p2 ctx_xfer_done
bra $p2 #ctx_xfer_done
ctx_xfer_post:
mov $r15 2
call ctx_4170s
call #ctx_4170s
clear b32 $r15
call ctx_86c
call strand_post
call ctx_4170w
call #ctx_86c
call #strand_post
call #ctx_4170w
clear b32 $r15
call ctx_4170s
call #ctx_4170s
bra not $p1 ctx_xfer_no_post_mmio
ld b32 $r1 D[$r0 + chan_mmio_count]
bra not $p1 #ctx_xfer_no_post_mmio
ld b32 $r1 D[$r0 + #chan_mmio_count]
or $r1 $r1
bra e ctx_xfer_no_post_mmio
call ctx_mmio_exec
bra e #ctx_xfer_no_post_mmio
call #ctx_mmio_exec
ctx_xfer_no_post_mmio:
call ctx_4160c
call #ctx_4160c
ctx_xfer_done:
ret

View File

@ -23,19 +23,21 @@ uint32_t nvc0_grhub_data[] = {
0x00000000,
0x00000000,
0x000000c0,
0x01340098,
0x013c00a0,
0x000000c1,
0x01380098,
0x014000a0,
0x000000c3,
0x01340098,
0x013c00a0,
0x000000c4,
0x01340098,
0x013c00a0,
0x000000c8,
0x01340098,
0x013c00a0,
0x000000ce,
0x01340098,
0x013c00a0,
0x000000cf,
0x01340098,
0x013c00a0,
0x000000d9,
0x01dc0140,
0x00000000,
0x0417e91c,
0x04400204,
@ -77,47 +79,45 @@ uint32_t nvc0_grhub_data[] = {
0x0c408900,
0x00408980,
0x044064c0,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x0417e91c,
0x04400204,
0x24404004,
0x00404044,
0x34404094,
0x184040d0,
0x004040f8,
0x08404130,
0x08404150,
0x04404164,
0x04404178,
0x1c404200,
0x34404404,
0x0c404460,
0x00404480,
0x00404498,
0x0c404604,
0x7c404618,
0x50404698,
0x044046f0,
0x54404700,
0x00405800,
0x08405830,
0x00405854,
0x0c405870,
0x04405a00,
0x00405a18,
0x00406020,
0x0c406028,
0x044064a8,
0x104064b4,
0x00407804,
0x1440780c,
0x004078bc,
0x18408000,
0x00408064,
0x08408800,
0x0c408900,
0x00408980,
0x00000000,
0x00000000,
0x00000000,

View File

@ -153,3 +153,240 @@ nvc0_pm_clocks_get(struct drm_device *dev, struct nouveau_pm_level *perflvl)
perflvl->vdec = read_clk(dev, 0x0e);
return 0;
}
struct nvc0_pm_clock {
u32 freq;
u32 ssel;
u32 mdiv;
u32 dsrc;
u32 ddiv;
u32 coef;
};
struct nvc0_pm_state {
struct nvc0_pm_clock eng[16];
};
static u32
calc_div(struct drm_device *dev, int clk, u32 ref, u32 freq, u32 *ddiv)
{
u32 div = min((ref * 2) / freq, (u32)65);
if (div < 2)
div = 2;
*ddiv = div - 2;
return (ref * 2) / div;
}
static u32
calc_src(struct drm_device *dev, int clk, u32 freq, u32 *dsrc, u32 *ddiv)
{
u32 sclk;
/* use one of the fixed frequencies if possible */
*ddiv = 0x00000000;
switch (freq) {
case 27000:
case 108000:
*dsrc = 0x00000000;
if (freq == 108000)
*dsrc |= 0x00030000;
return freq;
case 100000:
*dsrc = 0x00000002;
return freq;
default:
*dsrc = 0x00000003;
break;
}
/* otherwise, calculate the closest divider */
sclk = read_vco(dev, clk);
if (clk < 7)
sclk = calc_div(dev, clk, sclk, freq, ddiv);
return sclk;
}
static u32
calc_pll(struct drm_device *dev, int clk, u32 freq, u32 *coef)
{
struct pll_lims limits;
int N, M, P, ret;
ret = get_pll_limits(dev, 0x137000 + (clk * 0x20), &limits);
if (ret)
return 0;
limits.refclk = read_div(dev, clk, 0x137120, 0x137140);
if (!limits.refclk)
return 0;
ret = nva3_calc_pll(dev, &limits, freq, &N, NULL, &M, &P);
if (ret <= 0)
return 0;
*coef = (P << 16) | (N << 8) | M;
return ret;
}
/* A (likely rather simplified and incomplete) view of the clock tree
*
* Key:
*
* S: source select
* D: divider
* P: pll
* F: switch
*
* Engine clocks:
*
* 137250(D) ---- 137100(F0) ---- 137160(S)/1371d0(D) ------------------- ref
* (F1) ---- 1370X0(P) ---- 137120(S)/137140(D) ---- ref
*
* Not all registers exist for all clocks. For example: clocks >= 8 don't
* have their own PLL (all tied to clock 7's PLL when in PLL mode), nor do
* they have the divider at 1371d0, though the source selection at 137160
* still exists. You must use the divider at 137250 for these instead.
*
* Memory clock:
*
* TBD, read_mem() above is likely very wrong...
*
*/
static int
calc_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info, u32 freq)
{
u32 src0, div0, div1D, div1P = 0;
u32 clk0, clk1 = 0;
/* invalid clock domain */
if (!freq)
return 0;
/* first possible path, using only dividers */
clk0 = calc_src(dev, clk, freq, &src0, &div0);
clk0 = calc_div(dev, clk, clk0, freq, &div1D);
/* see if we can get any closer using PLLs */
if (clk0 != freq) {
if (clk < 7)
clk1 = calc_pll(dev, clk, freq, &info->coef);
else
clk1 = read_pll(dev, 0x1370e0);
clk1 = calc_div(dev, clk, clk1, freq, &div1P);
}
/* select the method which gets closest to target freq */
if (abs((int)freq - clk0) <= abs((int)freq - clk1)) {
info->dsrc = src0;
if (div0) {
info->ddiv |= 0x80000000;
info->ddiv |= div0 << 8;
info->ddiv |= div0;
}
if (div1D) {
info->mdiv |= 0x80000000;
info->mdiv |= div1D;
}
info->ssel = 0;
info->freq = clk0;
} else {
if (div1P) {
info->mdiv |= 0x80000000;
info->mdiv |= div1P << 8;
}
info->ssel = (1 << clk);
info->freq = clk1;
}
return 0;
}
void *
nvc0_pm_clocks_pre(struct drm_device *dev, struct nouveau_pm_level *perflvl)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvc0_pm_state *info;
int ret;
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return ERR_PTR(-ENOMEM);
/* NFI why this is still in the performance table, the ROPCs appear
* to get their clock from clock 2 ("hub07", actually hub05 on this
* chip, but, anyway...) as well. nvatiming confirms hub05 and ROP
* are always the same freq with the binary driver even when the
* performance table says they should differ.
*/
if (dev_priv->chipset == 0xd9)
perflvl->rop = 0;
if ((ret = calc_clk(dev, 0x00, &info->eng[0x00], perflvl->shader)) ||
(ret = calc_clk(dev, 0x01, &info->eng[0x01], perflvl->rop)) ||
(ret = calc_clk(dev, 0x02, &info->eng[0x02], perflvl->hub07)) ||
(ret = calc_clk(dev, 0x07, &info->eng[0x07], perflvl->hub06)) ||
(ret = calc_clk(dev, 0x08, &info->eng[0x08], perflvl->hub01)) ||
(ret = calc_clk(dev, 0x09, &info->eng[0x09], perflvl->copy)) ||
(ret = calc_clk(dev, 0x0c, &info->eng[0x0c], perflvl->daemon)) ||
(ret = calc_clk(dev, 0x0e, &info->eng[0x0e], perflvl->vdec))) {
kfree(info);
return ERR_PTR(ret);
}
return info;
}
static void
prog_clk(struct drm_device *dev, int clk, struct nvc0_pm_clock *info)
{
/* program dividers at 137160/1371d0 first */
if (clk < 7 && !info->ssel) {
nv_mask(dev, 0x1371d0 + (clk * 0x04), 0x80003f3f, info->ddiv);
nv_wr32(dev, 0x137160 + (clk * 0x04), info->dsrc);
}
/* switch clock to non-pll mode */
nv_mask(dev, 0x137100, (1 << clk), 0x00000000);
nv_wait(dev, 0x137100, (1 << clk), 0x00000000);
/* reprogram pll */
if (clk < 7) {
/* make sure it's disabled first... */
u32 base = 0x137000 + (clk * 0x20);
u32 ctrl = nv_rd32(dev, base + 0x00);
if (ctrl & 0x00000001) {
nv_mask(dev, base + 0x00, 0x00000004, 0x00000000);
nv_mask(dev, base + 0x00, 0x00000001, 0x00000000);
}
/* program it to new values, if necessary */
if (info->ssel) {
nv_wr32(dev, base + 0x04, info->coef);
nv_mask(dev, base + 0x00, 0x00000001, 0x00000001);
nv_wait(dev, base + 0x00, 0x00020000, 0x00020000);
nv_mask(dev, base + 0x00, 0x00020004, 0x00000004);
}
}
/* select pll/non-pll mode, and program final clock divider */
nv_mask(dev, 0x137100, (1 << clk), info->ssel);
nv_wait(dev, 0x137100, (1 << clk), info->ssel);
nv_mask(dev, 0x137250 + (clk * 0x04), 0x00003f3f, info->mdiv);
}
int
nvc0_pm_clocks_set(struct drm_device *dev, void *data)
{
struct nvc0_pm_state *info = data;
int i;
for (i = 0; i < 16; i++) {
if (!info->eng[i].freq)
continue;
prog_clk(dev, i, &info->eng[i]);
}
kfree(info);
return 0;
}

File diff suppressed because it is too large Load Diff