diff options
Diffstat (limited to 'drivers/gpu/drm/ast/ast_main.c')
-rw-r--r-- | drivers/gpu/drm/ast/ast_main.c | 527 |
1 files changed, 527 insertions, 0 deletions
diff --git a/drivers/gpu/drm/ast/ast_main.c b/drivers/gpu/drm/ast/ast_main.c new file mode 100644 index 000000000000..95ae55b8214b --- /dev/null +++ b/drivers/gpu/drm/ast/ast_main.c @@ -0,0 +1,527 @@ +/* + * Copyright 2012 Red Hat Inc. + * + * 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, sub license, 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 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 NON-INFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS 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. + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial portions + * of the Software. + * + */ +/* + * Authors: Dave Airlie <airlied@redhat.com> + */ +#include "drmP.h" +#include "ast_drv.h" + + +#include "drm_fb_helper.h" +#include "drm_crtc_helper.h" + +#include "ast_dram_tables.h" + +void ast_set_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, + uint8_t mask, uint8_t val) +{ + u8 tmp; + ast_io_write8(ast, base, index); + tmp = (ast_io_read8(ast, base + 1) & mask) | val; + ast_set_index_reg(ast, base, index, tmp); +} + +uint8_t ast_get_index_reg(struct ast_private *ast, + uint32_t base, uint8_t index) +{ + uint8_t ret; + ast_io_write8(ast, base, index); + ret = ast_io_read8(ast, base + 1); + return ret; +} + +uint8_t ast_get_index_reg_mask(struct ast_private *ast, + uint32_t base, uint8_t index, uint8_t mask) +{ + uint8_t ret; + ast_io_write8(ast, base, index); + ret = ast_io_read8(ast, base + 1) & mask; + return ret; +} + + +static int ast_detect_chip(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + if (dev->pdev->device == PCI_CHIP_AST1180) { + ast->chip = AST1100; + DRM_INFO("AST 1180 detected\n"); + } else { + if (dev->pdev->revision >= 0x20) { + ast->chip = AST2300; + DRM_INFO("AST 2300 detected\n"); + } else if (dev->pdev->revision >= 0x10) { + uint32_t data; + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + + data = ast_read32(ast, 0x1207c); + switch (data & 0x0300) { + case 0x0200: + ast->chip = AST1100; + DRM_INFO("AST 1100 detected\n"); + break; + case 0x0100: + ast->chip = AST2200; + DRM_INFO("AST 2200 detected\n"); + break; + case 0x0000: + ast->chip = AST2150; + DRM_INFO("AST 2150 detected\n"); + break; + default: + ast->chip = AST2100; + DRM_INFO("AST 2100 detected\n"); + break; + } + ast->vga2_clone = false; + } else { + ast->chip = 2000; + DRM_INFO("AST 2000 detected\n"); + } + } + return 0; +} + +static int ast_get_dram_info(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + uint32_t data, data2; + uint32_t denum, num, div, ref_pll; + + ast_write32(ast, 0xf004, 0x1e6e0000); + ast_write32(ast, 0xf000, 0x1); + + + ast_write32(ast, 0x10000, 0xfc600309); + + do { + ; + } while (ast_read32(ast, 0x10000) != 0x01); + data = ast_read32(ast, 0x10004); + + if (data & 0x400) + ast->dram_bus_width = 16; + else + ast->dram_bus_width = 32; + + if (ast->chip == AST2300) { + switch (data & 0x03) { + case 0: + ast->dram_type = AST_DRAM_512Mx16; + break; + default: + case 1: + ast->dram_type = AST_DRAM_1Gx16; + break; + case 2: + ast->dram_type = AST_DRAM_2Gx16; + break; + case 3: + ast->dram_type = AST_DRAM_4Gx16; + break; + } + } else { + switch (data & 0x0c) { + case 0: + case 4: + ast->dram_type = AST_DRAM_512Mx16; + break; + case 8: + if (data & 0x40) + ast->dram_type = AST_DRAM_1Gx16; + else + ast->dram_type = AST_DRAM_512Mx32; + break; + case 0xc: + ast->dram_type = AST_DRAM_1Gx32; + break; + } + } + + data = ast_read32(ast, 0x10120); + data2 = ast_read32(ast, 0x10170); + if (data2 & 0x2000) + ref_pll = 14318; + else + ref_pll = 12000; + + denum = data & 0x1f; + num = (data & 0x3fe0) >> 5; + data = (data & 0xc000) >> 14; + switch (data) { + case 3: + div = 0x4; + break; + case 2: + case 1: + div = 0x2; + break; + default: + div = 0x1; + break; + } + ast->mclk = ref_pll * (num + 2) / (denum + 2) * (div * 1000); + return 0; +} + +uint32_t ast_get_max_dclk(struct drm_device *dev, int bpp) +{ + struct ast_private *ast = dev->dev_private; + uint32_t dclk, jreg; + uint32_t dram_bus_width, mclk, dram_bandwidth, actual_dram_bandwidth, dram_efficency = 500; + + dram_bus_width = ast->dram_bus_width; + mclk = ast->mclk; + + if (ast->chip == AST2100 || + ast->chip == AST1100 || + ast->chip == AST2200 || + ast->chip == AST2150 || + ast->dram_bus_width == 16) + dram_efficency = 600; + else if (ast->chip == AST2300) + dram_efficency = 400; + + dram_bandwidth = mclk * dram_bus_width * 2 / 8; + actual_dram_bandwidth = dram_bandwidth * dram_efficency / 1000; + + if (ast->chip == AST1180) + dclk = actual_dram_bandwidth / ((bpp + 1) / 8); + else { + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xd0, 0xff); + if ((jreg & 0x08) && (ast->chip == AST2000)) + dclk = actual_dram_bandwidth / ((bpp + 1 + 16) / 8); + else if ((jreg & 0x08) && (bpp == 8)) + dclk = actual_dram_bandwidth / ((bpp + 1 + 24) / 8); + else + dclk = actual_dram_bandwidth / ((bpp + 1) / 8); + } + + if (ast->chip == AST2100 || + ast->chip == AST2200 || + ast->chip == AST2300 || + ast->chip == AST1180) { + if (dclk > 200) + dclk = 200; + } else { + if (dclk > 165) + dclk = 165; + } + + return dclk; +} + +static void ast_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct ast_framebuffer *ast_fb = to_ast_framebuffer(fb); + if (ast_fb->obj) + drm_gem_object_unreference_unlocked(ast_fb->obj); + + drm_framebuffer_cleanup(fb); + kfree(fb); +} + +static int ast_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file, + unsigned int *handle) +{ + return -EINVAL; +} + +static const struct drm_framebuffer_funcs ast_fb_funcs = { + .destroy = ast_user_framebuffer_destroy, + .create_handle = ast_user_framebuffer_create_handle, +}; + + +int ast_framebuffer_init(struct drm_device *dev, + struct ast_framebuffer *ast_fb, + struct drm_mode_fb_cmd2 *mode_cmd, + struct drm_gem_object *obj) +{ + int ret; + + ret = drm_framebuffer_init(dev, &ast_fb->base, &ast_fb_funcs); + if (ret) { + DRM_ERROR("framebuffer init failed %d\n", ret); + return ret; + } + drm_helper_mode_fill_fb_struct(&ast_fb->base, mode_cmd); + ast_fb->obj = obj; + return 0; +} + +static struct drm_framebuffer * +ast_user_framebuffer_create(struct drm_device *dev, + struct drm_file *filp, + struct drm_mode_fb_cmd2 *mode_cmd) +{ + struct drm_gem_object *obj; + struct ast_framebuffer *ast_fb; + int ret; + + obj = drm_gem_object_lookup(dev, filp, mode_cmd->handles[0]); + if (obj == NULL) + return ERR_PTR(-ENOENT); + + ast_fb = kzalloc(sizeof(*ast_fb), GFP_KERNEL); + if (!ast_fb) { + drm_gem_object_unreference_unlocked(obj); + return ERR_PTR(-ENOMEM); + } + + ret = ast_framebuffer_init(dev, ast_fb, mode_cmd, obj); + if (ret) { + drm_gem_object_unreference_unlocked(obj); + kfree(ast_fb); + return ERR_PTR(ret); + } + return &ast_fb->base; +} + +static const struct drm_mode_config_funcs ast_mode_funcs = { + .fb_create = ast_user_framebuffer_create, +}; + +static u32 ast_get_vram_info(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + u8 jreg; + + ast_open_key(ast); + + jreg = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xaa, 0xff); + switch (jreg & 3) { + case 0: return AST_VIDMEM_SIZE_8M; + case 1: return AST_VIDMEM_SIZE_16M; + case 2: return AST_VIDMEM_SIZE_32M; + case 3: return AST_VIDMEM_SIZE_64M; + } + return AST_VIDMEM_DEFAULT_SIZE; +} + +int ast_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct ast_private *ast; + int ret = 0; + + ast = kzalloc(sizeof(struct ast_private), GFP_KERNEL); + if (!ast) + return -ENOMEM; + + dev->dev_private = ast; + ast->dev = dev; + + ast->regs = pci_iomap(dev->pdev, 1, 0); + if (!ast->regs) { + ret = -EIO; + goto out_free; + } + ast->ioregs = pci_iomap(dev->pdev, 2, 0); + if (!ast->ioregs) { + ret = -EIO; + goto out_free; + } + + ast_detect_chip(dev); + + if (ast->chip != AST1180) { + ast_get_dram_info(dev); + ast->vram_size = ast_get_vram_info(dev); + DRM_INFO("dram %d %d %d %08x\n", ast->mclk, ast->dram_type, ast->dram_bus_width, ast->vram_size); + } + + ret = ast_mm_init(ast); + if (ret) + goto out_free; + + drm_mode_config_init(dev); + + dev->mode_config.funcs = (void *)&ast_mode_funcs; + dev->mode_config.min_width = 0; + dev->mode_config.min_height = 0; + dev->mode_config.preferred_depth = 24; + dev->mode_config.prefer_shadow = 1; + + if (ast->chip == AST2100 || + ast->chip == AST2200 || + ast->chip == AST2300 || + ast->chip == AST1180) { + dev->mode_config.max_width = 1920; + dev->mode_config.max_height = 2048; + } else { + dev->mode_config.max_width = 1600; + dev->mode_config.max_height = 1200; + } + + ret = ast_mode_init(dev); + if (ret) + goto out_free; + + ret = ast_fbdev_init(dev); + if (ret) + goto out_free; + + return 0; +out_free: + kfree(ast); + dev->dev_private = NULL; + return ret; +} + +int ast_driver_unload(struct drm_device *dev) +{ + struct ast_private *ast = dev->dev_private; + + ast_mode_fini(dev); + ast_fbdev_fini(dev); + drm_mode_config_cleanup(dev); + + ast_mm_fini(ast); + pci_iounmap(dev->pdev, ast->ioregs); + pci_iounmap(dev->pdev, ast->regs); + kfree(ast); + return 0; +} + +int ast_gem_create(struct drm_device *dev, + u32 size, bool iskernel, + struct drm_gem_object **obj) +{ + struct ast_bo *astbo; + int ret; + + *obj = NULL; + + size = roundup(size, PAGE_SIZE); + if (size == 0) + return -EINVAL; + + ret = ast_bo_create(dev, size, 0, 0, &astbo); + if (ret) { + if (ret != -ERESTARTSYS) + DRM_ERROR("failed to allocate GEM object\n"); + return ret; + } + *obj = &astbo->gem; + return 0; +} + +int ast_dumb_create(struct drm_file *file, + struct drm_device *dev, + struct drm_mode_create_dumb *args) +{ + int ret; + struct drm_gem_object *gobj; + u32 handle; + + args->pitch = args->width * ((args->bpp + 7) / 8); + args->size = args->pitch * args->height; + + ret = ast_gem_create(dev, args->size, false, + &gobj); + if (ret) + return ret; + + ret = drm_gem_handle_create(file, gobj, &handle); + drm_gem_object_unreference_unlocked(gobj); + if (ret) + return ret; + + args->handle = handle; + return 0; +} + +int ast_dumb_destroy(struct drm_file *file, + struct drm_device *dev, + uint32_t handle) +{ + return drm_gem_handle_delete(file, handle); +} + +int ast_gem_init_object(struct drm_gem_object *obj) +{ + BUG(); + return 0; +} + +void ast_bo_unref(struct ast_bo **bo) +{ + struct ttm_buffer_object *tbo; + + if ((*bo) == NULL) + return; + + tbo = &((*bo)->bo); + ttm_bo_unref(&tbo); + if (tbo == NULL) + *bo = NULL; + +} +void ast_gem_free_object(struct drm_gem_object *obj) +{ + struct ast_bo *ast_bo = gem_to_ast_bo(obj); + + if (!ast_bo) + return; + ast_bo_unref(&ast_bo); +} + + +static inline u64 ast_bo_mmap_offset(struct ast_bo *bo) +{ + return bo->bo.addr_space_offset; +} +int +ast_dumb_mmap_offset(struct drm_file *file, + struct drm_device *dev, + uint32_t handle, + uint64_t *offset) +{ + struct drm_gem_object *obj; + int ret; + struct ast_bo *bo; + + mutex_lock(&dev->struct_mutex); + obj = drm_gem_object_lookup(dev, file, handle); + if (obj == NULL) { + ret = -ENOENT; + goto out_unlock; + } + + bo = gem_to_ast_bo(obj); + *offset = ast_bo_mmap_offset(bo); + + drm_gem_object_unreference(obj); + ret = 0; +out_unlock: + mutex_unlock(&dev->struct_mutex); + return ret; + +} + |