diff options
| author | David S. Miller <davem@davemloft.net> | 2013-03-27 13:52:49 -0400 | 
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-03-27 13:52:49 -0400 | 
| commit | e2a553dbf18a5177fdebe29495c32a8e7fd3a4db (patch) | |
| tree | 5ccb3d498325a7aaf93f49549eca03cb7861ca1c /drivers/gpu | |
| parent | 7559d97993ae7d552c96313155286f372cf4cf7c (diff) | |
| parent | a8c45289f215e137825bf9630d0abb41c1dc41ff (diff) | |
| download | olio-linux-3.10-e2a553dbf18a5177fdebe29495c32a8e7fd3a4db.tar.xz olio-linux-3.10-e2a553dbf18a5177fdebe29495c32a8e7fd3a4db.zip  | |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
Conflicts:
	include/net/ipip.h
The changes made to ipip.h in 'net' were already included
in 'net-next' before that header was moved to another location.
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/gpu')
29 files changed, 577 insertions, 163 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index c194f4e680a..e2acfdbf7d3 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1634,7 +1634,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,  	unsigned vblank = (pt->vactive_vblank_hi & 0xf) << 8 | pt->vblank_lo;  	unsigned hsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc0) << 2 | pt->hsync_offset_lo;  	unsigned hsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x30) << 4 | pt->hsync_pulse_width_lo; -	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) >> 2 | pt->vsync_offset_pulse_width_lo >> 4; +	unsigned vsync_offset = (pt->hsync_vsync_offset_pulse_width_hi & 0xc) << 2 | pt->vsync_offset_pulse_width_lo >> 4;  	unsigned vsync_pulse_width = (pt->hsync_vsync_offset_pulse_width_hi & 0x3) << 4 | (pt->vsync_offset_pulse_width_lo & 0xf);  	/* ignore tiny modes */ @@ -1715,6 +1715,7 @@ set_size:  	}  	mode->type = DRM_MODE_TYPE_DRIVER; +	mode->vrefresh = drm_mode_vrefresh(mode);  	drm_mode_set_name(mode);  	return mode; diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimd.c b/drivers/gpu/drm/exynos/exynos_drm_fimd.c index 36493ce71f9..98cc14725ba 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimd.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimd.c @@ -38,11 +38,12 @@  /* position control register for hardware window 0, 2 ~ 4.*/  #define VIDOSD_A(win)		(VIDOSD_BASE + 0x00 + (win) * 16)  #define VIDOSD_B(win)		(VIDOSD_BASE + 0x04 + (win) * 16) -/* size control register for hardware window 0. */ -#define VIDOSD_C_SIZE_W0	(VIDOSD_BASE + 0x08) -/* alpha control register for hardware window 1 ~ 4. */ -#define VIDOSD_C(win)		(VIDOSD_BASE + 0x18 + (win) * 16) -/* size control register for hardware window 1 ~ 4. */ +/* + * size control register for hardware windows 0 and alpha control register + * for hardware windows 1 ~ 4 + */ +#define VIDOSD_C(win)		(VIDOSD_BASE + 0x08 + (win) * 16) +/* size control register for hardware windows 1 ~ 2. */  #define VIDOSD_D(win)		(VIDOSD_BASE + 0x0C + (win) * 16)  #define VIDWx_BUF_START(win, buf)	(VIDW_BUF_START(buf) + (win) * 8) @@ -50,9 +51,9 @@  #define VIDWx_BUF_SIZE(win, buf)	(VIDW_BUF_SIZE(buf) + (win) * 4)  /* color key control register for hardware window 1 ~ 4. */ -#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + (x * 8)) +#define WKEYCON0_BASE(x)		((WKEYCON0 + 0x140) + ((x - 1) * 8))  /* color key value register for hardware window 1 ~ 4. */ -#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + (x * 8)) +#define WKEYCON1_BASE(x)		((WKEYCON1 + 0x140) + ((x - 1) * 8))  /* FIMD has totally five hardware windows. */  #define WINDOWS_NR	5 @@ -109,9 +110,9 @@ struct fimd_context {  #ifdef CONFIG_OF  static const struct of_device_id fimd_driver_dt_match[] = { -	{ .compatible = "samsung,exynos4-fimd", +	{ .compatible = "samsung,exynos4210-fimd",  	  .data = &exynos4_fimd_driver_data }, -	{ .compatible = "samsung,exynos5-fimd", +	{ .compatible = "samsung,exynos5250-fimd",  	  .data = &exynos5_fimd_driver_data },  	{},  }; @@ -581,7 +582,7 @@ static void fimd_win_commit(struct device *dev, int zpos)  	if (win != 3 && win != 4) {  		u32 offset = VIDOSD_D(win);  		if (win == 0) -			offset = VIDOSD_C_SIZE_W0; +			offset = VIDOSD_C(win);  		val = win_data->ovl_width * win_data->ovl_height;  		writel(val, ctx->regs + offset); diff --git a/drivers/gpu/drm/exynos/exynos_drm_g2d.c b/drivers/gpu/drm/exynos/exynos_drm_g2d.c index 3b0da0378ac..47a493c8a71 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_g2d.c +++ b/drivers/gpu/drm/exynos/exynos_drm_g2d.c @@ -48,8 +48,14 @@  /* registers for base address */  #define G2D_SRC_BASE_ADDR		0x0304 +#define G2D_SRC_COLOR_MODE		0x030C +#define G2D_SRC_LEFT_TOP		0x0310 +#define G2D_SRC_RIGHT_BOTTOM		0x0314  #define G2D_SRC_PLANE2_BASE_ADDR	0x0318  #define G2D_DST_BASE_ADDR		0x0404 +#define G2D_DST_COLOR_MODE		0x040C +#define G2D_DST_LEFT_TOP		0x0410 +#define G2D_DST_RIGHT_BOTTOM		0x0414  #define G2D_DST_PLANE2_BASE_ADDR	0x0418  #define G2D_PAT_BASE_ADDR		0x0500  #define G2D_MSK_BASE_ADDR		0x0520 @@ -82,7 +88,7 @@  #define G2D_DMA_LIST_DONE_COUNT_OFFSET	17  /* G2D_DMA_HOLD_CMD */ -#define G2D_USET_HOLD			(1 << 2) +#define G2D_USER_HOLD			(1 << 2)  #define G2D_LIST_HOLD			(1 << 1)  #define G2D_BITBLT_HOLD			(1 << 0) @@ -91,13 +97,27 @@  #define G2D_START_NHOLT			(1 << 1)  #define G2D_START_BITBLT		(1 << 0) +/* buffer color format */ +#define G2D_FMT_XRGB8888		0 +#define G2D_FMT_ARGB8888		1 +#define G2D_FMT_RGB565			2 +#define G2D_FMT_XRGB1555		3 +#define G2D_FMT_ARGB1555		4 +#define G2D_FMT_XRGB4444		5 +#define G2D_FMT_ARGB4444		6 +#define G2D_FMT_PACKED_RGB888		7 +#define G2D_FMT_A8			11 +#define G2D_FMT_L8			12 + +/* buffer valid length */ +#define G2D_LEN_MIN			1 +#define G2D_LEN_MAX			8000 +  #define G2D_CMDLIST_SIZE		(PAGE_SIZE / 4)  #define G2D_CMDLIST_NUM			64  #define G2D_CMDLIST_POOL_SIZE		(G2D_CMDLIST_SIZE * G2D_CMDLIST_NUM)  #define G2D_CMDLIST_DATA_NUM		(G2D_CMDLIST_SIZE / sizeof(u32) - 2) -#define MAX_BUF_ADDR_NR			6 -  /* maximum buffer pool size of userptr is 64MB as default */  #define MAX_POOL		(64 * 1024 * 1024) @@ -106,6 +126,17 @@ enum {  	BUF_TYPE_USERPTR,  }; +enum g2d_reg_type { +	REG_TYPE_NONE = -1, +	REG_TYPE_SRC, +	REG_TYPE_SRC_PLANE2, +	REG_TYPE_DST, +	REG_TYPE_DST_PLANE2, +	REG_TYPE_PAT, +	REG_TYPE_MSK, +	MAX_REG_TYPE_NR +}; +  /* cmdlist data structure */  struct g2d_cmdlist {  	u32		head; @@ -113,6 +144,42 @@ struct g2d_cmdlist {  	u32		last;	/* last data offset */  }; +/* + * A structure of buffer description + * + * @format: color format + * @left_x: the x coordinates of left top corner + * @top_y: the y coordinates of left top corner + * @right_x: the x coordinates of right bottom corner + * @bottom_y: the y coordinates of right bottom corner + * + */ +struct g2d_buf_desc { +	unsigned int	format; +	unsigned int	left_x; +	unsigned int	top_y; +	unsigned int	right_x; +	unsigned int	bottom_y; +}; + +/* + * A structure of buffer information + * + * @map_nr: manages the number of mapped buffers + * @reg_types: stores regitster type in the order of requested command + * @handles: stores buffer handle in its reg_type position + * @types: stores buffer type in its reg_type position + * @descs: stores buffer description in its reg_type position + * + */ +struct g2d_buf_info { +	unsigned int		map_nr; +	enum g2d_reg_type	reg_types[MAX_REG_TYPE_NR]; +	unsigned long		handles[MAX_REG_TYPE_NR]; +	unsigned int		types[MAX_REG_TYPE_NR]; +	struct g2d_buf_desc	descs[MAX_REG_TYPE_NR]; +}; +  struct drm_exynos_pending_g2d_event {  	struct drm_pending_event	base;  	struct drm_exynos_g2d_event	event; @@ -131,14 +198,11 @@ struct g2d_cmdlist_userptr {  	bool			in_pool;  	bool			out_of_list;  }; -  struct g2d_cmdlist_node {  	struct list_head	list;  	struct g2d_cmdlist	*cmdlist; -	unsigned int		map_nr; -	unsigned long		handles[MAX_BUF_ADDR_NR]; -	unsigned int		obj_type[MAX_BUF_ADDR_NR];  	dma_addr_t		dma_addr; +	struct g2d_buf_info	buf_info;  	struct drm_exynos_pending_g2d_event	*event;  }; @@ -188,6 +252,7 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)  	struct exynos_drm_subdrv *subdrv = &g2d->subdrv;  	int nr;  	int ret; +	struct g2d_buf_info *buf_info;  	init_dma_attrs(&g2d->cmdlist_dma_attrs);  	dma_set_attr(DMA_ATTR_WRITE_COMBINE, &g2d->cmdlist_dma_attrs); @@ -209,11 +274,17 @@ static int g2d_init_cmdlist(struct g2d_data *g2d)  	}  	for (nr = 0; nr < G2D_CMDLIST_NUM; nr++) { +		unsigned int i; +  		node[nr].cmdlist =  			g2d->cmdlist_pool_virt + nr * G2D_CMDLIST_SIZE;  		node[nr].dma_addr =  			g2d->cmdlist_pool + nr * G2D_CMDLIST_SIZE; +		buf_info = &node[nr].buf_info; +		for (i = 0; i < MAX_REG_TYPE_NR; i++) +			buf_info->reg_types[i] = REG_TYPE_NONE; +  		list_add_tail(&node[nr].list, &g2d->free_cmdlist);  	} @@ -450,7 +521,7 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,  						DMA_BIDIRECTIONAL);  	if (ret < 0) {  		DRM_ERROR("failed to map sgt with dma region.\n"); -		goto err_free_sgt; +		goto err_sg_free_table;  	}  	g2d_userptr->dma_addr = sgt->sgl[0].dma_address; @@ -467,8 +538,10 @@ static dma_addr_t *g2d_userptr_get_dma_addr(struct drm_device *drm_dev,  	return &g2d_userptr->dma_addr; -err_free_sgt: +err_sg_free_table:  	sg_free_table(sgt); + +err_free_sgt:  	kfree(sgt);  	sgt = NULL; @@ -506,36 +579,172 @@ static void g2d_userptr_free_all(struct drm_device *drm_dev,  	g2d->current_pool = 0;  } +static enum g2d_reg_type g2d_get_reg_type(int reg_offset) +{ +	enum g2d_reg_type reg_type; + +	switch (reg_offset) { +	case G2D_SRC_BASE_ADDR: +	case G2D_SRC_COLOR_MODE: +	case G2D_SRC_LEFT_TOP: +	case G2D_SRC_RIGHT_BOTTOM: +		reg_type = REG_TYPE_SRC; +		break; +	case G2D_SRC_PLANE2_BASE_ADDR: +		reg_type = REG_TYPE_SRC_PLANE2; +		break; +	case G2D_DST_BASE_ADDR: +	case G2D_DST_COLOR_MODE: +	case G2D_DST_LEFT_TOP: +	case G2D_DST_RIGHT_BOTTOM: +		reg_type = REG_TYPE_DST; +		break; +	case G2D_DST_PLANE2_BASE_ADDR: +		reg_type = REG_TYPE_DST_PLANE2; +		break; +	case G2D_PAT_BASE_ADDR: +		reg_type = REG_TYPE_PAT; +		break; +	case G2D_MSK_BASE_ADDR: +		reg_type = REG_TYPE_MSK; +		break; +	default: +		reg_type = REG_TYPE_NONE; +		DRM_ERROR("Unknown register offset![%d]\n", reg_offset); +		break; +	}; + +	return reg_type; +} + +static unsigned long g2d_get_buf_bpp(unsigned int format) +{ +	unsigned long bpp; + +	switch (format) { +	case G2D_FMT_XRGB8888: +	case G2D_FMT_ARGB8888: +		bpp = 4; +		break; +	case G2D_FMT_RGB565: +	case G2D_FMT_XRGB1555: +	case G2D_FMT_ARGB1555: +	case G2D_FMT_XRGB4444: +	case G2D_FMT_ARGB4444: +		bpp = 2; +		break; +	case G2D_FMT_PACKED_RGB888: +		bpp = 3; +		break; +	default: +		bpp = 1; +		break; +	} + +	return bpp; +} + +static bool g2d_check_buf_desc_is_valid(struct g2d_buf_desc *buf_desc, +						enum g2d_reg_type reg_type, +						unsigned long size) +{ +	unsigned int width, height; +	unsigned long area; + +	/* +	 * check source and destination buffers only. +	 * so the others are always valid. +	 */ +	if (reg_type != REG_TYPE_SRC && reg_type != REG_TYPE_DST) +		return true; + +	width = buf_desc->right_x - buf_desc->left_x; +	if (width < G2D_LEN_MIN || width > G2D_LEN_MAX) { +		DRM_ERROR("width[%u] is out of range!\n", width); +		return false; +	} + +	height = buf_desc->bottom_y - buf_desc->top_y; +	if (height < G2D_LEN_MIN || height > G2D_LEN_MAX) { +		DRM_ERROR("height[%u] is out of range!\n", height); +		return false; +	} + +	area = (unsigned long)width * (unsigned long)height * +					g2d_get_buf_bpp(buf_desc->format); +	if (area > size) { +		DRM_ERROR("area[%lu] is out of range[%lu]!\n", area, size); +		return false; +	} + +	return true; +} +  static int g2d_map_cmdlist_gem(struct g2d_data *g2d,  				struct g2d_cmdlist_node *node,  				struct drm_device *drm_dev,  				struct drm_file *file)  {  	struct g2d_cmdlist *cmdlist = node->cmdlist; +	struct g2d_buf_info *buf_info = &node->buf_info;  	int offset; +	int ret;  	int i; -	for (i = 0; i < node->map_nr; i++) { +	for (i = 0; i < buf_info->map_nr; i++) { +		struct g2d_buf_desc *buf_desc; +		enum g2d_reg_type reg_type; +		int reg_pos;  		unsigned long handle;  		dma_addr_t *addr; -		offset = cmdlist->last - (i * 2 + 1); -		handle = cmdlist->data[offset]; +		reg_pos = cmdlist->last - 2 * (i + 1); + +		offset = cmdlist->data[reg_pos]; +		handle = cmdlist->data[reg_pos + 1]; + +		reg_type = g2d_get_reg_type(offset); +		if (reg_type == REG_TYPE_NONE) { +			ret = -EFAULT; +			goto err; +		} + +		buf_desc = &buf_info->descs[reg_type]; + +		if (buf_info->types[reg_type] == BUF_TYPE_GEM) { +			unsigned long size; + +			size = exynos_drm_gem_get_size(drm_dev, handle, file); +			if (!size) { +				ret = -EFAULT; +				goto err; +			} + +			if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, +									size)) { +				ret = -EFAULT; +				goto err; +			} -		if (node->obj_type[i] == BUF_TYPE_GEM) {  			addr = exynos_drm_gem_get_dma_addr(drm_dev, handle,  								file);  			if (IS_ERR(addr)) { -				node->map_nr = i; -				return -EFAULT; +				ret = -EFAULT; +				goto err;  			}  		} else {  			struct drm_exynos_g2d_userptr g2d_userptr;  			if (copy_from_user(&g2d_userptr, (void __user *)handle,  				sizeof(struct drm_exynos_g2d_userptr))) { -				node->map_nr = i; -				return -EFAULT; +				ret = -EFAULT; +				goto err; +			} + +			if (!g2d_check_buf_desc_is_valid(buf_desc, reg_type, +							g2d_userptr.size)) { +				ret = -EFAULT; +				goto err;  			}  			addr = g2d_userptr_get_dma_addr(drm_dev, @@ -544,16 +753,21 @@ static int g2d_map_cmdlist_gem(struct g2d_data *g2d,  							file,  							&handle);  			if (IS_ERR(addr)) { -				node->map_nr = i; -				return -EFAULT; +				ret = -EFAULT; +				goto err;  			}  		} -		cmdlist->data[offset] = *addr; -		node->handles[i] = handle; +		cmdlist->data[reg_pos + 1] = *addr; +		buf_info->reg_types[i] = reg_type; +		buf_info->handles[reg_type] = handle;  	}  	return 0; + +err: +	buf_info->map_nr = i; +	return ret;  }  static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d, @@ -561,22 +775,33 @@ static void g2d_unmap_cmdlist_gem(struct g2d_data *g2d,  				  struct drm_file *filp)  {  	struct exynos_drm_subdrv *subdrv = &g2d->subdrv; +	struct g2d_buf_info *buf_info = &node->buf_info;  	int i; -	for (i = 0; i < node->map_nr; i++) { -		unsigned long handle = node->handles[i]; +	for (i = 0; i < buf_info->map_nr; i++) { +		struct g2d_buf_desc *buf_desc; +		enum g2d_reg_type reg_type; +		unsigned long handle; -		if (node->obj_type[i] == BUF_TYPE_GEM) +		reg_type = buf_info->reg_types[i]; + +		buf_desc = &buf_info->descs[reg_type]; +		handle = buf_info->handles[reg_type]; + +		if (buf_info->types[reg_type] == BUF_TYPE_GEM)  			exynos_drm_gem_put_dma_addr(subdrv->drm_dev, handle,  							filp);  		else  			g2d_userptr_put_dma_addr(subdrv->drm_dev, handle,  							false); -		node->handles[i] = 0; +		buf_info->reg_types[i] = REG_TYPE_NONE; +		buf_info->handles[reg_type] = 0; +		buf_info->types[reg_type] = 0; +		memset(buf_desc, 0x00, sizeof(*buf_desc));  	} -	node->map_nr = 0; +	buf_info->map_nr = 0;  }  static void g2d_dma_start(struct g2d_data *g2d, @@ -589,10 +814,6 @@ static void g2d_dma_start(struct g2d_data *g2d,  	pm_runtime_get_sync(g2d->dev);  	clk_enable(g2d->gate_clk); -	/* interrupt enable */ -	writel_relaxed(G2D_INTEN_ACF | G2D_INTEN_UCF | G2D_INTEN_GCF, -			g2d->regs + G2D_INTEN); -  	writel_relaxed(node->dma_addr, g2d->regs + G2D_DMA_SFR_BASE_ADDR);  	writel_relaxed(G2D_DMA_START, g2d->regs + G2D_DMA_COMMAND);  } @@ -643,7 +864,6 @@ static void g2d_runqueue_worker(struct work_struct *work)  	struct g2d_data *g2d = container_of(work, struct g2d_data,  					    runqueue_work); -  	mutex_lock(&g2d->runqueue_mutex);  	clk_disable(g2d->gate_clk);  	pm_runtime_put_sync(g2d->dev); @@ -724,20 +944,14 @@ static int g2d_check_reg_offset(struct device *dev,  	int i;  	for (i = 0; i < nr; i++) { -		index = cmdlist->last - 2 * (i + 1); +		struct g2d_buf_info *buf_info = &node->buf_info; +		struct g2d_buf_desc *buf_desc; +		enum g2d_reg_type reg_type; +		unsigned long value; -		if (for_addr) { -			/* check userptr buffer type. */ -			reg_offset = (cmdlist->data[index] & -					~0x7fffffff) >> 31; -			if (reg_offset) { -				node->obj_type[i] = BUF_TYPE_USERPTR; -				cmdlist->data[index] &= ~G2D_BUF_USERPTR; -			} -		} +		index = cmdlist->last - 2 * (i + 1);  		reg_offset = cmdlist->data[index] & ~0xfffff000; -  		if (reg_offset < G2D_VALID_START || reg_offset > G2D_VALID_END)  			goto err;  		if (reg_offset % 4) @@ -753,8 +967,60 @@ static int g2d_check_reg_offset(struct device *dev,  			if (!for_addr)  				goto err; -			if (node->obj_type[i] != BUF_TYPE_USERPTR) -				node->obj_type[i] = BUF_TYPE_GEM; +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			/* check userptr buffer type. */ +			if ((cmdlist->data[index] & ~0x7fffffff) >> 31) { +				buf_info->types[reg_type] = BUF_TYPE_USERPTR; +				cmdlist->data[index] &= ~G2D_BUF_USERPTR; +			} else +				buf_info->types[reg_type] = BUF_TYPE_GEM; +			break; +		case G2D_SRC_COLOR_MODE: +		case G2D_DST_COLOR_MODE: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			buf_desc = &buf_info->descs[reg_type]; +			value = cmdlist->data[index + 1]; + +			buf_desc->format = value & 0xf; +			break; +		case G2D_SRC_LEFT_TOP: +		case G2D_DST_LEFT_TOP: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			buf_desc = &buf_info->descs[reg_type]; +			value = cmdlist->data[index + 1]; + +			buf_desc->left_x = value & 0x1fff; +			buf_desc->top_y = (value & 0x1fff0000) >> 16; +			break; +		case G2D_SRC_RIGHT_BOTTOM: +		case G2D_DST_RIGHT_BOTTOM: +			if (for_addr) +				goto err; + +			reg_type = g2d_get_reg_type(reg_offset); +			if (reg_type == REG_TYPE_NONE) +				goto err; + +			buf_desc = &buf_info->descs[reg_type]; +			value = cmdlist->data[index + 1]; + +			buf_desc->right_x = value & 0x1fff; +			buf_desc->bottom_y = (value & 0x1fff0000) >> 16;  			break;  		default:  			if (for_addr) @@ -860,9 +1126,23 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,  	cmdlist->data[cmdlist->last++] = G2D_SRC_BASE_ADDR;  	cmdlist->data[cmdlist->last++] = 0; +	/* +	 * 'LIST_HOLD' command should be set to the DMA_HOLD_CMD_REG +	 * and GCF bit should be set to INTEN register if user wants +	 * G2D interrupt event once current command list execution is +	 * finished. +	 * Otherwise only ACF bit should be set to INTEN register so +	 * that one interrupt is occured after all command lists +	 * have been completed. +	 */  	if (node->event) { +		cmdlist->data[cmdlist->last++] = G2D_INTEN; +		cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF | G2D_INTEN_GCF;  		cmdlist->data[cmdlist->last++] = G2D_DMA_HOLD_CMD;  		cmdlist->data[cmdlist->last++] = G2D_LIST_HOLD; +	} else { +		cmdlist->data[cmdlist->last++] = G2D_INTEN; +		cmdlist->data[cmdlist->last++] = G2D_INTEN_ACF;  	}  	/* Check size of cmdlist: last 2 is about G2D_BITBLT_START */ @@ -887,7 +1167,7 @@ int exynos_g2d_set_cmdlist_ioctl(struct drm_device *drm_dev, void *data,  	if (ret < 0)  		goto err_free_event; -	node->map_nr = req->cmd_buf_nr; +	node->buf_info.map_nr = req->cmd_buf_nr;  	if (req->cmd_buf_nr) {  		struct drm_exynos_g2d_cmd *cmd_buf; diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.c b/drivers/gpu/drm/exynos/exynos_drm_gem.c index 67e17ce112b..0e6fe000578 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.c @@ -164,6 +164,27 @@ out:  	exynos_gem_obj = NULL;  } +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, +						unsigned int gem_handle, +						struct drm_file *file_priv) +{ +	struct exynos_drm_gem_obj *exynos_gem_obj; +	struct drm_gem_object *obj; + +	obj = drm_gem_object_lookup(dev, file_priv, gem_handle); +	if (!obj) { +		DRM_ERROR("failed to lookup gem object.\n"); +		return 0; +	} + +	exynos_gem_obj = to_exynos_gem_obj(obj); + +	drm_gem_object_unreference_unlocked(obj); + +	return exynos_gem_obj->buffer->size; +} + +  struct exynos_drm_gem_obj *exynos_drm_gem_init(struct drm_device *dev,  						      unsigned long size)  { diff --git a/drivers/gpu/drm/exynos/exynos_drm_gem.h b/drivers/gpu/drm/exynos/exynos_drm_gem.h index 35ebac47dc2..468766bee45 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gem.h +++ b/drivers/gpu/drm/exynos/exynos_drm_gem.h @@ -130,6 +130,11 @@ int exynos_drm_gem_userptr_ioctl(struct drm_device *dev, void *data,  int exynos_drm_gem_get_ioctl(struct drm_device *dev, void *data,  				      struct drm_file *file_priv); +/* get buffer size to gem handle. */ +unsigned long exynos_drm_gem_get_size(struct drm_device *dev, +						unsigned int gem_handle, +						struct drm_file *file_priv); +  /* initialize gem object. */  int exynos_drm_gem_init_object(struct drm_gem_object *obj); diff --git a/drivers/gpu/drm/exynos/exynos_drm_vidi.c b/drivers/gpu/drm/exynos/exynos_drm_vidi.c index 13ccbd4bcfa..9504b0cd825 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_vidi.c +++ b/drivers/gpu/drm/exynos/exynos_drm_vidi.c @@ -117,13 +117,12 @@ static struct edid *vidi_get_edid(struct device *dev,  	}  	edid_len = (1 + ctx->raw_edid->extensions) * EDID_LENGTH; -	edid = kzalloc(edid_len, GFP_KERNEL); +	edid = kmemdup(ctx->raw_edid, edid_len, GFP_KERNEL);  	if (!edid) {  		DRM_DEBUG_KMS("failed to allocate edid\n");  		return ERR_PTR(-ENOMEM);  	} -	memcpy(edid, ctx->raw_edid, edid_len);  	return edid;  } @@ -563,12 +562,11 @@ int vidi_connection_ioctl(struct drm_device *drm_dev, void *data,  			return -EINVAL;  		}  		edid_len = (1 + raw_edid->extensions) * EDID_LENGTH; -		ctx->raw_edid = kzalloc(edid_len, GFP_KERNEL); +		ctx->raw_edid = kmemdup(raw_edid, edid_len, GFP_KERNEL);  		if (!ctx->raw_edid) {  			DRM_DEBUG_KMS("failed to allocate raw_edid.\n");  			return -ENOMEM;  		} -		memcpy(ctx->raw_edid, raw_edid, edid_len);  	} else {  		/*  		 * with connection = 0, free raw_edid diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index e919aba29b3..2f4f72f0704 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -818,7 +818,7 @@ static void mixer_win_disable(void *ctx, int win)  	mixer_ctx->win_data[win].enabled = false;  } -int mixer_check_timing(void *ctx, struct fb_videomode *timing) +static int mixer_check_timing(void *ctx, struct fb_videomode *timing)  {  	struct mixer_context *mixer_ctx = ctx;  	u32 w, h; diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c index aae31489c89..7299ea45dd0 100644 --- a/drivers/gpu/drm/i915/i915_debugfs.c +++ b/drivers/gpu/drm/i915/i915_debugfs.c @@ -103,7 +103,7 @@ static const char *cache_level_str(int type)  static void  describe_obj(struct seq_file *m, struct drm_i915_gem_object *obj)  { -	seq_printf(m, "%p: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s", +	seq_printf(m, "%pK: %s%s %8zdKiB %02x %02x %d %d %d%s%s%s",  		   &obj->base,  		   get_pin_flag(obj),  		   get_tiling_flag(obj), diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 0a8eceb7590..e9b57893db2 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c @@ -125,6 +125,11 @@ MODULE_PARM_DESC(preliminary_hw_support,  		"Enable Haswell and ValleyView Support. "  		"(default: false)"); +int i915_disable_power_well __read_mostly = 0; +module_param_named(disable_power_well, i915_disable_power_well, int, 0600); +MODULE_PARM_DESC(disable_power_well, +		 "Disable the power well when possible (default: false)"); +  static struct drm_driver driver;  extern int intel_agp_enabled; diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index e95337c9745..01769e2a995 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h @@ -1398,6 +1398,7 @@ extern int i915_enable_fbc __read_mostly;  extern bool i915_enable_hangcheck __read_mostly;  extern int i915_enable_ppgtt __read_mostly;  extern unsigned int i915_preliminary_hw_support __read_mostly; +extern int i915_disable_power_well __read_mostly;  extern int i915_suspend(struct drm_device *dev, pm_message_t state);  extern int i915_resume(struct drm_device *dev); diff --git a/drivers/gpu/drm/i915/i915_gem_execbuffer.c b/drivers/gpu/drm/i915/i915_gem_execbuffer.c index 2f2daebd0ee..3b11ab0fbc9 100644 --- a/drivers/gpu/drm/i915/i915_gem_execbuffer.c +++ b/drivers/gpu/drm/i915/i915_gem_execbuffer.c @@ -732,6 +732,8 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,  		   int count)  {  	int i; +	int relocs_total = 0; +	int relocs_max = INT_MAX / sizeof(struct drm_i915_gem_relocation_entry);  	for (i = 0; i < count; i++) {  		char __user *ptr = (char __user *)(uintptr_t)exec[i].relocs_ptr; @@ -740,10 +742,13 @@ validate_exec_list(struct drm_i915_gem_exec_object2 *exec,  		if (exec[i].flags & __EXEC_OBJECT_UNKNOWN_FLAGS)  			return -EINVAL; -		/* First check for malicious input causing overflow */ -		if (exec[i].relocation_count > -		    INT_MAX / sizeof(struct drm_i915_gem_relocation_entry)) +		/* First check for malicious input causing overflow in +		 * the worst case where we need to allocate the entire +		 * relocation tree as a single array. +		 */ +		if (exec[i].relocation_count > relocs_max - relocs_total)  			return -EINVAL; +		relocs_total += exec[i].relocation_count;  		length = exec[i].relocation_count *  			sizeof(struct drm_i915_gem_relocation_entry); diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 287b42c9d1a..b20d50192fc 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -5771,6 +5771,11 @@ static int haswell_crtc_mode_set(struct drm_crtc *crtc,  		num_connectors++;  	} +	if (is_cpu_edp) +		intel_crtc->cpu_transcoder = TRANSCODER_EDP; +	else +		intel_crtc->cpu_transcoder = pipe; +  	/* We are not sure yet this won't happen. */  	WARN(!HAS_PCH_LPT(dev), "Unexpected PCH type %d\n",  	     INTEL_PCH_TYPE(dev)); @@ -5837,11 +5842,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,  	int pipe = intel_crtc->pipe;  	int ret; -	if (IS_HASWELL(dev) && intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) -		intel_crtc->cpu_transcoder = TRANSCODER_EDP; -	else -		intel_crtc->cpu_transcoder = pipe; -  	drm_vblank_pre_modeset(dev, pipe);  	ret = dev_priv->display.crtc_mode_set(crtc, mode, adjusted_mode, diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6f728e5ee79..d7d4afe0134 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -820,6 +820,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,  	struct intel_link_m_n m_n;  	int pipe = intel_crtc->pipe;  	enum transcoder cpu_transcoder = intel_crtc->cpu_transcoder; +	int target_clock;  	/*  	 * Find the lane count in the intel_encoder private @@ -835,13 +836,22 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,  		}  	} +	target_clock = mode->clock; +	for_each_encoder_on_crtc(dev, crtc, intel_encoder) { +		if (intel_encoder->type == INTEL_OUTPUT_EDP) { +			target_clock = intel_edp_target_clock(intel_encoder, +							      mode); +			break; +		} +	} +  	/*  	 * Compute the GMCH and Link ratios. The '3' here is  	 * the number of bytes_per_pixel post-LUT, which we always  	 * set up for 8-bits of R/G/B, or 3 bytes total.  	 */  	intel_link_compute_m_n(intel_crtc->bpp, lane_count, -			       mode->clock, adjusted_mode->clock, &m_n); +			       target_clock, adjusted_mode->clock, &m_n);  	if (IS_HASWELL(dev)) {  		I915_WRITE(PIPE_DATA_M1(cpu_transcoder), @@ -1930,7 +1940,7 @@ intel_dp_start_link_train(struct intel_dp *intel_dp)  		for (i = 0; i < intel_dp->lane_count; i++)  			if ((intel_dp->train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)  				break; -		if (i == intel_dp->lane_count && voltage_tries == 5) { +		if (i == intel_dp->lane_count) {  			++loop_tries;  			if (loop_tries == 5) {  				DRM_DEBUG_KMS("too many full retries, give up\n"); diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c index acf8aec9ada..ef4744e1bf0 100644 --- a/drivers/gpu/drm/i915/intel_i2c.c +++ b/drivers/gpu/drm/i915/intel_i2c.c @@ -203,7 +203,13 @@ intel_gpio_setup(struct intel_gmbus *bus, u32 pin)  	algo->data = bus;  } -#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 4) +/* + * gmbus on gen4 seems to be able to generate legacy interrupts even when in MSI + * mode. This results in spurious interrupt warnings if the legacy irq no. is + * shared with another device. The kernel then disables that interrupt source + * and so prevents the other device from working properly. + */ +#define HAS_GMBUS_IRQ(dev) (INTEL_INFO(dev)->gen >= 5)  static int  gmbus_wait_hw_status(struct drm_i915_private *dev_priv,  		     u32 gmbus2_status, @@ -214,6 +220,9 @@ gmbus_wait_hw_status(struct drm_i915_private *dev_priv,  	u32 gmbus2 = 0;  	DEFINE_WAIT(wait); +	if (!HAS_GMBUS_IRQ(dev_priv->dev)) +		gmbus4_irq_en = 0; +  	/* Important: The hw handles only the first bit, so set only one! Since  	 * we also need to check for NAKs besides the hw ready/idle signal, we  	 * need to wake up periodically and check that ourselves. */ diff --git a/drivers/gpu/drm/i915/intel_panel.c b/drivers/gpu/drm/i915/intel_panel.c index a3730e0289e..bee8cb6108a 100644 --- a/drivers/gpu/drm/i915/intel_panel.c +++ b/drivers/gpu/drm/i915/intel_panel.c @@ -321,9 +321,6 @@ void intel_panel_enable_backlight(struct drm_device *dev,  	if (dev_priv->backlight_level == 0)  		dev_priv->backlight_level = intel_panel_get_max_backlight(dev); -	dev_priv->backlight_enabled = true; -	intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); -  	if (INTEL_INFO(dev)->gen >= 4) {  		uint32_t reg, tmp; @@ -359,12 +356,12 @@ void intel_panel_enable_backlight(struct drm_device *dev,  	}  set_level: -	/* Check the current backlight level and try to set again if it's zero. -	 * On some machines, BLC_PWM_CPU_CTL is cleared to zero automatically -	 * when BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1 are written. +	/* Call below after setting BLC_PWM_CPU_CTL2 and BLC_PWM_PCH_CTL1. +	 * BLC_PWM_CPU_CTL may be cleared to zero automatically when these +	 * registers are set.  	 */ -	if (!intel_panel_get_backlight(dev)) -		intel_panel_actually_set_backlight(dev, dev_priv->backlight_level); +	dev_priv->backlight_enabled = true; +	intel_panel_actually_set_backlight(dev, dev_priv->backlight_level);  }  static void intel_panel_init_backlight(struct drm_device *dev) diff --git a/drivers/gpu/drm/i915/intel_pm.c b/drivers/gpu/drm/i915/intel_pm.c index a1794c6df1b..adca00783e6 100644 --- a/drivers/gpu/drm/i915/intel_pm.c +++ b/drivers/gpu/drm/i915/intel_pm.c @@ -4079,6 +4079,9 @@ void intel_set_power_well(struct drm_device *dev, bool enable)  	if (!IS_HASWELL(dev))  		return; +	if (!i915_disable_power_well && !enable) +		return; +  	tmp = I915_READ(HSW_PWR_WELL_DRIVER);  	is_enabled = tmp & HSW_PWR_WELL_STATE;  	enable_requested = tmp & HSW_PWR_WELL_ENABLE; diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c index a274b9906ef..fe22bb780e1 100644 --- a/drivers/gpu/drm/mgag200/mgag200_mode.c +++ b/drivers/gpu/drm/mgag200/mgag200_mode.c @@ -382,19 +382,19 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock)  	m = n = p = 0;  	vcomax = 800000;  	vcomin = 400000; -	pllreffreq = 3333; +	pllreffreq = 33333;  	delta = 0xffffffff;  	permitteddelta = clock * 5 / 1000; -	for (testp = 16; testp > 0; testp--) { +	for (testp = 16; testp > 0; testp >>= 1) {  		if (clock * testp > vcomax)  			continue;  		if (clock * testp < vcomin)  			continue;  		for (testm = 1; testm < 33; testm++) { -			for (testn = 1; testn < 257; testn++) { +			for (testn = 17; testn < 257; testn++) {  				computed = (pllreffreq * testn) /  					(testm * testp);  				if (computed > clock) @@ -404,11 +404,11 @@ static int mga_g200eh_set_plls(struct mga_device *mdev, long clock)  				if (tmpdelta < delta) {  					delta = tmpdelta;  					n = testn - 1; -					m = (testm - 1) | ((n >> 1) & 0x80); +					m = (testm - 1);  					p = testp - 1;  				}  				if ((clock * testp) >= 600000) -					p |= 80; +					p |= 0x80;  			}  		}  	} 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/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/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_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 2db57990f65..7f0e6c3f37d 100644 --- a/drivers/gpu/drm/nouveau/nv50_display.c +++ b/drivers/gpu/drm/nouveau/nv50_display.c @@ -524,6 +524,8 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  	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)) @@ -586,8 +588,6 @@ nv50_display_flip_next(struct drm_crtc *crtc, struct drm_framebuffer *fb,  		sync->addr ^= 0x10;  		sync->data++;  		FIRE_RING (chan); -	} else { -		evo_sync(crtc->dev);  	}  	/* queue the flip */ diff --git a/drivers/gpu/drm/radeon/ni.c b/drivers/gpu/drm/radeon/ni.c index d4c633e1286..27769e724b6 100644 --- a/drivers/gpu/drm/radeon/ni.c +++ b/drivers/gpu/drm/radeon/ni.c @@ -468,13 +468,19 @@ static void cayman_gpu_init(struct radeon_device *rdev)  		    (rdev->pdev->device == 0x9907) ||  		    (rdev->pdev->device == 0x9908) ||  		    (rdev->pdev->device == 0x9909) || +		    (rdev->pdev->device == 0x990B) || +		    (rdev->pdev->device == 0x990C) || +		    (rdev->pdev->device == 0x990F) ||  		    (rdev->pdev->device == 0x9910) || -		    (rdev->pdev->device == 0x9917)) { +		    (rdev->pdev->device == 0x9917) || +		    (rdev->pdev->device == 0x9999)) {  			rdev->config.cayman.max_simds_per_se = 6;  			rdev->config.cayman.max_backends_per_se = 2;  		} else if ((rdev->pdev->device == 0x9903) ||  			   (rdev->pdev->device == 0x9904) ||  			   (rdev->pdev->device == 0x990A) || +			   (rdev->pdev->device == 0x990D) || +			   (rdev->pdev->device == 0x990E) ||  			   (rdev->pdev->device == 0x9913) ||  			   (rdev->pdev->device == 0x9918)) {  			rdev->config.cayman.max_simds_per_se = 4; @@ -483,6 +489,9 @@ static void cayman_gpu_init(struct radeon_device *rdev)  			   (rdev->pdev->device == 0x9990) ||  			   (rdev->pdev->device == 0x9991) ||  			   (rdev->pdev->device == 0x9994) || +			   (rdev->pdev->device == 0x9995) || +			   (rdev->pdev->device == 0x9996) || +			   (rdev->pdev->device == 0x999A) ||  			   (rdev->pdev->device == 0x99A0)) {  			rdev->config.cayman.max_simds_per_se = 3;  			rdev->config.cayman.max_backends_per_se = 1; @@ -616,11 +625,22 @@ static void cayman_gpu_init(struct radeon_device *rdev)  	WREG32(DMA_TILING_CONFIG + DMA0_REGISTER_OFFSET, gb_addr_config);  	WREG32(DMA_TILING_CONFIG + DMA1_REGISTER_OFFSET, gb_addr_config); -	tmp = gb_addr_config & NUM_PIPES_MASK; -	tmp = r6xx_remap_render_backend(rdev, tmp, -					rdev->config.cayman.max_backends_per_se * -					rdev->config.cayman.max_shader_engines, -					CAYMAN_MAX_BACKENDS, disabled_rb_mask); +	if ((rdev->config.cayman.max_backends_per_se == 1) && +	    (rdev->flags & RADEON_IS_IGP)) { +		if ((disabled_rb_mask & 3) == 1) { +			/* RB0 disabled, RB1 enabled */ +			tmp = 0x11111111; +		} else { +			/* RB1 disabled, RB0 enabled */ +			tmp = 0x00000000; +		} +	} else { +		tmp = gb_addr_config & NUM_PIPES_MASK; +		tmp = r6xx_remap_render_backend(rdev, tmp, +						rdev->config.cayman.max_backends_per_se * +						rdev->config.cayman.max_shader_engines, +						CAYMAN_MAX_BACKENDS, disabled_rb_mask); +	}  	WREG32(GB_BACKEND_MAP, tmp);  	cgts_tcc_disable = 0xffff0000; @@ -1771,6 +1791,7 @@ int cayman_resume(struct radeon_device *rdev)  int cayman_suspend(struct radeon_device *rdev)  {  	r600_audio_fini(rdev); +	radeon_vm_manager_fini(rdev);  	cayman_cp_enable(rdev, false);  	cayman_dma_stop(rdev);  	evergreen_irq_suspend(rdev); diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c index bedda9caadd..6e05a2e75a4 100644 --- a/drivers/gpu/drm/radeon/radeon_benchmark.c +++ b/drivers/gpu/drm/radeon/radeon_benchmark.c @@ -122,10 +122,7 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,  		goto out_cleanup;  	} -	/* r100 doesn't have dma engine so skip the test */ -	/* also, VRAM-to-VRAM test doesn't make much sense for DMA */ -	/* skip it as well if domains are the same */ -	if ((rdev->asic->copy.dma) && (sdomain != ddomain)) { +	if (rdev->asic->copy.dma) {  		time = radeon_benchmark_do_move(rdev, size, saddr, daddr,  						RADEON_BENCHMARK_COPY_DMA, n);  		if (time < 0) @@ -135,13 +132,15 @@ static void radeon_benchmark_move(struct radeon_device *rdev, unsigned size,  						     sdomain, ddomain, "dma");  	} -	time = radeon_benchmark_do_move(rdev, size, saddr, daddr, -					RADEON_BENCHMARK_COPY_BLIT, n); -	if (time < 0) -		goto out_cleanup; -	if (time > 0) -		radeon_benchmark_log_results(n, size, time, -					     sdomain, ddomain, "blit"); +	if (rdev->asic->copy.blit) { +		time = radeon_benchmark_do_move(rdev, size, saddr, daddr, +						RADEON_BENCHMARK_COPY_BLIT, n); +		if (time < 0) +			goto out_cleanup; +		if (time > 0) +			radeon_benchmark_log_results(n, size, time, +						     sdomain, ddomain, "blit"); +	}  out_cleanup:  	if (sobj) { diff --git a/drivers/gpu/drm/radeon/si.c b/drivers/gpu/drm/radeon/si.c index 9128120da04..bafbe321695 100644 --- a/drivers/gpu/drm/radeon/si.c +++ b/drivers/gpu/drm/radeon/si.c @@ -4469,6 +4469,7 @@ int si_resume(struct radeon_device *rdev)  int si_suspend(struct radeon_device *rdev)  { +	radeon_vm_manager_fini(rdev);  	si_cp_enable(rdev, false);  	cayman_dma_stop(rdev);  	si_irq_suspend(rdev);  |