162306a36Sopenharmony_ci /***************************************************************************\
262306a36Sopenharmony_ci|*                                                                           *|
362306a36Sopenharmony_ci|*       Copyright 2003 NVIDIA, Corporation.  All rights reserved.           *|
462306a36Sopenharmony_ci|*                                                                           *|
562306a36Sopenharmony_ci|*     NOTICE TO USER:   The source code  is copyrighted under  U.S. and     *|
662306a36Sopenharmony_ci|*     international laws.  Users and possessors of this source code are     *|
762306a36Sopenharmony_ci|*     hereby granted a nonexclusive,  royalty-free copyright license to     *|
862306a36Sopenharmony_ci|*     use this code in individual and commercial software.                  *|
962306a36Sopenharmony_ci|*                                                                           *|
1062306a36Sopenharmony_ci|*     Any use of this source code must include,  in the user documenta-     *|
1162306a36Sopenharmony_ci|*     tion and  internal comments to the code,  notices to the end user     *|
1262306a36Sopenharmony_ci|*     as follows:                                                           *|
1362306a36Sopenharmony_ci|*                                                                           *|
1462306a36Sopenharmony_ci|*       Copyright 2003 NVIDIA, Corporation.  All rights reserved.           *|
1562306a36Sopenharmony_ci|*                                                                           *|
1662306a36Sopenharmony_ci|*     NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY     *|
1762306a36Sopenharmony_ci|*     OF  THIS SOURCE  CODE  FOR ANY PURPOSE.  IT IS  PROVIDED  "AS IS"     *|
1862306a36Sopenharmony_ci|*     WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND.  NVIDIA, CORPOR-     *|
1962306a36Sopenharmony_ci|*     ATION DISCLAIMS ALL WARRANTIES  WITH REGARD  TO THIS SOURCE CODE,     *|
2062306a36Sopenharmony_ci|*     INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE-     *|
2162306a36Sopenharmony_ci|*     MENT,  AND FITNESS  FOR A PARTICULAR PURPOSE.   IN NO EVENT SHALL     *|
2262306a36Sopenharmony_ci|*     NVIDIA, CORPORATION  BE LIABLE FOR ANY SPECIAL,  INDIRECT,  INCI-     *|
2362306a36Sopenharmony_ci|*     DENTAL, OR CONSEQUENTIAL DAMAGES,  OR ANY DAMAGES  WHATSOEVER RE-     *|
2462306a36Sopenharmony_ci|*     SULTING FROM LOSS OF USE,  DATA OR PROFITS,  WHETHER IN AN ACTION     *|
2562306a36Sopenharmony_ci|*     OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  ARISING OUT OF     *|
2662306a36Sopenharmony_ci|*     OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE.     *|
2762306a36Sopenharmony_ci|*                                                                           *|
2862306a36Sopenharmony_ci|*     U.S. Government  End  Users.   This source code  is a "commercial     *|
2962306a36Sopenharmony_ci|*     item,"  as that  term is  defined at  48 C.F.R. 2.101 (OCT 1995),     *|
3062306a36Sopenharmony_ci|*     consisting  of "commercial  computer  software"  and  "commercial     *|
3162306a36Sopenharmony_ci|*     computer  software  documentation,"  as such  terms  are  used in     *|
3262306a36Sopenharmony_ci|*     48 C.F.R. 12.212 (SEPT 1995)  and is provided to the U.S. Govern-     *|
3362306a36Sopenharmony_ci|*     ment only as  a commercial end item.   Consistent with  48 C.F.R.     *|
3462306a36Sopenharmony_ci|*     12.212 and  48 C.F.R. 227.7202-1 through  227.7202-4 (JUNE 1995),     *|
3562306a36Sopenharmony_ci|*     all U.S. Government End Users  acquire the source code  with only     *|
3662306a36Sopenharmony_ci|*     those rights set forth herein.                                        *|
3762306a36Sopenharmony_ci|*                                                                           *|
3862306a36Sopenharmony_ci \***************************************************************************/
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
4262306a36Sopenharmony_ci * XFree86 'nv' driver, this source code is provided under MIT-style licensing
4362306a36Sopenharmony_ci * where the source code is provided "as is" without warranty of any kind.
4462306a36Sopenharmony_ci * The only usage restriction is for the copyright notices to be retained
4562306a36Sopenharmony_ci * whenever code is used.
4662306a36Sopenharmony_ci *
4762306a36Sopenharmony_ci * Antonino Daplas <adaplas@pol.net> 2005-03-11
4862306a36Sopenharmony_ci */
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci#include <video/vga.h>
5162306a36Sopenharmony_ci#include <linux/delay.h>
5262306a36Sopenharmony_ci#include <linux/pci.h>
5362306a36Sopenharmony_ci#include <linux/slab.h>
5462306a36Sopenharmony_ci#include "nv_type.h"
5562306a36Sopenharmony_ci#include "nv_local.h"
5662306a36Sopenharmony_ci#include "nv_proto.h"
5762306a36Sopenharmony_ci/*
5862306a36Sopenharmony_ci * Override VGA I/O routines.
5962306a36Sopenharmony_ci */
6062306a36Sopenharmony_civoid NVWriteCrtc(struct nvidia_par *par, u8 index, u8 value)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	VGA_WR08(par->PCIO, par->IOBase + 0x04, index);
6362306a36Sopenharmony_ci	VGA_WR08(par->PCIO, par->IOBase + 0x05, value);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ciu8 NVReadCrtc(struct nvidia_par *par, u8 index)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	VGA_WR08(par->PCIO, par->IOBase + 0x04, index);
6862306a36Sopenharmony_ci	return (VGA_RD08(par->PCIO, par->IOBase + 0x05));
6962306a36Sopenharmony_ci}
7062306a36Sopenharmony_civoid NVWriteGr(struct nvidia_par *par, u8 index, u8 value)
7162306a36Sopenharmony_ci{
7262306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_GFX_I, index);
7362306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_GFX_D, value);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ciu8 NVReadGr(struct nvidia_par *par, u8 index)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_GFX_I, index);
7862306a36Sopenharmony_ci	return (VGA_RD08(par->PVIO, VGA_GFX_D));
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_civoid NVWriteSeq(struct nvidia_par *par, u8 index, u8 value)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_SEQ_I, index);
8362306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_SEQ_D, value);
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ciu8 NVReadSeq(struct nvidia_par *par, u8 index)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_SEQ_I, index);
8862306a36Sopenharmony_ci	return (VGA_RD08(par->PVIO, VGA_SEQ_D));
8962306a36Sopenharmony_ci}
9062306a36Sopenharmony_civoid NVWriteAttr(struct nvidia_par *par, u8 index, u8 value)
9162306a36Sopenharmony_ci{
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	VGA_RD08(par->PCIO, par->IOBase + 0x0a);
9462306a36Sopenharmony_ci	if (par->paletteEnabled)
9562306a36Sopenharmony_ci		index &= ~0x20;
9662306a36Sopenharmony_ci	else
9762306a36Sopenharmony_ci		index |= 0x20;
9862306a36Sopenharmony_ci	VGA_WR08(par->PCIO, VGA_ATT_IW, index);
9962306a36Sopenharmony_ci	VGA_WR08(par->PCIO, VGA_ATT_W, value);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ciu8 NVReadAttr(struct nvidia_par *par, u8 index)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	VGA_RD08(par->PCIO, par->IOBase + 0x0a);
10462306a36Sopenharmony_ci	if (par->paletteEnabled)
10562306a36Sopenharmony_ci		index &= ~0x20;
10662306a36Sopenharmony_ci	else
10762306a36Sopenharmony_ci		index |= 0x20;
10862306a36Sopenharmony_ci	VGA_WR08(par->PCIO, VGA_ATT_IW, index);
10962306a36Sopenharmony_ci	return (VGA_RD08(par->PCIO, VGA_ATT_R));
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_civoid NVWriteMiscOut(struct nvidia_par *par, u8 value)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	VGA_WR08(par->PVIO, VGA_MIS_W, value);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ciu8 NVReadMiscOut(struct nvidia_par *par)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	return (VGA_RD08(par->PVIO, VGA_MIS_R));
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_civoid NVWriteDacMask(struct nvidia_par *par, u8 value)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	VGA_WR08(par->PDIO, VGA_PEL_MSK, value);
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_civoid NVWriteDacReadAddr(struct nvidia_par *par, u8 value)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	VGA_WR08(par->PDIO, VGA_PEL_IR, value);
12662306a36Sopenharmony_ci}
12762306a36Sopenharmony_civoid NVWriteDacWriteAddr(struct nvidia_par *par, u8 value)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	VGA_WR08(par->PDIO, VGA_PEL_IW, value);
13062306a36Sopenharmony_ci}
13162306a36Sopenharmony_civoid NVWriteDacData(struct nvidia_par *par, u8 value)
13262306a36Sopenharmony_ci{
13362306a36Sopenharmony_ci	VGA_WR08(par->PDIO, VGA_PEL_D, value);
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ciu8 NVReadDacData(struct nvidia_par *par)
13662306a36Sopenharmony_ci{
13762306a36Sopenharmony_ci	return (VGA_RD08(par->PDIO, VGA_PEL_D));
13862306a36Sopenharmony_ci}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_cistatic int NVIsConnected(struct nvidia_par *par, int output)
14162306a36Sopenharmony_ci{
14262306a36Sopenharmony_ci	volatile u32 __iomem *PRAMDAC = par->PRAMDAC0;
14362306a36Sopenharmony_ci	u32 reg52C, reg608, dac0_reg608 = 0;
14462306a36Sopenharmony_ci	int present;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	if (output) {
14762306a36Sopenharmony_ci	    dac0_reg608 = NV_RD32(PRAMDAC, 0x0608);
14862306a36Sopenharmony_ci	    PRAMDAC += 0x800;
14962306a36Sopenharmony_ci	}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	reg52C = NV_RD32(PRAMDAC, 0x052C);
15262306a36Sopenharmony_ci	reg608 = NV_RD32(PRAMDAC, 0x0608);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	NV_WR32(PRAMDAC, 0x0608, reg608 & ~0x00010000);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	NV_WR32(PRAMDAC, 0x052C, reg52C & 0x0000FEEE);
15762306a36Sopenharmony_ci	msleep(1);
15862306a36Sopenharmony_ci	NV_WR32(PRAMDAC, 0x052C, NV_RD32(PRAMDAC, 0x052C) | 1);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	NV_WR32(par->PRAMDAC0, 0x0610, 0x94050140);
16162306a36Sopenharmony_ci	NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) |
16262306a36Sopenharmony_ci		0x00001000);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	msleep(1);
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	present = (NV_RD32(PRAMDAC, 0x0608) & (1 << 28)) ? 1 : 0;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (present)
16962306a36Sopenharmony_ci		printk("nvidiafb: CRTC%i analog found\n", output);
17062306a36Sopenharmony_ci	else
17162306a36Sopenharmony_ci		printk("nvidiafb: CRTC%i analog not found\n", output);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	if (output)
17462306a36Sopenharmony_ci	    NV_WR32(par->PRAMDAC0, 0x0608, dac0_reg608);
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	NV_WR32(PRAMDAC, 0x052C, reg52C);
17762306a36Sopenharmony_ci	NV_WR32(PRAMDAC, 0x0608, reg608);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return present;
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic void NVSelectHeadRegisters(struct nvidia_par *par, int head)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	if (head) {
18562306a36Sopenharmony_ci		par->PCIO = par->PCIO0 + 0x2000;
18662306a36Sopenharmony_ci		par->PCRTC = par->PCRTC0 + 0x800;
18762306a36Sopenharmony_ci		par->PRAMDAC = par->PRAMDAC0 + 0x800;
18862306a36Sopenharmony_ci		par->PDIO = par->PDIO0 + 0x2000;
18962306a36Sopenharmony_ci	} else {
19062306a36Sopenharmony_ci		par->PCIO = par->PCIO0;
19162306a36Sopenharmony_ci		par->PCRTC = par->PCRTC0;
19262306a36Sopenharmony_ci		par->PRAMDAC = par->PRAMDAC0;
19362306a36Sopenharmony_ci		par->PDIO = par->PDIO0;
19462306a36Sopenharmony_ci	}
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic void nv4GetConfig(struct nvidia_par *par)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	if (NV_RD32(par->PFB, 0x0000) & 0x00000100) {
20062306a36Sopenharmony_ci		par->RamAmountKBytes =
20162306a36Sopenharmony_ci		    ((NV_RD32(par->PFB, 0x0000) >> 12) & 0x0F) * 1024 * 2 +
20262306a36Sopenharmony_ci		    1024 * 2;
20362306a36Sopenharmony_ci	} else {
20462306a36Sopenharmony_ci		switch (NV_RD32(par->PFB, 0x0000) & 0x00000003) {
20562306a36Sopenharmony_ci		case 0:
20662306a36Sopenharmony_ci			par->RamAmountKBytes = 1024 * 32;
20762306a36Sopenharmony_ci			break;
20862306a36Sopenharmony_ci		case 1:
20962306a36Sopenharmony_ci			par->RamAmountKBytes = 1024 * 4;
21062306a36Sopenharmony_ci			break;
21162306a36Sopenharmony_ci		case 2:
21262306a36Sopenharmony_ci			par->RamAmountKBytes = 1024 * 8;
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci		case 3:
21562306a36Sopenharmony_ci		default:
21662306a36Sopenharmony_ci			par->RamAmountKBytes = 1024 * 16;
21762306a36Sopenharmony_ci			break;
21862306a36Sopenharmony_ci		}
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci	par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & 0x00000040) ?
22162306a36Sopenharmony_ci	    14318 : 13500;
22262306a36Sopenharmony_ci	par->CURSOR = &par->PRAMIN[0x1E00];
22362306a36Sopenharmony_ci	par->MinVClockFreqKHz = 12000;
22462306a36Sopenharmony_ci	par->MaxVClockFreqKHz = 350000;
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void nv10GetConfig(struct nvidia_par *par)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct pci_dev *dev;
23062306a36Sopenharmony_ci	u32 implementation = par->Chipset & 0x0ff0;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci#ifdef __BIG_ENDIAN
23362306a36Sopenharmony_ci	/* turn on big endian register access */
23462306a36Sopenharmony_ci	if (!(NV_RD32(par->PMC, 0x0004) & 0x01000001)) {
23562306a36Sopenharmony_ci		NV_WR32(par->PMC, 0x0004, 0x01000001);
23662306a36Sopenharmony_ci		mb();
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci#endif
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	dev = pci_get_domain_bus_and_slot(pci_domain_nr(par->pci_dev->bus),
24162306a36Sopenharmony_ci					  0, 1);
24262306a36Sopenharmony_ci	if ((par->Chipset & 0xffff) == 0x01a0) {
24362306a36Sopenharmony_ci		u32 amt;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci		pci_read_config_dword(dev, 0x7c, &amt);
24662306a36Sopenharmony_ci		par->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024;
24762306a36Sopenharmony_ci	} else if ((par->Chipset & 0xffff) == 0x01f0) {
24862306a36Sopenharmony_ci		u32 amt;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci		pci_read_config_dword(dev, 0x84, &amt);
25162306a36Sopenharmony_ci		par->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024;
25262306a36Sopenharmony_ci	} else {
25362306a36Sopenharmony_ci		par->RamAmountKBytes =
25462306a36Sopenharmony_ci		    (NV_RD32(par->PFB, 0x020C) & 0xFFF00000) >> 10;
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci	pci_dev_put(dev);
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 6)) ?
25962306a36Sopenharmony_ci	    14318 : 13500;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (par->twoHeads && (implementation != 0x0110)) {
26262306a36Sopenharmony_ci		if (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 22))
26362306a36Sopenharmony_ci			par->CrystalFreqKHz = 27000;
26462306a36Sopenharmony_ci	}
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_ci	par->CURSOR = NULL;	/* can't set this here */
26762306a36Sopenharmony_ci	par->MinVClockFreqKHz = 12000;
26862306a36Sopenharmony_ci	par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000;
26962306a36Sopenharmony_ci}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ciint NVCommonSetup(struct fb_info *info)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	struct nvidia_par *par = info->par;
27462306a36Sopenharmony_ci	struct fb_var_screeninfo *var;
27562306a36Sopenharmony_ci	u16 implementation = par->Chipset & 0x0ff0;
27662306a36Sopenharmony_ci	u8 *edidA = NULL, *edidB = NULL;
27762306a36Sopenharmony_ci	struct fb_monspecs *monitorA, *monitorB;
27862306a36Sopenharmony_ci	struct fb_monspecs *monA = NULL, *monB = NULL;
27962306a36Sopenharmony_ci	int mobile = 0;
28062306a36Sopenharmony_ci	int tvA = 0;
28162306a36Sopenharmony_ci	int tvB = 0;
28262306a36Sopenharmony_ci	int FlatPanel = -1;	/* really means the CRTC is slaved */
28362306a36Sopenharmony_ci	int Television = 0;
28462306a36Sopenharmony_ci	int err = 0;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	var = kzalloc(sizeof(struct fb_var_screeninfo), GFP_KERNEL);
28762306a36Sopenharmony_ci	monitorA = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);
28862306a36Sopenharmony_ci	monitorB = kzalloc(sizeof(struct fb_monspecs), GFP_KERNEL);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (!var || !monitorA || !monitorB) {
29162306a36Sopenharmony_ci		err = -ENOMEM;
29262306a36Sopenharmony_ci		goto done;
29362306a36Sopenharmony_ci	}
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	par->PRAMIN = par->REGS + (0x00710000 / 4);
29662306a36Sopenharmony_ci	par->PCRTC0 = par->REGS + (0x00600000 / 4);
29762306a36Sopenharmony_ci	par->PRAMDAC0 = par->REGS + (0x00680000 / 4);
29862306a36Sopenharmony_ci	par->PFB = par->REGS + (0x00100000 / 4);
29962306a36Sopenharmony_ci	par->PFIFO = par->REGS + (0x00002000 / 4);
30062306a36Sopenharmony_ci	par->PGRAPH = par->REGS + (0x00400000 / 4);
30162306a36Sopenharmony_ci	par->PEXTDEV = par->REGS + (0x00101000 / 4);
30262306a36Sopenharmony_ci	par->PTIMER = par->REGS + (0x00009000 / 4);
30362306a36Sopenharmony_ci	par->PMC = par->REGS + (0x00000000 / 4);
30462306a36Sopenharmony_ci	par->FIFO = par->REGS + (0x00800000 / 4);
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci	/* 8 bit registers */
30762306a36Sopenharmony_ci	par->PCIO0 = (u8 __iomem *) par->REGS + 0x00601000;
30862306a36Sopenharmony_ci	par->PDIO0 = (u8 __iomem *) par->REGS + 0x00681000;
30962306a36Sopenharmony_ci	par->PVIO = (u8 __iomem *) par->REGS + 0x000C0000;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	par->twoHeads = (par->Architecture >= NV_ARCH_10) &&
31262306a36Sopenharmony_ci	    (implementation != 0x0100) &&
31362306a36Sopenharmony_ci	    (implementation != 0x0150) &&
31462306a36Sopenharmony_ci	    (implementation != 0x01A0) && (implementation != 0x0200);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	par->fpScaler = (par->FpScale && par->twoHeads &&
31762306a36Sopenharmony_ci			 (implementation != 0x0110));
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	par->twoStagePLL = (implementation == 0x0310) ||
32062306a36Sopenharmony_ci	    (implementation == 0x0340) || (par->Architecture >= NV_ARCH_40);
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	par->WaitVSyncPossible = (par->Architecture >= NV_ARCH_10) &&
32362306a36Sopenharmony_ci	    (implementation != 0x0100);
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	par->BlendingPossible = ((par->Chipset & 0xffff) != 0x0020);
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* look for known laptop chips */
32862306a36Sopenharmony_ci	switch (par->Chipset & 0xffff) {
32962306a36Sopenharmony_ci	case 0x0112:
33062306a36Sopenharmony_ci	case 0x0174:
33162306a36Sopenharmony_ci	case 0x0175:
33262306a36Sopenharmony_ci	case 0x0176:
33362306a36Sopenharmony_ci	case 0x0177:
33462306a36Sopenharmony_ci	case 0x0179:
33562306a36Sopenharmony_ci	case 0x017C:
33662306a36Sopenharmony_ci	case 0x017D:
33762306a36Sopenharmony_ci	case 0x0186:
33862306a36Sopenharmony_ci	case 0x0187:
33962306a36Sopenharmony_ci	case 0x018D:
34062306a36Sopenharmony_ci	case 0x01D7:
34162306a36Sopenharmony_ci	case 0x0228:
34262306a36Sopenharmony_ci	case 0x0286:
34362306a36Sopenharmony_ci	case 0x028C:
34462306a36Sopenharmony_ci	case 0x0316:
34562306a36Sopenharmony_ci	case 0x0317:
34662306a36Sopenharmony_ci	case 0x031A:
34762306a36Sopenharmony_ci	case 0x031B:
34862306a36Sopenharmony_ci	case 0x031C:
34962306a36Sopenharmony_ci	case 0x031D:
35062306a36Sopenharmony_ci	case 0x031E:
35162306a36Sopenharmony_ci	case 0x031F:
35262306a36Sopenharmony_ci	case 0x0324:
35362306a36Sopenharmony_ci	case 0x0325:
35462306a36Sopenharmony_ci	case 0x0328:
35562306a36Sopenharmony_ci	case 0x0329:
35662306a36Sopenharmony_ci	case 0x032C:
35762306a36Sopenharmony_ci	case 0x032D:
35862306a36Sopenharmony_ci	case 0x0347:
35962306a36Sopenharmony_ci	case 0x0348:
36062306a36Sopenharmony_ci	case 0x0349:
36162306a36Sopenharmony_ci	case 0x034B:
36262306a36Sopenharmony_ci	case 0x034C:
36362306a36Sopenharmony_ci	case 0x0160:
36462306a36Sopenharmony_ci	case 0x0166:
36562306a36Sopenharmony_ci	case 0x0169:
36662306a36Sopenharmony_ci	case 0x016B:
36762306a36Sopenharmony_ci	case 0x016C:
36862306a36Sopenharmony_ci	case 0x016D:
36962306a36Sopenharmony_ci	case 0x00C8:
37062306a36Sopenharmony_ci	case 0x00CC:
37162306a36Sopenharmony_ci	case 0x0144:
37262306a36Sopenharmony_ci	case 0x0146:
37362306a36Sopenharmony_ci	case 0x0147:
37462306a36Sopenharmony_ci	case 0x0148:
37562306a36Sopenharmony_ci	case 0x0098:
37662306a36Sopenharmony_ci	case 0x0099:
37762306a36Sopenharmony_ci		mobile = 1;
37862306a36Sopenharmony_ci		break;
37962306a36Sopenharmony_ci	default:
38062306a36Sopenharmony_ci		break;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (par->Architecture == NV_ARCH_04)
38462306a36Sopenharmony_ci		nv4GetConfig(par);
38562306a36Sopenharmony_ci	else
38662306a36Sopenharmony_ci		nv10GetConfig(par);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	NVSelectHeadRegisters(par, 0);
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	NVLockUnlock(par, 0);
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	par->IOBase = (NVReadMiscOut(par) & 0x01) ? 0x3d0 : 0x3b0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	par->Television = 0;
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_ci	nvidia_create_i2c_busses(par);
39762306a36Sopenharmony_ci	if (!par->twoHeads) {
39862306a36Sopenharmony_ci		par->CRTCnumber = 0;
39962306a36Sopenharmony_ci		if (nvidia_probe_i2c_connector(info, 1, &edidA))
40062306a36Sopenharmony_ci			nvidia_probe_of_connector(info, 1, &edidA);
40162306a36Sopenharmony_ci		if (edidA && !fb_parse_edid(edidA, var)) {
40262306a36Sopenharmony_ci			printk("nvidiafb: EDID found from BUS1\n");
40362306a36Sopenharmony_ci			monA = monitorA;
40462306a36Sopenharmony_ci			fb_edid_to_monspecs(edidA, monA);
40562306a36Sopenharmony_ci			FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci			/* NV4 doesn't support FlatPanels */
40862306a36Sopenharmony_ci			if ((par->Chipset & 0x0fff) <= 0x0020)
40962306a36Sopenharmony_ci				FlatPanel = 0;
41062306a36Sopenharmony_ci		} else {
41162306a36Sopenharmony_ci			VGA_WR08(par->PCIO, 0x03D4, 0x28);
41262306a36Sopenharmony_ci			if (VGA_RD08(par->PCIO, 0x03D5) & 0x80) {
41362306a36Sopenharmony_ci				VGA_WR08(par->PCIO, 0x03D4, 0x33);
41462306a36Sopenharmony_ci				if (!(VGA_RD08(par->PCIO, 0x03D5) & 0x01))
41562306a36Sopenharmony_ci					Television = 1;
41662306a36Sopenharmony_ci				FlatPanel = 1;
41762306a36Sopenharmony_ci			} else {
41862306a36Sopenharmony_ci				FlatPanel = 0;
41962306a36Sopenharmony_ci			}
42062306a36Sopenharmony_ci			printk("nvidiafb: HW is currently programmed for %s\n",
42162306a36Sopenharmony_ci			       FlatPanel ? (Television ? "TV" : "DFP") :
42262306a36Sopenharmony_ci			       "CRT");
42362306a36Sopenharmony_ci		}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci		if (par->FlatPanel == -1) {
42662306a36Sopenharmony_ci			par->FlatPanel = FlatPanel;
42762306a36Sopenharmony_ci			par->Television = Television;
42862306a36Sopenharmony_ci		} else {
42962306a36Sopenharmony_ci			printk("nvidiafb: Forcing display type to %s as "
43062306a36Sopenharmony_ci			       "specified\n", par->FlatPanel ? "DFP" : "CRT");
43162306a36Sopenharmony_ci		}
43262306a36Sopenharmony_ci	} else {
43362306a36Sopenharmony_ci		u8 outputAfromCRTC, outputBfromCRTC;
43462306a36Sopenharmony_ci		int CRTCnumber = -1;
43562306a36Sopenharmony_ci		u8 slaved_on_A, slaved_on_B;
43662306a36Sopenharmony_ci		int analog_on_A, analog_on_B;
43762306a36Sopenharmony_ci		u32 oldhead;
43862306a36Sopenharmony_ci		u8 cr44;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci		if (implementation != 0x0110) {
44162306a36Sopenharmony_ci			if (NV_RD32(par->PRAMDAC0, 0x0000052C) & 0x100)
44262306a36Sopenharmony_ci				outputAfromCRTC = 1;
44362306a36Sopenharmony_ci			else
44462306a36Sopenharmony_ci				outputAfromCRTC = 0;
44562306a36Sopenharmony_ci			if (NV_RD32(par->PRAMDAC0, 0x0000252C) & 0x100)
44662306a36Sopenharmony_ci				outputBfromCRTC = 1;
44762306a36Sopenharmony_ci			else
44862306a36Sopenharmony_ci				outputBfromCRTC = 0;
44962306a36Sopenharmony_ci			analog_on_A = NVIsConnected(par, 0);
45062306a36Sopenharmony_ci			analog_on_B = NVIsConnected(par, 1);
45162306a36Sopenharmony_ci		} else {
45262306a36Sopenharmony_ci			outputAfromCRTC = 0;
45362306a36Sopenharmony_ci			outputBfromCRTC = 1;
45462306a36Sopenharmony_ci			analog_on_A = 0;
45562306a36Sopenharmony_ci			analog_on_B = 0;
45662306a36Sopenharmony_ci		}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D4, 0x44);
45962306a36Sopenharmony_ci		cr44 = VGA_RD08(par->PCIO, 0x03D5);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D5, 3);
46262306a36Sopenharmony_ci		NVSelectHeadRegisters(par, 1);
46362306a36Sopenharmony_ci		NVLockUnlock(par, 0);
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D4, 0x28);
46662306a36Sopenharmony_ci		slaved_on_B = VGA_RD08(par->PCIO, 0x03D5) & 0x80;
46762306a36Sopenharmony_ci		if (slaved_on_B) {
46862306a36Sopenharmony_ci			VGA_WR08(par->PCIO, 0x03D4, 0x33);
46962306a36Sopenharmony_ci			tvB = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01);
47062306a36Sopenharmony_ci		}
47162306a36Sopenharmony_ci
47262306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D4, 0x44);
47362306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D5, 0);
47462306a36Sopenharmony_ci		NVSelectHeadRegisters(par, 0);
47562306a36Sopenharmony_ci		NVLockUnlock(par, 0);
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D4, 0x28);
47862306a36Sopenharmony_ci		slaved_on_A = VGA_RD08(par->PCIO, 0x03D5) & 0x80;
47962306a36Sopenharmony_ci		if (slaved_on_A) {
48062306a36Sopenharmony_ci			VGA_WR08(par->PCIO, 0x03D4, 0x33);
48162306a36Sopenharmony_ci			tvA = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01);
48262306a36Sopenharmony_ci		}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci		oldhead = NV_RD32(par->PCRTC0, 0x00000860);
48562306a36Sopenharmony_ci		NV_WR32(par->PCRTC0, 0x00000860, oldhead | 0x00000010);
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci		if (nvidia_probe_i2c_connector(info, 1, &edidA))
48862306a36Sopenharmony_ci			nvidia_probe_of_connector(info, 1, &edidA);
48962306a36Sopenharmony_ci		if (edidA && !fb_parse_edid(edidA, var)) {
49062306a36Sopenharmony_ci			printk("nvidiafb: EDID found from BUS1\n");
49162306a36Sopenharmony_ci			monA = monitorA;
49262306a36Sopenharmony_ci			fb_edid_to_monspecs(edidA, monA);
49362306a36Sopenharmony_ci		}
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		if (nvidia_probe_i2c_connector(info, 2, &edidB))
49662306a36Sopenharmony_ci			nvidia_probe_of_connector(info, 2, &edidB);
49762306a36Sopenharmony_ci		if (edidB && !fb_parse_edid(edidB, var)) {
49862306a36Sopenharmony_ci			printk("nvidiafb: EDID found from BUS2\n");
49962306a36Sopenharmony_ci			monB = monitorB;
50062306a36Sopenharmony_ci			fb_edid_to_monspecs(edidB, monB);
50162306a36Sopenharmony_ci		}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci		if (slaved_on_A && !tvA) {
50462306a36Sopenharmony_ci			CRTCnumber = 0;
50562306a36Sopenharmony_ci			FlatPanel = 1;
50662306a36Sopenharmony_ci			printk("nvidiafb: CRTC 0 is currently programmed for "
50762306a36Sopenharmony_ci			       "DFP\n");
50862306a36Sopenharmony_ci		} else if (slaved_on_B && !tvB) {
50962306a36Sopenharmony_ci			CRTCnumber = 1;
51062306a36Sopenharmony_ci			FlatPanel = 1;
51162306a36Sopenharmony_ci			printk("nvidiafb: CRTC 1 is currently programmed "
51262306a36Sopenharmony_ci			       "for DFP\n");
51362306a36Sopenharmony_ci		} else if (analog_on_A) {
51462306a36Sopenharmony_ci			CRTCnumber = outputAfromCRTC;
51562306a36Sopenharmony_ci			FlatPanel = 0;
51662306a36Sopenharmony_ci			printk("nvidiafb: CRTC %i appears to have a "
51762306a36Sopenharmony_ci			       "CRT attached\n", CRTCnumber);
51862306a36Sopenharmony_ci		} else if (analog_on_B) {
51962306a36Sopenharmony_ci			CRTCnumber = outputBfromCRTC;
52062306a36Sopenharmony_ci			FlatPanel = 0;
52162306a36Sopenharmony_ci			printk("nvidiafb: CRTC %i appears to have a "
52262306a36Sopenharmony_ci			       "CRT attached\n", CRTCnumber);
52362306a36Sopenharmony_ci		} else if (slaved_on_A) {
52462306a36Sopenharmony_ci			CRTCnumber = 0;
52562306a36Sopenharmony_ci			FlatPanel = 1;
52662306a36Sopenharmony_ci			Television = 1;
52762306a36Sopenharmony_ci			printk("nvidiafb: CRTC 0 is currently programmed "
52862306a36Sopenharmony_ci			       "for TV\n");
52962306a36Sopenharmony_ci		} else if (slaved_on_B) {
53062306a36Sopenharmony_ci			CRTCnumber = 1;
53162306a36Sopenharmony_ci			FlatPanel = 1;
53262306a36Sopenharmony_ci			Television = 1;
53362306a36Sopenharmony_ci			printk("nvidiafb: CRTC 1 is currently programmed for "
53462306a36Sopenharmony_ci			       "TV\n");
53562306a36Sopenharmony_ci		} else if (monA) {
53662306a36Sopenharmony_ci			FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0;
53762306a36Sopenharmony_ci		} else if (monB) {
53862306a36Sopenharmony_ci			FlatPanel = (monB->input & FB_DISP_DDI) ? 1 : 0;
53962306a36Sopenharmony_ci		}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci		if (par->FlatPanel == -1) {
54262306a36Sopenharmony_ci			if (FlatPanel != -1) {
54362306a36Sopenharmony_ci				par->FlatPanel = FlatPanel;
54462306a36Sopenharmony_ci				par->Television = Television;
54562306a36Sopenharmony_ci			} else {
54662306a36Sopenharmony_ci				printk("nvidiafb: Unable to detect display "
54762306a36Sopenharmony_ci				       "type...\n");
54862306a36Sopenharmony_ci				if (mobile) {
54962306a36Sopenharmony_ci					printk("...On a laptop, assuming "
55062306a36Sopenharmony_ci					       "DFP\n");
55162306a36Sopenharmony_ci					par->FlatPanel = 1;
55262306a36Sopenharmony_ci				} else {
55362306a36Sopenharmony_ci					printk("...Using default of CRT\n");
55462306a36Sopenharmony_ci					par->FlatPanel = 0;
55562306a36Sopenharmony_ci				}
55662306a36Sopenharmony_ci			}
55762306a36Sopenharmony_ci		} else {
55862306a36Sopenharmony_ci			printk("nvidiafb: Forcing display type to %s as "
55962306a36Sopenharmony_ci			       "specified\n", par->FlatPanel ? "DFP" : "CRT");
56062306a36Sopenharmony_ci		}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		if (par->CRTCnumber == -1) {
56362306a36Sopenharmony_ci			if (CRTCnumber != -1)
56462306a36Sopenharmony_ci				par->CRTCnumber = CRTCnumber;
56562306a36Sopenharmony_ci			else {
56662306a36Sopenharmony_ci				printk("nvidiafb: Unable to detect which "
56762306a36Sopenharmony_ci				       "CRTCNumber...\n");
56862306a36Sopenharmony_ci				if (par->FlatPanel)
56962306a36Sopenharmony_ci					par->CRTCnumber = 1;
57062306a36Sopenharmony_ci				else
57162306a36Sopenharmony_ci					par->CRTCnumber = 0;
57262306a36Sopenharmony_ci				printk("...Defaulting to CRTCNumber %i\n",
57362306a36Sopenharmony_ci				       par->CRTCnumber);
57462306a36Sopenharmony_ci			}
57562306a36Sopenharmony_ci		} else {
57662306a36Sopenharmony_ci			printk("nvidiafb: Forcing CRTCNumber %i as "
57762306a36Sopenharmony_ci			       "specified\n", par->CRTCnumber);
57862306a36Sopenharmony_ci		}
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci		if (monA) {
58162306a36Sopenharmony_ci			if (((monA->input & FB_DISP_DDI) &&
58262306a36Sopenharmony_ci			     par->FlatPanel) ||
58362306a36Sopenharmony_ci			    ((!(monA->input & FB_DISP_DDI)) &&
58462306a36Sopenharmony_ci			     !par->FlatPanel)) {
58562306a36Sopenharmony_ci				if (monB) {
58662306a36Sopenharmony_ci					fb_destroy_modedb(monB->modedb);
58762306a36Sopenharmony_ci					monB = NULL;
58862306a36Sopenharmony_ci				}
58962306a36Sopenharmony_ci			} else {
59062306a36Sopenharmony_ci				fb_destroy_modedb(monA->modedb);
59162306a36Sopenharmony_ci				monA = NULL;
59262306a36Sopenharmony_ci			}
59362306a36Sopenharmony_ci		}
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci		if (monB) {
59662306a36Sopenharmony_ci			if (((monB->input & FB_DISP_DDI) &&
59762306a36Sopenharmony_ci			     !par->FlatPanel) ||
59862306a36Sopenharmony_ci			    ((!(monB->input & FB_DISP_DDI)) &&
59962306a36Sopenharmony_ci			     par->FlatPanel)) {
60062306a36Sopenharmony_ci				fb_destroy_modedb(monB->modedb);
60162306a36Sopenharmony_ci				monB = NULL;
60262306a36Sopenharmony_ci			} else
60362306a36Sopenharmony_ci				monA = monB;
60462306a36Sopenharmony_ci		}
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci		if (implementation == 0x0110)
60762306a36Sopenharmony_ci			cr44 = par->CRTCnumber * 0x3;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci		NV_WR32(par->PCRTC0, 0x00000860, oldhead);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D4, 0x44);
61262306a36Sopenharmony_ci		VGA_WR08(par->PCIO, 0x03D5, cr44);
61362306a36Sopenharmony_ci		NVSelectHeadRegisters(par, par->CRTCnumber);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	printk("nvidiafb: Using %s on CRTC %i\n",
61762306a36Sopenharmony_ci	       par->FlatPanel ? (par->Television ? "TV" : "DFP") : "CRT",
61862306a36Sopenharmony_ci	       par->CRTCnumber);
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	if (par->FlatPanel && !par->Television) {
62162306a36Sopenharmony_ci		par->fpWidth = NV_RD32(par->PRAMDAC, 0x0820) + 1;
62262306a36Sopenharmony_ci		par->fpHeight = NV_RD32(par->PRAMDAC, 0x0800) + 1;
62362306a36Sopenharmony_ci		par->fpSyncs = NV_RD32(par->PRAMDAC, 0x0848) & 0x30000033;
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci		printk("nvidiafb: Panel size is %i x %i\n", par->fpWidth, par->fpHeight);
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (monA)
62962306a36Sopenharmony_ci		info->monspecs = *monA;
63062306a36Sopenharmony_ci
63162306a36Sopenharmony_ci	if (!par->FlatPanel || !par->twoHeads)
63262306a36Sopenharmony_ci		par->FPDither = 0;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci	par->LVDS = 0;
63562306a36Sopenharmony_ci	if (par->FlatPanel && par->twoHeads) {
63662306a36Sopenharmony_ci		NV_WR32(par->PRAMDAC0, 0x08B0, 0x00010004);
63762306a36Sopenharmony_ci		if (NV_RD32(par->PRAMDAC0, 0x08b4) & 1)
63862306a36Sopenharmony_ci			par->LVDS = 1;
63962306a36Sopenharmony_ci		printk("nvidiafb: Panel is %s\n", par->LVDS ? "LVDS" : "TMDS");
64062306a36Sopenharmony_ci	}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci	kfree(edidA);
64362306a36Sopenharmony_ci	kfree(edidB);
64462306a36Sopenharmony_cidone:
64562306a36Sopenharmony_ci	kfree(var);
64662306a36Sopenharmony_ci	kfree(monitorA);
64762306a36Sopenharmony_ci	kfree(monitorB);
64862306a36Sopenharmony_ci	return err;
64962306a36Sopenharmony_ci}
650