Commit 5a32c854 authored by Antonino Daplas's avatar Antonino Daplas Committed by Linus Torvalds

[PATCH] Updates to rivafb driver

The patch updates rivafb to the following:

1.  Fixed cursor corruption and simplified cursor code.

2.  Maximized var->yres_virtual on initial mode setting.  Scrolling,
   therefore, defaults to y-panning which is significantly faster.

3.  Restricted var->xres_virtual and var->yres_virtual to 0x7fff
   (hardware limitation?).  Otherwise, var->yres_virtual > 0x7fff + panning
   will hang the GPU.

4.  Added I2C/DDC support.  This feature enables independent mode setup
   to rivafb.  'stty rows n cols n' should now work correctly.  This is a
   configurable option.

5. Various/minor fixes to drawing code.
Signed-off-by: default avatarAntonino Daplas <adaplas@pol.net>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0b0fe5ae
......@@ -432,6 +432,11 @@ config FB_RIVA
To compile this driver as a module, choose M here: the
module will be called rivafb.
config FB_RIVA_I2C
bool "Enable DDC Support"
depends on FB_RIVA && I2C
help
config FB_I810
tristate "Intel 810/815 support (EXPERIMENTAL)"
depends on FB && AGP && AGP_INTEL && EXPERIMENTAL && PCI
......
......@@ -2,6 +2,10 @@
# Makefile for the Riva framebuffer driver
#
obj-$(CONFIG_FB_RIVA) += rivafb.o
obj-$(CONFIG_FB_RIVA) += rivafb.o
rivafb-objs := fbdev.o riva_hw.o nv_driver.o
rivafb-objs := fbdev.o riva_hw.o nv_driver.o
ifdef CONFIG_FB_RIVA_I2C
rivafb-objs += rivafb-i2c.o
endif
......@@ -296,9 +296,8 @@ static int forceCRTC __initdata = -1;
static int nomtrr __initdata = 0;
#endif
#ifndef MODULE
static char *mode_option __initdata = NULL;
#endif
static int strictmode = 0;
static struct fb_fix_screeninfo rivafb_fix = {
.id = "nVidia",
......@@ -493,54 +492,30 @@ static inline void reverse_order(u32 *l)
* rivafb_cursor()
*/
static void rivafb_load_cursor_image(struct riva_par *par, u8 *data8,
u8 *mask8, u16 bg, u16 fg, u32 w, u32 h)
u16 bg, u16 fg, u32 w, u32 h)
{
int i, j, k = 0;
u32 b, m, tmp;
u32 b, tmp;
u32 *data = (u32 *)data8;
u32 *mask = (u32 *)mask8;
for (i = 0; i < h; i++) {
b = *data++;
m = *mask++;
reverse_order(&b);
for (j = 0; j < w/2; j++) {
tmp = 0;
#if defined (__BIG_ENDIAN)
if (m & (1 << 31)) {
fg |= 1 << 15;
bg |= 1 << 15;
}
tmp = (b & (1 << 31)) ? fg << 16 : bg << 16;
b <<= 1;
m <<= 1;
if (m & (1 << 31)) {
fg |= 1 << 15;
bg |= 1 << 15;
}
tmp |= (b & (1 << 31)) ? fg : bg;
b <<= 1;
m <<= 1;
#else
if (m & 1) {
fg |= 1 << 15;
bg |= 1 << 15;
}
tmp = (b & 1) ? fg : bg;
b >>= 1;
m >>= 1;
if (m & 1) {
fg |= 1 << 15;
bg |= 1 << 15;
}
tmp |= (b & 1) ? fg << 16 : bg << 16;
b >>= 1;
m >>= 1;
#endif
writel(tmp, par->riva.CURSOR + k++);
writel(tmp, &par->riva.CURSOR[k++]);
}
k += (MAX_CURS - w)/2;
}
......@@ -833,6 +808,24 @@ static void riva_load_video_mode(struct fb_info *info)
rivafb_blank(0, info);
}
static void riva_update_var(struct fb_var_screeninfo *var, struct fb_videomode *modedb)
{
var->xres = var->xres_virtual = modedb->xres;
var->yres = modedb->yres;
if (var->yres_virtual < var->yres)
var->yres_virtual = var->yres;
var->xoffset = var->yoffset = 0;
var->pixclock = modedb->pixclock;
var->left_margin = modedb->left_margin;
var->right_margin = modedb->right_margin;
var->upper_margin = modedb->upper_margin;
var->lower_margin = modedb->lower_margin;
var->hsync_len = modedb->hsync_len;
var->vsync_len = modedb->vsync_len;
var->sync = modedb->sync;
var->vmode = modedb->vmode;
}
/**
* rivafb_do_maximize -
* @info: pointer to fb_info object containing info for current riva board
......@@ -872,7 +865,7 @@ static int rivafb_do_maximize(struct fb_info *info,
"using maximum available virtual resolution\n");
for (i = 0; modes[i].xres != -1; i++) {
if (modes[i].xres * nom / den * modes[i].yres <
info->fix.smem_len / 2)
info->fix.smem_len)
break;
}
if (modes[i].xres == -1) {
......@@ -927,35 +920,47 @@ static int rivafb_do_maximize(struct fb_info *info,
"virtual Y resolution (%d) is smaller than real\n", var->yres_virtual);
return -EINVAL;
}
if (var->xres_virtual > 0x7fff)
var->xres_virtual = 0x7fff;
if (var->yres_virtual > 0x7fff)
var->yres_virtual = 0x7fff;
return 0;
}
static void
riva_set_pattern(struct riva_par *par, int clr0, int clr1, int pat0, int pat1)
{
RIVA_FIFO_FREE(par->riva, Patt, 4);
par->riva.Patt->Color0 = clr0;
par->riva.Patt->Color1 = clr1;
par->riva.Patt->Monochrome[0] = pat0;
par->riva.Patt->Monochrome[1] = pat1;
}
/* acceleration routines */
inline void wait_for_idle(struct riva_par *par)
{
while (par->riva.Busy(&par->riva));
}
/* set copy ROP, no mask */
static void riva_setup_ROP(struct riva_par *par)
/*
* Set ROP. Translate X rop into ROP3. Internal routine.
*/
static void
riva_set_rop_solid(struct riva_par *par, int rop)
{
RIVA_FIFO_FREE(par->riva, Patt, 5);
par->riva.Patt->Shape = 0;
par->riva.Patt->Color0 = 0xffffffff;
par->riva.Patt->Color1 = 0xffffffff;
par->riva.Patt->Monochrome[0] = 0xffffffff;
par->riva.Patt->Monochrome[1] = 0xffffffff;
RIVA_FIFO_FREE(par->riva, Rop, 1);
par->riva.Rop->Rop3 = 0xCC;
riva_set_pattern(par, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF);
RIVA_FIFO_FREE(par->riva, Rop, 1);
par->riva.Rop->Rop3 = rop;
}
void riva_setup_accel(struct riva_par *par)
{
RIVA_FIFO_FREE(par->riva, Clip, 2);
par->riva.Clip->TopLeft = 0x0;
par->riva.Clip->WidthHeight = 0x80008000;
riva_setup_ROP(par);
par->riva.Clip->WidthHeight = 0x7fff7fff;
riva_set_rop_solid(par, 0xcc);
wait_for_idle(par);
}
......@@ -1043,7 +1048,9 @@ static int rivafb_release(struct fb_info *info, int user)
static int rivafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
struct fb_monspecs *specs = &info->monspecs;
int nom, den; /* translating from pixels->bytes */
int mode_valid = 0;
switch (var->bits_per_pixel) {
case 1 ... 8:
......@@ -1094,6 +1101,73 @@ static int rivafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
return -EINVAL;
}
if (!strictmode) {
if (!fb_validate_mode(var, info))
mode_valid = 1;
}
/* find best mode from modedb */
if (!mode_valid && specs->modedb_len) {
int i, best, best_refresh, best_x, best_y, diff_x, diff_y;
best_refresh = best = best_x = best_y = 0;
diff_x = diff_y = -1;
for (i = 0; i < specs->modedb_len; i++) {
if (var->xres <= specs->modedb[i].xres &&
!(specs->modedb[i].flag & FB_MODE_IS_CALCULATED) &&
specs->modedb[i].xres - var->xres < diff_x) {
best_x = specs->modedb[i].xres;
diff_x = best_x - var->xres;
}
if (!diff_x) break;
}
if (diff_x != -1) {
for (i = 0; i < specs->modedb_len; i++) {
if (best_x == specs->modedb[i].xres &&
var->yres <= specs->modedb[i].yres &&
!(specs->modedb[i].flag &
FB_MODE_IS_CALCULATED) &&
specs->modedb[i].yres-var->yres < diff_y) {
best_y = specs->modedb[i].yres;
diff_y = best_y - var->yres;
}
if (!diff_y) break;
}
}
if (diff_y != -1) {
for (i = 0; i < specs->modedb_len; i++) {
if (best_x == specs->modedb[i].xres &&
best_y == specs->modedb[i].yres &&
!(specs->modedb[i].flag &
FB_MODE_IS_CALCULATED) &&
specs->modedb[i].refresh > best_refresh) {
best_refresh=specs->modedb[i].refresh;
best = i;
}
}
}
if (best_refresh) {
riva_update_var(var, &specs->modedb[best]);
mode_valid = 1;
}
}
/* calculate modeline if supported by monitor */
if (!mode_valid && info->monspecs.gtf) {
if (!fb_get_mode(FB_MAXTIMINGS, 0, var, info))
mode_valid = 1;
}
if (!mode_valid && info->monspecs.modedb_len)
return -EINVAL;
if (var->xres_virtual < var->xres)
var->xres_virtual = var->xres;
if (var->yres_virtual <= var->yres)
var->yres_virtual = -1;
if (rivafb_do_maximize(info, var, nom, den) < 0)
return -EINVAL;
......@@ -1329,8 +1403,7 @@ static void rivafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect
break;
}
RIVA_FIFO_FREE(par->riva, Rop, 1);
par->riva.Rop->Rop3 = rop;
riva_set_rop_solid(par, rop);
RIVA_FIFO_FREE(par->riva, Bitmap, 1);
par->riva.Bitmap->Color1A = color;
......@@ -1338,10 +1411,12 @@ static void rivafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect
RIVA_FIFO_FREE(par->riva, Bitmap, 2);
par->riva.Bitmap->UnclippedRectangle[0].TopLeft =
(rect->dx << 16) | rect->dy;
mb();
par->riva.Bitmap->UnclippedRectangle[0].WidthHeight =
(rect->width << 16) | rect->height;
RIVA_FIFO_FREE(par->riva, Rop, 1);
par->riva.Rop->Rop3 = 0xCC; // back to COPY
mb();
riva_set_rop_solid(par, 0xcc);
}
/**
......@@ -1362,8 +1437,9 @@ static void rivafb_copyarea(struct fb_info *info, const struct fb_copyarea *regi
RIVA_FIFO_FREE(par->riva, Blt, 3);
par->riva.Blt->TopLeftSrc = (region->sy << 16) | region->sx;
par->riva.Blt->TopLeftDst = (region->dy << 16) | region->dx;
mb();
par->riva.Blt->WidthHeight = (region->height << 16) | region->width;
wait_for_idle(par);
mb();
}
static inline void convert_bgcolor_16(u32 *col)
......@@ -1372,6 +1448,7 @@ static inline void convert_bgcolor_16(u32 *col)
| ((*col & 0x000003E0) << 6)
| ((*col & 0x0000001F) << 3)
| 0xFF000000;
mb();
}
/**
......@@ -1478,7 +1555,6 @@ static int rivafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
struct riva_par *par = (struct riva_par *) info->par;
u8 data[MAX_CURS * MAX_CURS/8];
u8 mask[MAX_CURS * MAX_CURS/8];
u16 fg, bg;
int i;
......@@ -1508,7 +1584,7 @@ static int rivafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
info->cursor.image.fg_color = cursor->image.fg_color;
}
if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP)) {
if (cursor->set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETCUR)) {
u32 bg_idx = info->cursor.image.bg_color;
u32 fg_idx = info->cursor.image.fg_color;
u32 s_pitch = (info->cursor.image.width+7) >> 3;
......@@ -1517,34 +1593,35 @@ static int rivafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
u8 *msk = (u8 *) info->cursor.mask;
u8 src[64];
info->cursor.image.data = cursor->image.data;
switch (info->cursor.rop) {
case ROP_XOR:
for (i = 0; i < s_pitch * info->cursor.image.height; i++)
src[i] = dat[i] ^ msk[i];
for (i = 0; i < s_pitch * info->cursor.image.height;
i++)
src[i] = dat[i] ^ msk[i];
break;
case ROP_COPY:
default:
for (i = 0; i < s_pitch * info->cursor.image.height; i++)
src[i] = dat[i] & msk[i];
for (i = 0; i < s_pitch * info->cursor.image.height;
i++)
src[i] = dat[i] & msk[i];
break;
}
fb_move_buf_aligned(info, &info->sprite, data, d_pitch, src, s_pitch, info->cursor.image.height);
fb_move_buf_aligned(info, &info->sprite, mask, d_pitch, msk, s_pitch, info->cursor.image.height);
fb_move_buf_aligned(info, &info->sprite, data, d_pitch, src,
s_pitch, info->cursor.image.height);
bg = ((info->cmap.red[bg_idx] & 0xf8) << 7) |
((info->cmap.green[bg_idx] & 0xf8) << 2) |
((info->cmap.blue[bg_idx] & 0xf8) >> 3);
((info->cmap.blue[bg_idx] & 0xf8) >> 3) | 1 << 15;
fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
((info->cmap.green[fg_idx] & 0xf8) << 2) |
((info->cmap.blue[fg_idx] & 0xf8) >> 3);
((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
par->riva.LockUnlock(&par->riva, 0);
rivafb_load_cursor_image(par, data, mask, bg, fg,
rivafb_load_cursor_image(par, data, bg, fg,
info->cursor.image.width,
info->cursor.image.height);
}
......@@ -1586,24 +1663,14 @@ static struct fb_ops riva_fb_ops = {
static int __devinit riva_set_fbinfo(struct fb_info *info)
{
struct riva_par *par = (struct riva_par *) info->par;
unsigned int cmap_len;
info->flags = FBINFO_FLAG_DEFAULT;
info->var = rivafb_default_var;
info->fix = rivafb_fix;
info->fbops = &riva_fb_ops;
info->fix.visual = (info->var.bits_per_pixel == 8) ?
FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
info->pseudo_palette = pseudo_palette;
#ifndef MODULE
if (mode_option)
fb_find_mode(&info->var, info, mode_option,
NULL, 0, NULL, 8);
#endif
if (par->use_default_var)
/* We will use the modified default var */
info->var = rivafb_default_var;
cmap_len = riva_get_cmap_len(&info->var);
fb_alloc_cmap(&info->cmap, cmap_len, 0);
......@@ -1611,7 +1678,8 @@ static int __devinit riva_set_fbinfo(struct fb_info *info)
info->pixmap.buf_align = 4;
info->pixmap.scan_align = 4;
info->pixmap.flags = FB_PIXMAP_SYSTEM;
return 0;
info->var.yres_virtual = -1;
return (rivafb_check_var(&info->var, info));
}
#ifdef CONFIG_PPC_OF
......@@ -1645,77 +1713,35 @@ static int riva_get_EDID_OF(struct fb_info *info, struct pci_dev *pd)
}
#endif /* CONFIG_PPC_OF */
static int riva_dfp_parse_EDID(struct riva_par *par)
static void riva_update_default_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
unsigned char *block = par->EDID;
if (!block)
return 0;
/* jump to detailed timing block section */
block += 54;
par->clock = (block[0] + (block[1] << 8));
par->panel_xres = (block[2] + ((block[4] & 0xf0) << 4));
par->hblank = (block[3] + ((block[4] & 0x0f) << 8));
par->panel_yres = (block[5] + ((block[7] & 0xf0) << 4));
par->vblank = (block[6] + ((block[7] & 0x0f) << 8));
par->hOver_plus = (block[8] + ((block[11] & 0xc0) << 2));
par->hSync_width = (block[9] + ((block[11] & 0x30) << 4));
par->vOver_plus = ((block[10] >> 4) + ((block[11] & 0x0c) << 2));
par->vSync_width = ((block[10] & 0x0f) + ((block[11] & 0x03) << 4));
par->interlaced = ((block[17] & 0x80) >> 7);
par->synct = ((block[17] & 0x18) >> 3);
par->misc = ((block[17] & 0x06) >> 1);
par->hAct_high = par->vAct_high = 0;
if (par->synct == 3) {
if (par->misc & 2)
par->hAct_high = 1;
if (par->misc & 1)
par->vAct_high = 1;
struct fb_monspecs *specs = &info->monspecs;
struct fb_videomode modedb;
/* respect mode options */
if (mode_option) {
fb_find_mode(var, info, mode_option,
specs->modedb, specs->modedb_len,
NULL, 8);
} else if (specs->modedb != NULL) {
/* get preferred timing */
if (info->monspecs.misc & FB_MISC_1ST_DETAIL) {
int i;
for (i = 0; i < specs->modedb_len; i++) {
if (specs->modedb[i].flag & FB_MODE_IS_FIRST) {
modedb = specs->modedb[i];
break;
}
}
} else {
/* otherwise, get first mode in database */
modedb = specs->modedb[0];
}
var->bits_per_pixel = 8;
riva_update_var(var, &modedb);
}
printk(KERN_INFO PFX
"detected DFP panel size from EDID: %dx%d\n",
par->panel_xres, par->panel_yres);
par->got_dfpinfo = 1;
return 1;
}
static void riva_update_default_var(struct fb_info *info)
{
struct fb_var_screeninfo *var = &rivafb_default_var;
struct riva_par *par = (struct riva_par *) info->par;
var->xres = par->panel_xres;
var->yres = par->panel_yres;
var->xres_virtual = par->panel_xres;
var->yres_virtual = par->panel_yres;
var->xoffset = var->yoffset = 0;
var->bits_per_pixel = 8;
var->pixclock = 100000000 / par->clock;
var->left_margin = (par->hblank - par->hOver_plus - par->hSync_width);
var->right_margin = par->hOver_plus;
var->upper_margin = (par->vblank - par->vOver_plus - par->vSync_width);
var->lower_margin = par->vOver_plus;
var->hsync_len = par->hSync_width;
var->vsync_len = par->vSync_width;
var->sync = 0;
if (par->synct == 3) {
if (par->hAct_high)
var->sync |= FB_SYNC_HOR_HIGH_ACT;
if (par->vAct_high)
var->sync |= FB_SYNC_VERT_HIGH_ACT;
}
var->vmode = 0;
if (par->interlaced)
var->vmode |= FB_VMODE_INTERLACED;
var->accel_flags |= FB_ACCELF_TEXT;
par->use_default_var = 1;
}
......@@ -1726,19 +1752,27 @@ static void riva_get_EDID(struct fb_info *info, struct pci_dev *pdev)
printk("rivafb: could not retrieve EDID from OF\n");
#else
/* XXX use other methods later */
#ifdef CONFIG_FB_RIVA_I2C
struct riva_par *par = (struct riva_par *) info->par;
riva_create_i2c_busses(par);
riva_probe_i2c_connector(par, 1, &par->EDID);
riva_delete_i2c_busses(par);
#endif
#endif
}
static void riva_get_dfpinfo(struct fb_info *info)
static void riva_get_edidinfo(struct fb_info *info)
{
struct fb_var_screeninfo *var = &rivafb_default_var;
struct riva_par *par = (struct riva_par *) info->par;
if (riva_dfp_parse_EDID(par))
riva_update_default_var(info);
fb_edid_to_monspecs(par->EDID, &info->monspecs);
riva_update_default_var(var, info);
/* if user specified flatpanel, we respect that */
if (par->got_dfpinfo == 1)
if (info->monspecs.input & FB_DISP_DDI)
par->FlatPanel = 1;
}
......@@ -1768,6 +1802,7 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
memset(info, 0, sizeof(struct fb_info));
memset(default_par, 0, sizeof(struct riva_par));
default_par->pdev = pd;
info->pixmap.addr = kmalloc(64 * 1024, GFP_KERNEL);
if (info->pixmap.addr == NULL)
......@@ -1815,10 +1850,6 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
info->par = default_par;
riva_get_EDID(info, pd);
riva_get_dfpinfo(info);
switch (default_par->riva.Architecture) {
case NV_ARCH_03:
/* Riva128's PRAMIN is in the "framebuffer" space
......@@ -1883,6 +1914,11 @@ static int __devinit rivafb_probe(struct pci_dev *pd,
}
#endif /* CONFIG_MTRR */
info->fbops = &riva_fb_ops;
info->fix = rivafb_fix;
riva_get_EDID(info, pd);
riva_get_edidinfo(info);
if (riva_set_fbinfo(info) < 0) {
printk(KERN_ERR PFX "error setting initial video mode\n");
goto err_out_iounmap_fb;
......@@ -1991,6 +2027,8 @@ int __init rivafb_setup(char *options)
} else if (!strncmp(this_opt, "nomtrr", 6)) {
nomtrr = 1;
#endif
} else if (!strncmp(this_opt, "strictmode", 10)) {
strictmode = 1;
} else
mode_option = this_opt;
}
......@@ -2039,6 +2077,8 @@ MODULE_PARM_DESC(forceCRTC, "Forces usage of a particular CRTC in case autodetec
#ifdef CONFIG_MTRR
MODULE_PARM(nomtrr, "i");
MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) (default=0)");
MODULE_PARM(strictmode, "i");
MODULE_PARM_DESC(strictmode, "Only use video modes from EDID");
#endif
#endif /* MODULE */
......
/*
* linux/drivers/video/riva/fbdev-i2c.c - nVidia i2c
*
* Maintained by Ani Joshi <ajoshi@shell.unixbox.com>
*
* Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
*
* Based on radeonfb-i2c.c
*
* 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/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/fb.h>
#include <asm/io.h>
#include "rivafb.h"
#include "../edid.h"
#define RIVA_DDC 0x50
static void riva_gpio_setscl(void* data, int state)
{
struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
struct riva_par *par = chan->par;
u32 val;
VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
val = VGA_RD08(par->riva.PCIO, 0x3d5) & 0xf0;
if (state)
val |= 0x20;
else
val &= ~0x20;
VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
VGA_WR08(par->riva.PCIO, 0x3d5, val | 0x1);
}
static void riva_gpio_setsda(void* data, int state)
{
struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
struct riva_par *par = chan->par;
u32 val;
VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
val = VGA_RD08(par->riva.PCIO, 0x3d5) & 0xf0;
if (state)
val |= 0x10;
else
val &= ~0x10;
VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base + 1);
VGA_WR08(par->riva.PCIO, 0x3d5, val | 0x1);
}
static int riva_gpio_getscl(void* data)
{
struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
struct riva_par *par = chan->par;
u32 val = 0;
VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base);
if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x04)
val = 1;
val = VGA_RD08(par->riva.PCIO, 0x3d5);
return val;
}
static int riva_gpio_getsda(void* data)
{
struct riva_i2c_chan *chan = (struct riva_i2c_chan *)data;
struct riva_par *par = chan->par;
u32 val = 0;
VGA_WR08(par->riva.PCIO, 0x3d4, chan->ddc_base);
if (VGA_RD08(par->riva.PCIO, 0x3d5) & 0x08)
val = 1;
return val;
}
#define I2C_ALGO_RIVA 0x0e0000
static int riva_setup_i2c_bus(struct riva_i2c_chan *chan, const char *name)
{
int rc;
strcpy(chan->adapter.name, name);
chan->adapter.owner = THIS_MODULE;
chan->adapter.id = I2C_ALGO_RIVA;
chan->adapter.algo_data = &chan->algo;
chan->adapter.dev.parent = &chan->par->pdev->dev;
chan->algo.setsda = riva_gpio_setsda;
chan->algo.setscl = riva_gpio_setscl;
chan->algo.getsda = riva_gpio_getsda;
chan->algo.getscl = riva_gpio_getscl;
chan->algo.udelay = 40;
chan->algo.timeout = 20;
chan->algo.data = chan;
i2c_set_adapdata(&chan->adapter, chan);
/* Raise SCL and SDA */
riva_gpio_setsda(chan, 1);
riva_gpio_setscl(chan, 1);
udelay(20);
rc = i2c_bit_add_bus(&chan->adapter);
if (rc == 0)
dev_dbg(&chan->par->pdev->dev, "I2C bus %s registered.\n", name);
else
dev_warn(&chan->par->pdev->dev, "Failed to register I2C bus %s.\n", name);
return rc;
}
void riva_create_i2c_busses(struct riva_par *par)
{
par->chan[0].par = par;
par->chan[1].par = par;
par->chan[2].par = par;
switch (par->riva.Architecture) {
#if 0 /* no support yet for other nVidia chipsets */
par->chan[2].ddc_base = 0x50;
riva_setup_i2c_bus(&par->chan[2], "BUS2");
#endif
case NV_ARCH_10:
case NV_ARCH_20:
case NV_ARCH_04:
par->chan[1].ddc_base = 0x36;
riva_setup_i2c_bus(&par->chan[1], "BUS1");
case NV_ARCH_03:
par->chan[0].ddc_base = 0x3e;
riva_setup_i2c_bus(&par->chan[0], "BUS0");
}
}
void riva_delete_i2c_busses(struct riva_par *par)
{
if (par->chan[0].par)
i2c_bit_del_bus(&par->chan[0].adapter);
par->chan[0].par = NULL;
if (par->chan[1].par)
i2c_bit_del_bus(&par->chan[1].adapter);
par->chan[1].par = NULL;
}
static u8 *riva_do_probe_i2c_edid(struct riva_i2c_chan *chan)
{
u8 start = 0x0;
struct i2c_msg msgs[] = {
{
.addr = RIVA_DDC,
.len = 1,
.buf = &start,
}, {
.addr = RIVA_DDC,
.flags = I2C_M_RD,
.len = EDID_LENGTH,
},
};
u8 *buf;
buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
if (!buf) {
dev_warn(&chan->par->pdev->dev, "Out of memory!\n");
return NULL;
}
msgs[1].buf = buf;
if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
return buf;
dev_dbg(&chan->par->pdev->dev, "Unable to read EDID block.\n");
kfree(buf);
return NULL;
}
int riva_probe_i2c_connector(struct riva_par *par, int conn, u8 **out_edid)
{
u8 *edid = NULL;
int i;
for (i = 0; i < 3; i++) {
/* Do the real work */
edid = riva_do_probe_i2c_edid(&par->chan[conn-1]);
if (edid)
break;
}
if (out_edid)
*out_edid = edid;
if (!edid)
return 1;
return 0;
}
......@@ -4,6 +4,10 @@
#include <linux/config.h>
#include <linux/fb.h>
#include <video/vga.h>
#include <linux/i2c.h>
#include <linux/i2c-id.h>
#include <linux/i2c-algo-bit.h>
#include "riva_hw.h"
/* GGI compatibility macros */
......@@ -12,6 +16,12 @@
#define NUM_GRC_REGS 0x09
#define NUM_ATC_REGS 0x15
/* I2C */
#define DDC_SCL_READ_MASK (1 << 2)
#define DDC_SCL_WRITE_MASK (1 << 5)
#define DDC_SDA_READ_MASK (1 << 3)
#define DDC_SDA_WRITE_MASK (1 << 4)
/* holds the state of the VGA core and extended Riva hw state from riva_hw.c.
* From KGI originally. */
struct riva_regs {
......@@ -23,6 +33,15 @@ struct riva_regs {
RIVA_HW_STATE ext;
};
struct riva_par;
struct riva_i2c_chan {
struct riva_par *par;
unsigned long ddc_base;
struct i2c_adapter adapter;
struct i2c_algo_bit_data algo;
};
struct riva_par {
RIVA_HW_INST riva; /* interface to riva_hw.c */
......@@ -36,26 +55,22 @@ struct riva_par {
u32 cursor_data[32 * 32/4];
int cursor_reset;
unsigned char *EDID;
int panel_xres, panel_yres;
int hOver_plus, hSync_width, hblank;
int vOver_plus, vSync_width, vblank;
int hAct_high, vAct_high, interlaced;
int synct, misc, clock;
int use_default_var;
int got_dfpinfo;
unsigned int Chipset;
int forceCRTC;
Bool SecondCRTC;
int FlatPanel;
struct pci_dev *pdev;
#ifdef CONFIG_MTRR
struct { int vram; int vram_valid; } mtrr;
#endif
struct riva_i2c_chan chan[3];
};
void riva_common_setup(struct riva_par *);
unsigned long riva_get_memlen(struct riva_par *);
unsigned long riva_get_maxdclk(struct riva_par *);
void riva_delete_i2c_busses(struct riva_par *par);
void riva_create_i2c_busses(struct riva_par *par);
int riva_probe_i2c_connector(struct riva_par *par, int conn, u8 **out_edid);
#endif /* __RIVAFB_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment