mirror of
https://github.com/torvalds/linux.git
synced 2024-11-05 03:21:32 +00:00
Merge remote branch 'nouveau/for-airlied' of ../drm-nouveau-next into drm-linus
* 'nouveau/for-airlied' of ../drm-nouveau-next: drm/nv50: prevent switching off SOR when in use for DVI-over-DP drm/nv50: fail auxch transaction if reply count not what we expect drm/nouveau: fix failure path if userspace specifies no valid memtypes drm/nouveau: report LVDS as disconnected if lid closed drm/nv50: prevent accidently turning off encoders we're actually using drm/nv50: fix alignment of per-channel fifo cache drm/nouveau: Evict buffers in VRAM before freeing sgdma drm/nouveau: Acknowledge DMA_VTX_PROTECTION PGRAPH interrupts drm/nouveau: fix thinko in nv04_instmem.c drm/nouveau: fix a race condition in nouveau_dma_wait()
This commit is contained in:
commit
8d586fe65a
@ -24,9 +24,12 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <acpi/button.h>
|
||||||
|
|
||||||
#include "drmP.h"
|
#include "drmP.h"
|
||||||
#include "drm_edid.h"
|
#include "drm_edid.h"
|
||||||
#include "drm_crtc_helper.h"
|
#include "drm_crtc_helper.h"
|
||||||
|
|
||||||
#include "nouveau_reg.h"
|
#include "nouveau_reg.h"
|
||||||
#include "nouveau_drv.h"
|
#include "nouveau_drv.h"
|
||||||
#include "nouveau_encoder.h"
|
#include "nouveau_encoder.h"
|
||||||
@ -235,6 +238,10 @@ nouveau_connector_detect(struct drm_connector *connector)
|
|||||||
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
|
||||||
nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
|
||||||
if (nv_encoder && nv_connector->native_mode) {
|
if (nv_encoder && nv_connector->native_mode) {
|
||||||
|
#ifdef CONFIG_ACPI
|
||||||
|
if (!nouveau_ignorelid && !acpi_lid_open())
|
||||||
|
return connector_status_disconnected;
|
||||||
|
#endif
|
||||||
nouveau_connector_set_encoder(connector, nv_encoder);
|
nouveau_connector_set_encoder(connector, nv_encoder);
|
||||||
return connector_status_connected;
|
return connector_status_connected;
|
||||||
}
|
}
|
||||||
|
@ -126,47 +126,52 @@ OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
|
|||||||
chan->dma.cur += nr_dwords;
|
chan->dma.cur += nr_dwords;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool
|
/* Fetch and adjust GPU GET pointer
|
||||||
READ_GET(struct nouveau_channel *chan, uint32_t *get)
|
*
|
||||||
|
* Returns:
|
||||||
|
* value >= 0, the adjusted GET pointer
|
||||||
|
* -EINVAL if GET pointer currently outside main push buffer
|
||||||
|
* -EBUSY if timeout exceeded
|
||||||
|
*/
|
||||||
|
static inline int
|
||||||
|
READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)
|
||||||
{
|
{
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
|
|
||||||
val = nvchan_rd32(chan, chan->user_get);
|
val = nvchan_rd32(chan, chan->user_get);
|
||||||
if (val < chan->pushbuf_base ||
|
|
||||||
val > chan->pushbuf_base + (chan->dma.max << 2)) {
|
/* reset counter as long as GET is still advancing, this is
|
||||||
/* meaningless to dma_wait() except to know whether the
|
* to avoid misdetecting a GPU lockup if the GPU happens to
|
||||||
* GPU has stalled or not
|
* just be processing an operation that takes a long time
|
||||||
*/
|
*/
|
||||||
*get = val;
|
if (val != *prev_get) {
|
||||||
return false;
|
*prev_get = val;
|
||||||
|
*timeout = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
*get = (val - chan->pushbuf_base) >> 2;
|
if ((++*timeout & 0xff) == 0) {
|
||||||
return true;
|
DRM_UDELAY(1);
|
||||||
|
if (*timeout > 100000)
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val < chan->pushbuf_base ||
|
||||||
|
val > chan->pushbuf_base + (chan->dma.max << 2))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
return (val - chan->pushbuf_base) >> 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
nouveau_dma_wait(struct nouveau_channel *chan, int size)
|
nouveau_dma_wait(struct nouveau_channel *chan, int size)
|
||||||
{
|
{
|
||||||
uint32_t get, prev_get = 0, cnt = 0;
|
uint32_t prev_get = 0, cnt = 0;
|
||||||
bool get_valid;
|
int get;
|
||||||
|
|
||||||
while (chan->dma.free < size) {
|
while (chan->dma.free < size) {
|
||||||
/* reset counter as long as GET is still advancing, this is
|
get = READ_GET(chan, &prev_get, &cnt);
|
||||||
* to avoid misdetecting a GPU lockup if the GPU happens to
|
if (unlikely(get == -EBUSY))
|
||||||
* just be processing an operation that takes a long time
|
return -EBUSY;
|
||||||
*/
|
|
||||||
get_valid = READ_GET(chan, &get);
|
|
||||||
if (get != prev_get) {
|
|
||||||
prev_get = get;
|
|
||||||
cnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((++cnt & 0xff) == 0) {
|
|
||||||
DRM_UDELAY(1);
|
|
||||||
if (cnt > 100000)
|
|
||||||
return -EBUSY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* loop until we have a usable GET pointer. the value
|
/* loop until we have a usable GET pointer. the value
|
||||||
* we read from the GPU may be outside the main ring if
|
* we read from the GPU may be outside the main ring if
|
||||||
@ -177,7 +182,7 @@ nouveau_dma_wait(struct nouveau_channel *chan, int size)
|
|||||||
* from the SKIPS area, so the code below doesn't have to deal
|
* from the SKIPS area, so the code below doesn't have to deal
|
||||||
* with some fun corner cases.
|
* with some fun corner cases.
|
||||||
*/
|
*/
|
||||||
if (!get_valid || get < NOUVEAU_DMA_SKIPS)
|
if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (get <= chan->dma.cur) {
|
if (get <= chan->dma.cur) {
|
||||||
@ -203,6 +208,19 @@ nouveau_dma_wait(struct nouveau_channel *chan, int size)
|
|||||||
* after processing the currently pending commands.
|
* after processing the currently pending commands.
|
||||||
*/
|
*/
|
||||||
OUT_RING(chan, chan->pushbuf_base | 0x20000000);
|
OUT_RING(chan, chan->pushbuf_base | 0x20000000);
|
||||||
|
|
||||||
|
/* wait for GET to depart from the skips area.
|
||||||
|
* prevents writing GET==PUT and causing a race
|
||||||
|
* condition that causes us to think the GPU is
|
||||||
|
* idle when it's not.
|
||||||
|
*/
|
||||||
|
do {
|
||||||
|
get = READ_GET(chan, &prev_get, &cnt);
|
||||||
|
if (unlikely(get == -EBUSY))
|
||||||
|
return -EBUSY;
|
||||||
|
if (unlikely(get == -EINVAL))
|
||||||
|
continue;
|
||||||
|
} while (get <= NOUVEAU_DMA_SKIPS);
|
||||||
WRITE_PUT(NOUVEAU_DMA_SKIPS);
|
WRITE_PUT(NOUVEAU_DMA_SKIPS);
|
||||||
|
|
||||||
/* we're now submitting commands at the start of
|
/* we're now submitting commands at the start of
|
||||||
|
@ -490,7 +490,8 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
|
|||||||
if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
|
if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
|
||||||
NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
|
NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
|
||||||
nv_rd32(dev, NV50_AUXCH_CTRL(index)));
|
nv_rd32(dev, NV50_AUXCH_CTRL(index)));
|
||||||
return -EBUSY;
|
ret = -EBUSY;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
udelay(400);
|
udelay(400);
|
||||||
@ -501,6 +502,11 @@ nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) {
|
||||||
|
ret = -EREMOTEIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (cmd & 1) {
|
if (cmd & 1) {
|
||||||
for (i = 0; i < 4; i++) {
|
for (i = 0; i < 4; i++) {
|
||||||
data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
|
data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
|
||||||
|
@ -71,6 +71,10 @@ MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)");
|
|||||||
int nouveau_uscript_tmds = -1;
|
int nouveau_uscript_tmds = -1;
|
||||||
module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400);
|
module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400);
|
||||||
|
|
||||||
|
MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
|
||||||
|
int nouveau_ignorelid = 0;
|
||||||
|
module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
|
||||||
|
|
||||||
MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
|
MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
|
||||||
"\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
|
"\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
|
||||||
"\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
|
"\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
|
||||||
|
@ -677,6 +677,7 @@ extern char *nouveau_tv_norm;
|
|||||||
extern int nouveau_reg_debug;
|
extern int nouveau_reg_debug;
|
||||||
extern char *nouveau_vbios;
|
extern char *nouveau_vbios;
|
||||||
extern int nouveau_ctxfw;
|
extern int nouveau_ctxfw;
|
||||||
|
extern int nouveau_ignorelid;
|
||||||
|
|
||||||
/* nouveau_state.c */
|
/* nouveau_state.c */
|
||||||
extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
|
extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
|
||||||
|
@ -321,6 +321,7 @@ retry:
|
|||||||
else {
|
else {
|
||||||
NV_ERROR(dev, "invalid valid domains: 0x%08x\n",
|
NV_ERROR(dev, "invalid valid domains: 0x%08x\n",
|
||||||
b->valid_domains);
|
b->valid_domains);
|
||||||
|
list_add_tail(&nvbo->entry, &op->both_list);
|
||||||
validate_fini(op, NULL);
|
validate_fini(op, NULL);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -483,6 +483,13 @@ nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource)
|
|||||||
if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
|
if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
|
||||||
if (nouveau_pgraph_intr_swmthd(dev, &trap))
|
if (nouveau_pgraph_intr_swmthd(dev, &trap))
|
||||||
unhandled = 1;
|
unhandled = 1;
|
||||||
|
} else if (nsource & NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION) {
|
||||||
|
uint32_t v = nv_rd32(dev, 0x402000);
|
||||||
|
nv_wr32(dev, 0x402000, v);
|
||||||
|
|
||||||
|
/* dump the error anyway for now: it's useful for
|
||||||
|
Gallium development */
|
||||||
|
unhandled = 1;
|
||||||
} else {
|
} else {
|
||||||
unhandled = 1;
|
unhandled = 1;
|
||||||
}
|
}
|
||||||
|
@ -386,7 +386,6 @@ void nouveau_mem_close(struct drm_device *dev)
|
|||||||
nouveau_bo_unpin(dev_priv->vga_ram);
|
nouveau_bo_unpin(dev_priv->vga_ram);
|
||||||
nouveau_bo_ref(NULL, &dev_priv->vga_ram);
|
nouveau_bo_ref(NULL, &dev_priv->vga_ram);
|
||||||
|
|
||||||
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
|
|
||||||
ttm_bo_device_release(&dev_priv->ttm.bdev);
|
ttm_bo_device_release(&dev_priv->ttm.bdev);
|
||||||
|
|
||||||
nouveau_ttm_global_release(dev_priv);
|
nouveau_ttm_global_release(dev_priv);
|
||||||
|
@ -525,6 +525,7 @@ static void nouveau_card_takedown(struct drm_device *dev)
|
|||||||
engine->mc.takedown(dev);
|
engine->mc.takedown(dev);
|
||||||
|
|
||||||
mutex_lock(&dev->struct_mutex);
|
mutex_lock(&dev->struct_mutex);
|
||||||
|
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
|
||||||
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
|
ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
|
||||||
mutex_unlock(&dev->struct_mutex);
|
mutex_unlock(&dev->struct_mutex);
|
||||||
nouveau_sgdma_takedown(dev);
|
nouveau_sgdma_takedown(dev);
|
||||||
|
@ -30,7 +30,7 @@ nv04_instmem_determine_amount(struct drm_device *dev)
|
|||||||
* of vram. For now, only reserve a small piece until we know
|
* of vram. For now, only reserve a small piece until we know
|
||||||
* more about what each chipset requires.
|
* more about what each chipset requires.
|
||||||
*/
|
*/
|
||||||
switch (dev_priv->chipset & 0xf0) {
|
switch (dev_priv->chipset) {
|
||||||
case 0x40:
|
case 0x40:
|
||||||
case 0x47:
|
case 0x47:
|
||||||
case 0x49:
|
case 0x49:
|
||||||
|
@ -432,6 +432,7 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
|
|||||||
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
|
||||||
struct drm_device *dev = crtc->dev;
|
struct drm_device *dev = crtc->dev;
|
||||||
struct drm_encoder *encoder;
|
struct drm_encoder *encoder;
|
||||||
|
uint32_t dac = 0, sor = 0;
|
||||||
|
|
||||||
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
|
||||||
|
|
||||||
@ -439,9 +440,28 @@ nv50_crtc_prepare(struct drm_crtc *crtc)
|
|||||||
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||||
|
|
||||||
if (drm_helper_encoder_in_use(encoder))
|
if (!drm_helper_encoder_in_use(encoder))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
|
||||||
|
nv_encoder->dcb->type == OUTPUT_TV)
|
||||||
|
dac |= (1 << nv_encoder->or);
|
||||||
|
else
|
||||||
|
sor |= (1 << nv_encoder->or);
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
|
||||||
|
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||||
|
|
||||||
|
if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
|
||||||
|
nv_encoder->dcb->type == OUTPUT_TV) {
|
||||||
|
if (dac & (1 << nv_encoder->or))
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
if (sor & (1 << nv_encoder->or))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
nv_encoder->disconnect(nv_encoder);
|
nv_encoder->disconnect(nv_encoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,7 +272,7 @@ nv50_fifo_create_context(struct nouveau_channel *chan)
|
|||||||
return ret;
|
return ret;
|
||||||
ramfc = chan->ramfc->gpuobj;
|
ramfc = chan->ramfc->gpuobj;
|
||||||
|
|
||||||
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 256,
|
ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 1024,
|
||||||
0, &chan->cache);
|
0, &chan->cache);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -90,11 +90,24 @@ nv50_sor_dpms(struct drm_encoder *encoder, int mode)
|
|||||||
{
|
{
|
||||||
struct drm_device *dev = encoder->dev;
|
struct drm_device *dev = encoder->dev;
|
||||||
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
|
||||||
|
struct drm_encoder *enc;
|
||||||
uint32_t val;
|
uint32_t val;
|
||||||
int or = nv_encoder->or;
|
int or = nv_encoder->or;
|
||||||
|
|
||||||
NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
|
NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
|
||||||
|
|
||||||
|
nv_encoder->last_dpms = mode;
|
||||||
|
list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
|
||||||
|
struct nouveau_encoder *nvenc = nouveau_encoder(enc);
|
||||||
|
|
||||||
|
if (nvenc == nv_encoder ||
|
||||||
|
nvenc->dcb->or != nv_encoder->dcb->or)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (nvenc->last_dpms == DRM_MODE_DPMS_ON)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* wait for it to be done */
|
/* wait for it to be done */
|
||||||
if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
|
if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
|
||||||
NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
|
NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user