提交 5d56fe5f 编写于 作者: D Dave Airlie

Merge branch 'drm-nouveau-next' of...

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
  ...
无相关合并请求
......@@ -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 \
......
......@@ -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;
}
/* Dithering */
if (property == dev->mode_config.dithering_mode_property) {
struct nouveau_crtc *nv_crtc = NULL;
/* Underscan */
if (property == disp->underscan_property) {
if (nv_connector->underscan != value) {
nv_connector->underscan = value;
if (!nv_crtc || !nv_crtc->set_scale)
return 0;
if (value == DRM_MODE_DITHERING_ON)
nv_connector->use_dithering = true;
else
nv_connector->use_dithering = false;
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 == disp->dithering_mode) {
nv_connector->dithering_mode = value;
if (!nv_crtc || !nv_crtc->set_dither)
return 0;
if (connector->encoder && connector->encoder->crtc)
nv_crtc = nouveau_crtc(connector->encoder->crtc);
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, nv_connector->use_dithering,
true);
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,96 +846,175 @@ 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;
}
/* defaults, will get overridden in detect() */
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
/* 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;
}
drm_connector_init(dev, connector, funcs, type);
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
/* 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);
}
/* Check if we need dithering enabled */
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
bool dummy, is_24bit = false;
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;
}
}
ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &is_24bit);
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 "
"LVDS\n");
goto fail;
NV_ERROR(dev, "Error parsing LVDS table, disabling\n");
kfree(nv_connector);
return ERR_PTR(ret);
}
nv_connector->use_dithering = !is_24bit;
funcs = &nouveau_connector_funcs_lvds;
} else {
funcs = &nouveau_connector_funcs;
}
/* defaults, will get overridden in detect() */
connector->interlace_allowed = false;
connector->doublescan_allowed = false;
drm_connector_init(dev, connector, funcs, type);
drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
/* 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,16 +359,10 @@ nouveau_pci_resume(struct pci_dev *pdev)
NV_ERROR(dev, "Could not pin/map cursor.\n");
}
engine->display.init(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
u32 offset = nv_crtc->cursor.nvbo->bo.offset;
nouveau_fbcon_set_suspend(dev, 0);
nouveau_fbcon_zfill_all(dev);
nv_crtc->cursor.set_offset(nv_crtc, offset);
nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
nv_crtc->cursor_saved_y);
}
nouveau_display_init(dev);
/* Force CLUT to get re-loaded during modeset */
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
......@@ -376,15 +371,17 @@ nouveau_pci_resume(struct pci_dev *pdev)
nv_crtc->lut.depth = 0;
}
console_lock();
nouveau_fbcon_set_suspend(dev, 0);
console_unlock();
drm_helper_resume_force_mode(dev);
nouveau_fbcon_zfill_all(dev);
list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
u32 offset = nv_crtc->cursor.nvbo->bo.offset;
drm_helper_resume_force_mode(dev);
nv_crtc->cursor.set_offset(nv_crtc, offset);
nv_crtc->cursor.set_pos(nv_crtc, nv_crtc->cursor_saved_x,
nv_crtc->cursor_saved_y);
}
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);
/* nv50_calc. */
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.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)
......
/*
* 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);
}
}
}
/*
* 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
/*
* 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);
}
/*
* 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"
#define T_TIMEOUT 2200000
#define T_RISEFALL 1000
#define T_HOLD 5000
static void
nv04_i2c_setscl(void *data, int state)
i2c_drive_scl(void *data, int state)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
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;
val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
nv_wr32(dev, i2c->wr, val | 0x01);
udelay((nsec + 500) / 1000);
}
static void
nv4e_i2c_setsda(void *data, int state)
static bool
i2c_raise_scl(struct nouveau_i2c_chan *port)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
uint8_t val;
u32 timeout = T_TIMEOUT / T_RISEFALL;
i2c_drive_scl(port, 1);
do {
i2c_delay(port, T_RISEFALL);
} while (!i2c_sense_scl(port) && --timeout);
val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
nv_wr32(dev, i2c->wr, val | 0x01);
return timeout != 0;
}
static int
nv4e_i2c_getscl(void *data)
i2c_start(struct nouveau_i2c_chan *port)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
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;
}
return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
i2c_drive_sda(port, 0);
i2c_delay(port, T_HOLD);
i2c_drive_scl(port, 0);
i2c_delay(port, T_HOLD);
return ret;
}
static int
nv4e_i2c_getsda(void *data)
static void
i2c_stop(struct nouveau_i2c_chan *port)
{
struct nouveau_i2c_chan *i2c = data;
struct drm_device *dev = i2c->dev;
return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
i2c_drive_scl(port, 0);
i2c_drive_sda(port, 0);
i2c_delay(port, T_RISEFALL);
i2c_drive_scl(port, 1);
i2c_delay(port, T_HOLD);
i2c_drive_sda(port, 1);
i2c_delay(port, T_HOLD);
}
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)
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) & 1);
}
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
nv50_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;
i2c_drive_sda(port, 1);
i2c_delay(port, T_RISEFALL);
return !!(nv_rd32(dev, i2c->rd) & 2);
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 void
nv50_i2c_setscl(void *data, int state)
static int
i2c_get_byte(struct nouveau_i2c_chan *port, u8 *byte, bool last)
{
struct nouveau_i2c_chan *i2c = data;
int i, bit;
*byte = 0;
for (i = 7; i >= 0; i--) {
bit = i2c_bitr(port);
if (bit < 0)
return bit;
*byte |= bit << i;
}
nv_wr32(i2c->dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
return i2c_bitw(port, last ? 1 : 0);
}
static void
nv50_i2c_setsda(void *data, int state)
static int
i2c_put_byte(struct nouveau_i2c_chan *port, u8 byte)
{
struct nouveau_i2c_chan *i2c = data;
int i, ret;
for (i = 7; i >= 0; i--) {
ret = i2c_bitw(port, !!(byte & (1 << i)));
if (ret < 0)
return ret;
}
nv_mask(i2c->dev, i2c->wr, 0x00000006, 4 | (state ? 2 : 0));
i2c->data = state;
ret = i2c_bitr(port);
if (ret == 1) /* nack */
ret = -EIO;
return ret;
}
static int
nvd0_i2c_getscl(void *data)
i2c_addr(struct nouveau_i2c_chan *port, struct i2c_msg *msg)
{
struct nouveau_i2c_chan *i2c = data;
return !!(nv_rd32(i2c->dev, i2c->rd) & 0x10);
u32 addr = msg->addr << 1;
if (msg->flags & I2C_M_RD)
addr |= 1;
return i2c_put_byte(port, addr);
}
static int
nvd0_i2c_getsda(void *data)
i2c_bit_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
{
struct nouveau_i2c_chan *i2c = data;
return !!(nv_rd32(i2c->dev, i2c->rd) & 0x20);
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;
}
int
nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
static u32
i2c_bit_func(struct i2c_adapter *adap)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nouveau_i2c_chan *i2c;
int ret;
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
};
if (entry->chan)
return -EEXIST;
static u8 *
i2c_table(struct drm_device *dev, u8 *version)
{
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]);
}
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;
/* early revisions had no version number, use dcb version */
if (i2c) {
*version = dcb[0];
if (*version >= 0x30)
*version = i2c[0];
}
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;
return i2c;
}
int
nouveau_i2c_init(struct drm_device *dev)
{
struct drm_nouveau_private *dev_priv = dev->dev_private;
struct nvbios *bios = &dev_priv->vbios;
struct nouveau_i2c_chan *port;
u8 *i2c, *entry, legacy[2][4] = {};
u8 version, entries, recordlen;
int ret, i;
INIT_LIST_HEAD(&dev_priv->i2c_ports);
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];
}
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 == 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;
}
}
if (index >= DCB_MAX_NUM_I2C_ENTRIES)
return NULL;
list_for_each_entry(port, &dev_priv->i2c_ports, head) {
if (port->index == index)
break;
}
if (dev_priv->card_type >= NV_50 && (i2c->entry & 0x00000100)) {
uint32_t reg = 0xe500, val;
if (&port->head == &dev_priv->i2c_ports)
return NULL;
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);
}
......
/*
* 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 < 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]);
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;
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
......
......@@ -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;
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册