mirror of https://gitee.com/openkylin/linux.git
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:
commit
5d56fe5fd7
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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__ */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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, ®, &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, ®, &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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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, ®, &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, ®, &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);
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -152,7 +152,7 @@ uint32_t nva3_pcopy_code[] = {
|
|||
0xf10010fe,
|
||||
0xf1040017,
|
||||
0xf0fff327,
|
||||
0x22d00023,
|
||||
0x12d00023,
|
||||
0x0c25f0c0,
|
||||
0xf40012d0,
|
||||
0x17f11031,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -145,7 +145,7 @@ uint32_t nvc0_pcopy_code[] = {
|
|||
0xf10010fe,
|
||||
0xf1040017,
|
||||
0xf0fff327,
|
||||
0x22d00023,
|
||||
0x12d00023,
|
||||
0x0c25f0c0,
|
||||
0xf40012d0,
|
||||
0x17f11031,
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -87,6 +87,7 @@ nvc0_graph_class(struct drm_device *dev)
|
|||
case 0xc1:
|
||||
return 0x9197;
|
||||
case 0xc8:
|
||||
case 0xd9:
|
||||
return 0x9297;
|
||||
default:
|
||||
return 0;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue