diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig index 88910e5..91f1732 100644 --- a/drivers/gpu/drm/Kconfig +++ b/drivers/gpu/drm/Kconfig @@ -38,6 +38,11 @@ config DRM_TTM config DRM_TDFX tristate "3dfx Banshee/Voodoo3+" depends on DRM && PCI + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + select DRM_KMS_HELPER + select DRM_TTM help Choose this option if you have a 3dfx Banshee or Voodoo3 (or later), graphics card. If M is selected, the module will be called tdfx. diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 08c4c92..6261adf 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c @@ -390,7 +390,7 @@ int drm_fb_helper_blank(int blank, struct fb_info *info) break; /* Display: Off; HSync: On, VSync: On */ case FB_BLANK_NORMAL: - drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY); + drm_fb_helper_off(info, DRM_MODE_DPMS_ON); break; /* Display: Off; HSync: Off, VSync: On */ case FB_BLANK_HSYNC_SUSPEND: diff --git a/drivers/gpu/drm/tdfx/Makefile b/drivers/gpu/drm/tdfx/Makefile index 0379f29..3a40fbc 100644 --- a/drivers/gpu/drm/tdfx/Makefile +++ b/drivers/gpu/drm/tdfx/Makefile @@ -3,6 +3,7 @@ # Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher. ccflags-y := -Iinclude/drm -tdfx-y := tdfx_drv.o +tdfx-y := tdfx_drv.o tdfx_display.o tdfx_i2c.o tdfx_crt.o tdfx_ttm.o \ + tdfx_state.o tdfx_fb.o tdfx_fbcon.o init_ttm.o obj-$(CONFIG_DRM_TDFX) += tdfx.o diff --git a/drivers/gpu/drm/tdfx/init_ttm.c b/drivers/gpu/drm/tdfx/init_ttm.c new file mode 100644 index 0000000..7c519c4 --- /dev/null +++ b/drivers/gpu/drm/tdfx/init_ttm.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2010 James Simmons + * All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) 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 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. + */ + +#include "drmP.h" + +#include "tdfx_drv.h" + +static int +generic_ttm_mem_global_init(struct ttm_global_reference *ref) +{ + return ttm_mem_global_init(ref->object); +} + +static void +generic_ttm_mem_global_release(struct ttm_global_reference *ref) +{ + ttm_mem_global_release(ref->object); +} + +void +generic_ttm_global_release(struct ttm *ttm) +{ + if (ttm->mem_global_ref.release == NULL) + return; + + ttm_global_item_unref(&ttm->bo_global_ref.ref); + ttm_global_item_unref(&ttm->mem_global_ref); + ttm->mem_global_ref.release = NULL; +} + +int +generic_ttm_global_init(struct ttm *ttm) +{ + struct ttm_global_reference *global_ref; + int ret; + + global_ref = &ttm->mem_global_ref; + global_ref->global_type = TTM_GLOBAL_TTM_MEM; + global_ref->size = sizeof(struct ttm_mem_global); + global_ref->init = &generic_ttm_mem_global_init; + global_ref->release = &generic_ttm_mem_global_release; + + ret = ttm_global_item_ref(global_ref); + if (unlikely(ret != 0)) { + DRM_ERROR("Failed setting up TTM memory accounting\n"); + ttm->mem_global_ref.release = NULL; + return ret; + } + return ret; +} diff --git a/drivers/gpu/drm/tdfx/tdfx_crt.c b/drivers/gpu/drm/tdfx/tdfx_crt.c new file mode 100644 index 0000000..7f86e9c --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_crt.c @@ -0,0 +1,240 @@ +/* + * Copyright © 2009 James Simmons + * + * 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 (including the next + * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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: + * James Simmons + */ + +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "tdfx_drv.h" + +/* + * Routines for controlling stuff on the analog port + */ +static const struct drm_encoder_funcs tdfx_crt_enc_funcs = { + .destroy = drm_encoder_cleanup, +}; + +/* Power down the DAC */ +static void +tdfx_crt_dpms(struct drm_encoder *encoder, int mode) +{ + struct drm_device *dev = encoder->dev; + drm_tdfx_private_t *dev_priv; + u32 dacmode; + + dev_priv = dev->dev_private; + dacmode = DRM_READ32(dev_priv->mmio, DACMODE); + + switch (mode) { + case DRM_MODE_DPMS_SUSPEND: + dacmode |= BIT(3); // VSync off + break; + case DRM_MODE_DPMS_STANDBY: + dacmode |= BIT(1); // HSync off + break; + case DRM_MODE_DPMS_OFF: + dacmode |= (BIT(1) | BIT(3)); // HSync and VSync off + break; + case DRM_MODE_DPMS_ON: + default: + dacmode &= ~(BIT(1) | BIT(3)); + break; + } + + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, DACMODE, dacmode); +} + +/* Pass our mode to the connectors and the CRTC to give them a chance to + * adjust it according to limitations or connector properties, and also + * a chance to reject the mode entirely. Usefule for things like scaling. + */ +static bool +tdfx_crt_mode_fixup(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + /* Could sometimes be 8 */ + adjusted_mode->hdisplay = (adjusted_mode->hdisplay + 15) & ~15; + + drm_mode_set_crtcinfo(adjusted_mode, 0); + return true; +} + +static void +tdfx_encoder_prepare(struct drm_encoder *encoder) +{ +} + +static void +tdfx_encoder_commit(struct drm_encoder *encoder) +{ +} + +static void +tdfx_crt_mode_set(struct drm_encoder *encoder, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ +} + +static const struct drm_encoder_helper_funcs tdfx_crt_enc_helper_funcs = { + .dpms = tdfx_crt_dpms, + .mode_fixup = tdfx_crt_mode_fixup, + .prepare = tdfx_encoder_prepare, + .commit = tdfx_encoder_commit, + .mode_set = tdfx_crt_mode_set, +}; + +static enum drm_connector_status +tdfx_crt_detect(struct drm_connector *connector) +{ + /*struct drm_device *dev = connector->dev; + + if (drm_do_probe_ddc_edid(connector))*/ + return connector_status_connected; + + return connector_status_unknown; +} + +static void +tdfx_crt_destroy(struct drm_connector *connector) +{ + tdfx_i2c_destroy(connector->dev, DRM_MODE_CONNECTOR_VGA); + drm_sysfs_connector_remove(connector); + drm_connector_cleanup(connector); + kfree(connector); +} + +static int +tdfx_crt_set_property(struct drm_connector *connector, + struct drm_property *property, + uint64_t value) +{ + struct drm_device *dev = connector->dev; + + if (property == dev->mode_config.dpms_property && connector->encoder) + tdfx_crt_dpms(connector->encoder, (uint32_t)(value & 0xf)); + return 0; +} + +static const struct drm_connector_funcs tdfx_crt_connector_funcs = { + .detect = tdfx_crt_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .destroy = tdfx_crt_destroy, + .set_property = tdfx_crt_set_property, +}; + +/* This function test if the drm_display_modes generated by a display + * monitor that is connected is supported by the graphics card. + */ +static int +tdfx_crt_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + struct drm_device *dev = connector->dev; + drm_tdfx_private_t *dev_priv = dev->dev_private; + + /* + * Banshee doesn't support interlace, but Voodoo4/5 + * and probably Voodoo3 do. + * no direct information about device id now? + * use max_pixclock for this... + */ + if ((mode->flags & DRM_MODE_FLAG_INTERLACE) && + !connector->interlace_allowed) + return MODE_NO_INTERLACE; + + if (mode->clock > dev_priv->max_pixclock) + return MODE_CLOCK_HIGH; + return MODE_OK; +} + +static struct drm_encoder* +tdfx_best_encoder(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + drm_tdfx_private_t *dev_priv = dev->dev_private; + + /* Only one encoder i.e DAC for connector */ + return dev_priv->best; +} + +static const struct drm_connector_helper_funcs tdfx_crt_connector_helper_funcs = { + .mode_valid = tdfx_crt_mode_valid, + .get_modes = tdfx_get_modes, + .best_encoder = tdfx_best_encoder, +}; + +void tdfx_crt_init(struct drm_device *dev) +{ + struct drm_tdfx_private *dev_priv = dev->dev_private; + struct drm_connector *connector; + struct drm_encoder *enc; + void *par; + int size; + + size = sizeof(struct drm_connector) + sizeof(struct drm_encoder); + par = kzalloc(size, GFP_KERNEL); + if (!par) { + DRM_ERROR("Failed to allocate connector and encoder\n"); + return; + } + enc = par + sizeof(struct drm_encoder); + connector = par; + + /* Piece together our connector */ + drm_connector_init(dev, connector, &tdfx_crt_connector_funcs, + DRM_MODE_CONNECTOR_VGA); + drm_connector_helper_add(connector, &tdfx_crt_connector_helper_funcs); + + /* Banshee doesn't support interlace, but Voodoo4/5 and probably + * Voodoo3 do. */ + if (dev->pdev->device == PCI_DEVICE_ID_3DFX_BANSHEE) + connector->interlace_allowed = false; + else + connector->interlace_allowed = true; + connector->doublescan_allowed = true; + + /* Set up the DDC bus. */ + dev_priv->ddc_bus = tdfx_i2c_create(dev, DRM_MODE_CONNECTOR_VGA); + if (!dev_priv->ddc_bus) { + DRM_ERROR("DDC bus registration failed.\n"); + return; + } + + /* Setup the encoders and attach them */ + drm_encoder_init(dev, enc, &tdfx_crt_enc_funcs, DRM_MODE_ENCODER_DAC); + drm_encoder_helper_add(enc, &tdfx_crt_enc_helper_funcs); + dev_priv->best = enc; + enc->possible_clones = 1; //clone_mask; + enc->possible_crtcs = 1; //crtc_mask; + + drm_mode_connector_attach_encoder(connector, enc); + +// drm_sysfs_connector_add(connector); +} diff --git a/drivers/gpu/drm/tdfx/tdfx_display.c b/drivers/gpu/drm/tdfx/tdfx_display.c new file mode 100644 index 0000000..8f8fbfe --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_display.c @@ -0,0 +1,556 @@ +/* tdfx_display.c -- tdfx driver -*- linux-c -*- + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT 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. + * + * Authors: + * James Simmons + */ + +#include "drmP.h" +#include "drm_mode.h" +#include "drm_crtc_helper.h" + +#include "tdfx_drv.h" + +extern void tdfx_framebuffer_init(struct drm_device *dev); +extern inline void banshee_make_room(drm_tdfx_private_t *dev_priv, int); + +/* ------------------------------------------------------------------------- + * Hardware-specific funcions + * ------------------------------------------------------------------------- */ + +static inline void gra_outb(drm_tdfx_private_t *par, u32 idx, u8 val) +{ + vga_outb(par, GRA_I, idx); + wmb(); + vga_outb(par, GRA_D, val); + wmb(); +} + +inline void seq_outb(drm_tdfx_private_t *par, u32 idx, u8 val) +{ + vga_outb(par, SEQ_I, idx); + wmb(); + vga_outb(par, SEQ_D, val); + wmb(); +} + +inline u8 seq_inb(drm_tdfx_private_t *par, u32 idx) +{ + vga_outb(par, SEQ_I, idx); + mb(); + return vga_inb(par, SEQ_D); +} + +static inline void crt_outb(drm_tdfx_private_t *par, u32 idx, u8 val) +{ + vga_outb(par, CRT_I, idx); + wmb(); + vga_outb(par, CRT_D, val); + wmb(); +} + +static inline u8 crt_inb(drm_tdfx_private_t *par, u32 idx) +{ + vga_outb(par, CRT_I, idx); + mb(); + return vga_inb(par, CRT_D); +} + +static inline void att_outb(drm_tdfx_private_t *par, u32 idx, u8 val) +{ + unsigned char tmp; + + tmp = vga_inb(par, IS1_R); + vga_outb(par, ATT_IW, idx); + vga_outb(par, ATT_IW, val); +} + +static inline void vga_enable_palette(drm_tdfx_private_t *par) +{ + vga_inb(par, IS1_R); + mb(); + vga_outb(par, ATT_IW, 0x20); +} + +u32 do_calc_pll(int freq, int *freq_out) +{ + int m, n, k, best_m, best_n, best_k, f_cur, best_error; + int minm, maxm; + + /* this really could be done with more intelligence -- + 255*63*4 = 64260 iterations is silly */ + freq *= 100; + + best_error = freq; + best_n = best_m = best_k = 0; + minm = 1; + maxm = 64; + + for (n = 1; n < 256; n++) { + f_cur = REFFREQ * (n + 2); + if (f_cur < freq) { + f_cur = f_cur / 3; + if (freq - f_cur < best_error) { + best_error = freq - f_cur; + best_n = n; + best_m = 1; + best_k = 0; + continue; + } + } + for (m = minm; m < maxm; m++) { + for (k = 0; k < 4; k++) { + f_cur = REFFREQ * (n + 2) / (m + 2) / (1 << k); + if (abs(f_cur - freq) < best_error) { + best_error = abs(f_cur - freq); + best_n = n; + best_m = m; + best_k = k; + } + } + } + } + n = best_n; + m = best_m; + k = best_k; + *freq_out = REFFREQ * (n + 2) / (m + 2) / (1 << k) / 100; + return (n << 8) | (m << 2) | k; +} + +void do_write_regs(struct drm_crtc *crtc, struct banshee_reg *reg) +{ + struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + int i; + + banshee_wait_idle(crtc->dev); + DRM_WRITE32(dev_priv->mmio, MISCINIT1, DRM_READ32(dev_priv->mmio, MISCINIT1) | 0x01); + + crt_outb(dev_priv, 0x11, crt_inb(dev_priv, 0x11) & 0x7f); /* CRT unprotect */ + + banshee_make_room(dev_priv, 3); + DRM_WRITE32(dev_priv->mmio, VGAINIT1, reg->vgainit1 & 0x001FFFFF); + DRM_WRITE32(dev_priv->mmio, VIDPROCCFG, reg->vidcfg & ~0x00000001); +#if 0 + DRM_WRITE32(dev_priv->mmio, PLLCTRL1, reg->mempll); + DRM_WRITE32(dev_priv->mmio, PLLCTRL2, reg->gfxpll); +#endif + DRM_WRITE32(dev_priv->mmio, PLLCTRL0, reg->vidpll); + + vga_outb(dev_priv, MISC_W, reg->misc[0x00] | 0x01); + + for (i = 0; i < 5; i++) + seq_outb(dev_priv, i, reg->seq[i]); + + for (i = 0; i < 25; i++) + crt_outb(dev_priv, i, reg->crt[i]); + + for (i = 0; i < 9; i++) + gra_outb(dev_priv, i, reg->gra[i]); + + for (i = 0; i < 21; i++) + att_outb(dev_priv, i, reg->att[i]); + + crt_outb(dev_priv, 0x1a, reg->ext[0]); + crt_outb(dev_priv, 0x1b, reg->ext[1]); + + vga_enable_palette(dev_priv); + crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON); + + banshee_make_room(dev_priv, 9); + DRM_WRITE32(dev_priv->mmio, VGAINIT0, reg->vgainit0); + DRM_WRITE32(dev_priv->mmio, DACMODE, reg->dacmode); + DRM_WRITE32(dev_priv->mmio, VIDDESKSTRIDE, reg->stride); + DRM_WRITE32(dev_priv->mmio, HWCURPATADDR, dev_priv->framebuffer->size); + DRM_WRITE32(dev_priv->mmio, VIDSCREENSIZE, reg->screensize); + DRM_WRITE32(dev_priv->mmio, VIDDESKSTART, reg->startaddr); + DRM_WRITE32(dev_priv->mmio, VIDPROCCFG, reg->vidcfg); + DRM_WRITE32(dev_priv->mmio, VGAINIT1, reg->vgainit1); + DRM_WRITE32(dev_priv->mmio, MISCINIT0, reg->miscinit0); + + banshee_make_room(dev_priv, 8); + DRM_WRITE32(dev_priv->mmio, SRCBASE, reg->srcaddr); + DRM_WRITE32(dev_priv->mmio, DSTBASE, reg->dstaddr); + DRM_WRITE32(dev_priv->mmio, COMMANDEXTRA_2D, 0); + DRM_WRITE32(dev_priv->mmio, CLIP0MIN, 0); + DRM_WRITE32(dev_priv->mmio, CLIP0MAX, 0x0fff0fff); + DRM_WRITE32(dev_priv->mmio, CLIP1MIN, 0); + DRM_WRITE32(dev_priv->mmio, CLIP1MAX, 0x0fff0fff); + DRM_WRITE32(dev_priv->mmio, SRCXY, 0); + + banshee_wait_idle(crtc->dev); +} + +// Disable/Enable video +void +tdfx_crtc_dpms(struct drm_crtc *crtc, int mode) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + unsigned char s; + + s = seq_inb(dev_priv, 0x01); + if (mode == DRM_MODE_DPMS_ON) + s &= 0xdf; + else + s |= 0x20; + seq_outb(dev_priv, 0x00, 0x01); + seq_outb(dev_priv, 0x01, s); + seq_outb(dev_priv, 0x00, 0x03); +} + +static void +tdfx_crtc_prepare(struct drm_crtc *crtc) +{ +} + +static void +tdfx_crtc_commit(struct drm_crtc *crtc) +{ +} + +/* Pass our mode to the CRTC to give them a chance to adjust it according + * to CRTC limitations, and also a chance to reject the mode entirely. + * Useful for things like scaling. + */ +static bool +tdfx_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + + if (mode->clock > dev_priv->max_pixclock) + return false; + return true; +} + +static int +tdfx_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode, int x, int y, + struct drm_framebuffer *old_fb) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + u32 hdispend = mode->hdisplay, hsyncsta = mode->hsync_start; + u32 hsyncend = mode->hsync_end, htotal = mode->htotal; + u32 wd, hd, hs, he, ht, hbs, hbe, vd, vs, ve, vt, vbs, vbe; + struct drm_framebuffer *fb = crtc->fb; + u32 cpp = (fb->bits_per_pixel + 7) >> 3; + struct banshee_reg reg; + int fout, freq; + + memset(®, 0, sizeof(reg)); + + /* PLL settings */ + freq = mode->clock; + reg.vidcfg = VIDCFG_VIDPROC_ENABLE | VIDCFG_DESK_ENABLE | + VIDCFG_CURS_X11 | + ((cpp - 1) << VIDCFG_PIXFMT_SHIFT) | + (cpp != 1 ? VIDCFG_CLUT_BYPASS : 0); + + reg.vidcfg &= ~VIDCFG_2X; + if (freq > dev_priv->max_pixclock / 2) { + freq = freq > dev_priv->max_pixclock ? dev_priv->max_pixclock : freq; + reg.dacmode |= DACMODE_2X; + reg.vidcfg |= VIDCFG_2X; + hdispend >>= 1; + hsyncsta >>= 1; + hsyncend >>= 1; + htotal >>= 1; + } + + wd = (hdispend >> 3) - 1; + hd = wd; + hs = (hsyncsta >> 3) - 1; + he = (hsyncend >> 3) - 1; + ht = (htotal >> 3) - 1; + hbs = hd; + hbe = ht; + + if (mode->flags & DRM_MODE_FLAG_DBLSCAN) { + vd = (mode->vdisplay << 1) - 1; + vs = (mode->vsync_start << 1); + ve = (mode->vsync_end << 1); + vt = (mode->vtotal << 1) - 1; + reg.screensize = mode->hdisplay | (mode->vdisplay << 13); + reg.vidcfg |= VIDCFG_HALF_MODE; + reg.crt[0x09] = 0x80; + } else { + vd = mode->vdisplay - 1; + vs = mode->vsync_start; + ve = mode->vsync_end; + vt = mode->vtotal - 1; + reg.screensize = mode->hdisplay | (mode->vdisplay << 12); + reg.vidcfg &= ~VIDCFG_HALF_MODE; + } + vbs = vd; + vbe = vt; + + /* this is all pretty standard VGA register stuffing */ + reg.misc[0x00] = 0x0f | + (mode->hdisplay < 400 ? 0xa0 : + mode->hdisplay < 480 ? 0x60 : + mode->hdisplay < 768 ? 0xe0 : 0x20); + + reg.gra[0x05] = 0x40; + reg.gra[0x06] = 0x05; + reg.gra[0x07] = 0x0f; + reg.gra[0x08] = 0xff; + + reg.att[0x00] = 0x00; + reg.att[0x01] = 0x01; + reg.att[0x02] = 0x02; + reg.att[0x03] = 0x03; + reg.att[0x04] = 0x04; + reg.att[0x05] = 0x05; + reg.att[0x06] = 0x06; + reg.att[0x07] = 0x07; + reg.att[0x08] = 0x08; + reg.att[0x09] = 0x09; + reg.att[0x0a] = 0x0a; + reg.att[0x0b] = 0x0b; + reg.att[0x0c] = 0x0c; + reg.att[0x0d] = 0x0d; + reg.att[0x0e] = 0x0e; + reg.att[0x0f] = 0x0f; + reg.att[0x10] = 0x41; + reg.att[0x12] = 0x0f; + + reg.seq[0x00] = 0x03; + reg.seq[0x01] = 0x01; /* fixme: clkdiv2? */ + reg.seq[0x02] = 0x0f; + reg.seq[0x03] = 0x00; + reg.seq[0x04] = 0x0e; + + reg.crt[0x00] = ht - 4; + reg.crt[0x01] = hd; + reg.crt[0x02] = hbs; + reg.crt[0x03] = 0x80 | (hbe & 0x1f); + reg.crt[0x04] = hs; + reg.crt[0x05] = ((hbe & 0x20) << 2) | (he & 0x1f); + reg.crt[0x06] = vt; + reg.crt[0x07] = ((vs & 0x200) >> 2) | + ((vd & 0x200) >> 3) | + ((vt & 0x200) >> 4) | 0x10 | + ((vbs & 0x100) >> 5) | + ((vs & 0x100) >> 6) | + ((vd & 0x100) >> 7) | + ((vt & 0x100) >> 8); + reg.crt[0x09] |= 0x40 | ((vbs & 0x200) >> 4); + reg.crt[0x10] = vs; + reg.crt[0x11] = (ve & 0x0f) | 0x20; + reg.crt[0x12] = vd; + reg.crt[0x13] = wd; + reg.crt[0x15] = vbs; + reg.crt[0x16] = vbe + 1; + reg.crt[0x17] = 0xc3; + reg.crt[0x18] = 0xff; + + /* Banshee's nonvga stuff */ + reg.ext[0x00] = (((ht & 0x100) >> 8) | + ((hd & 0x100) >> 6) | + ((hbs & 0x100) >> 4) | + ((hbe & 0x40) >> 1) | + ((hs & 0x100) >> 2) | + ((he & 0x20) << 2)); + reg.ext[0x01] = (((vt & 0x400) >> 10) | + ((vd & 0x400) >> 8) | + ((vbs & 0x400) >> 6) | + ((vbe & 0x400) >> 4)); + + reg.vgainit0 = VGAINIT0_8BIT_DAC | + VGAINIT0_EXT_ENABLE | + VGAINIT0_WAKEUP_3C3 | + VGAINIT0_ALT_READBACK | + VGAINIT0_EXTSHIFTOUT; + reg.vgainit1 = DRM_READ32(dev_priv->mmio, VGAINIT1) & 0x1fffff; + fb->pitch = reg.stride = mode->hdisplay * cpp; + reg.startaddr = y * fb->pitch + x * cpp; + reg.srcaddr = reg.startaddr; + reg.dstaddr = reg.startaddr; + + reg.vidpll = do_calc_pll(freq, &fout); +#if 0 + reg.mempll = do_calc_pll(..., &fout); + reg.gfxpll = do_calc_pll(..., &fout); +#endif + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + reg.vidcfg |= VIDCFG_INTERLACE; + reg.miscinit0 = DRM_READ32(dev_priv->mmio, MISCINIT0); +#if defined(__BIG_ENDIAN) + switch (fb->bits_per_pixel) { + case 8: + case 24: + reg.miscinit0 &= ~BIT(30); + reg.miscinit0 &= ~BIT(31); + break; + case 16: + reg.miscinit0 |= BIT(30); + reg.miscinit0 |= BIT(31); + break; + case 32: + reg.miscinit0 |= BIT(30); + reg.miscinit0 &= ~BIT(31); + break; + } +#endif + do_write_regs(crtc, ®); + return 0; +} + +static int +tdfx_crtc_set_base(struct drm_crtc *crtc, int x, int y, + struct drm_framebuffer *old_fb) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + //u32 addr = y * crtc->fb->pitch; + u32 addr = y * old_fb->pitch; + + if (x) return -EINVAL; + + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, VIDDESKSTART, addr); + + if (crtc->fb != old_fb) { + if (old_fb->depth != crtc->fb->depth) { + // Depth changed + } + } + return 0; +} + +static void tdfx_crtc_load_lut(struct drm_crtc *crtc) +{ +} + +static const struct drm_crtc_helper_funcs tdfx_helper_funcs = { + .dpms = tdfx_crtc_dpms, + .prepare = tdfx_crtc_prepare, + .commit = tdfx_crtc_commit, + .mode_fixup = tdfx_crtc_mode_fixup, + .mode_set = tdfx_crtc_mode_set, + .mode_set_base = tdfx_crtc_set_base, + .load_lut = tdfx_crtc_load_lut, +}; + +static int +tdfx_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_prv, + uint32_t handle, uint32_t width, uint32_t height) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + u32 vidcfg; + + // What about the depth :-( + if (width > 64 || height > 64) + return -EINVAL; + + vidcfg = DRM_READ32(dev_priv->mmio, VIDPROCCFG); + banshee_make_room(dev_priv, 1); + if (handle) { + DRM_WRITE32(dev_priv->mmio, VIDPROCCFG, vidcfg | VIDCFG_HWCURSOR_ENABLE); + } else + DRM_WRITE32(dev_priv->mmio, VIDPROCCFG, vidcfg & ~VIDCFG_HWCURSOR_ENABLE); + return 0; +} + +static int +tdfx_crtc_cursor_move(struct drm_crtc *crtc, int x, int y) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, HWCURLOC, ((y + 64) << 16) | (x + 64)); + return 0; +} + +void +tdfx_crtc_gamma_set(struct drm_crtc *crtc, u16 *red, u16 *green, u16 *blue, + uint32_t size) +{ + drm_tdfx_private_t *dev_priv = crtc->dev->dev_private; + struct drm_framebuffer *fb = crtc->fb; + int color, regno, tmp = 0; + + if (size > 256 || fb->depth != 8) + return; + + color = (((u32) *red & 0xff00) << 8) | + (((u32) *green & 0xff00) << 0) | + (((u32) *blue & 0xff00) >> 8); + + for (regno = 0; regno < size; regno++) { + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, DACADDR, regno); + /* read after write makes it working */ + DRM_READ32(dev_priv->mmio, DACADDR); + + /* Change color only if needed */ + tmp = DRM_READ32(dev_priv->mmio, DACDATA); + if (color != tmp) { + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, DACDATA, color); + } + } +} + +static const struct drm_crtc_funcs tdfx_crtc_funcs = { +/* .save = tdfx_crtc_save, + .restore = tdfx_crtc_restore, +*/ + .cursor_set = tdfx_crtc_cursor_set, + .cursor_move = tdfx_crtc_cursor_move, + .gamma_set = tdfx_crtc_gamma_set, + .destroy = drm_crtc_cleanup, + .set_config = drm_crtc_helper_set_config, +}; + +static int +tdfx_crtc_init(struct drm_device *dev, int pipe) +{ + struct drm_crtc *crtc; + + crtc = kzalloc(sizeof(struct drm_crtc) + sizeof(struct drm_connector *), GFP_KERNEL); + if (!crtc) + return -ENOMEM; + + drm_crtc_init(dev, crtc, &tdfx_crtc_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); + drm_crtc_helper_add(crtc, &tdfx_helper_funcs); + return 0; +} + +void tdfx_modeset_init(struct drm_device *dev) +{ + drm_mode_config_init(dev); + + dev->mode_config.min_width = 320; + dev->mode_config.min_height = 200; + dev->mode_config.max_width = 2048; + dev->mode_config.max_height = 2048; + + // Only one CRT Controller chip on board + tdfx_crtc_init(dev, 0); + + // which only supports CRT type monitors + tdfx_crt_init(dev); + // except for some banshee cards which have TV ports : TODO + //tdfx_tv_init(dev); + + tdfx_framebuffer_init(dev); +} diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c index ec5a43e..f91bba1 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.c +++ b/drivers/gpu/drm/tdfx/tdfx_drv.c @@ -31,16 +31,327 @@ */ #include "drmP.h" +#include "drm_pciids.h" +#include + #include "tdfx_drv.h" -#include "drm_pciids.h" +extern int tdfx_modeset_init(struct drm_device *dev); + +static inline void crt_outb(drm_tdfx_private_t *dev_priv, u32 idx, u8 val) +{ + vga_outb(dev_priv, CRT_I, idx); + vga_outb(dev_priv, CRT_D, val); +} + +int tdfx_preinit(struct drm_device *dev) +{ + u32 pciinit0,miscinit0,miscinit1,draminit0,draminit1,agpinit0; + u32 pllctrl1,pllctrl2,sgrammode, strapinfo, cmd; + drm_tdfx_private_t *dev_priv = dev->dev_private; + u16 romconftable, oemconftable; + void __iomem *romaddr; + size_t rom_size; + int rc = 0, i; + + // Test if memory is initialized + pci_read_config_dword(dev->pdev, PCI_COMMAND, &cmd); + if (cmd & PCI_COMMAND_MEMORY) { + DRM_INFO("PCI memory enabled\n"); + return rc; // It was initialized from bios. + } else { + DRM_INFO("PCI memory disabled\n"); + } + + // Getting ROM size + rom_size=(DRM_READ32(dev_priv->mmio, MISCINIT1) & MISCINIT1_ROM_SIZE) ? 64*1024 : 32*1024; + // Mapping ROM area + romaddr = pci_map_rom(dev->pdev, &rom_size); + + // Get reg. init values + romconftable=readw(romaddr+0x50); + if (romconftable > rom_size - 4) { + rc = -ENXIO; + goto err; + } + oemconftable=readw(romaddr+romconftable); + if (oemconftable > rom_size - 9 * 4) { + rc = -ENXIO; + goto err; + } + pciinit0=readl(romaddr+oemconftable); + miscinit0=readl(romaddr+oemconftable+4); + miscinit1=readl(romaddr+oemconftable+8); + draminit0=readl(romaddr+oemconftable+12); + draminit1=readl(romaddr+oemconftable+16); + agpinit0=readl(romaddr+oemconftable+20); + pllctrl1=readl(romaddr+oemconftable+24); + pllctrl2=readl(romaddr+oemconftable+28); + sgrammode=readl(romaddr+oemconftable+32); + + strapinfo=DRM_READ32(dev_priv->mmio, STRAPINFO); + + // pciinit0, agp bus + if (strapinfo & 8) pciinit0 |= 0x300; + + // VMI_DATA 0-4 + miscinit1=((strapinfo & 0x1f) << 24) | miscinit1; + // VMI_DATA 5-6 + draminit0=((strapinfo & 0x60) << 26) | draminit0; + + // VMI_ADDR 1-2 + draminit1=((strapinfo & 0x200) << 16) | + ((strapinfo & 0x400) << 20) | + draminit1; + + DRM_WRITE32(dev_priv->mmio, PCIINIT0,pciinit0); + DRM_WRITE32(dev_priv->mmio, VGAINIT0, VGAINIT0_EXT_ENABLE | + VGAINIT0_WAKEUP_3C3 | + VGAINIT0_LEGACY_DISABLE | + VGAINIT0_8BIT_DAC); + + if (dev->pdev->device != PCI_DEVICE_ID_3DFX_BANSHEE) + DRM_WRITE32(dev_priv->mmio, VIDINFORMAT, 0); + +// vga_outb(dev_priv, 0x3c3, 1); + vga_outb(dev_priv, 0x3c2, 0x67); + vga_outb(dev_priv, 0x3ce, 0x6); + vga_outb(dev_priv, 0x3cf, 1); // graphics mode + + crt_outb(dev_priv, 0x1c, 0); + if (dev->pdev->device != PCI_DEVICE_ID_3DFX_BANSHEE) { + crt_outb(dev_priv, 0x1d, 0); + crt_outb(dev_priv, 0x1e, 0); + } + + DRM_WRITE32(dev_priv->mmio, VIDSERPARPORT, (DRM_READ32(dev_priv->mmio,VIDSERPARPORT) & 0xf07fffff) | 0x80000000); + DRM_WRITE32(dev_priv->mmio, VIDINFORMAT, 0); + + DRM_WRITE32(dev_priv->mmio, DRAMINIT1, draminit1); + DRM_WRITE32(dev_priv->mmio, TMUGBEINIT, 0xf0ff0); + DRM_WRITE32(dev_priv->mmio, DRAMINIT0, draminit0); + DRM_WRITE32(dev_priv->mmio, MISCINIT0, miscinit0); + DRM_WRITE32(dev_priv->mmio, DRAMDATA, sgrammode); + DRM_WRITE32(dev_priv->mmio, DRAMCOMMAND, 0x10d);// write sgram mode + DRM_WRITE32(dev_priv->mmio, MISCINIT1, miscinit1); + DRM_WRITE32(dev_priv->mmio, AGPINIT, agpinit0); + DRM_WRITE32(dev_priv->mmio, PLLCTRL1, pllctrl1); +// DRM_WRITE32(dev_priv->mmio, PLLCTRL2, pllctrl2); + DRM_WRITE32(dev_priv->mmio, LFBMEMORYCONFIG, 0xa2200 | 0x1fff); + + DRM_WRITE32(dev_priv->mmio, SIPMONITOR, 0x40000000); +// DRM_WRITE32(dev_priv->mmio, VGAINIT1, 0); + + // initial vga core settings + vga_outb(dev_priv, 0x3c8, 0); + for (i = 0; i < 256; i++) { + vga_outb(dev_priv, 0x3c9, 8); + vga_outb(dev_priv, 0x3c9, 8); + vga_outb(dev_priv, 0x3c9, 8); + } + +err: + pci_unmap_rom(dev->pdev, romaddr); + return rc; +} + +unsigned long do_lfb_size(struct drm_device *dev) +{ + drm_tdfx_private_t *par = dev->dev_private; + u32 draminit0_strap, draminit1_strap; + u32 draminit1, miscinit1; + int memtype = MEM_TYPE_SGRAM; + int memsize = 0, partsize; /* in MB */ + int nchips; + int has_sgram; + + draminit1_strap = DRM_READ32(par->mmio, DRAMINIT1); + draminit1_strap &= DRAMINIT1_MEM_SDRAM; + if (draminit1_strap) + memtype = MEM_TYPE_SDRAM; + draminit1 = 2 << SST_SGRAM_OFLOP_DEL_ADJ_SHIFT; + draminit1 |= SST_SGRAM_CLK_NODELAY; + draminit1 |= SST_DRAM_REFRESH_EN; + draminit1 |= + (0x18 << SST_DRAM_REFRESH_VALUE_SHIFT) & + SST_DRAM_REFRESH_VALUE; + draminit1 &= ~SST_MCTL_TYPE_SDRAM; + draminit1 |= draminit1_strap; + DRM_WRITE32(par->mmio, DRAMINIT1, draminit1); + + draminit0_strap = DRM_READ32(par->mmio, DRAMINIT0); + nchips = ((draminit0_strap & SST_SGRAM_NUM_CHIPSETS) == 0) ? 4 : 8; + has_sgram = draminit1_strap & DRAMINIT1_MEM_SDRAM; + if (has_sgram) + memtype = MEM_TYPE_SDRAM; + if (dev->pdev->device <= PCI_DEVICE_ID_3DFX_VOODOO3) { + /* Banshee/Voodoo3 */ + if (memtype == MEM_TYPE_SGRAM) { + switch (draminit0_strap & SST_SGRAM_TYPE) { + case SST_SGRAM_TYPE_8MBIT: + partsize = 8; + break; + case SST_SGRAM_TYPE_16MBIT: + partsize = 16; + break; + default: + DRM_ERROR("Invalid sgram type %lu\n", + draminit0_strap & SST_SGRAM_TYPE); + return 0; + } + memsize = (nchips * partsize) >> 3; + } else { + memsize = 16 * 1024; + } + } else { + /* Voodoo4/5 */ + int banks = ((draminit0_strap & BIT(30)) == 0) ? 2 : 4; + + partsize = 1 << ((draminit0_strap & 0x38000000) >> 28); + memsize = nchips * partsize * banks; + } + + /* disable block writes for SDRAM */ + miscinit1 = DRM_READ32(par->mmio, MISCINIT1); + if (memtype == MEM_TYPE_SDRAM) + miscinit1 |= SST_DISABLE_2D_BLOCK_WRITE; + miscinit1 |= MISCINIT1_CLUT_INV; + DRM_WRITE32(par->mmio, MISCINIT1, miscinit1); + + return memsize * 1024; // # of KBytes +} + +int tdfx_driver_unload(struct drm_device *dev) +{ + struct drm_tdfx_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev->pdev; + + if (dev_priv->framebuffer) + drm_rmmap(dev, dev_priv->framebuffer); + if (dev_priv->mmio) + drm_rmmap(dev, dev_priv->mmio); + if (dev_priv->iobase) + release_region(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2)); + kfree(dev_priv); + return 0; +} + +int tdfx_driver_load(struct drm_device *dev, unsigned long flags) +{ + struct drm_tdfx_private *dev_priv = dev->dev_private; + struct pci_dev *pdev = dev->pdev; + int size, rc = -ENOMEM; + + dev_priv = kzalloc(sizeof(drm_tdfx_private_t), GFP_KERNEL); + if (!dev_priv) + return rc; + + memset(dev_priv, 0, sizeof(drm_tdfx_private_t)); + dev->dev_private = dev_priv; + dev_priv->dev = dev; + + switch (pdev->device) { + case PCI_DEVICE_ID_3DFX_BANSHEE: + dev->devname = kstrdup("3Dfx Banshee", GFP_KERNEL); + dev_priv->max_pixclock = BANSHEE_MAX_PIXCLOCK; + break; + case PCI_DEVICE_ID_3DFX_VOODOO3: + dev->devname = kstrdup("3Dfx Voodoo3", GFP_KERNEL); + dev_priv->max_pixclock = VOODOO3_MAX_PIXCLOCK; + break; + case PCI_DEVICE_ID_3DFX_VOODOO5: + dev->devname = kstrdup("3Dfx Voodoo5", GFP_KERNEL); + dev_priv->max_pixclock = VOODOO5_MAX_PIXCLOCK; + break; + } + if (!dev->devname) + goto err; + + // Setup IO space - drm_addmap doesn't handle PIO :-( + dev_priv->iobase = pci_resource_start(pdev, 2); + if (!request_region(pci_resource_start(pdev, 2), + pci_resource_len(pdev, 2), "tdfx iobase")) { + DRM_ERROR("3Dfx: Can't reserve iobase\n"); + rc = -ENXIO; + goto err; + } + + // drm_addmap is missing request_mem_region :-( + rc = drm_addmap(dev, pci_resource_start(pdev, 0), + pci_resource_len(pdev, 0), _DRM_REGISTERS, + _DRM_READ_ONLY, &dev_priv->mmio); + if (rc) { + DRM_ERROR("3Dfx: Can't reserve register base\n"); + goto err; + } + + dev->mode_config.fb_base = pci_resource_start(pdev, 1); + tdfx_preinit(dev); + size = do_lfb_size(dev); + + rc = generic_ttm_global_init(&dev_priv->ttm); + if (rc) { + DRM_ERROR("3Dfx: Failed to setup TTM\n"); + goto err; + } + + rc = ttm_bo_device_init(&dev_priv->ttm.bdev, + dev_priv->ttm.bo_global_ref.ref.object, + &tdfx_bo_driver, DRM_FILE_PAGE_OFFSET, + false); + if (rc) { + DRM_ERROR("3Dfx: Error initialising bo driver: %d\n", rc); + goto err; + } + + if (size > drm_get_resource_len(dev, 1)) + size = drm_get_resource_len(dev, 1); + + /* reserve 8192 bits for cursor */ + /* the 2.4 driver says PAGE_MASK boundary is not enough for Voodoo4 */ + size = (size - 1024) & (PAGE_MASK << 1); + + /* mappable vram */ + rc = ttm_bo_init_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM, size >> PAGE_SHIFT); + if (rc) { + DRM_ERROR("3Dfx: Failed VRAM mm init: %d\n", rc); + goto err; + } + + /* + rc = ttm_bo_create(&dev_priv->ttm.bdev, size, + TTM_PL_VRAM, &nvbo->placement, align, 0, + false, NULL, **bo); + if (rc) { + * Call cleanup * + goto err; + }*/ + + rc = drm_addmap(dev, pci_resource_start(pdev, 1), size, + _DRM_FRAME_BUFFER, _DRM_WRITE_COMBINING, + &dev_priv->framebuffer); + if (rc) { + DRM_ERROR("3Dfx: Can't reserve framebuffer\n"); + goto err; + } + + if (!tdfx_modeset_init(dev)) + return 0; +err: + tdfx_driver_unload(dev); + return rc; +} static struct pci_device_id pciidlist[] = { tdfx_PCI_IDS }; +MODULE_DEVICE_TABLE(pci, pciidlist); -static struct drm_driver driver = { - .driver_features = DRIVER_USE_MTRR, +static struct drm_driver tdfx_driver = { + .driver_features = DRIVER_USE_MTRR | DRIVER_MODESET, + .load = tdfx_driver_load, + .unload = tdfx_driver_unload, .reclaim_buffers = drm_core_reclaim_buffers, .get_map_ofs = drm_core_get_map_ofs, .get_reg_ofs = drm_core_get_reg_ofs, @@ -54,8 +365,8 @@ static struct drm_driver driver = { .fasync = drm_fasync, }, .pci_driver = { - .name = DRIVER_NAME, - .id_table = pciidlist, + .name = DRIVER_NAME, + .id_table = pciidlist, }, .name = DRIVER_NAME, @@ -68,12 +379,12 @@ static struct drm_driver driver = { static int __init tdfx_init(void) { - return drm_init(&driver); + return drm_init(&tdfx_driver); } static void __exit tdfx_exit(void) { - drm_exit(&driver); + drm_exit(&tdfx_driver); } module_init(tdfx_init); diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.h b/drivers/gpu/drm/tdfx/tdfx_drv.h index 84204ec..5fa5eec 100644 --- a/drivers/gpu/drm/tdfx/tdfx_drv.h +++ b/drivers/gpu/drm/tdfx/tdfx_drv.h @@ -28,20 +28,311 @@ * Gareth Hughes */ -#ifndef __TDFX_H__ -#define __TDFX_H__ +#ifndef _TDFX_H +#define _TDFX_H /* General customization: */ - -#define DRIVER_AUTHOR "VA Linux Systems Inc." +#define DRIVER_AUTHOR "James Simmons" #define DRIVER_NAME "tdfx" #define DRIVER_DESC "3dfx Banshee/Voodoo3+" -#define DRIVER_DATE "20010216" +#define DRIVER_DATE "20090304" -#define DRIVER_MAJOR 1 +#define DRIVER_MAJOR 2 #define DRIVER_MINOR 0 #define DRIVER_PATCHLEVEL 0 -#endif +#define SST_SGRAM_OFLOP_DEL_ADJ_SHIFT 20 +#define SST_SGRAM_CLK_NODELAY BIT(13) +#define SST_DRAM_REFRESH_EN BIT(0) +#define SST_DRAM_REFRESH_VALUE_SHIFT 1 +#define SST_DRAM_REFRESH_VALUE (0x1FF< +#include +#include +#include +#include + +#define BANSHEE_MAX_PIXCLOCK 270000 +#define VOODOO3_MAX_PIXCLOCK 300000 +#define VOODOO5_MAX_PIXCLOCK 350000 + +struct banshee_reg { + /* VGA rubbish */ + unsigned char att[21]; + unsigned char crt[25]; + unsigned char gra[9]; + unsigned char misc[1]; + unsigned char seq[5]; + + /* Banshee extensions */ + unsigned char ext[2]; + unsigned long vidcfg; + unsigned long vidpll; + unsigned long mempll; + unsigned long gfxpll; + unsigned long dacmode; + unsigned long vgainit0; + unsigned long vgainit1; + unsigned long screensize; + unsigned long stride; + unsigned long startaddr; + unsigned long srcaddr; + unsigned long dstaddr; + unsigned long clip0min; + unsigned long clip0max; + unsigned long clip1min; + unsigned long clip1max; + unsigned long miscinit0; +}; + +#include +#include + +struct tdfx_i2c_chan { + struct i2c_adapter adapter; + struct i2c_algo_bit_data algo; +}; + +#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT) +struct ttm { + struct ttm_global_reference mem_global_ref; + struct ttm_bo_global_ref bo_global_ref; + struct ttm_bo_device bdev; + /*spinlock_t bo_list_lock; + struct list_head bo_list; + atomic_t validate_sequence;*/ +}; + + +typedef struct drm_tdfx_private { + struct drm_device *dev; + u32 max_pixclock; + drm_local_map_t *framebuffer; + drm_local_map_t *mmio; + unsigned long iobase; + void *ddc_bus; + void *i2c_bus; + struct drm_encoder *best; + + struct tdfx_i2c_chan chan[2]; + + struct ttm ttm; +} drm_tdfx_private_t; + +void tdfx_crt_init(struct drm_device *dev); +void *tdfx_i2c_create(struct drm_device *dev, int connector_type); +void tdfx_i2c_destroy(struct drm_device *dev, int connector_type); +int tdfx_get_modes(struct drm_connector *connector); + +extern struct ttm_bo_driver tdfx_bo_driver; +int generic_ttm_global_init(struct ttm *ttm); + +int banshee_wait_idle(struct drm_device *dev); +void tdfx_fillrect(struct drm_fb_helper *helper, struct drm_clip_rect *rect, + u32 color, u32 rop); +void tdfx_copyarea(struct drm_fb_helper *helper, const struct fb_copyarea *area); +int tdfx_imageblit(struct drm_fb_helper *helper, const struct fb_image *image); + +static inline u8 vga_inb(drm_tdfx_private_t *par, u32 reg) +{ + return inb((unsigned long) par->iobase + reg - 0x300); +} + +static inline void vga_outb(drm_tdfx_private_t *par, u32 reg, u8 val) +{ + outb(val, (unsigned long) par->iobase + reg - 0x300); +} + +static inline void banshee_make_room(drm_tdfx_private_t *par, int size) +{ + /* Note: The Voodoo3's onboard FIFO has 32 slots. This loop + * won't quit if you ask for more. */ + while ((DRM_READ32(par->mmio, STATUS) & 0x1f) < size - 1) + cpu_relax(); +} + +#endif /* __KERNEL__ */ + +#endif /* _TDFX_H */ diff --git a/drivers/gpu/drm/tdfx/tdfx_fb.c b/drivers/gpu/drm/tdfx/tdfx_fb.c new file mode 100644 index 0000000..6a5ee37 --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_fb.c @@ -0,0 +1,126 @@ +/* tdfx_fb.c -- tdfx driver -*- linux-c -*- + * + * 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 (including the next + * paragraph) 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 + * PRECISION INSIGHT 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. + * + * Authors: + * James Simmons + */ + +#include "drmP.h" +#include "drm_crtc_helper.h" + +#include "tdfx_drv.h" + +extern int tdfxfb_remove(struct drm_device *dev,struct drm_framebuffer *fb); +extern int tdfxfb_changed(struct drm_device *dev); + +static void tdfx_user_framebuffer_destroy(struct drm_framebuffer *fb) +{ + struct ttm_object_file *tfile = fb->helper_private; + struct ttm_base_object *base; + + /*base = ttm_base_object_lookup(tfile, handle); + if (unlikely(base == NULL)) + return -EINVAL; + + ttm_ref_object_base_unref(tfile, base->hash.key, TTM_REF_USAGE); + //struct drm_device *dev = fb->dev; + + if (fb->fbdev) + tdfxfb_remove(dev, fb);*/ + + drm_framebuffer_cleanup(fb); +} + +static int tdfx_user_framebuffer_create_handle(struct drm_framebuffer *fb, + struct drm_file *file_priv, + unsigned int *handle) +{ + struct ttm_object_file *tfile; + + fb->helper_private = tfile; + return 0; +} + +static const struct drm_framebuffer_funcs tdfx_fb_funcs = { + .destroy = tdfx_user_framebuffer_destroy, + .create_handle = tdfx_user_framebuffer_create_handle, +}; + +struct drm_framebuffer * +tdfx_framebuffer_alloc(struct drm_device *dev, struct drm_mode_fb_cmd *mode_cmd) +{ + struct drm_framebuffer *fb = NULL; + + //mode_cmd->height = map->size / mode_cmd->pitch; + + fb = kzalloc(sizeof(struct drm_framebuffer), GFP_KERNEL); + + if (fb) { + if (drm_framebuffer_init(dev, fb, &tdfx_fb_funcs)) { + DRM_ERROR("framebuffer init failed\n"); + kfree(fb); + } + drm_helper_mode_fill_fb_struct(fb, mode_cmd); + } + return fb; +} + +static struct drm_framebuffer * +tdfx_user_framebuffer_create(struct drm_device *dev, struct drm_file *filp, + struct drm_mode_fb_cmd *mode_cmd) +{ + struct drm_framebuffer *fb = NULL; + drm_local_map_t *framebuffer; + + framebuffer = drm_core_findmap(dev, mode_cmd->handle); + + fb = tdfx_framebuffer_alloc(dev, mode_cmd); + return fb; +} + +void tdfx_output_poll_changed(struct drm_device *dev) +{ + /*drm_i915_private_t *dev_priv = dev->dev_private; + + drm_fb_helper_hotplug_event(&dev_priv->fbdev->helper);*/ +} + +static const struct drm_mode_config_funcs tdfx_mode_funcs = { + .fb_create = tdfx_user_framebuffer_create, + .output_poll_changed = tdfx_output_poll_changed, +}; + +int tdfx_framebuffer_init(struct drm_device *dev) +{ + /*dev->mode_config.funcs = (void *)&tdfx_mode_funcs; + + ret = drm_fb_helper_init(dev, &ifbdev->helper, 1, 1); + if (ret) { + kfree(ifbdev); + return ret; + } + + drm_fb_helper_single_add_all_connectors(&ifbdev->helper); + drm_fb_helper_initial_config(&ifbdev->helper, 32); + + drm_helper_initial_config(dev);*/ + return 0; +} diff --git a/drivers/gpu/drm/tdfx/tdfx_fbcon.c b/drivers/gpu/drm/tdfx/tdfx_fbcon.c new file mode 100644 index 0000000..1f34ea5 --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_fbcon.c @@ -0,0 +1,357 @@ +/* + * tdfxfb.c + * + * Author: Hannu Mallat + * + * Copyright © 1999 Hannu Mallat + * All rights reserved + * + * Created : Thu Sep 23 18:17:43 1999, hmallat + * Last modified: Tue Nov 2 21:19:47 1999, hmallat + * + * I2C part copied from the i2c-voodoo3.c driver by: + * Frodo Looijaard , + * Philip Edelbrock , + * Ralph Metzler , and + * Mark D. Studebaker + * + * Lots of the information here comes from the Daryll Strauss' Banshee + * patches to the XF86 server, and the rest comes from the 3dfx + * Banshee specification. I'm very much indebted to Daryll for his + * work on the X server. + * + * Voodoo3 support was contributed Harold Oga. Lots of additions + * (proper acceleration, 24 bpp, hardware cursor) and bug fixes by Attila + * Kesmarki. Thanks guys! + * + * Voodoo1 and Voodoo2 support aren't relevant to this driver as they + * behave very differently from the Voodoo3/4/5. For anyone wanting to + * use frame buffer on the Voodoo1/2, see the sstfb driver (which is + * located at http://www.sourceforge.net/projects/sstfb). + * + * While I _am_ grateful to 3Dfx for releasing the specs for Banshee, + * I do wish the next version is a bit more complete. Without the XF86 + * patches I couldn't have gotten even this far... for instance, the + * extensions to the VGA register set go completely unmentioned in the + * spec! Also, lots of references are made to the 'SST core', but no + * spec is publicly available, AFAIK. + * + * The structure of this driver comes pretty much from the Permedia + * driver by Ilario Nardinocchi, which in turn is based on skeletonfb. + * + * TODO: + * - multihead support (basically need to support an array of fb_infos) + * - support other architectures (PPC, Alpha); does the fact that the VGA + * core can be accessed only thru I/O (not memory mapped) complicate + * things? + * + * Version history: + * + * 0.1.4 (released 2002-05-28) ported over to new fbdev api by James Simmons + * + * 0.1.3 (released 1999-11-02) added Attila's panning support, code + * reorg, hwcursor address page size alignment + * (for mmaping both frame buffer and regs), + * and my changes to get rid of hardcoded + * VGA i/o register locations (uses PCI + * configuration info now) + * 0.1.2 (released 1999-10-19) added Attila Kesmarki's bug fixes and + * improvements + * 0.1.1 (released 1999-10-07) added Voodoo3 support by Harold Oga. + * 0.1.0 (released 1999-10-06) initial version + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmP.h" +#include "drm_fb_helper.h" +#include "drm_crtc_helper.h" + +#include "tdfx_drv.h" + +struct drm_framebuffer *tdfx_framebuffer_alloc(struct drm_device *dev, struct drm_mode_fb_cmd *mode_cmd); + +/* + * FillRect 2D command (solidfill or invert (via ROP_XOR)) + */ +static void tdfxfb_fillrect(struct fb_info *info, + const struct fb_fillrect *rect) +{ + struct drm_fb_helper *helper = info->par; + struct drm_clip_rect area; + + area.x1 = rect->dx; + area.y1 = rect->dy; + area.x2 = rect->dx + rect->width; + area.y2 = rect->dy + rect->height; + tdfx_fillrect(helper, &area, rect->color, rect->rop); +} + +/* + * Screen-to-Screen BitBlt 2D command (for the bmove fb op.) + */ +static void tdfxfb_copyarea(struct fb_info *info, + const struct fb_copyarea *area) +{ + struct drm_fb_helper *helper = info->par; + + tdfx_copyarea(helper, area); +} + +static void tdfxfb_imageblit(struct fb_info *info, const struct fb_image *image) +{ + struct drm_fb_helper *helper = info->par; + + if (tdfx_imageblit(helper, image)) + cfb_imageblit(info, image); +} + +static int tdfxfb_sync(struct fb_info *info) +{ + struct drm_fb_helper *helper = info->par; + + return banshee_wait_idle(helper->dev); +} + +static int tdfxfb_cursor(struct fb_info *info, struct fb_cursor *cursor) +{ + struct drm_fb_helper *helper = info->par; + struct drm_mode_set *modeset = &helper->crtc_info[0].mode_set; + struct drm_crtc *crtc = modeset->crtc; + struct drm_device *dev = helper->dev; + drm_tdfx_private_t *dev_priv; + + dev_priv = dev->dev_private; + + /* wrong bpp :-( DRM needs to check this as well */ + if (cursor->image.depth > 1) + return -EINVAL; + + crtc->funcs->cursor_set(crtc, NULL, cursor->enable, cursor->image.width, + cursor->image.height); + + /* + * If the cursor is not be changed this means either we want the + * current cursor state (if enable is set) or we want to query what + * we can do with the cursor (if enable is not set) + */ + if (!cursor->set) + return 0; + + if (cursor->set & FB_CUR_SETPOS) { + int x = cursor->image.dx - info->var.xoffset; + int y = cursor->image.dy - info->var.yoffset; + + crtc->funcs->cursor_move(crtc, x, y); + } + + // Ugh. No mapping to DRM. + if (cursor->set & FB_CUR_SETCMAP) { + struct fb_cmap cmap = info->cmap; + u32 bg_idx = cursor->image.bg_color; + u32 fg_idx = cursor->image.fg_color; + unsigned long bg_color, fg_color; + + fg_color = (((u32)cmap.red[fg_idx] & 0xff00) << 8) | + (((u32)cmap.green[fg_idx] & 0xff00) << 0) | + (((u32)cmap.blue[fg_idx] & 0xff00) >> 8); + bg_color = (((u32)cmap.red[bg_idx] & 0xff00) << 8) | + (((u32)cmap.green[bg_idx] & 0xff00) << 0) | + (((u32)cmap.blue[bg_idx] & 0xff00) >> 8); + banshee_make_room(dev_priv, 2); + DRM_WRITE32(dev_priv->mmio, HWCURC0, bg_color); + DRM_WRITE32(dev_priv->mmio, HWCURC1, fg_color); + } + + if (cursor->set & (FB_CUR_SETIMAGE | FB_CUR_SETSHAPE)) { + /* + * Voodoo 3 and above cards use 2 monochrome cursor patterns. + * The reason is so the card can fetch 8 words at a time + * and are stored on chip for use for the next 8 scanlines. + * This reduces the number of times for access to draw the + * cursor for each screen refresh. + * Each pattern is a bitmap of 64 bit wide and 64 bit high + * (total of 8192 bits or 1024 bytes). The two patterns are + * stored in such a way that pattern 0 always resides in the + * lower half (least significant 64 bits) of a 128 bit word + * and pattern 1 the upper half. If you examine the data of + * the cursor image the graphics card uses then from the + * begining you see line one of pattern 0, line one of + * pattern 1, line two of pattern 0, line two of pattern 1, + * etc etc. The linear stride for the cursor is always 16 bytes + * (128 bits) which is the maximum cursor width times two for + * the two monochrome patterns. + */ + int cursorbase = dev_priv->framebuffer->size; + u8 *bitmap = (u8 *)cursor->image.data; + u8 *mask = (u8 *)cursor->mask; + int i; + + for (i = 0; i < 1024;i++) + DRM_WRITE8(dev_priv->framebuffer, cursorbase + i, 0); + + for (i = 0; i < cursor->image.height; i++) { + int j = (cursor->image.width + 7) >> 3, h = 0; + + for (; j > 0; j--) { + u8 data = *mask ^ *bitmap; + if (cursor->rop == ROP_COPY) + data = *mask & *bitmap; + /* Pattern 0. Copy the cursor mask to it */ + DRM_WRITE8(dev_priv->framebuffer, cursorbase + h, *mask); + mask++; + /* Pattern 1. Copy the cursor bitmap to it */ + DRM_WRITE8(dev_priv->framebuffer, cursorbase + h + 8, data); + bitmap++; + h++; + } + cursorbase += 16; + } + } + return 0; +} + +static struct fb_ops tdfxfb_ops = { + .owner = THIS_MODULE, + .fb_set_par = drm_fb_helper_set_par, + .fb_setcmap = drm_fb_helper_setcmap, + .fb_blank = drm_fb_helper_blank, + .fb_pan_display = drm_fb_helper_pan_display, + //.fb_cursor = tdfxfb_cursor, + .fb_fillrect = tdfxfb_fillrect, + .fb_copyarea = tdfxfb_copyarea, + .fb_imageblit = tdfxfb_imageblit, + .fb_sync = tdfxfb_sync, +}; + +/** + * tdfxfb_probe - Device Initializiation + * + * @pdev: PCI Device to initialize + * @id: PCI Device ID + * + * Initializes and allocates resources for PCI device @pdev. + * + */ +static int tdfxfb_create(struct drm_fb_helper *helper, + struct drm_fb_helper_surface_size *sizes) +{ + struct drm_mode_fb_cmd mode_cmd; + struct drm_device *dev = helper->dev; + struct drm_framebuffer *fb; + struct drm_map_list *r_list, *list_t; + struct drm_local_map *map = NULL; + struct fb_info *info; + int rc = 0; + + mode_cmd.width = sizes->surface_width; + mode_cmd.height = sizes->surface_height; + + mode_cmd.bpp = sizes->surface_bpp; + mode_cmd.depth = sizes->surface_depth; + mode_cmd.pitch = mode_cmd.width * ((mode_cmd.bpp + 7) >> 3); + + fb = tdfx_framebuffer_alloc(dev, &mode_cmd); + if (!fb) + goto out_err; + + info = framebuffer_alloc(0, &dev->pdev->dev); + if (!info) + return -ENOMEM; + + helper->fbdev = info; + info->par = helper; + + info->fbops = &tdfxfb_ops; + info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN; + info->flags |= FBINFO_HWACCEL_FILLRECT | + FBINFO_HWACCEL_COPYAREA | + FBINFO_HWACCEL_IMAGEBLIT | + FBINFO_READS_FAST; + + /* Configure the default fb_fix_screeninfo */ + strcpy(info->fix.id, dev->devname); + info->fix.mmio_start = info->fix.mmio_len = 0; + + list_for_each_entry_safe(r_list, list_t, &dev->maplist, head) { + map = r_list->map; + if (map->type == _DRM_FRAME_BUFFER) { + map->handle = ioremap_nocache(map->offset, map->size); + if (!map->handle) { + DRM_ERROR("fb: Can't remap framebuffer.\n"); + return rc; + } + break; + } + } + + /* reserve 8192 bits for cursor */ + /* the 2.4 driver says PAGE_MASK boundary is not enough for Voodoo4 */ + map->size = (map->size - 1024) & (PAGE_MASK << 1); + + info->fix.smem_start = map->offset; + info->fix.smem_len = map->size; + if (!info->fix.smem_len) { + DRM_ERROR("fb: Can't count %s memory.\n", info->fix.id); + goto out_err; + } + + info->screen_base = map->handle; + if (!info->screen_base) { + DRM_ERROR("fb: Can't remap %s framebuffer.\n", + info->fix.id); + goto out_err; + } + + DRM_INFO("fb: %s memory = %dK\n", info->fix.id, + info->fix.smem_len >> 10); + + if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) { + DRM_ERROR("tdfxfb: Can't allocate color map\n"); + goto out_err; + } + return 0; + +out_err: + framebuffer_release(info); + return -ENXIO; +} + +/** + * tdfxfb_remove - Device removal + * + * @pdev: PCI Device to cleanup + * + * Releases all resources allocated during the course of the driver's + * lifetime for the PCI device @pdev. + * + */ +int tdfxfb_remove(struct drm_device *dev,struct drm_framebuffer *fb) +{ + /*struct fb_info *info = fb->fbdev; + + if (!fb) + return -EINVAL; + + info = fb->fbdev; + if (info) { + unregister_framebuffer(info); + iounmap(info->screen_base); + fb_dealloc_cmap(&info->cmap); + framebuffer_release(info); + }*/ + return 0; +} + +MODULE_AUTHOR("James Simmons "); +MODULE_DESCRIPTION("3Dfx DRM framebuffer device driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpu/drm/tdfx/tdfx_i2c.c b/drivers/gpu/drm/tdfx/tdfx_i2c.c new file mode 100644 index 0000000..af06d0a --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_i2c.c @@ -0,0 +1,215 @@ +/* + * tdfx I2C + */ + +#include +#include +#include "drmP.h" +#include "drm.h" +#include "drm_crtc.h" +#include "drm_crtc_helper.h" + +#include "tdfx_drv.h" + +/* I2C bit locations in the VIDSERPARPORT register */ +#define DDC_ENAB 0x00040000 +#define DDC_SCL_OUT 0x00080000 +#define DDC_SDA_OUT 0x00100000 +#define DDC_SCL_IN 0x00200000 +#define DDC_SDA_IN 0x00400000 +#define I2C_ENAB 0x00800000 +#define I2C_SCL_OUT 0x01000000 +#define I2C_SDA_OUT 0x02000000 +#define I2C_SCL_IN 0x04000000 +#define I2C_SDA_IN 0x08000000 + +/* The voo GPIO registers don't have individual masks for each bit + so we always have to read before writing. */ + +static void tdfx_i2c_setscl(void *data, int val) +{ + drm_tdfx_private_t *dev_priv = data; + unsigned int r; + + r = DRM_READ32(dev_priv->mmio, VIDSERPARPORT); + if (val) + r |= I2C_SCL_OUT; + else + r &= ~I2C_SCL_OUT; + DRM_WRITE32(dev_priv->mmio, VIDSERPARPORT, r); + DRM_READ32(dev_priv->mmio, VIDSERPARPORT); /* flush posted write */ +} + +static void tdfx_i2c_setsda(void *data, int val) +{ + drm_tdfx_private_t *dev_priv = data; + unsigned int r; + + r = DRM_READ32(dev_priv->mmio, VIDSERPARPORT); + if (val) + r |= I2C_SDA_OUT; + else + r &= ~I2C_SDA_OUT; + DRM_WRITE32(dev_priv->mmio, VIDSERPARPORT, r); + DRM_READ32(dev_priv->mmio, VIDSERPARPORT); /* flush posted write */ +} + +/* The GPIO pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. */ + +static int tdfx_i2c_getscl(void *data) +{ + drm_tdfx_private_t *dev_priv = data; + return (0 != (DRM_READ32(dev_priv->mmio, VIDSERPARPORT) & I2C_SCL_IN)); +} + +static int tdfx_i2c_getsda(void *data) +{ + drm_tdfx_private_t *dev_priv = data; + return (0 != (DRM_READ32(dev_priv->mmio, VIDSERPARPORT) & I2C_SDA_IN)); +} + +static void tdfx_ddc_setscl(void *data, int val) +{ + drm_tdfx_private_t *dev_priv = data; + unsigned int r; + + r = DRM_READ32(dev_priv->mmio, VIDSERPARPORT); + if (val) + r |= DDC_SCL_OUT; + else + r &= ~DDC_SCL_OUT; + DRM_WRITE32(dev_priv->mmio, VIDSERPARPORT, r); + DRM_READ32(dev_priv->mmio, VIDSERPARPORT); /* flush posted write */ +} + +static void tdfx_ddc_setsda(void *data, int val) +{ + drm_tdfx_private_t *dev_priv = data; + unsigned int r; + + r = DRM_READ32(dev_priv->mmio, VIDSERPARPORT); + if (val) + r |= DDC_SDA_OUT; + else + r &= ~DDC_SDA_OUT; + DRM_WRITE32(dev_priv->mmio, VIDSERPARPORT, r); + DRM_READ32(dev_priv->mmio, VIDSERPARPORT); /* flush posted write */ +} + +static int tdfx_ddc_getscl(void *data) +{ + drm_tdfx_private_t *dev_priv = data; + return (0 != (DRM_READ32(dev_priv->mmio, VIDSERPARPORT) & DDC_SCL_IN)); +} + +static int tdfx_ddc_getsda(void *data) +{ + drm_tdfx_private_t *dev_priv = data; + return (0 != (DRM_READ32(dev_priv->mmio, VIDSERPARPORT) & DDC_SDA_IN)); +} + +static int tdfx_setup_ddc_bus(struct tdfx_i2c_chan *chan, const char *name, + struct drm_device *dev) +{ + drm_tdfx_private_t *dev_priv = dev->dev_private; + int rc; + + strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); + chan->adapter.owner = THIS_MODULE; + chan->adapter.class = I2C_CLASS_DDC; + chan->adapter.algo_data = &chan->algo; + chan->adapter.dev.parent = &dev->pdev->dev; + chan->algo.setsda = tdfx_ddc_setsda; + chan->algo.setscl = tdfx_ddc_setscl; + chan->algo.getsda = tdfx_ddc_getsda; + chan->algo.getscl = tdfx_ddc_getscl; + chan->algo.udelay = 10; + chan->algo.timeout = msecs_to_jiffies(500); + chan->algo.data = dev_priv; + + i2c_set_adapdata(&chan->adapter, dev_priv); + + rc = i2c_bit_add_bus(&chan->adapter); + if (rc == 0) + printk(KERN_INFO "I2C bus %s registered.\n", name); + return rc; +} + +static int tdfx_setup_i2c_bus(struct tdfx_i2c_chan *chan, const char *name, + struct drm_device *dev) +{ + drm_tdfx_private_t *dev_priv = dev->dev_private; + int rc; + + strlcpy(chan->adapter.name, name, sizeof(chan->adapter.name)); + chan->adapter.owner = THIS_MODULE; + chan->adapter.class = I2C_CLASS_TV_ANALOG; + chan->adapter.algo_data = &chan->algo; + chan->adapter.dev.parent = &dev->pdev->dev; + chan->algo.setsda = tdfx_i2c_setsda; + chan->algo.setscl = tdfx_i2c_setscl; + chan->algo.getsda = tdfx_i2c_getsda; + chan->algo.getscl = tdfx_i2c_getscl; + chan->algo.udelay = 10; + chan->algo.timeout = msecs_to_jiffies(500); + chan->algo.data = dev_priv; + + i2c_set_adapdata(&chan->adapter, dev_priv); + + rc = i2c_bit_add_bus(&chan->adapter); + if (rc == 0) + printk(KERN_INFO "I2C bus %s registered.\n", name); + return rc; +} + +void *tdfx_i2c_create(struct drm_device *dev, int connector_type) +{ + drm_tdfx_private_t *dev_priv = dev->dev_private; + + DRM_WRITE32(dev_priv->mmio, VIDINFORMAT, 0x8160); + DRM_WRITE32(dev_priv->mmio, VIDSERPARPORT, 0xcffc0020); + + tdfx_setup_ddc_bus(&dev_priv->chan[0], "Voodoo3-DDC", dev); + tdfx_setup_i2c_bus(&dev_priv->chan[1], "Voodoo3-I2C", dev); + return &dev_priv->chan[0].adapter; +} + +void tdfx_i2c_destroy(struct drm_device *dev, int connector_type) +{ + drm_tdfx_private_t *dev_priv = dev->dev_private; + struct i2c_adapter *adapter = NULL; + + if (connector_type == DRM_MODE_CONNECTOR_VGA) + adapter = dev_priv->ddc_bus; + else + adapter = dev_priv->i2c_bus; + + if (adapter) + i2c_del_adapter(adapter); +} + +int tdfx_get_modes(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + drm_tdfx_private_t *dev_priv = dev->dev_private; + struct i2c_adapter *adapter = NULL; + struct edid *edid; + int ret = 0; + + if (connector->connector_type == DRM_MODE_CONNECTOR_VGA) + adapter = dev_priv->ddc_bus; + else + adapter = dev_priv->i2c_bus; + + if (adapter) { + edid = drm_get_edid(connector, adapter); + if (edid) { + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + kfree(edid); + } + } + return ret; +} diff --git a/drivers/gpu/drm/tdfx/tdfx_state.c b/drivers/gpu/drm/tdfx/tdfx_state.c new file mode 100644 index 0000000..a9c30f5 --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_state.c @@ -0,0 +1,292 @@ +/* + * tdfxfb.c + * + * Author: Hannu Mallat + * + * Copyright © 1999 Hannu Mallat + * All rights reserved + * + * Created : Thu Sep 23 18:17:43 1999, hmallat + * Last modified: Tue Nov 2 21:19:47 1999, hmallat + * + * Lots of the information here comes from the Daryll Strauss' Banshee + * patches to the XF86 server, and the rest comes from the 3dfx + * Banshee specification. I'm very much indebted to Daryll for his + * work on the X server. + * + * Voodoo3 support was contributed Harold Oga. Lots of additions + * (proper acceleration, 24 bpp, hardware cursor) and bug fixes by Attila + * Kesmarki. Thanks guys! + * + * Voodoo1 and Voodoo2 support aren't relevant to this driver as they + * behave very differently from the Voodoo3/4/5. For anyone wanting to + * use frame buffer on the Voodoo1/2, see the sstfb driver (which is + * located at http://www.sourceforge.net/projects/sstfb). + * + * While I _am_ grateful to 3Dfx for releasing the specs for Banshee, + * I do wish the next version is a bit more complete. Without the XF86 + * patches I couldn't have gotten even this far... for instance, the + * extensions to the VGA register set go completely unmentioned in the + * spec! Also, lots of references are made to the 'SST core', but no + * spec is publicly available, AFAIK. + * + * The structure of this driver comes pretty much from the Permedia + * driver by Ilario Nardinocchi, which in turn is based on skeletonfb. + * + * TODO: + * - multihead support (basically need to support an array of fb_infos) + * - support other architectures (PPC, Alpha); does the fact that the VGA + * core can be accessed only thru I/O (not memory mapped) complicate + * things? + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "drmP.h" + +#include "tdfx_drv.h" + +#define DPRINTK(a, b...) pr_debug("fb: %s: " a, __func__ , ## b) + +struct drm_par { + struct drm_mode_set modeset[1]; + int cursor_handle; +}; + +/* ------------------------------------------------------------------------- + * Hardware-specific funcions + * ------------------------------------------------------------------------- */ + +int banshee_wait_idle(struct drm_device *dev) +{ + drm_tdfx_private_t *dev_priv = dev->dev_private; + int i = 0; + + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, COMMAND_3D, COMMAND_3D_NOP); + + do { + if ((DRM_READ32(dev_priv->mmio, STATUS) & STATUS_BUSY) == 0) + i++; + } while (i < 3); + + return 0; +} + +/* + * FillRect 2D command (solidfill or invert (via ROP_XOR)) + */ +void tdfx_fillrect(struct drm_fb_helper *fb_helper, struct drm_clip_rect *area, + u32 color, u32 rop) +{ + u32 width = area->x2 - area->x1, height = area->y2 - area->y1; + struct drm_framebuffer *fb = fb_helper->fb; + u32 bpp = fb->bits_per_pixel, stride = fb->pitch; + u32 fmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13); + u32 dx = area->x1, dy = area->y1, dstbase = 0; + drm_tdfx_private_t *dev_priv; + int tdfx_rop; + + dev_priv = fb->dev->dev_private; + + if (rop == ROP_COPY) + tdfx_rop = TDFX_ROP_COPY; + else + tdfx_rop = TDFX_ROP_XOR; + + /* assume always rect->height < 4096 */ + if (area->y2 > 4095) { + dstbase = stride * dy; + dy = 0; + } + /* assume always rect->width < 4096 */ + if (area->x2 > 4095) { + dstbase += dx * bpp >> 3; + dx = 0; + } + banshee_make_room(dev_priv, 7); + DRM_WRITE32(dev_priv->mmio, DSTFORMAT, fmt); + + /* Test for pseudo color mode */ + if (bpp > 8) + bpp = ((u32 *) (fb_helper->pseudo_palette))[color]; + else + bpp = color; + + DRM_WRITE32(dev_priv->mmio, COLORFORE, bpp); + DRM_WRITE32(dev_priv->mmio, COMMAND_2D, COMMAND_2D_FILLRECT | (tdfx_rop << 24)); + DRM_WRITE32(dev_priv->mmio, DSTBASE, dstbase); + DRM_WRITE32(dev_priv->mmio, DSTSIZE, width | (height << 16)); + DRM_WRITE32(dev_priv->mmio, LAUNCH_2D, dx | (dy << 16)); + DRM_WRITE32(dev_priv->mmio, DSTBASE, 0); +} + +/* + * Screen-to-Screen BitBlt 2D command (for the bmove fb op.) + */ +void tdfx_copyarea(struct drm_fb_helper *fb_helper, + const struct fb_copyarea *area) +{ + u32 sx = area->sx, sy = area->sy, dx = area->dx, dy = area->dy; + struct drm_framebuffer *fb = fb_helper->fb; + u32 bpp = fb->bits_per_pixel, stride = fb->pitch; + u32 blitcmd = COMMAND_2D_S2S_BITBLT | (TDFX_ROP_COPY << 24); + u32 fmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13); + drm_tdfx_private_t *dev_priv; + u32 dstbase = 0, srcbase = 0; + + dev_priv = fb->dev->dev_private; + + /* asume always area->height < 4096 */ + if (sy + area->height > 4095) { + srcbase = stride * sy; + sy = 0; + } + /* asume always area->width < 4096 */ + if (sx + area->width > 4095) { + srcbase += sx * bpp >> 3; + sx = 0; + } + /* asume always area->height < 4096 */ + if (dy + area->height > 4095) { + dstbase = stride * dy; + dy = 0; + } + /* asume always area->width < 4096 */ + if (dx + area->width > 4095) { + dstbase += dx * bpp >> 3; + dx = 0; + } + + if (area->sx <= area->dx) { + /* -X */ + blitcmd |= BIT(14); + sx += area->width - 1; + dx += area->width - 1; + } + if (area->sy <= area->dy) { + /* -Y */ + blitcmd |= BIT(15); + sy += area->height - 1; + dy += area->height - 1; + } + + banshee_make_room(dev_priv, 8); + + DRM_WRITE32(dev_priv->mmio, SRCFORMAT, fmt); + DRM_WRITE32(dev_priv->mmio, DSTFORMAT, fmt); + DRM_WRITE32(dev_priv->mmio, COMMAND_2D, blitcmd); + DRM_WRITE32(dev_priv->mmio, DSTSIZE, area->width | (area->height << 16)); + DRM_WRITE32(dev_priv->mmio, DSTXY, dx | (dy << 16)); + DRM_WRITE32(dev_priv->mmio, SRCBASE, srcbase); + DRM_WRITE32(dev_priv->mmio, DSTBASE, dstbase); + DRM_WRITE32(dev_priv->mmio, LAUNCH_2D, sx | (sy << 16)); + + banshee_make_room(dev_priv, 2); + DRM_WRITE32(dev_priv->mmio, DSTBASE, 0); + DRM_WRITE32(dev_priv->mmio, SRCBASE, 0); +} + +int tdfx_imageblit(struct drm_fb_helper *fb_helper, const struct fb_image *image) +{ + struct drm_framebuffer *fb = fb_helper->fb; + u32 bpp = fb->bits_per_pixel, stride = fb->pitch; + drm_tdfx_private_t *dev_priv = fb->dev->dev_private; + int size = image->height * ((image->width * image->depth + 7) >> 3); + u32 dstfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13); + u32 dx = image->dx, dy = image->dy, srcfmt, dstbase = 0; + u8 *chardata = (u8 *) image->data; + u32 fg_color, bg_color; + int fifo_free, i; + + if (image->depth != 1) { +#ifdef BROKEN_CODE + banshee_make_room(dev_priv, 6 + ((size + 3) >> 2)); + srcfmt = stride | ((bpp + ((bpp == 8) ? 0 : 8)) << 13) | + 0x400000; +#else + return 1; +#endif + } + banshee_make_room(dev_priv, 9); + + if (bpp > 8) { + bg_color = ((u32 *)(fb_helper->pseudo_palette))[image->bg_color]; + fg_color = ((u32 *)(fb_helper->pseudo_palette))[image->fg_color]; + } else { + fg_color = image->fg_color; + bg_color = image->bg_color; + } + + DRM_WRITE32(dev_priv->mmio, COLORFORE, fg_color); + DRM_WRITE32(dev_priv->mmio, COLORBACK, bg_color); + +#ifdef __BIG_ENDIAN + srcfmt = SRCFORMAT_BYTEPACKED | SRCFORMAT_BYTESWIZZLE; +#else + srcfmt = SRCFORMAT_BYTEPACKED; +#endif + /* asume always image->height < 4096 */ + if (dy + image->height > 4095) { + dstbase = stride * dy; + dy = 0; + } + /* asume always image->width < 4096 */ + if (dx + image->width > 4095) { + dstbase += dx * bpp >> 3; + dx = 0; + } + + DRM_WRITE32(dev_priv->mmio, DSTBASE, dstbase); + DRM_WRITE32(dev_priv->mmio, SRCXY, 0); + DRM_WRITE32(dev_priv->mmio, DSTXY, dx | (dy << 16)); + DRM_WRITE32(dev_priv->mmio, COMMAND_2D, + COMMAND_2D_H2S_BITBLT | (TDFX_ROP_COPY << 24)); + DRM_WRITE32(dev_priv->mmio, SRCFORMAT, srcfmt); + DRM_WRITE32(dev_priv->mmio, DSTFORMAT, dstfmt); + DRM_WRITE32(dev_priv->mmio, DSTSIZE, image->width | (image->height << 16)); + + /* A count of how many free FIFO entries we've requested. + * When this goes negative, we need to request more. */ + fifo_free = 0; + + /* Send four bytes at a time of data */ + for (i = (size >> 2); i > 0; i--) { + if (--fifo_free < 0) { + fifo_free = 31; + banshee_make_room(dev_priv, fifo_free); + } + DRM_WRITE32(dev_priv->mmio, LAUNCH_2D, *(u32 *)chardata); + chardata += 4; + } + + /* Send the leftovers now */ + banshee_make_room(dev_priv, 3); + switch (size % 4) { + case 0: + break; + case 1: + DRM_WRITE32(dev_priv->mmio, LAUNCH_2D, *chardata); + break; + case 2: + DRM_WRITE32(dev_priv->mmio, LAUNCH_2D, *(u16 *)chardata); + break; + case 3: + DRM_WRITE32(dev_priv->mmio, LAUNCH_2D, + *(u16 *)chardata | (chardata[3] << 24)); + break; + } + + banshee_make_room(dev_priv, 1); + DRM_WRITE32(dev_priv->mmio, DSTBASE, 0); + return 0; +} diff --git a/drivers/gpu/drm/tdfx/tdfx_ttm.c b/drivers/gpu/drm/tdfx/tdfx_ttm.c new file mode 100644 index 0000000..d48b20b --- /dev/null +++ b/drivers/gpu/drm/tdfx/tdfx_ttm.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2010 James Simmons + * All Rights Reserved. + * + * 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 above copyright notice and this permission notice (including the + * next paragraph) 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 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. + */ + +#include "drmP.h" + +#include "tdfx_drv.h" + +static struct ttm_backend * +tdfx_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev) +{ + return NULL; +} + +static int +tdfx_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags) +{ + return 0; +} + +static int +tdfx_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type, + struct ttm_mem_type_manager *man) +{ + switch (type) { + case TTM_PL_VRAM: + man->flags = TTM_MEMTYPE_FLAG_FIXED | + TTM_MEMTYPE_FLAG_MAPPABLE; + man->available_caching = TTM_PL_FLAG_UNCACHED | + TTM_PL_FLAG_WC; + man->default_caching = TTM_PL_FLAG_WC; + //man->gpu_offset = dev_priv->vm_vram_base; + break; + default: + DRM_ERROR("Unsupported memory type %u\n", (unsigned)type); + return -EINVAL; + } + return 0; +} + +static void +tdfx_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl) +{ +} + +static int +tdfx_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr, + bool no_wait_reserve, bool no_wait_gpu, + struct ttm_mem_reg *new_mem) +{ + return 0; +} + +static int +tdfx_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp) +{ + return 0; +} + +static int +tdfx_ttm_io_mem_reserve(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ + return 0; +} + +static void +tdfx_ttm_io_mem_free(struct ttm_bo_device *bdev, struct ttm_mem_reg *mem) +{ +} + +static int +tdfx_ttm_fault_reserve_notify(struct ttm_buffer_object *bo) +{ + return 0; +} + +struct ttm_bo_driver tdfx_bo_driver = { + .create_ttm_backend_entry = tdfx_bo_create_ttm_backend_entry, + .invalidate_caches = tdfx_bo_invalidate_caches, + .init_mem_type = tdfx_bo_init_mem_type, + .evict_flags = tdfx_bo_evict_flags, + .move = tdfx_bo_move, + .verify_access = tdfx_bo_verify_access, + .fault_reserve_notify = &tdfx_ttm_fault_reserve_notify, + .io_mem_reserve = &tdfx_ttm_io_mem_reserve, + .io_mem_free = &tdfx_ttm_io_mem_free, +};