rendered paste body#include <sys/param.h>#include <sys/kernel.h>#include <sys/module.h>#include <sys/bus.h>#include <machine/bus.h>#include <machine/resource.h>#include <sys/rman.h>#include <sys/systm.h>#include <sys/proc.h>#include <sys/signal.h>#include <sys/signalvar.h>#include <sys/lock.h>#include <sys/mutex.h>#include <dev/pci/pcivar.h>#include <dev/pci/pcireg.h>#include <dev/iicbus/iiconf.h>#include "iicbus_if.h"#include <vm/vm.h>#include <vm/vm_extern.h>#include <vm/pmap.h> /* req. by machine/pmap.h */#include <machine/pmap.h> /* vtophys */#include <vm/vm_map.h>#include <vm/vm_object.h>#include <vm/vm_kern.h>#include "saa713x_reg.h"#include "saa713x_ioctl.h"#include "saa713x_var.h"#include "saa713x_i2c.h"#include "saa713x_video.h"#include "saa713x_audio.h"static void saa_intr(void *arg);static device_probe_t saa_probe;static device_attach_t saa_attach;static device_detach_t saa_detach;static device_shutdown_t saa_shutdown;static bus_print_child_t saa_print_child;static device_method_t saa_methods[] = { DEVMETHOD(device_probe, saa_probe), DEVMETHOD(device_attach, saa_attach), DEVMETHOD(device_detach, saa_detach), DEVMETHOD(device_shutdown, saa_shutdown), DEVMETHOD(bus_print_child, saa_print_child), DEVMETHOD(iicbus_callback, iicbus_null_callback), DEVMETHOD(iicbus_repeated_start, saa_i2c_start), DEVMETHOD(iicbus_start, saa_i2c_start), DEVMETHOD(iicbus_stop, saa_i2c_stop), DEVMETHOD(iicbus_write, saa_i2c_write), DEVMETHOD(iicbus_read, saa_i2c_read), DEVMETHOD(iicbus_reset, saa_i2c_reset), { 0, 0 }};static driver_t saa_driver = { "saa", saa_methods, sizeof(struct saa_softc)};devclass_t saa_devclass;DRIVER_MODULE(saa, pci, saa_driver, saa_devclass, 0, 0);MODULE_VERSION(saa, 1);MODULE_DEPEND(saa, iicbus, 1, 1, 1);#ifdef SAA_DSHMMODULE_DEPEND(saa, sysvshm, 1, 1, 1);#endifextern driver_t iicbus_driver;extern devclass_t iicbus_devclass;DRIVER_MODULE(iicbus, saa, iicbus_driver, iicbus_devclass, 0, 0);static intsaa_print_child(device_t d, device_t child){ device_printf(child, "<SAA713x I2C Controller> on saa%d\n", device_get_unit(d)); return 0;}struct saa_spprt_dev { uint16_t sdv_vendor_id; uint16_t sdv_dev_id; const char *sdv_dev_desc;};static struct saa_spprt_dev spprt_devs[] = { { SAA_VENDOR_ID, SAA7130_DEV_ID, "Philips SAA7130 AV broadcast decoder" }, { SAA_VENDOR_ID, SAA7133_DEV_ID, "Philips SAA7133 AV broadcast decoder" }, { SAA_VENDOR_ID, SAA7134_DEV_ID, "Philips SAA7134 AV broadcast decoder" }, { SAA_VENDOR_ID, SAA7135_DEV_ID, "Philips SAA7135 AV broadcast decoder" }, { 0, 0, 0 }};static intsaa_probe(device_t dev){ uint16_t did, vid; int i; vid = pci_get_vendor(dev); did = pci_get_device(dev); i = 0; while(spprt_devs[i].sdv_vendor_id != 0) { if (vid == spprt_devs[i].sdv_vendor_id && did == spprt_devs[i].sdv_dev_id) { device_set_desc(dev, spprt_devs[i].sdv_dev_desc); return 0; } ++i; } return ENXIO;}static voidpta_dma_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error){ struct saa_softc *sc = (struct saa_softc*)arg; int i, j; if (error == EFBIG) { sc->sc_pta_map_loaderror = EFBIG; wakeup(sc); return; } i = 0; while (i < nsegs) { j = 0; while(j < segs[i].ds_len && sc->sc_pta_map_loadidx < SAA_DMA_NRCHANS) { sc->sc_pta_paddr[sc->sc_pta_map_loadidx++] = segs[i].ds_addr + j; j += 4096; } ++i; } if (sc->sc_pta_map_loadidx == SAA_DMA_NRCHANS) wakeup(sc);}static intsaa_free_pta(struct saa_softc *sc){ int rval = 0; if (sc->sc_pta_map_unloadflag) bus_dmamap_unload(sc->sc_pta_tag, sc->sc_pta_map); if (sc->sc_pta_map_destroyflag) bus_dmamem_free(sc->sc_pta_tag, sc->sc_pta, sc->sc_pta_map); if (!rval && sc->sc_pta_tag) rval = bus_dma_tag_destroy(sc->sc_pta_tag); return rval;}static intsaa_alloc_pta(struct saa_softc *sc){ int rval, sz; sz = 4096 * SAA_DMA_NRCHANS; sc->sc_pta_map_destroyflag = 0; sc->sc_pta_map_unloadflag = 0; if (bus_dma_tag_create(0, 4096, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, 0, 0, sz, 1, sz, 0, 0, 0, &sc->sc_pta_tag) != 0) { device_printf(sc->sc_dev, "Error creating pta tag\n"); return EAGAIN; } if (bus_dmamem_alloc(sc->sc_pta_tag, (void**)&sc->sc_pta, BUS_DMA_WAITOK | BUS_DMA_ZERO | BUS_DMA_COHERENT, &sc->sc_pta_map) != 0) { device_printf(sc->sc_dev, "Error allocating pta memory\n"); return EAGAIN; } sc->sc_pta_map_destroyflag = 1; sc->sc_pta_map_loadidx = 0; sc->sc_pta_map_loaderror = 0; rval = bus_dmamap_load(sc->sc_pta_tag, sc->sc_pta_map, sc->sc_pta, sz, pta_dma_cb, sc, 0); if (rval == EINPROGRESS) tsleep(sc, PRI_MAX_KERN, "dmapld", 0); else if (rval != 0) { device_printf(sc->sc_dev, "Error loading pta map\n"); return rval; } if (sc->sc_pta_map_loaderror != 0) { device_printf(sc->sc_dev, "Error, callback failed " "to load pta map (%d)\n", sc->sc_pta_map_loadidx); return EAGAIN; } sc->sc_pta_map_unloadflag = 1; return 0;} static intrelease_resources(struct saa_softc *sc){ int rval = 0; if (saa_audio_dnit(sc) != 0) device_printf(sc->sc_dev, "Error audio_dnit failed\n"); if (saa_video_dnit(sc) != 0) device_printf(sc->sc_dev, "Error video_dnit failed\n"); if (saa_free_pta(sc) != 0) device_printf(sc->sc_dev, "Error freeing pta memory\n"); if (!rval && sc->sc_intr_cookie != 0) rval = bus_teardown_intr(sc->sc_dev, sc->sc_intr_res, sc->sc_intr_cookie); if (!rval && sc->sc_intr_res) rval = bus_release_resource(sc->sc_dev, SYS_RES_IRQ, sc->sc_intr_rid, sc->sc_intr_res); if (!rval && sc->sc_iicbus_dev) rval = device_delete_child(sc->sc_dev, sc->sc_iicbus_dev); if (!rval && sc->sc_base_res) rval = bus_release_resource(sc->sc_dev, SYS_RES_MEMORY, sc->sc_base_rid, sc->sc_base_res); return rval;}static intsaa_attach(device_t dev){ struct saa_softc *sc; const char *emsg; sc = (struct saa_softc *)device_get_softc(dev); sc->sc_dev = dev; sc->sc_base_rid = SAA_PCIR_MEMBASE; sc->sc_base_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &sc->sc_base_rid, 0, ~0, 1, RF_ACTIVE); if (!sc->sc_base_res) { emsg = "Error allocating resource (BASEPTR)"; goto attach_failed; } sc->sc_base_bustag = rman_get_bustag(sc->sc_base_res); sc->sc_base_bushandle = rman_get_bushandle(sc->sc_base_res); pci_enable_busmaster(dev); REG_WRITE(REG_IEN, 0); REG_WRITE(REG_FIEN, 0); REG_WRITE(REG_MCR, 0); REG_WRITE(REG_IREP, REG_READ(REG_IREP));/* reset pend. interrupts */ /* FIFO is set in capture, and overlay depending on format */ FIFO_SET_THRESH(0, 0, 0, 0); /* XXX */ if (saa_alloc_pta(sc) != 0) { emsg = "Error allocating device pta memory"; goto attach_failed; } sc->sc_iicbus_dev = device_add_child(dev, "iicbus", -1); if (!sc->sc_iicbus_dev || bus_generic_attach(sc->sc_dev)) { emsg = "Error adding iicbus device"; goto attach_failed; } sc->sc_intr_rid = 0; sc->sc_intr_res = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->sc_intr_rid, 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); if (!sc->sc_intr_res) { emsg = "Error allocating interrupt resource"; goto attach_failed; } if (bus_setup_intr(dev, sc->sc_intr_res, INTR_TYPE_AV | INTR_MPSAFE, NULL, saa_intr, sc, &sc->sc_intr_cookie) != 0) { emsg = "Error setting up interrupt"; goto attach_failed; } if (saa_video_init(sc) != 0) { emsg = "Error initialising video subsection"; goto attach_failed; } if (saa_audio_init(sc) != 0) { emsg = "Error initialising audio subsection"; goto attach_failed; } return 0;attach_failed: device_printf(dev, "%s\n", emsg); if (release_resources(sc) != 0) device_printf(dev, "Error releasing some/all resources\n"); return ENODEV;}static intsaa_detach(device_t dev){ int rval; struct saa_softc *sc = (struct saa_softc *)device_get_softc(dev); REG_WRITE(REG_IEN, 0); REG_WRITE(REG_FIEN, 0); REG_WRITE(REG_MCR, 0); if ( (rval = release_resources(sc)) != 0) device_printf(dev, "Error releasing some/all resources\n"); return rval;}static intsaa_shutdown(device_t dev){ struct saa_softc *sc = (struct saa_softc *)device_get_softc(dev); REG_WRITE(REG_IEN, 0); REG_WRITE(REG_FIEN, 0); REG_WRITE(REG_MCR, 0); return 0;}static voidsaa_intr(void *arg){ struct saa_softc *sc = (struct saa_softc *)arg; uint32_t irep, ists, avsts, r, sflag; struct proc *p; irep = REG_READ(REG_IREP); sc->sc_sts->ssts_irep = irep; ists = REG_READ(REG_ISTS); avsts = REG_READ(REG_AVSTS); sc->sc_sts->ssts_video_sts = avsts;#if 0 device_printf(sc->sc_dev, "INTR: AVSTS (0x%x) IREP(0x%x) " "ISTS(0x%x)\n", avsts, irep, ists);#endif sc->sc_sts->ssts_sig_video = 0; sc->sc_sts->ssts_sig_audio = 0; sc->sc_sts->ssts_sig_sts = 0; if ((irep >> 4) & sc->sc_video_spec.svs_cap_sigmask) sc->sc_sts->ssts_sig_sts = 1; sc->sc_sts->ssts_audio_vptr = REG_READ(REG_SLICE3_STS); if (irep & IREP_SLICE3) { sc->sc_sts->ssts_audio_lastbuf = ((ists >> 28) & 1); sc->sc_sts->ssts_sig_audio = 1; ++sc->sc_sts->ssts_audio_framectr; if (sc->sc_sts->ssts_audio_mb_count > 1) saa_audio_multibuf_switchbuf(sc); } if (irep & IREP_SLICE0) { r = REG_READ(REG_SLICE0_STS); sc->sc_sts->ssts_video_vptr = r; if ((r & SLICE_STS_SOURCE) == 0) {/* Video region*/ sc->sc_sts->ssts_sig_video = 1; /* Only switch buffer at end of frames (ie. fld-2) */ if ((ists & ISTS_SLICE0_FEILD) != 0) { /* Feild 2 (aka. EVEN) */ ++sc->sc_sts->ssts_video_framectr; if ((r & SLICE_STS_TASK) == 0) { ++sc->sc_sts->ssts_video_t1_framectr; sc->sc_sts->ssts_video_lasttask = 0; } else { ++sc->sc_sts->ssts_video_t2_framectr; sc->sc_sts->ssts_video_lasttask = 1; } sc->sc_sts->ssts_video_feild = 0; if (sc->sc_sts->ssts_video_mb_count > 1) saa_video_multibuf_switchbuf(sc); } else { /* Feild 1 (aka. ODD) */ if ((r & SLICE_STS_TASK) == 0) sc->sc_sts->ssts_video_lasttask = 0; else sc->sc_sts->ssts_video_lasttask = 1; sc->sc_sts->ssts_video_feild = 1; } } } /* SFLAG is used to optimise when sc_video_capbuf_sigpid is * is equal sc_audio_capbuf_sigpid */ sflag = 0; /* USR1 - video capture * USR2 - audio capture, status * When both SIGUSR2 and USR1 are applicable send only USR1 * client should check ssts_sig_* * * Interrupts are selectivly enabled based on svs_flags * hence we optimise the if conditions by ORing * CAPTURE_SIGNAL and STATUS_SIGNAL without causing any * ill effects (e.g. delivering sig_video when CAPTURE_SIGNAL * is not set) */ /* Video Process */ if ((sc->sc_sts->ssts_sig_video | sc->sc_sts->ssts_sig_sts) && (sc->sc_video_spec.svs_flags & (VSPEC_FLAG_CAPTURE_SIGNAL | VSPEC_FLAG_STATUS_SIGNAL)) && sc->sc_video_capbuf_sigpid != 0) { if (sc->sc_sts->ssts_sig_video) { if ((p = pfind(sc->sc_video_capbuf_sigpid)) != NULL) { psignal(p, SIGUSR1); PROC_UNLOCK(p); ++sflag; } else sc->sc_video_capbuf_sigpid = 0; } else { if ((p = pfind(sc->sc_video_capbuf_sigpid)) != NULL) { psignal(p, SIGUSR2); PROC_UNLOCK(p); ++sflag; } else sc->sc_video_capbuf_sigpid = 0; } } /* Audio Process */ if ((sc->sc_sts->ssts_sig_audio | sc->sc_sts->ssts_sig_sts) && (sc->sc_audio_spec.sas_flags & (ASPEC_FLAG_CAPTURE_SIGNAL | ASPEC_FLAG_STATUS_SIGNAL)) && sc->sc_audio_capbuf_sigpid != 0) { if (sflag == 0 || (sc->sc_audio_capbuf_sigpid != sc->sc_video_capbuf_sigpid)) { if ((p = pfind(sc->sc_audio_capbuf_sigpid)) != NULL) { psignal(p, SIGUSR2); PROC_UNLOCK(p); } else sc->sc_audio_capbuf_sigpid = 0; } } sc->sc_sts->ssts_video_sig_count += sc->sc_sts->ssts_sig_video; sc->sc_sts->ssts_audio_sig_count += sc->sc_sts->ssts_sig_audio; REG_WRITE(REG_ISTS, ists); REG_WRITE(REG_IREP, irep);}void saa_build_pta(struct saa_softc *sc, bus_dma_segment_t *segs, int nsegs, uint32_t *pta){ int pta_idx, seg_idx, seg_offset; pta_idx = seg_offset = seg_idx = 0; /* Invariant nsegs > 0 */ while(pta_idx < 1024) { pta[pta_idx++] = segs[seg_idx].ds_addr + seg_offset; seg_offset += 4096; if (seg_offset >= segs[seg_idx].ds_len) { seg_offset = 0; if ( ++seg_idx >= nsegs) break; if ((segs[seg_idx].ds_len % 4096) != 0) panic("saa:saa_build_pta: Invalid segment len " "= %td idx = %d\n", segs[seg_idx].ds_len, seg_idx); } }}