/*
 * This file is part of the coreboot project.
 *
 * Copyright 2013 Google Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#include <types.h>
#include <string.h>
#include <stdlib.h>
#include <device/device.h>
#include <device/device.h>
#include <device/pci_def.h>
#include <device/pci_ops.h>
#include <console/console.h>
#include <delay.h>
#include <pc80/mc146818rtc.h>
#include <arch/acpi.h>
#include <arch/io.h>
#include <arch/interrupt.h>
#include <boot/coreboot_tables.h>
#include <smbios.h>
#include <device/pci.h>
#include <ec/google/chromeec/ec.h>

#include <cpu/x86/tsc.h>
#include <cpu/x86/cache.h>
#include <cpu/x86/mtrr.h>
#include <cpu/x86/msr.h>
#include <edid.h>
#include <drivers/intel/gma/i915.h>

/* how many bytes do we need for the framebuffer?
 * Well, this gets messy. To get an exact answer, we have
 * to ask the panel, but we'd rather zero the memory
 * and set up the gtt while the panel powers up. So,
 * we take a reasonable guess, secure in the knowledge that the
 * MRC has to overestimate the number of bytes used.
 * 8 MiB is a very safe guess. There may be a better way later, but
 * fact is, the initial framebuffer is only very temporary. And taking
 * a little long is ok; this is done much faster than the AUX
 * channel is ready for IO.
 */
#define FRAME_BUFFER_BYTES (8*MiB)
/* how many 4096-byte pages do we need for the framebuffer?
 * There are hard ways to get this, and easy ways:
 * there are FRAME_BUFFER_BYTES/4096 pages, since pages are 4096
 * on this chip (and in fact every Intel graphics chip we've seen).
 */
#define FRAME_BUFFER_PAGES (FRAME_BUFFER_BYTES/(4096))

static int verbose = 0;

static unsigned int *mmio;
static unsigned int graphics;
static unsigned short addrport;
static unsigned short dataport;
static unsigned int physbase;

const u32 link_edid_data[] = {
	0xffffff00, 0x00ffffff, 0x0379e430, 0x00000000,
	0x04011500, 0x96121ba5, 0xa2d54f02, 0x26935259,
	0x00545017, 0x01010000, 0x01010101, 0x01010101,
	0x01010101, 0x6f6d0101, 0xa4a0a000, 0x20306031,
	0xb510003a, 0x19000010, 0x00000000, 0x00000000,
	0x00000000, 0x00000000, 0x00000000, 0x4c00fe00,
	0x69442047, 0x616c7073, 0x20200a79, 0xfe000000,
	0x31504c00, 0x45513932, 0x50532d31, 0x24003141,
};

static int ioread = 0, iowrite = 0;

static char *regname(unsigned long addr)
{
	static char name[16];
	snprintf(name, sizeof (name), "0x%lx", addr);
	return name;
}

unsigned long io_i915_read32(unsigned long addr)
{
	unsigned long val;
	outl(addr, addrport);
	val = inl(dataport);
	ioread += 2;
	if (verbose & vio)printk(BIOS_SPEW, "%s: Got %08lx\n", regname(addr), val);
	return val;
}

void io_i915_write32(unsigned long val, unsigned long addr)
{
	if (verbose & vio)printk(BIOS_SPEW, "%s: outl %08lx\n", regname(addr), val);
	outl(addr, addrport);
	outl(val, dataport);
	iowrite += 2;
}

/* GTT is the Global Translation Table for the graphics pipeline.
 * It is used to translate graphics addresses to physical
 * memory addresses. As in the CPU, GTTs map 4K pages.
 * The setgtt function adds a further bit of flexibility:
 * it allows you to set a range (the first two parameters) to point
 * to a physical address (third parameter);the physical address is
 * incremented by a count (fourth parameter) for each GTT in the
 * range.
 * Why do it this way? For ultrafast startup,
 * we can point all the GTT entries to point to one page,
 * and set that page to 0s:
 * memset(physbase, 0, 4096);
 * setgtt(0, 4250, physbase, 0);
 * this takes about 2 ms, and is a win because zeroing
 * the page takes a up to 200 ms.
 * This call sets the GTT to point to a linear range of pages
 * starting at physbase.
 */

static void
setgtt(int start, int end, unsigned long base, int inc)
{
	int i;

	for(i = start; i < end; i++){
		u32 word = base + i*inc;
		io_i915_write32(word|1,(i*4)|1);
	}
}

static unsigned long tickspermicrosecond = 1795;
static unsigned long long globalstart;

static unsigned long
microseconds(unsigned long long start, unsigned long long end)
{
	unsigned long ret;
	ret = ((end - start)/tickspermicrosecond);
	return ret;
}

static unsigned long globalmicroseconds(void)
{
	return microseconds(globalstart, rdtscll());
}

static int i915_init_done = 0;

int i915lightup(unsigned int physbase, unsigned int iobase, unsigned int mmio,
		unsigned int gfx);

int i915lightup(unsigned int pphysbase, unsigned int piobase,
		unsigned int pmmio, unsigned int pgfx)
{
	int must_cycle_power = 0;

	/* frame buffer pointer */
	u32 *l;
	int i;
	unsigned long before_gtt, after_gtt;

	mmio = (void *)pmmio;
	addrport = piobase;
	dataport = addrport + 4;
	physbase = pphysbase;
	graphics = pgfx;
	printk(BIOS_SPEW,
	       "i915lightup: graphics %p mmio %p"
	       "addrport %04x physbase %08x\n",
	       (void *)graphics, mmio, addrport, physbase);
	globalstart = rdtscll();

	/* turn it on. The VBIOS does it this way, so we hope that's ok. */
	verbose = 0;
	io_i915_write32(0xabcd000f, PCH_PP_CONTROL);

	/* the AUX channel needs a small amount of time to spin up.
	 * Rather than udelay, do some useful work:
	 * Zero out the frame buffer memory,
	 * and set the global translation table (GTT)
	 */
	printk(BIOS_SPEW, "Set not-White (%08x) for %d pixels\n", 0xffffff,
	       FRAME_BUFFER_BYTES/sizeof(u32));
	for(l = (u32 *)graphics, i = 0;
		i < FRAME_BUFFER_BYTES/sizeof(u32); i++){
		l[i] = 0x1122ff;
	}
	printk(BIOS_SPEW, "GTT: set %d pages starting at %p\n",
				FRAME_BUFFER_PAGES, (void *)physbase);
	before_gtt = globalmicroseconds();
	setgtt(0, FRAME_BUFFER_PAGES, physbase, 4096);
	after_gtt = globalmicroseconds();

	/* The reset is basically harmless, and can be
	 * repeated by the VBIOS in any event.
	 */

	graphics_register_reset(DPA_AUX_CH_CTL, DPA_AUX_CH_DATA1, verbose);

	/* failures after this point can return without
	 * powering off the panel.
	 */

	if (1)
		goto fail;
	/* failures after this point MUST power off the panel
	 * and wait 600 ms.
	 */

	i915_init_done = 1;
	return i915_init_done;

fail:
	printk(BIOS_SPEW, "Graphics could not be started;");
	if (must_cycle_power){
		printk(BIOS_SPEW, "Turn off power and wait ...");
		io_i915_write32(0xabcd0000, PCH_PP_CONTROL);
		udelay(600000);
	}
	printk(BIOS_SPEW, "Returning.\n");
	return 0;

}