/*- * Copyright 2003 John-Mark Gurney * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Id: zr_os.c,v 1.20 2003/05/01 09:02:27 jmg Exp $ */ /* * Cribbed from bktr driver. */ #include /* defines used in kernel.h */ #include /* types used in module initialization */ #include /* uprintf */ #include #include /* cdevsw struct */ #include /* uio struct */ #include #include #include #include #include #include /* structs, prototypes for pci bus stuff */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* For get_pci macros! */ #include #include #include "iicbb_if.h" #include #include #include #include #include #include #include #define ZR_DEBUG 1 #define CDEV_MAJOR 242 #if ZR_DEBUG #define ZR_DPRINTF(...) do { printf("%s: ", zr->zr_xname); printf(__VA_ARGS__); } while (0) #define ZR_PRINTF(...) do { printf(__VA_ARGS__); } while (0) #else #define ZR_DPRINTF(x) do {} while (0) #define ZR_PRINTF(x) do {} while (0) #endif /* Prototypes */ static int zr_probe(device_t dev); static int zr_attach(device_t dev); static int zr_shutdown(device_t dev); static int zr_detach(device_t dev); static int zr_suspend(device_t dev); static int zr_resume(device_t dev); static int zr_read_ivar(device_t dev, device_t child, int indx, uintptr_t *res); static int zr_write_ivar(device_t dev, device_t child, int indx, uintptr_t val); static void zr_child_detached(device_t dev, device_t child); static int zr_dev_po_read(device_t dev, u_int8_t id, u_int8_t reg, u_int8_t *data); static int zr_dev_po_write(device_t dev, u_int8_t id, u_int8_t reg, u_int8_t data); static void zr_dev_po_set_durrec(device_t dev, u_int8_t id, u_int8_t dur, u_int8_t rec); static void zr_dev_po_gpio(device_t dev, u_int8_t pin, u_int8_t val); static void zr_dev_po_enable_ints(device_t dev, u_int8_t intr); static void zr_intr(void *arg); static d_open_t zr_os_open; static d_close_t zr_os_close; static d_read_t zr_os_read; static d_ioctl_t zr_os_ioctl; static d_mmap_t zr_os_mmap; /* Data Structures & Defs */ static struct cdevsw zr_cdevsw = { .d_open = zr_os_open, .d_close = zr_os_close, .d_read = zr_os_read, .d_ioctl = zr_os_ioctl, .d_mmap = zr_os_mmap, .d_name = "zr", #ifndef MAJOR_AUTO .d_maj = CDEV_MAJOR, #endif }; static device_method_t zr_methods[] = { /* device interface */ DEVMETHOD(device_probe, zr_probe), DEVMETHOD(device_attach, zr_attach), DEVMETHOD(device_detach, zr_detach), DEVMETHOD(device_shutdown, zr_shutdown), DEVMETHOD(device_suspend, zr_suspend), DEVMETHOD(device_resume, zr_resume), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), DEVMETHOD(bus_read_ivar, zr_read_ivar), DEVMETHOD(bus_write_ivar, zr_write_ivar), DEVMETHOD(bus_child_detached, zr_child_detached), /* iicbb interface */ DEVMETHOD(iicbb_callback, zr_i2c_callback), DEVMETHOD(iicbb_setsda, zr_i2c_setsda), DEVMETHOD(iicbb_setscl, zr_i2c_setscl), DEVMETHOD(iicbb_getsda, zr_i2c_getsda), DEVMETHOD(iicbb_getscl, zr_i2c_getscl), DEVMETHOD(iicbb_reset, zr_i2c_reset), /* postoffice interface */ DEVMETHOD(postoffice_read, zr_dev_po_read), DEVMETHOD(postoffice_write, zr_dev_po_write), DEVMETHOD(postoffice_set_durrec, zr_dev_po_set_durrec), DEVMETHOD(postoffice_gpio, zr_dev_po_gpio), DEVMETHOD(postoffice_enable_ints, zr_dev_po_enable_ints), { 0, 0} }; static driver_t zr_driver = { "zr", zr_methods, sizeof(struct zr_softc) }; static devclass_t zr_devclass; video_mode_t zr_mode; SYSCTL_INT(_hw, OID_AUTO, zr_mode, CTLFLAG_RW, &zr_mode, 0, "default video mode"); unsigned long zr_input; SYSCTL_INT(_hw, OID_AUTO, zr_input, CTLFLAG_RW, &zr_input, 0, "default input"); u_int zr_masksize = 768 * 576 / 8; TUNABLE_INT("hw.zr.masksize", &zr_masksize); u_int zr_maxbufsize = 128 * 1024; TUNABLE_INT("hw.zr.maxbufsize", &zr_maxbufsize); MALLOC_DEFINE(M_ZR, "zr", "Zoran data"); /* Public Functions */ /* * if copy is true, then memory will be allocated to copy the struct */ void zr_set_norm(struct zr_softc *zr, const struct tvnorm *tnp, int copy) { struct tvnorm *alloc; alloc = NULL; if (copy) alloc = malloc(sizeof *alloc, M_ZR, M_WAITOK); mtx_lock(&zr->zr_lock); if (copy) { if (!zr->zr_dec_norm_allocated) { zr->zr_dec_norm_allocated = 1; } else { free(alloc, M_ZR); alloc = (struct tvnorm *)(uintptr_t)zr->zr_dec_norm; } *alloc = *tnp; zr->zr_dec_norm = alloc; } else { if (zr->zr_dec_norm_allocated) free((void *)(uintptr_t)zr->zr_dec_norm, M_ZR); zr->zr_dec_norm = tnp; } mtx_unlock(&zr->zr_lock); } void zr_gpio(struct zr_softc *zr, u_int8_t pin, u_int8_t val) { asrs_t asr; asr.reg = zr_read_asrs(zr, ZR_GPPR_REG); if (val) asr.gppr.GenPurIO |= 1 << pin; else asr.gppr.GenPurIO &= ~(1 << pin); zr_write_asrs(zr, ZR_GPPR_REG, asr.reg); } uint32_t zr_read_asrs(struct zr_softc *zr, int off) { if (off > (0x400 - 4)) { /* XXX - panic? */ ZRSC_PRINTF(zr, "invalid offset passed to zr_read_asrs\n"); return 0; } return bus_space_read_4(zr->zr_bus.memt, zr->zr_bus.memh, off); } void zr_write_asrs(struct zr_softc *zr, int off, uint32_t val) { if (off > (0x400 - 4)) { /* XXX - panic? */ ZRSC_PRINTF(zr, "invalid offset passed to zr_read_asrs\n"); return; } bus_space_write_4(zr->zr_bus.memt,zr->zr_bus.memh, off, val); } /* * Function to verify that base address is valid. It requires that the * mapping be in physical contigious space. * Return 0 if contigious and same object. Otherwise returns errno to * indicate error. * * We use a u_int32_t because the chip is only a 32bit device. We will * return an EFAULT if the physical address is larger than 32bits. * * XXX - This function should go away and be replaced by the bus_dma * functions. I have to map the userland buffers into kernel space myself * and that's a bit complicated. * * XXX - 5.0-R has a broken bus_dmamap_load for buffers. I get a double * page fault using it. */ int verify_contig(os_proc_t proc, caddr_t base, int len, u_int32_t *phys) { int error; vm_offset_t uva, pageno; int page_offset; vm_map_t tmap; vm_prot_t reqprot, out_prot; vm_map_entry_t out_entry; vm_object_t object, oobj; vm_pindex_t pindex; boolean_t wired; vm_page_t m; object = NULL; reqprot = VM_PROT_WRITE; oobj = NULL; error = 0; for (uva = (vm_offset_t)base; error == 0 && uva < (vm_offset_t)base + len; uva += PAGE_SIZE) { pageno = trunc_page(uva); page_offset = uva - pageno; tmap = &proc->p_vmspace->vm_map; /* Fault the page in */ if ((error = vm_fault(tmap, pageno, reqprot, VM_FAULT_NORMAL))) return error; if ((error = vm_map_lookup(&tmap, pageno, reqprot, &out_entry, &object, &pindex, &out_prot, &wired))) { return EFAULT; } /* find the real underlying page */ m = vm_page_lookup(object, pindex); while (m == NULL && object->backing_object) { pindex += OFF_TO_IDX(object->backing_object_offset); object = object->backing_object; m = vm_page_lookup(object, pindex); } if (m == NULL) { error = ENOENT; printf("page not found\n"); goto fail; } if (uva == (vm_offset_t)base) { *phys = (u_int32_t)((caddr_t)m->phys_addr + page_offset); /* make sure the address isn't >32bits */ if (((caddr_t)*phys) != ((caddr_t)m->phys_addr + page_offset)) goto fail; } if (oobj == NULL) oobj = object; else if (oobj != object) { printf("object not match, base: %p, uva: 0x%x\n", base, uva); error = EFAULT; } else if (m->phys_addr != (caddr_t)*phys + uva - base) { printf( "base not contig, base phys: 0x%x, phys: 0x%x, uva: 0x%x, base: %p\n", *phys, m->phys_addr, uva, base); error = EFAULT; } /* XXX - should I keep a ref for the object? */ vm_map_lookup_done(tmap, out_entry); } return error; fail: object = NULL; vm_map_lookup_done(tmap, out_entry); #if 0 printf("verify_contig error: %d, uva: 0x%x, base: %p, len: 0x%x\n", error, uva, base, len); #endif return error; } #ifdef ZR_USE_BUS_DMA static void zr_dma_callback(void *data, bus_dma_segment_t *segs, int nsegs, int error) { struct zr_dmamem *zdp; int i; zdp = (struct zr_dmamem *)data; printf("zr: zdp: %p, nsegs: %d\n", zdp, nsegs); if (error != 0 || (nsegs != 1 && nsegs > zdp->zd_maxsegs)) { printf("zr: in zr_dma_callback error: %d, nsegs: %d\n", error, nsegs); return; } for (i = 0; i < nsegs; i++) zdp->zd_segs[i] = segs[i]; zdp->zd_nsegs = nsegs; } static void zr_dma_callback2(void *data, bus_dma_segment_t *segs, int nsegs, bus_size_t size, int error) { zr_dma_callback(data, segs, nsegs, error); } int zr_get_user_uio(struct zr_softc *zr, bus_dma_tag_t pt, struct zr_dmamem **zd, struct uio *uio, int nsegs, int read) { int error; struct zr_dmamem *zdp; error = 0; zdp = malloc(sizeof *zdp + (nsegs - 1) * sizeof *zdp->zd_segs, M_ZR, M_WAITOK); if ((error = bus_dma_tag_create(pt, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, BUS_SPACE_MAXSIZE, BUS_SPACE_MAXSIZE, nsegs, BUS_DMA_ALLOCNOW, &zdp->zd_tag))) goto fail; if ((error = bus_dmamap_create(zdp->zd_tag, 0, &zdp->zd_map))) goto fail1; if ((error = bus_dmamap_load_uio(zdp->zd_tag, zdp->zd_map, uio, zr_dma_callback2, zd, 0))) goto fail2; *zd = zdp; return 0; fail2: bus_dmamap_destroy(zdp->zd_tag, zdp->zd_map); zdp->zd_map = NULL; fail1: bus_dma_tag_destroy(zdp->zd_tag); zdp->zd_tag = NULL; fail: free(zdp, M_ZR); return error; } int zr_free_user_uio(struct zr_softc *zr, struct zr_dmamem *zd) { int error; bus_dmamap_unload(zd->zd_tag, zd->zd_map); bus_dmamap_destroy(zd->zd_tag, zd->zd_map); zd->zd_map = NULL; if ((error = bus_dma_tag_destroy(zd->zd_tag))) { ZRSC_PRINTF(zr, "bus_dma_tag_destroy failed: %d\n", error); return error; } zd->zd_tag = NULL; return 0; } #if notyet int zr_get_user_buf(struct zr_softc *zr, bus_dma_tag_t pt, struct zr_dmamem **zd, caddr_t uaddr, int size, os_proc_t proc, int nsegs, int read) { struct zr_dmamem *zdp; int error; vm_offset_t uva, pageno; int page_offset; vm_map_t tmap; vm_prot_t reqprot, out_prot; vm_map_entry_t out_entry; vm_object_t object, oobj; vm_pindex_t pindex; boolean_t wired; vm_page_t m; error = 0; zdp = malloc(sizeof *zdp + (nsegs - 1) * sizeof *zdp->zd_segs, M_ZR, M_WAITOK); if ((error = bus_dma_tag_create(pt, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, size, size, nsegs, BUS_DMA_ALLOCNOW, zdp->zd_tag))) goto fail; /* map user space addr into kernel space */ object = NULL; reqprot = read ? VM_PROT_READ : VM_PROT_WRITE; oobj = NULL; error = 0; for (uva = (vm_offset_t)uaddr; error == 0 && uva < (vm_offset_t)uaddr + size; uva += PAGE_SIZE) { pageno = trunc_page(uva); page_offset = uva - pageno; tmap = &proc->p_vmspace->vm_map; if ((error = vm_map_lookup(&tmap, pageno, reqprot, &out_entry, &object, &pindex, &out_prot, &wired))) { return EFAULT; } *zd = zdp; return 0; fail: free(zdp, M_ZR); return error; } int zr_free_user_buf(struct zr_softc *zr, struct zr_dmamem *zd) { } #endif int zr_get_mem(struct zr_softc *zr, bus_dma_tag_t pt, struct zr_dmamem *zd, int size, int nsegs) { struct zr_dmamem dm; int error; dm.zd_size = size; dm.zd_nsegs = 0; if ((error = bus_dma_tag_create(pt, 1, 0, BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL, size, size, nsegs, BUS_DMA_ALLOCNOW, &dm.zd_tag))) return error; if ((error = bus_dmamem_alloc(dm.zd_tag, &dm.zd_vaddr, BUS_DMA_NOWAIT, &dm.zd_map))) goto fail; ZRSC_PRINTF(zr, "vaddr: %p, zd: %p\n", dm.zd_vaddr, zd); *zd = dm; if ((error = bus_dmamap_load(dm.zd_tag, dm.zd_map, dm.zd_vaddr, size, zr_dma_callback, zd, 0)) != 0 && error != EINPROGRESS) goto fail1; if (error == EINPROGRESS) ZRSC_PRINTF(zr, "zr_get_mem EINPROGRESS\n"); return 0; fail1: bus_dmamem_free(dm.zd_tag, dm.zd_vaddr, dm.zd_map); dm.zd_map = NULL; fail: bus_dma_tag_destroy(dm.zd_tag); dm.zd_tag = NULL; return error; } int zr_free_mem(struct zr_softc *zr, struct zr_dmamem *zd) { int error; bus_dmamap_unload(zd->zd_tag, zd->zd_map); bus_dmamem_free(zd->zd_tag, zd->zd_vaddr, zd->zd_map); zd->zd_vaddr = NULL; zd->zd_map = NULL; if ((error = bus_dma_tag_destroy(zd->zd_tag))) { ZRSC_PRINTF(zr, "bus_dma_tag_destroy failed: %d\n", error); return error; } zd->zd_tag = NULL; return 0; } #else /* !defined(ZR_USE_BUS_DMA) */ u_int32_t zr_dmamem_to_phys(struct zr_softc *zr, struct zr_dmamem *zd) { return vtophys(zd->zd_vaddr); } int zr_get_mem(struct zr_softc *zr, bus_dma_tag_t pt, struct zr_dmamem *zd, int size, int nsegs) { zd->zd_size = size; zd->zd_vaddr = vm_page_alloc_contig(size, 0, 0xffffffff, PAGE_SIZE); /*ZRSC_PRINTF(zr, "zd_size: %d, zd_vaddr: 0x%08x\n", size, zd->zd_vaddr);*/ if (zd->zd_vaddr == NULL) return ENOMEM; return 0; } int zr_free_mem(struct zr_softc *zr, struct zr_dmamem *zd) { kmem_free(kernel_map, zd->zd_vaddr, zd->zd_size); return 0; } #endif /* ZR_USE_BUS_DMA */ void zr_release_coders(struct zr_softc *zr) { if (zr->zr_encoder != NULL) { device_unbusy(zr->zr_encoder); zr->zr_encoder = NULL; } if (zr->zr_decoder != NULL) { device_unbusy(zr->zr_decoder); zr->zr_decoder = NULL; } } void zr_find_coders(struct zr_softc *zr) { device_t dev = zr->zr_self; int i; int error; if (zr->zr_encoder == NULL) { i = 1; if ((error = videocode_findtype(dev, VIDEO_ENCODER, &zr->zr_encoder, &i)) || i != 1) ZRSC_PRINTF(zr, "findtype for encoder: error: %d, i: %d\n", error, i); if (zr->zr_encoder != NULL) device_busy(zr->zr_encoder); } if (zr->zr_decoder == NULL) { i = 1; if ((error = videocode_findtype(dev, VIDEO_DECODER, &zr->zr_decoder, &i)) || i != 1) ZRSC_PRINTF(zr, "findtype for decoder: error: %d, i: %d\n", error, i); if (zr->zr_decoder != NULL) device_busy(zr->zr_decoder); } /*ZRSC_PRINTF(zr, "encoder: %p, decoder: %p\n", zr->zr_encoder, zr->zr_decoder);*/ } /* Functions */ /* Module/Device Functions */ static int zr_probe(device_t dev) { switch (pci_get_vendor(dev)) { case PCI_VENDOR_ZORAN: /* Zoran */ switch (pci_get_device(dev)) { case PCI_PRODUCT_ZR360x7: device_set_desc(dev, "Zoran ZR360x7 MultiMedia Controller"); return 0; } } return ENXIO; } /* * Attach does the following: * sets up PCI bus * maps ASRS * sets up interupt * i2c_attach */ static int zr_attach(device_t dev) { struct po_devinfo *pd; const struct zr_subchip *zs; int error = 0; int i; uint32_t val; ZR_SOFTC; memset(zr, 0, sizeof *zr); zr->zr_xname = device_get_nameunit(dev); zr->zr_self = dev; zr->zr_encoder = NULL; zr->zr_decoder = NULL; zr->zr_state = ZR_DISABLED; BUS_READ_IVAR(device_get_parent(dev), dev, PCI_IVAR_REVID, &val); zr->zr_revid = val; if (!mtx_initialized(&zr->zr_lock)) mtx_init(&zr->zr_lock, zr->zr_xname, NULL, MTX_DEF); /* * Enable bus mastering and Memory Mapped device */ PCI_ENABLE_BUSMASTER(device_get_parent(dev), dev); PCI_ENABLE_IO(device_get_parent(dev), dev, SYS_RES_MEMORY); val = pci_read_config(dev, ZR_SUBSYSTEM_CONF, 4); zr->zr_subsysid = (val >> 16) & 0xffff; zr->zr_subvenid = (val) & 0xffff; /* * Map ASRS */ zr->zr_bus.mem_rid = PCIR_MAPS; zr->zr_bus.res_mem = bus_alloc_resource(dev, SYS_RES_MEMORY, &zr->zr_bus.mem_rid, 0, ~0, 4096, RF_ACTIVE); if (!zr->zr_bus.res_mem) { device_printf(dev, "could not map memory\n"); error = ENXIO; goto fail; } zr->zr_bus.memt = rman_get_bustag(zr->zr_bus.res_mem); zr->zr_bus.memh = rman_get_bushandle(zr->zr_bus.res_mem); /* make sure the card is in soft reset mode */ /* XXX - pretend that we are enabled to make zr_disable not complain */ zr->zr_state = ZR_ENABLED; zr->zr_enabled = 1; zr_disable(zr); /* * Allocate our interrupt. */ zr->zr_bus.irq_rid = 0; zr->zr_bus.res_irq = bus_alloc_resource(dev, SYS_RES_IRQ, &zr->zr_bus.irq_rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE); if (zr->zr_bus.res_irq == NULL) { device_printf(dev, "could not map interrupt\n"); error = ENXIO; goto fail; } error = bus_setup_intr(dev, zr->zr_bus.res_irq, INTR_TYPE_TTY, zr_intr, zr, &zr->zr_bus.res_ih); if (error) { device_printf(dev, "could not setup irq\n"); goto fail; } /* * allocate bus_dma info in prep for mask buffer (and code buffer) */ if ((error = bus_dma_tag_create(NULL, 1, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MAXBSIZE, zr_maxbufsize, ZR_MAXFRAG_ENTS, BUS_DMA_ALLOCNOW, &zr->zr_bus.zb_tag))) { zr->zr_bus.zb_tag = NULL; ZRSC_PRINTF(zr, "bus_dma_tag_create failed: %d\n", error); } /* * we can't twiddle the i2c bus if the card is disabled. */ zr_enable(zr); if ((error = zr_i2c_attach(dev))) { ZR_DPRINTF("i2c failed\n"); goto fail; } /* * the card description should now be set */ if ((error = zr_core_attach(zr))) goto fail2; if (zr->zr_card != NULL) { ZR_FORCARDSUBCHIPS(zr, i) { zs = &zr->zr_card->subchips[i]; pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK); pd->pd_id = zs->subid; pd->pd_irq = zs->irq; pd->pd_reset = zs->reset; pd->pd_sleep = zs->sleep; pd->pd_dev = device_add_child(dev, NULL, -1); device_set_ivars(pd->pd_dev, pd); zr->zr_subchips[i] = pd; } } /* * find and attach any other children. */ bus_generic_probe(dev); bus_generic_attach(dev); zr_enable_ints(zr); zr->zr_dev.zrdev = make_dev(&zr_cdevsw, device_get_unit(dev), 0, 0, 0640, zr->zr_xname); return 0; fail2: zr_i2c_detach(dev); zr_disable(zr); fail: if (zr->zr_bus.res_irq) bus_release_resource(dev, SYS_RES_IRQ, zr->zr_bus.irq_rid, zr->zr_bus.res_irq); if (zr->zr_bus.res_mem) bus_release_resource(dev, SYS_RES_IRQ, zr->zr_bus.mem_rid, zr->zr_bus.res_mem); mtx_destroy(&zr->zr_lock); return error; } static int zr_shutdown(device_t dev) { ZR_SOFTC; bus_generic_shutdown(dev); zr_disable(zr); ZR_DPRINTF("shutdown\n"); return 0; } /* * Detach does the following: * i2c_detach * disable ints * tear down int * release irq and memory */ static int zr_detach(device_t dev) { uint32_t val; int error; device_t *devlist; void *iv; int cnt; ZR_SOFTC; zr_disable_ints(zr); /* remove devfs entry */ destroy_dev(zr->zr_dev.zrdev); zr->zr_dev.zrdev = NULL; if ((error = zr_i2c_detach(dev))) return error; /* delete any children */ if ((error = device_get_children(dev, &devlist, &cnt))) return error; for (;cnt > 0; cnt--) { if ((iv = device_get_ivars(devlist[cnt - 1])) != NULL) { device_set_ivars(devlist[cnt - 1], NULL); free(iv, M_DEVBUF); } if ((error = device_detach(devlist[cnt - 1])) || (error = device_delete_child(dev, devlist[cnt - 1]))) return error; } free(devlist, M_TEMP); zr_disable(zr); if ((error = bus_dma_tag_destroy(zr->zr_bus.zb_tag))) ZRSC_PRINTF(zr, "bus_dma_tag_destroy failed: %d, ptr: %p\n", error, zr->zr_bus.zb_tag); /* remove resources */ if ((error = bus_teardown_intr(dev, zr->zr_bus.res_irq, zr->zr_bus.res_ih)) || (error = bus_release_resource(dev, SYS_RES_IRQ, zr->zr_bus.irq_rid, zr->zr_bus.res_irq)) || (error = bus_release_resource(dev, SYS_RES_MEMORY, zr->zr_bus.mem_rid, zr->zr_bus.res_mem))) return error; /* * disable bus mastering and Memory Mapped device */ val = pci_read_config(dev, PCIR_COMMAND, 4); val &= ~(PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN); pci_write_config(dev, PCIR_COMMAND, val, 4); mtx_destroy(&zr->zr_lock); return 0; } static void zr_child_detached(device_t dev, device_t child) { ZR_SOFTC; struct po_devinfo *pd; int i; ZR_FORSUBCHIPS(zr, i) { pd = zr->zr_subchips[i]; /* make sure intrs are disabled */ if (pd->pd_irq == 0) ZR_SET_ASR(zr, ZR_ICR_REG, icr, GIRQ0En, 0); else if (pd->pd_irq == 1) ZR_SET_ASR(zr, ZR_ICR_REG, icr, GIRQ1En, 0); } } static int zr_suspend(device_t dev) { ZR_SOFTC; bus_generic_suspend(dev); zr_disable(zr); ZR_DPRINTF("susspend\n"); return 0; } static int zr_resume(device_t dev) { ZR_SOFTC; /* we need to completely reset the card */ zr_enable(zr); ZR_DPRINTF("resume\n"); bus_generic_resume(dev); return 0; } static int zr_read_ivar(device_t dev, device_t child, int indx, uintptr_t *res) { struct po_devinfo *pd; if ((pd = device_get_ivars(child)) == NULL) return EFAULT; switch (indx) { case PO_IVAR_ID: *res = pd->pd_id; break; case PO_IVAR_IRQ: *res = pd->pd_irq; break; case PO_IVAR_RESET: *res = pd->pd_reset; break; case PO_IVAR_SLEEP: *res = pd->pd_sleep; break; default: return ENOENT; } return 0; } static int zr_write_ivar(device_t dev, device_t child, int indx, uintptr_t val) { switch (indx) { case PO_IVAR_ID: case PO_IVAR_IRQ: case PO_IVAR_RESET: case PO_IVAR_SLEEP: return EINVAL; default: return ENOENT; } return 0; } static void zr_intr(void *arg) { struct zr_softc *zr = (struct zr_softc *)arg; struct po_devinfo *pd; int i, i0, i1; i0 = ZR_READ_ASR(zr, ZR_ISR_REG, isr, GIRQ0); i1 = ZR_READ_ASR(zr, ZR_ISR_REG, isr, GIRQ1); if (i0 || i1) ZR_FORSUBCHIPS(zr, i) { pd = zr->zr_subchips[i]; if ((pd->pd_irq == 0 && i0) || (pd->pd_irq == 1 && i1)) VIDEOSUBDEV_INTR(pd->pd_dev); } /* all ints are cleared after the following function call */ zr_core_intr(zr); } static int zr_os_open(dev_t devt, int oflags, int devtype, struct thread *td) { int error; ZR_DEV_SOFTC; if (zr == NULL) return ENXIO; error = 0; device_busy(zr->zr_self); error = zr_goto_state(zr, ZR_DEVOPENED); if (error != 0 && error != EBUSY) device_unbusy(zr->zr_self); else if (error == 0) { zr->zr_proc = td->td_proc; ZR_CALLSUBCHIPS(zr, VIDEOSUBDEV_OPEN, error, error != 0); } return error; } static int zr_os_close(dev_t devt, int fflag, int devtype, struct thread *td) { int error; ZR_DEV_SOFTC; error = 0; if (zr == NULL) return ENXIO; error = zr_goto_state(zr, ZR_DEVCLOSED); if (zr->zr_mask) { if ((error = zr_mask_free(zr))) ZRSC_PRINTF(zr, "zr_mask_free error on close: %d\n", error); } zr_set_norm(zr, NULL, 0); zr_release_coders(zr); device_unbusy(zr->zr_self); /* XXX - stop any on-going captures */ ZR_CALLSUBCHIPS(zr, VIDEOSUBDEV_CLOSE, error, error != 0); return error; } static int zr_os_read(dev_t devt, struct uio *uio, int ioflag) { int error; ZR_DEV_SOFTC; if ((error = zr_goto_state(zr, ZR_CAPBUF))) return error; zr_goto_state(zr, ZR_CAPBUFFREE); return error; } static int zr_os_ioctl(dev_t devt, u_long cmd, caddr_t data, int fflag, struct thread *td) { int error; ZR_DEV_SOFTC; if (zr == NULL) return ENXIO; if (td == NULL) { ZRSC_PRINTF(zr, "td NULL in ioctl!\n"); return EFAULT; } error = zr_ioctl(zr, cmd, data, td->td_proc); if (error) { /* try the children, they might have some input */ ZR_CALLSUBCHIPS(zr, VIDEOSUBDEV_IOCTL, error, error == 0, cmd, data, fflag, td); } return error; } static int zr_os_mmap(dev_t devt, vm_offset_t off, int nprot) { ZR_DEV_SOFTC; if (zr == NULL || !zr->zr_opened || !zr->zr_mask) return ENXIO; /*ZRSC_PRINTF(zr, "off: %d, nprot: %x\n", (int)off, nprot);*/ #if 1 if (nprot & PROT_EXEC || off < 0 || off > zr->zr_bus.zb_mask.zd_size) return -1; return atop(zr_dmamem_to_phys(zr, &zr->zr_bus.zb_mask) + off); #else return ENXIO; #endif } /* PostOffice Functions */ static int zr_dev_po_read(device_t dev, u_int8_t id, u_int8_t reg, u_int8_t *data) { ZR_SOFTC; return zr_po_read(zr, id, reg, data); } static int zr_dev_po_write(device_t dev, u_int8_t id, u_int8_t reg, u_int8_t data) { ZR_SOFTC; return zr_po_write(zr, id, reg, data); } static void zr_dev_po_set_durrec(device_t dev, u_int8_t id, u_int8_t dur, u_int8_t rec) { /* we don't list 15 since that'll be the default */ const u_int8_t clmp[] = {3, 4, 12}; int i; ZR_SOFTC; if (id >= 8) { ZRSC_PRINTF(zr, "invalid id passed to POSTOFFICE_SET_DURREC\n"); return; } for (i = 0; i < sizeof clmp / sizeof *clmp && dur > clmp[i]; i++); dur = i; for (i = 0; i < sizeof clmp / sizeof *clmp && rec > clmp[i]; i++); rec = i; zr->zr_gb_dur[id] = dur; zr->zr_gb_rec[id] = rec; /*ZRSC_PRINTF(zr, "GB: %d, Dur: %u, Rec: %u\n", id, (u_int)dur, (u_int)rec);*/ /* let the new dur/rec timings get installed */ zr_reset(zr); } static void zr_dev_po_gpio(device_t dev, u_int8_t pin, u_int8_t val) { ZR_SOFTC; zr_gpio(zr, pin, val); } static void zr_dev_po_enable_ints(device_t dev, u_int8_t intr) { ZR_SOFTC; if (intr == 0) ZR_SET_ASR(zr, ZR_ICR_REG, icr, GIRQ0En, 1); else if (intr == 1) ZR_SET_ASR(zr, ZR_ICR_REG, icr, GIRQ1En, 1); } /* Module Definitions */ DRIVER_MODULE(zr, pci, zr_driver, zr_devclass, 0, 0); DRIVER_MODULE(iicbb, zr, iicbb_driver, iicbb_devclass, 0, 0); MODULE_DEPEND(zr, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER); MODULE_DEPEND(zr, postoffice, POSTOFFICE_MINVER, POSTOFFICE_PREFVER, POSTOFFICE_MAXVER); MODULE_DEPEND(zr, videocode, VIDEOCODE_MINVER, VIDEOCODE_PREFVER, VIDEOCODE_MAXVER); MODULE_DEPEND(zr, videosubdev, VIDEOSUBDEV_MINVER, VIDEOSUBDEV_PREFVER, VIDEOSUBDEV_MAXVER); MODULE_VERSION(zr, 1);