diff options
author | Dave Airlie <airlied@redhat.com> | 2009-12-18 09:56:49 +1000 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2009-12-18 09:56:49 +1000 |
commit | dcd6dfcfe959bade75fbf49499775985d2cac5d5 (patch) | |
tree | 45e37b24d7e34a1bdefe04e65633168873476ca7 /drivers | |
parent | cbc8cc049aefd571ebc3c255dc3e265f736751c0 (diff) | |
parent | d785d78bbdb53580b12c40e820af5a3281ce2fc8 (diff) | |
download | linux-dcd6dfcfe959bade75fbf49499775985d2cac5d5.tar.bz2 |
Merge branch 'drm-linus' into drm-core-next
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 d823e6319516..b1bc1ea182b8 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 320a14bceb99..aa2dfbc3e351 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 0cff7eb3690a..dacac9a0842a 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 11f831f0ddc5..18fd8ac9fca7 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 02755712ed3d..5158a12f7844 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 d3e0a2a6acf8..7e8547cb5833 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 feb52eee4314..b5f5fe75e6af 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 824cc6480a06..84e5df766d3f 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 ca50903dd2bb..7188c3778ee2 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 eb740fc3549f..20942127c46b 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 83378c39d0e3..83490c2b5061 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 f5cf874dc62a..5c6058c6ddde 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 000000000000..99e2c3891a7d --- /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 000000000000..fcc949df0e5d --- /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 e2d1f5f33f7e..d0e28ffdeda9 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 c938bb54123c..cd650fd3964e 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 c5c45e626d74..dbd56ef82f9c 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 b4f23ec93201..0d1d908e5225 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 2944486871b0..60df2d7e7e4c 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 44d4b652ea12..3dcbe130c422 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 544e18ffaf22..d9ffe1f56e8f 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 f6b69c2c0d00..a02f18011ad1 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 34b08d307c81..8bce64cdc320 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 d2ed896cca01..7bed4122528c 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 c1fcdddb6be6..368415df5f3a 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 2d124bb57762..f58dc6710802 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) { |