summaryrefslogtreecommitdiffstats
path: root/drivers/video/fbdev/nvidia
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-09-13 12:27:00 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-09-13 12:27:00 -0700
commit22098763a10d9c1340827fcf6edab66f153b27f0 (patch)
tree3962f95a34df034652ff93686cdd156c7197f2d9 /drivers/video/fbdev/nvidia
downloadlinux-stable-22098763a10d9c1340827fcf6edab66f153b27f0.tar.gz
linux-stable-22098763a10d9c1340827fcf6edab66f153b27f0.zip
Merge tag 'trace-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-tracegrafted
Pull tracing fixes from Steven Rostedt: - Don't destroy user event fields when removal fails User event fields are destroyed before the event is removed from visibility. But that can fail leaving the still visible event with no fields. Move the destroying of the fields to after the event is successfully removed from visibility. - Initialize function graph state is fork before calling copy_exec_state() For non-CLONE_VM forks, copy_exec_state() allocates a new task_exec_state. If that allocation fails, ftrace_graph_exit_task() will free the tasks ret_stack pointer. Since that pointer is still using the parent's ret_stack, it mistakenly frees the parent's pointer too. Call ftrace_graph_init() on the task first which will NULL out the new tasks's ret_stack and if the copy fails, it will not free anything. - Remove FGRAPH_MAX_INDEX The macro FGRAPH_MAX_INDEX was added but never used. Remove it. - Save ent_size in function graph printing of nested functions The function graph tracer needs to look at the next event to see if the next event is the return of the current function entry. If it is, it prints a single line: ktime_get(); Otherwise it prints it like a nested function: tick_nohz_irq_exit() { ktime_get(); kcpustat_irq_exit(); } In order to look at the next event, it must save the current event so that it has the information to print from it. It saves the event in the iterator descriptor called "ent". What it doesn't save is the ent_size of the event which is now used to know if the function graph arguments are to be printed. The peek doesn't save the size so the size used happens to be that of the size of the last event that was seen. Save the entry event size in the iterator descriptor so that the correct size is used. - Fix several errors with freeing data in the histogram code The histogram code had a lot of leaked or or incorrect accounting when failures happen. Correct them. - Fix histogram regression of .percent and .graph modifiers Up until 6.3 histogram values could have "percent" or "graph" modifiers that changed how they were printed. But a change that added restricting histograms values from being strings, stack traces and other modifiers inadvertently prevented them from using the percent and graph modifiers, which were legal use cases for values. Put back the percent and graph modifiers. - Fix various typos in the comments - Set the trace_clock before initializing a histogram with clock argument The histogram API allows the user to specific which trace clock to use via a "clock=" string. The histogram is set up first before the clock is checked. If the passed in clock is not valid, it exits without fully fixing up the histogram leaving it on the list and a use-after-free can trigger. Update the clock argument first and if it fails then exit gracefully before the histogram trigger is placed on any lists. - Restore :mod: trailer after parsing in ftrace_set_clr_event The function ftrace_set_clr_event() modifies the parse string and needs to put it back to what was passed in. It searches for ":mod:" via a strsep() but fails to put back the first ':' in the string. Add back the ':' in the passed in string. - Take trace_array reference when opening a tracer options file The options files are dynamically created and some tracers add their own options. When a tracer adds their own list of options, the trace_array holding them has an array to hold the list of options for each tracer. This array increases in size via a krealloc(), and the new entry gets a newly allocated array to hold the options of the new tracer being added. The element in each entry of the tracer's option array holds a pointer back to the trace_array, a pointer to the tracer it is associated to, a pointer to the flags of the option. The issue is that these arrays are freed when the trace_array is freed when its instance it represents is removed from the instances directory. There's a race that an open of one of these options files can happen when the instance is being removed. Add a new helper function to be called by the open function of the options file to iterate all existing trace_arrays under a lock and find the one that has the given option element in one of it's tracer arrays. If found, then update the associated trace_array's reference counter to keep it from being freed. If not found, have the open call return -ENODEV. - Disable interrupts when acquiring the lock in rb_wake_up_waiters() The function rb_wake_up_waiters() assumes it will be called in interrupt context and does not disable irqs when taking cpu_buffer->reader_lock, which can be called in hard interrupt context. The issue is in PREEMPT_RT, this function is called in thread context leaving this lock open to a deadlock. Take the lock with interrupts disabled. - Use rcu_assign_pointer() for tmp_ops filter hash The tmp_ops used in update_ftrace_direct_mod() assigns its filter_hash field directly, but that field is annotated as __rcu and sparse complains. Assign it with rcu_assign_pointer() - Fix use-after-free in enable_trigger_private_data_free() The trace_event_call is accessed through the event_trigger_data's trace_event_file pointer to put the trace_event_call on freeing. The issue is that the trace_event_file data may have been freed already causing a use-after-free. Add a field to the event_trigger_data that points directly to the trace_event_call so that it can decrement its reference directly without needing to go through the trace_event_file. - Fix accounting of buffer data remote headers trace_buffer_desc_size() and trace_remote_alloc_buffer() undercount the number of pages is needed for the asked for size as it doesn't take into account the meta data on each page. Add a helper function to do the calculation properly and use that in these functions. - Catch nr_page_va overflow in ring_buffer_desc sizing The number of pages per remote ring buffer is capped by ring_buffer_desc::nr_page_va (32 bits). A buffer_size large enough to overflow that field would silently allocate a descriptor smaller than what was asked for. - Do not resize the subbuf order if any per_cpu buffer is disabled The mmapping of ring buffers disables resizing the subbuffers, but it is done per-cpu whereas the subbuf size change is done for all the per_cpu buffers under the buffer->mutex. It could change the size of some while the mapping is happening on others. Have the resize of the subbuf order check all the per_cpu buffers under the lock to see if any of them is disabled before starting and causing an inconsistency between buffers that are being mapped. * tag 'trace-v7.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/trace/linux-trace: (25 commits) ring-buffer: Check resize_disabled before publishing the new subbuf order tracing/remotes: Catch nr_page_va overflow in ring_buffer_desc sizing tracing/remotes: Account for ring buffer page header in size calculation tracing: Don't dereference trace_event_file in deferred trigger free ftrace: Use rcu_assign_pointer() for tmp_ops filter hash ring-buffer: Acquire the lock with irqsave in rb_wake_up_waiters() tracing: Take trace_array reference when opening a tracer options file tracing: Fix ring_buffer_read_page_size() kernel-doc tracing: Restore :mod: trailer after parsing in ftrace_set_clr_event() tracing: Fix memory corruption from a "STACKTRACE" histogram key tracing: Fix memory corruption from the histogram stacktrace modifier tracing: Undo the registration when enabling the histogram trigger fails tracing: Take the reference before publishing the named histogram trigger tracing: Set the trace clock before registering the histogram trigger tracing: Fix typo "preceeded" in comment tracing: Fix typo "availabe" in comment tracing: Let histogram values keep the percent and graph modifiers tracing: Keep the entry count when the histogram stats allocation fails tracing: Free histogram the field rejected for a bad modifier tracing: Free histogram the var ref when its initialization fails ...
Diffstat (limited to 'drivers/video/fbdev/nvidia')
-rw-r--r--drivers/video/fbdev/nvidia/Makefile13
-rw-r--r--drivers/video/fbdev/nvidia/nv_accel.c418
-rw-r--r--drivers/video/fbdev/nvidia/nv_backlight.c131
-rw-r--r--drivers/video/fbdev/nvidia/nv_dma.h188
-rw-r--r--drivers/video/fbdev/nvidia/nv_hw.c1688
-rw-r--r--drivers/video/fbdev/nvidia/nv_i2c.c171
-rw-r--r--drivers/video/fbdev/nvidia/nv_local.h114
-rw-r--r--drivers/video/fbdev/nvidia/nv_of.c78
-rw-r--r--drivers/video/fbdev/nvidia/nv_proto.h68
-rw-r--r--drivers/video/fbdev/nvidia/nv_setup.c649
-rw-r--r--drivers/video/fbdev/nvidia/nv_type.h176
-rw-r--r--drivers/video/fbdev/nvidia/nvidia.c1596
12 files changed, 5290 insertions, 0 deletions
diff --git a/drivers/video/fbdev/nvidia/Makefile b/drivers/video/fbdev/nvidia/Makefile
new file mode 100644
index 000000000..cdd6e8ac4
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/Makefile
@@ -0,0 +1,13 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the nVidia framebuffer driver
+#
+
+obj-$(CONFIG_FB_NVIDIA) += nvidiafb.o
+
+nvidiafb-y := nvidia.o nv_hw.o nv_setup.o \
+ nv_accel.o nv_of.o
+nvidiafb-$(CONFIG_FB_NVIDIA_I2C) += nv_i2c.o
+nvidiafb-$(CONFIG_FB_NVIDIA_BACKLIGHT) += nv_backlight.o
+
+nvidiafb-objs := $(nvidiafb-y)
diff --git a/drivers/video/fbdev/nvidia/nv_accel.c b/drivers/video/fbdev/nvidia/nv_accel.c
new file mode 100644
index 000000000..7341fed63
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_accel.c
@@ -0,0 +1,418 @@
+ /***************************************************************************\
+|* *|
+|* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
+|* international laws. Users and possessors of this source code are *|
+|* hereby granted a nonexclusive, royalty-free copyright license to *|
+|* use this code in individual and commercial software. *|
+|* *|
+|* Any use of this source code must include, in the user documenta- *|
+|* tion and internal comments to the code, notices to the end user *|
+|* as follows: *|
+|* *|
+|* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
+|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
+|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
+|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
+|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
+|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
+|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
+|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
+|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
+|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
+|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
+|* *|
+|* U.S. Government End Users. This source code is a "commercial *|
+|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
+|* consisting of "commercial computer software" and "commercial *|
+|* computer software documentation," as such terms are used in *|
+|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
+|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
+|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
+|* all U.S. Government End Users acquire the source code with only *|
+|* those rights set forth herein. *|
+|* *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#include <linux/fb.h>
+#include <linux/nmi.h>
+
+#include "nv_type.h"
+#include "nv_proto.h"
+#include "nv_dma.h"
+#include "nv_local.h"
+
+/* There is a HW race condition with videoram command buffers.
+ You can't jump to the location of your put offset. We write put
+ at the jump offset + SKIPS dwords with noop padding in between
+ to solve this problem */
+#define SKIPS 8
+
+static const int NVCopyROP[16] = {
+ 0xCC, /* copy */
+ 0x55 /* invert */
+};
+
+static const int NVCopyROP_PM[16] = {
+ 0xCA, /* copy */
+ 0x5A, /* invert */
+};
+
+static inline void nvidiafb_safe_mode(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+
+ touch_softlockup_watchdog();
+ info->pixmap.scan_align = 1;
+ par->lockup = 1;
+}
+
+static inline void NVFlush(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ int count = 1000000000;
+
+ while (--count && READ_GET(par) != par->dmaPut) ;
+
+ if (!count) {
+ printk("nvidiafb: DMA Flush lockup\n");
+ nvidiafb_safe_mode(info);
+ }
+}
+
+static inline void NVSync(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ int count = 1000000000;
+
+ while (--count && NV_RD32(par->PGRAPH, 0x0700)) ;
+
+ if (!count) {
+ printk("nvidiafb: DMA Sync lockup\n");
+ nvidiafb_safe_mode(info);
+ }
+}
+
+static void NVDmaKickoff(struct nvidia_par *par)
+{
+ if (par->dmaCurrent != par->dmaPut) {
+ par->dmaPut = par->dmaCurrent;
+ WRITE_PUT(par, par->dmaPut);
+ }
+}
+
+static void NVDmaWait(struct fb_info *info, int size)
+{
+ struct nvidia_par *par = info->par;
+ int dmaGet;
+ int count = 1000000000, cnt;
+ size++;
+
+ while (par->dmaFree < size && --count && !par->lockup) {
+ dmaGet = READ_GET(par);
+
+ if (par->dmaPut >= dmaGet) {
+ par->dmaFree = par->dmaMax - par->dmaCurrent;
+ if (par->dmaFree < size) {
+ NVDmaNext(par, 0x20000000);
+ if (dmaGet <= SKIPS) {
+ if (par->dmaPut <= SKIPS)
+ WRITE_PUT(par, SKIPS + 1);
+ cnt = 1000000000;
+ do {
+ dmaGet = READ_GET(par);
+ } while (--cnt && dmaGet <= SKIPS);
+ if (!cnt) {
+ printk("DMA Get lockup\n");
+ par->lockup = 1;
+ }
+ }
+ WRITE_PUT(par, SKIPS);
+ par->dmaCurrent = par->dmaPut = SKIPS;
+ par->dmaFree = dmaGet - (SKIPS + 1);
+ }
+ } else
+ par->dmaFree = dmaGet - par->dmaCurrent - 1;
+ }
+
+ if (!count) {
+ printk("nvidiafb: DMA Wait Lockup\n");
+ nvidiafb_safe_mode(info);
+ }
+}
+
+static void NVSetPattern(struct fb_info *info, u32 clr0, u32 clr1,
+ u32 pat0, u32 pat1)
+{
+ struct nvidia_par *par = info->par;
+
+ NVDmaStart(info, par, PATTERN_COLOR_0, 4);
+ NVDmaNext(par, clr0);
+ NVDmaNext(par, clr1);
+ NVDmaNext(par, pat0);
+ NVDmaNext(par, pat1);
+}
+
+static void NVSetRopSolid(struct fb_info *info, u32 rop, u32 planemask)
+{
+ struct nvidia_par *par = info->par;
+
+ if (planemask != ~0) {
+ NVSetPattern(info, 0, planemask, ~0, ~0);
+ if (par->currentRop != (rop + 32)) {
+ NVDmaStart(info, par, ROP_SET, 1);
+ NVDmaNext(par, NVCopyROP_PM[rop]);
+ par->currentRop = rop + 32;
+ }
+ } else if (par->currentRop != rop) {
+ if (par->currentRop >= 16)
+ NVSetPattern(info, ~0, ~0, ~0, ~0);
+ NVDmaStart(info, par, ROP_SET, 1);
+ NVDmaNext(par, NVCopyROP[rop]);
+ par->currentRop = rop;
+ }
+}
+
+static void NVSetClippingRectangle(struct fb_info *info, int x1, int y1,
+ int x2, int y2)
+{
+ struct nvidia_par *par = info->par;
+ int h = y2 - y1 + 1;
+ int w = x2 - x1 + 1;
+
+ NVDmaStart(info, par, CLIP_POINT, 2);
+ NVDmaNext(par, (y1 << 16) | x1);
+ NVDmaNext(par, (h << 16) | w);
+}
+
+void NVResetGraphics(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ u32 surfaceFormat, patternFormat, rectFormat, lineFormat;
+ int pitch, i;
+
+ pitch = info->fix.line_length;
+
+ par->dmaBase = (u32 __iomem *) (&par->FbStart[par->FbUsableSize]);
+
+ for (i = 0; i < SKIPS; i++)
+ NV_WR32(&par->dmaBase[i], 0, 0x00000000);
+
+ NV_WR32(&par->dmaBase[0x0 + SKIPS], 0, 0x00040000);
+ NV_WR32(&par->dmaBase[0x1 + SKIPS], 0, 0x80000010);
+ NV_WR32(&par->dmaBase[0x2 + SKIPS], 0, 0x00042000);
+ NV_WR32(&par->dmaBase[0x3 + SKIPS], 0, 0x80000011);
+ NV_WR32(&par->dmaBase[0x4 + SKIPS], 0, 0x00044000);
+ NV_WR32(&par->dmaBase[0x5 + SKIPS], 0, 0x80000012);
+ NV_WR32(&par->dmaBase[0x6 + SKIPS], 0, 0x00046000);
+ NV_WR32(&par->dmaBase[0x7 + SKIPS], 0, 0x80000013);
+ NV_WR32(&par->dmaBase[0x8 + SKIPS], 0, 0x00048000);
+ NV_WR32(&par->dmaBase[0x9 + SKIPS], 0, 0x80000014);
+ NV_WR32(&par->dmaBase[0xA + SKIPS], 0, 0x0004A000);
+ NV_WR32(&par->dmaBase[0xB + SKIPS], 0, 0x80000015);
+ NV_WR32(&par->dmaBase[0xC + SKIPS], 0, 0x0004C000);
+ NV_WR32(&par->dmaBase[0xD + SKIPS], 0, 0x80000016);
+ NV_WR32(&par->dmaBase[0xE + SKIPS], 0, 0x0004E000);
+ NV_WR32(&par->dmaBase[0xF + SKIPS], 0, 0x80000017);
+
+ par->dmaPut = 0;
+ par->dmaCurrent = 16 + SKIPS;
+ par->dmaMax = 8191;
+ par->dmaFree = par->dmaMax - par->dmaCurrent;
+
+ switch (info->var.bits_per_pixel) {
+ case 32:
+ case 24:
+ surfaceFormat = SURFACE_FORMAT_DEPTH24;
+ patternFormat = PATTERN_FORMAT_DEPTH24;
+ rectFormat = RECT_FORMAT_DEPTH24;
+ lineFormat = LINE_FORMAT_DEPTH24;
+ break;
+ case 16:
+ surfaceFormat = SURFACE_FORMAT_DEPTH16;
+ patternFormat = PATTERN_FORMAT_DEPTH16;
+ rectFormat = RECT_FORMAT_DEPTH16;
+ lineFormat = LINE_FORMAT_DEPTH16;
+ break;
+ default:
+ surfaceFormat = SURFACE_FORMAT_DEPTH8;
+ patternFormat = PATTERN_FORMAT_DEPTH8;
+ rectFormat = RECT_FORMAT_DEPTH8;
+ lineFormat = LINE_FORMAT_DEPTH8;
+ break;
+ }
+
+ NVDmaStart(info, par, SURFACE_FORMAT, 4);
+ NVDmaNext(par, surfaceFormat);
+ NVDmaNext(par, pitch | (pitch << 16));
+ NVDmaNext(par, 0);
+ NVDmaNext(par, 0);
+
+ NVDmaStart(info, par, PATTERN_FORMAT, 1);
+ NVDmaNext(par, patternFormat);
+
+ NVDmaStart(info, par, RECT_FORMAT, 1);
+ NVDmaNext(par, rectFormat);
+
+ NVDmaStart(info, par, LINE_FORMAT, 1);
+ NVDmaNext(par, lineFormat);
+
+ par->currentRop = ~0; /* set to something invalid */
+ NVSetRopSolid(info, ROP_COPY, ~0);
+
+ NVSetClippingRectangle(info, 0, 0, info->var.xres_virtual,
+ info->var.yres_virtual);
+
+ NVDmaKickoff(par);
+}
+
+int nvidiafb_sync(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return 0;
+
+ if (!par->lockup)
+ NVFlush(info);
+
+ if (!par->lockup)
+ NVSync(info);
+
+ return 0;
+}
+
+void nvidiafb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
+{
+ struct nvidia_par *par = info->par;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+
+ if (par->lockup) {
+ cfb_copyarea(info, region);
+ return;
+ }
+
+ NVDmaStart(info, par, BLIT_POINT_SRC, 3);
+ NVDmaNext(par, (region->sy << 16) | region->sx);
+ NVDmaNext(par, (region->dy << 16) | region->dx);
+ NVDmaNext(par, (region->height << 16) | region->width);
+
+ NVDmaKickoff(par);
+}
+
+void nvidiafb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
+{
+ struct nvidia_par *par = info->par;
+ u32 color;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+
+ if (par->lockup) {
+ cfb_fillrect(info, rect);
+ return;
+ }
+
+ if (info->var.bits_per_pixel == 8)
+ color = rect->color;
+ else
+ color = ((u32 *) info->pseudo_palette)[rect->color];
+
+ if (rect->rop != ROP_COPY)
+ NVSetRopSolid(info, rect->rop, ~0);
+
+ NVDmaStart(info, par, RECT_SOLID_COLOR, 1);
+ NVDmaNext(par, color);
+
+ NVDmaStart(info, par, RECT_SOLID_RECTS(0), 2);
+ NVDmaNext(par, (rect->dx << 16) | rect->dy);
+ NVDmaNext(par, (rect->width << 16) | rect->height);
+
+ NVDmaKickoff(par);
+
+ if (rect->rop != ROP_COPY)
+ NVSetRopSolid(info, ROP_COPY, ~0);
+}
+
+static void nvidiafb_mono_color_expand(struct fb_info *info,
+ const struct fb_image *image)
+{
+ struct nvidia_par *par = info->par;
+ u32 fg, bg, mask = ~(~0 >> (32 - info->var.bits_per_pixel));
+ u32 dsize, width, *data = (u32 *) image->data, tmp;
+ int j, k = 0;
+
+ width = (image->width + 31) & ~31;
+ dsize = (width * image->height) >> 5;
+
+ if (info->var.bits_per_pixel == 8) {
+ fg = image->fg_color | mask;
+ bg = image->bg_color | mask;
+ } else {
+ fg = ((u32 *) info->pseudo_palette)[image->fg_color] | mask;
+ bg = ((u32 *) info->pseudo_palette)[image->bg_color] | mask;
+ }
+
+ NVDmaStart(info, par, RECT_EXPAND_TWO_COLOR_CLIP, 7);
+ NVDmaNext(par, (image->dy << 16) | (image->dx & 0xffff));
+ NVDmaNext(par, ((image->dy + image->height) << 16) |
+ ((image->dx + image->width) & 0xffff));
+ NVDmaNext(par, bg);
+ NVDmaNext(par, fg);
+ NVDmaNext(par, (image->height << 16) | width);
+ NVDmaNext(par, (image->height << 16) | width);
+ NVDmaNext(par, (image->dy << 16) | (image->dx & 0xffff));
+
+ while (dsize >= RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS) {
+ NVDmaStart(info, par, RECT_EXPAND_TWO_COLOR_DATA(0),
+ RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS);
+
+ for (j = RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS; j--;) {
+ tmp = data[k++];
+ reverse_order(&tmp);
+ NVDmaNext(par, tmp);
+ }
+
+ dsize -= RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS;
+ }
+
+ if (dsize) {
+ NVDmaStart(info, par, RECT_EXPAND_TWO_COLOR_DATA(0), dsize);
+
+ for (j = dsize; j--;) {
+ tmp = data[k++];
+ reverse_order(&tmp);
+ NVDmaNext(par, tmp);
+ }
+ }
+
+ NVDmaKickoff(par);
+}
+
+void nvidiafb_imageblit(struct fb_info *info, const struct fb_image *image)
+{
+ struct nvidia_par *par = info->par;
+
+ if (info->state != FBINFO_STATE_RUNNING)
+ return;
+
+ if (image->depth == 1 && !par->lockup)
+ nvidiafb_mono_color_expand(info, image);
+ else
+ cfb_imageblit(info, image);
+}
diff --git a/drivers/video/fbdev/nvidia/nv_backlight.c b/drivers/video/fbdev/nvidia/nv_backlight.c
new file mode 100644
index 000000000..7edd47ab1
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_backlight.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Backlight code for nVidia based graphic cards
+ *
+ * Copyright 2004 Antonino Daplas <adaplas@pol.net>
+ * Copyright (c) 2006 Michael Hanselmann <linux-kernel@hansmi.ch>
+ */
+
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+#include <asm/backlight.h>
+#endif
+
+#include "nv_local.h"
+#include "nv_type.h"
+#include "nv_proto.h"
+
+/* We do not have any information about which values are allowed, thus
+ * we used safe values.
+ */
+#define MIN_LEVEL 0x158
+#define MAX_LEVEL 0x534
+#define LEVEL_STEP ((MAX_LEVEL - MIN_LEVEL) / FB_BACKLIGHT_MAX)
+
+static int nvidia_bl_get_level_brightness(struct nvidia_par *par,
+ int level)
+{
+ struct fb_info *info = pci_get_drvdata(par->pci_dev);
+ int nlevel;
+
+ /* Get and convert the value */
+ /* No locking of bl_curve since we read a single value */
+ nlevel = MIN_LEVEL + info->bl_curve[level] * LEVEL_STEP;
+
+ if (nlevel < 0)
+ nlevel = 0;
+ else if (nlevel < MIN_LEVEL)
+ nlevel = MIN_LEVEL;
+ else if (nlevel > MAX_LEVEL)
+ nlevel = MAX_LEVEL;
+
+ return nlevel;
+}
+
+static int nvidia_bl_update_status(struct backlight_device *bd)
+{
+ struct nvidia_par *par = bl_get_data(bd);
+ u32 tmp_pcrt, tmp_pmc, fpcontrol;
+ int level = backlight_get_brightness(bd);
+
+ if (!par->FlatPanel)
+ return 0;
+
+ tmp_pmc = NV_RD32(par->PMC, 0x10F0) & 0x0000FFFF;
+ tmp_pcrt = NV_RD32(par->PCRTC0, 0x081C) & 0xFFFFFFFC;
+ fpcontrol = NV_RD32(par->PRAMDAC, 0x0848) & 0xCFFFFFCC;
+
+ if (level > 0) {
+ tmp_pcrt |= 0x1;
+ tmp_pmc |= (1 << 31); /* backlight bit */
+ tmp_pmc |= nvidia_bl_get_level_brightness(par, level) << 16;
+ fpcontrol |= par->fpSyncs;
+ } else
+ fpcontrol |= 0x20000022;
+
+ NV_WR32(par->PCRTC0, 0x081C, tmp_pcrt);
+ NV_WR32(par->PMC, 0x10F0, tmp_pmc);
+ NV_WR32(par->PRAMDAC, 0x848, fpcontrol);
+
+ return 0;
+}
+
+static const struct backlight_ops nvidia_bl_ops = {
+ .update_status = nvidia_bl_update_status,
+};
+
+void nvidia_bl_init(struct nvidia_par *par)
+{
+ struct backlight_properties props;
+ struct fb_info *info = pci_get_drvdata(par->pci_dev);
+ struct backlight_device *bd;
+ char name[12];
+
+ if (!par->FlatPanel)
+ return;
+
+#ifdef CONFIG_PMAC_BACKLIGHT
+ if (!machine_is(powermac) ||
+ !pmac_has_backlight_type("mnca"))
+ return;
+#endif
+
+ snprintf(name, sizeof(name), "nvidiabl%d", info->node);
+
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.type = BACKLIGHT_RAW;
+ props.max_brightness = FB_BACKLIGHT_LEVELS - 1;
+ bd = backlight_device_register(name, info->device, par, &nvidia_bl_ops,
+ &props);
+ if (IS_ERR(bd)) {
+ info->bl_dev = NULL;
+ printk(KERN_WARNING "nvidia: Backlight registration failed\n");
+ goto error;
+ }
+
+ info->bl_dev = bd;
+ fb_bl_default_curve(info, 0,
+ 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL,
+ 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL);
+
+ bd->props.brightness = bd->props.max_brightness;
+ bd->props.power = BACKLIGHT_POWER_ON;
+ backlight_update_status(bd);
+
+ printk("nvidia: Backlight initialized (%s)\n", name);
+
+error:
+ return;
+}
+
+void nvidia_bl_exit(struct nvidia_par *par)
+{
+ struct fb_info *info = pci_get_drvdata(par->pci_dev);
+ struct backlight_device *bd = info->bl_dev;
+
+ backlight_device_unregister(bd);
+ printk("nvidia: Backlight unloaded\n");
+}
diff --git a/drivers/video/fbdev/nvidia/nv_dma.h b/drivers/video/fbdev/nvidia/nv_dma.h
new file mode 100644
index 000000000..a7ed1c0ac
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_dma.h
@@ -0,0 +1,188 @@
+
+ /***************************************************************************\
+|* *|
+|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
+|* international laws. Users and possessors of this source code are *|
+|* hereby granted a nonexclusive, royalty-free copyright license to *|
+|* use this code in individual and commercial software. *|
+|* *|
+|* Any use of this source code must include, in the user documenta- *|
+|* tion and internal comments to the code, notices to the end user *|
+|* as follows: *|
+|* *|
+|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
+|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
+|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
+|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
+|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
+|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
+|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
+|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
+|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
+|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
+|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
+|* *|
+|* U.S. Government End Users. This source code is a "commercial *|
+|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
+|* consisting of "commercial computer software" and "commercial *|
+|* computer software documentation," as such terms are used in *|
+|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
+|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
+|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
+|* all U.S. Government End Users acquire the source code with only *|
+|* those rights set forth herein. *|
+|* *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#define SURFACE_FORMAT 0x00000300
+#define SURFACE_FORMAT_DEPTH8 0x00000001
+#define SURFACE_FORMAT_DEPTH15 0x00000002
+#define SURFACE_FORMAT_DEPTH16 0x00000004
+#define SURFACE_FORMAT_DEPTH24 0x00000006
+#define SURFACE_PITCH 0x00000304
+#define SURFACE_PITCH_SRC 15:0
+#define SURFACE_PITCH_DST 31:16
+#define SURFACE_OFFSET_SRC 0x00000308
+#define SURFACE_OFFSET_DST 0x0000030C
+
+#define ROP_SET 0x00002300
+
+#define PATTERN_FORMAT 0x00004300
+#define PATTERN_FORMAT_DEPTH8 0x00000003
+#define PATTERN_FORMAT_DEPTH16 0x00000001
+#define PATTERN_FORMAT_DEPTH24 0x00000003
+#define PATTERN_COLOR_0 0x00004310
+#define PATTERN_COLOR_1 0x00004314
+#define PATTERN_PATTERN_0 0x00004318
+#define PATTERN_PATTERN_1 0x0000431C
+
+#define CLIP_POINT 0x00006300
+#define CLIP_POINT_X 15:0
+#define CLIP_POINT_Y 31:16
+#define CLIP_SIZE 0x00006304
+#define CLIP_SIZE_WIDTH 15:0
+#define CLIP_SIZE_HEIGHT 31:16
+
+#define LINE_FORMAT 0x00008300
+#define LINE_FORMAT_DEPTH8 0x00000003
+#define LINE_FORMAT_DEPTH16 0x00000001
+#define LINE_FORMAT_DEPTH24 0x00000003
+#define LINE_COLOR 0x00008304
+#define LINE_MAX_LINES 16
+#define LINE_LINES(i) 0x00008400\
+ +(i)*8
+#define LINE_LINES_POINT0_X 15:0
+#define LINE_LINES_POINT0_Y 31:16
+#define LINE_LINES_POINT1_X 47:32
+#define LINE_LINES_POINT1_Y 63:48
+
+#define BLIT_POINT_SRC 0x0000A300
+#define BLIT_POINT_SRC_X 15:0
+#define BLIT_POINT_SRC_Y 31:16
+#define BLIT_POINT_DST 0x0000A304
+#define BLIT_POINT_DST_X 15:0
+#define BLIT_POINT_DST_Y 31:16
+#define BLIT_SIZE 0x0000A308
+#define BLIT_SIZE_WIDTH 15:0
+#define BLIT_SIZE_HEIGHT 31:16
+
+#define RECT_FORMAT 0x0000C300
+#define RECT_FORMAT_DEPTH8 0x00000003
+#define RECT_FORMAT_DEPTH16 0x00000001
+#define RECT_FORMAT_DEPTH24 0x00000003
+#define RECT_SOLID_COLOR 0x0000C3FC
+#define RECT_SOLID_RECTS_MAX_RECTS 32
+#define RECT_SOLID_RECTS(i) 0x0000C400\
+ +(i)*8
+#define RECT_SOLID_RECTS_Y 15:0
+#define RECT_SOLID_RECTS_X 31:16
+#define RECT_SOLID_RECTS_HEIGHT 47:32
+#define RECT_SOLID_RECTS_WIDTH 63:48
+
+#define RECT_EXPAND_ONE_COLOR_CLIP 0x0000C7EC
+#define RECT_EXPAND_ONE_COLOR_CLIP_POINT0_X 15:0
+#define RECT_EXPAND_ONE_COLOR_CLIP_POINT0_Y 31:16
+#define RECT_EXPAND_ONE_COLOR_CLIP_POINT1_X 47:32
+#define RECT_EXPAND_ONE_COLOR_CLIP_POINT1_Y 63:48
+#define RECT_EXPAND_ONE_COLOR_COLOR 0x0000C7F4
+#define RECT_EXPAND_ONE_COLOR_SIZE 0x0000C7F8
+#define RECT_EXPAND_ONE_COLOR_SIZE_WIDTH 15:0
+#define RECT_EXPAND_ONE_COLOR_SIZE_HEIGHT 31:16
+#define RECT_EXPAND_ONE_COLOR_POINT 0x0000C7FC
+#define RECT_EXPAND_ONE_COLOR_POINT_X 15:0
+#define RECT_EXPAND_ONE_COLOR_POINT_Y 31:16
+#define RECT_EXPAND_ONE_COLOR_DATA_MAX_DWORDS 128
+#define RECT_EXPAND_ONE_COLOR_DATA(i) 0x0000C800\
+ +(i)*4
+
+#define RECT_EXPAND_TWO_COLOR_CLIP 0x0000CBE4
+#define RECT_EXPAND_TWO_COLOR_CLIP_POINT0_X 15:0
+#define RECT_EXPAND_TWO_COLOR_CLIP_POINT0_Y 31:16
+#define RECT_EXPAND_TWO_COLOR_CLIP_POINT1_X 47:32
+#define RECT_EXPAND_TWO_COLOR_CLIP_POINT1_Y 63:48
+#define RECT_EXPAND_TWO_COLOR_COLOR_0 0x0000CBEC
+#define RECT_EXPAND_TWO_COLOR_COLOR_1 0x0000CBF0
+#define RECT_EXPAND_TWO_COLOR_SIZE_IN 0x0000CBF4
+#define RECT_EXPAND_TWO_COLOR_SIZE_IN_WIDTH 15:0
+#define RECT_EXPAND_TWO_COLOR_SIZE_IN_HEIGHT 31:16
+#define RECT_EXPAND_TWO_COLOR_SIZE_OUT 0x0000CBF8
+#define RECT_EXPAND_TWO_COLOR_SIZE_OUT_WIDTH 15:0
+#define RECT_EXPAND_TWO_COLOR_SIZE_OUT_HEIGHT 31:16
+#define RECT_EXPAND_TWO_COLOR_POINT 0x0000CBFC
+#define RECT_EXPAND_TWO_COLOR_POINT_X 15:0
+#define RECT_EXPAND_TWO_COLOR_POINT_Y 31:16
+#define RECT_EXPAND_TWO_COLOR_DATA_MAX_DWORDS 128
+#define RECT_EXPAND_TWO_COLOR_DATA(i) 0x0000CC00\
+ +(i)*4
+
+#define STRETCH_BLIT_FORMAT 0x0000E300
+#define STRETCH_BLIT_FORMAT_DEPTH8 0x00000004
+#define STRETCH_BLIT_FORMAT_DEPTH16 0x00000007
+#define STRETCH_BLIT_FORMAT_DEPTH24 0x00000004
+#define STRETCH_BLIT_FORMAT_X8R8G8B8 0x00000004
+#define STRETCH_BLIT_FORMAT_YUYV 0x00000005
+#define STRETCH_BLIT_FORMAT_UYVY 0x00000006
+#define STRETCH_BLIT_CLIP_POINT 0x0000E308
+#define STRETCH_BLIT_CLIP_POINT_X 15:0
+#define STRETCH_BLIT_CLIP_POINT_Y 31:16
+#define STRETCH_BLIT_CLIP_POINT 0x0000E308
+#define STRETCH_BLIT_CLIP_SIZE 0x0000E30C
+#define STRETCH_BLIT_CLIP_SIZE_WIDTH 15:0
+#define STRETCH_BLIT_CLIP_SIZE_HEIGHT 31:16
+#define STRETCH_BLIT_DST_POINT 0x0000E310
+#define STRETCH_BLIT_DST_POINT_X 15:0
+#define STRETCH_BLIT_DST_POINT_Y 31:16
+#define STRETCH_BLIT_DST_SIZE 0x0000E314
+#define STRETCH_BLIT_DST_SIZE_WIDTH 15:0
+#define STRETCH_BLIT_DST_SIZE_HEIGHT 31:16
+#define STRETCH_BLIT_DU_DX 0x0000E318
+#define STRETCH_BLIT_DV_DY 0x0000E31C
+#define STRETCH_BLIT_SRC_SIZE 0x0000E400
+#define STRETCH_BLIT_SRC_SIZE_WIDTH 15:0
+#define STRETCH_BLIT_SRC_SIZE_HEIGHT 31:16
+#define STRETCH_BLIT_SRC_FORMAT 0x0000E404
+#define STRETCH_BLIT_SRC_FORMAT_PITCH 15:0
+#define STRETCH_BLIT_SRC_FORMAT_ORIGIN 23:16
+#define STRETCH_BLIT_SRC_FORMAT_ORIGIN_CENTER 0x00000001
+#define STRETCH_BLIT_SRC_FORMAT_ORIGIN_CORNER 0x00000002
+#define STRETCH_BLIT_SRC_FORMAT_FILTER 31:24
+#define STRETCH_BLIT_SRC_FORMAT_FILTER_POINT_SAMPLE 0x00000000
+#define STRETCH_BLIT_SRC_FORMAT_FILTER_BILINEAR 0x00000001
+#define STRETCH_BLIT_SRC_OFFSET 0x0000E408
+#define STRETCH_BLIT_SRC_POINT 0x0000E40C
+#define STRETCH_BLIT_SRC_POINT_U 15:0
+#define STRETCH_BLIT_SRC_POINT_V 31:16
diff --git a/drivers/video/fbdev/nvidia/nv_hw.c b/drivers/video/fbdev/nvidia/nv_hw.c
new file mode 100644
index 000000000..75afaa07e
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_hw.c
@@ -0,0 +1,1688 @@
+ /***************************************************************************\
+|* *|
+|* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
+|* international laws. Users and possessors of this source code are *|
+|* hereby granted a nonexclusive, royalty-free copyright license to *|
+|* use this code in individual and commercial software. *|
+|* *|
+|* Any use of this source code must include, in the user documenta- *|
+|* tion and internal comments to the code, notices to the end user *|
+|* as follows: *|
+|* *|
+|* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
+|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
+|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
+|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
+|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
+|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
+|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
+|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
+|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
+|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
+|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
+|* *|
+|* U.S. Government End Users. This source code is a "commercial *|
+|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
+|* consisting of "commercial computer software" and "commercial *|
+|* computer software documentation," as such terms are used in *|
+|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
+|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
+|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
+|* all U.S. Government End Users acquire the source code with only *|
+|* those rights set forth herein. *|
+|* *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nv_hw.c,v 1.4 2003/11/03 05:11:25 tsi Exp $ */
+
+#include <linux/pci.h>
+#include "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+
+void NVLockUnlock(struct nvidia_par *par, int Lock)
+{
+ u8 cr11;
+
+ VGA_WR08(par->PCIO, 0x3D4, 0x1F);
+ VGA_WR08(par->PCIO, 0x3D5, Lock ? 0x99 : 0x57);
+
+ VGA_WR08(par->PCIO, 0x3D4, 0x11);
+ cr11 = VGA_RD08(par->PCIO, 0x3D5);
+ if (Lock)
+ cr11 |= 0x80;
+ else
+ cr11 &= ~0x80;
+ VGA_WR08(par->PCIO, 0x3D5, cr11);
+}
+
+int NVShowHideCursor(struct nvidia_par *par, int ShowHide)
+{
+ int cur = par->CurrentState->cursor1;
+
+ par->CurrentState->cursor1 = (par->CurrentState->cursor1 & 0xFE) |
+ (ShowHide & 0x01);
+ VGA_WR08(par->PCIO, 0x3D4, 0x31);
+ VGA_WR08(par->PCIO, 0x3D5, par->CurrentState->cursor1);
+
+ if (par->Architecture == NV_ARCH_40)
+ NV_WR32(par->PRAMDAC, 0x0300, NV_RD32(par->PRAMDAC, 0x0300));
+
+ return (cur & 0x01);
+}
+
+/****************************************************************************\
+* *
+* The video arbitration routines calculate some "magic" numbers. Fixes *
+* the snow seen when accessing the framebuffer without it. *
+* It just works (I hope). *
+* *
+\****************************************************************************/
+
+typedef struct {
+ int graphics_lwm;
+ int video_lwm;
+ int graphics_burst_size;
+ int video_burst_size;
+ int valid;
+} nv4_fifo_info;
+
+typedef struct {
+ int pclk_khz;
+ int mclk_khz;
+ int nvclk_khz;
+ char mem_page_miss;
+ char mem_latency;
+ int memory_width;
+ char enable_video;
+ char gr_during_vid;
+ char pix_bpp;
+ char mem_aligned;
+ char enable_mp;
+} nv4_sim_state;
+
+typedef struct {
+ int graphics_lwm;
+ int video_lwm;
+ int graphics_burst_size;
+ int video_burst_size;
+ int valid;
+} nv10_fifo_info;
+
+typedef struct {
+ int pclk_khz;
+ int mclk_khz;
+ int nvclk_khz;
+ char mem_page_miss;
+ char mem_latency;
+ u32 memory_type;
+ int memory_width;
+ char enable_video;
+ char gr_during_vid;
+ char pix_bpp;
+ char mem_aligned;
+ char enable_mp;
+} nv10_sim_state;
+
+static void nvGetClocks(struct nvidia_par *par, unsigned int *MClk,
+ unsigned int *NVClk)
+{
+ unsigned int pll, N, M, MB, NB, P;
+
+ if (par->Architecture >= NV_ARCH_40) {
+ pll = NV_RD32(par->PMC, 0x4020);
+ P = (pll >> 16) & 0x07;
+ pll = NV_RD32(par->PMC, 0x4024);
+ M = pll & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ if (((par->Chipset & 0xfff0) == 0x0290) ||
+ ((par->Chipset & 0xfff0) == 0x0390)) {
+ MB = 1;
+ NB = 1;
+ } else {
+ MB = (pll >> 16) & 0xFF;
+ NB = (pll >> 24) & 0xFF;
+ }
+ *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+
+ pll = NV_RD32(par->PMC, 0x4000);
+ P = (pll >> 16) & 0x07;
+ pll = NV_RD32(par->PMC, 0x4004);
+ M = pll & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ MB = (pll >> 16) & 0xFF;
+ NB = (pll >> 24) & 0xFF;
+
+ *NVClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+ } else if (par->twoStagePLL) {
+ pll = NV_RD32(par->PRAMDAC0, 0x0504);
+ M = pll & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x0F;
+ pll = NV_RD32(par->PRAMDAC0, 0x0574);
+ if (pll & 0x80000000) {
+ MB = pll & 0xFF;
+ NB = (pll >> 8) & 0xFF;
+ } else {
+ MB = 1;
+ NB = 1;
+ }
+ *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+
+ pll = NV_RD32(par->PRAMDAC0, 0x0500);
+ M = pll & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x0F;
+ pll = NV_RD32(par->PRAMDAC0, 0x0570);
+ if (pll & 0x80000000) {
+ MB = pll & 0xFF;
+ NB = (pll >> 8) & 0xFF;
+ } else {
+ MB = 1;
+ NB = 1;
+ }
+ *NVClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+ } else
+ if (((par->Chipset & 0x0ff0) == 0x0300) ||
+ ((par->Chipset & 0x0ff0) == 0x0330)) {
+ pll = NV_RD32(par->PRAMDAC0, 0x0504);
+ M = pll & 0x0F;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x07;
+ if (pll & 0x00000080) {
+ MB = (pll >> 4) & 0x07;
+ NB = (pll >> 19) & 0x1f;
+ } else {
+ MB = 1;
+ NB = 1;
+ }
+ *MClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+
+ pll = NV_RD32(par->PRAMDAC0, 0x0500);
+ M = pll & 0x0F;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x07;
+ if (pll & 0x00000080) {
+ MB = (pll >> 4) & 0x07;
+ NB = (pll >> 19) & 0x1f;
+ } else {
+ MB = 1;
+ NB = 1;
+ }
+ *NVClk = ((N * NB * par->CrystalFreqKHz) / (M * MB)) >> P;
+ } else {
+ pll = NV_RD32(par->PRAMDAC0, 0x0504);
+ M = pll & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x0F;
+ *MClk = (N * par->CrystalFreqKHz / M) >> P;
+
+ pll = NV_RD32(par->PRAMDAC0, 0x0500);
+ M = pll & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x0F;
+ *NVClk = (N * par->CrystalFreqKHz / M) >> P;
+ }
+}
+
+static void nv4CalcArbitration(nv4_fifo_info * fifo, nv4_sim_state * arb)
+{
+ int data, pagemiss, cas, width, video_enable, bpp;
+ int nvclks, mclks, pclks, vpagemiss, crtpagemiss, vbs;
+ int found, mclk_extra, mclk_loop, cbs, m1, p1;
+ int mclk_freq, pclk_freq, nvclk_freq, mp_enable;
+ int us_m, us_n, us_p, video_drain_rate, crtc_drain_rate;
+ int vpm_us, us_video, vlwm, video_fill_us, cpm_us, us_crt, clwm;
+
+ fifo->valid = 1;
+ pclk_freq = arb->pclk_khz;
+ mclk_freq = arb->mclk_khz;
+ nvclk_freq = arb->nvclk_khz;
+ pagemiss = arb->mem_page_miss;
+ cas = arb->mem_latency;
+ width = arb->memory_width >> 6;
+ video_enable = arb->enable_video;
+ bpp = arb->pix_bpp;
+ mp_enable = arb->enable_mp;
+ clwm = 0;
+ vlwm = 0;
+ cbs = 128;
+ pclks = 2;
+ nvclks = 2;
+ nvclks += 2;
+ nvclks += 1;
+ mclks = 5;
+ mclks += 3;
+ mclks += 1;
+ mclks += cas;
+ mclks += 1;
+ mclks += 1;
+ mclks += 1;
+ mclks += 1;
+ mclk_extra = 3;
+ nvclks += 2;
+ nvclks += 1;
+ nvclks += 1;
+ nvclks += 1;
+ if (mp_enable)
+ mclks += 4;
+ nvclks += 0;
+ pclks += 0;
+ found = 0;
+ vbs = 0;
+ while (found != 1) {
+ fifo->valid = 1;
+ found = 1;
+ mclk_loop = mclks + mclk_extra;
+ us_m = mclk_loop * 1000 * 1000 / mclk_freq;
+ us_n = nvclks * 1000 * 1000 / nvclk_freq;
+ us_p = nvclks * 1000 * 1000 / pclk_freq;
+ if (video_enable) {
+ video_drain_rate = pclk_freq * 2;
+ crtc_drain_rate = pclk_freq * bpp / 8;
+ vpagemiss = 2;
+ vpagemiss += 1;
+ crtpagemiss = 2;
+ vpm_us =
+ (vpagemiss * pagemiss) * 1000 * 1000 / mclk_freq;
+ if (nvclk_freq * 2 > mclk_freq * width)
+ video_fill_us =
+ cbs * 1000 * 1000 / 16 / nvclk_freq;
+ else
+ video_fill_us =
+ cbs * 1000 * 1000 / (8 * width) /
+ mclk_freq;
+ us_video = vpm_us + us_m + us_n + us_p + video_fill_us;
+ vlwm = us_video * video_drain_rate / (1000 * 1000);
+ vlwm++;
+ vbs = 128;
+ if (vlwm > 128)
+ vbs = 64;
+ if (vlwm > (256 - 64))
+ vbs = 32;
+ if (nvclk_freq * 2 > mclk_freq * width)
+ video_fill_us =
+ vbs * 1000 * 1000 / 16 / nvclk_freq;
+ else
+ video_fill_us =
+ vbs * 1000 * 1000 / (8 * width) /
+ mclk_freq;
+ cpm_us =
+ crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+ us_crt =
+ us_video + video_fill_us + cpm_us + us_m + us_n +
+ us_p;
+ clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+ clwm++;
+ } else {
+ crtc_drain_rate = pclk_freq * bpp / 8;
+ crtpagemiss = 2;
+ crtpagemiss += 1;
+ cpm_us =
+ crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+ us_crt = cpm_us + us_m + us_n + us_p;
+ clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+ clwm++;
+ }
+ m1 = clwm + cbs - 512;
+ p1 = m1 * pclk_freq / mclk_freq;
+ p1 = p1 * bpp / 8;
+ if ((p1 < m1) && (m1 > 0)) {
+ fifo->valid = 0;
+ found = 0;
+ if (mclk_extra == 0)
+ found = 1;
+ mclk_extra--;
+ } else if (video_enable) {
+ if ((clwm > 511) || (vlwm > 255)) {
+ fifo->valid = 0;
+ found = 0;
+ if (mclk_extra == 0)
+ found = 1;
+ mclk_extra--;
+ }
+ } else {
+ if (clwm > 519) {
+ fifo->valid = 0;
+ found = 0;
+ if (mclk_extra == 0)
+ found = 1;
+ mclk_extra--;
+ }
+ }
+ if (clwm < 384)
+ clwm = 384;
+ if (vlwm < 128)
+ vlwm = 128;
+ data = (int)(clwm);
+ fifo->graphics_lwm = data;
+ fifo->graphics_burst_size = 128;
+ data = (int)((vlwm + 15));
+ fifo->video_lwm = data;
+ fifo->video_burst_size = vbs;
+ }
+}
+
+static void nv4UpdateArbitrationSettings(unsigned VClk,
+ unsigned pixelDepth,
+ unsigned *burst,
+ unsigned *lwm, struct nvidia_par *par)
+{
+ nv4_fifo_info fifo_data;
+ nv4_sim_state sim_data;
+ unsigned int MClk, NVClk, cfg1;
+
+ nvGetClocks(par, &MClk, &NVClk);
+
+ cfg1 = NV_RD32(par->PFB, 0x00000204);
+ sim_data.pix_bpp = (char)pixelDepth;
+ sim_data.enable_video = 0;
+ sim_data.enable_mp = 0;
+ sim_data.memory_width = (NV_RD32(par->PEXTDEV, 0x0000) & 0x10) ?
+ 128 : 64;
+ sim_data.mem_latency = (char)cfg1 & 0x0F;
+ sim_data.mem_aligned = 1;
+ sim_data.mem_page_miss =
+ (char)(((cfg1 >> 4) & 0x0F) + ((cfg1 >> 31) & 0x01));
+ sim_data.gr_during_vid = 0;
+ sim_data.pclk_khz = VClk;
+ sim_data.mclk_khz = MClk;
+ sim_data.nvclk_khz = NVClk;
+ nv4CalcArbitration(&fifo_data, &sim_data);
+ if (fifo_data.valid) {
+ int b = fifo_data.graphics_burst_size >> 4;
+ *burst = 0;
+ while (b >>= 1)
+ (*burst)++;
+ *lwm = fifo_data.graphics_lwm >> 3;
+ }
+}
+
+static void nv10CalcArbitration(nv10_fifo_info * fifo, nv10_sim_state * arb)
+{
+ int data, pagemiss, width, video_enable, bpp;
+ int nvclks, mclks, pclks, vpagemiss, crtpagemiss;
+ int nvclk_fill;
+ int found, mclk_extra, mclk_loop, cbs, m1;
+ int mclk_freq, pclk_freq, nvclk_freq, mp_enable;
+ int us_m, us_m_min, us_n, us_p, crtc_drain_rate;
+ int vus_m;
+ int vpm_us, us_video, cpm_us, us_crt, clwm;
+ int clwm_rnd_down;
+ int m2us, us_pipe_min, p1clk, p2;
+ int min_mclk_extra;
+ int us_min_mclk_extra;
+
+ fifo->valid = 1;
+ pclk_freq = arb->pclk_khz; /* freq in KHz */
+ mclk_freq = arb->mclk_khz;
+ nvclk_freq = arb->nvclk_khz;
+ pagemiss = arb->mem_page_miss;
+ width = arb->memory_width / 64;
+ video_enable = arb->enable_video;
+ bpp = arb->pix_bpp;
+ mp_enable = arb->enable_mp;
+ clwm = 0;
+
+ cbs = 512;
+
+ pclks = 4; /* lwm detect. */
+
+ nvclks = 3; /* lwm -> sync. */
+ nvclks += 2; /* fbi bus cycles (1 req + 1 busy) */
+ /* 2 edge sync. may be very close to edge so just put one. */
+ mclks = 1;
+ mclks += 1; /* arb_hp_req */
+ mclks += 5; /* ap_hp_req tiling pipeline */
+
+ mclks += 2; /* tc_req latency fifo */
+ mclks += 2; /* fb_cas_n_ memory request to fbio block */
+ mclks += 7; /* sm_d_rdv data returned from fbio block */
+
+ /* fb.rd.d.Put_gc need to accumulate 256 bits for read */
+ if (arb->memory_type == 0)
+ if (arb->memory_width == 64) /* 64 bit bus */
+ mclks += 4;
+ else
+ mclks += 2;
+ else if (arb->memory_width == 64) /* 64 bit bus */
+ mclks += 2;
+ else
+ mclks += 1;
+
+ if ((!video_enable) && (arb->memory_width == 128)) {
+ mclk_extra = (bpp == 32) ? 31 : 42; /* Margin of error */
+ min_mclk_extra = 17;
+ } else {
+ mclk_extra = (bpp == 32) ? 8 : 4; /* Margin of error */
+ /* mclk_extra = 4; *//* Margin of error */
+ min_mclk_extra = 18;
+ }
+
+ /* 2 edge sync. may be very close to edge so just put one. */
+ nvclks += 1;
+ nvclks += 1; /* fbi_d_rdv_n */
+ nvclks += 1; /* Fbi_d_rdata */
+ nvclks += 1; /* crtfifo load */
+
+ if (mp_enable)
+ mclks += 4; /* Mp can get in with a burst of 8. */
+ /* Extra clocks determined by heuristics */
+
+ nvclks += 0;
+ pclks += 0;
+ found = 0;
+ while (found != 1) {
+ fifo->valid = 1;
+ found = 1;
+ mclk_loop = mclks + mclk_extra;
+ /* Mclk latency in us */
+ us_m = mclk_loop * 1000 * 1000 / mclk_freq;
+ /* Minimum Mclk latency in us */
+ us_m_min = mclks * 1000 * 1000 / mclk_freq;
+ us_min_mclk_extra = min_mclk_extra * 1000 * 1000 / mclk_freq;
+ /* nvclk latency in us */
+ us_n = nvclks * 1000 * 1000 / nvclk_freq;
+ /* nvclk latency in us */
+ us_p = pclks * 1000 * 1000 / pclk_freq;
+ us_pipe_min = us_m_min + us_n + us_p;
+
+ /* Mclk latency in us */
+ vus_m = mclk_loop * 1000 * 1000 / mclk_freq;
+
+ if (video_enable) {
+ crtc_drain_rate = pclk_freq * bpp / 8; /* MB/s */
+
+ vpagemiss = 1; /* self generating page miss */
+ vpagemiss += 1; /* One higher priority before */
+
+ crtpagemiss = 2; /* self generating page miss */
+ if (mp_enable)
+ crtpagemiss += 1; /* if MA0 conflict */
+
+ vpm_us =
+ (vpagemiss * pagemiss) * 1000 * 1000 / mclk_freq;
+
+ /* Video has separate read return path */
+ us_video = vpm_us + vus_m;
+
+ cpm_us =
+ crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+ /* Wait for video */
+ us_crt = us_video
+ + cpm_us /* CRT Page miss */
+ + us_m + us_n + us_p /* other latency */
+ ;
+
+ clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+ /* fixed point <= float_point - 1. Fixes that */
+ clwm++;
+ } else {
+ /* bpp * pclk/8 */
+ crtc_drain_rate = pclk_freq * bpp / 8;
+
+ crtpagemiss = 1; /* self generating page miss */
+ crtpagemiss += 1; /* MA0 page miss */
+ if (mp_enable)
+ crtpagemiss += 1; /* if MA0 conflict */
+ cpm_us =
+ crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
+ us_crt = cpm_us + us_m + us_n + us_p;
+ clwm = us_crt * crtc_drain_rate / (1000 * 1000);
+ /* fixed point <= float_point - 1. Fixes that */
+ clwm++;
+
+ /* Finally, a heuristic check when width == 64 bits */
+ if (width == 1) {
+ nvclk_fill = nvclk_freq * 8;
+ if (crtc_drain_rate * 100 >= nvclk_fill * 102)
+ /*Large number to fail */
+ clwm = 0xfff;
+
+ else if (crtc_drain_rate * 100 >=
+ nvclk_fill * 98) {
+ clwm = 1024;
+ cbs = 512;
+ }
+ }
+ }
+
+ /*
+ Overfill check:
+ */
+
+ clwm_rnd_down = ((int)clwm / 8) * 8;
+ if (clwm_rnd_down < clwm)
+ clwm += 8;
+
+ m1 = clwm + cbs - 1024; /* Amount of overfill */
+ m2us = us_pipe_min + us_min_mclk_extra;
+
+ /* pclk cycles to drain */
+ p1clk = m2us * pclk_freq / (1000 * 1000);
+ p2 = p1clk * bpp / 8; /* bytes drained. */
+
+ if ((p2 < m1) && (m1 > 0)) {
+ fifo->valid = 0;
+ found = 0;
+ if (min_mclk_extra == 0) {
+ if (cbs <= 32) {
+ /* Can't adjust anymore! */
+ found = 1;
+ } else {
+ /* reduce the burst size */
+ cbs = cbs / 2;
+ }
+ } else {
+ min_mclk_extra--;
+ }
+ } else {
+ if (clwm > 1023) { /* Have some margin */
+ fifo->valid = 0;
+ found = 0;
+ if (min_mclk_extra == 0)
+ /* Can't adjust anymore! */
+ found = 1;
+ else
+ min_mclk_extra--;
+ }
+ }
+
+ if (clwm < (1024 - cbs + 8))
+ clwm = 1024 - cbs + 8;
+ data = (int)(clwm);
+ /* printf("CRT LWM: %f bytes, prog: 0x%x, bs: 256\n",
+ clwm, data ); */
+ fifo->graphics_lwm = data;
+ fifo->graphics_burst_size = cbs;
+
+ fifo->video_lwm = 1024;
+ fifo->video_burst_size = 512;
+ }
+}
+
+static void nv10UpdateArbitrationSettings(unsigned VClk,
+ unsigned pixelDepth,
+ unsigned *burst,
+ unsigned *lwm,
+ struct nvidia_par *par)
+{
+ nv10_fifo_info fifo_data;
+ nv10_sim_state sim_data;
+ unsigned int MClk, NVClk, cfg1;
+
+ nvGetClocks(par, &MClk, &NVClk);
+
+ cfg1 = NV_RD32(par->PFB, 0x0204);
+ sim_data.pix_bpp = (char)pixelDepth;
+ sim_data.enable_video = 1;
+ sim_data.enable_mp = 0;
+ sim_data.memory_type = (NV_RD32(par->PFB, 0x0200) & 0x01) ? 1 : 0;
+ sim_data.memory_width = (NV_RD32(par->PEXTDEV, 0x0000) & 0x10) ?
+ 128 : 64;
+ sim_data.mem_latency = (char)cfg1 & 0x0F;
+ sim_data.mem_aligned = 1;
+ sim_data.mem_page_miss =
+ (char)(((cfg1 >> 4) & 0x0F) + ((cfg1 >> 31) & 0x01));
+ sim_data.gr_during_vid = 0;
+ sim_data.pclk_khz = VClk;
+ sim_data.mclk_khz = MClk;
+ sim_data.nvclk_khz = NVClk;
+ nv10CalcArbitration(&fifo_data, &sim_data);
+ if (fifo_data.valid) {
+ int b = fifo_data.graphics_burst_size >> 4;
+ *burst = 0;
+ while (b >>= 1)
+ (*burst)++;
+ *lwm = fifo_data.graphics_lwm >> 3;
+ }
+}
+
+static void nv30UpdateArbitrationSettings (
+ struct nvidia_par *par,
+ unsigned int *burst,
+ unsigned int *lwm
+)
+{
+ unsigned int MClk, NVClk;
+ unsigned int fifo_size, burst_size, graphics_lwm;
+
+ fifo_size = 2048;
+ burst_size = 512;
+ graphics_lwm = fifo_size - burst_size;
+
+ nvGetClocks(par, &MClk, &NVClk);
+
+ *burst = 0;
+ burst_size >>= 5;
+ while(burst_size >>= 1) (*burst)++;
+ *lwm = graphics_lwm >> 3;
+}
+
+static void nForceUpdateArbitrationSettings(unsigned VClk,
+ unsigned pixelDepth,
+ unsigned *burst,
+ unsigned *lwm,
+ struct nvidia_par *par)
+{
+ nv10_fifo_info fifo_data;
+ nv10_sim_state sim_data;
+ unsigned int M, N, P, pll, MClk, NVClk, memctrl;
+ struct pci_dev *dev;
+ int domain = pci_domain_nr(par->pci_dev->bus);
+
+ if ((par->Chipset & 0x0FF0) == 0x01A0) {
+ unsigned int uMClkPostDiv;
+ dev = pci_get_domain_bus_and_slot(domain, 0, 3);
+ pci_read_config_dword(dev, 0x6C, &uMClkPostDiv);
+ uMClkPostDiv = (uMClkPostDiv >> 8) & 0xf;
+
+ if (!uMClkPostDiv)
+ uMClkPostDiv = 4;
+ MClk = 400000 / uMClkPostDiv;
+ } else {
+ dev = pci_get_domain_bus_and_slot(domain, 0, 5);
+ pci_read_config_dword(dev, 0x4c, &MClk);
+ MClk /= 1000;
+ }
+ pci_dev_put(dev);
+ pll = NV_RD32(par->PRAMDAC0, 0x0500);
+ M = (pll >> 0) & 0xFF;
+ N = (pll >> 8) & 0xFF;
+ P = (pll >> 16) & 0x0F;
+ NVClk = (N * par->CrystalFreqKHz / M) >> P;
+ sim_data.pix_bpp = (char)pixelDepth;
+ sim_data.enable_video = 0;
+ sim_data.enable_mp = 0;
+ dev = pci_get_domain_bus_and_slot(domain, 0, 1);
+ pci_read_config_dword(dev, 0x7C, &sim_data.memory_type);
+ pci_dev_put(dev);
+ sim_data.memory_type = (sim_data.memory_type >> 12) & 1;
+ sim_data.memory_width = 64;
+
+ dev = pci_get_domain_bus_and_slot(domain, 0, 3);
+ pci_read_config_dword(dev, 0, &memctrl);
+ pci_dev_put(dev);
+ memctrl >>= 16;
+
+ if ((memctrl == 0x1A9) || (memctrl == 0x1AB) || (memctrl == 0x1ED)) {
+ u32 dimm[3];
+
+ dev = pci_get_domain_bus_and_slot(domain, 0, 2);
+ pci_read_config_dword(dev, 0x40, &dimm[0]);
+ dimm[0] = (dimm[0] >> 8) & 0x4f;
+ pci_read_config_dword(dev, 0x44, &dimm[1]);
+ dimm[1] = (dimm[1] >> 8) & 0x4f;
+ pci_read_config_dword(dev, 0x48, &dimm[2]);
+ dimm[2] = (dimm[2] >> 8) & 0x4f;
+
+ if ((dimm[0] + dimm[1]) != dimm[2]) {
+ printk("nvidiafb: your nForce DIMMs are not arranged "
+ "in optimal banks!\n");
+ }
+ pci_dev_put(dev);
+ }
+
+ sim_data.mem_latency = 3;
+ sim_data.mem_aligned = 1;
+ sim_data.mem_page_miss = 10;
+ sim_data.gr_during_vid = 0;
+ sim_data.pclk_khz = VClk;
+ sim_data.mclk_khz = MClk;
+ sim_data.nvclk_khz = NVClk;
+ nv10CalcArbitration(&fifo_data, &sim_data);
+ if (fifo_data.valid) {
+ int b = fifo_data.graphics_burst_size >> 4;
+ *burst = 0;
+ while (b >>= 1)
+ (*burst)++;
+ *lwm = fifo_data.graphics_lwm >> 3;
+ }
+}
+
+/****************************************************************************\
+* *
+* RIVA Mode State Routines *
+* *
+\****************************************************************************/
+
+/*
+ * Calculate the Video Clock parameters for the PLL.
+ */
+static void CalcVClock(int clockIn,
+ int *clockOut, u32 * pllOut, struct nvidia_par *par)
+{
+ unsigned lowM, highM;
+ unsigned DeltaNew, DeltaOld;
+ unsigned VClk, Freq;
+ unsigned M, N, P;
+
+ DeltaOld = 0xFFFFFFFF;
+
+ VClk = (unsigned)clockIn;
+
+ if (par->CrystalFreqKHz == 13500) {
+ lowM = 7;
+ highM = 13;
+ } else {
+ lowM = 8;
+ highM = 14;
+ }
+
+ for (P = 0; P <= 4; P++) {
+ Freq = VClk << P;
+ if ((Freq >= 128000) && (Freq <= 350000)) {
+ for (M = lowM; M <= highM; M++) {
+ N = ((VClk << P) * M) / par->CrystalFreqKHz;
+ if (N <= 255) {
+ Freq =
+ ((par->CrystalFreqKHz * N) /
+ M) >> P;
+ if (Freq > VClk)
+ DeltaNew = Freq - VClk;
+ else
+ DeltaNew = VClk - Freq;
+ if (DeltaNew < DeltaOld) {
+ *pllOut =
+ (P << 16) | (N << 8) | M;
+ *clockOut = Freq;
+ DeltaOld = DeltaNew;
+ }
+ }
+ }
+ }
+ }
+}
+
+static void CalcVClock2Stage(int clockIn,
+ int *clockOut,
+ u32 * pllOut,
+ u32 * pllBOut, struct nvidia_par *par)
+{
+ unsigned DeltaNew, DeltaOld;
+ unsigned VClk, Freq;
+ unsigned M, N, P;
+
+ DeltaOld = 0xFFFFFFFF;
+
+ *pllBOut = 0x80000401; /* fixed at x4 for now */
+
+ VClk = (unsigned)clockIn;
+
+ for (P = 0; P <= 6; P++) {
+ Freq = VClk << P;
+ if ((Freq >= 400000) && (Freq <= 1000000)) {
+ for (M = 1; M <= 13; M++) {
+ N = ((VClk << P) * M) /
+ (par->CrystalFreqKHz << 2);
+ if ((N >= 5) && (N <= 255)) {
+ Freq =
+ (((par->CrystalFreqKHz << 2) * N) /
+ M) >> P;
+ if (Freq > VClk)
+ DeltaNew = Freq - VClk;
+ else
+ DeltaNew = VClk - Freq;
+ if (DeltaNew < DeltaOld) {
+ *pllOut =
+ (P << 16) | (N << 8) | M;
+ *clockOut = Freq;
+ DeltaOld = DeltaNew;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * Calculate extended mode parameters (SVGA) and save in a
+ * mode state structure.
+ */
+void NVCalcStateExt(struct nvidia_par *par,
+ RIVA_HW_STATE * state,
+ int bpp,
+ int width,
+ int hDisplaySize, int height, int dotClock, int flags)
+{
+ int pixelDepth, VClk = 0;
+ /*
+ * Save mode parameters.
+ */
+ state->bpp = bpp; /* this is not bitsPerPixel, it's 8,15,16,32 */
+ state->width = width;
+ state->height = height;
+ /*
+ * Extended RIVA registers.
+ */
+ pixelDepth = (bpp + 1) / 8;
+ if (par->twoStagePLL)
+ CalcVClock2Stage(dotClock, &VClk, &state->pll, &state->pllB,
+ par);
+ else
+ CalcVClock(dotClock, &VClk, &state->pll, par);
+
+ switch (par->Architecture) {
+ case NV_ARCH_04:
+ nv4UpdateArbitrationSettings(VClk,
+ pixelDepth * 8,
+ &(state->arbitration0),
+ &(state->arbitration1), par);
+ state->cursor0 = 0x00;
+ state->cursor1 = 0xbC;
+ if (flags & FB_VMODE_DOUBLE)
+ state->cursor1 |= 2;
+ state->cursor2 = 0x00000000;
+ state->pllsel = 0x10000700;
+ state->config = 0x00001114;
+ state->general = bpp == 16 ? 0x00101100 : 0x00100100;
+ state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00;
+ break;
+ case NV_ARCH_40:
+ if (!par->FlatPanel)
+ state->control = NV_RD32(par->PRAMDAC0, 0x0580) &
+ 0xeffffeff;
+ fallthrough;
+ case NV_ARCH_10:
+ case NV_ARCH_20:
+ case NV_ARCH_30:
+ default:
+ if ((par->Chipset & 0xfff0) == 0x0240 ||
+ (par->Chipset & 0xfff0) == 0x03d0) {
+ state->arbitration0 = 256;
+ state->arbitration1 = 0x0480;
+ } else if (((par->Chipset & 0xffff) == 0x01A0) ||
+ ((par->Chipset & 0xffff) == 0x01f0)) {
+ nForceUpdateArbitrationSettings(VClk,
+ pixelDepth * 8,
+ &(state->arbitration0),
+ &(state->arbitration1),
+ par);
+ } else if (par->Architecture < NV_ARCH_30) {
+ nv10UpdateArbitrationSettings(VClk,
+ pixelDepth * 8,
+ &(state->arbitration0),
+ &(state->arbitration1),
+ par);
+ } else {
+ nv30UpdateArbitrationSettings(par,
+ &(state->arbitration0),
+ &(state->arbitration1));
+ }
+
+ state->cursor0 = 0x80 | (par->CursorStart >> 17);
+ state->cursor1 = (par->CursorStart >> 11) << 2;
+ state->cursor2 = par->CursorStart >> 24;
+ if (flags & FB_VMODE_DOUBLE)
+ state->cursor1 |= 2;
+ state->pllsel = 0x10000700;
+ state->config = NV_RD32(par->PFB, 0x00000200);
+ state->general = bpp == 16 ? 0x00101100 : 0x00100100;
+ state->repaint1 = hDisplaySize < 1280 ? 0x04 : 0x00;
+ break;
+ }
+
+ if (bpp != 8) /* DirectColor */
+ state->general |= 0x00000030;
+
+ state->repaint0 = (((width / 8) * pixelDepth) & 0x700) >> 3;
+ state->pixel = (pixelDepth > 2) ? 3 : pixelDepth;
+}
+
+void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state)
+{
+ int i, j;
+
+ NV_WR32(par->PMC, 0x0140, 0x00000000);
+ NV_WR32(par->PMC, 0x0200, 0xFFFF00FF);
+ NV_WR32(par->PMC, 0x0200, 0xFFFFFFFF);
+
+ NV_WR32(par->PTIMER, 0x0200 * 4, 0x00000008);
+ NV_WR32(par->PTIMER, 0x0210 * 4, 0x00000003);
+ NV_WR32(par->PTIMER, 0x0140 * 4, 0x00000000);
+ NV_WR32(par->PTIMER, 0x0100 * 4, 0xFFFFFFFF);
+
+ if (par->Architecture == NV_ARCH_04) {
+ if (state)
+ NV_WR32(par->PFB, 0x0200, state->config);
+ } else if ((par->Architecture < NV_ARCH_40) ||
+ (par->Chipset & 0xfff0) == 0x0040) {
+ for (i = 0; i < 8; i++) {
+ NV_WR32(par->PFB, 0x0240 + (i * 0x10), 0);
+ NV_WR32(par->PFB, 0x0244 + (i * 0x10),
+ par->FbMapSize - 1);
+ }
+ } else {
+ int regions = 12;
+
+ if (((par->Chipset & 0xfff0) == 0x0090) ||
+ ((par->Chipset & 0xfff0) == 0x01D0) ||
+ ((par->Chipset & 0xfff0) == 0x0290) ||
+ ((par->Chipset & 0xfff0) == 0x0390) ||
+ ((par->Chipset & 0xfff0) == 0x03D0))
+ regions = 15;
+ for(i = 0; i < regions; i++) {
+ NV_WR32(par->PFB, 0x0600 + (i * 0x10), 0);
+ NV_WR32(par->PFB, 0x0604 + (i * 0x10),
+ par->FbMapSize - 1);
+ }
+ }
+
+ if (par->Architecture >= NV_ARCH_40) {
+ NV_WR32(par->PRAMIN, 0x0000 * 4, 0x80000010);
+ NV_WR32(par->PRAMIN, 0x0001 * 4, 0x00101202);
+ NV_WR32(par->PRAMIN, 0x0002 * 4, 0x80000011);
+ NV_WR32(par->PRAMIN, 0x0003 * 4, 0x00101204);
+ NV_WR32(par->PRAMIN, 0x0004 * 4, 0x80000012);
+ NV_WR32(par->PRAMIN, 0x0005 * 4, 0x00101206);
+ NV_WR32(par->PRAMIN, 0x0006 * 4, 0x80000013);
+ NV_WR32(par->PRAMIN, 0x0007 * 4, 0x00101208);
+ NV_WR32(par->PRAMIN, 0x0008 * 4, 0x80000014);
+ NV_WR32(par->PRAMIN, 0x0009 * 4, 0x0010120A);
+ NV_WR32(par->PRAMIN, 0x000A * 4, 0x80000015);
+ NV_WR32(par->PRAMIN, 0x000B * 4, 0x0010120C);
+ NV_WR32(par->PRAMIN, 0x000C * 4, 0x80000016);
+ NV_WR32(par->PRAMIN, 0x000D * 4, 0x0010120E);
+ NV_WR32(par->PRAMIN, 0x000E * 4, 0x80000017);
+ NV_WR32(par->PRAMIN, 0x000F * 4, 0x00101210);
+ NV_WR32(par->PRAMIN, 0x0800 * 4, 0x00003000);
+ NV_WR32(par->PRAMIN, 0x0801 * 4, par->FbMapSize - 1);
+ NV_WR32(par->PRAMIN, 0x0802 * 4, 0x00000002);
+ NV_WR32(par->PRAMIN, 0x0808 * 4, 0x02080062);
+ NV_WR32(par->PRAMIN, 0x0809 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x080A * 4, 0x00001200);
+ NV_WR32(par->PRAMIN, 0x080B * 4, 0x00001200);
+ NV_WR32(par->PRAMIN, 0x080C * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x080D * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0810 * 4, 0x02080043);
+ NV_WR32(par->PRAMIN, 0x0811 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0812 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0813 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0814 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0815 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0818 * 4, 0x02080044);
+ NV_WR32(par->PRAMIN, 0x0819 * 4, 0x02000000);
+ NV_WR32(par->PRAMIN, 0x081A * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x081B * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x081C * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x081D * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0820 * 4, 0x02080019);
+ NV_WR32(par->PRAMIN, 0x0821 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0822 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0823 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0824 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0825 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0828 * 4, 0x020A005C);
+ NV_WR32(par->PRAMIN, 0x0829 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x082A * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x082B * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x082C * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x082D * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0830 * 4, 0x0208009F);
+ NV_WR32(par->PRAMIN, 0x0831 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0832 * 4, 0x00001200);
+ NV_WR32(par->PRAMIN, 0x0833 * 4, 0x00001200);
+ NV_WR32(par->PRAMIN, 0x0834 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0835 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0838 * 4, 0x0208004A);
+ NV_WR32(par->PRAMIN, 0x0839 * 4, 0x02000000);
+ NV_WR32(par->PRAMIN, 0x083A * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x083B * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x083C * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x083D * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0840 * 4, 0x02080077);
+ NV_WR32(par->PRAMIN, 0x0841 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0842 * 4, 0x00001200);
+ NV_WR32(par->PRAMIN, 0x0843 * 4, 0x00001200);
+ NV_WR32(par->PRAMIN, 0x0844 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0845 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x084C * 4, 0x00003002);
+ NV_WR32(par->PRAMIN, 0x084D * 4, 0x00007FFF);
+ NV_WR32(par->PRAMIN, 0x084E * 4,
+ par->FbUsableSize | 0x00000002);
+
+#ifdef __BIG_ENDIAN
+ NV_WR32(par->PRAMIN, 0x080A * 4,
+ NV_RD32(par->PRAMIN, 0x080A * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x0812 * 4,
+ NV_RD32(par->PRAMIN, 0x0812 * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x081A * 4,
+ NV_RD32(par->PRAMIN, 0x081A * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x0822 * 4,
+ NV_RD32(par->PRAMIN, 0x0822 * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x082A * 4,
+ NV_RD32(par->PRAMIN, 0x082A * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x0832 * 4,
+ NV_RD32(par->PRAMIN, 0x0832 * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x083A * 4,
+ NV_RD32(par->PRAMIN, 0x083A * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x0842 * 4,
+ NV_RD32(par->PRAMIN, 0x0842 * 4) | 0x01000000);
+ NV_WR32(par->PRAMIN, 0x0819 * 4, 0x01000000);
+ NV_WR32(par->PRAMIN, 0x0839 * 4, 0x01000000);
+#endif
+ } else {
+ NV_WR32(par->PRAMIN, 0x0000 * 4, 0x80000010);
+ NV_WR32(par->PRAMIN, 0x0001 * 4, 0x80011201);
+ NV_WR32(par->PRAMIN, 0x0002 * 4, 0x80000011);
+ NV_WR32(par->PRAMIN, 0x0003 * 4, 0x80011202);
+ NV_WR32(par->PRAMIN, 0x0004 * 4, 0x80000012);
+ NV_WR32(par->PRAMIN, 0x0005 * 4, 0x80011203);
+ NV_WR32(par->PRAMIN, 0x0006 * 4, 0x80000013);
+ NV_WR32(par->PRAMIN, 0x0007 * 4, 0x80011204);
+ NV_WR32(par->PRAMIN, 0x0008 * 4, 0x80000014);
+ NV_WR32(par->PRAMIN, 0x0009 * 4, 0x80011205);
+ NV_WR32(par->PRAMIN, 0x000A * 4, 0x80000015);
+ NV_WR32(par->PRAMIN, 0x000B * 4, 0x80011206);
+ NV_WR32(par->PRAMIN, 0x000C * 4, 0x80000016);
+ NV_WR32(par->PRAMIN, 0x000D * 4, 0x80011207);
+ NV_WR32(par->PRAMIN, 0x000E * 4, 0x80000017);
+ NV_WR32(par->PRAMIN, 0x000F * 4, 0x80011208);
+ NV_WR32(par->PRAMIN, 0x0800 * 4, 0x00003000);
+ NV_WR32(par->PRAMIN, 0x0801 * 4, par->FbMapSize - 1);
+ NV_WR32(par->PRAMIN, 0x0802 * 4, 0x00000002);
+ NV_WR32(par->PRAMIN, 0x0803 * 4, 0x00000002);
+ if (par->Architecture >= NV_ARCH_10)
+ NV_WR32(par->PRAMIN, 0x0804 * 4, 0x01008062);
+ else
+ NV_WR32(par->PRAMIN, 0x0804 * 4, 0x01008042);
+ NV_WR32(par->PRAMIN, 0x0805 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0806 * 4, 0x12001200);
+ NV_WR32(par->PRAMIN, 0x0807 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0808 * 4, 0x01008043);
+ NV_WR32(par->PRAMIN, 0x0809 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x080A * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x080B * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x080C * 4, 0x01008044);
+ NV_WR32(par->PRAMIN, 0x080D * 4, 0x00000002);
+ NV_WR32(par->PRAMIN, 0x080E * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x080F * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0810 * 4, 0x01008019);
+ NV_WR32(par->PRAMIN, 0x0811 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0812 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0813 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0814 * 4, 0x0100A05C);
+ NV_WR32(par->PRAMIN, 0x0815 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0816 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0817 * 4, 0x00000000);
+ if (par->WaitVSyncPossible)
+ NV_WR32(par->PRAMIN, 0x0818 * 4, 0x0100809F);
+ else
+ NV_WR32(par->PRAMIN, 0x0818 * 4, 0x0100805F);
+ NV_WR32(par->PRAMIN, 0x0819 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x081A * 4, 0x12001200);
+ NV_WR32(par->PRAMIN, 0x081B * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x081C * 4, 0x0100804A);
+ NV_WR32(par->PRAMIN, 0x081D * 4, 0x00000002);
+ NV_WR32(par->PRAMIN, 0x081E * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x081F * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0820 * 4, 0x01018077);
+ NV_WR32(par->PRAMIN, 0x0821 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0822 * 4, 0x12001200);
+ NV_WR32(par->PRAMIN, 0x0823 * 4, 0x00000000);
+ NV_WR32(par->PRAMIN, 0x0824 * 4, 0x00003002);
+ NV_WR32(par->PRAMIN, 0x0825 * 4, 0x00007FFF);
+ NV_WR32(par->PRAMIN, 0x0826 * 4,
+ par->FbUsableSize | 0x00000002);
+ NV_WR32(par->PRAMIN, 0x0827 * 4, 0x00000002);
+#ifdef __BIG_ENDIAN
+ NV_WR32(par->PRAMIN, 0x0804 * 4,
+ NV_RD32(par->PRAMIN, 0x0804 * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x0808 * 4,
+ NV_RD32(par->PRAMIN, 0x0808 * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x080C * 4,
+ NV_RD32(par->PRAMIN, 0x080C * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x0810 * 4,
+ NV_RD32(par->PRAMIN, 0x0810 * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x0814 * 4,
+ NV_RD32(par->PRAMIN, 0x0814 * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x0818 * 4,
+ NV_RD32(par->PRAMIN, 0x0818 * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x081C * 4,
+ NV_RD32(par->PRAMIN, 0x081C * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x0820 * 4,
+ NV_RD32(par->PRAMIN, 0x0820 * 4) | 0x00080000);
+ NV_WR32(par->PRAMIN, 0x080D * 4, 0x00000001);
+ NV_WR32(par->PRAMIN, 0x081D * 4, 0x00000001);
+#endif
+ }
+ if (par->Architecture < NV_ARCH_10) {
+ if ((par->Chipset & 0x0fff) == 0x0020) {
+ NV_WR32(par->PRAMIN, 0x0824 * 4,
+ NV_RD32(par->PRAMIN, 0x0824 * 4) | 0x00020000);
+ NV_WR32(par->PRAMIN, 0x0826 * 4,
+ NV_RD32(par->PRAMIN,
+ 0x0826 * 4) + par->FbAddress);
+ }
+ NV_WR32(par->PGRAPH, 0x0080, 0x000001FF);
+ NV_WR32(par->PGRAPH, 0x0080, 0x1230C000);
+ NV_WR32(par->PGRAPH, 0x0084, 0x72111101);
+ NV_WR32(par->PGRAPH, 0x0088, 0x11D5F071);
+ NV_WR32(par->PGRAPH, 0x008C, 0x0004FF31);
+ NV_WR32(par->PGRAPH, 0x008C, 0x4004FF31);
+ NV_WR32(par->PGRAPH, 0x0140, 0x00000000);
+ NV_WR32(par->PGRAPH, 0x0100, 0xFFFFFFFF);
+ NV_WR32(par->PGRAPH, 0x0170, 0x10010100);
+ NV_WR32(par->PGRAPH, 0x0710, 0xFFFFFFFF);
+ NV_WR32(par->PGRAPH, 0x0720, 0x00000001);
+ NV_WR32(par->PGRAPH, 0x0810, 0x00000000);
+ NV_WR32(par->PGRAPH, 0x0608, 0xFFFFFFFF);
+ } else {
+ NV_WR32(par->PGRAPH, 0x0080, 0xFFFFFFFF);
+ NV_WR32(par->PGRAPH, 0x0080, 0x00000000);
+
+ NV_WR32(par->PGRAPH, 0x0140, 0x00000000);
+ NV_WR32(par->PGRAPH, 0x0100, 0xFFFFFFFF);
+ NV_WR32(par->PGRAPH, 0x0144, 0x10010100);
+ NV_WR32(par->PGRAPH, 0x0714, 0xFFFFFFFF);
+ NV_WR32(par->PGRAPH, 0x0720, 0x00000001);
+ NV_WR32(par->PGRAPH, 0x0710,
+ NV_RD32(par->PGRAPH, 0x0710) & 0x0007ff00);
+ NV_WR32(par->PGRAPH, 0x0710,
+ NV_RD32(par->PGRAPH, 0x0710) | 0x00020100);
+
+ if (par->Architecture == NV_ARCH_10) {
+ NV_WR32(par->PGRAPH, 0x0084, 0x00118700);
+ NV_WR32(par->PGRAPH, 0x0088, 0x24E00810);
+ NV_WR32(par->PGRAPH, 0x008C, 0x55DE0030);
+
+ for (i = 0; i < 32; i++)
+ NV_WR32(&par->PGRAPH[(0x0B00 / 4) + i], 0,
+ NV_RD32(&par->PFB[(0x0240 / 4) + i],
+ 0));
+
+ NV_WR32(par->PGRAPH, 0x640, 0);
+ NV_WR32(par->PGRAPH, 0x644, 0);
+ NV_WR32(par->PGRAPH, 0x684, par->FbMapSize - 1);
+ NV_WR32(par->PGRAPH, 0x688, par->FbMapSize - 1);
+
+ NV_WR32(par->PGRAPH, 0x0810, 0x00000000);
+ NV_WR32(par->PGRAPH, 0x0608, 0xFFFFFFFF);
+ } else {
+ if (par->Architecture >= NV_ARCH_40) {
+ NV_WR32(par->PGRAPH, 0x0084, 0x401287c0);
+ NV_WR32(par->PGRAPH, 0x008C, 0x60de8051);
+ NV_WR32(par->PGRAPH, 0x0090, 0x00008000);
+ NV_WR32(par->PGRAPH, 0x0610, 0x00be3c5f);
+ NV_WR32(par->PGRAPH, 0x0bc4,
+ NV_RD32(par->PGRAPH, 0x0bc4) |
+ 0x00008000);
+
+ j = NV_RD32(par->REGS, 0x1540) & 0xff;
+
+ if (j) {
+ for (i = 0; !(j & 1); j >>= 1, i++);
+ NV_WR32(par->PGRAPH, 0x5000, i);
+ }
+
+ if ((par->Chipset & 0xfff0) == 0x0040) {
+ NV_WR32(par->PGRAPH, 0x09b0,
+ 0x83280fff);
+ NV_WR32(par->PGRAPH, 0x09b4,
+ 0x000000a0);
+ } else {
+ NV_WR32(par->PGRAPH, 0x0820,
+ 0x83280eff);
+ NV_WR32(par->PGRAPH, 0x0824,
+ 0x000000a0);
+ }
+
+ switch (par->Chipset & 0xfff0) {
+ case 0x0040:
+ case 0x0210:
+ NV_WR32(par->PGRAPH, 0x09b8,
+ 0x0078e366);
+ NV_WR32(par->PGRAPH, 0x09bc,
+ 0x0000014c);
+ NV_WR32(par->PFB, 0x033C,
+ NV_RD32(par->PFB, 0x33C) &
+ 0xffff7fff);
+ break;
+ case 0x00C0:
+ case 0x0120:
+ NV_WR32(par->PGRAPH, 0x0828,
+ 0x007596ff);
+ NV_WR32(par->PGRAPH, 0x082C,
+ 0x00000108);
+ break;
+ case 0x0160:
+ case 0x01D0:
+ case 0x0240:
+ case 0x03D0:
+ NV_WR32(par->PMC, 0x1700,
+ NV_RD32(par->PFB, 0x020C));
+ NV_WR32(par->PMC, 0x1704, 0);
+ NV_WR32(par->PMC, 0x1708, 0);
+ NV_WR32(par->PMC, 0x170C,
+ NV_RD32(par->PFB, 0x020C));
+ NV_WR32(par->PGRAPH, 0x0860, 0);
+ NV_WR32(par->PGRAPH, 0x0864, 0);
+ NV_WR32(par->PRAMDAC, 0x0608,
+ NV_RD32(par->PRAMDAC,
+ 0x0608) | 0x00100000);
+ break;
+ case 0x0140:
+ NV_WR32(par->PGRAPH, 0x0828,
+ 0x0072cb77);
+ NV_WR32(par->PGRAPH, 0x082C,
+ 0x00000108);
+ break;
+ case 0x0220:
+ NV_WR32(par->PGRAPH, 0x0860, 0);
+ NV_WR32(par->PGRAPH, 0x0864, 0);
+ NV_WR32(par->PRAMDAC, 0x0608,
+ NV_RD32(par->PRAMDAC, 0x0608) |
+ 0x00100000);
+ break;
+ case 0x0090:
+ case 0x0290:
+ case 0x0390:
+ NV_WR32(par->PRAMDAC, 0x0608,
+ NV_RD32(par->PRAMDAC, 0x0608) |
+ 0x00100000);
+ NV_WR32(par->PGRAPH, 0x0828,
+ 0x07830610);
+ NV_WR32(par->PGRAPH, 0x082C,
+ 0x0000016A);
+ break;
+ default:
+ break;
+ }
+
+ NV_WR32(par->PGRAPH, 0x0b38, 0x2ffff800);
+ NV_WR32(par->PGRAPH, 0x0b3c, 0x00006000);
+ NV_WR32(par->PGRAPH, 0x032C, 0x01000000);
+ NV_WR32(par->PGRAPH, 0x0220, 0x00001200);
+ } else if (par->Architecture == NV_ARCH_30) {
+ NV_WR32(par->PGRAPH, 0x0084, 0x40108700);
+ NV_WR32(par->PGRAPH, 0x0890, 0x00140000);
+ NV_WR32(par->PGRAPH, 0x008C, 0xf00e0431);
+ NV_WR32(par->PGRAPH, 0x0090, 0x00008000);
+ NV_WR32(par->PGRAPH, 0x0610, 0xf04b1f36);
+ NV_WR32(par->PGRAPH, 0x0B80, 0x1002d888);
+ NV_WR32(par->PGRAPH, 0x0B88, 0x62ff007f);
+ } else {
+ NV_WR32(par->PGRAPH, 0x0084, 0x00118700);
+ NV_WR32(par->PGRAPH, 0x008C, 0xF20E0431);
+ NV_WR32(par->PGRAPH, 0x0090, 0x00000000);
+ NV_WR32(par->PGRAPH, 0x009C, 0x00000040);
+
+ if ((par->Chipset & 0x0ff0) >= 0x0250) {
+ NV_WR32(par->PGRAPH, 0x0890,
+ 0x00080000);
+ NV_WR32(par->PGRAPH, 0x0610,
+ 0x304B1FB6);
+ NV_WR32(par->PGRAPH, 0x0B80,
+ 0x18B82880);
+ NV_WR32(par->PGRAPH, 0x0B84,
+ 0x44000000);
+ NV_WR32(par->PGRAPH, 0x0098,
+ 0x40000080);
+ NV_WR32(par->PGRAPH, 0x0B88,
+ 0x000000ff);
+ } else {
+ NV_WR32(par->PGRAPH, 0x0880,
+ 0x00080000);
+ NV_WR32(par->PGRAPH, 0x0094,
+ 0x00000005);
+ NV_WR32(par->PGRAPH, 0x0B80,
+ 0x45CAA208);
+ NV_WR32(par->PGRAPH, 0x0B84,
+ 0x24000000);
+ NV_WR32(par->PGRAPH, 0x0098,
+ 0x00000040);
+ NV_WR32(par->PGRAPH, 0x0750,
+ 0x00E00038);
+ NV_WR32(par->PGRAPH, 0x0754,
+ 0x00000030);
+ NV_WR32(par->PGRAPH, 0x0750,
+ 0x00E10038);
+ NV_WR32(par->PGRAPH, 0x0754,
+ 0x00000030);
+ }
+ }
+
+ if ((par->Architecture < NV_ARCH_40) ||
+ ((par->Chipset & 0xfff0) == 0x0040)) {
+ for (i = 0; i < 32; i++) {
+ NV_WR32(par->PGRAPH, 0x0900 + i*4,
+ NV_RD32(par->PFB, 0x0240 +i*4));
+ NV_WR32(par->PGRAPH, 0x6900 + i*4,
+ NV_RD32(par->PFB, 0x0240 +i*4));
+ }
+ } else {
+ if (((par->Chipset & 0xfff0) == 0x0090) ||
+ ((par->Chipset & 0xfff0) == 0x01D0) ||
+ ((par->Chipset & 0xfff0) == 0x0290) ||
+ ((par->Chipset & 0xfff0) == 0x0390) ||
+ ((par->Chipset & 0xfff0) == 0x03D0)) {
+ for (i = 0; i < 60; i++) {
+ NV_WR32(par->PGRAPH,
+ 0x0D00 + i*4,
+ NV_RD32(par->PFB,
+ 0x0600 + i*4));
+ NV_WR32(par->PGRAPH,
+ 0x6900 + i*4,
+ NV_RD32(par->PFB,
+ 0x0600 + i*4));
+ }
+ } else {
+ for (i = 0; i < 48; i++) {
+ NV_WR32(par->PGRAPH,
+ 0x0900 + i*4,
+ NV_RD32(par->PFB,
+ 0x0600 + i*4));
+ if(((par->Chipset & 0xfff0)
+ != 0x0160) &&
+ ((par->Chipset & 0xfff0)
+ != 0x0220) &&
+ ((par->Chipset & 0xfff0)
+ != 0x240))
+ NV_WR32(par->PGRAPH,
+ 0x6900 + i*4,
+ NV_RD32(par->PFB,
+ 0x0600 + i*4));
+ }
+ }
+ }
+
+ if (par->Architecture >= NV_ARCH_40) {
+ if ((par->Chipset & 0xfff0) == 0x0040) {
+ NV_WR32(par->PGRAPH, 0x09A4,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x09A8,
+ NV_RD32(par->PFB, 0x0204));
+ NV_WR32(par->PGRAPH, 0x69A4,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x69A8,
+ NV_RD32(par->PFB, 0x0204));
+
+ NV_WR32(par->PGRAPH, 0x0820, 0);
+ NV_WR32(par->PGRAPH, 0x0824, 0);
+ NV_WR32(par->PGRAPH, 0x0864,
+ par->FbMapSize - 1);
+ NV_WR32(par->PGRAPH, 0x0868,
+ par->FbMapSize - 1);
+ } else {
+ if ((par->Chipset & 0xfff0) == 0x0090 ||
+ (par->Chipset & 0xfff0) == 0x01D0 ||
+ (par->Chipset & 0xfff0) == 0x0290 ||
+ (par->Chipset & 0xfff0) == 0x0390) {
+ NV_WR32(par->PGRAPH, 0x0DF0,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x0DF4,
+ NV_RD32(par->PFB, 0x0204));
+ } else {
+ NV_WR32(par->PGRAPH, 0x09F0,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x09F4,
+ NV_RD32(par->PFB, 0x0204));
+ }
+ NV_WR32(par->PGRAPH, 0x69F0,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x69F4,
+ NV_RD32(par->PFB, 0x0204));
+
+ NV_WR32(par->PGRAPH, 0x0840, 0);
+ NV_WR32(par->PGRAPH, 0x0844, 0);
+ NV_WR32(par->PGRAPH, 0x08a0,
+ par->FbMapSize - 1);
+ NV_WR32(par->PGRAPH, 0x08a4,
+ par->FbMapSize - 1);
+ }
+ } else {
+ NV_WR32(par->PGRAPH, 0x09A4,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x09A8,
+ NV_RD32(par->PFB, 0x0204));
+ NV_WR32(par->PGRAPH, 0x0750, 0x00EA0000);
+ NV_WR32(par->PGRAPH, 0x0754,
+ NV_RD32(par->PFB, 0x0200));
+ NV_WR32(par->PGRAPH, 0x0750, 0x00EA0004);
+ NV_WR32(par->PGRAPH, 0x0754,
+ NV_RD32(par->PFB, 0x0204));
+
+ NV_WR32(par->PGRAPH, 0x0820, 0);
+ NV_WR32(par->PGRAPH, 0x0824, 0);
+ NV_WR32(par->PGRAPH, 0x0864,
+ par->FbMapSize - 1);
+ NV_WR32(par->PGRAPH, 0x0868,
+ par->FbMapSize - 1);
+ }
+ NV_WR32(par->PGRAPH, 0x0B20, 0x00000000);
+ NV_WR32(par->PGRAPH, 0x0B04, 0xFFFFFFFF);
+ }
+ }
+ NV_WR32(par->PGRAPH, 0x053C, 0);
+ NV_WR32(par->PGRAPH, 0x0540, 0);
+ NV_WR32(par->PGRAPH, 0x0544, 0x00007FFF);
+ NV_WR32(par->PGRAPH, 0x0548, 0x00007FFF);
+
+ NV_WR32(par->PFIFO, 0x0140 * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x0141 * 4, 0x00000001);
+ NV_WR32(par->PFIFO, 0x0480 * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x0494 * 4, 0x00000000);
+ if (par->Architecture >= NV_ARCH_40)
+ NV_WR32(par->PFIFO, 0x0481 * 4, 0x00010000);
+ else
+ NV_WR32(par->PFIFO, 0x0481 * 4, 0x00000100);
+ NV_WR32(par->PFIFO, 0x0490 * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x0491 * 4, 0x00000000);
+ if (par->Architecture >= NV_ARCH_40)
+ NV_WR32(par->PFIFO, 0x048B * 4, 0x00001213);
+ else
+ NV_WR32(par->PFIFO, 0x048B * 4, 0x00001209);
+ NV_WR32(par->PFIFO, 0x0400 * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x0414 * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x0084 * 4, 0x03000100);
+ NV_WR32(par->PFIFO, 0x0085 * 4, 0x00000110);
+ NV_WR32(par->PFIFO, 0x0086 * 4, 0x00000112);
+ NV_WR32(par->PFIFO, 0x0143 * 4, 0x0000FFFF);
+ NV_WR32(par->PFIFO, 0x0496 * 4, 0x0000FFFF);
+ NV_WR32(par->PFIFO, 0x0050 * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x0040 * 4, 0xFFFFFFFF);
+ NV_WR32(par->PFIFO, 0x0415 * 4, 0x00000001);
+ NV_WR32(par->PFIFO, 0x048C * 4, 0x00000000);
+ NV_WR32(par->PFIFO, 0x04A0 * 4, 0x00000000);
+#ifdef __BIG_ENDIAN
+ NV_WR32(par->PFIFO, 0x0489 * 4, 0x800F0078);
+#else
+ NV_WR32(par->PFIFO, 0x0489 * 4, 0x000F0078);
+#endif
+ NV_WR32(par->PFIFO, 0x0488 * 4, 0x00000001);
+ NV_WR32(par->PFIFO, 0x0480 * 4, 0x00000001);
+ NV_WR32(par->PFIFO, 0x0494 * 4, 0x00000001);
+ NV_WR32(par->PFIFO, 0x0495 * 4, 0x00000001);
+ NV_WR32(par->PFIFO, 0x0140 * 4, 0x00000001);
+
+ if (!state) {
+ par->CurrentState = NULL;
+ return;
+ }
+
+ if (par->Architecture >= NV_ARCH_10) {
+ if (par->twoHeads) {
+ NV_WR32(par->PCRTC0, 0x0860, state->head);
+ NV_WR32(par->PCRTC0, 0x2860, state->head2);
+ }
+ NV_WR32(par->PRAMDAC, 0x0404, NV_RD32(par->PRAMDAC, 0x0404) |
+ (1 << 25));
+
+ NV_WR32(par->PMC, 0x8704, 1);
+ NV_WR32(par->PMC, 0x8140, 0);
+ NV_WR32(par->PMC, 0x8920, 0);
+ NV_WR32(par->PMC, 0x8924, 0);
+ NV_WR32(par->PMC, 0x8908, par->FbMapSize - 1);
+ NV_WR32(par->PMC, 0x890C, par->FbMapSize - 1);
+ NV_WR32(par->PMC, 0x1588, 0);
+
+ NV_WR32(par->PCRTC, 0x0810, state->cursorConfig);
+ NV_WR32(par->PCRTC, 0x0830, state->displayV - 3);
+ NV_WR32(par->PCRTC, 0x0834, state->displayV - 1);
+
+ if (par->FlatPanel) {
+ if ((par->Chipset & 0x0ff0) == 0x0110) {
+ NV_WR32(par->PRAMDAC, 0x0528, state->dither);
+ } else if (par->twoHeads) {
+ NV_WR32(par->PRAMDAC, 0x083C, state->dither);
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x53);
+ VGA_WR08(par->PCIO, 0x03D5, state->timingH);
+ VGA_WR08(par->PCIO, 0x03D4, 0x54);
+ VGA_WR08(par->PCIO, 0x03D5, state->timingV);
+ VGA_WR08(par->PCIO, 0x03D4, 0x21);
+ VGA_WR08(par->PCIO, 0x03D5, 0xfa);
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x41);
+ VGA_WR08(par->PCIO, 0x03D5, state->extra);
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x19);
+ VGA_WR08(par->PCIO, 0x03D5, state->repaint0);
+ VGA_WR08(par->PCIO, 0x03D4, 0x1A);
+ VGA_WR08(par->PCIO, 0x03D5, state->repaint1);
+ VGA_WR08(par->PCIO, 0x03D4, 0x25);
+ VGA_WR08(par->PCIO, 0x03D5, state->screen);
+ VGA_WR08(par->PCIO, 0x03D4, 0x28);
+ VGA_WR08(par->PCIO, 0x03D5, state->pixel);
+ VGA_WR08(par->PCIO, 0x03D4, 0x2D);
+ VGA_WR08(par->PCIO, 0x03D5, state->horiz);
+ VGA_WR08(par->PCIO, 0x03D4, 0x1C);
+ VGA_WR08(par->PCIO, 0x03D5, state->fifo);
+ VGA_WR08(par->PCIO, 0x03D4, 0x1B);
+ VGA_WR08(par->PCIO, 0x03D5, state->arbitration0);
+ VGA_WR08(par->PCIO, 0x03D4, 0x20);
+ VGA_WR08(par->PCIO, 0x03D5, state->arbitration1);
+
+ if(par->Architecture >= NV_ARCH_30) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x47);
+ VGA_WR08(par->PCIO, 0x03D5, state->arbitration1 >> 8);
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x30);
+ VGA_WR08(par->PCIO, 0x03D5, state->cursor0);
+ VGA_WR08(par->PCIO, 0x03D4, 0x31);
+ VGA_WR08(par->PCIO, 0x03D5, state->cursor1);
+ VGA_WR08(par->PCIO, 0x03D4, 0x2F);
+ VGA_WR08(par->PCIO, 0x03D5, state->cursor2);
+ VGA_WR08(par->PCIO, 0x03D4, 0x39);
+ VGA_WR08(par->PCIO, 0x03D5, state->interlace);
+
+ if (!par->FlatPanel) {
+ if (par->Architecture >= NV_ARCH_40)
+ NV_WR32(par->PRAMDAC0, 0x0580, state->control);
+
+ NV_WR32(par->PRAMDAC0, 0x050C, state->pllsel);
+ NV_WR32(par->PRAMDAC0, 0x0508, state->vpll);
+ if (par->twoHeads)
+ NV_WR32(par->PRAMDAC0, 0x0520, state->vpll2);
+ if (par->twoStagePLL) {
+ NV_WR32(par->PRAMDAC0, 0x0578, state->vpllB);
+ NV_WR32(par->PRAMDAC0, 0x057C, state->vpll2B);
+ }
+ } else {
+ NV_WR32(par->PRAMDAC, 0x0848, state->scale);
+ NV_WR32(par->PRAMDAC, 0x0828, state->crtcSync +
+ par->PanelTweak);
+ }
+
+ NV_WR32(par->PRAMDAC, 0x0600, state->general);
+
+ NV_WR32(par->PCRTC, 0x0140, 0);
+ NV_WR32(par->PCRTC, 0x0100, 1);
+
+ par->CurrentState = state;
+}
+
+void NVUnloadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x19);
+ state->repaint0 = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x1A);
+ state->repaint1 = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x25);
+ state->screen = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x28);
+ state->pixel = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x2D);
+ state->horiz = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x1C);
+ state->fifo = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x1B);
+ state->arbitration0 = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x20);
+ state->arbitration1 = VGA_RD08(par->PCIO, 0x03D5);
+
+ if(par->Architecture >= NV_ARCH_30) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x47);
+ state->arbitration1 |= (VGA_RD08(par->PCIO, 0x03D5) & 1) << 8;
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x30);
+ state->cursor0 = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x31);
+ state->cursor1 = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x2F);
+ state->cursor2 = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x39);
+ state->interlace = VGA_RD08(par->PCIO, 0x03D5);
+ state->vpll = NV_RD32(par->PRAMDAC0, 0x0508);
+ if (par->twoHeads)
+ state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
+ if (par->twoStagePLL) {
+ state->vpllB = NV_RD32(par->PRAMDAC0, 0x0578);
+ state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
+ }
+ state->pllsel = NV_RD32(par->PRAMDAC0, 0x050C);
+ state->general = NV_RD32(par->PRAMDAC, 0x0600);
+ state->scale = NV_RD32(par->PRAMDAC, 0x0848);
+ state->config = NV_RD32(par->PFB, 0x0200);
+
+ if (par->Architecture >= NV_ARCH_40 && !par->FlatPanel)
+ state->control = NV_RD32(par->PRAMDAC0, 0x0580);
+
+ if (par->Architecture >= NV_ARCH_10) {
+ if (par->twoHeads) {
+ state->head = NV_RD32(par->PCRTC0, 0x0860);
+ state->head2 = NV_RD32(par->PCRTC0, 0x2860);
+ VGA_WR08(par->PCIO, 0x03D4, 0x44);
+ state->crtcOwner = VGA_RD08(par->PCIO, 0x03D5);
+ }
+ VGA_WR08(par->PCIO, 0x03D4, 0x41);
+ state->extra = VGA_RD08(par->PCIO, 0x03D5);
+ state->cursorConfig = NV_RD32(par->PCRTC, 0x0810);
+
+ if ((par->Chipset & 0x0ff0) == 0x0110) {
+ state->dither = NV_RD32(par->PRAMDAC, 0x0528);
+ } else if (par->twoHeads) {
+ state->dither = NV_RD32(par->PRAMDAC, 0x083C);
+ }
+
+ if (par->FlatPanel) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x53);
+ state->timingH = VGA_RD08(par->PCIO, 0x03D5);
+ VGA_WR08(par->PCIO, 0x03D4, 0x54);
+ state->timingV = VGA_RD08(par->PCIO, 0x03D5);
+ }
+ }
+}
+
+void NVSetStartAddress(struct nvidia_par *par, u32 start)
+{
+ NV_WR32(par->PCRTC, 0x800, start);
+}
diff --git a/drivers/video/fbdev/nvidia/nv_i2c.c b/drivers/video/fbdev/nvidia/nv_i2c.c
new file mode 100644
index 000000000..0b48965a6
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_i2c.c
@@ -0,0 +1,171 @@
+/*
+ * linux/drivers/video/nvidia/nvidia-i2c.c - nVidia i2c
+ *
+ * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
+ *
+ * Based on rivafb-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/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+
+#include "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+
+#include "../edid.h"
+
+static void nvidia_gpio_setscl(void *data, int state)
+{
+ struct nvidia_i2c_chan *chan = data;
+ struct nvidia_par *par = chan->par;
+ u32 val;
+
+ val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0;
+
+ if (state)
+ val |= 0x20;
+ else
+ val &= ~0x20;
+
+ NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01);
+}
+
+static void nvidia_gpio_setsda(void *data, int state)
+{
+ struct nvidia_i2c_chan *chan = data;
+ struct nvidia_par *par = chan->par;
+ u32 val;
+
+ val = NVReadCrtc(par, chan->ddc_base + 1) & 0xf0;
+
+ if (state)
+ val |= 0x10;
+ else
+ val &= ~0x10;
+
+ NVWriteCrtc(par, chan->ddc_base + 1, val | 0x01);
+}
+
+static int nvidia_gpio_getscl(void *data)
+{
+ struct nvidia_i2c_chan *chan = data;
+ struct nvidia_par *par = chan->par;
+ u32 val = 0;
+
+ if (NVReadCrtc(par, chan->ddc_base) & 0x04)
+ val = 1;
+
+ return val;
+}
+
+static int nvidia_gpio_getsda(void *data)
+{
+ struct nvidia_i2c_chan *chan = data;
+ struct nvidia_par *par = chan->par;
+ u32 val = 0;
+
+ if (NVReadCrtc(par, chan->ddc_base) & 0x08)
+ val = 1;
+
+ return val;
+}
+
+static int nvidia_setup_i2c_bus(struct nvidia_i2c_chan *chan, const char *name,
+ unsigned int i2c_class)
+{
+ int rc;
+
+ strscpy(chan->adapter.name, name, sizeof(chan->adapter.name));
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.class = i2c_class;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &chan->par->pci_dev->dev;
+ chan->algo.setsda = nvidia_gpio_setsda;
+ chan->algo.setscl = nvidia_gpio_setscl;
+ chan->algo.getsda = nvidia_gpio_getsda;
+ chan->algo.getscl = nvidia_gpio_getscl;
+ chan->algo.udelay = 40;
+ chan->algo.timeout = msecs_to_jiffies(2);
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ /* Raise SCL and SDA */
+ nvidia_gpio_setsda(chan, 1);
+ nvidia_gpio_setscl(chan, 1);
+ udelay(20);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ dev_dbg(&chan->par->pci_dev->dev,
+ "I2C bus %s registered.\n", name);
+ else {
+ dev_warn(&chan->par->pci_dev->dev,
+ "Failed to register I2C bus %s.\n", name);
+ chan->par = NULL;
+ }
+
+ return rc;
+}
+
+void nvidia_create_i2c_busses(struct nvidia_par *par)
+{
+ par->chan[0].par = par;
+ par->chan[1].par = par;
+ par->chan[2].par = par;
+
+ par->chan[0].ddc_base = (par->reverse_i2c) ? 0x36 : 0x3e;
+ nvidia_setup_i2c_bus(&par->chan[0], "nvidia #0",
+ (par->reverse_i2c) ? I2C_CLASS_HWMON : 0);
+
+ par->chan[1].ddc_base = (par->reverse_i2c) ? 0x3e : 0x36;
+ nvidia_setup_i2c_bus(&par->chan[1], "nvidia #1",
+ (par->reverse_i2c) ? 0 : I2C_CLASS_HWMON);
+
+ par->chan[2].ddc_base = 0x50;
+ nvidia_setup_i2c_bus(&par->chan[2], "nvidia #2", 0);
+}
+
+void nvidia_delete_i2c_busses(struct nvidia_par *par)
+{
+ int i;
+
+ for (i = 0; i < 3; i++) {
+ if (!par->chan[i].par)
+ continue;
+ i2c_del_adapter(&par->chan[i].adapter);
+ par->chan[i].par = NULL;
+ }
+}
+
+int nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid)
+{
+ struct nvidia_par *par = info->par;
+ u8 *edid = NULL;
+
+ if (par->chan[conn - 1].par)
+ edid = fb_ddc_read(&par->chan[conn - 1].adapter);
+
+ if (!edid && conn == 1) {
+ /* try to get from firmware */
+ const u8 *e = fb_firmware_edid(info->device);
+
+ if (e != NULL)
+ edid = kmemdup(e, EDID_LENGTH, GFP_KERNEL);
+ }
+
+ *out_edid = edid;
+
+ return (edid) ? 0 : 1;
+}
diff --git a/drivers/video/fbdev/nvidia/nv_local.h b/drivers/video/fbdev/nvidia/nv_local.h
new file mode 100644
index 000000000..93aff3530
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_local.h
@@ -0,0 +1,114 @@
+/***************************************************************************\
+|* *|
+|* Copyright 1993-2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
+|* international laws. Users and possessors of this source code are *|
+|* hereby granted a nonexclusive, royalty-free copyright license to *|
+|* use this code in individual and commercial software. *|
+|* *|
+|* Any use of this source code must include, in the user documenta- *|
+|* tion and internal comments to the code, notices to the end user *|
+|* as follows: *|
+|* *|
+|* Copyright 1993-1999 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
+|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
+|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
+|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
+|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
+|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
+|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
+|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
+|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
+|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
+|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
+|* *|
+|* U.S. Government End Users. This source code is a "commercial *|
+|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
+|* consisting of "commercial computer software" and "commercial *|
+|* computer software documentation," as such terms are used in *|
+|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
+|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
+|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
+|* all U.S. Government End Users acquire the source code with only *|
+|* those rights set forth herein. *|
+|* *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#ifndef __NV_LOCAL_H__
+#define __NV_LOCAL_H__
+
+/*
+ * This file includes any environment or machine specific values to access the
+ * HW. Put all affected includes, typdefs, etc. here so the riva_hw.* files
+ * can stay generic in nature.
+ */
+
+/*
+ * HW access macros. These assume memory-mapped I/O, and not normal I/O space.
+ */
+#define NV_WR08(p,i,d) (__raw_writeb((d), (void __iomem *)(p) + (i)))
+#define NV_RD08(p,i) (__raw_readb((void __iomem *)(p) + (i)))
+#define NV_WR16(p,i,d) (__raw_writew((d), (void __iomem *)(p) + (i)))
+#define NV_RD16(p,i) (__raw_readw((void __iomem *)(p) + (i)))
+#define NV_WR32(p,i,d) (__raw_writel((d), (void __iomem *)(p) + (i)))
+#define NV_RD32(p,i) (__raw_readl((void __iomem *)(p) + (i)))
+
+/* VGA I/O is now always done through MMIO */
+#define VGA_WR08(p,i,d) (writeb((d), (void __iomem *)(p) + (i)))
+#define VGA_RD08(p,i) (readb((void __iomem *)(p) + (i)))
+
+#define NVDmaNext(par, data) \
+ NV_WR32(&(par)->dmaBase[(par)->dmaCurrent++], 0, (data))
+
+#define NVDmaStart(info, par, tag, size) { \
+ if((par)->dmaFree <= (size)) \
+ NVDmaWait(info, size); \
+ NVDmaNext(par, ((size) << 18) | (tag)); \
+ (par)->dmaFree -= ((size) + 1); \
+}
+
+#if defined(__i386__) && !defined(CONFIG_UML)
+#define _NV_FENCE() outb(0, 0x3D0);
+#else
+#define _NV_FENCE() mb();
+#endif
+
+#define WRITE_PUT(par, data) { \
+ _NV_FENCE() \
+ NV_RD08((par)->FbStart, 0); \
+ NV_WR32(&(par)->FIFO[0x0010], 0, (data) << 2); \
+ mb(); \
+}
+
+#define READ_GET(par) (NV_RD32(&(par)->FIFO[0x0011], 0) >> 2)
+
+#ifdef __LITTLE_ENDIAN
+
+#include <linux/bitrev.h>
+
+#define reverse_order(l) \
+do { \
+ u8 *a = (u8 *)(l); \
+ a[0] = bitrev8(a[0]); \
+ a[1] = bitrev8(a[1]); \
+ a[2] = bitrev8(a[2]); \
+ a[3] = bitrev8(a[3]); \
+} while(0)
+#else
+#define reverse_order(l) do { } while(0)
+#endif /* __LITTLE_ENDIAN */
+
+#endif /* __NV_LOCAL_H__ */
diff --git a/drivers/video/fbdev/nvidia/nv_of.c b/drivers/video/fbdev/nvidia/nv_of.c
new file mode 100644
index 000000000..d20b8779b
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_of.c
@@ -0,0 +1,78 @@
+/*
+ * linux/drivers/video/nvidia/nv_of.c
+ *
+ * Copyright 2004 Antonino A. Daplas <adaplas @pol.net>
+ *
+ * Based on rivafb-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/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/gfp.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <asm/io.h>
+
+#include "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+
+#include "../edid.h"
+
+int nvidia_probe_of_connector(struct fb_info *info, int conn, u8 **out_edid)
+{
+ struct nvidia_par *par = info->par;
+ struct device_node *parent, *dp;
+ const unsigned char *pedid = NULL;
+ static char *propnames[] = {
+ "DFP,EDID", "LCD,EDID", "EDID", "EDID1",
+ "EDID,B", "EDID,A", NULL };
+ int i;
+
+ parent = pci_device_to_OF_node(par->pci_dev);
+ if (parent == NULL)
+ return -1;
+ if (par->twoHeads) {
+ const char *pname;
+ int len;
+
+ for_each_child_of_node(parent, dp) {
+ pname = of_get_property(dp, "name", NULL);
+ if (!pname)
+ continue;
+ len = strlen(pname);
+ if ((pname[len-1] == 'A' && conn == 1) ||
+ (pname[len-1] == 'B' && conn == 2)) {
+ for (i = 0; propnames[i] != NULL; ++i) {
+ pedid = of_get_property(dp,
+ propnames[i], NULL);
+ if (pedid != NULL)
+ break;
+ }
+ of_node_put(dp);
+ break;
+ }
+ }
+ }
+ if (pedid == NULL) {
+ for (i = 0; propnames[i] != NULL; ++i) {
+ pedid = of_get_property(parent, propnames[i], NULL);
+ if (pedid != NULL)
+ break;
+ }
+ }
+ if (pedid) {
+ *out_edid = kmemdup(pedid, EDID_LENGTH, GFP_KERNEL);
+ if (*out_edid == NULL)
+ return -1;
+ printk(KERN_DEBUG "nvidiafb: Found OF EDID for head %d\n", conn);
+ return 0;
+ }
+ return -1;
+}
diff --git a/drivers/video/fbdev/nvidia/nv_proto.h b/drivers/video/fbdev/nvidia/nv_proto.h
new file mode 100644
index 000000000..fb9c5ebf2
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_proto.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nv_proto.h,v 1.10 2003/07/31 20:24:29 mvojkovi Exp $ */
+
+#ifndef __NV_PROTO_H__
+#define __NV_PROTO_H__
+
+/* in nv_setup.c */
+int NVCommonSetup(struct fb_info *info);
+void NVWriteCrtc(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadCrtc(struct nvidia_par *par, u8 index);
+void NVWriteGr(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadGr(struct nvidia_par *par, u8 index);
+void NVWriteSeq(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadSeq(struct nvidia_par *par, u8 index);
+void NVWriteAttr(struct nvidia_par *par, u8 index, u8 value);
+u8 NVReadAttr(struct nvidia_par *par, u8 index);
+void NVWriteMiscOut(struct nvidia_par *par, u8 value);
+u8 NVReadMiscOut(struct nvidia_par *par);
+void NVWriteDacMask(struct nvidia_par *par, u8 value);
+void NVWriteDacReadAddr(struct nvidia_par *par, u8 value);
+void NVWriteDacWriteAddr(struct nvidia_par *par, u8 value);
+void NVWriteDacData(struct nvidia_par *par, u8 value);
+u8 NVReadDacData(struct nvidia_par *par);
+
+/* in nv_hw.c */
+void NVCalcStateExt(struct nvidia_par *par, struct _riva_hw_state *,
+ int, int, int, int, int, int);
+void NVLoadStateExt(struct nvidia_par *par, struct _riva_hw_state *);
+void NVUnloadStateExt(struct nvidia_par *par, struct _riva_hw_state *);
+void NVSetStartAddress(struct nvidia_par *par, u32);
+int NVShowHideCursor(struct nvidia_par *par, int);
+void NVLockUnlock(struct nvidia_par *par, int);
+
+/* in nvidia-i2c.c */
+#ifdef CONFIG_FB_NVIDIA_I2C
+void nvidia_create_i2c_busses(struct nvidia_par *par);
+void nvidia_delete_i2c_busses(struct nvidia_par *par);
+int nvidia_probe_i2c_connector(struct fb_info *info, int conn,
+ u8 ** out_edid);
+#else
+#define nvidia_create_i2c_busses(...)
+#define nvidia_delete_i2c_busses(...)
+#define nvidia_probe_i2c_connector(p, c, edid) (-1)
+#endif
+
+int nvidia_probe_of_connector(struct fb_info *info, int conn,
+ u8 ** out_edid);
+
+/* in nv_accel.c */
+extern void NVResetGraphics(struct fb_info *info);
+extern void nvidiafb_copyarea(struct fb_info *info,
+ const struct fb_copyarea *region);
+extern void nvidiafb_fillrect(struct fb_info *info,
+ const struct fb_fillrect *rect);
+extern void nvidiafb_imageblit(struct fb_info *info,
+ const struct fb_image *image);
+extern int nvidiafb_sync(struct fb_info *info);
+
+/* in nv_backlight.h */
+#ifdef CONFIG_FB_NVIDIA_BACKLIGHT
+extern void nvidia_bl_init(struct nvidia_par *par);
+extern void nvidia_bl_exit(struct nvidia_par *par);
+#else
+static inline void nvidia_bl_init(struct nvidia_par *par) {}
+static inline void nvidia_bl_exit(struct nvidia_par *par) {}
+#endif
+
+#endif /* __NV_PROTO_H__ */
diff --git a/drivers/video/fbdev/nvidia/nv_setup.c b/drivers/video/fbdev/nvidia/nv_setup.c
new file mode 100644
index 000000000..281c7f2ee
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_setup.c
@@ -0,0 +1,649 @@
+ /***************************************************************************\
+|* *|
+|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NOTICE TO USER: The source code is copyrighted under U.S. and *|
+|* international laws. Users and possessors of this source code are *|
+|* hereby granted a nonexclusive, royalty-free copyright license to *|
+|* use this code in individual and commercial software. *|
+|* *|
+|* Any use of this source code must include, in the user documenta- *|
+|* tion and internal comments to the code, notices to the end user *|
+|* as follows: *|
+|* *|
+|* Copyright 2003 NVIDIA, Corporation. All rights reserved. *|
+|* *|
+|* NVIDIA, CORPORATION MAKES NO REPRESENTATION ABOUT THE SUITABILITY *|
+|* OF THIS SOURCE CODE FOR ANY PURPOSE. IT IS PROVIDED "AS IS" *|
+|* WITHOUT EXPRESS OR IMPLIED WARRANTY OF ANY KIND. NVIDIA, CORPOR- *|
+|* ATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOURCE CODE, *|
+|* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY, NONINFRINGE- *|
+|* MENT, AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL *|
+|* NVIDIA, CORPORATION BE LIABLE FOR ANY SPECIAL, INDIRECT, INCI- *|
+|* DENTAL, OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RE- *|
+|* SULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION *|
+|* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF *|
+|* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOURCE CODE. *|
+|* *|
+|* U.S. Government End Users. This source code is a "commercial *|
+|* item," as that term is defined at 48 C.F.R. 2.101 (OCT 1995), *|
+|* consisting of "commercial computer software" and "commercial *|
+|* computer software documentation," as such terms are used in *|
+|* 48 C.F.R. 12.212 (SEPT 1995) and is provided to the U.S. Govern- *|
+|* ment only as a commercial end item. Consistent with 48 C.F.R. *|
+|* 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (JUNE 1995), *|
+|* all U.S. Government End Users acquire the source code with only *|
+|* those rights set forth herein. *|
+|* *|
+ \***************************************************************************/
+
+/*
+ * GPL Licensing Note - According to Mark Vojkovich, author of the Xorg/
+ * XFree86 'nv' driver, this source code is provided under MIT-style licensing
+ * where the source code is provided "as is" without warranty of any kind.
+ * The only usage restriction is for the copyright notices to be retained
+ * whenever code is used.
+ *
+ * Antonino Daplas <adaplas@pol.net> 2005-03-11
+ */
+
+#include <video/vga.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include "nv_type.h"
+#include "nv_local.h"
+#include "nv_proto.h"
+/*
+ * Override VGA I/O routines.
+ */
+void NVWriteCrtc(struct nvidia_par *par, u8 index, u8 value)
+{
+ VGA_WR08(par->PCIO, par->IOBase + 0x04, index);
+ VGA_WR08(par->PCIO, par->IOBase + 0x05, value);
+}
+u8 NVReadCrtc(struct nvidia_par *par, u8 index)
+{
+ VGA_WR08(par->PCIO, par->IOBase + 0x04, index);
+ return (VGA_RD08(par->PCIO, par->IOBase + 0x05));
+}
+void NVWriteGr(struct nvidia_par *par, u8 index, u8 value)
+{
+ VGA_WR08(par->PVIO, VGA_GFX_I, index);
+ VGA_WR08(par->PVIO, VGA_GFX_D, value);
+}
+u8 NVReadGr(struct nvidia_par *par, u8 index)
+{
+ VGA_WR08(par->PVIO, VGA_GFX_I, index);
+ return (VGA_RD08(par->PVIO, VGA_GFX_D));
+}
+void NVWriteSeq(struct nvidia_par *par, u8 index, u8 value)
+{
+ VGA_WR08(par->PVIO, VGA_SEQ_I, index);
+ VGA_WR08(par->PVIO, VGA_SEQ_D, value);
+}
+u8 NVReadSeq(struct nvidia_par *par, u8 index)
+{
+ VGA_WR08(par->PVIO, VGA_SEQ_I, index);
+ return (VGA_RD08(par->PVIO, VGA_SEQ_D));
+}
+void NVWriteAttr(struct nvidia_par *par, u8 index, u8 value)
+{
+
+ VGA_RD08(par->PCIO, par->IOBase + 0x0a);
+ if (par->paletteEnabled)
+ index &= ~0x20;
+ else
+ index |= 0x20;
+ VGA_WR08(par->PCIO, VGA_ATT_IW, index);
+ VGA_WR08(par->PCIO, VGA_ATT_W, value);
+}
+u8 NVReadAttr(struct nvidia_par *par, u8 index)
+{
+ VGA_RD08(par->PCIO, par->IOBase + 0x0a);
+ if (par->paletteEnabled)
+ index &= ~0x20;
+ else
+ index |= 0x20;
+ VGA_WR08(par->PCIO, VGA_ATT_IW, index);
+ return (VGA_RD08(par->PCIO, VGA_ATT_R));
+}
+void NVWriteMiscOut(struct nvidia_par *par, u8 value)
+{
+ VGA_WR08(par->PVIO, VGA_MIS_W, value);
+}
+u8 NVReadMiscOut(struct nvidia_par *par)
+{
+ return (VGA_RD08(par->PVIO, VGA_MIS_R));
+}
+void NVWriteDacMask(struct nvidia_par *par, u8 value)
+{
+ VGA_WR08(par->PDIO, VGA_PEL_MSK, value);
+}
+void NVWriteDacReadAddr(struct nvidia_par *par, u8 value)
+{
+ VGA_WR08(par->PDIO, VGA_PEL_IR, value);
+}
+void NVWriteDacWriteAddr(struct nvidia_par *par, u8 value)
+{
+ VGA_WR08(par->PDIO, VGA_PEL_IW, value);
+}
+void NVWriteDacData(struct nvidia_par *par, u8 value)
+{
+ VGA_WR08(par->PDIO, VGA_PEL_D, value);
+}
+u8 NVReadDacData(struct nvidia_par *par)
+{
+ return (VGA_RD08(par->PDIO, VGA_PEL_D));
+}
+
+static int NVIsConnected(struct nvidia_par *par, int output)
+{
+ volatile u32 __iomem *PRAMDAC = par->PRAMDAC0;
+ u32 reg52C, reg608, dac0_reg608 = 0;
+ int present;
+
+ if (output) {
+ dac0_reg608 = NV_RD32(PRAMDAC, 0x0608);
+ PRAMDAC += 0x800;
+ }
+
+ reg52C = NV_RD32(PRAMDAC, 0x052C);
+ reg608 = NV_RD32(PRAMDAC, 0x0608);
+
+ NV_WR32(PRAMDAC, 0x0608, reg608 & ~0x00010000);
+
+ NV_WR32(PRAMDAC, 0x052C, reg52C & 0x0000FEEE);
+ msleep(1);
+ NV_WR32(PRAMDAC, 0x052C, NV_RD32(PRAMDAC, 0x052C) | 1);
+
+ NV_WR32(par->PRAMDAC0, 0x0610, 0x94050140);
+ NV_WR32(par->PRAMDAC0, 0x0608, NV_RD32(par->PRAMDAC0, 0x0608) |
+ 0x00001000);
+
+ msleep(1);
+
+ present = (NV_RD32(PRAMDAC, 0x0608) & (1 << 28)) ? 1 : 0;
+
+ if (present)
+ printk("nvidiafb: CRTC%i analog found\n", output);
+ else
+ printk("nvidiafb: CRTC%i analog not found\n", output);
+
+ if (output)
+ NV_WR32(par->PRAMDAC0, 0x0608, dac0_reg608);
+
+ NV_WR32(PRAMDAC, 0x052C, reg52C);
+ NV_WR32(PRAMDAC, 0x0608, reg608);
+
+ return present;
+}
+
+static void NVSelectHeadRegisters(struct nvidia_par *par, int head)
+{
+ if (head) {
+ par->PCIO = par->PCIO0 + 0x2000;
+ par->PCRTC = par->PCRTC0 + 0x800;
+ par->PRAMDAC = par->PRAMDAC0 + 0x800;
+ par->PDIO = par->PDIO0 + 0x2000;
+ } else {
+ par->PCIO = par->PCIO0;
+ par->PCRTC = par->PCRTC0;
+ par->PRAMDAC = par->PRAMDAC0;
+ par->PDIO = par->PDIO0;
+ }
+}
+
+static void nv4GetConfig(struct nvidia_par *par)
+{
+ if (NV_RD32(par->PFB, 0x0000) & 0x00000100) {
+ par->RamAmountKBytes =
+ ((NV_RD32(par->PFB, 0x0000) >> 12) & 0x0F) * 1024 * 2 +
+ 1024 * 2;
+ } else {
+ switch (NV_RD32(par->PFB, 0x0000) & 0x00000003) {
+ case 0:
+ par->RamAmountKBytes = 1024 * 32;
+ break;
+ case 1:
+ par->RamAmountKBytes = 1024 * 4;
+ break;
+ case 2:
+ par->RamAmountKBytes = 1024 * 8;
+ break;
+ case 3:
+ default:
+ par->RamAmountKBytes = 1024 * 16;
+ break;
+ }
+ }
+ par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & 0x00000040) ?
+ 14318 : 13500;
+ par->CURSOR = &par->PRAMIN[0x1E00];
+ par->MinVClockFreqKHz = 12000;
+ par->MaxVClockFreqKHz = 350000;
+}
+
+static void nv10GetConfig(struct nvidia_par *par)
+{
+ struct pci_dev *dev;
+ u32 implementation = par->Chipset & 0x0ff0;
+
+#ifdef __BIG_ENDIAN
+ /* turn on big endian register access */
+ if (!(NV_RD32(par->PMC, 0x0004) & 0x01000001)) {
+ NV_WR32(par->PMC, 0x0004, 0x01000001);
+ mb();
+ }
+#endif
+
+ dev = pci_get_domain_bus_and_slot(pci_domain_nr(par->pci_dev->bus),
+ 0, 1);
+ if ((par->Chipset & 0xffff) == 0x01a0) {
+ u32 amt;
+
+ pci_read_config_dword(dev, 0x7c, &amt);
+ par->RamAmountKBytes = (((amt >> 6) & 31) + 1) * 1024;
+ } else if ((par->Chipset & 0xffff) == 0x01f0) {
+ u32 amt;
+
+ pci_read_config_dword(dev, 0x84, &amt);
+ par->RamAmountKBytes = (((amt >> 4) & 127) + 1) * 1024;
+ } else {
+ par->RamAmountKBytes =
+ (NV_RD32(par->PFB, 0x020C) & 0xFFF00000) >> 10;
+ }
+ pci_dev_put(dev);
+
+ par->CrystalFreqKHz = (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 6)) ?
+ 14318 : 13500;
+
+ if (par->twoHeads && (implementation != 0x0110)) {
+ if (NV_RD32(par->PEXTDEV, 0x0000) & (1 << 22))
+ par->CrystalFreqKHz = 27000;
+ }
+
+ par->CURSOR = NULL; /* can't set this here */
+ par->MinVClockFreqKHz = 12000;
+ par->MaxVClockFreqKHz = par->twoStagePLL ? 400000 : 350000;
+}
+
+int NVCommonSetup(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ struct fb_var_screeninfo *var;
+ u16 implementation = par->Chipset & 0x0ff0;
+ u8 *edidA = NULL, *edidB = NULL;
+ struct fb_monspecs *monitorA, *monitorB;
+ struct fb_monspecs *monA = NULL, *monB = NULL;
+ int mobile = 0;
+ int tvA = 0;
+ int tvB = 0;
+ int FlatPanel = -1; /* really means the CRTC is slaved */
+ int Television = 0;
+ int err = 0;
+
+ var = kzalloc_obj(struct fb_var_screeninfo);
+ monitorA = kzalloc_obj(struct fb_monspecs);
+ monitorB = kzalloc_obj(struct fb_monspecs);
+
+ if (!var || !monitorA || !monitorB) {
+ err = -ENOMEM;
+ goto done;
+ }
+
+ par->PRAMIN = par->REGS + (0x00710000 / 4);
+ par->PCRTC0 = par->REGS + (0x00600000 / 4);
+ par->PRAMDAC0 = par->REGS + (0x00680000 / 4);
+ par->PFB = par->REGS + (0x00100000 / 4);
+ par->PFIFO = par->REGS + (0x00002000 / 4);
+ par->PGRAPH = par->REGS + (0x00400000 / 4);
+ par->PEXTDEV = par->REGS + (0x00101000 / 4);
+ par->PTIMER = par->REGS + (0x00009000 / 4);
+ par->PMC = par->REGS + (0x00000000 / 4);
+ par->FIFO = par->REGS + (0x00800000 / 4);
+
+ /* 8 bit registers */
+ par->PCIO0 = (u8 __iomem *) par->REGS + 0x00601000;
+ par->PDIO0 = (u8 __iomem *) par->REGS + 0x00681000;
+ par->PVIO = (u8 __iomem *) par->REGS + 0x000C0000;
+
+ par->twoHeads = (par->Architecture >= NV_ARCH_10) &&
+ (implementation != 0x0100) &&
+ (implementation != 0x0150) &&
+ (implementation != 0x01A0) && (implementation != 0x0200);
+
+ par->fpScaler = (par->FpScale && par->twoHeads &&
+ (implementation != 0x0110));
+
+ par->twoStagePLL = (implementation == 0x0310) ||
+ (implementation == 0x0340) || (par->Architecture >= NV_ARCH_40);
+
+ par->WaitVSyncPossible = (par->Architecture >= NV_ARCH_10) &&
+ (implementation != 0x0100);
+
+ par->BlendingPossible = ((par->Chipset & 0xffff) != 0x0020);
+
+ /* look for known laptop chips */
+ switch (par->Chipset & 0xffff) {
+ case 0x0112:
+ case 0x0174:
+ case 0x0175:
+ case 0x0176:
+ case 0x0177:
+ case 0x0179:
+ case 0x017C:
+ case 0x017D:
+ case 0x0186:
+ case 0x0187:
+ case 0x018D:
+ case 0x01D7:
+ case 0x0228:
+ case 0x0286:
+ case 0x028C:
+ case 0x0316:
+ case 0x0317:
+ case 0x031A:
+ case 0x031B:
+ case 0x031C:
+ case 0x031D:
+ case 0x031E:
+ case 0x031F:
+ case 0x0324:
+ case 0x0325:
+ case 0x0328:
+ case 0x0329:
+ case 0x032C:
+ case 0x032D:
+ case 0x0347:
+ case 0x0348:
+ case 0x0349:
+ case 0x034B:
+ case 0x034C:
+ case 0x0160:
+ case 0x0166:
+ case 0x0169:
+ case 0x016B:
+ case 0x016C:
+ case 0x016D:
+ case 0x00C8:
+ case 0x00CC:
+ case 0x0144:
+ case 0x0146:
+ case 0x0147:
+ case 0x0148:
+ case 0x0098:
+ case 0x0099:
+ mobile = 1;
+ break;
+ default:
+ break;
+ }
+
+ if (par->Architecture == NV_ARCH_04)
+ nv4GetConfig(par);
+ else
+ nv10GetConfig(par);
+
+ NVSelectHeadRegisters(par, 0);
+
+ NVLockUnlock(par, 0);
+
+ par->IOBase = (NVReadMiscOut(par) & 0x01) ? 0x3d0 : 0x3b0;
+
+ par->Television = 0;
+
+ nvidia_create_i2c_busses(par);
+ if (!par->twoHeads) {
+ par->CRTCnumber = 0;
+ if (nvidia_probe_i2c_connector(info, 1, &edidA))
+ nvidia_probe_of_connector(info, 1, &edidA);
+ if (edidA && !fb_parse_edid(edidA, var)) {
+ printk("nvidiafb: EDID found from BUS1\n");
+ monA = monitorA;
+ fb_edid_to_monspecs(edidA, monA);
+ FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0;
+
+ /* NV4 doesn't support FlatPanels */
+ if ((par->Chipset & 0x0fff) <= 0x0020)
+ FlatPanel = 0;
+ } else {
+ VGA_WR08(par->PCIO, 0x03D4, 0x28);
+ if (VGA_RD08(par->PCIO, 0x03D5) & 0x80) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x33);
+ if (!(VGA_RD08(par->PCIO, 0x03D5) & 0x01))
+ Television = 1;
+ FlatPanel = 1;
+ } else {
+ FlatPanel = 0;
+ }
+ printk("nvidiafb: HW is currently programmed for %s\n",
+ FlatPanel ? (Television ? "TV" : "DFP") :
+ "CRT");
+ }
+
+ if (par->FlatPanel == -1) {
+ par->FlatPanel = FlatPanel;
+ par->Television = Television;
+ } else {
+ printk("nvidiafb: Forcing display type to %s as "
+ "specified\n", par->FlatPanel ? "DFP" : "CRT");
+ }
+ } else {
+ u8 outputAfromCRTC, outputBfromCRTC;
+ int CRTCnumber = -1;
+ u8 slaved_on_A, slaved_on_B;
+ int analog_on_A, analog_on_B;
+ u32 oldhead;
+ u8 cr44;
+
+ if (implementation != 0x0110) {
+ if (NV_RD32(par->PRAMDAC0, 0x0000052C) & 0x100)
+ outputAfromCRTC = 1;
+ else
+ outputAfromCRTC = 0;
+ if (NV_RD32(par->PRAMDAC0, 0x0000252C) & 0x100)
+ outputBfromCRTC = 1;
+ else
+ outputBfromCRTC = 0;
+ analog_on_A = NVIsConnected(par, 0);
+ analog_on_B = NVIsConnected(par, 1);
+ } else {
+ outputAfromCRTC = 0;
+ outputBfromCRTC = 1;
+ analog_on_A = 0;
+ analog_on_B = 0;
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x44);
+ cr44 = VGA_RD08(par->PCIO, 0x03D5);
+
+ VGA_WR08(par->PCIO, 0x03D5, 3);
+ NVSelectHeadRegisters(par, 1);
+ NVLockUnlock(par, 0);
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x28);
+ slaved_on_B = VGA_RD08(par->PCIO, 0x03D5) & 0x80;
+ if (slaved_on_B) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x33);
+ tvB = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01);
+ }
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x44);
+ VGA_WR08(par->PCIO, 0x03D5, 0);
+ NVSelectHeadRegisters(par, 0);
+ NVLockUnlock(par, 0);
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x28);
+ slaved_on_A = VGA_RD08(par->PCIO, 0x03D5) & 0x80;
+ if (slaved_on_A) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x33);
+ tvA = !(VGA_RD08(par->PCIO, 0x03D5) & 0x01);
+ }
+
+ oldhead = NV_RD32(par->PCRTC0, 0x00000860);
+ NV_WR32(par->PCRTC0, 0x00000860, oldhead | 0x00000010);
+
+ if (nvidia_probe_i2c_connector(info, 1, &edidA))
+ nvidia_probe_of_connector(info, 1, &edidA);
+ if (edidA && !fb_parse_edid(edidA, var)) {
+ printk("nvidiafb: EDID found from BUS1\n");
+ monA = monitorA;
+ fb_edid_to_monspecs(edidA, monA);
+ }
+
+ if (nvidia_probe_i2c_connector(info, 2, &edidB))
+ nvidia_probe_of_connector(info, 2, &edidB);
+ if (edidB && !fb_parse_edid(edidB, var)) {
+ printk("nvidiafb: EDID found from BUS2\n");
+ monB = monitorB;
+ fb_edid_to_monspecs(edidB, monB);
+ }
+
+ if (slaved_on_A && !tvA) {
+ CRTCnumber = 0;
+ FlatPanel = 1;
+ printk("nvidiafb: CRTC 0 is currently programmed for "
+ "DFP\n");
+ } else if (slaved_on_B && !tvB) {
+ CRTCnumber = 1;
+ FlatPanel = 1;
+ printk("nvidiafb: CRTC 1 is currently programmed "
+ "for DFP\n");
+ } else if (analog_on_A) {
+ CRTCnumber = outputAfromCRTC;
+ FlatPanel = 0;
+ printk("nvidiafb: CRTC %i appears to have a "
+ "CRT attached\n", CRTCnumber);
+ } else if (analog_on_B) {
+ CRTCnumber = outputBfromCRTC;
+ FlatPanel = 0;
+ printk("nvidiafb: CRTC %i appears to have a "
+ "CRT attached\n", CRTCnumber);
+ } else if (slaved_on_A) {
+ CRTCnumber = 0;
+ FlatPanel = 1;
+ Television = 1;
+ printk("nvidiafb: CRTC 0 is currently programmed "
+ "for TV\n");
+ } else if (slaved_on_B) {
+ CRTCnumber = 1;
+ FlatPanel = 1;
+ Television = 1;
+ printk("nvidiafb: CRTC 1 is currently programmed for "
+ "TV\n");
+ } else if (monA) {
+ FlatPanel = (monA->input & FB_DISP_DDI) ? 1 : 0;
+ } else if (monB) {
+ FlatPanel = (monB->input & FB_DISP_DDI) ? 1 : 0;
+ }
+
+ if (par->FlatPanel == -1) {
+ if (FlatPanel != -1) {
+ par->FlatPanel = FlatPanel;
+ par->Television = Television;
+ } else {
+ printk("nvidiafb: Unable to detect display "
+ "type...\n");
+ if (mobile) {
+ printk("...On a laptop, assuming "
+ "DFP\n");
+ par->FlatPanel = 1;
+ } else {
+ printk("...Using default of CRT\n");
+ par->FlatPanel = 0;
+ }
+ }
+ } else {
+ printk("nvidiafb: Forcing display type to %s as "
+ "specified\n", par->FlatPanel ? "DFP" : "CRT");
+ }
+
+ if (par->CRTCnumber == -1) {
+ if (CRTCnumber != -1)
+ par->CRTCnumber = CRTCnumber;
+ else {
+ printk("nvidiafb: Unable to detect which "
+ "CRTCNumber...\n");
+ if (par->FlatPanel)
+ par->CRTCnumber = 1;
+ else
+ par->CRTCnumber = 0;
+ printk("...Defaulting to CRTCNumber %i\n",
+ par->CRTCnumber);
+ }
+ } else {
+ printk("nvidiafb: Forcing CRTCNumber %i as "
+ "specified\n", par->CRTCnumber);
+ }
+
+ if (monA) {
+ if (((monA->input & FB_DISP_DDI) &&
+ par->FlatPanel) ||
+ ((!(monA->input & FB_DISP_DDI)) &&
+ !par->FlatPanel)) {
+ if (monB) {
+ fb_destroy_modedb(monB->modedb);
+ monB = NULL;
+ }
+ } else {
+ fb_destroy_modedb(monA->modedb);
+ monA = NULL;
+ }
+ }
+
+ if (monB) {
+ if (((monB->input & FB_DISP_DDI) &&
+ !par->FlatPanel) ||
+ ((!(monB->input & FB_DISP_DDI)) &&
+ par->FlatPanel)) {
+ fb_destroy_modedb(monB->modedb);
+ monB = NULL;
+ } else
+ monA = monB;
+ }
+
+ if (implementation == 0x0110)
+ cr44 = par->CRTCnumber * 0x3;
+
+ NV_WR32(par->PCRTC0, 0x00000860, oldhead);
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x44);
+ VGA_WR08(par->PCIO, 0x03D5, cr44);
+ NVSelectHeadRegisters(par, par->CRTCnumber);
+ }
+
+ printk("nvidiafb: Using %s on CRTC %i\n",
+ par->FlatPanel ? (par->Television ? "TV" : "DFP") : "CRT",
+ par->CRTCnumber);
+
+ if (par->FlatPanel && !par->Television) {
+ par->fpWidth = NV_RD32(par->PRAMDAC, 0x0820) + 1;
+ par->fpHeight = NV_RD32(par->PRAMDAC, 0x0800) + 1;
+ par->fpSyncs = NV_RD32(par->PRAMDAC, 0x0848) & 0x30000033;
+
+ printk("nvidiafb: Panel size is %i x %i\n", par->fpWidth, par->fpHeight);
+ }
+
+ if (monA)
+ info->monspecs = *monA;
+
+ if (!par->FlatPanel || !par->twoHeads)
+ par->FPDither = 0;
+
+ par->LVDS = 0;
+ if (par->FlatPanel && par->twoHeads) {
+ NV_WR32(par->PRAMDAC0, 0x08B0, 0x00010004);
+ if (NV_RD32(par->PRAMDAC0, 0x08b4) & 1)
+ par->LVDS = 1;
+ printk("nvidiafb: Panel is %s\n", par->LVDS ? "LVDS" : "TMDS");
+ }
+
+ kfree(edidA);
+ kfree(edidB);
+done:
+ kfree(var);
+ kfree(monitorA);
+ kfree(monitorB);
+ return err;
+}
diff --git a/drivers/video/fbdev/nvidia/nv_type.h b/drivers/video/fbdev/nvidia/nv_type.h
new file mode 100644
index 000000000..d7a1d4363
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nv_type.h
@@ -0,0 +1,176 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __NV_TYPE_H__
+#define __NV_TYPE_H__
+
+#include <linux/fb.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#include <video/vga.h>
+
+#define NV_ARCH_04 0x04
+#define NV_ARCH_10 0x10
+#define NV_ARCH_20 0x20
+#define NV_ARCH_30 0x30
+#define NV_ARCH_40 0x40
+
+#define BITMASK(t,b) (((unsigned)(1U << (((t)-(b)+1)))-1) << (b))
+#define MASKEXPAND(mask) BITMASK(1?mask,0?mask)
+#define SetBF(mask,value) ((value) << (0?mask))
+#define GetBF(var,mask) (((unsigned)((var) & MASKEXPAND(mask))) >> (0?mask) )
+#define SetBitField(value,from,to) SetBF(to, GetBF(value,from))
+#define SetBit(n) (1<<(n))
+#define Set8Bits(value) ((value)&0xff)
+
+#define V_DBLSCAN 1
+
+typedef struct {
+ int bitsPerPixel;
+ int depth;
+ int displayWidth;
+ int weight;
+} NVFBLayout;
+
+#define NUM_SEQ_REGS 0x05
+#define NUM_CRT_REGS 0x41
+#define NUM_GRC_REGS 0x09
+#define NUM_ATC_REGS 0x15
+
+struct nvidia_par;
+
+struct nvidia_i2c_chan {
+ struct nvidia_par *par;
+ unsigned long ddc_base;
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+};
+
+typedef struct _riva_hw_state {
+ u8 attr[NUM_ATC_REGS];
+ u8 crtc[NUM_CRT_REGS];
+ u8 gra[NUM_GRC_REGS];
+ u8 seq[NUM_SEQ_REGS];
+ u8 misc_output;
+ u32 bpp;
+ u32 width;
+ u32 height;
+ u32 interlace;
+ u32 repaint0;
+ u32 repaint1;
+ u32 screen;
+ u32 scale;
+ u32 dither;
+ u32 extra;
+ u32 fifo;
+ u32 pixel;
+ u32 horiz;
+ u32 arbitration0;
+ u32 arbitration1;
+ u32 pll;
+ u32 pllB;
+ u32 vpll;
+ u32 vpll2;
+ u32 vpllB;
+ u32 vpll2B;
+ u32 pllsel;
+ u32 general;
+ u32 crtcOwner;
+ u32 head;
+ u32 head2;
+ u32 config;
+ u32 cursorConfig;
+ u32 cursor0;
+ u32 cursor1;
+ u32 cursor2;
+ u32 timingH;
+ u32 timingV;
+ u32 displayV;
+ u32 crtcSync;
+ u32 control;
+} RIVA_HW_STATE;
+
+struct riva_regs {
+ RIVA_HW_STATE ext;
+};
+
+struct nvidia_par {
+ RIVA_HW_STATE SavedReg;
+ RIVA_HW_STATE ModeReg;
+ RIVA_HW_STATE initial_state;
+ RIVA_HW_STATE *CurrentState;
+ struct vgastate vgastate;
+ u32 pseudo_palette[16];
+ struct pci_dev *pci_dev;
+ u32 Architecture;
+ u32 CursorStart;
+ int Chipset;
+ unsigned long FbAddress;
+ u8 __iomem *FbStart;
+ u32 FbMapSize;
+ u32 FbUsableSize;
+ u32 ScratchBufferSize;
+ u32 ScratchBufferStart;
+ int FpScale;
+ u32 MinVClockFreqKHz;
+ u32 MaxVClockFreqKHz;
+ u32 CrystalFreqKHz;
+ u32 RamAmountKBytes;
+ u32 IOBase;
+ NVFBLayout CurrentLayout;
+ int cursor_reset;
+ int lockup;
+ int videoKey;
+ int FlatPanel;
+ int FPDither;
+ int Television;
+ int CRTCnumber;
+ int alphaCursor;
+ int twoHeads;
+ int twoStagePLL;
+ int fpScaler;
+ int fpWidth;
+ int fpHeight;
+ int PanelTweak;
+ int paneltweak;
+ int LVDS;
+ int pm_state;
+ int reverse_i2c;
+ u32 crtcSync_read;
+ u32 fpSyncs;
+ u32 dmaPut;
+ u32 dmaCurrent;
+ u32 dmaFree;
+ u32 dmaMax;
+ u32 __iomem *dmaBase;
+ u32 currentRop;
+ int WaitVSyncPossible;
+ int BlendingPossible;
+ u32 paletteEnabled;
+ u32 forceCRTC;
+ u32 open_count;
+ u8 DDCBase;
+ int wc_cookie;
+ struct nvidia_i2c_chan chan[3];
+
+ volatile u32 __iomem *REGS;
+ volatile u32 __iomem *PCRTC0;
+ volatile u32 __iomem *PCRTC;
+ volatile u32 __iomem *PRAMDAC0;
+ volatile u32 __iomem *PFB;
+ volatile u32 __iomem *PFIFO;
+ volatile u32 __iomem *PGRAPH;
+ volatile u32 __iomem *PEXTDEV;
+ volatile u32 __iomem *PTIMER;
+ volatile u32 __iomem *PMC;
+ volatile u32 __iomem *PRAMIN;
+ volatile u32 __iomem *FIFO;
+ volatile u32 __iomem *CURSOR;
+ volatile u8 __iomem *PCIO0;
+ volatile u8 __iomem *PCIO;
+ volatile u8 __iomem *PVIO;
+ volatile u8 __iomem *PDIO0;
+ volatile u8 __iomem *PDIO;
+ volatile u32 __iomem *PRAMDAC;
+};
+
+#endif /* __NV_TYPE_H__ */
diff --git a/drivers/video/fbdev/nvidia/nvidia.c b/drivers/video/fbdev/nvidia/nvidia.c
new file mode 100644
index 000000000..4ef7a5ba4
--- /dev/null
+++ b/drivers/video/fbdev/nvidia/nvidia.c
@@ -0,0 +1,1596 @@
+/*
+ * 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/aperture.h>
+#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>
+#include <linux/string_choices.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_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
+ .class = PCI_BASE_CLASS_DISPLAY << 16,
+ .class_mask = 0xff0000
+ },
+ { }
+};
+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;
+static int backlight = IS_BUILTIN(CONFIG_PMAC_BACKLIGHT);
+
+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, i);
+
+ for (i = 0; i < NUM_GRC_REGS; i++)
+ state->gra[i] = NVReadGr(par, i);
+
+ for (i = 0; i < NUM_SEQ_REGS; i++)
+ state->seq[i] = NVReadSeq(par, i);
+ NVTRACE_LEAVE();
+}
+
+#undef DUMP_REG
+
+static void nvidia_write_regs(struct nvidia_par *par,
+ struct _riva_hw_state *state)
+{
+ int i;
+
+ NVTRACE_ENTER();
+
+ NVLoadStateExt(par, state);
+
+ NVWriteMiscOut(par, state->misc_output);
+
+ for (i = 1; i < NUM_SEQ_REGS; i++) {
+#ifdef DUMP_REG
+ printk(" SEQ[%02x] = %08x\n", i, state->seq[i]);
+#endif
+ NVWriteSeq(par, i, state->seq[i]);
+ }
+
+ /* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 of CRTC[17] */
+ NVWriteCrtc(par, 0x11, state->crtc[0x11] & ~0x80);
+
+ for (i = 0; i < NUM_CRT_REGS; i++) {
+ switch (i) {
+ case 0x19:
+ case 0x20 ... 0x40:
+ break;
+ default:
+#ifdef DUMP_REG
+ printk("CRTC[%02x] = %08x\n", i, state->crtc[i]);
+#endif
+ NVWriteCrtc(par, i, state->crtc[i]);
+ }
+ }
+
+ for (i = 0; i < NUM_GRC_REGS; i++) {
+#ifdef DUMP_REG
+ printk(" GRA[%02x] = %08x\n", i, state->gra[i]);
+#endif
+ NVWriteGr(par, i, state->gra[i]);
+ }
+
+ for (i = 0; i < NUM_ATC_REGS; i++) {
+#ifdef DUMP_REG
+ printk("ATTR[%02x] = %08x\n", i, state->attr[i]);
+#endif
+ NVWriteAttr(par, i, state->attr[i]);
+ }
+
+ NVTRACE_LEAVE();
+}
+
+static int nvidia_calc_regs(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ struct _riva_hw_state *state = &par->ModeReg;
+ int i, depth = fb_get_color_depth(&info->var, &info->fix);
+ int h_display = info->var.xres / 8 - 1;
+ int h_start = (info->var.xres + info->var.right_margin) / 8 - 1;
+ int h_end = (info->var.xres + info->var.right_margin +
+ info->var.hsync_len) / 8 - 1;
+ int h_total = (info->var.xres + info->var.right_margin +
+ info->var.hsync_len + info->var.left_margin) / 8 - 5;
+ int h_blank_s = h_display;
+ int h_blank_e = h_total + 4;
+ int v_display = info->var.yres - 1;
+ int v_start = info->var.yres + info->var.lower_margin - 1;
+ int v_end = (info->var.yres + info->var.lower_margin +
+ info->var.vsync_len) - 1;
+ int v_total = (info->var.yres + info->var.lower_margin +
+ info->var.vsync_len + info->var.upper_margin) - 2;
+ int v_blank_s = v_display;
+ int v_blank_e = v_total + 1;
+
+ /*
+ * Set all CRTC values.
+ */
+
+ if (info->var.vmode & FB_VMODE_INTERLACED)
+ v_total |= 1;
+
+ if (par->FlatPanel == 1) {
+ v_start = v_total - 3;
+ v_end = v_total - 2;
+ v_blank_s = v_start;
+ h_start = h_total - 5;
+ h_end = h_total - 2;
+ h_blank_e = h_total + 4;
+ }
+
+ state->crtc[0x0] = Set8Bits(h_total);
+ state->crtc[0x1] = Set8Bits(h_display);
+ state->crtc[0x2] = Set8Bits(h_blank_s);
+ state->crtc[0x3] = SetBitField(h_blank_e, 4: 0, 4:0)
+ | SetBit(7);
+ state->crtc[0x4] = Set8Bits(h_start);
+ state->crtc[0x5] = SetBitField(h_blank_e, 5: 5, 7:7)
+ | SetBitField(h_end, 4: 0, 4:0);
+ state->crtc[0x6] = SetBitField(v_total, 7: 0, 7:0);
+ state->crtc[0x7] = SetBitField(v_total, 8: 8, 0:0)
+ | SetBitField(v_display, 8: 8, 1:1)
+ | SetBitField(v_start, 8: 8, 2:2)
+ | SetBitField(v_blank_s, 8: 8, 3:3)
+ | SetBit(4)
+ | SetBitField(v_total, 9: 9, 5:5)
+ | SetBitField(v_display, 9: 9, 6:6)
+ | SetBitField(v_start, 9: 9, 7:7);
+ state->crtc[0x9] = SetBitField(v_blank_s, 9: 9, 5:5)
+ | SetBit(6)
+ | ((info->var.vmode & FB_VMODE_DOUBLE) ? 0x80 : 0x00);
+ state->crtc[0x10] = Set8Bits(v_start);
+ state->crtc[0x11] = SetBitField(v_end, 3: 0, 3:0) | SetBit(5);
+ state->crtc[0x12] = Set8Bits(v_display);
+ state->crtc[0x13] = ((info->var.xres_virtual / 8) *
+ (info->var.bits_per_pixel / 8));
+ state->crtc[0x15] = Set8Bits(v_blank_s);
+ state->crtc[0x16] = Set8Bits(v_blank_e);
+
+ state->attr[0x10] = 0x01;
+
+ if (par->Television)
+ state->attr[0x11] = 0x00;
+
+ state->screen = SetBitField(h_blank_e, 6: 6, 4:4)
+ | SetBitField(v_blank_s, 10: 10, 3:3)
+ | SetBitField(v_start, 10: 10, 2:2)
+ | SetBitField(v_display, 10: 10, 1:1)
+ | SetBitField(v_total, 10: 10, 0:0);
+
+ state->horiz = SetBitField(h_total, 8: 8, 0:0)
+ | SetBitField(h_display, 8: 8, 1:1)
+ | SetBitField(h_blank_s, 8: 8, 2:2)
+ | SetBitField(h_start, 8: 8, 3:3);
+
+ state->extra = SetBitField(v_total, 11: 11, 0:0)
+ | SetBitField(v_display, 11: 11, 2:2)
+ | SetBitField(v_start, 11: 11, 4:4)
+ | SetBitField(v_blank_s, 11: 11, 6:6);
+
+ if (info->var.vmode & FB_VMODE_INTERLACED) {
+ h_total = (h_total >> 1) & ~1;
+ state->interlace = Set8Bits(h_total);
+ state->horiz |= SetBitField(h_total, 8: 8, 4:4);
+ } else {
+ state->interlace = 0xff; /* interlace off */
+ }
+
+ /*
+ * Calculate the extended registers.
+ */
+
+ if (depth < 24)
+ i = depth;
+ else
+ i = 32;
+
+ if (par->Architecture >= NV_ARCH_10)
+ par->CURSOR = (volatile u32 __iomem *)(info->screen_base +
+ par->CursorStart);
+
+ if (info->var.sync & FB_SYNC_HOR_HIGH_ACT)
+ state->misc_output &= ~0x40;
+ else
+ state->misc_output |= 0x40;
+ if (info->var.sync & FB_SYNC_VERT_HIGH_ACT)
+ state->misc_output &= ~0x80;
+ else
+ state->misc_output |= 0x80;
+
+ NVCalcStateExt(par, state, i, info->var.xres_virtual,
+ info->var.xres, info->var.yres_virtual,
+ 1000000000 / info->var.pixclock, info->var.vmode);
+
+ state->scale = NV_RD32(par->PRAMDAC, 0x00000848) & 0xfff000ff;
+ if (par->FlatPanel == 1) {
+ state->pixel |= (1 << 7);
+
+ if (!par->fpScaler || (par->fpWidth <= info->var.xres)
+ || (par->fpHeight <= info->var.yres)) {
+ state->scale |= (1 << 8);
+ }
+
+ if (!par->crtcSync_read) {
+ state->crtcSync = NV_RD32(par->PRAMDAC, 0x0828);
+ par->crtcSync_read = 1;
+ }
+
+ par->PanelTweak = nvidia_panel_tweak(par, state);
+ }
+
+ state->vpll = state->pll;
+ state->vpll2 = state->pll;
+ state->vpllB = state->pllB;
+ state->vpll2B = state->pllB;
+
+ VGA_WR08(par->PCIO, 0x03D4, 0x1C);
+ state->fifo = VGA_RD08(par->PCIO, 0x03D5) & ~(1<<5);
+
+ if (par->CRTCnumber) {
+ state->head = NV_RD32(par->PCRTC0, 0x00000860) & ~0x00001000;
+ state->head2 = NV_RD32(par->PCRTC0, 0x00002860) | 0x00001000;
+ state->crtcOwner = 3;
+ state->pllsel |= 0x20000800;
+ state->vpll = NV_RD32(par->PRAMDAC0, 0x00000508);
+ if (par->twoStagePLL)
+ state->vpllB = NV_RD32(par->PRAMDAC0, 0x00000578);
+ } else if (par->twoHeads) {
+ state->head = NV_RD32(par->PCRTC0, 0x00000860) | 0x00001000;
+ state->head2 = NV_RD32(par->PCRTC0, 0x00002860) & ~0x00001000;
+ state->crtcOwner = 0;
+ state->vpll2 = NV_RD32(par->PRAMDAC0, 0x0520);
+ if (par->twoStagePLL)
+ state->vpll2B = NV_RD32(par->PRAMDAC0, 0x057C);
+ }
+
+ state->cursorConfig = 0x00000100;
+
+ if (info->var.vmode & FB_VMODE_DOUBLE)
+ state->cursorConfig |= (1 << 4);
+
+ if (par->alphaCursor) {
+ if ((par->Chipset & 0x0ff0) != 0x0110)
+ state->cursorConfig |= 0x04011000;
+ else
+ state->cursorConfig |= 0x14011000;
+ state->general |= (1 << 29);
+ } else
+ state->cursorConfig |= 0x02000000;
+
+ if (par->twoHeads) {
+ if ((par->Chipset & 0x0ff0) == 0x0110) {
+ state->dither = NV_RD32(par->PRAMDAC, 0x0528) &
+ ~0x00010000;
+ if (par->FPDither)
+ state->dither |= 0x00010000;
+ } else {
+ state->dither = NV_RD32(par->PRAMDAC, 0x083C) & ~1;
+ if (par->FPDither)
+ state->dither |= 1;
+ }
+ }
+
+ state->timingH = 0;
+ state->timingV = 0;
+ state->displayV = info->var.xres;
+
+ return 0;
+}
+
+static void nvidia_init_vga(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ struct _riva_hw_state *state = &par->ModeReg;
+ int i;
+
+ for (i = 0; i < 0x10; i++)
+ state->attr[i] = i;
+ state->attr[0x10] = 0x41;
+ state->attr[0x11] = 0xff;
+ state->attr[0x12] = 0x0f;
+ state->attr[0x13] = 0x00;
+ state->attr[0x14] = 0x00;
+
+ memset(state->crtc, 0x00, NUM_CRT_REGS);
+ state->crtc[0x0a] = 0x20;
+ state->crtc[0x17] = 0xe3;
+ state->crtc[0x18] = 0xff;
+ state->crtc[0x28] = 0x40;
+
+ memset(state->gra, 0x00, NUM_GRC_REGS);
+ state->gra[0x05] = 0x40;
+ state->gra[0x06] = 0x05;
+ state->gra[0x07] = 0x0f;
+ state->gra[0x08] = 0xff;
+
+ state->seq[0x00] = 0x03;
+ state->seq[0x01] = 0x01;
+ state->seq[0x02] = 0x0f;
+ state->seq[0x03] = 0x00;
+ state->seq[0x04] = 0x0e;
+
+ state->misc_output = 0xeb;
+}
+
+static int nvidiafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ struct nvidia_par *par = info->par;
+ u8 data[MAX_CURS * MAX_CURS / 8];
+ int i, set = cursor->set;
+ u16 fg, bg;
+
+ if (cursor->image.width > MAX_CURS || cursor->image.height > MAX_CURS)
+ return -ENXIO;
+
+ NVShowHideCursor(par, 0);
+
+ if (par->cursor_reset) {
+ set = FB_CUR_SETALL;
+ par->cursor_reset = 0;
+ }
+
+ if (set & FB_CUR_SETSIZE)
+ memset_io(par->CURSOR, 0, MAX_CURS * MAX_CURS * 2);
+
+ if (set & FB_CUR_SETPOS) {
+ u32 xx, yy, temp;
+
+ yy = cursor->image.dy - info->var.yoffset;
+ xx = cursor->image.dx - info->var.xoffset;
+ temp = xx & 0xFFFF;
+ temp |= yy << 16;
+
+ NV_WR32(par->PRAMDAC, 0x0000300, temp);
+ }
+
+ if (set & (FB_CUR_SETSHAPE | FB_CUR_SETCMAP | FB_CUR_SETIMAGE)) {
+ u32 bg_idx = cursor->image.bg_color;
+ u32 fg_idx = cursor->image.fg_color;
+ u32 s_pitch = (cursor->image.width + 7) >> 3;
+ u32 d_pitch = MAX_CURS / 8;
+ u8 *dat = (u8 *) cursor->image.data;
+ u8 *msk = (u8 *) cursor->mask;
+ u8 *src;
+
+ src = kmalloc_array(s_pitch, cursor->image.height, GFP_ATOMIC);
+
+ if (src) {
+ switch (cursor->rop) {
+ case ROP_XOR:
+ for (i = 0; i < s_pitch * cursor->image.height; i++)
+ src[i] = dat[i] ^ msk[i];
+ break;
+ case ROP_COPY:
+ default:
+ for (i = 0; i < s_pitch * cursor->image.height; i++)
+ src[i] = dat[i] & msk[i];
+ break;
+ }
+
+ fb_pad_aligned_buffer(data, d_pitch, src, s_pitch,
+ 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) | 1 << 15;
+
+ fg = ((info->cmap.red[fg_idx] & 0xf8) << 7) |
+ ((info->cmap.green[fg_idx] & 0xf8) << 2) |
+ ((info->cmap.blue[fg_idx] & 0xf8) >> 3) | 1 << 15;
+
+ NVLockUnlock(par, 0);
+
+ nvidiafb_load_cursor_image(par, data, bg, fg,
+ cursor->image.width,
+ cursor->image.height);
+ kfree(src);
+ }
+ }
+
+ if (cursor->enable)
+ NVShowHideCursor(par, 1);
+
+ return 0;
+}
+
+static struct fb_ops nvidia_fb_ops;
+
+static int nvidiafb_set_par(struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+
+ NVTRACE_ENTER();
+
+ NVLockUnlock(par, 1);
+ if (!par->FlatPanel || !par->twoHeads)
+ par->FPDither = 0;
+
+ if (par->FPDither < 0) {
+ if ((par->Chipset & 0x0ff0) == 0x0110)
+ par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x0528)
+ & 0x00010000);
+ else
+ par->FPDither = !!(NV_RD32(par->PRAMDAC, 0x083C) & 1);
+ printk(KERN_INFO PFX "Flat panel dithering %s\n",
+ str_enabled_disabled(par->FPDither));
+ }
+
+ info->fix.visual = (info->var.bits_per_pixel == 8) ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
+
+ nvidia_init_vga(info);
+ nvidia_calc_regs(info);
+
+ NVLockUnlock(par, 0);
+ if (par->twoHeads) {
+ VGA_WR08(par->PCIO, 0x03D4, 0x44);
+ VGA_WR08(par->PCIO, 0x03D5, par->ModeReg.crtcOwner);
+ NVLockUnlock(par, 0);
+ }
+
+ nvidia_screen_off(par, 1);
+
+ nvidia_write_regs(par, &par->ModeReg);
+ NVSetStartAddress(par, 0);
+
+#if defined (__BIG_ENDIAN)
+ /* turn on LFB swapping */
+ {
+ unsigned char tmp;
+
+ VGA_WR08(par->PCIO, 0x3d4, 0x46);
+ tmp = VGA_RD08(par->PCIO, 0x3d5);
+ tmp |= (1 << 7);
+ VGA_WR08(par->PCIO, 0x3d5, tmp);
+ }
+#endif
+
+ info->fix.line_length = (info->var.xres_virtual *
+ info->var.bits_per_pixel) >> 3;
+ if (info->var.accel_flags) {
+ nvidia_fb_ops.fb_imageblit = nvidiafb_imageblit;
+ nvidia_fb_ops.fb_fillrect = nvidiafb_fillrect;
+ nvidia_fb_ops.fb_copyarea = nvidiafb_copyarea;
+ nvidia_fb_ops.fb_sync = nvidiafb_sync;
+ info->pixmap.scan_align = 4;
+ info->flags &= ~FBINFO_HWACCEL_DISABLED;
+ info->flags |= FBINFO_READS_FAST;
+ NVResetGraphics(info);
+ } else {
+ nvidia_fb_ops.fb_imageblit = cfb_imageblit;
+ nvidia_fb_ops.fb_fillrect = cfb_fillrect;
+ nvidia_fb_ops.fb_copyarea = cfb_copyarea;
+ nvidia_fb_ops.fb_sync = NULL;
+ info->pixmap.scan_align = 1;
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+ info->flags &= ~FBINFO_READS_FAST;
+ }
+
+ par->cursor_reset = 1;
+
+ nvidia_screen_off(par, 0);
+
+#ifdef CONFIG_BOOTX_TEXT
+ /* Update debug text engine */
+ btext_update_display(info->fix.smem_start,
+ info->var.xres, info->var.yres,
+ info->var.bits_per_pixel, info->fix.line_length);
+#endif
+
+ NVLockUnlock(par, 0);
+ NVTRACE_LEAVE();
+ return 0;
+}
+
+static int nvidiafb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ int i;
+
+ NVTRACE_ENTER();
+ if (regno >= (1 << info->var.green.length))
+ return -EINVAL;
+
+ if (info->var.grayscale) {
+ /* gray = 0.30*R + 0.59*G + 0.11*B */
+ red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8;
+ }
+
+ if (regno < 16 && info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
+ ((u32 *) info->pseudo_palette)[regno] =
+ (regno << info->var.red.offset) |
+ (regno << info->var.green.offset) |
+ (regno << info->var.blue.offset);
+ }
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ /* "transparent" stuff is completely ignored. */
+ nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
+ break;
+ case 16:
+ if (info->var.green.length == 5) {
+ for (i = 0; i < 8; i++) {
+ nvidia_write_clut(par, regno * 8 + i, red >> 8,
+ green >> 8, blue >> 8);
+ }
+ } else {
+ u8 r, g, b;
+
+ if (regno < 32) {
+ for (i = 0; i < 8; i++) {
+ nvidia_write_clut(par, regno * 8 + i,
+ red >> 8, green >> 8,
+ blue >> 8);
+ }
+ }
+
+ nvidia_read_clut(par, regno * 4, &r, &g, &b);
+
+ for (i = 0; i < 4; i++)
+ nvidia_write_clut(par, regno * 4 + i, r,
+ green >> 8, b);
+ }
+ break;
+ case 32:
+ nvidia_write_clut(par, regno, red >> 8, green >> 8, blue >> 8);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+
+ NVTRACE_LEAVE();
+ return 0;
+}
+
+static int nvidiafb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ int memlen, vramlen, mode_valid = 0;
+ int pitch, err = 0;
+
+ NVTRACE_ENTER();
+ if (!var->pixclock)
+ return -EINVAL;
+
+ var->transp.offset = 0;
+ var->transp.length = 0;
+
+ var->xres &= ~7;
+
+ if (var->bits_per_pixel <= 8)
+ var->bits_per_pixel = 8;
+ else if (var->bits_per_pixel <= 16)
+ var->bits_per_pixel = 16;
+ else
+ var->bits_per_pixel = 32;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16:
+ var->green.length = (var->green.length < 6) ? 5 : 6;
+ var->red.length = 5;
+ var->blue.length = 5;
+ var->transp.length = 6 - var->green.length;
+ var->blue.offset = 0;
+ var->green.offset = 5;
+ var->red.offset = 5 + var->green.length;
+ var->transp.offset = (5 + var->red.offset) & 15;
+ break;
+ case 32: /* RGBA 8888 */
+ var->red.offset = 16;
+ var->red.length = 8;
+ var->green.offset = 8;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.length = 8;
+ var->transp.offset = 24;
+ break;
+ }
+
+ var->red.msb_right = 0;
+ var->green.msb_right = 0;
+ var->blue.msb_right = 0;
+ var->transp.msb_right = 0;
+
+ if (!info->monspecs.hfmax || !info->monspecs.vfmax ||
+ !info->monspecs.dclkmax || !fb_validate_mode(var, info))
+ 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) {
+ const struct fb_videomode *mode;
+
+ mode = fb_find_best_mode(var, &info->modelist);
+ if (mode) {
+ fb_videomode_to_var(var, mode);
+ mode_valid = 1;
+ }
+ }
+
+ if (!mode_valid && info->monspecs.modedb_len)
+ return -EINVAL;
+
+ /*
+ * If we're on a flat panel, check if the mode is outside of the
+ * panel dimensions. If so, cap it and try for the next best mode
+ * before bailing out.
+ */
+ if (par->fpWidth && par->fpHeight && (par->fpWidth < var->xres ||
+ par->fpHeight < var->yres)) {
+ const struct fb_videomode *mode;
+
+ var->xres = par->fpWidth;
+ var->yres = par->fpHeight;
+
+ mode = fb_find_best_mode(var, &info->modelist);
+ if (!mode) {
+ printk(KERN_ERR PFX "mode out of range of flat "
+ "panel dimensions\n");
+ return -EINVAL;
+ }
+
+ fb_videomode_to_var(var, mode);
+ }
+
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ if (var->xres_virtual < var->xres)
+ var->xres_virtual = var->xres;
+
+ var->xres_virtual = (var->xres_virtual + 63) & ~63;
+
+ vramlen = info->screen_size;
+ pitch = ((var->xres_virtual * var->bits_per_pixel) + 7) / 8;
+ memlen = pitch * var->yres_virtual;
+
+ if (memlen > vramlen) {
+ var->yres_virtual = vramlen / pitch;
+
+ if (var->yres_virtual < var->yres) {
+ var->yres_virtual = var->yres;
+ var->xres_virtual = vramlen / var->yres_virtual;
+ var->xres_virtual /= var->bits_per_pixel / 8;
+ var->xres_virtual &= ~63;
+ pitch = (var->xres_virtual *
+ var->bits_per_pixel + 7) / 8;
+ memlen = pitch * var->yres;
+
+ if (var->xres_virtual < var->xres) {
+ printk("nvidiafb: required video memory, "
+ "%d bytes, for %dx%d-%d (virtual) "
+ "is out of range\n",
+ memlen, var->xres_virtual,
+ var->yres_virtual, var->bits_per_pixel);
+ err = -ENOMEM;
+ }
+ }
+ }
+
+ if (var->accel_flags) {
+ if (var->yres_virtual > 0x7fff)
+ var->yres_virtual = 0x7fff;
+ if (var->xres_virtual > 0x7fff)
+ var->xres_virtual = 0x7fff;
+ }
+
+ var->xres_virtual &= ~63;
+
+ NVTRACE_LEAVE();
+
+ return err;
+}
+
+static int nvidiafb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ u32 total;
+
+ total = var->yoffset * info->fix.line_length + var->xoffset;
+
+ NVSetStartAddress(par, total);
+
+ return 0;
+}
+
+static int nvidiafb_blank(int blank, struct fb_info *info)
+{
+ struct nvidia_par *par = info->par;
+ unsigned char tmp, vesa;
+
+ tmp = NVReadSeq(par, 0x01) & ~0x20; /* screen on/off */
+ vesa = NVReadCrtc(par, 0x1a) & ~0xc0; /* sync on/off */
+
+ NVTRACE_ENTER();
+
+ if (blank)
+ tmp |= 0x20;
+
+ switch (blank) {
+ case FB_BLANK_UNBLANK:
+ case FB_BLANK_NORMAL:
+ break;
+ case FB_BLANK_VSYNC_SUSPEND:
+ vesa |= 0x80;
+ break;
+ case FB_BLANK_HSYNC_SUSPEND:
+ vesa |= 0x40;
+ break;
+ case FB_BLANK_POWERDOWN:
+ vesa |= 0xc0;
+ break;
+ }
+
+ NVWriteSeq(par, 0x01, tmp);
+ NVWriteCrtc(par, 0x1a, vesa);
+
+ NVTRACE_LEAVE();
+
+ return 0;
+}
+
+/*
+ * Because the VGA registers are not mapped linearly in its MMIO space,
+ * restrict VGA register saving and restore to x86 only, where legacy VGA IO
+ * access is legal. Consequently, we must also check if the device is the
+ * primary display.
+ */
+#ifdef CONFIG_X86
+static void save_vga_x86(struct nvidia_par *par)
+{
+ struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
+
+ if (res && res->flags & IORESOURCE_ROM_SHADOW) {
+ memset(&par->vgastate, 0, sizeof(par->vgastate));
+ par->vgastate.flags = VGA_SAVE_MODE | VGA_SAVE_FONTS |
+ VGA_SAVE_CMAP;
+ save_vga(&par->vgastate);
+ }
+}
+
+static void restore_vga_x86(struct nvidia_par *par)
+{
+ struct resource *res= &par->pci_dev->resource[PCI_ROM_RESOURCE];
+
+ if (res && res->flags & IORESOURCE_ROM_SHADOW)
+ restore_vga(&par->vgastate);
+}
+#else
+#define save_vga_x86(x) do {} while (0)
+#define restore_vga_x86(x) do {} while (0)
+#endif /* X86 */
+
+static int nvidiafb_open(struct fb_info *info, int user)
+{
+ struct nvidia_par *par = info->par;
+
+ if (!par->open_count) {
+ save_vga_x86(par);
+ nvidia_save_vga(par, &par->initial_state);
+ }
+
+ par->open_count++;
+ return 0;
+}
+
+static int nvidiafb_release(struct fb_info *info, int user)
+{
+ struct nvidia_par *par = info->par;
+ int err = 0;
+
+ if (!par->open_count) {
+ err = -EINVAL;
+ goto done;
+ }
+
+ if (par->open_count == 1) {
+ nvidia_write_regs(par, &par->initial_state);
+ restore_vga_x86(par);
+ }
+
+ par->open_count--;
+done:
+ return err;
+}
+
+static struct fb_ops nvidia_fb_ops = {
+ .owner = THIS_MODULE,
+ .fb_open = nvidiafb_open,
+ .fb_release = nvidiafb_release,
+ __FB_DEFAULT_IOMEM_OPS_RDWR,
+ .fb_check_var = nvidiafb_check_var,
+ .fb_set_par = nvidiafb_set_par,
+ .fb_setcolreg = nvidiafb_setcolreg,
+ .fb_pan_display = nvidiafb_pan_display,
+ .fb_blank = nvidiafb_blank,
+ .fb_fillrect = nvidiafb_fillrect,
+ .fb_copyarea = nvidiafb_copyarea,
+ .fb_imageblit = nvidiafb_imageblit,
+ .fb_cursor = nvidiafb_cursor,
+ .fb_sync = nvidiafb_sync,
+ __FB_DEFAULT_IOMEM_OPS_MMAP,
+};
+
+static int nvidiafb_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct nvidia_par *par = info->par;
+
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ console_lock();
+ par->pm_state = mesg.event;
+
+ if (mesg.event & PM_EVENT_SLEEP) {
+ fb_set_suspend(info, 1);
+ nvidiafb_blank(FB_BLANK_POWERDOWN, info);
+ nvidia_write_regs(par, &par->SavedReg);
+ }
+ dev->power.power_state = mesg;
+
+ console_unlock();
+ return 0;
+}
+
+static int __maybe_unused nvidiafb_suspend(struct device *dev)
+{
+ return nvidiafb_suspend_late(dev, PMSG_SUSPEND);
+}
+
+static int __maybe_unused nvidiafb_hibernate(struct device *dev)
+{
+ return nvidiafb_suspend_late(dev, PMSG_HIBERNATE);
+}
+
+static int __maybe_unused nvidiafb_freeze(struct device *dev)
+{
+ return nvidiafb_suspend_late(dev, PMSG_FREEZE);
+}
+
+static int __maybe_unused nvidiafb_resume(struct device *dev)
+{
+ struct fb_info *info = dev_get_drvdata(dev);
+ struct nvidia_par *par = info->par;
+
+ console_lock();
+
+ par->pm_state = PM_EVENT_ON;
+ nvidiafb_set_par(info);
+ fb_set_suspend (info, 0);
+ nvidiafb_blank(FB_BLANK_UNBLANK, info);
+
+ console_unlock();
+ return 0;
+}
+
+static const struct dev_pm_ops nvidiafb_pm_ops = {
+#ifdef CONFIG_PM_SLEEP
+ .suspend = nvidiafb_suspend,
+ .resume = nvidiafb_resume,
+ .freeze = nvidiafb_freeze,
+ .thaw = nvidiafb_resume,
+ .poweroff = nvidiafb_hibernate,
+ .restore = nvidiafb_resume,
+#endif /* CONFIG_PM_SLEEP */
+};
+
+static int nvidia_set_fbinfo(struct fb_info *info)
+{
+ struct fb_monspecs *specs = &info->monspecs;
+ struct fb_videomode modedb;
+ struct nvidia_par *par = info->par;
+ int lpitch;
+
+ NVTRACE_ENTER();
+ info->flags =
+ FBINFO_HWACCEL_IMAGEBLIT
+ | FBINFO_HWACCEL_FILLRECT
+ | FBINFO_HWACCEL_COPYAREA
+ | FBINFO_HWACCEL_YPAN;
+
+ fb_videomode_to_modelist(info->monspecs.modedb,
+ info->monspecs.modedb_len, &info->modelist);
+ fb_var_to_videomode(&modedb, &nvidiafb_default_var);
+
+ switch (bpp) {
+ case 0 ... 8:
+ bpp = 8;
+ break;
+ case 9 ... 16:
+ bpp = 16;
+ break;
+ default:
+ bpp = 32;
+ break;
+ }
+
+ if (specs->modedb != NULL) {
+ const struct fb_videomode *mode;
+
+ mode = fb_find_best_display(specs, &info->modelist);
+ fb_videomode_to_var(&nvidiafb_default_var, mode);
+ nvidiafb_default_var.bits_per_pixel = bpp;
+ } else if (par->fpWidth && par->fpHeight) {
+ char buf[16];
+
+ memset(buf, 0, 16);
+ snprintf(buf, 15, "%dx%dMR", par->fpWidth, par->fpHeight);
+ fb_find_mode(&nvidiafb_default_var, info, buf, specs->modedb,
+ specs->modedb_len, &modedb, bpp);
+ }
+
+ if (mode_option)
+ fb_find_mode(&nvidiafb_default_var, info, mode_option,
+ specs->modedb, specs->modedb_len, &modedb, bpp);
+
+ info->var = nvidiafb_default_var;
+ info->fix.visual = (info->var.bits_per_pixel == 8) ?
+ FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_DIRECTCOLOR;
+ info->pseudo_palette = par->pseudo_palette;
+ fb_alloc_cmap(&info->cmap, 256, 0);
+ fb_destroy_modedb(info->monspecs.modedb);
+ info->monspecs.modedb = NULL;
+
+ /* maximize virtual vertical length */
+ lpitch = info->var.xres_virtual *
+ ((info->var.bits_per_pixel + 7) >> 3);
+ info->var.yres_virtual = info->screen_size / lpitch;
+
+ info->pixmap.scan_align = 4;
+ info->pixmap.buf_align = 4;
+ info->pixmap.access_align = 32;
+ info->pixmap.size = 8 * 1024;
+ info->pixmap.flags = FB_PIXMAP_SYSTEM;
+
+ if (!hwcur)
+ nvidia_fb_ops.fb_cursor = NULL;
+
+ info->var.accel_flags = (!noaccel);
+
+ switch (par->Architecture) {
+ case NV_ARCH_04:
+ info->fix.accel = FB_ACCEL_NV4;
+ break;
+ case NV_ARCH_10:
+ info->fix.accel = FB_ACCEL_NV_10;
+ break;
+ case NV_ARCH_20:
+ info->fix.accel = FB_ACCEL_NV_20;
+ break;
+ case NV_ARCH_30:
+ info->fix.accel = FB_ACCEL_NV_30;
+ break;
+ case NV_ARCH_40:
+ info->fix.accel = FB_ACCEL_NV_40;
+ break;
+ }
+
+ NVTRACE_LEAVE();
+
+ return nvidiafb_check_var(&info->var, info);
+}
+
+static u32 nvidia_get_chipset(struct pci_dev *pci_dev,
+ volatile u32 __iomem *REGS)
+{
+ u32 id = (pci_dev->vendor << 16) | pci_dev->device;
+
+ printk(KERN_INFO PFX "Device ID: %x \n", id);
+
+ if ((id & 0xfff0) == 0x00f0 ||
+ (id & 0xfff0) == 0x02e0) {
+ /* pci-e */
+ id = NV_RD32(REGS, 0x1800);
+
+ if ((id & 0x0000ffff) == 0x000010DE)
+ id = 0x10DE0000 | (id >> 16);
+ else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
+ id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
+ ((id >> 8) & 0x000000ff);
+ printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
+ }
+
+ return id;
+}
+
+static u32 nvidia_get_arch(u32 Chipset)
+{
+ u32 arch = 0;
+
+ switch (Chipset & 0x0ff0) {
+ case 0x0100: /* GeForce 256 */
+ case 0x0110: /* GeForce2 MX */
+ case 0x0150: /* GeForce2 */
+ case 0x0170: /* GeForce4 MX */
+ case 0x0180: /* GeForce4 MX (8x AGP) */
+ case 0x01A0: /* nForce */
+ case 0x01F0: /* nForce2 */
+ arch = NV_ARCH_10;
+ break;
+ case 0x0200: /* GeForce3 */
+ case 0x0250: /* GeForce4 Ti */
+ case 0x0280: /* GeForce4 Ti (8x AGP) */
+ arch = NV_ARCH_20;
+ break;
+ case 0x0300: /* GeForceFX 5800 */
+ case 0x0310: /* GeForceFX 5600 */
+ case 0x0320: /* GeForceFX 5200 */
+ case 0x0330: /* GeForceFX 5900 */
+ case 0x0340: /* GeForceFX 5700 */
+ arch = NV_ARCH_30;
+ break;
+ case 0x0040: /* GeForce 6800 */
+ case 0x00C0: /* GeForce 6800 */
+ case 0x0120: /* GeForce 6800 */
+ case 0x0140: /* GeForce 6600 */
+ case 0x0160: /* GeForce 6200 */
+ case 0x01D0: /* GeForce 7200, 7300, 7400 */
+ case 0x0090: /* GeForce 7800 */
+ case 0x0210: /* GeForce 6800 */
+ case 0x0220: /* GeForce 6200 */
+ case 0x0240: /* GeForce 6100 */
+ case 0x0290: /* GeForce 7900 */
+ case 0x0390: /* GeForce 7600 */
+ case 0x03D0:
+ arch = NV_ARCH_40;
+ break;
+ case 0x0020: /* TNT, TNT2 */
+ arch = NV_ARCH_04;
+ break;
+ default: /* unknown architecture */
+ break;
+ }
+
+ return arch;
+}
+
+static int nvidiafb_probe(struct pci_dev *pd, const struct pci_device_id *ent)
+{
+ struct nvidia_par *par;
+ struct fb_info *info;
+ unsigned short cmd;
+ int ret;
+ volatile u32 __iomem *REGS;
+ int Chipset;
+ u32 Architecture;
+
+ NVTRACE_ENTER();
+ assert(pd != NULL);
+
+ if (pcim_enable_device(pd)) {
+ printk(KERN_ERR PFX "cannot enable PCI device\n");
+ return -ENODEV;
+ }
+
+ /* enable IO and mem if not already done */
+ pci_read_config_word(pd, PCI_COMMAND, &cmd);
+ cmd |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY);
+ pci_write_config_word(pd, PCI_COMMAND, cmd);
+
+ nvidiafb_fix.mmio_start = pci_resource_start(pd, 0);
+ nvidiafb_fix.mmio_len = pci_resource_len(pd, 0);
+
+ REGS = devm_ioremap(&pd->dev, nvidiafb_fix.mmio_start, nvidiafb_fix.mmio_len);
+ if (!REGS) {
+ printk(KERN_ERR PFX "cannot ioremap MMIO base\n");
+ return -ENODEV;
+ }
+
+ Chipset = nvidia_get_chipset(pd, REGS);
+ Architecture = nvidia_get_arch(Chipset);
+ if (Architecture == 0) {
+ printk(KERN_ERR PFX "unknown NV_ARCH\n");
+ goto err_out;
+ }
+
+ ret = aperture_remove_conflicting_pci_devices(pd, "nvidiafb");
+ if (ret)
+ goto err_out;
+
+ info = framebuffer_alloc(sizeof(struct nvidia_par), &pd->dev);
+ if (!info)
+ goto err_out;
+
+ par = info->par;
+ par->pci_dev = pd;
+ info->pixmap.addr = kzalloc(8 * 1024, GFP_KERNEL);
+
+ if (info->pixmap.addr == NULL)
+ goto err_out_kfree;
+
+ if (pcim_request_all_regions(pd, "nvidiafb")) {
+ printk(KERN_ERR PFX "cannot request PCI regions\n");
+ goto err_out_enable;
+ }
+
+ par->FlatPanel = flatpanel;
+ if (flatpanel == 1)
+ printk(KERN_INFO PFX "flatpanel support enabled\n");
+ par->FPDither = fpdither;
+
+ par->CRTCnumber = forceCRTC;
+ par->FpScale = (!noscale);
+ par->paneltweak = paneltweak;
+ par->reverse_i2c = reverse_i2c;
+
+ nvidiafb_fix.smem_start = pci_resource_start(pd, 1);
+
+ par->REGS = REGS;
+
+ par->Chipset = Chipset;
+ par->Architecture = Architecture;
+
+ sprintf(nvidiafb_fix.id, "NV%x", (pd->device & 0x0ff0) >> 4);
+
+ if (NVCommonSetup(info))
+ goto err_out_enable;
+
+ par->FbAddress = nvidiafb_fix.smem_start;
+ par->FbMapSize = par->RamAmountKBytes * 1024;
+ if (vram && vram * 1024 * 1024 < par->FbMapSize)
+ par->FbMapSize = vram * 1024 * 1024;
+
+ /* Limit amount of vram to 64 MB */
+ if (par->FbMapSize > 64 * 1024 * 1024)
+ par->FbMapSize = 64 * 1024 * 1024;
+
+ if(par->Architecture >= NV_ARCH_40)
+ par->FbUsableSize = par->FbMapSize - (560 * 1024);
+ else
+ par->FbUsableSize = par->FbMapSize - (128 * 1024);
+ par->ScratchBufferSize = (par->Architecture < NV_ARCH_10) ? 8 * 1024 :
+ 16 * 1024;
+ par->ScratchBufferStart = par->FbUsableSize - par->ScratchBufferSize;
+ par->CursorStart = par->FbUsableSize + (32 * 1024);
+
+ info->screen_base = devm_ioremap_wc(&pd->dev, nvidiafb_fix.smem_start,
+ par->FbMapSize);
+ info->screen_size = par->FbUsableSize;
+ nvidiafb_fix.smem_len = par->RamAmountKBytes * 1024;
+
+ if (!info->screen_base) {
+ printk(KERN_ERR PFX "cannot ioremap FB base\n");
+ goto err_out_free_base1;
+ }
+
+ par->FbStart = info->screen_base;
+
+ if (!nomtrr)
+ par->wc_cookie = arch_phys_wc_add(nvidiafb_fix.smem_start,
+ par->RamAmountKBytes * 1024);
+
+ info->fbops = &nvidia_fb_ops;
+ info->fix = nvidiafb_fix;
+
+ if (nvidia_set_fbinfo(info) < 0) {
+ printk(KERN_ERR PFX "error setting initial video mode\n");
+ goto err_out_iounmap_fb;
+ }
+
+ nvidia_save_vga(par, &par->SavedReg);
+
+ pci_set_drvdata(pd, info);
+
+ if (register_framebuffer(info) < 0) {
+ printk(KERN_ERR PFX "error registering nVidia framebuffer\n");
+ goto err_out_iounmap_fb;
+ }
+
+ if (backlight)
+ nvidia_bl_init(par);
+
+ printk(KERN_INFO PFX
+ "PCI nVidia %s framebuffer (%dMB @ 0x%lX)\n",
+ info->fix.id,
+ par->FbMapSize / (1024 * 1024), info->fix.smem_start);
+
+ NVTRACE_LEAVE();
+ return 0;
+
+err_out_iounmap_fb:
+ fb_destroy_modelist(&info->modelist);
+err_out_free_base1:
+ fb_destroy_modedb(info->monspecs.modedb);
+ nvidia_delete_i2c_busses(par);
+err_out_enable:
+ kfree(info->pixmap.addr);
+err_out_kfree:
+ framebuffer_release(info);
+err_out:
+ return -ENODEV;
+}
+
+static void nvidiafb_remove(struct pci_dev *pd)
+{
+ struct fb_info *info = pci_get_drvdata(pd);
+ struct nvidia_par *par = info->par;
+
+ NVTRACE_ENTER();
+
+ nvidia_bl_exit(par);
+ unregister_framebuffer(info);
+
+ arch_phys_wc_del(par->wc_cookie);
+ fb_destroy_modedb(info->monspecs.modedb);
+ nvidia_delete_i2c_busses(par);
+ kfree(info->pixmap.addr);
+ framebuffer_release(info);
+ NVTRACE_LEAVE();
+}
+
+/* ------------------------------------------------------------------------- *
+ *
+ * initialization
+ *
+ * ------------------------------------------------------------------------- */
+
+#ifndef MODULE
+static int nvidiafb_setup(char *options)
+{
+ char *this_opt;
+
+ NVTRACE_ENTER();
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!strncmp(this_opt, "forceCRTC", 9)) {
+ char *p;
+
+ p = this_opt + 9;
+ if (!*p || !*(++p))
+ continue;
+ forceCRTC = *p - '0';
+ if (forceCRTC < 0 || forceCRTC > 1)
+ forceCRTC = -1;
+ } else if (!strncmp(this_opt, "flatpanel", 9)) {
+ flatpanel = 1;
+ } else if (!strncmp(this_opt, "hwcur", 5)) {
+ hwcur = 1;
+ } else if (!strncmp(this_opt, "noaccel", 7)) {
+ noaccel = 1;
+ } else if (!strncmp(this_opt, "noscale", 7)) {
+ noscale = 1;
+ } else if (!strncmp(this_opt, "reverse_i2c", 11)) {
+ reverse_i2c = 1;
+ } else if (!strncmp(this_opt, "paneltweak:", 11)) {
+ paneltweak = simple_strtoul(this_opt+11, NULL, 0);
+ } else if (!strncmp(this_opt, "vram:", 5)) {
+ vram = simple_strtoul(this_opt+5, NULL, 0);
+ } else if (!strncmp(this_opt, "backlight:", 10)) {
+ backlight = simple_strtoul(this_opt+10, NULL, 0);
+ } else if (!strncmp(this_opt, "nomtrr", 6)) {
+ nomtrr = true;
+ } else if (!strncmp(this_opt, "fpdither:", 9)) {
+ fpdither = simple_strtol(this_opt+9, NULL, 0);
+ } else if (!strncmp(this_opt, "bpp:", 4)) {
+ bpp = simple_strtoul(this_opt+4, NULL, 0);
+ } else
+ mode_option = this_opt;
+ }
+ NVTRACE_LEAVE();
+ return 0;
+}
+#endif /* !MODULE */
+
+static struct pci_driver nvidiafb_driver = {
+ .name = "nvidiafb",
+ .id_table = nvidiafb_pci_tbl,
+ .probe = nvidiafb_probe,
+ .driver.pm = &nvidiafb_pm_ops,
+ .remove = nvidiafb_remove,
+};
+
+/* ------------------------------------------------------------------------- *
+ *
+ * modularization
+ *
+ * ------------------------------------------------------------------------- */
+
+static int nvidiafb_init(void)
+{
+#ifndef MODULE
+ char *option = NULL;
+#endif
+
+ if (fb_modesetting_disabled("nvidiafb"))
+ return -ENODEV;
+
+#ifndef MODULE
+ if (fb_get_options("nvidiafb", &option))
+ return -ENODEV;
+ nvidiafb_setup(option);
+#endif
+ return pci_register_driver(&nvidiafb_driver);
+}
+
+module_init(nvidiafb_init);
+
+static void __exit nvidiafb_exit(void)
+{
+ pci_unregister_driver(&nvidiafb_driver);
+}
+
+module_exit(nvidiafb_exit);
+
+module_param(flatpanel, int, 0);
+MODULE_PARM_DESC(flatpanel,
+ "Enables experimental flat panel support for some chipsets. "
+ "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
+module_param(fpdither, int, 0);
+MODULE_PARM_DESC(fpdither,
+ "Enables dithering of flat panel for 6 bits panels. "
+ "(0=disabled, 1=enabled, -1=autodetect) (default=-1)");
+module_param(hwcur, int, 0);
+MODULE_PARM_DESC(hwcur,
+ "Enables hardware cursor implementation. (0 or 1=enabled) "
+ "(default=0)");
+module_param(noaccel, int, 0);
+MODULE_PARM_DESC(noaccel,
+ "Disables hardware acceleration. (0 or 1=disable) "
+ "(default=0)");
+module_param(noscale, int, 0);
+MODULE_PARM_DESC(noscale,
+ "Disables screen scaling. (0 or 1=disable) "
+ "(default=0, do scaling)");
+module_param(paneltweak, int, 0);
+MODULE_PARM_DESC(paneltweak,
+ "Tweak display settings for flatpanels. "
+ "(default=0, no tweaks)");
+module_param(forceCRTC, int, 0);
+MODULE_PARM_DESC(forceCRTC,
+ "Forces usage of a particular CRTC in case autodetection "
+ "fails. (0 or 1) (default=autodetect)");
+module_param(vram, int, 0);
+MODULE_PARM_DESC(vram,
+ "amount of framebuffer memory to remap in MiB"
+ "(default=0 - remap entire memory)");
+module_param(mode_option, charp, 0);
+MODULE_PARM_DESC(mode_option, "Specify initial video mode");
+module_param(bpp, int, 0);
+MODULE_PARM_DESC(bpp, "pixel width in bits"
+ "(default=8)");
+module_param(reverse_i2c, int, 0);
+MODULE_PARM_DESC(reverse_i2c, "reverse port assignment of the i2c bus");
+module_param(nomtrr, bool, false);
+MODULE_PARM_DESC(nomtrr, "Disables MTRR support (0 or 1=disabled) "
+ "(default=0)");
+
+MODULE_AUTHOR("Antonino Daplas");
+MODULE_DESCRIPTION("Framebuffer driver for nVidia graphics chipset");
+MODULE_LICENSE("GPL");