diff options
Diffstat (limited to 'drivers')
26 files changed, 1065 insertions, 66 deletions
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index d823e631951..b1bc1ea182b 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig @@ -30,11 +30,12 @@ config DRM_NOUVEAU_DEBUG  	  via debugfs.  menu "I2C encoder or helper chips" -     depends on DRM +     depends on DRM && I2C  config DRM_I2C_CH7006  	tristate "Chrontel ch7006 TV encoder" -	default m if DRM_NOUVEAU +	depends on DRM_NOUVEAU +	default m  	help  	  Support for Chrontel ch7006 and similar TV encoders, found  	  on some nVidia video cards. diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c index 320a14bceb9..aa2dfbc3e35 100644 --- a/drivers/gpu/drm/nouveau/nouveau_bo.c +++ b/drivers/gpu/drm/nouveau/nouveau_bo.c @@ -311,8 +311,10 @@ nouveau_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev)  	struct drm_device *dev = dev_priv->dev;  	switch (dev_priv->gart_info.type) { +#if __OS_HAS_AGP  	case NOUVEAU_GART_AGP:  		return ttm_agp_backend_init(bdev, dev->agp->bridge); +#endif  	case NOUVEAU_GART_SGDMA:  		return nouveau_sgdma_init_ttm(dev);  	default: diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c index 0cff7eb3690..dacac9a0842 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fence.c +++ b/drivers/gpu/drm/nouveau/nouveau_fence.c @@ -205,7 +205,7 @@ nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)  			schedule_timeout(1);  		if (intr && signal_pending(current)) { -			ret = -ERESTART; +			ret = -ERESTARTSYS;  			break;  		}  	} diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c index 11f831f0ddc..18fd8ac9fca 100644 --- a/drivers/gpu/drm/nouveau/nouveau_gem.c +++ b/drivers/gpu/drm/nouveau/nouveau_gem.c @@ -342,8 +342,6 @@ retry:  			}  			ret = ttm_bo_wait_cpu(&nvbo->bo, false); -			if (ret == -ERESTART) -				ret = -EAGAIN;  			if (ret)  				return ret;  			goto retry; @@ -915,8 +913,6 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,  			goto out;  		ret = ttm_bo_wait_cpu(&nvbo->bo, no_wait); -		if (ret == -ERESTART) -			ret = -EAGAIN;  		if (ret)  			goto out;  	} @@ -925,9 +921,6 @@ nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,  		ret = ttm_bo_wait(&nvbo->bo, false, false, no_wait);  	} else {  		ret = ttm_bo_synccpu_write_grab(&nvbo->bo, no_wait); -		if (ret == -ERESTART) -			ret = -EAGAIN; -		else  		if (ret == 0)  			nvbo->cpu_filp = file_priv;  	} diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c index 02755712ed3..5158a12f784 100644 --- a/drivers/gpu/drm/nouveau/nouveau_mem.c +++ b/drivers/gpu/drm/nouveau/nouveau_mem.c @@ -407,6 +407,7 @@ uint64_t nouveau_mem_fb_amount(struct drm_device *dev)  	return 0;  } +#if __OS_HAS_AGP  static void nouveau_mem_reset_agp(struct drm_device *dev)  {  	uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable; @@ -432,10 +433,12 @@ static void nouveau_mem_reset_agp(struct drm_device *dev)  	nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19);  	nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1);  } +#endif  int  nouveau_mem_init_agp(struct drm_device *dev)  { +#if __OS_HAS_AGP  	struct drm_nouveau_private *dev_priv = dev->dev_private;  	struct drm_agp_info info;  	struct drm_agp_mode mode; @@ -471,6 +474,7 @@ nouveau_mem_init_agp(struct drm_device *dev)  	dev_priv->gart_info.type	= NOUVEAU_GART_AGP;  	dev_priv->gart_info.aper_base	= info.aperture_base;  	dev_priv->gart_info.aper_size	= info.aperture_size; +#endif  	return 0;  } diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c index d3e0a2a6acf..7e8547cb583 100644 --- a/drivers/gpu/drm/nouveau/nv40_graph.c +++ b/drivers/gpu/drm/nouveau/nv40_graph.c @@ -252,8 +252,9 @@ nv40_grctx_init(struct drm_device *dev)  		memcpy(pgraph->ctxprog, fw->data, fw->size);  		cp = pgraph->ctxprog; -		if (cp->signature != 0x5043564e || cp->version != 0 || -		    cp->length != ((fw->size - 7) / 4)) { +		if (le32_to_cpu(cp->signature) != 0x5043564e || +		    cp->version != 0 || +		    le16_to_cpu(cp->length) != ((fw->size - 7) / 4)) {  			NV_ERROR(dev, "ctxprog invalid\n");  			release_firmware(fw);  			nv40_grctx_fini(dev); @@ -281,8 +282,9 @@ nv40_grctx_init(struct drm_device *dev)  		memcpy(pgraph->ctxvals, fw->data, fw->size);  		cv = (void *)pgraph->ctxvals; -		if (cv->signature != 0x5643564e || cv->version != 0 || -		    cv->length != ((fw->size - 9) / 8)) { +		if (le32_to_cpu(cv->signature) != 0x5643564e || +		    cv->version != 0 || +		    le32_to_cpu(cv->length) != ((fw->size - 9) / 8)) {  			NV_ERROR(dev, "ctxvals invalid\n");  			release_firmware(fw);  			nv40_grctx_fini(dev); @@ -294,8 +296,9 @@ nv40_grctx_init(struct drm_device *dev)  	cp = pgraph->ctxprog;  	nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0); -	for (i = 0; i < cp->length; i++) -		nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp->data[i]); +	for (i = 0; i < le16_to_cpu(cp->length); i++) +		nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, +			le32_to_cpu(cp->data[i]));  	pgraph->accel_blocked = false;  	return 0; @@ -329,8 +332,9 @@ nv40_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx)  	if (!cv)  		return; -	for (i = 0; i < cv->length; i++) -		nv_wo32(dev, ctx, cv->data[i].offset, cv->data[i].value); +	for (i = 0; i < le32_to_cpu(cv->length); i++) +		nv_wo32(dev, ctx, le32_to_cpu(cv->data[i].offset), +			le32_to_cpu(cv->data[i].value));  }  /* diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index feb52eee431..b5f5fe75e6a 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile @@ -49,7 +49,7 @@ radeon-y += radeon_device.o radeon_kms.o \  	radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \  	rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \  	r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ -	r600_blit_kms.o radeon_pm.o atombios_dp.o +	r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o  radeon-$(CONFIG_COMPAT) += radeon_ioc32.o diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index 824cc6480a0..84e5df766d3 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -1374,7 +1374,6 @@ static int r100_packet0_check(struct radeon_cs_parser *p,  		case RADEON_TXFORMAT_ARGB4444:  		case RADEON_TXFORMAT_VYUY422:  		case RADEON_TXFORMAT_YVYU422: -		case RADEON_TXFORMAT_DXT1:  		case RADEON_TXFORMAT_SHADOW16:  		case RADEON_TXFORMAT_LDUDV655:  		case RADEON_TXFORMAT_DUDV88: @@ -1382,12 +1381,19 @@ static int r100_packet0_check(struct radeon_cs_parser *p,  			break;  		case RADEON_TXFORMAT_ARGB8888:  		case RADEON_TXFORMAT_RGBA8888: -		case RADEON_TXFORMAT_DXT23: -		case RADEON_TXFORMAT_DXT45:  		case RADEON_TXFORMAT_SHADOW32:  		case RADEON_TXFORMAT_LDUDUV8888:  			track->textures[i].cpp = 4;  			break; +		case RADEON_TXFORMAT_DXT1: +			track->textures[i].cpp = 1; +			track->textures[i].compress_format = R100_TRACK_COMP_DXT1; +			break; +		case RADEON_TXFORMAT_DXT23: +		case RADEON_TXFORMAT_DXT45: +			track->textures[i].cpp = 1; +			track->textures[i].compress_format = R100_TRACK_COMP_DXT35; +			break;  		}  		track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf);  		track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf); @@ -2731,6 +2737,7 @@ static inline void r100_cs_track_texture_print(struct r100_cs_track_texture *t)  	DRM_ERROR("coordinate type            %d\n", t->tex_coord_type);  	DRM_ERROR("width round to power of 2  %d\n", t->roundup_w);  	DRM_ERROR("height round to power of 2 %d\n", t->roundup_h); +	DRM_ERROR("compress format            %d\n", t->compress_format);  }  static int r100_cs_track_cube(struct radeon_device *rdev, @@ -2760,6 +2767,36 @@ static int r100_cs_track_cube(struct radeon_device *rdev,  	return 0;  } +static int r100_track_compress_size(int compress_format, int w, int h) +{ +	int block_width, block_height, block_bytes; +	int wblocks, hblocks; +	int min_wblocks; +	int sz; + +	block_width = 4; +	block_height = 4; + +	switch (compress_format) { +	case R100_TRACK_COMP_DXT1: +		block_bytes = 8; +		min_wblocks = 4; +		break; +	default: +	case R100_TRACK_COMP_DXT35: +		block_bytes = 16; +		min_wblocks = 2; +		break; +	} + +	hblocks = (h + block_height - 1) / block_height; +	wblocks = (w + block_width - 1) / block_width; +	if (wblocks < min_wblocks) +		wblocks = min_wblocks; +	sz = wblocks * hblocks * block_bytes; +	return sz; +} +  static int r100_cs_track_texture_check(struct radeon_device *rdev,  				       struct r100_cs_track *track)  { @@ -2797,9 +2834,15 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev,  			h = h / (1 << i);  			if (track->textures[u].roundup_h)  				h = roundup_pow_of_two(h); -			size += w * h; +			if (track->textures[u].compress_format) { + +				size += r100_track_compress_size(track->textures[u].compress_format, w, h); +				/* compressed textures are block based */ +			} else +				size += w * h;  		}  		size *= track->textures[u].cpp; +  		switch (track->textures[u].tex_coord_type) {  		case 0:  			break; @@ -2967,6 +3010,7 @@ void r100_cs_track_clear(struct radeon_device *rdev, struct r100_cs_track *track  		track->arrays[i].esize = 0x7F;  	}  	for (i = 0; i < track->num_texture; i++) { +		track->textures[i].compress_format = R100_TRACK_COMP_NONE;  		track->textures[i].pitch = 16536;  		track->textures[i].width = 16536;  		track->textures[i].height = 16536; @@ -3399,6 +3443,8 @@ int r100_init(struct radeon_device *rdev)  	r100_errata(rdev);  	/* Initialize clocks */  	radeon_get_clock_info(rdev->ddev); +	/* Initialize power management */ +	radeon_pm_init(rdev);  	/* Get vram informations */  	r100_vram_info(rdev);  	/* Initialize memory controller (also test AGP) */ diff --git a/drivers/gpu/drm/radeon/r100_track.h b/drivers/gpu/drm/radeon/r100_track.h index ca50903dd2b..7188c3778ee 100644 --- a/drivers/gpu/drm/radeon/r100_track.h +++ b/drivers/gpu/drm/radeon/r100_track.h @@ -28,6 +28,10 @@ struct r100_cs_cube_info {  	unsigned		height;  }; +#define R100_TRACK_COMP_NONE   0 +#define R100_TRACK_COMP_DXT1   1 +#define R100_TRACK_COMP_DXT35  2 +  struct r100_cs_track_texture {  	struct radeon_bo	*robj;  	struct r100_cs_cube_info cube_info[5]; /* info for 5 non-primary faces */ @@ -44,6 +48,7 @@ struct r100_cs_track_texture {  	bool			enabled;  	bool			roundup_w;  	bool			roundup_h; +	unsigned                compress_format;  };  struct r100_cs_track_limits { diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c index eb740fc3549..20942127c46 100644 --- a/drivers/gpu/drm/radeon/r200.c +++ b/drivers/gpu/drm/radeon/r200.c @@ -401,7 +401,6 @@ int r200_packet0_check(struct radeon_cs_parser *p,  		case R200_TXFORMAT_Y8:  			track->textures[i].cpp = 1;  			break; -		case R200_TXFORMAT_DXT1:  		case R200_TXFORMAT_AI88:  		case R200_TXFORMAT_ARGB1555:  		case R200_TXFORMAT_RGB565: @@ -418,9 +417,16 @@ int r200_packet0_check(struct radeon_cs_parser *p,  		case R200_TXFORMAT_ABGR8888:  		case R200_TXFORMAT_BGR111110:  		case R200_TXFORMAT_LDVDU8888: +			track->textures[i].cpp = 4; +			break; +		case R200_TXFORMAT_DXT1: +			track->textures[i].cpp = 1; +			track->textures[i].compress_format = R100_TRACK_COMP_DXT1; +			break;  		case R200_TXFORMAT_DXT23:  		case R200_TXFORMAT_DXT45: -			track->textures[i].cpp = 4; +			track->textures[i].cpp = 1; +			track->textures[i].compress_format = R100_TRACK_COMP_DXT1;  			break;  		}  		track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf); diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 83378c39d0e..83490c2b506 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -686,7 +686,15 @@ static int r300_packet0_check(struct radeon_cs_parser *p,  			r100_cs_dump_packet(p, pkt);  			return r;  		} -		ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset); + +		if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO) +			tile_flags |= R300_TXO_MACRO_TILE; +		if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO) +			tile_flags |= R300_TXO_MICRO_TILE; + +		tmp = idx_value + ((u32)reloc->lobj.gpu_offset); +		tmp |= tile_flags; +		ib[idx] = tmp;  		track->textures[i].robj = reloc->robj;  		break;  	/* Tracked registers */ @@ -852,7 +860,6 @@ static int r300_packet0_check(struct radeon_cs_parser *p,  		case R300_TX_FORMAT_Z6Y5X5:  		case R300_TX_FORMAT_W4Z4Y4X4:  		case R300_TX_FORMAT_W1Z5Y5X5: -		case R300_TX_FORMAT_DXT1:  		case R300_TX_FORMAT_D3DMFT_CxV8U8:  		case R300_TX_FORMAT_B8G8_B8G8:  		case R300_TX_FORMAT_G8R8_G8B8: @@ -866,8 +873,6 @@ static int r300_packet0_check(struct radeon_cs_parser *p,  		case 0x17:  		case R300_TX_FORMAT_FL_I32:  		case 0x1e: -		case R300_TX_FORMAT_DXT3: -		case R300_TX_FORMAT_DXT5:  			track->textures[i].cpp = 4;  			break;  		case R300_TX_FORMAT_W16Z16Y16X16: @@ -878,6 +883,15 @@ static int r300_packet0_check(struct radeon_cs_parser *p,  		case R300_TX_FORMAT_FL_R32G32B32A32:  			track->textures[i].cpp = 16;  			break; +		case R300_TX_FORMAT_DXT1: +			track->textures[i].cpp = 1; +			track->textures[i].compress_format = R100_TRACK_COMP_DXT1; +			break; +		case R300_TX_FORMAT_DXT3: +		case R300_TX_FORMAT_DXT5: +			track->textures[i].cpp = 1; +			track->textures[i].compress_format = R100_TRACK_COMP_DXT35; +			break;  		default:  			DRM_ERROR("Invalid texture format %u\n",  				  (idx_value & 0x1F)); @@ -1324,6 +1338,8 @@ int r300_init(struct radeon_device *rdev)  	r300_errata(rdev);  	/* Initialize clocks */  	radeon_get_clock_info(rdev->ddev); +	/* Initialize power management */ +	radeon_pm_init(rdev);  	/* Get vram informations */  	r300_vram_info(rdev);  	/* Initialize memory controller (also test AGP) */ diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c index f5cf874dc62..5c6058c6ddd 100644 --- a/drivers/gpu/drm/radeon/r600.c +++ b/drivers/gpu/drm/radeon/r600.c @@ -1863,6 +1863,14 @@ int r600_startup(struct radeon_device *rdev)  	}  	r600_gpu_init(rdev); +	if (!rdev->r600_blit.shader_obj) { +		r = r600_blit_init(rdev); +		if (r) { +			DRM_ERROR("radeon: failed blitter (%d).\n", r); +			return r; +		} +	} +  	r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);  	if (unlikely(r != 0))  		return r; @@ -2038,12 +2046,6 @@ int r600_init(struct radeon_device *rdev)  	if (r)  		return r; -	r = r600_blit_init(rdev); -	if (r) { -		DRM_ERROR("radeon: failed blitter (%d).\n", r); -		return r; -	} -  	rdev->accel_working = true;  	r = r600_startup(rdev);  	if (r) { @@ -2065,6 +2067,10 @@ int r600_init(struct radeon_device *rdev)  			rdev->accel_working = false;  		}  	} + +	r = r600_audio_init(rdev); +	if (r) +		return r; /* TODO error handling */  	return 0;  } @@ -2073,6 +2079,7 @@ void r600_fini(struct radeon_device *rdev)  	/* Suspend operations */  	r600_suspend(rdev); +	r600_audio_fini(rdev);  	r600_blit_fini(rdev);  	r600_irq_fini(rdev);  	radeon_irq_kms_fini(rdev); diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c new file mode 100644 index 00000000000..99e2c3891a7 --- /dev/null +++ b/drivers/gpu/drm/radeon/r600_audio.c @@ -0,0 +1,267 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Christian König. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + */ +#include "drmP.h" +#include "radeon.h" +#include "radeon_reg.h" +#include "atom.h" + +#define AUDIO_TIMER_INTERVALL 100 /* 1/10 sekund should be enough */ + +/* + * check if the chipset is supported + */ +static int r600_audio_chipset_supported(struct radeon_device *rdev) +{ +	return rdev->family >= CHIP_R600 +		|| rdev->family == CHIP_RS600 +		|| rdev->family == CHIP_RS690 +		|| rdev->family == CHIP_RS740; +} + +/* + * current number of channels + */ +static int r600_audio_channels(struct radeon_device *rdev) +{ +	return (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0x7) + 1; +} + +/* + * current bits per sample + */ +static int r600_audio_bits_per_sample(struct radeon_device *rdev) +{ +	uint32_t value = (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0xF0) >> 4; +	switch (value) { +	case 0x0: return  8; +	case 0x1: return 16; +	case 0x2: return 20; +	case 0x3: return 24; +	case 0x4: return 32; +	} + +	DRM_ERROR("Unknown bits per sample 0x%x using 16 instead.\n", (int)value); + +	return 16; +} + +/* + * current sampling rate in HZ + */ +static int r600_audio_rate(struct radeon_device *rdev) +{ +	uint32_t value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL); +	uint32_t result; + +	if (value & 0x4000) +		result = 44100; +	else +		result = 48000; + +	result *= ((value >> 11) & 0x7) + 1; +	result /= ((value >> 8) & 0x7) + 1; + +	return result; +} + +/* + * iec 60958 status bits + */ +static uint8_t r600_audio_status_bits(struct radeon_device *rdev) +{ +	return RREG32(R600_AUDIO_STATUS_BITS) & 0xff; +} + +/* + * iec 60958 category code + */ +static uint8_t r600_audio_category_code(struct radeon_device *rdev) +{ +	return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff; +} + +/* + * update all hdmi interfaces with current audio parameters + */ +static void r600_audio_update_hdmi(unsigned long param) +{ +	struct radeon_device *rdev = (struct radeon_device *)param; +	struct drm_device *dev = rdev->ddev; + +	int channels = r600_audio_channels(rdev); +	int rate = r600_audio_rate(rdev); +	int bps = r600_audio_bits_per_sample(rdev); +	uint8_t status_bits = r600_audio_status_bits(rdev); +	uint8_t category_code = r600_audio_category_code(rdev); + +	struct drm_encoder *encoder; +	int changes = 0; + +	changes |= channels != rdev->audio_channels; +	changes |= rate != rdev->audio_rate; +	changes |= bps != rdev->audio_bits_per_sample; +	changes |= status_bits != rdev->audio_status_bits; +	changes |= category_code != rdev->audio_category_code; + +	if (changes) { +		rdev->audio_channels = channels; +		rdev->audio_rate = rate; +		rdev->audio_bits_per_sample = bps; +		rdev->audio_status_bits = status_bits; +		rdev->audio_category_code = category_code; +	} + +	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { +		if (changes || r600_hdmi_buffer_status_changed(encoder)) +			r600_hdmi_update_audio_settings( +				encoder, channels, +				rate, bps, status_bits, +				category_code); +	} + +	mod_timer(&rdev->audio_timer, +		jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL)); +} + +/* + * initialize the audio vars and register the update timer + */ +int r600_audio_init(struct radeon_device *rdev) +{ +	if (!r600_audio_chipset_supported(rdev)) +		return 0; + +	DRM_INFO("%s audio support", radeon_audio ? "Enabling" : "Disabling"); +	WREG32_P(R600_AUDIO_ENABLE, radeon_audio ? 0x81000000 : 0x0, ~0x81000000); + +	rdev->audio_channels = -1; +	rdev->audio_rate = -1; +	rdev->audio_bits_per_sample = -1; +	rdev->audio_status_bits = 0; +	rdev->audio_category_code = 0; + +	setup_timer( +		&rdev->audio_timer, +		r600_audio_update_hdmi, +		(unsigned long)rdev); + +	mod_timer(&rdev->audio_timer, jiffies + 1); + +	return 0; +} + +/* + * determin how the encoders and audio interface is wired together + */ +int r600_audio_tmds_index(struct drm_encoder *encoder) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	struct drm_encoder *other; + +	switch (radeon_encoder->encoder_id) { +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: +		return 0; + +	case ENCODER_OBJECT_ID_INTERNAL_LVTM1: +		/* special case check if an TMDS1 is present */ +		list_for_each_entry(other, &dev->mode_config.encoder_list, head) { +			if (to_radeon_encoder(other)->encoder_id == +				ENCODER_OBJECT_ID_INTERNAL_TMDS1) +				return 1; +		} +		return 0; + +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: +		return 1; + +	default: +		DRM_ERROR("Unsupported encoder type 0x%02X\n", +			  radeon_encoder->encoder_id); +		return -1; +	} +} + +/* + * atach the audio codec to the clock source of the encoder + */ +void r600_audio_set_clock(struct drm_encoder *encoder, int clock) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int base_rate = 48000; + +	switch (radeon_encoder->encoder_id) { +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: +	case ENCODER_OBJECT_ID_INTERNAL_LVTM1: +		WREG32_P(R600_AUDIO_TIMING, 0, ~0x301); +		break; + +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: +		WREG32_P(R600_AUDIO_TIMING, 0x100, ~0x301); +		break; + +	default: +		DRM_ERROR("Unsupported encoder type 0x%02X\n", +			  radeon_encoder->encoder_id); +		return; +	} + +	switch (r600_audio_tmds_index(encoder)) { +	case 0: +		WREG32(R600_AUDIO_PLL1_MUL, base_rate*50); +		WREG32(R600_AUDIO_PLL1_DIV, clock*100); +		WREG32(R600_AUDIO_CLK_SRCSEL, 0); +		break; + +	case 1: +		WREG32(R600_AUDIO_PLL2_MUL, base_rate*50); +		WREG32(R600_AUDIO_PLL2_DIV, clock*100); +		WREG32(R600_AUDIO_CLK_SRCSEL, 1); +		break; +	} +} + +/* + * release the audio timer + * TODO: How to do this correctly on SMP systems? + */ +void r600_audio_fini(struct radeon_device *rdev) +{ +	if (!r600_audio_chipset_supported(rdev)) +		return; + +	WREG32_P(R600_AUDIO_ENABLE, 0x0, ~0x81000000); + +	del_timer(&rdev->audio_timer); +} diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c new file mode 100644 index 00000000000..fcc949df0e5 --- /dev/null +++ b/drivers/gpu/drm/radeon/r600_hdmi.c @@ -0,0 +1,506 @@ +/* + * Copyright 2008 Advanced Micro Devices, Inc. + * Copyright 2008 Red Hat Inc. + * Copyright 2009 Christian König. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + * Authors: Christian König + */ +#include "drmP.h" +#include "radeon_drm.h" +#include "radeon.h" +#include "atom.h" + +/* + * HDMI color format + */ +enum r600_hdmi_color_format { +	RGB = 0, +	YCC_422 = 1, +	YCC_444 = 2 +}; + +/* + * IEC60958 status bits + */ +enum r600_hdmi_iec_status_bits { +	AUDIO_STATUS_DIG_ENABLE   = 0x01, +	AUDIO_STATUS_V	    = 0x02, +	AUDIO_STATUS_VCFG	 = 0x04, +	AUDIO_STATUS_EMPHASIS     = 0x08, +	AUDIO_STATUS_COPYRIGHT    = 0x10, +	AUDIO_STATUS_NONAUDIO     = 0x20, +	AUDIO_STATUS_PROFESSIONAL = 0x40, +	AUDIO_STATUS_LEVEL	= 0x80 +}; + +struct { +	uint32_t Clock; + +	int N_32kHz; +	int CTS_32kHz; + +	int N_44_1kHz; +	int CTS_44_1kHz; + +	int N_48kHz; +	int CTS_48kHz; + +} r600_hdmi_ACR[] = { +    /*	     32kHz	  44.1kHz	48kHz    */ +    /* Clock      N     CTS      N     CTS      N     CTS */ +    {  25174,  4576,  28125,  7007,  31250,  6864,  28125 }, /*  25,20/1.001 MHz */ +    {  25200,  4096,  25200,  6272,  28000,  6144,  25200 }, /*  25.20       MHz */ +    {  27000,  4096,  27000,  6272,  30000,  6144,  27000 }, /*  27.00       MHz */ +    {  27027,  4096,  27027,  6272,  30030,  6144,  27027 }, /*  27.00*1.001 MHz */ +    {  54000,  4096,  54000,  6272,  60000,  6144,  54000 }, /*  54.00       MHz */ +    {  54054,  4096,  54054,  6272,  60060,  6144,  54054 }, /*  54.00*1.001 MHz */ +    {  74175, 11648, 210937, 17836, 234375, 11648, 140625 }, /*  74.25/1.001 MHz */ +    {  74250,  4096,  74250,  6272,  82500,  6144,  74250 }, /*  74.25       MHz */ +    { 148351, 11648, 421875,  8918, 234375,  5824, 140625 }, /* 148.50/1.001 MHz */ +    { 148500,  4096, 148500,  6272, 165000,  6144, 148500 }, /* 148.50       MHz */ +    {      0,  4096,      0,  6272,      0,  6144,      0 }  /* Other */ +}; + +/* + * calculate CTS value if it's not found in the table + */ +static void r600_hdmi_calc_CTS(uint32_t clock, int *CTS, int N, int freq) +{ +	if (*CTS == 0) +		*CTS = clock*N/(128*freq)*1000; +	DRM_DEBUG("Using ACR timing N=%d CTS=%d for frequency %d\n", +		  N, *CTS, freq); +} + +/* + * update the N and CTS parameters for a given pixel clock rate + */ +static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; +	int CTS; +	int N; +	int i; + +	for (i = 0; r600_hdmi_ACR[i].Clock != clock && r600_hdmi_ACR[i].Clock != 0; i++); + +	CTS = r600_hdmi_ACR[i].CTS_32kHz; +	N = r600_hdmi_ACR[i].N_32kHz; +	r600_hdmi_calc_CTS(clock, &CTS, N, 32000); +	WREG32(offset+R600_HDMI_32kHz_CTS, CTS << 12); +	WREG32(offset+R600_HDMI_32kHz_N, N); + +	CTS = r600_hdmi_ACR[i].CTS_44_1kHz; +	N = r600_hdmi_ACR[i].N_44_1kHz; +	r600_hdmi_calc_CTS(clock, &CTS, N, 44100); +	WREG32(offset+R600_HDMI_44_1kHz_CTS, CTS << 12); +	WREG32(offset+R600_HDMI_44_1kHz_N, N); + +	CTS = r600_hdmi_ACR[i].CTS_48kHz; +	N = r600_hdmi_ACR[i].N_48kHz; +	r600_hdmi_calc_CTS(clock, &CTS, N, 48000); +	WREG32(offset+R600_HDMI_48kHz_CTS, CTS << 12); +	WREG32(offset+R600_HDMI_48kHz_N, N); +} + +/* + * calculate the crc for a given info frame + */ +static void r600_hdmi_infoframe_checksum(uint8_t packetType, +					 uint8_t versionNumber, +					 uint8_t length, +					 uint8_t *frame) +{ +    int i; +    frame[0] = packetType + versionNumber + length; +    for (i = 1; i <= length; i++) +	frame[0] += frame[i]; +    frame[0] = 0x100 - frame[0]; +} + +/* + * build a HDMI Video Info Frame + */ +static void r600_hdmi_videoinfoframe( +	struct drm_encoder *encoder, +	enum r600_hdmi_color_format color_format, +	int active_information_present, +	uint8_t active_format_aspect_ratio, +	uint8_t scan_information, +	uint8_t colorimetry, +	uint8_t ex_colorimetry, +	uint8_t quantization, +	int ITC, +	uint8_t picture_aspect_ratio, +	uint8_t video_format_identification, +	uint8_t pixel_repetition, +	uint8_t non_uniform_picture_scaling, +	uint8_t bar_info_data_valid, +	uint16_t top_bar, +	uint16_t bottom_bar, +	uint16_t left_bar, +	uint16_t right_bar +) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + +	uint8_t frame[14]; + +	frame[0x0] = 0; +	frame[0x1] = +		(scan_information & 0x3) | +		((bar_info_data_valid & 0x3) << 2) | +		((active_information_present & 0x1) << 4) | +		((color_format & 0x3) << 5); +	frame[0x2] = +		(active_format_aspect_ratio & 0xF) | +		((picture_aspect_ratio & 0x3) << 4) | +		((colorimetry & 0x3) << 6); +	frame[0x3] = +		(non_uniform_picture_scaling & 0x3) | +		((quantization & 0x3) << 2) | +		((ex_colorimetry & 0x7) << 4) | +		((ITC & 0x1) << 7); +	frame[0x4] = (video_format_identification & 0x7F); +	frame[0x5] = (pixel_repetition & 0xF); +	frame[0x6] = (top_bar & 0xFF); +	frame[0x7] = (top_bar >> 8); +	frame[0x8] = (bottom_bar & 0xFF); +	frame[0x9] = (bottom_bar >> 8); +	frame[0xA] = (left_bar & 0xFF); +	frame[0xB] = (left_bar >> 8); +	frame[0xC] = (right_bar & 0xFF); +	frame[0xD] = (right_bar >> 8); + +	r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame); + +	WREG32(offset+R600_HDMI_VIDEOINFOFRAME_0, +		frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); +	WREG32(offset+R600_HDMI_VIDEOINFOFRAME_1, +		frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24)); +	WREG32(offset+R600_HDMI_VIDEOINFOFRAME_2, +		frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24)); +	WREG32(offset+R600_HDMI_VIDEOINFOFRAME_3, +		frame[0xC] | (frame[0xD] << 8)); +} + +/* + * build a Audio Info Frame + */ +static void r600_hdmi_audioinfoframe( +	struct drm_encoder *encoder, +	uint8_t channel_count, +	uint8_t coding_type, +	uint8_t sample_size, +	uint8_t sample_frequency, +	uint8_t format, +	uint8_t channel_allocation, +	uint8_t level_shift, +	int downmix_inhibit +) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + +	uint8_t frame[11]; + +	frame[0x0] = 0; +	frame[0x1] = (channel_count & 0x7) | ((coding_type & 0xF) << 4); +	frame[0x2] = (sample_size & 0x3) | ((sample_frequency & 0x7) << 2); +	frame[0x3] = format; +	frame[0x4] = channel_allocation; +	frame[0x5] = ((level_shift & 0xF) << 3) | ((downmix_inhibit & 0x1) << 7); +	frame[0x6] = 0; +	frame[0x7] = 0; +	frame[0x8] = 0; +	frame[0x9] = 0; +	frame[0xA] = 0; + +	r600_hdmi_infoframe_checksum(0x84, 0x01, 0x0A, frame); + +	WREG32(offset+R600_HDMI_AUDIOINFOFRAME_0, +		frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24)); +	WREG32(offset+R600_HDMI_AUDIOINFOFRAME_1, +		frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24)); +} + +/* + * test if audio buffer is filled enough to start playing + */ +static int r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + +	return (RREG32(offset+R600_HDMI_STATUS) & 0x10) != 0; +} + +/* + * have buffer status changed since last call? + */ +int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	int status, result; + +	if (!radeon_encoder->hdmi_offset) +		return 0; + +	status = r600_hdmi_is_audio_buffer_filled(encoder); +	result = radeon_encoder->hdmi_buffer_status != status; +	radeon_encoder->hdmi_buffer_status = status; + +	return result; +} + +/* + * write the audio workaround status to the hardware + */ +void r600_hdmi_audio_workaround(struct drm_encoder *encoder) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	uint32_t offset = radeon_encoder->hdmi_offset; + +	if (!offset) +		return; + +	if (r600_hdmi_is_audio_buffer_filled(encoder)) { +		/* disable audio workaround and start delivering of audio frames */ +		WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001); + +	} else if (radeon_encoder->hdmi_audio_workaround) { +		/* enable audio workaround and start delivering of audio frames */ +		WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001); + +	} else { +		/* disable audio workaround and stop delivering of audio frames */ +		WREG32_P(offset+R600_HDMI_CNTL, 0x00000000, ~0x00001001); +	} +} + + +/* + * update the info frames with the data from the current display mode + */ +void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + +	if (!offset) +		return; + +	r600_audio_set_clock(encoder, mode->clock); + +	WREG32(offset+R600_HDMI_UNKNOWN_0, 0x1000); +	WREG32(offset+R600_HDMI_UNKNOWN_1, 0x0); +	WREG32(offset+R600_HDMI_UNKNOWN_2, 0x1000); + +	r600_hdmi_update_ACR(encoder, mode->clock); + +	WREG32(offset+R600_HDMI_VIDEOCNTL, 0x13); + +	WREG32(offset+R600_HDMI_VERSION, 0x202); + +	r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0, +		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); + +	/* it's unknown what these bits do excatly, but it's indeed quite usefull for debugging */ +	WREG32(offset+R600_HDMI_AUDIO_DEBUG_0, 0x00FFFFFF); +	WREG32(offset+R600_HDMI_AUDIO_DEBUG_1, 0x007FFFFF); +	WREG32(offset+R600_HDMI_AUDIO_DEBUG_2, 0x00000001); +	WREG32(offset+R600_HDMI_AUDIO_DEBUG_3, 0x00000001); + +	r600_hdmi_audio_workaround(encoder); + +	/* audio packets per line, does anyone know how to calc this ? */ +	WREG32_P(offset+R600_HDMI_CNTL, 0x00040000, ~0x001F0000); + +	/* update? reset? don't realy know */ +	WREG32_P(offset+R600_HDMI_CNTL, 0x14000000, ~0x14000000); +} + +/* + * update settings with current parameters from audio engine + */ +void r600_hdmi_update_audio_settings(struct drm_encoder *encoder, +				     int channels, +				     int rate, +				     int bps, +				     uint8_t status_bits, +				     uint8_t category_code) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + +	uint32_t iec; + +	if (!offset) +		return; + +	DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n", +		 r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped", +		channels, rate, bps); +	DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n", +		  (int)status_bits, (int)category_code); + +	iec = 0; +	if (status_bits & AUDIO_STATUS_PROFESSIONAL) +		iec |= 1 << 0; +	if (status_bits & AUDIO_STATUS_NONAUDIO) +		iec |= 1 << 1; +	if (status_bits & AUDIO_STATUS_COPYRIGHT) +		iec |= 1 << 2; +	if (status_bits & AUDIO_STATUS_EMPHASIS) +		iec |= 1 << 3; + +	iec |= category_code << 8; + +	switch (rate) { +	case  32000: iec |= 0x3 << 24; break; +	case  44100: iec |= 0x0 << 24; break; +	case  88200: iec |= 0x8 << 24; break; +	case 176400: iec |= 0xc << 24; break; +	case  48000: iec |= 0x2 << 24; break; +	case  96000: iec |= 0xa << 24; break; +	case 192000: iec |= 0xe << 24; break; +	} + +	WREG32(offset+R600_HDMI_IEC60958_1, iec); + +	iec = 0; +	switch (bps) { +	case 16: iec |= 0x2; break; +	case 20: iec |= 0x3; break; +	case 24: iec |= 0xb; break; +	} +	if (status_bits & AUDIO_STATUS_V) +		iec |= 0x5 << 16; + +	WREG32_P(offset+R600_HDMI_IEC60958_2, iec, ~0x5000f); + +	/* 0x021 or 0x031 sets the audio frame length */ +	WREG32(offset+R600_HDMI_AUDIOCNTL, 0x31); +	r600_hdmi_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0); + +	r600_hdmi_audio_workaround(encoder); + +	/* update? reset? don't realy know */ +	WREG32_P(offset+R600_HDMI_CNTL, 0x04000000, ~0x04000000); +} + +/* + * enable/disable the HDMI engine + */ +void r600_hdmi_enable(struct drm_encoder *encoder, int enable) +{ +	struct drm_device *dev = encoder->dev; +	struct radeon_device *rdev = dev->dev_private; +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); +	uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset; + +	if (!offset) +		return; + +	DRM_DEBUG("%s HDMI interface @ 0x%04X\n", enable ? "Enabling" : "Disabling", offset); + +	/* some version of atombios ignore the enable HDMI flag +	 * so enabling/disabling HDMI was moved here for TMDS1+2 */ +	switch (radeon_encoder->encoder_id) { +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: +		WREG32_P(AVIVO_TMDSA_CNTL, enable ? 0x4 : 0x0, ~0x4); +		WREG32(offset+R600_HDMI_ENABLE, enable ? 0x101 : 0x0); +		break; + +	case ENCODER_OBJECT_ID_INTERNAL_LVTM1: +		WREG32_P(AVIVO_LVTMA_CNTL, enable ? 0x4 : 0x0, ~0x4); +		WREG32(offset+R600_HDMI_ENABLE, enable ? 0x105 : 0x0); +		break; + +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: +		/* This part is doubtfull in my opinion */ +		WREG32(offset+R600_HDMI_ENABLE, enable ? 0x110 : 0x0); +		break; + +	default: +		DRM_ERROR("unknown HDMI output type\n"); +		break; +	} +} + +/* + * determin at which register offset the HDMI encoder is + */ +void r600_hdmi_init(struct drm_encoder *encoder) +{ +	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + +	switch (radeon_encoder->encoder_id) { +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY: +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1: +		radeon_encoder->hdmi_offset = R600_HDMI_TMDS1; +		break; + +	case ENCODER_OBJECT_ID_INTERNAL_LVTM1: +		switch (r600_audio_tmds_index(encoder)) { +		case 0: +			radeon_encoder->hdmi_offset = R600_HDMI_TMDS1; +			break; +		case 1: +			radeon_encoder->hdmi_offset = R600_HDMI_TMDS2; +			break; +		default: +			radeon_encoder->hdmi_offset = 0; +			break; +		} +	case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2: +		radeon_encoder->hdmi_offset = R600_HDMI_TMDS2; +		break; + +	case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA: +		radeon_encoder->hdmi_offset = R600_HDMI_DIG; +		break; + +	default: +		radeon_encoder->hdmi_offset = 0; +		break; +	} + +	DRM_DEBUG("using HDMI engine at offset 0x%04X for encoder 0x%x\n", +		  radeon_encoder->hdmi_offset, radeon_encoder->encoder_id); + +	/* TODO: make this configureable */ +	radeon_encoder->hdmi_audio_workaround = 0; +} diff --git a/drivers/gpu/drm/radeon/r600_reg.h b/drivers/gpu/drm/radeon/r600_reg.h index e2d1f5f33f7..d0e28ffdeda 100644 --- a/drivers/gpu/drm/radeon/r600_reg.h +++ b/drivers/gpu/drm/radeon/r600_reg.h @@ -110,5 +110,79 @@  #define R600_BIOS_6_SCRATCH               0x173c  #define R600_BIOS_7_SCRATCH               0x1740 +/* Audio, these regs were reverse enginered, + * so the chance is high that the naming is wrong + * R6xx+ ??? */ + +/* Audio clocks */ +#define R600_AUDIO_PLL1_MUL               0x0514 +#define R600_AUDIO_PLL1_DIV               0x0518 +#define R600_AUDIO_PLL2_MUL               0x0524 +#define R600_AUDIO_PLL2_DIV               0x0528 +#define R600_AUDIO_CLK_SRCSEL             0x0534 + +/* Audio general */ +#define R600_AUDIO_ENABLE                 0x7300 +#define R600_AUDIO_TIMING                 0x7344 + +/* Audio params */ +#define R600_AUDIO_VENDOR_ID              0x7380 +#define R600_AUDIO_REVISION_ID            0x7384 +#define R600_AUDIO_ROOT_NODE_COUNT        0x7388 +#define R600_AUDIO_NID1_NODE_COUNT        0x738c +#define R600_AUDIO_NID1_TYPE              0x7390 +#define R600_AUDIO_SUPPORTED_SIZE_RATE    0x7394 +#define R600_AUDIO_SUPPORTED_CODEC        0x7398 +#define R600_AUDIO_SUPPORTED_POWER_STATES 0x739c +#define R600_AUDIO_NID2_CAPS              0x73a0 +#define R600_AUDIO_NID3_CAPS              0x73a4 +#define R600_AUDIO_NID3_PIN_CAPS          0x73a8 + +/* Audio conn list */ +#define R600_AUDIO_CONN_LIST_LEN          0x73ac +#define R600_AUDIO_CONN_LIST              0x73b0 + +/* Audio verbs */ +#define R600_AUDIO_RATE_BPS_CHANNEL       0x73c0 +#define R600_AUDIO_PLAYING                0x73c4 +#define R600_AUDIO_IMPLEMENTATION_ID      0x73c8 +#define R600_AUDIO_CONFIG_DEFAULT         0x73cc +#define R600_AUDIO_PIN_SENSE              0x73d0 +#define R600_AUDIO_PIN_WIDGET_CNTL        0x73d4 +#define R600_AUDIO_STATUS_BITS            0x73d8 + +/* HDMI base register addresses */ +#define R600_HDMI_TMDS1                   0x7400 +#define R600_HDMI_TMDS2                   0x7700 +#define R600_HDMI_DIG                     0x7800 + +/* HDMI registers */ +#define R600_HDMI_ENABLE           0x00 +#define R600_HDMI_STATUS           0x04 +#define R600_HDMI_CNTL             0x08 +#define R600_HDMI_UNKNOWN_0        0x0C +#define R600_HDMI_AUDIOCNTL        0x10 +#define R600_HDMI_VIDEOCNTL        0x14 +#define R600_HDMI_VERSION          0x18 +#define R600_HDMI_UNKNOWN_1        0x28 +#define R600_HDMI_VIDEOINFOFRAME_0 0x54 +#define R600_HDMI_VIDEOINFOFRAME_1 0x58 +#define R600_HDMI_VIDEOINFOFRAME_2 0x5c +#define R600_HDMI_VIDEOINFOFRAME_3 0x60 +#define R600_HDMI_32kHz_CTS        0xac +#define R600_HDMI_32kHz_N          0xb0 +#define R600_HDMI_44_1kHz_CTS      0xb4 +#define R600_HDMI_44_1kHz_N        0xb8 +#define R600_HDMI_48kHz_CTS        0xbc +#define R600_HDMI_48kHz_N          0xc0 +#define R600_HDMI_AUDIOINFOFRAME_0 0xcc +#define R600_HDMI_AUDIOINFOFRAME_1 0xd0 +#define R600_HDMI_IEC60958_1       0xd4 +#define R600_HDMI_IEC60958_2       0xd8 +#define R600_HDMI_UNKNOWN_2        0xdc +#define R600_HDMI_AUDIO_DEBUG_0    0xe0 +#define R600_HDMI_AUDIO_DEBUG_1    0xe4 +#define R600_HDMI_AUDIO_DEBUG_2    0xe8 +#define R600_HDMI_AUDIO_DEBUG_3    0xec  #endif diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index c938bb54123..cd650fd3964 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -89,6 +89,7 @@ extern int radeon_testing;  extern int radeon_connector_table;  extern int radeon_tv;  extern int radeon_new_pll; +extern int radeon_audio;  /*   * Copy from radeon_drv.h so we don't have to include both and have conflicting @@ -814,6 +815,14 @@ struct radeon_device {  	struct r600_ih ih; /* r6/700 interrupt ring */  	struct workqueue_struct *wq;  	struct work_struct hotplug_work; + +	/* audio stuff */ +	struct timer_list	audio_timer; +	int			audio_channels; +	int			audio_rate; +	int			audio_bits_per_sample; +	uint8_t			audio_status_bits; +	uint8_t			audio_category_code;  };  int radeon_device_init(struct radeon_device *rdev, @@ -1016,6 +1025,7 @@ extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data);  extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);  extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);  extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); +extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);  /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */  struct r100_mc_save { @@ -1146,6 +1156,21 @@ extern void r600_irq_fini(struct radeon_device *rdev);  extern void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size);  extern int r600_irq_set(struct radeon_device *rdev); +extern int r600_audio_init(struct radeon_device *rdev); +extern int r600_audio_tmds_index(struct drm_encoder *encoder); +extern void r600_audio_set_clock(struct drm_encoder *encoder, int clock); +extern void r600_audio_fini(struct radeon_device *rdev); +extern void r600_hdmi_init(struct drm_encoder *encoder); +extern void r600_hdmi_enable(struct drm_encoder *encoder, int enable); +extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode); +extern int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder); +extern void r600_hdmi_update_audio_settings(struct drm_encoder *encoder, +					    int channels, +					    int rate, +					    int bps, +					    uint8_t status_bits, +					    uint8_t category_code); +  #include "radeon_object.h"  #endif diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index c5c45e626d7..dbd56ef82f9 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -87,6 +87,7 @@ int radeon_testing = 0;  int radeon_connector_table = 0;  int radeon_tv = 1;  int radeon_new_pll = 1; +int radeon_audio = 1;  MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");  module_param_named(no_wb, radeon_no_wb, int, 0444); @@ -124,6 +125,9 @@ module_param_named(tv, radeon_tv, int, 0444);  MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips");  module_param_named(new_pll, radeon_new_pll, int, 0444); +MODULE_PARM_DESC(audio, "Audio enable (0 = disable)"); +module_param_named(audio, radeon_audio, int, 0444); +  static int radeon_suspend(struct drm_device *dev, pm_message_t state)  {  	drm_radeon_private_t *dev_priv = dev->dev_private; diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c index b4f23ec9320..0d1d908e522 100644 --- a/drivers/gpu/drm/radeon/radeon_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_encoders.c @@ -438,6 +438,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)  	struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);  	union lvds_encoder_control args;  	int index = 0; +	int hdmi_detected = 0;  	uint8_t frev, crev;  	struct radeon_encoder_atom_dig *dig;  	struct drm_connector *connector; @@ -458,6 +459,9 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)  	if (!radeon_connector->con_priv)  		return; +	if (drm_detect_hdmi_monitor(radeon_connector->edid)) +		hdmi_detected = 1; +  	dig_connector = radeon_connector->con_priv;  	memset(&args, 0, sizeof(args)); @@ -487,7 +491,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)  		case 1:  			args.v1.ucMisc = 0;  			args.v1.ucAction = action; -			if (drm_detect_hdmi_monitor(radeon_connector->edid)) +			if (hdmi_detected)  				args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;  			args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);  			if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) { @@ -512,7 +516,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)  				if (dig->coherent_mode)  					args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;  			} -			if (drm_detect_hdmi_monitor(radeon_connector->edid)) +			if (hdmi_detected)  				args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;  			args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);  			args.v2.ucTruncate = 0; @@ -552,7 +556,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)  	}  	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); - +	r600_hdmi_enable(encoder, hdmi_detected);  }  int @@ -893,7 +897,6 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t  	}  	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); -  }  static void @@ -1162,7 +1165,6 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)  	}  	atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); -  }  static void @@ -1265,6 +1267,8 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,  		break;  	}  	atombios_apply_encoder_quirks(encoder, adjusted_mode); + +	r600_hdmi_setmode(encoder, adjusted_mode);  }  static bool @@ -1510,4 +1514,6 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su  		drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);  		break;  	} + +	r600_hdmi_init(encoder);  } diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index 2944486871b..60df2d7e7e4 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -66,8 +66,9 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,  	}  	r = radeon_bo_create(rdev, gobj, size, kernel, initial_domain, &robj);  	if (r) { -		DRM_ERROR("Failed to allocate GEM object (%d, %d, %u)\n", -			  size, initial_domain, alignment); +		if (r != -ERESTARTSYS) +			DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n", +				  size, initial_domain, alignment, r);  		mutex_lock(&rdev->ddev->struct_mutex);  		drm_gem_object_unreference(gobj);  		mutex_unlock(&rdev->ddev->struct_mutex); @@ -350,9 +351,10 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,  	rbo = gobj->driver_private;  	r = radeon_bo_reserve(rbo, false);  	if (unlikely(r != 0)) -		return r; +		goto out;  	radeon_bo_get_tiling_flags(rbo, &args->tiling_flags, &args->pitch);  	radeon_bo_unreserve(rbo); +out:  	mutex_lock(&dev->struct_mutex);  	drm_gem_object_unreference(gobj);  	mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h index 44d4b652ea1..3dcbe130c42 100644 --- a/drivers/gpu/drm/radeon/radeon_mode.h +++ b/drivers/gpu/drm/radeon/radeon_mode.h @@ -334,6 +334,9 @@ struct radeon_encoder {  	enum radeon_rmx_type rmx_type;  	struct drm_display_mode native_mode;  	void *enc_priv; +	int hdmi_offset; +	int hdmi_audio_workaround; +	int hdmi_buffer_status;  };  struct radeon_connector_atom_dig { diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index 544e18ffaf2..d9ffe1f56e8 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -56,6 +56,13 @@ static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)  	kfree(bo);  } +bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo) +{ +	if (bo->destroy == &radeon_ttm_bo_destroy) +		return true; +	return false; +} +  void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)  {  	u32 c = 0; @@ -71,6 +78,8 @@ void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)  		rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;  	if (domain & RADEON_GEM_DOMAIN_CPU)  		rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; +	if (!c) +		rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;  	rbo->placement.num_placement = c;  	rbo->placement.num_busy_placement = c;  } @@ -481,14 +490,20 @@ int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,  }  void radeon_bo_move_notify(struct ttm_buffer_object *bo, -				struct ttm_mem_reg *mem) +			   struct ttm_mem_reg *mem)  { -	struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo); +	struct radeon_bo *rbo; +	if (!radeon_ttm_bo_is_radeon_bo(bo)) +		return; +	rbo = container_of(bo, struct radeon_bo, tbo);  	radeon_bo_check_tiling(rbo, 0, 1);  }  void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)  { -	struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo); +	struct radeon_bo *rbo; +	if (!radeon_ttm_bo_is_radeon_bo(bo)) +		return; +	rbo = container_of(bo, struct radeon_bo, tbo);  	radeon_bo_check_tiling(rbo, 0, 0);  } diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h index f6b69c2c0d0..a02f18011ad 100644 --- a/drivers/gpu/drm/radeon/radeon_object.h +++ b/drivers/gpu/drm/radeon/radeon_object.h @@ -59,19 +59,17 @@ static inline unsigned radeon_mem_type_to_domain(u32 mem_type)   *   * Returns:   * -EBUSY: buffer is busy and @no_wait is true - * -ERESTART: A wait for the buffer to become unreserved was interrupted by + * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by   * a signal. Release all buffer reservations and return to user-space.   */  static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait)  {  	int r; -retry:  	r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);  	if (unlikely(r != 0)) { -		if (r == -ERESTART) -			goto retry; -		dev_err(bo->rdev->dev, "%p reserve failed\n", bo); +		if (r != -ERESTARTSYS) +			dev_err(bo->rdev->dev, "%p reserve failed\n", bo);  		return r;  	}  	return 0; @@ -125,12 +123,10 @@ static inline int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,  {  	int r; -retry:  	r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);  	if (unlikely(r != 0)) { -		if (r == -ERESTART) -			goto retry; -		dev_err(bo->rdev->dev, "%p reserve failed for wait\n", bo); +		if (r != -ERESTARTSYS) +			dev_err(bo->rdev->dev, "%p reserve failed for wait\n", bo);  		return r;  	}  	spin_lock(&bo->tbo.lock); @@ -140,8 +136,6 @@ retry:  		r = ttm_bo_wait(&bo->tbo, true, true, no_wait);  	spin_unlock(&bo->tbo.lock);  	ttm_bo_unreserve(&bo->tbo); -	if (unlikely(r == -ERESTART)) -		goto retry;  	return r;  } diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c index 34b08d307c8..8bce64cdc32 100644 --- a/drivers/gpu/drm/radeon/radeon_pm.c +++ b/drivers/gpu/drm/radeon/radeon_pm.c @@ -44,8 +44,11 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)  	struct drm_device *dev = node->minor->dev;  	struct radeon_device *rdev = dev->dev_private; -	seq_printf(m, "engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); -	seq_printf(m, "memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev)); +	seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk); +	seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev)); +	seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk); +	if (rdev->asic->get_memory_clock) +		seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));  	return 0;  } diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c index d2ed896cca0..7bed4122528 100644 --- a/drivers/gpu/drm/radeon/radeon_ttm.c +++ b/drivers/gpu/drm/radeon/radeon_ttm.c @@ -200,7 +200,19 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,  static void radeon_evict_flags(struct ttm_buffer_object *bo,  				struct ttm_placement *placement)  { -	struct radeon_bo *rbo = container_of(bo, struct radeon_bo, tbo); +	struct radeon_bo *rbo; +	static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM; + +	if (!radeon_ttm_bo_is_radeon_bo(bo)) { +		placement->fpfn = 0; +		placement->lpfn = 0; +		placement->placement = &placements; +		placement->busy_placement = &placements; +		placement->num_placement = 1; +		placement->num_busy_placement = 1; +		return; +	} +	rbo = container_of(bo, struct radeon_bo, tbo);  	switch (bo->mem.mem_type) {  	case TTM_PL_VRAM:  		radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT); diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c index c1fcdddb6be..368415df5f3 100644 --- a/drivers/gpu/drm/radeon/rs400.c +++ b/drivers/gpu/drm/radeon/rs400.c @@ -497,6 +497,8 @@ int rs400_init(struct radeon_device *rdev)  	/* Initialize clocks */  	radeon_get_clock_info(rdev->ddev); +	/* Initialize power management */ +	radeon_pm_init(rdev);  	/* Get vram informations */  	rs400_vram_info(rdev);  	/* Initialize memory controller (also test AGP) */ diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c index 2d124bb5776..f58dc671080 100644 --- a/drivers/gpu/drm/radeon/rv770.c +++ b/drivers/gpu/drm/radeon/rv770.c @@ -892,6 +892,14 @@ static int rv770_startup(struct radeon_device *rdev)  	}  	rv770_gpu_init(rdev); +	if (!rdev->r600_blit.shader_obj) { +		r = r600_blit_init(rdev); +		if (r) { +			DRM_ERROR("radeon: failed blitter (%d).\n", r); +			return r; +		} +	} +  	r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);  	if (unlikely(r != 0))  		return r; @@ -1051,12 +1059,6 @@ int rv770_init(struct radeon_device *rdev)  	if (r)  		return r; -	r = r600_blit_init(rdev); -	if (r) { -		DRM_ERROR("radeon: failed blitter (%d).\n", r); -		return r; -	} -  	rdev->accel_working = true;  	r = rv770_startup(rdev);  	if (r) {  |