Module Name: src Committed By: mlelstv Date: Tue Jan 21 14:52:07 UTC 2014
Modified Files: src/sys/dev/pci: files.pci Added Files: src/sys/dev/pci: igma.c igmareg.h igmavar.h src/sys/dev/pci/igma: igmafb.c Log Message: wscons driver for Intel Graphics Media Accelerator. Initial commit that already works for a couple of Notebooks based on G35, G45, Sandy Bridge and Ivy Bridge chips. Despite the word 'Accelerator' there is nothing acclerated yet. To generate a diff of this commit: cvs rdiff -u -r1.367 -r1.368 src/sys/dev/pci/files.pci cvs rdiff -u -r0 -r1.1 src/sys/dev/pci/igma.c src/sys/dev/pci/igmareg.h \ src/sys/dev/pci/igmavar.h cvs rdiff -u -r0 -r1.1 src/sys/dev/pci/igma/igmafb.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/files.pci diff -u src/sys/dev/pci/files.pci:1.367 src/sys/dev/pci/files.pci:1.368 --- src/sys/dev/pci/files.pci:1.367 Wed Sep 18 14:30:45 2013 +++ src/sys/dev/pci/files.pci Tue Jan 21 14:52:07 2014 @@ -1,4 +1,4 @@ -# $NetBSD: files.pci,v 1.367 2013/09/18 14:30:45 macallan Exp $ +# $NetBSD: files.pci,v 1.368 2014/01/21 14:52:07 mlelstv Exp $ # # Config file and device description for machine-independent PCI code. # Included by ports that need it. Requires that the SCSI files be @@ -1128,6 +1128,12 @@ device lynxfb: wsemuldisplaydev, rasops1 attach lynxfb at pci file dev/pci/lynxfb.c lynxfb needs-flag +include "dev/pci/igma/files.igma" +# Intel GMA +device igma: igmabus, i2cbus, i2c_bitbang, ddc_read_edid, edid +attach igma at pci +file dev/pci/igma.c igma + # 3Dfx Voodoo Graphics defflag opt_tdvfb.h TDVFB_CONSOLE device tdvfb: wsemuldisplaydev, rasops16, rasops32, vcons, videomode Added files: Index: src/sys/dev/pci/igma.c diff -u /dev/null src/sys/dev/pci/igma.c:1.1 --- /dev/null Tue Jan 21 14:52:07 2014 +++ src/sys/dev/pci/igma.c Tue Jan 21 14:52:07 2014 @@ -0,0 +1,642 @@ +/* $NetBSD: igma.c,v 1.1 2014/01/21 14:52:07 mlelstv Exp $ */ + +/* + * Copyright (c) 2014 Michael van Elst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING 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 SOFTWARE. + */ + +/* + * Intel Graphic Media Accelerator + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: igma.c,v 1.1 2014/01/21 14:52:07 mlelstv Exp $"); + +#include "vga.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/bus.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/pciio.h> + +#include <dev/i2c/i2cvar.h> +#include <dev/i2c/i2c_bitbang.h> +#include <dev/i2c/ddcvar.h> + +#include <dev/videomode/videomode.h> +#include <dev/videomode/edidvar.h> + +#include <dev/wscons/wsdisplayvar.h> + +#if NVGA > 0 +#include <dev/ic/mc6845reg.h> +#include <dev/ic/pcdisplayvar.h> +#include <dev/ic/vgareg.h> +#include <dev/ic/vgavar.h> +#endif + +#include <dev/pci/igmareg.h> +#include <dev/pci/igmavar.h> + +#include "igmafb.h" + +struct igma_softc; +struct igma_i2c { + kmutex_t ii_lock; + struct igma_softc *ii_sc; + bus_addr_t ii_reg; + struct i2c_controller ii_i2c; + const char *ii_name; + u_int32_t ii_dir; +}; + +struct igma_softc { + device_t sc_dev; + struct igma_chip sc_chip; + struct igma_i2c sc_ii[GMBUS_NUM_PORTS]; +}; + +static int igma_match(device_t, cfdata_t, void *); +static void igma_attach(device_t, device_t, void *); +static int igma_print(void *, const char *); + +static void igma_i2c_attach(struct igma_softc *); + +CFATTACH_DECL_NEW(igma, sizeof(struct igma_softc), + igma_match, igma_attach, NULL, NULL); + +static int igma_i2c_acquire_bus(void *, int); +static void igma_i2c_release_bus(void *, int); +static int igma_i2c_send_start(void *, int); +static int igma_i2c_send_stop(void *, int); +static int igma_i2c_initiate_xfer(void *, i2c_addr_t, int); +static int igma_i2c_read_byte(void *, uint8_t *, int); +static int igma_i2c_write_byte(void *, uint8_t, int); +static void igma_i2cbb_set_bits(void *, uint32_t); +static void igma_i2cbb_set_dir(void *, uint32_t); +static uint32_t igma_i2cbb_read(void *); + +static void igma_reg_barrier(const struct igma_chip *, int); +static u_int32_t igma_reg_read(const struct igma_chip *, int); +static void igma_reg_write(const struct igma_chip *, int, u_int32_t); +static u_int8_t igma_vga_read(const struct igma_chip *, int); +static void igma_vga_write(const struct igma_chip *, int , u_int8_t); +#if 0 +static u_int8_t igma_crtc_read(const struct igma_chip *, int); +static void igma_crtc_write(const struct igma_chip *, int, u_int8_t); +#endif + +static const struct i2c_bitbang_ops igma_i2cbb_ops = { + igma_i2cbb_set_bits, + igma_i2cbb_set_dir, + igma_i2cbb_read, + { 1, 2, 0, 1 } +}; + +static const struct igma_chip_ops igma_bus_ops = { + igma_reg_barrier, + igma_reg_read, + igma_reg_write, + igma_vga_read, + igma_vga_write, +#if 0 + igma_crtc_read, + igma_crtc_write, +#endif +}; + +static struct igma_product { + u_int16_t product; + int gentype; + int num_pipes; +} const igma_products[] = { + /* i830 */ + { PCI_PRODUCT_INTEL_82830MP_IV, 200,2 }, + /* i845g */ + { PCI_PRODUCT_INTEL_82845G_IGD, 200,2 }, + /* i85x */ + { PCI_PRODUCT_INTEL_82855GM_IGD, 200,2 }, +// 0x358e ? + /* i865g */ + { PCI_PRODUCT_INTEL_82865_IGD, 200,2 }, + /* i915g */ + { PCI_PRODUCT_INTEL_82915G_IGD, 200,2 }, + { PCI_PRODUCT_INTEL_E7221_IGD, 200,2 }, + /* i915gm */ + { PCI_PRODUCT_INTEL_82915GM_IGD, 300,2 }, + /* i945g */ + { PCI_PRODUCT_INTEL_82945P_IGD, 300,2 }, + /* i945gm */ + { PCI_PRODUCT_INTEL_82945GM_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82945GM_IGD_1, 300,2 }, + { PCI_PRODUCT_INTEL_82945GME_IGD, 300,2 }, + /* i965g */ + { PCI_PRODUCT_INTEL_82946GZ_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82G35_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82G35_IGD_1, 300,2 }, + { PCI_PRODUCT_INTEL_82965Q_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82965Q_IGD_1, 300,2 }, + { PCI_PRODUCT_INTEL_82965G_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82965G_IGD_1, 300,2 }, + /* g33 */ + { PCI_PRODUCT_INTEL_82G33_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82G33_IGD_1, 300,2 }, + { PCI_PRODUCT_INTEL_82Q33_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82Q33_IGD_1, 300,2 }, + { PCI_PRODUCT_INTEL_82Q35_IGD, 300,2 }, + { PCI_PRODUCT_INTEL_82Q35_IGD_1, 300,2 }, + /* pineview */ + { PCI_PRODUCT_INTEL_PINEVIEW_IGD, 350,2 }, + { PCI_PRODUCT_INTEL_PINEVIEW_M_IGD, 350,2 }, + /* i965gm */ + { PCI_PRODUCT_INTEL_82965PM_IGD, 400,2 }, + { PCI_PRODUCT_INTEL_82965PM_IGD_1, 400,2 }, + { PCI_PRODUCT_INTEL_82965GME_IGD, 400,2 }, + /* gm45 */ + { PCI_PRODUCT_INTEL_82GM45_IGD, 450,2 }, + { PCI_PRODUCT_INTEL_82GM45_IGD_1, 450,2 }, + /* g45 */ + { PCI_PRODUCT_INTEL_82IGD_E_IGD, 450,2 }, + { PCI_PRODUCT_INTEL_82Q45_IGD, 450,2 }, + { PCI_PRODUCT_INTEL_82G45_IGD, 450,2 }, + { PCI_PRODUCT_INTEL_82G41_IGD, 450,2 }, + { PCI_PRODUCT_INTEL_82B43_IGD, 450,2 }, +// 0x2e92 ? + /* ironlake d */ + { PCI_PRODUCT_INTEL_IRONLAKE_D_IGD, 500,2 }, + /* ironlake m */ + { PCI_PRODUCT_INTEL_IRONLAKE_M_IGD, 500,2 }, + /* sandy bridge */ + { PCI_PRODUCT_INTEL_SANDYBRIDGE_IGD, 600,2 }, + { PCI_PRODUCT_INTEL_SANDYBRIDGE_IGD_1, 600,2 }, + { PCI_PRODUCT_INTEL_SANDYBRIDGE_IGD_2, 600,2 }, + /* sandy bridge m */ + { PCI_PRODUCT_INTEL_SANDYBRIDGE_M_IGD, 600,2 }, + { PCI_PRODUCT_INTEL_SANDYBRIDGE_M_IGD_1, 600,2 }, + { PCI_PRODUCT_INTEL_SANDYBRIDGE_M_IGD_2, 600,2 }, + /* sandy bridge s */ + { PCI_PRODUCT_INTEL_SANDYBRIDGE_S_IGD, 600,2 }, + /* ivy bridge */ + { PCI_PRODUCT_INTEL_IVYBRIDGE_IGD, 700,3 }, + { PCI_PRODUCT_INTEL_IVYBRIDGE_IGD_1, 700,3 }, + /* ivy bridge m */ + { PCI_PRODUCT_INTEL_IVYBRIDGE_M_IGD, 700,3 }, + { PCI_PRODUCT_INTEL_IVYBRIDGE_M_IGD_1, 700,3 }, + /* ivy bridge s */ + { PCI_PRODUCT_INTEL_IVYBRIDGE_S_IGD, 700,3 }, + { PCI_PRODUCT_INTEL_IVYBRIDGE_S_IGD_1, 700,3 }, +#if 0 + /* valleyview d */ + /* valleyview m */ + { PCI_PRODUCT_INTEL_HASWELL_IGD_1, 800,3 }, + /* haswell d */ + { PCI_PRODUCT_INTEL_HASWELL_IGD, 800,3 }, + { PCI_PRODUCT_INTEL_HASWELL_IGD_1, 800,3 }, + /* haswell m */ + /* broadwell d */ + /* broadwell m */ +#endif +}; + +static int +igma_newpch_match(const struct pci_attach_args *pa) +{ + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) + return 0; + switch (0xff00 & PCI_PRODUCT(pa->pa_id)) { + case 0x3b00: /* ibex peak */ + case 0x1c00: /* cougar point */ + case 0x1e00: /* panther point */ + case 0x8c00: /* lynx point */ + case 0x9c00: /* lynx point lp */ + return 1; + } + + return 0; +} + +static const struct igma_product * +igma_lookup(const struct pci_attach_args *pa) +{ + const struct igma_product *ip; + int i; + + if (PCI_VENDOR(pa->pa_id) != PCI_VENDOR_INTEL) + return NULL; + for (i=0; i < __arraycount(igma_products); ++i) { + ip = &igma_products[i]; + if (PCI_PRODUCT(pa->pa_id) == ip->product) + return ip; + } + return NULL; +} + +static void +igma_product_to_chip(const struct pci_attach_args *pa, struct igma_chip *cd) +{ + const struct igma_product *ip; + struct pci_attach_args PA; + + ip = igma_lookup(pa); + KASSERT(ip != NULL); + + cd->ops = &igma_bus_ops; + cd->num_gmbus = 6; + cd->num_pipes = ip->num_pipes; + cd->quirks = 0; + cd->backlight_factor = 1; + + cd->gpio_offset = OLD_GPIOA; + cd->vga_cntrl = PCH_VGA_CNTRL; + cd->backlight_cntrl = OLD_BLC_PWM_CTL; + cd->backlight_cntrl2 = OLD_BLC_PWM_CTL2; + + PA = *pa; + if (pci_find_device(&PA, igma_newpch_match)) { + cd->gpio_offset = PCH_GPIOA; + cd->vga_cntrl = CPU_VGA_CNTRL; + cd->backlight_cntrl = CPU_BLC_PWM_CTL; + cd->backlight_cntrl2 = CPU_BLC_PWM_CTL2; + } + + switch (ip->gentype) { + case 200: + cd->backlight_factor = 2; + break; + case 300: + case 350: + cd->backlight_factor = 2; + cd->quirks |= IGMA_PFITDISABLE_QUIRK; + break; + case 450: + cd->pri_cntrl = PRI_CTRL_NOTRICKLE; + cd->quirks |= IGMA_PLANESTART_QUIRK; + break; + default: + cd->pri_cntrl = 0; + break; + } +} + +static void +igma_adjust_chip(struct igma_softc *sc, struct igma_chip *cd) +{ + const struct igma_chip_ops *co = cd->ops; + u_int32_t reg; + + reg = co->read_reg(cd, cd->vga_cntrl); + if (reg & VGA_PIPE_B_SELECT) + cd->use_pipe = 1; +} + +static int +igma_print(void *aux, const char *pnp) +{ + if (pnp) + aprint_normal("drm at %s", pnp); + return (UNCONF); +} + +static int +igma_match(device_t parent, cfdata_t match, void *aux) +{ + struct pci_attach_args *pa = (struct pci_attach_args *)aux; + const struct igma_product *ip; + + if (PCI_CLASS(pa->pa_class) != PCI_CLASS_DISPLAY) + return 0; + + ip = igma_lookup(pa); + if (ip != NULL) + return 100; + + return 0; +} + +static void +igma_attach(device_t parent, device_t self, void *aux) +{ + struct igma_softc *sc = device_private(self); + const struct pci_attach_args *pa = (struct pci_attach_args *)aux; + struct igma_attach_args iaa; + bus_space_tag_t gttmmt, gmt, regt; + bus_space_handle_t gttmmh, gmh, regh; + bus_addr_t gttmmb, gmb; + + pci_aprint_devinfo(pa, NULL); + + sc->sc_dev = self; + + /* Initialize according to chip type */ + igma_product_to_chip(pa, &sc->sc_chip); + + if (pci_mapreg_map(pa, PCI_BAR0, PCI_MAPREG_TYPE_MEM, + BUS_SPACE_MAP_LINEAR, + >tmmt, >tmmh, >tmmb, NULL)) { + aprint_error_dev(sc->sc_dev, "unable to map GTTMM\n"); + return; + } + sc->sc_chip.mmiot = gttmmt; + if (bus_space_subregion(gttmmt, gttmmh, 0, 2*1024*1024, + &sc->sc_chip.mmioh)) { + aprint_error_dev(sc->sc_dev, "unable to submap MMIO\n"); + return; + } + sc->sc_chip.gttt = gttmmt; + if (bus_space_subregion(gttmmt, gttmmh, 2*1024*1024, 2*1024*1024, + &sc->sc_chip.gtth)) { + aprint_error_dev(sc->sc_dev, "unable to submap GTT\n"); + return; + } + + if (pci_mapreg_map(pa, PCI_BAR2, PCI_MAPREG_TYPE_MEM, + BUS_SPACE_MAP_LINEAR | BUS_SPACE_MAP_PREFETCHABLE, + &gmt, &gmh, &gmb, NULL)) { + aprint_error_dev(sc->sc_dev, "unable to map aperture\n"); + return; + } + sc->sc_chip.gmt = gmt; + sc->sc_chip.gmh = gmh; + sc->sc_chip.gmb = gmb; + + if (pci_mapreg_map(pa, PCI_BAR4, PCI_MAPREG_TYPE_IO, 0, + ®t, ®h, NULL, NULL)) { + aprint_error_dev(sc->sc_dev, "unable to map IO registers\n"); + return; + } + +#if NVGA > 0 + iaa.iaa_console = vga_cndetach() ? true : false; + /* Hack */ + if (iaa.iaa_console) + wsdisplay_cndetach(); +#else + iaa.iaa_console = 0; +#endif + sc->sc_chip.vgat = regt; + if (bus_space_map(regt, 0x3c0, 0x10, 0, &sc->sc_chip.vgah)) { + aprint_error_dev(sc->sc_dev, "unable to map VGA registers\n"); + return; + } + + /* Check hardware for more information */ + igma_adjust_chip(sc, &sc->sc_chip); + + aprint_normal("%s: VGA_CNTRL: 0x%x\n",device_xname(sc->sc_dev), + sc->sc_chip.vga_cntrl); + aprint_normal("%s: GPIO_OFFSET: 0x%x\n",device_xname(sc->sc_dev), + sc->sc_chip.gpio_offset); + aprint_normal("%s: BACKLIGHT_CTRL: 0x%x\n",device_xname(sc->sc_dev), + sc->sc_chip.backlight_cntrl); + aprint_normal("%s: BACKLIGHT_CTRL2: 0x%x\n",device_xname(sc->sc_dev), + sc->sc_chip.backlight_cntrl2); + +#if NIGMAFB > 0 + strcpy(iaa.iaa_name, "igmafb"); + iaa.iaa_chip = sc->sc_chip; + config_found_ia(sc->sc_dev, "igmabus", &iaa, igma_print); +#endif + + igma_i2c_attach(sc); +} + +static void +igma_i2c_attach(struct igma_softc *sc) +{ + struct igma_i2c *ii; + int i; +#if 0 + struct i2cbus_attach_args iba; +#endif + + for (i=0; i<sc->sc_chip.num_gmbus; ++i) { + ii = &sc->sc_ii[i]; + ii->ii_sc = sc; + + /* XXX */ + ii->ii_reg = sc->sc_chip.gpio_offset - PCH_GPIOA; + switch (i) { + case 0: + ii->ii_reg += PCH_GPIOB; + ii->ii_name = "ssc"; + break; + case 1: + ii->ii_reg += PCH_GPIOA; + ii->ii_name = "vga"; + break; + case 2: + ii->ii_reg += PCH_GPIOC; + ii->ii_name = "panel"; + break; + case 3: + ii->ii_reg += PCH_GPIOD; + ii->ii_name = "dpc"; + break; + case 4: + ii->ii_reg += PCH_GPIOE; + ii->ii_name = "dpb"; + break; + case 5: + ii->ii_reg += PCH_GPIOF; + ii->ii_name = "dpd"; + break; + default: + panic("don't know GMBUS %d\n",i); + } + + mutex_init(&ii->ii_lock, MUTEX_DEFAULT, IPL_NONE); + + ii->ii_i2c.ic_cookie = ii; + ii->ii_i2c.ic_acquire_bus = igma_i2c_acquire_bus; + ii->ii_i2c.ic_release_bus = igma_i2c_release_bus; + ii->ii_i2c.ic_send_start = igma_i2c_send_start; + ii->ii_i2c.ic_send_stop = igma_i2c_send_stop; + ii->ii_i2c.ic_initiate_xfer = igma_i2c_initiate_xfer; + ii->ii_i2c.ic_read_byte = igma_i2c_read_byte; + ii->ii_i2c.ic_write_byte = igma_i2c_write_byte; + ii->ii_i2c.ic_exec = NULL; + +#if 0 + iba.iba_type = I2C_TYPE_SMBUS; + iba.iba_tag = &ii->ii_i2c; + config_found_ia(sc->sc_dev, "i2cbus", &iba, iicbus_print); +#endif + } +} + +/* + * I2C interface + */ + +static int +igma_i2c_acquire_bus(void *cookie, int flags) +{ + struct igma_i2c *ii = cookie; + mutex_enter(&ii->ii_lock); + return 0; +} + +static void +igma_i2c_release_bus(void *cookie, int flags) +{ + struct igma_i2c *ii = cookie; + mutex_exit(&ii->ii_lock); +} + +static int +igma_i2c_send_start(void *cookie, int flags) +{ + return i2c_bitbang_send_start(cookie, flags, &igma_i2cbb_ops); +} + +static int +igma_i2c_send_stop(void *cookie, int flags) +{ + return i2c_bitbang_send_stop(cookie, flags, &igma_i2cbb_ops); +} + +static int +igma_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags) +{ + return i2c_bitbang_initiate_xfer(cookie, addr, flags, &igma_i2cbb_ops); +} + +static int +igma_i2c_read_byte(void *cookie, uint8_t *valp, int flags) +{ + return i2c_bitbang_read_byte(cookie, valp, flags, &igma_i2cbb_ops); +} + +static int +igma_i2c_write_byte(void *cookie, uint8_t val, int flags) +{ + return i2c_bitbang_write_byte(cookie, val, flags, &igma_i2cbb_ops); +} + +static void +igma_i2cbb_set_bits(void *cookie, uint32_t bits) +{ + struct igma_i2c *ii = cookie; + struct igma_softc *sc = ii->ii_sc; + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + uint32_t reg; + + reg = co->read_reg(cd, ii->ii_reg); + reg &= GPIO_DATA_PULLUP_DISABLE | GPIO_CLOCK_PULLUP_DISABLE; + + if ((bits | ii->ii_dir) & 1) + /* make data input, signal is pulled high */ + reg |= GPIO_DATA_DIR_IN | GPIO_DATA_DIR_MASK; + else + /* make data output, signal is driven low */ + reg |= GPIO_DATA_DIR_OUT | GPIO_DATA_DIR_MASK + | GPIO_DATA_VAL_MASK; + + if (bits & 2) + /* make clock input, signal is pulled high */ + reg |= GPIO_CLOCK_DIR_IN | GPIO_CLOCK_DIR_MASK; + else + /* make clock output, signal is driven low */ + reg |= GPIO_CLOCK_DIR_OUT | GPIO_CLOCK_DIR_MASK + | GPIO_CLOCK_VAL_MASK; + + co->write_reg(cd, ii->ii_reg, reg); +#if 1 + reg = co->read_reg(cd, ii->ii_reg); +#else + co->barrier(cd, ii->ii_reg); +#endif +} + +static void +igma_i2cbb_set_dir(void *cookie, uint32_t bits) +{ + struct igma_i2c *ii = cookie; + + ii->ii_dir = bits; +} + +static uint32_t +igma_i2cbb_read(void *cookie) +{ + struct igma_i2c *ii = cookie; + struct igma_softc *sc = ii->ii_sc; + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + uint32_t reg; + int sda, scl; + + reg = co->read_reg(cd, ii->ii_reg); + + sda = reg & GPIO_DATA_VAL_IN; + scl = reg & GPIO_CLOCK_VAL_IN; + + reg = (sda ? 1 : 0) | (scl ? 2 : 0); + return reg; +} + +static void +igma_reg_barrier(const struct igma_chip *cd, int r) +{ + bus_space_barrier(cd->mmiot, cd->mmioh, r, sizeof(u_int32_t), + BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); +} + +static u_int32_t +igma_reg_read(const struct igma_chip *cd, int r) +{ + return bus_space_read_4(cd->mmiot, cd->mmioh, r); +} + +static void +igma_reg_write(const struct igma_chip *cd, int r, u_int32_t v) +{ + bus_space_write_4(cd->mmiot, cd->mmioh, r, v); +} + +static u_int8_t +igma_vga_read(const struct igma_chip *cd, int r) +{ + bus_space_write_1(cd->vgat, cd->vgah, 0x4, r | 0x20); + return bus_space_read_1(cd->vgat, cd->vgah, 0x5); +} + +static void +igma_vga_write(const struct igma_chip *cd, int r, u_int8_t v) +{ + bus_space_write_1(cd->vgat, cd->vgah, 0x4, r | 0x20); + bus_space_write_1(cd->vgat, cd->vgah, 0x5, v); +} + +#if 0 +static u_int8_t +igma_crtc_read(const struct igma_chip *cd, int r) +{ + bus_space_write_1(cd->crtct, cd->crtch, 0x4, r); + return bus_space_read_1(cd->crtct, cd->crtch, 0x5); +} + +static void +igma_crtc_write(const struct igma_chip *cd, int r, u_int8_t v) +{ + bus_space_write_1(cd->crtct, cd->crtch, 0x4, r); + bus_space_write_1(cd->crtct, cd->crtch, 0x5, v); +} +#endif Index: src/sys/dev/pci/igmareg.h diff -u /dev/null src/sys/dev/pci/igmareg.h:1.1 --- /dev/null Tue Jan 21 14:52:07 2014 +++ src/sys/dev/pci/igmareg.h Tue Jan 21 14:52:07 2014 @@ -0,0 +1,197 @@ +/* $NetBSD: igmareg.h,v 1.1 2014/01/21 14:52:07 mlelstv Exp $ */ + +/* + * Copyright (c) 2014 Michael van Elst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING 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 SOFTWARE. + */ + +#ifndef IGMAREG_H +#define IGMAREG_H + +/* North display */ + +#define CPU_VGA_CNTRL 0x41000 +#define PCH_VGA_CNTRL 0x71400 +#define VGA_CNTRL_DISABLE (1L << 31) +#define VGA_2X_MODE (1L << 29) +#define VGA_PIPE_B_SELECT (1L << 29) + +#define PF_WINPOS(i) (0x68070+i*0x800) +#define PF_WINPOS_VAL(x,y) ((x) << 16 | (y)) +#define PF_WINSZ(i) (0x68074+i*0x800) +#define PF_WINSZ_VAL(w,h) ((w) << 16 | (h)) +#define PF_WINSZ_GET_WIDTH(r) (((r) >> 16) & 0x1fff) +#define PF_WINSZ_GET_HEIGHT(r) (((r) >> 0) & 0xfff) +#define PF_CTRL(i) (0x68080+i*0x800) +#define PF_CTRL_I965 (0x61230) +#define PF_ENABLE (1L << 31) + +#define PIPE_HTOTAL(i) (0x60000+i*0x1000) +#define PIPE_HOTAL_VAL(t,a) (((t)-1) << 16 | ((a)-1)) +#define PIPE_HTOTAL_GET_TOTAL(r) ((((r) >> 16) & 0x1fff)+1) +#define PIPE_HTOTAL_GET_ACTIVE(r) ((((r) >> 0) & 0x1fff)+1) +#define PIPE_HBLANK(i) (0x60004+i*0x1000) +#define PIPE_HBLANK_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define PIPE_HSYNC(i) (0x60008+i*0x1000) +#define PIPE_HSYNC_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define PIPE_VTOTAL(i) (0x6000c+i*0x1000) +#define PIPE_VTOTAL_VAL(t,a) (((t)-1) << 16 | ((a)-1)) +#define PIPE_VTOTAL_GET_TOTAL(r) ((((r) >> 16) & 0xfff)+1) +#define PIPE_VTOTAL_GET_ACTIVE(r) ((((r) >> 0) & 0xfff)+1) +#define PIPE_VBLANK(i) (0x60010+i*0x1000) +#define PIPE_VBLANK_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define PIPE_VSYNC(i) (0x60014+i*0x1000) +#define PIPE_VSYNC_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define PIPE_SRCSZ(i) (0x6001c+i*0x1000) +#define PIPE_SRCSZ_VAL(w,h) (((w)-1) << 16 | ((h)-1)) +#define PIPE_VSHIFT(i) (0x60028+i*0x1000) +#define PIPE_DATAM1(i) (0x60030+i*0x1000) +#define PIPE_DATAM2(i) (0x60034+i*0x1000) +#define PIPE_DATAN1(i) (0x60038+i*0x1000) +#define PIPE_DATAN2(i) (0x6003c+i*0x1000) +#define PIPE_DPLINKM1(i) (0x60040+i*0x1000) +#define PIPE_DPLINKM2(i) (0x60044+i*0x1000) +#define PIPE_DPLINKN1(i) (0x60048+i*0x1000) +#define PIPE_DPLINKN2(i) (0x6004c+i*0x1000) +#define PIPE_CONF(i) (0x70080+i*0x1000) +#define PIPE_CONF_ENABLE (1L << 31) +#define PIPE_CONF_STATE (1L << 30) +#define PIPE_CONF_GAMMA8 (0L << 24) +#define PIPE_CONF_GAMMA10 (1L << 24) +#define PIPE_CONF_GAMMA12 (2L << 24) +#define PIPE_CONF_PFPD (0L << 21) +#define PIPE_CONF_PFID (1L << 21) +#define PIPE_CONF_IFID (3L << 21) +#define PIPE_CONF_IFID_DBL (4L << 21) +#define PIPE_CONF_PFID_DBL (5L << 21) +#define PIPE_CONF_POWERSAVE (1L << 20) +#define PIPE_CONF_MSA1 (0L << 18) +#define PIPE_CONF_MSA2 (1L << 18) +#define PIPE_CONF_MSA3 (2L << 18) +#define PIPE_CONF_MSA4 (3L << 18) +#define PIPE_CONF_ROT0 (0L << 14) +#define PIPE_CONF_ROT90 (1L << 14) +#define PIPE_CONF_ROT180 (2L << 14) +#define PIPE_CONF_ROT270 (3L << 14) +#define PIPE_CONF_CE (1L << 13) +#define PIPE_CONF_8BPP (0L << 5) +#define PIPE_CONF_6BPP (2L << 5) +#define PIPE_CONF_DITHER (4L << 2) +#define PIPE_CONF_DITHERST1 (5L << 2) +#define PIPE_CONF_DITHERST2 (6L << 2) +#define PIPE_CONF_DITHERT (7L << 2) +#define CUR_CNTR(i) (0x70080+i*0x1000) +#define CUR_BASE(i) (0x70084+i*0x1000) +#define PRI_CTRL(i) (0x70180+i*0x1000) +#define PRI_CTRL_ENABLE (1L << 31) +#define PRI_CTRL_GAMMA (1L << 30) +#define PRI_CTRL_IND8 (2L << 26) +#define PRI_CTRL_BGR565 (5L << 26) +#define PRI_CTRL_BGR (6L << 26) +#define PRI_CTRL_RGB10 (8L << 26) +#define PRI_CTRL_BGR10 (10L << 26) +#define PRI_CTRL_RGBFP (12L << 26) +#define PRI_CTRL_RGB (14L << 26) +#define PRI_CTRL_PIXFMTMSK (31L << 26) +#define PRI_CTRL_CSC (1L << 24) +#define PRI_CTRL_ROT180 (1L << 15) +#define PRI_CTRL_NOTRICKLE (1L << 14) +#define PRI_CTRL_TILED (1L << 10) +#define PRI_CTRL_ASYNC (1L << 9) +#define PRI_LINOFF(i) (0x70184+i*0x1000) +#define PRI_STRIDE(i) (0x70188+i*0x1000) +#define PRI_SURF(i) (0x7019c+i*0x1000) +#define PRI_TILEOFF(i) (0x701a4+i*0x1000) +#define FDI_TX_CTL(i) (0x60100+i*0x1000) + +#define FW_BLC_SELF (0x20e0) +#define FW_BLC_SELF_EN (1L << 15) + +/* South display */ + +#define DREF_CTL (0xc6200) +#define RAWCLK_FREQ (0xc6204) +#define DPLL_SEL (0xc7000) + +#define DAC_CTL (0xe1100) +#define HDMI_CTL (0xe1140) +#define HDMI_BUF_CTL (0xfd024) +#define LVDS_CTL (0xe1180) +#define PCH_DP_CTL(i) (0xe4100+i*0x100) + +#define PCH_DPLL_CTL(i) (0xc6014+i*0x0008) +#define PCH_DPLL_FP0(i) (0xc6040+i*0x0008) +#define PCH_DPLL_FP1(i) (0xc6044+i*0x0008) +#define TRANS_HTOTAL(i) (0xe0000+i*0x1000) +#define TRANS_HOTAL_VAL(t,a) (((t)-1) << 16 | ((a)-1)) +#define TRANS_HBLANK(i) (0xe0004+i*0x1000) +#define TRANS_HBLANK_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define TRANS_HSYNC(i) (0xe0008+i*0x1000) +#define TRANS_HSYNC_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define TRANS_VTOTAL(i) (0xe000c+i*0x1000) +#define TRANS_VTOTAL_VAL(t,a) (((t)-1) << 16 | ((a)-1)) +#define TRANS_VBLANK(i) (0xe0010+i*0x1000) +#define TRANS_VBLANK_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define TRANS_VSYNC(i) (0xe0014+i*0x1000) +#define TRANS_VSYNC_VAL(e,s) (((e)-1) << 16 | ((s)-1)) +#define TRANS_CONF(i) (0xf0008+i*0x1000) +#define FDI_RX_CTL(i) (0xf000c+i*0x1000) + +#define OLD_BLC_PWM_CTL2 (0x61250) +#define OLD_BLC_PWM_CTL (0x61254) +#define BLM_PWM_ENABLE (1L << 31) +#define BLM_PIPE(p) ((p) << 29) +#define BLM_PHASEIN_INTST (1L << 26) +#define BLM_PHASEIN_ENABLE (1L << 25) +#define BLM_PHASEIN_INTEN (1L << 24) +#define BLM_PHASEIN_TIME(t) ((t) << 16) +#define BLM_PHASEIN_COUNT(c) ((c) << 8) +#define BLM_PHASEIN_INCR(i) ((i) << 0) +#define CPU_BLC_PWM_CTL2 (0x48250) +#define CPU_BLC_PWM_CTL (0x48254) +#define HSW_BLC_PWM_CTL (0x48350) +#define BACKLIGHT_VAL(f,l,v) ((f) << 17 | (l) << 16 | (v)) +#define BACKLIGHT_GET_FREQ(r) (((r) >> 17) & 0x7fff) +#define BACKLIGHT_GET_LEGACY(r) (((r) >> 16) & 0x1) +#define BACKLIGHT_GET_CYCLE(r) (((r) >> 0) & 0xffff) + +#define GMBUS_NUM_PORTS 6 +#define OLD_GPIOA (0x5010) +#define OLD_GPIOB (0x5014) +#define OLD_GPIOC (0x5018) +#define OLD_GPIOD (0x501c) +#define OLD_GPIOE (0x5020) +#define OLD_GPIOF (0x5024) +#define PCH_GPIOA (0xc5010) +#define PCH_GPIOB (0xc5014) +#define PCH_GPIOC (0xc5018) +#define PCH_GPIOD (0xc501c) +#define PCH_GPIOE (0xc5020) +#define PCH_GPIOF (0xc5024) +#define GPIO_CLOCK_DIR_MASK (1 << 0) +#define GPIO_CLOCK_DIR_IN (0 << 1) +#define GPIO_CLOCK_DIR_OUT (1 << 1) +#define GPIO_CLOCK_VAL_MASK (1 << 2) +#define GPIO_CLOCK_VAL_OUT (1 << 3) +#define GPIO_CLOCK_VAL_IN (1 << 4) +#define GPIO_CLOCK_PULLUP_DISABLE (1 << 5) +#define GPIO_DATA_DIR_MASK (1 << 8) +#define GPIO_DATA_DIR_IN (0 << 9) +#define GPIO_DATA_DIR_OUT (1 << 9) +#define GPIO_DATA_VAL_MASK (1 << 10) +#define GPIO_DATA_VAL_OUT (1 << 11) +#define GPIO_DATA_VAL_IN (1 << 12) +#define GPIO_DATA_PULLUP_DISABLE (1 << 13) + +#endif Index: src/sys/dev/pci/igmavar.h diff -u /dev/null src/sys/dev/pci/igmavar.h:1.1 --- /dev/null Tue Jan 21 14:52:07 2014 +++ src/sys/dev/pci/igmavar.h Tue Jan 21 14:52:07 2014 @@ -0,0 +1,76 @@ +/* $NetBSD: igmavar.h,v 1.1 2014/01/21 14:52:07 mlelstv Exp $ */ + +/* + * Copyright (c) 2014 Michael van Elst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING 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 SOFTWARE. + */ + +#ifndef IGMAVAR_H +#define IGMAVAR_H + +#include <dev/pci/pcivar.h> +#include <dev/pci/pcireg.h> + +struct igma_chip; +struct igma_chip_ops { + void (*barrier)(const struct igma_chip *, int); + u_int32_t (*read_reg)(const struct igma_chip *, int); + void (*write_reg)(const struct igma_chip *, int, u_int32_t); + u_int8_t (*read_vga)(const struct igma_chip *, int); + void (*write_vga)(const struct igma_chip *, int, u_int8_t); +#if 0 + u_int8_t (*read_crtc)(const struct igma_chip *, int); + void (*write_crtc)(const struct igma_chip *, int, u_int8_t); +#endif +}; + +struct igma_chip { + const struct igma_chip_ops *ops; + + bus_space_tag_t gttt; + bus_space_handle_t gtth; + + bus_space_tag_t mmiot; + bus_space_handle_t mmioh; + + bus_space_tag_t vgat; + bus_space_handle_t vgah; + + bus_space_tag_t gmt; + bus_space_handle_t gmh; + bus_addr_t gmb; + + int num_gmbus; + int gpio_offset; + int vga_cntrl; + int num_pipes; + int use_pipe; + u_int32_t pri_cntrl; + int backlight_cntrl; + int backlight_cntrl2; + int backlight_factor; + + unsigned quirks; +}; +#define IGMA_PLANESTART_QUIRK (1L << 0) +#define IGMA_PFITDISABLE_QUIRK (1L << 1) + +struct igma_attach_args { + const struct igma_chip_ops *iaa_chip_ops; + struct igma_chip iaa_chip; + bool iaa_console; + char iaa_name[32]; +}; + +#endif Index: src/sys/dev/pci/igma/igmafb.c diff -u /dev/null src/sys/dev/pci/igma/igmafb.c:1.1 --- /dev/null Tue Jan 21 14:52:07 2014 +++ src/sys/dev/pci/igma/igmafb.c Tue Jan 21 14:52:07 2014 @@ -0,0 +1,590 @@ +/* $NetBSD: igmafb.c,v 1.1 2014/01/21 14:52:07 mlelstv Exp $ */ + +/* + * Copyright (c) 2012 Michael van Elst + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING 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 SOFTWARE. + */ + +/* + * Intel Graphic Media Accelerator + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: igmafb.c,v 1.1 2014/01/21 14:52:07 mlelstv Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/bus.h> +#include <sys/kmem.h> + +#include <dev/pci/pcireg.h> +#include <dev/pci/pcivar.h> +#include <dev/pci/pcidevs.h> +#include <dev/pci/pciio.h> + +#include <dev/videomode/videomode.h> + +#include <dev/wscons/wsdisplayvar.h> +#include <dev/wscons/wsconsio.h> +#include <dev/wsfont/wsfont.h> +#include <dev/rasops/rasops.h> +#include <dev/wscons/wsdisplay_vconsvar.h> +#include <dev/pci/wsdisplay_pci.h> + +#include <dev/pci/igmareg.h> +#include <dev/pci/igmavar.h> + +#include "opt_voyagerfb.h" + +struct igmafb_softc { + device_t sc_dev; + + int sc_width; + int sc_height; + int sc_depth; + int sc_stride; + void *sc_fbaddr; + bus_size_t sc_fbsize; + struct vcons_screen sc_console_screen; + struct wsscreen_descr sc_defaultscreen_descr; + const struct wsscreen_descr *sc_screens[1]; + struct wsscreen_list sc_screenlist; + struct vcons_data vd; + + struct igma_chip sc_chip; + void *sc_vga_save; + + int sc_backlight; + int sc_brightness; + int sc_brightness_max; +}; + +static int igmafb_match(device_t, cfdata_t, void *); +static void igmafb_attach(device_t, device_t, void *); + +CFATTACH_DECL_NEW(igmafb, sizeof(struct igmafb_softc), + igmafb_match, igmafb_attach, NULL, NULL); + +static int igmafb_ioctl(void *, void *, u_long, void *, int, struct lwp *); +static paddr_t igmafb_mmap(void *, void *, off_t, int); +static void igmafb_pollc(void *v, int); + +static /*const*/ struct wsdisplay_accessops igmafb_accessops = { + igmafb_ioctl, + igmafb_mmap, + NULL, /* alloc_screen */ + NULL, /* free_screen */ + NULL, /* show_screen */ + NULL, /* load_font */ + igmafb_pollc, /* pollc */ + NULL /* scroll */ +}; + +static void igmafb_init_screen(void *, struct vcons_screen *, int, long *); +static void igmafb_guess_size(struct igmafb_softc *, int *, int*); +static void igmafb_set_mode(struct igmafb_softc *, bool); + +static void igmafb_planestart_quirk(struct igmafb_softc *); +static void igmafb_pfitdisable_quirk(struct igmafb_softc *); + +static void igmafb_get_brightness_max(struct igmafb_softc *, int *); +static void igmafb_get_brightness(struct igmafb_softc *, int *); +static void igmafb_set_brightness(struct igmafb_softc *, int); + +static int +igmafb_match(device_t parent, cfdata_t match, void *aux) +{ + struct igma_attach_args *iaa = (struct igma_attach_args *)aux; + + if (strcmp(iaa->iaa_name, "igmafb") == 0) return 100; + return 0; +} + +static void +igmafb_attach(device_t parent, device_t self, void *aux) +{ + struct igmafb_softc *sc = device_private(self); + struct igma_attach_args *iaa = (struct igma_attach_args *)aux; + struct rasops_info *ri; + prop_dictionary_t dict; + bool is_console; + unsigned long defattr; + struct wsemuldisplaydev_attach_args waa; + + sc->sc_dev = self; + + aprint_normal("\n"); + + dict = device_properties(self); + prop_dictionary_get_bool(dict, "is_console", &is_console); + if (iaa->iaa_console) + is_console = true; + + sc->sc_chip = iaa->iaa_chip; + + sc->sc_fbaddr = bus_space_vaddr(sc->sc_chip.gmt, sc->sc_chip.gmh); + sc->sc_fbsize = 16 * 1024 * 1024; + + igmafb_guess_size(sc, &sc->sc_width, &sc->sc_height); + sc->sc_depth = 32; + sc->sc_stride = (sc->sc_width*4 + 511)/512*512; + + aprint_normal("%s: %d x %d, %d bit, stride %d\n", device_xname(self), + sc->sc_width, sc->sc_height, sc->sc_depth, sc->sc_stride); + + aprint_normal("%s: %d MB video memory at 0x%p\n", device_xname(self), + (int)sc->sc_fbsize >> 20, (void *)sc->sc_chip.gmb); + + sc->sc_vga_save = kmem_alloc(256*1024, KM_SLEEP); + + igmafb_get_brightness(sc, &sc->sc_brightness); + igmafb_get_brightness_max(sc, &sc->sc_brightness_max); + sc->sc_backlight = sc->sc_brightness != 0; + + sc->sc_defaultscreen_descr = (struct wsscreen_descr){ + "default", + 0, 0, + NULL, + 8, 16, + WSSCREEN_WSCOLORS | WSSCREEN_HILIT, + NULL + }; + sc->sc_screens[0] = &sc->sc_defaultscreen_descr; + sc->sc_screenlist = (struct wsscreen_list){1, sc->sc_screens}; + + vcons_init(&sc->vd, sc, &sc->sc_defaultscreen_descr, + &igmafb_accessops); + sc->vd.init_screen = igmafb_init_screen; + + /* enable hardware display */ + igmafb_set_mode(sc, true); + + ri = &sc->sc_console_screen.scr_ri; + + if (is_console) { + vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, + &defattr); + + sc->sc_console_screen.scr_flags |= VCONS_SCREEN_IS_STATIC + | VCONS_NO_COPYROWS | VCONS_NO_COPYCOLS; + vcons_redraw_screen(&sc->sc_console_screen); + + sc->sc_defaultscreen_descr.textops = &ri->ri_ops; + sc->sc_defaultscreen_descr.capabilities = ri->ri_caps; + sc->sc_defaultscreen_descr.nrows = ri->ri_rows; + sc->sc_defaultscreen_descr.ncols = ri->ri_cols; + + wsdisplay_cnattach(&sc->sc_defaultscreen_descr, ri, 0, 0, + defattr); + vcons_replay_msgbuf(&sc->sc_console_screen); + } else { + if (sc->sc_console_screen.scr_ri.ri_rows == 0) { + /* do some minimal setup to avoid weirdness later */ + vcons_init_screen(&sc->vd, &sc->sc_console_screen, 1, + &defattr); + } else + (*ri->ri_ops.allocattr)(ri, 0, 0, 0, &defattr); + } + + waa.console = is_console; + waa.scrdata = &sc->sc_screenlist; + waa.accessops = &igmafb_accessops; + waa.accesscookie = &sc->vd; + + config_found(sc->sc_dev, &waa, wsemuldisplaydevprint); +} + +/* + * wsdisplay accessops + */ + +static int +igmafb_ioctl(void *v, void *vs, u_long cmd, void *data, int flags, + struct lwp *l) +{ + struct vcons_data *vd = v; + struct igmafb_softc *sc = vd->cookie; + struct wsdisplay_fbinfo *wdf; + struct vcons_screen *ms = vd->active; + struct wsdisplayio_fbinfo *fbi; + struct wsdisplay_param *param; + int val; + + switch (cmd) { + case WSDISPLAYIO_GTYPE: + *(u_int *)data = WSDISPLAY_TYPE_PCIMISC; + return 0; + case WSDISPLAYIO_GINFO: + if (ms == NULL) + return ENODEV; + wdf = data; + wdf->width = ms->scr_ri.ri_width; + wdf->height = ms->scr_ri.ri_height; + wdf->depth = ms->scr_ri.ri_depth; + wdf->cmsize = 256; /* XXX */ + return 0; + case WSDISPLAYIO_LINEBYTES: + if (ms == NULL) + return ENODEV; + *(u_int *)data = ms->scr_ri.ri_stride; + return 0; + case WSDISPLAYIO_GET_FBINFO: + fbi = data; + return wsdisplayio_get_fbinfo(&ms->scr_ri, fbi); + case WSDISPLAYIO_SVIDEO: + val = (*(u_int *)data) != WSDISPLAYIO_VIDEO_OFF; + sc->sc_backlight = val; + if (val) + igmafb_set_brightness(sc, sc->sc_brightness); + else + igmafb_set_brightness(sc, 0); + return 0; + case WSDISPLAYIO_GETPARAM: + param = (struct wsdisplay_param *)data; + switch (param->param) { + case WSDISPLAYIO_PARAM_BRIGHTNESS: + param->min = 0; + param->max = 255; + if (sc->sc_backlight) + igmafb_get_brightness(sc, &val); + else + val = sc->sc_brightness; + val = val * 255 / sc->sc_brightness_max; + param->curval = val; + return 0; + case WSDISPLAYIO_PARAM_BACKLIGHT: + param->min = 0; + param->max = 1; + param->curval = sc->sc_backlight; + return 0; + } + return EPASSTHROUGH; + case WSDISPLAYIO_SETPARAM: + param = (struct wsdisplay_param *)data; + switch (param->param) { + case WSDISPLAYIO_PARAM_BRIGHTNESS: + val = param->curval; + if (val < 0) + val = 0; + if (val > 255) + val = 255; + val = val * sc->sc_brightness_max / 255; + sc->sc_brightness = val; + if (sc->sc_backlight) + igmafb_set_brightness(sc, val); + return 0; + case WSDISPLAYIO_PARAM_BACKLIGHT: + val = param->curval; + sc->sc_backlight = val; + if (val) + igmafb_set_brightness(sc, sc->sc_brightness); + else + igmafb_set_brightness(sc, 0); + return 0; + } + return EPASSTHROUGH; + } + + return EPASSTHROUGH; +} + +static paddr_t +igmafb_mmap(void *v, void *vs, off_t offset, int prot) +{ + struct vcons_data *vd = v; + struct igmafb_softc *sc = vd->cookie; + + if ((offset & PAGE_MASK) != 0) + return -1; + + if (offset < 0 || offset >= sc->sc_fbsize) + return -1; + + return bus_space_mmap(sc->sc_chip.gmt, sc->sc_chip.gmb, offset, prot, + BUS_SPACE_MAP_LINEAR); +} + +static void +igmafb_pollc(void *v, int on) +{ + struct vcons_data *vd = v; + struct igmafb_softc *sc = vd->cookie; + + if (sc == NULL) + return; + if (sc->sc_console_screen.scr_vd == NULL) + return; + + if (on) + vcons_enable_polling(&sc->vd); + else + vcons_disable_polling(&sc->vd); +} + +static void +igmafb_init_screen(void *cookie, struct vcons_screen *scr, + int existing, long *defattr) +{ + struct igmafb_softc *sc = cookie; + struct rasops_info *ri = &scr->scr_ri; + + memset(ri, 0, sizeof(struct rasops_info)); + + ri->ri_depth = sc->sc_depth; + ri->ri_width = sc->sc_width; + ri->ri_height = sc->sc_height; + ri->ri_stride = sc->sc_stride; + ri->ri_flg = RI_CENTER | RI_FULLCLEAR; + + ri->ri_bits = (char *)sc->sc_fbaddr; + + if (existing) { + ri->ri_flg |= RI_CLEAR; + } + + switch (sc->sc_depth) { + case 32: + ri->ri_rnum = 8; + ri->ri_gnum = 8; + ri->ri_bnum = 8; + ri->ri_rpos = 16; + ri->ri_gpos = 8; + ri->ri_bpos = 0; + break; + } + + rasops_init(ri, 0, 0); + ri->ri_caps = WSSCREEN_WSCOLORS; + + rasops_reconfig(ri, sc->sc_height / ri->ri_font->fontheight, + sc->sc_width / ri->ri_font->fontwidth); + + ri->ri_hw = scr; +} + +static void +igmafb_guess_size(struct igmafb_softc *sc, int *widthp, int *heightp) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + int pipe = cd->use_pipe; + u_int32_t r; + + r = co->read_reg(cd, PIPE_HTOTAL(pipe)); + *widthp = PIPE_HTOTAL_GET_ACTIVE(r); + r = co->read_reg(cd, PIPE_VTOTAL(pipe)); + *heightp = PIPE_VTOTAL_GET_ACTIVE(r); + + aprint_normal("%s: vga active size %d x %d\n", + device_xname(sc->sc_dev), + *widthp, *heightp); + + if (*widthp < 640 || *heightp < 400) { + r = co->read_reg(cd, PF_WINSZ(pipe)); + *widthp = PF_WINSZ_GET_WIDTH(r); + *heightp = PF_WINSZ_GET_HEIGHT(r); + + aprint_normal("%s: window size %d x %d\n", + device_xname(sc->sc_dev), + *widthp, *heightp); + } + + if (*widthp < 640) *widthp = 640; + if (*heightp < 400) *heightp = 400; +} + +static void +igmafb_set_mode(struct igmafb_softc *sc, bool enable) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + int pipe = cd->use_pipe; + u_int32_t r; + u_int8_t b; + int i; + + if (enable) { + /* disable VGA machinery */ + b = co->read_vga(cd, 0x01); + co->write_vga(cd, 0x01, b | 0x20); + + /* disable VGA compatible display */ + r = co->read_reg(cd, sc->sc_chip.vga_cntrl); + co->write_reg(cd, sc->sc_chip.vga_cntrl, r | VGA_CNTRL_DISABLE); + + /* save VGA memory */ + memcpy(sc->sc_vga_save, sc->sc_fbaddr, 256*1024); + + /* configure panel fitter */ + co->write_reg(cd, PF_WINPOS(pipe), + PF_WINPOS_VAL(0, 0)); + co->write_reg(cd, PF_WINSZ(pipe), + PF_WINSZ_VAL(sc->sc_width, sc->sc_height)); + + /* pipe size */ + co->write_reg(cd, PIPE_SRCSZ(pipe), + PIPE_SRCSZ_VAL(sc->sc_width, sc->sc_height)); + + /* enable pipe */ + co->write_reg(cd, PIPE_CONF(pipe), + PIPE_CONF_ENABLE | PIPE_CONF_8BPP); + + /* configure planes */ + r = co->read_reg(cd, PRI_CTRL(pipe)); + r &= ~(PRI_CTRL_PIXFMTMSK | PRI_CTRL_TILED); + r |= PRI_CTRL_ENABLE | PRI_CTRL_BGR; + co->write_reg(cd, PRI_CTRL(pipe), r | cd->pri_cntrl); + co->write_reg(cd, PRI_LINOFF(pipe), 0); + co->write_reg(cd, PRI_STRIDE(pipe), sc->sc_stride); + co->write_reg(cd, PRI_SURF(pipe), 0); + co->write_reg(cd, PRI_TILEOFF(pipe), 0); + + if (cd->quirks & IGMA_PLANESTART_QUIRK) + igmafb_planestart_quirk(sc); + + if (cd->quirks & IGMA_PFITDISABLE_QUIRK) + igmafb_pfitdisable_quirk(sc); + } else { + /* disable planes */ + co->write_reg(cd, PRI_CTRL(pipe), 0 | cd->pri_cntrl); + co->write_reg(cd, PRI_LINOFF(pipe), 0); + co->write_reg(cd, PRI_STRIDE(pipe), 2560); + co->write_reg(cd, PRI_SURF(pipe), 0); + co->write_reg(cd, PRI_TILEOFF(pipe), 0); + + /* pipe size */ + co->write_reg(cd, PIPE_SRCSZ(pipe), + PIPE_SRCSZ_VAL(720,400)); + + /* disable pipe */ + co->write_reg(cd, PIPE_CONF(pipe), 0); + for (i=0; i<10; ++i) { + delay(10); + if ((co->read_reg(cd, PIPE_CONF(pipe)) & PIPE_CONF_STATE) == 0) + break; + } + + /* workaround before enabling VGA */ + r = co->read_reg(cd, 0x42000); + co->write_reg(cd, 0x42000, (r & 0x1fffffff) | 0xa0000000); + r = co->read_reg(cd, 0x42004); + co->write_reg(cd, 0x42004, (r & 0xfbffffff) | 0x00000000); + + /* configure panel fitter */ + co->write_reg(cd, PF_WINPOS(pipe), + PF_WINPOS_VAL(0, 0)); + co->write_reg(cd, PF_WINSZ(pipe), + PF_WINSZ_VAL(sc->sc_width, sc->sc_height)); + + /* enable VGA compatible display */ + r = co->read_reg(cd, sc->sc_chip.vga_cntrl); + co->write_reg(cd, sc->sc_chip.vga_cntrl, r & ~VGA_CNTRL_DISABLE); + + /* enable VGA machinery */ + b = co->read_vga(cd, 0x01); + co->write_vga(cd, 0x01, b & ~0x20); + + /* restore VGA memory */ + memcpy(sc->sc_fbaddr, sc->sc_vga_save, 256*1024); + + /* enable pipe again */ + co->write_reg(cd, PIPE_CONF(pipe), + PIPE_CONF_ENABLE | PIPE_CONF_6BPP | PIPE_CONF_DITHER); + } +} + +static void +igmafb_planestart_quirk(struct igmafb_softc *sc) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + int pipe = cd->use_pipe; + u_int32_t cntrl, fwbcl; + + /* disable self refresh */ + fwbcl = co->read_reg(cd, FW_BLC_SELF); + co->write_reg(cd, FW_BLC_SELF, fwbcl & ~FW_BLC_SELF_EN); + + cntrl = co->read_reg(cd, CUR_CNTR(pipe)); + co->write_reg(cd, CUR_CNTR(pipe), 1<<5 | 0x07); + + /* "wait for vblank" */ + delay(40000); + + co->write_reg(cd, CUR_CNTR(pipe), cntrl); + co->write_reg(cd, CUR_BASE(pipe), + co->read_reg(cd, CUR_BASE(pipe))); + + co->write_reg(cd, FW_BLC_SELF, fwbcl); +} + +static void +igmafb_pfitdisable_quirk(struct igmafb_softc *sc) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + u_int32_t r; + + /* disable i965 panel fitter */ + r = co->read_reg(cd, PF_CTRL_I965); + co->write_reg(cd, PF_CTRL_I965, r & ~PF_ENABLE); +} + +static void +igmafb_get_brightness_max(struct igmafb_softc *sc, int *valp) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + u_int32_t r, f; + + r = co->read_reg(cd, cd->backlight_cntrl); + f = BACKLIGHT_GET_FREQ(r); + if (f == 0) { + r = co->read_reg(cd, RAWCLK_FREQ); + f = r * 1000000 / (200 * 128); + if (f == 0 || f > 32767) + f = 125 * 100000 / (200 * 128); + } + + *valp = f; +} + +static void +igmafb_get_brightness(struct igmafb_softc *sc, int *valp) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + u_int32_t r, v; + + r = co->read_reg(cd, cd->backlight_cntrl); + v = BACKLIGHT_GET_CYCLE(r); + *valp = v; +} + +static void +igmafb_set_brightness(struct igmafb_softc *sc, int val) +{ + const struct igma_chip *cd = &sc->sc_chip; + const struct igma_chip_ops *co = cd->ops; + u_int32_t r, f, l; + + r = co->read_reg(cd, cd->backlight_cntrl); + f = BACKLIGHT_GET_FREQ(r); + l = BACKLIGHT_GET_LEGACY(r); + + co->write_reg(cd, cd->backlight_cntrl, + BACKLIGHT_VAL(f,l,val)); +} +