/* $NetBSD: igma.c,v 1.3 2016/02/14 19:54:21 chs 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 __KERNEL_RCSID(0, "$NetBSD: igma.c,v 1.3 2016/02/14 19:54:21 chs Exp $"); #include "vga.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if NVGA > 0 #include #include #include #include #endif #include #include #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; #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; isc_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 memset(&iba, 0, sizeof(iba)); 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