diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau')
19 files changed, 291 insertions, 157 deletions
diff --git a/drivers/gpu/drm/nouveau/core/core/object.c b/drivers/gpu/drm/nouveau/core/core/object.c index 0daab62ea14..3b2e7b6304d 100644 --- a/drivers/gpu/drm/nouveau/core/core/object.c +++ b/drivers/gpu/drm/nouveau/core/core/object.c @@ -278,7 +278,6 @@ nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)  	struct nouveau_object *parent = NULL;  	struct nouveau_object *namedb = NULL;  	struct nouveau_handle *handle = NULL; -	int ret = -EINVAL;  	parent = nouveau_handle_ref(client, _parent);  	if (!parent) @@ -295,7 +294,7 @@ nouveau_object_del(struct nouveau_object *client, u32 _parent, u32 _handle)  	}  	nouveau_object_ref(NULL, &parent); -	return ret; +	return handle ? 0 : -EINVAL;  }  int diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c index 5fa13267bd9..02e369f8044 100644 --- a/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c +++ b/drivers/gpu/drm/nouveau/core/engine/disp/nv50.c @@ -544,13 +544,13 @@ nv50_disp_curs_ofuncs = {  static void  nv50_disp_base_vblank_enable(struct nouveau_event *event, int head)  { -	nv_mask(event->priv, 0x61002c, (1 << head), (1 << head)); +	nv_mask(event->priv, 0x61002c, (4 << head), (4 << head));  }  static void  nv50_disp_base_vblank_disable(struct nouveau_event *event, int head)  { -	nv_mask(event->priv, 0x61002c, (1 << head), (0 << head)); +	nv_mask(event->priv, 0x61002c, (4 << head), 0);  }  static int diff --git a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c index 61cec0f6ff1..4857f913efd 100644 --- a/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c +++ b/drivers/gpu/drm/nouveau/core/engine/graph/nve0.c @@ -350,7 +350,7 @@ nve0_graph_init_gpc_0(struct nvc0_graph_priv *priv)  		nv_wr32(priv, GPC_UNIT(gpc, 0x0918), magicgpc918);  	} -	nv_wr32(priv, GPC_BCAST(0x1bd4), magicgpc918); +	nv_wr32(priv, GPC_BCAST(0x3fd4), magicgpc918);  	nv_wr32(priv, GPC_BCAST(0x08ac), nv_rd32(priv, 0x100800));  } diff --git a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h index 6b17b614629..0b20fc0d19c 100644 --- a/drivers/gpu/drm/nouveau/core/include/subdev/therm.h +++ b/drivers/gpu/drm/nouveau/core/include/subdev/therm.h @@ -4,7 +4,7 @@  #include <core/device.h>  #include <core/subdev.h> -enum nouveau_therm_mode { +enum nouveau_therm_fan_mode {  	NOUVEAU_THERM_CTRL_NONE = 0,  	NOUVEAU_THERM_CTRL_MANUAL = 1,  	NOUVEAU_THERM_CTRL_AUTO = 2, diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c index e816f06637a..0e2c1a4f165 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/base.c @@ -248,6 +248,22 @@ nouveau_bios_shadow_pci(struct nouveau_bios *bios)  	}  } +static void +nouveau_bios_shadow_platform(struct nouveau_bios *bios) +{ +	struct pci_dev *pdev = nv_device(bios)->pdev; +	size_t size; + +	void __iomem *rom = pci_platform_rom(pdev, &size); +	if (rom && size) { +		bios->data = kmalloc(size, GFP_KERNEL); +		if (bios->data) { +			memcpy_fromio(bios->data, rom, size); +			bios->size = size; +		} +	} +} +  static int  nouveau_bios_score(struct nouveau_bios *bios, const bool writeable)  { @@ -288,6 +304,7 @@ nouveau_bios_shadow(struct nouveau_bios *bios)  		{ "PROM", nouveau_bios_shadow_prom, false, 0, 0, NULL },  		{ "ACPI", nouveau_bios_shadow_acpi, true, 0, 0, NULL },  		{ "PCIROM", nouveau_bios_shadow_pci, true, 0, 0, NULL }, +		{ "PLATFORM", nouveau_bios_shadow_platform, true, 0, 0, NULL },  		{}  	};  	struct methods *mthd, *best; diff --git a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c index 2cc1e6a5eb6..9c41b58d57e 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/bios/init.c +++ b/drivers/gpu/drm/nouveau/core/subdev/bios/init.c @@ -869,7 +869,7 @@ init_idx_addr_latched(struct nvbios_init *init)  		init->offset += 2;  		init_wr32(init, dreg, idata); -		init_mask(init, creg, ~mask, data | idata); +		init_mask(init, creg, ~mask, data | iaddr);  	}  } diff --git a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c index a114a0ed7e9..2e98e8a3f1a 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/i2c/base.c @@ -142,6 +142,7 @@ nouveau_i2c_port_create_(struct nouveau_object *parent,  	/* drop port's i2c subdev refcount, i2c handles this itself */  	if (ret == 0) {  		list_add_tail(&port->head, &i2c->ports); +		atomic_dec(&parent->refcount);  		atomic_dec(&engine->refcount);  	} diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c index f794dc89a3b..a00a5a76e2d 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/base.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/base.c @@ -134,7 +134,7 @@ nouveau_therm_alarm(struct nouveau_alarm *alarm)  }  int -nouveau_therm_mode(struct nouveau_therm *therm, int mode) +nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode)  {  	struct nouveau_therm_priv *priv = (void *)therm;  	struct nouveau_device *device = nv_device(therm); @@ -149,10 +149,15 @@ nouveau_therm_mode(struct nouveau_therm *therm, int mode)  	    (mode != NOUVEAU_THERM_CTRL_NONE && device->card_type >= NV_C0))  		return -EINVAL; +	/* do not allow automatic fan management if the thermal sensor is +	 * not available */ +	if (priv->mode == 2 && therm->temp_get(therm) < 0) +		return -EINVAL; +  	if (priv->mode == mode)  		return 0; -	nv_info(therm, "Thermal management: %s\n", name[mode]); +	nv_info(therm, "fan management: %s\n", name[mode]);  	nouveau_therm_update(therm, mode);  	return 0;  } @@ -213,7 +218,7 @@ nouveau_therm_attr_set(struct nouveau_therm *therm,  		priv->fan->bios.max_duty = value;  		return 0;  	case NOUVEAU_THERM_ATTR_FAN_MODE: -		return nouveau_therm_mode(therm, value); +		return nouveau_therm_fan_mode(therm, value);  	case NOUVEAU_THERM_ATTR_THRS_FAN_BOOST:  		priv->bios_sensor.thrs_fan_boost.temp = value;  		priv->sensor.program_alarms(therm); @@ -263,7 +268,7 @@ _nouveau_therm_init(struct nouveau_object *object)  		return ret;  	if (priv->suspend >= 0) -		nouveau_therm_mode(therm, priv->mode); +		nouveau_therm_fan_mode(therm, priv->mode);  	priv->sensor.program_alarms(therm);  	return 0;  } @@ -313,11 +318,12 @@ nouveau_therm_create_(struct nouveau_object *parent,  int  nouveau_therm_preinit(struct nouveau_therm *therm)  { -	nouveau_therm_ic_ctor(therm);  	nouveau_therm_sensor_ctor(therm); +	nouveau_therm_ic_ctor(therm);  	nouveau_therm_fan_ctor(therm); -	nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_NONE); +	nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_NONE); +	nouveau_therm_sensor_preinit(therm);  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c index e24090bac19..8b3adec5fbb 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/ic.c @@ -32,6 +32,7 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,  			struct i2c_board_info *info)  {  	struct nouveau_therm_priv *priv = (void *)nouveau_therm(i2c); +	struct nvbios_therm_sensor *sensor = &priv->bios_sensor;  	struct i2c_client *client;  	request_module("%s%s", I2C_MODULE_PREFIX, info->type); @@ -46,8 +47,9 @@ probe_monitoring_device(struct nouveau_i2c_port *i2c,  	}  	nv_info(priv, -		"Found an %s at address 0x%x (controlled by lm_sensors)\n", -		info->type, info->addr); +		"Found an %s at address 0x%x (controlled by lm_sensors, " +		"temp offset %+i C)\n", +		info->type, info->addr, sensor->offset_constant);  	priv->ic = client;  	return true; diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c index 0f5363edb96..a70d1b7e397 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/nv40.c @@ -29,54 +29,83 @@ struct nv40_therm_priv {  	struct nouveau_therm_priv base;  }; +enum nv40_sensor_style { INVALID_STYLE = -1, OLD_STYLE = 0, NEW_STYLE = 1 }; + +static enum nv40_sensor_style +nv40_sensor_style(struct nouveau_therm *therm) +{ +	struct nouveau_device *device = nv_device(therm); + +	switch (device->chipset) { +	case 0x43: +	case 0x44: +	case 0x4a: +	case 0x47: +		return OLD_STYLE; + +	case 0x46: +	case 0x49: +	case 0x4b: +	case 0x4e: +	case 0x4c: +	case 0x67: +	case 0x68: +	case 0x63: +		return NEW_STYLE; +	default: +		return INVALID_STYLE; +	} +} +  static int  nv40_sensor_setup(struct nouveau_therm *therm)  { -	struct nouveau_device *device = nv_device(therm); +	enum nv40_sensor_style style = nv40_sensor_style(therm);  	/* enable ADC readout and disable the ALARM threshold */ -	if (device->chipset >= 0x46) { +	if (style == NEW_STYLE) {  		nv_mask(therm, 0x15b8, 0x80000000, 0);  		nv_wr32(therm, 0x15b0, 0x80003fff); -		mdelay(10); /* wait for the temperature to stabilize */ +		mdelay(20); /* wait for the temperature to stabilize */  		return nv_rd32(therm, 0x15b4) & 0x3fff; -	} else { +	} else if (style == OLD_STYLE) {  		nv_wr32(therm, 0x15b0, 0xff); +		mdelay(20); /* wait for the temperature to stabilize */  		return nv_rd32(therm, 0x15b4) & 0xff; -	} +	} else +		return -ENODEV;  }  static int  nv40_temp_get(struct nouveau_therm *therm)  {  	struct nouveau_therm_priv *priv = (void *)therm; -	struct nouveau_device *device = nv_device(therm);  	struct nvbios_therm_sensor *sensor = &priv->bios_sensor; +	enum nv40_sensor_style style = nv40_sensor_style(therm);  	int core_temp; -	if (device->chipset >= 0x46) { +	if (style == NEW_STYLE) {  		nv_wr32(therm, 0x15b0, 0x80003fff);  		core_temp = nv_rd32(therm, 0x15b4) & 0x3fff; -	} else { +	} else if (style == OLD_STYLE) {  		nv_wr32(therm, 0x15b0, 0xff);  		core_temp = nv_rd32(therm, 0x15b4) & 0xff; -	} - -	/* Setup the sensor if the temperature is 0 */ -	if (core_temp == 0) -		core_temp = nv40_sensor_setup(therm); +	} else +		return -ENODEV; -	if (sensor->slope_div == 0) -		sensor->slope_div = 1; -	if (sensor->offset_den == 0) -		sensor->offset_den = 1; -	if (sensor->slope_mult < 1) -		sensor->slope_mult = 1; +	/* if the slope or the offset is unset, do no use the sensor */ +	if (!sensor->slope_div || !sensor->slope_mult || +	    !sensor->offset_num || !sensor->offset_den) +	    return -ENODEV;  	core_temp = core_temp * sensor->slope_mult / sensor->slope_div;  	core_temp = core_temp + sensor->offset_num / sensor->offset_den;  	core_temp = core_temp + sensor->offset_constant - 8; +	/* reserve negative temperatures for errors */ +	if (core_temp < 0) +		core_temp = 0; +  	return core_temp;  } diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h index 06b98706b3f..438d9824b77 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/priv.h @@ -102,7 +102,7 @@ struct nouveau_therm_priv {  	struct i2c_client *ic;  }; -int nouveau_therm_mode(struct nouveau_therm *therm, int mode); +int nouveau_therm_fan_mode(struct nouveau_therm *therm, int mode);  int nouveau_therm_attr_get(struct nouveau_therm *therm,  		       enum nouveau_therm_attr_type type);  int nouveau_therm_attr_set(struct nouveau_therm *therm, @@ -122,6 +122,7 @@ int nouveau_therm_fan_sense(struct nouveau_therm *therm);  int nouveau_therm_preinit(struct nouveau_therm *); +void nouveau_therm_sensor_preinit(struct nouveau_therm *);  void nouveau_therm_sensor_set_threshold_state(struct nouveau_therm *therm,  					     enum nouveau_therm_thrs thrs,  					     enum nouveau_therm_thrs_state st); diff --git a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c index b37624af829..470f6a47b65 100644 --- a/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c +++ b/drivers/gpu/drm/nouveau/core/subdev/therm/temp.c @@ -34,10 +34,6 @@ nouveau_therm_temp_set_defaults(struct nouveau_therm *therm)  {  	struct nouveau_therm_priv *priv = (void *)therm; -	priv->bios_sensor.slope_mult = 1; -	priv->bios_sensor.slope_div = 1; -	priv->bios_sensor.offset_num = 0; -	priv->bios_sensor.offset_den = 1;  	priv->bios_sensor.offset_constant = 0;  	priv->bios_sensor.thrs_fan_boost.temp = 90; @@ -60,11 +56,6 @@ nouveau_therm_temp_safety_checks(struct nouveau_therm *therm)  	struct nouveau_therm_priv *priv = (void *)therm;  	struct nvbios_therm_sensor *s = &priv->bios_sensor; -	if (!priv->bios_sensor.slope_div) -		priv->bios_sensor.slope_div = 1; -	if (!priv->bios_sensor.offset_den) -		priv->bios_sensor.offset_den = 1; -  	/* enforce a minimum hysteresis on thresholds */  	s->thrs_fan_boost.hysteresis = max_t(u8, s->thrs_fan_boost.hysteresis, 2);  	s->thrs_down_clock.hysteresis = max_t(u8, s->thrs_down_clock.hysteresis, 2); @@ -106,16 +97,16 @@ void nouveau_therm_sensor_event(struct nouveau_therm *therm,  	const char *thresolds[] = {  		"fanboost", "downclock", "critical", "shutdown"  	}; -	uint8_t temperature = therm->temp_get(therm); +	int temperature = therm->temp_get(therm);  	if (thrs < 0 || thrs > 3)  		return;  	if (dir == NOUVEAU_THERM_THRS_FALLING) -		nv_info(therm, "temperature (%u C) went below the '%s' threshold\n", +		nv_info(therm, "temperature (%i C) went below the '%s' threshold\n",  			temperature, thresolds[thrs]);  	else -		nv_info(therm, "temperature (%u C) hit the '%s' threshold\n", +		nv_info(therm, "temperature (%i C) hit the '%s' threshold\n",  			temperature, thresolds[thrs]);  	active = (dir == NOUVEAU_THERM_THRS_RISING); @@ -123,7 +114,7 @@ void nouveau_therm_sensor_event(struct nouveau_therm *therm,  	case NOUVEAU_THERM_THRS_FANBOOST:  		if (active) {  			nouveau_therm_fan_set(therm, true, 100); -			nouveau_therm_mode(therm, NOUVEAU_THERM_CTRL_AUTO); +			nouveau_therm_fan_mode(therm, NOUVEAU_THERM_CTRL_AUTO);  		}  		break;  	case NOUVEAU_THERM_THRS_DOWNCLOCK: @@ -202,7 +193,7 @@ alarm_timer_callback(struct nouveau_alarm *alarm)  					     NOUVEAU_THERM_THRS_SHUTDOWN);  	/* schedule the next poll in one second */ -	if (list_empty(&alarm->head)) +	if (therm->temp_get(therm) >= 0 && list_empty(&alarm->head))  		ptimer->alarm(ptimer, 1000 * 1000 * 1000, alarm);  	spin_unlock_irqrestore(&priv->sensor.alarm_program_lock, flags); @@ -225,6 +216,17 @@ nouveau_therm_program_alarms_polling(struct nouveau_therm *therm)  	alarm_timer_callback(&priv->sensor.therm_poll_alarm);  } +void +nouveau_therm_sensor_preinit(struct nouveau_therm *therm) +{ +	const char *sensor_avail = "yes"; + +	if (therm->temp_get(therm) < 0) +		sensor_avail = "no"; + +	nv_info(therm, "internal sensor: %s\n", sensor_avail); +} +  int  nouveau_therm_sensor_ctor(struct nouveau_therm *therm)  { diff --git a/drivers/gpu/drm/nouveau/nouveau_abi16.c b/drivers/gpu/drm/nouveau/nouveau_abi16.c index 41241922263..5eb3e0da7c6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_abi16.c +++ b/drivers/gpu/drm/nouveau/nouveau_abi16.c @@ -116,6 +116,11 @@ nouveau_abi16_chan_fini(struct nouveau_abi16 *abi16,  {  	struct nouveau_abi16_ntfy *ntfy, *temp; +	/* wait for all activity to stop before releasing notify object, which +	 * may be still in use */ +	if (chan->chan && chan->ntfy) +		nouveau_channel_idle(chan->chan); +  	/* cleanup notifier state */  	list_for_each_entry_safe(ntfy, temp, &chan->notifiers, head) {  		nouveau_abi16_ntfy_fini(chan, ntfy); @@ -386,7 +391,7 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)  	struct nouveau_drm *drm = nouveau_drm(dev);  	struct nouveau_device *device = nv_device(drm->device);  	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); -	struct nouveau_abi16_chan *chan, *temp; +	struct nouveau_abi16_chan *chan = NULL, *temp;  	struct nouveau_abi16_ntfy *ntfy;  	struct nouveau_object *object;  	struct nv_dma_class args = {}; @@ -399,10 +404,11 @@ nouveau_abi16_ioctl_notifierobj_alloc(ABI16_IOCTL_ARGS)  	if (unlikely(nv_device(abi16->device)->card_type >= NV_C0))  		return nouveau_abi16_put(abi16, -EINVAL); -	list_for_each_entry_safe(chan, temp, &abi16->channels, head) { -		if (chan->chan->handle == (NVDRM_CHAN | info->channel)) +	list_for_each_entry(temp, &abi16->channels, head) { +		if (temp->chan->handle == (NVDRM_CHAN | info->channel)) { +			chan = temp;  			break; -		chan = NULL; +		}  	}  	if (!chan) @@ -454,17 +460,18 @@ nouveau_abi16_ioctl_gpuobj_free(ABI16_IOCTL_ARGS)  {  	struct drm_nouveau_gpuobj_free *fini = data;  	struct nouveau_abi16 *abi16 = nouveau_abi16_get(file_priv, dev); -	struct nouveau_abi16_chan *chan, *temp; +	struct nouveau_abi16_chan *chan = NULL, *temp;  	struct nouveau_abi16_ntfy *ntfy;  	int ret;  	if (unlikely(!abi16))  		return -ENOMEM; -	list_for_each_entry_safe(chan, temp, &abi16->channels, head) { -		if (chan->chan->handle == (NVDRM_CHAN | fini->channel)) +	list_for_each_entry(temp, &abi16->channels, head) { +		if (temp->chan->handle == (NVDRM_CHAN | fini->channel)) { +			chan = temp;  			break; -		chan = NULL; +		}  	}  	if (!chan) diff --git a/drivers/gpu/drm/nouveau/nouveau_agp.c b/drivers/gpu/drm/nouveau/nouveau_agp.c index d28430cd2ba..6e7a55f93a8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_agp.c +++ b/drivers/gpu/drm/nouveau/nouveau_agp.c @@ -47,6 +47,18 @@ nouveau_agp_enabled(struct nouveau_drm *drm)  	if (drm->agp.stat == UNKNOWN) {  		if (!nouveau_agpmode)  			return false; +#ifdef __powerpc__ +		/* Disable AGP by default on all PowerPC machines for +		 * now -- At least some UniNorth-2 AGP bridges are +		 * known to be broken: DMA from the host to the card +		 * works just fine, but writeback from the card to the +		 * host goes straight to memory untranslated bypassing +		 * the GATT somehow, making them quite painful to deal +		 * with... +		 */ +		if (nouveau_agpmode == -1) +			return false; +#endif  		return true;  	} diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 11ca82148ed..7ff10711a4d 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -801,7 +801,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,  		stride  = 16 * 4;  		height  = amount / stride; -		if (new_mem->mem_type == TTM_PL_VRAM && +		if (old_mem->mem_type == TTM_PL_VRAM &&  		    nouveau_bo_tile_layout(nvbo)) {  			ret = RING_SPACE(chan, 8);  			if (ret) @@ -823,7 +823,7 @@ nv50_bo_move_m2mf(struct nouveau_channel *chan, struct ttm_buffer_object *bo,  			BEGIN_NV04(chan, NvSubCopy, 0x0200, 1);  			OUT_RING  (chan, 1);  		} -		if (old_mem->mem_type == TTM_PL_VRAM && +		if (new_mem->mem_type == TTM_PL_VRAM &&  		    nouveau_bo_tile_layout(nvbo)) {  			ret = RING_SPACE(chan, 8);  			if (ret) diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index d1099365bfc..c95decf543e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -72,11 +72,25 @@ module_param_named(modeset, nouveau_modeset, int, 0400);  static struct drm_driver driver;  static int +nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head) +{ +	struct nouveau_drm *drm = +		container_of(event, struct nouveau_drm, vblank[head]); +	drm_handle_vblank(drm->dev, head); +	return NVKM_EVENT_KEEP; +} + +static int  nouveau_drm_vblank_enable(struct drm_device *dev, int head)  {  	struct nouveau_drm *drm = nouveau_drm(dev);  	struct nouveau_disp *pdisp = nouveau_disp(drm->device); -	nouveau_event_get(pdisp->vblank, head, &drm->vblank); + +	if (WARN_ON_ONCE(head > ARRAY_SIZE(drm->vblank))) +		return -EIO; +	WARN_ON_ONCE(drm->vblank[head].func); +	drm->vblank[head].func = nouveau_drm_vblank_handler; +	nouveau_event_get(pdisp->vblank, head, &drm->vblank[head]);  	return 0;  } @@ -85,16 +99,11 @@ nouveau_drm_vblank_disable(struct drm_device *dev, int head)  {  	struct nouveau_drm *drm = nouveau_drm(dev);  	struct nouveau_disp *pdisp = nouveau_disp(drm->device); -	nouveau_event_put(pdisp->vblank, head, &drm->vblank); -} - -static int -nouveau_drm_vblank_handler(struct nouveau_eventh *event, int head) -{ -	struct nouveau_drm *drm = -		container_of(event, struct nouveau_drm, vblank); -	drm_handle_vblank(drm->dev, head); -	return NVKM_EVENT_KEEP; +	if (drm->vblank[head].func) +		nouveau_event_put(pdisp->vblank, head, &drm->vblank[head]); +	else +		WARN_ON_ONCE(1); +	drm->vblank[head].func = NULL;  }  static u64 @@ -292,7 +301,6 @@ nouveau_drm_load(struct drm_device *dev, unsigned long flags)  	dev->dev_private = drm;  	drm->dev = dev; -	drm->vblank.func = nouveau_drm_vblank_handler;  	INIT_LIST_HEAD(&drm->clients);  	spin_lock_init(&drm->tile.lock); diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.h b/drivers/gpu/drm/nouveau/nouveau_drm.h index b25df374c90..9c39bafbef2 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.h +++ b/drivers/gpu/drm/nouveau/nouveau_drm.h @@ -113,7 +113,7 @@ struct nouveau_drm {  	struct nvbios vbios;  	struct nouveau_display *display;  	struct backlight_device *backlight; -	struct nouveau_eventh vblank; +	struct nouveau_eventh vblank[4];  	/* power management */  	struct nouveau_pm *pm; diff --git a/drivers/gpu/drm/nouveau/nouveau_pm.c b/drivers/gpu/drm/nouveau/nouveau_pm.c index bb54098c6d9..936b442a6ab 100644 --- a/drivers/gpu/drm/nouveau/nouveau_pm.c +++ b/drivers/gpu/drm/nouveau/nouveau_pm.c @@ -402,8 +402,12 @@ nouveau_hwmon_show_temp(struct device *d, struct device_attribute *a, char *buf)  	struct drm_device *dev = dev_get_drvdata(d);  	struct nouveau_drm *drm = nouveau_drm(dev);  	struct nouveau_therm *therm = nouveau_therm(drm->device); +	int temp = therm->temp_get(therm); -	return snprintf(buf, PAGE_SIZE, "%d\n", therm->temp_get(therm) * 1000); +	if (temp < 0) +		return temp; + +	return snprintf(buf, PAGE_SIZE, "%d\n", temp * 1000);  }  static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, nouveau_hwmon_show_temp,  						  NULL, 0); @@ -871,7 +875,12 @@ static SENSOR_DEVICE_ATTR(pwm1_max, S_IRUGO | S_IWUSR,  			  nouveau_hwmon_get_pwm1_max,  			  nouveau_hwmon_set_pwm1_max, 0); -static struct attribute *hwmon_attributes[] = { +static struct attribute *hwmon_default_attributes[] = { +	&sensor_dev_attr_name.dev_attr.attr, +	&sensor_dev_attr_update_rate.dev_attr.attr, +	NULL +}; +static struct attribute *hwmon_temp_attributes[] = {  	&sensor_dev_attr_temp1_input.dev_attr.attr,  	&sensor_dev_attr_temp1_auto_point1_pwm.dev_attr.attr,  	&sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, @@ -882,8 +891,6 @@ static struct attribute *hwmon_attributes[] = {  	&sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,  	&sensor_dev_attr_temp1_emergency.dev_attr.attr,  	&sensor_dev_attr_temp1_emergency_hyst.dev_attr.attr, -	&sensor_dev_attr_name.dev_attr.attr, -	&sensor_dev_attr_update_rate.dev_attr.attr,  	NULL  };  static struct attribute *hwmon_fan_rpm_attributes[] = { @@ -898,8 +905,11 @@ static struct attribute *hwmon_pwm_fan_attributes[] = {  	NULL  }; -static const struct attribute_group hwmon_attrgroup = { -	.attrs = hwmon_attributes, +static const struct attribute_group hwmon_default_attrgroup = { +	.attrs = hwmon_default_attributes, +}; +static const struct attribute_group hwmon_temp_attrgroup = { +	.attrs = hwmon_temp_attributes,  };  static const struct attribute_group hwmon_fan_rpm_attrgroup = {  	.attrs = hwmon_fan_rpm_attributes, @@ -931,13 +941,22 @@ nouveau_hwmon_init(struct drm_device *dev)  	}  	dev_set_drvdata(hwmon_dev, dev); -	/* default sysfs entries */ -	ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_attrgroup); +	/* set the default attributes */ +	ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_default_attrgroup);  	if (ret) {  		if (ret)  			goto error;  	} +	/* if the card has a working thermal sensor */ +	if (therm->temp_get(therm) >= 0) { +		ret = sysfs_create_group(&hwmon_dev->kobj, &hwmon_temp_attrgroup); +		if (ret) { +			if (ret) +				goto error; +		} +	} +  	/* if the card has a pwm fan */  	/*XXX: incorrect, need better detection for this, some boards have  	 *     the gpio entries for pwm fan control even when there's no @@ -979,11 +998,10 @@ nouveau_hwmon_fini(struct drm_device *dev)  	struct nouveau_pm *pm = nouveau_pm(dev);  	if (pm->hwmon) { -		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_attrgroup); -		sysfs_remove_group(&pm->hwmon->kobj, -				   &hwmon_pwm_fan_attrgroup); -		sysfs_remove_group(&pm->hwmon->kobj, -				   &hwmon_fan_rpm_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_default_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_temp_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_pwm_fan_attrgroup); +		sysfs_remove_group(&pm->hwmon->kobj, &hwmon_fan_rpm_attrgroup);  		hwmon_device_unregister(pm->hwmon);  	} diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c index a6237c9cbbc..7f0e6c3f37d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -55,9 +55,9 @@  /* offsets in shared sync bo of various structures */  #define EVO_SYNC(c, o) ((c) * 0x0100 + (o)) -#define EVO_MAST_NTFY     EVO_SYNC(  0, 0x00) -#define EVO_FLIP_SEM0(c)  EVO_SYNC((c), 0x00) -#define EVO_FLIP_SEM1(c)  EVO_SYNC((c), 0x10) +#define EVO_MAST_NTFY     EVO_SYNC(      0, 0x00) +#define EVO_FLIP_SEM0(c)  EVO_SYNC((c) + 1, 0x00) +#define EVO_FLIP_SEM1(c)  EVO_SYNC((c) + 1, 0x10)  #define EVO_CORE_HANDLE      (0xd1500000)  #define EVO_CHAN_HANDLE(t,i) (0xd15c0000 | (((t) & 0x00ff) << 8) | (i)) @@ -341,10 +341,8 @@ struct nv50_curs {  struct nv50_sync {  	struct nv50_dmac base; -	struct { -		u32 offset; -		u16 value; -	} sem; +	u32 addr; +	u32 data;  };  struct nv50_ovly { @@ -471,13 +469,33 @@ nv50_display_crtc_sema(struct drm_device *dev, int crtc)  	return nv50_disp(dev)->sync;  } +struct nv50_display_flip { +	struct nv50_disp *disp; +	struct nv50_sync *chan; +}; + +static bool +nv50_display_flip_wait(void *data) +{ +	struct nv50_display_flip *flip = data; +	if (nouveau_bo_rd32(flip->disp->sync, flip->chan->addr / 4) == +					      flip->chan->data); +		return true; +	usleep_range(1, 2); +	return false; +} +  void  nv50_display_flip_stop(struct drm_crtc *crtc)  { -	struct nv50_sync *sync = nv50_sync(crtc); +	struct nouveau_device *device = nouveau_dev(crtc->dev); +	struct nv50_display_flip flip = { +		.disp = nv50_disp(crtc->dev), +		.chan = nv50_sync(crtc), +	};  	u32 *push; -	push = evo_wait(sync, 8); +	push = evo_wait(flip.chan, 8);  	if (push) {  		evo_mthd(push, 0x0084, 1);  		evo_data(push, 0x00000000); @@ -487,8 +505,10 @@ nv50_display_flip_stop(struct drm_crtc *crtc)  		evo_data(push, 0x00000000);  		evo_mthd(push, 0x0080, 1);  		evo_data(push, 0x00000000); -		evo_kick(push, sync); +		evo_kick(push, flip.chan);  	} + +	nv_wait_cb(device, nv50_display_flip_wait, &flip);  }  int @@ -496,73 +516,78 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  		       struct nouveau_channel *chan, u32 swap_interval)  {  	struct nouveau_framebuffer *nv_fb = nouveau_framebuffer(fb); -	struct nv50_disp *disp = nv50_disp(crtc->dev);  	struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);  	struct nv50_sync *sync = nv50_sync(crtc); +	int head = nv_crtc->index, ret;  	u32 *push; -	int ret;  	swap_interval <<= 4;  	if (swap_interval == 0)  		swap_interval |= 0x100; +	if (chan == NULL) +		evo_sync(crtc->dev);  	push = evo_wait(sync, 128);  	if (unlikely(push == NULL))  		return -EBUSY; -	/* synchronise with the rendering channel, if necessary */ -	if (likely(chan)) { -		ret = RING_SPACE(chan, 10); +	if (chan && nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) { +		ret = RING_SPACE(chan, 8);  		if (ret)  			return ret; -		if (nv_mclass(chan->object) < NV84_CHANNEL_IND_CLASS) { -			BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); -			OUT_RING  (chan, NvEvoSema0 + nv_crtc->index); -			OUT_RING  (chan, sync->sem.offset); -			BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); -			OUT_RING  (chan, 0xf00d0000 | sync->sem.value); -			BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); -			OUT_RING  (chan, sync->sem.offset ^ 0x10); -			OUT_RING  (chan, 0x74b1e000); -			BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); -			OUT_RING  (chan, NvSema); -		} else -		if (nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { -			u64 offset = nv84_fence_crtc(chan, nv_crtc->index); -			offset += sync->sem.offset; +		BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 2); +		OUT_RING  (chan, NvEvoSema0 + head); +		OUT_RING  (chan, sync->addr ^ 0x10); +		BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_RELEASE, 1); +		OUT_RING  (chan, sync->data + 1); +		BEGIN_NV04(chan, 0, NV11_SUBCHAN_SEMAPHORE_OFFSET, 2); +		OUT_RING  (chan, sync->addr); +		OUT_RING  (chan, sync->data); +	} else +	if (chan && nv_mclass(chan->object) < NVC0_CHANNEL_IND_CLASS) { +		u64 addr = nv84_fence_crtc(chan, head) + sync->addr; +		ret = RING_SPACE(chan, 12); +		if (ret) +			return ret; -			BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); -			OUT_RING  (chan, upper_32_bits(offset)); -			OUT_RING  (chan, lower_32_bits(offset)); -			OUT_RING  (chan, 0xf00d0000 | sync->sem.value); -			OUT_RING  (chan, 0x00000002); -			BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); -			OUT_RING  (chan, upper_32_bits(offset)); -			OUT_RING  (chan, lower_32_bits(offset ^ 0x10)); -			OUT_RING  (chan, 0x74b1e000); -			OUT_RING  (chan, 0x00000001); -		} else { -			u64 offset = nv84_fence_crtc(chan, nv_crtc->index); -			offset += sync->sem.offset; +		BEGIN_NV04(chan, 0, NV11_SUBCHAN_DMA_SEMAPHORE, 1); +		OUT_RING  (chan, chan->vram); +		BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); +		OUT_RING  (chan, upper_32_bits(addr ^ 0x10)); +		OUT_RING  (chan, lower_32_bits(addr ^ 0x10)); +		OUT_RING  (chan, sync->data + 1); +		OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG); +		BEGIN_NV04(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); +		OUT_RING  (chan, upper_32_bits(addr)); +		OUT_RING  (chan, lower_32_bits(addr)); +		OUT_RING  (chan, sync->data); +		OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL); +	} else +	if (chan) { +		u64 addr = nv84_fence_crtc(chan, head) + sync->addr; +		ret = RING_SPACE(chan, 10); +		if (ret) +			return ret; -			BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); -			OUT_RING  (chan, upper_32_bits(offset)); -			OUT_RING  (chan, lower_32_bits(offset)); -			OUT_RING  (chan, 0xf00d0000 | sync->sem.value); -			OUT_RING  (chan, 0x00001002); -			BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); -			OUT_RING  (chan, upper_32_bits(offset)); -			OUT_RING  (chan, lower_32_bits(offset ^ 0x10)); -			OUT_RING  (chan, 0x74b1e000); -			OUT_RING  (chan, 0x00001001); -		} +		BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); +		OUT_RING  (chan, upper_32_bits(addr ^ 0x10)); +		OUT_RING  (chan, lower_32_bits(addr ^ 0x10)); +		OUT_RING  (chan, sync->data + 1); +		OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_WRITE_LONG | +				 NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); +		BEGIN_NVC0(chan, 0, NV84_SUBCHAN_SEMAPHORE_ADDRESS_HIGH, 4); +		OUT_RING  (chan, upper_32_bits(addr)); +		OUT_RING  (chan, lower_32_bits(addr)); +		OUT_RING  (chan, sync->data); +		OUT_RING  (chan, NV84_SUBCHAN_SEMAPHORE_TRIGGER_ACQUIRE_EQUAL | +				 NVC0_SUBCHAN_SEMAPHORE_TRIGGER_YIELD); +	} +	if (chan) { +		sync->addr ^= 0x10; +		sync->data++;  		FIRE_RING (chan); -	} else { -		nouveau_bo_wr32(disp->sync, sync->sem.offset / 4, -				0xf00d0000 | sync->sem.value); -		evo_sync(crtc->dev);  	}  	/* queue the flip */ @@ -575,9 +600,9 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  		evo_data(push, 0x40000000);  	}  	evo_mthd(push, 0x0088, 4); -	evo_data(push, sync->sem.offset); -	evo_data(push, 0xf00d0000 | sync->sem.value); -	evo_data(push, 0x74b1e000); +	evo_data(push, sync->addr); +	evo_data(push, sync->data++); +	evo_data(push, sync->data);  	evo_data(push, NvEvoSync);  	evo_mthd(push, 0x00a0, 2);  	evo_data(push, 0x00000000); @@ -605,9 +630,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  	evo_mthd(push, 0x0080, 1);  	evo_data(push, 0x00000000);  	evo_kick(push, sync); - -	sync->sem.offset ^= 0x10; -	sync->sem.value++;  	return 0;  } @@ -1379,7 +1401,8 @@ nv50_crtc_create(struct drm_device *dev, struct nouveau_object *core, int index)  	if (ret)  		goto out; -	head->sync.sem.offset = EVO_SYNC(1 + index, 0x00); +	head->sync.addr = EVO_FLIP_SEM0(index); +	head->sync.data = 0x00000000;  	/* allocate overlay resources */  	ret = nv50_pioc_create(disp->core, NV50_DISP_OIMM_CLASS, index, @@ -2112,15 +2135,23 @@ nv50_display_fini(struct drm_device *dev)  int  nv50_display_init(struct drm_device *dev)  { -	u32 *push = evo_wait(nv50_mast(dev), 32); -	if (push) { -		evo_mthd(push, 0x0088, 1); -		evo_data(push, NvEvoSync); -		evo_kick(push, nv50_mast(dev)); -		return 0; +	struct nv50_disp *disp = nv50_disp(dev); +	struct drm_crtc *crtc; +	u32 *push; + +	push = evo_wait(nv50_mast(dev), 32); +	if (!push) +		return -EBUSY; + +	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { +		struct nv50_sync *sync = nv50_sync(crtc); +		nouveau_bo_wr32(disp->sync, sync->addr / 4, sync->data);  	} -	return -EBUSY; +	evo_mthd(push, 0x0088, 1); +	evo_data(push, NvEvoSync); +	evo_kick(push, nv50_mast(dev)); +	return 0;  }  void @@ -2245,6 +2276,7 @@ nv50_display_create(struct drm_device *dev)  			NV_WARN(drm, "failed to create encoder %d/%d/%d: %d\n",  				     dcbe->location, dcbe->type,  				     ffs(dcbe->or) - 1, ret); +			ret = 0;  		}  	}  |