/*
* linux/drivers/video/nvidia/nvidia.c - nVidia fb driver
*
* Copyright 2004 Antonino Daplas <adaplas@pol.net>
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/console.h>
#include <linux/backlight.h>
#ifdef CONFIG_BOOTX_TEXT
#include <asm/btext.h>
#endif
#include "nv_local.h"
#include "nv_type.h"
#include "nv_proto.h"
#include "nv_dma.h"
#ifdef CONFIG_FB_NVIDIA_DEBUG
#define NVTRACE printk
#else
#define NVTRACE if (0) printk
#endif
#define NVTRACE_ENTER(...) NVTRACE("%s START\n", __func__)
#define NVTRACE_LEAVE(...) NVTRACE("%s END\n", __func__)
#ifdef CONFIG_FB_NVIDIA_DEBUG
#define assert(expr) \
if (!(expr)) { \
printk( "Assertion failed! %s,%s,%s,line=%d\n",\
#expr,__FILE__,__func__,__LINE__); \
BUG(); \
}
#else
#define assert(expr)
#endif
#define PFX "nvidiafb: "
/* HW cursor parameters */
#define MAX_CURS 32
static const struct pci_device_id nvidiafb_pci_tbl[] = {
{PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
/* command line data, set in nvidiafb_setup() */
static int flatpanel = -1; /* Autodetect later */
static int fpdither = -1;
static int forceCRTC = -1;
static int hwcur = 0;
static int noaccel = 0;
static int noscale = 0;
static int paneltweak = 0;
static int vram = 0;
static int bpp = 8;
static int reverse_i2c;
static bool nomtrr = false;
#ifdef CONFIG_PMAC_BACKLIGHT
static int backlight = 1;
#else
static int backlight = 0;
#endif
static char *mode_option = NULL;
static struct fb_fix_screeninfo nvidiafb_fix = {
.type = FB_TYPE_PACKED_PIXELS,
.xpanstep = 8,
.ypanstep = 1,
};
static struct fb_var_screeninfo nvidiafb_default_var = {
.xres = 640,
.yres = 480,
.xres_virtual = 640,
.yres_virtual = 480,
.bits_per_pixel = 8,
.red = {0, 8, 0},
.green = {0, 8, 0},
.blue = {0, 8, 0},
.transp = {0, 0, 0},
.activate = FB_ACTIVATE_NOW,
.height = -1,
.width = -1,
.pixclock = 39721,
.left_margin = 40,
.right_margin = 24,
.upper_margin = 32,
.lower_margin = 11,
.hsync_len = 96,
.vsync_len = 2,
.vmode = FB_VMODE_NONINTERLACED
};
static void nvidiafb_load_cursor_image(struct nvidia_par *par, u8 * data8,
u16 bg, u16 fg, u32 w, u32 h)
{
u32 *data = (u32 *) data8;
int i, j, k = 0;
u32 b, tmp;
w = (w + 1) & ~1;
for (i = 0; i < h; i++) {
b = *data++;
reverse_order(&b);
for (j = 0; j < w / 2; j++) {
tmp = 0;
#if defined (__BIG_ENDIAN)
tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
b <<= 1;
tmp |= (b & (1 << 31)) ? fg : bg;
b <<= 1;
#else
tmp = (b & 1) ? fg : bg;
b >>= 1;
tmp |= (b & 1) ? fg << 16 : bg << 16;
b >>= 1;
#endif
NV_WR32(&par->CURSOR[k++], 0, tmp);
}
k += (MAX_CURS - w) / 2;
}
}
static void nvidia_write_clut(struct nvidia_par *par,
u8 regnum, u8 red, u8 green, u8 blue)
{
NVWriteDacMask(par, 0xff);
NVWriteDacWriteAddr(par, regnum);
NVWriteDacData(par, red);
NVWriteDacData(par, green);
NVWriteDacData(par, blue);
}
static void nvidia_read_clut(struct nvidia_par *par,
u8 regnum, u8 * red, u8 * green, u8 * blue)
{
NVWriteDacMask(par, 0xff);
NVWriteDacReadAddr(par, regnum);
*red = NVReadDacData(par);
*green = NVReadDacData(par);
*blue = NVReadDacData(par);
}
static int nvidia_panel_tweak(struct nvidia_par *par,
struct _riva_hw_state *state)
{
int tweak = 0;
if (par->paneltweak) {
tweak = par->paneltweak;
} else {
/* begin flat panel hacks */
/* This is unfortunate, but some chips need this register
tweaked or else you get artifacts where adjacent pixels are
swapped. There are no hard rules for what to set here so all
we can do is experiment and apply hacks. */
if(((par->Chipset & 0xffff) == 0x0328) && (state->bpp == 32)) {
/* At least one NV34 laptop needs this workaround. */
tweak = -1;
}
if((par->Chipset & 0xfff0) == 0x0310) {
tweak = 1;
}
/* end flat panel hacks */
}
return tweak;
}
static void nvidia_screen_off(struct nvidia_par *par, int on)
{
unsigned char tmp;
if (on) {
/*
* Turn off screen and disable sequencer.
*/
tmp = NVReadSeq(par, 0x01);
NVWriteSeq(par, 0x00, 0x01); /* Synchronous Reset */
NVWriteSeq(par, 0x01, tmp | 0x20); /* disable the display */
} else {
/*
* Reenable sequencer, then turn on screen.
*/
tmp = NVReadSeq(par, 0x01);
NVWriteSeq(par, 0x01, tmp & ~0x20); /* reenable display */
NVWriteSeq(par, 0x00, 0x03); /* End Reset */
}
}
static void nvidia_save_vga(struct nvidia_par *par,
struct _riva_hw_state *state)
{
int i;
NVTRACE_ENTER();
NVLockUnlock(par, 0);
NVUnloadStateExt(par, state);
state->misc_output = NVReadMiscOut(par);
for (i = 0; i < NUM_CRT_REGS; i++)
state->crtc[i] = NVReadCrtc(par, i);
for (i = 0; i < NUM_ATC_REGS; i++)
state->attr[i] = NVReadAttr(par,
|