#include "opt_bktr.h"
#define FIFO_RISC_DISABLED 0
#define ALL_INTS_DISABLED 0
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/device.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/signalvar.h>
#include <sys/malloc.h>
#include <sys/mman.h>
#include <sys/event.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/thread2.h>
#include <vm/vm.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>
#include <vm/vm_extern.h>
#include <bus/pci/pcivar.h>
#include <bus/pci/pcireg.h>
#include "pcidevs.h"
#include <sys/sysctl.h>
int bt848_card = -1;
int bt848_tuner = -1;
int bt848_reverse_mute = -1;
int bt848_format = -1;
int bt848_slow_msp_audio = -1;
#ifdef BKTR_NEW_MSP34XX_DRIVER
int bt848_stereo_once = 0;
int bt848_amsound = 0;
int bt848_dolby = 0;
#endif
SYSCTL_NODE(_hw, OID_AUTO, bt848, CTLFLAG_RW, 0, "Bt848 Driver mgmt");
SYSCTL_INT(_hw_bt848, OID_AUTO, card, CTLFLAG_RW, &bt848_card, -1, "");
SYSCTL_INT(_hw_bt848, OID_AUTO, tuner, CTLFLAG_RW, &bt848_tuner, -1, "");
SYSCTL_INT(_hw_bt848, OID_AUTO, reverse_mute, CTLFLAG_RW, &bt848_reverse_mute, -1, "");
SYSCTL_INT(_hw_bt848, OID_AUTO, format, CTLFLAG_RW, &bt848_format, -1, "");
SYSCTL_INT(_hw_bt848, OID_AUTO, slow_msp_audio, CTLFLAG_RW, &bt848_slow_msp_audio, -1, "");
#ifdef BKTR_NEW_MSP34XX_DRIVER
SYSCTL_INT(_hw_bt848, OID_AUTO, stereo_once, CTLFLAG_RW, &bt848_stereo_once, 0, "");
SYSCTL_INT(_hw_bt848, OID_AUTO, amsound, CTLFLAG_RW, &bt848_amsound, 0, "");
SYSCTL_INT(_hw_bt848, OID_AUTO, dolby, CTLFLAG_RW, &bt848_dolby, 0, "");
#endif
#include <dev/video/meteor/ioctl_meteor.h>
#include <dev/video/bktr/ioctl_bt848.h>
#include <dev/video/bktr/bktr_reg.h>
#include <dev/video/bktr/bktr_tuner.h>
#include <dev/video/bktr/bktr_card.h>
#include <dev/video/bktr/bktr_audio.h>
#include <dev/video/bktr/bktr_core.h>
#include <dev/video/bktr/bktr_os.h>
static int bktr_probe( device_t dev );
static int bktr_attach( device_t dev );
static int bktr_detach( device_t dev );
static int bktr_shutdown( device_t dev );
static void bktr_intr(void *arg) { common_bktr_intr(arg); }
static device_method_t bktr_methods[] = {
DEVMETHOD(device_probe, bktr_probe),
DEVMETHOD(device_attach, bktr_attach),
DEVMETHOD(device_detach, bktr_detach),
DEVMETHOD(device_shutdown, bktr_shutdown),
DEVMETHOD_END
};
static driver_t bktr_driver = {
"bktr",
bktr_methods,
sizeof(struct bktr_softc),
};
static devclass_t bktr_devclass;
static d_open_t bktr_open;
static d_close_t bktr_close;
static d_read_t bktr_read;
static d_write_t bktr_write;
static d_ioctl_t bktr_ioctl;
static d_mmap_t bktr_mmap;
static d_kqfilter_t bktr_kqfilter;
static void bktr_filter_detach(struct knote *);
static int bktr_filter(struct knote *, long);
static struct dev_ops bktr_ops = {
{ "bktr", 0, 0 },
.d_open = bktr_open,
.d_close = bktr_close,
.d_read = bktr_read,
.d_write = bktr_write,
.d_ioctl = bktr_ioctl,
.d_kqfilter = bktr_kqfilter,
.d_mmap = bktr_mmap,
};
DRIVER_MODULE(bktr, pci, bktr_driver, bktr_devclass, NULL, NULL);
MODULE_DEPEND(bktr, bktr_mem, 1,1,1);
MODULE_VERSION(bktr, 1);
static int
bktr_probe( device_t dev )
{
unsigned int type = pci_get_devid(dev);
unsigned int rev = pci_get_revid(dev);
if (PCI_VENDOR(type) == PCI_VENDOR_BROOKTREE)
{
switch (PCI_PRODUCT(type)) {
case PCI_PRODUCT_BROOKTREE_BT848:
if (rev == 0x12)
device_set_desc(dev, "BrookTree 848A");
else
device_set_desc(dev, "BrookTree 848");
return BUS_PROBE_DEFAULT;
case PCI_PRODUCT_BROOKTREE_BT849:
device_set_desc(dev, "BrookTree 849A");
return BUS_PROBE_DEFAULT;
case PCI_PRODUCT_BROOKTREE_BT878:
device_set_desc(dev, "BrookTree 878");
return BUS_PROBE_DEFAULT;
case PCI_PRODUCT_BROOKTREE_BT879:
device_set_desc(dev, "BrookTree 879");
return BUS_PROBE_DEFAULT;
}
}
return ENXIO;
}
static int
bktr_attach( device_t dev )
{
u_long latency;
u_long fun;
u_long val;
unsigned int rev;
unsigned int unit;
int error = 0;
#ifdef BROOKTREE_IRQ
u_long old_irq, new_irq;
#endif
struct bktr_softc *bktr = device_get_softc(dev);
unit = device_get_unit(dev);
ksnprintf(bktr->bktr_xname, sizeof(bktr->bktr_xname), "bktr%d",unit);
val = pci_read_config(dev, PCIR_COMMAND, 4);
val |= (PCIM_CMD_MEMEN|PCIM_CMD_BUSMASTEREN);
pci_write_config(dev, PCIR_COMMAND, val, 4);
bktr->mem_rid = PCIR_BAR(0);
bktr->res_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
&bktr->mem_rid, RF_ACTIVE);
if (!bktr->res_mem) {
device_printf(dev, "could not map memory\n");
error = ENXIO;
goto fail;
}
bktr->memt = rman_get_bustag(bktr->res_mem);
bktr->memh = rman_get_bushandle(bktr->res_mem);
OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED);
OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED);
#ifdef BROOKTREE_IRQ
old_irq = pci_conf_read(tag, PCI_INTERRUPT_REG);
pci_conf_write(tag, PCI_INTERRUPT_REG, BROOKTREE_IRQ);
new_irq = pci_conf_read(tag, PCI_INTERRUPT_REG);
kprintf("bktr%d: attach: irq changed from %d to %d\n",
unit, (old_irq & 0xff), (new_irq & 0xff));
#endif
bktr->irq_rid = 0;
bktr->res_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&bktr->irq_rid, RF_SHAREABLE | RF_ACTIVE);
if (bktr->res_irq == NULL) {
device_printf(dev, "could not map interrupt\n");
error = ENXIO;
goto fail;
}
error = bus_setup_intr(dev, bktr->res_irq, 0,
bktr_intr, bktr, &bktr->res_ih, NULL);
if (error) {
device_printf(dev, "could not setup irq\n");
goto fail;
}
fun = pci_read_config( dev, 0x40, 2);
fun = fun | 1;
#if defined( BKTR_430_FX_MODE )
if (bootverbose) kprintf("Using 430 FX chipset compatibility mode\n");
fun = fun | 2;
#endif
#if defined( BKTR_SIS_VIA_MODE )
if (bootverbose) kprintf("Using SiS/VIA chipset compatibility mode\n");
fun = fun | 4;
#endif
pci_write_config(dev, 0x40, fun, 2);
#ifndef BROOKTREE_DEF_LATENCY_VALUE
#define BROOKTREE_DEF_LATENCY_VALUE 10
#endif
latency = pci_read_config(dev, PCI_LATENCY_TIMER, 4);
latency = (latency >> 8) & 0xff;
if ( bootverbose ) {
if (latency)
kprintf("brooktree%d: PCI bus latency is", unit);
else
kprintf("brooktree%d: PCI bus latency was 0 changing to",
unit);
}
if ( !latency ) {
latency = BROOKTREE_DEF_LATENCY_VALUE;
pci_write_config(dev, PCI_LATENCY_TIMER, latency<<8, 4);
}
if ( bootverbose ) {
kprintf(" %d.\n", (int) latency);
}
fun = pci_get_devid(dev);
rev = pci_get_revid(dev);
common_bktr_attach( bktr, unit, fun, rev );
make_dev(&bktr_ops, unit, 0, 0, 0444, "bktr%d", unit);
make_dev(&bktr_ops, unit+16, 0, 0, 0444, "tuner%d", unit);
make_dev(&bktr_ops, unit+32, 0, 0, 0444, "vbi%d" , unit);
return 0;
fail:
if (bktr->res_irq)
bus_release_resource(dev, SYS_RES_IRQ, bktr->irq_rid, bktr->res_irq);
if (bktr->res_mem)
bus_release_resource(dev, SYS_RES_IRQ, bktr->mem_rid, bktr->res_mem);
return error;
}
static int
bktr_detach( device_t dev )
{
struct bktr_softc *bktr = device_get_softc(dev);
#ifdef BKTR_NEW_MSP34XX_DRIVER
if (bktr->msp3400c_info != NULL)
msp_detach(bktr);
#endif
OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED);
OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED);
#ifdef USE_VBIMUTEX
mtx_destroy(&bktr->vbimutex);
#endif
dev_ops_remove_minor(&bktr_ops, device_get_unit(dev));
bus_teardown_intr(dev, bktr->res_irq, bktr->res_ih);
bus_release_resource(dev, SYS_RES_IRQ, bktr->irq_rid, bktr->res_irq);
bus_release_resource(dev, SYS_RES_MEMORY, bktr->mem_rid, bktr->res_mem);
return 0;
}
static int
bktr_shutdown( device_t dev )
{
struct bktr_softc *bktr = device_get_softc(dev);
OUTL(bktr, BKTR_INT_MASK, ALL_INTS_DISABLED);
OUTW(bktr, BKTR_GPIO_DMA_CTL, FIFO_RISC_DISABLED);
return 0;
}
vm_offset_t
get_bktr_mem( int unit, unsigned size )
{
vm_offset_t addr = 0;
addr = (vm_offset_t)contigmalloc(size, M_DEVBUF, M_NOWAIT, 0,
0xffffffff, 1<<24, 0);
if (addr == 0)
addr = (vm_offset_t)contigmalloc(size, M_DEVBUF, M_NOWAIT, 0,
0xffffffff, PAGE_SIZE, 0);
if (addr == 0) {
kprintf("bktr%d: Unable to allocate %d bytes of memory.\n",
unit, size);
}
return( addr );
}
#define VIDEO_DEV 0x00
#define TUNER_DEV 0x01
#define VBI_DEV 0x02
#define UNIT(x) ((x) & 0x0f)
#define FUNCTION(x) (x >> 4)
static int
bktr_open(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
bktr_ptr_t bktr;
int unit;
int result;
unit = UNIT( minor(dev) );
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
return (ENXIO);
}
if (!(bktr->flags & METEOR_INITALIZED))
return( ENXIO );
device_busy(devclass_get_device(bktr_devclass, unit));
if (bt848_card != -1) {
if ((bt848_card >> 8 == unit ) &&
( (bt848_card & 0xff) < Bt848_MAX_CARD )) {
if ( bktr->bt848_card != (bt848_card & 0xff) ) {
bktr->bt848_card = (bt848_card & 0xff);
probeCard(bktr, FALSE, unit);
}
}
}
if (bt848_tuner != -1) {
if ((bt848_tuner >> 8 == unit ) &&
( (bt848_tuner & 0xff) < Bt848_MAX_TUNER )) {
if ( bktr->bt848_tuner != (bt848_tuner & 0xff) ) {
bktr->bt848_tuner = (bt848_tuner & 0xff);
probeCard(bktr, FALSE, unit);
}
}
}
if (bt848_reverse_mute != -1) {
if ((bt848_reverse_mute >> 8) == unit ) {
bktr->reverse_mute = bt848_reverse_mute & 0xff;
}
}
if (bt848_slow_msp_audio != -1) {
if ((bt848_slow_msp_audio >> 8) == unit ) {
bktr->slow_msp_audio = (bt848_slow_msp_audio & 0xff);
}
}
#ifdef BKTR_NEW_MSP34XX_DRIVER
if (bt848_stereo_once != 0) {
if ((bt848_stereo_once >> 8) == unit ) {
bktr->stereo_once = (bt848_stereo_once & 0xff);
}
}
if (bt848_amsound != -1) {
if ((bt848_amsound >> 8) == unit ) {
bktr->amsound = (bt848_amsound & 0xff);
}
}
if (bt848_dolby != -1) {
if ((bt848_dolby >> 8) == unit ) {
bktr->dolby = (bt848_dolby & 0xff);
}
}
#endif
switch ( FUNCTION( minor(dev) ) ) {
case VIDEO_DEV:
result = video_open( bktr );
break;
case TUNER_DEV:
result = tuner_open( bktr );
break;
case VBI_DEV:
result = vbi_open( bktr );
break;
default:
result = ENXIO;
break;
}
if (result != 0)
device_unbusy(devclass_get_device(bktr_devclass, unit));
return( result );
}
static int
bktr_close(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
bktr_ptr_t bktr;
int unit;
int result;
unit = UNIT( minor(dev) );
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
return (ENXIO);
}
switch ( FUNCTION( minor(dev) ) ) {
case VIDEO_DEV:
result = video_close( bktr );
break;
case TUNER_DEV:
result = tuner_close( bktr );
break;
case VBI_DEV:
result = vbi_close( bktr );
break;
default:
return (ENXIO);
break;
}
device_unbusy(devclass_get_device(bktr_devclass, unit));
return( result );
}
static int
bktr_read(struct dev_read_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
return (ENXIO);
}
switch ( FUNCTION( minor(dev) ) ) {
case VIDEO_DEV:
return( video_read( bktr, unit, dev, ap->a_uio ) );
case VBI_DEV:
return( vbi_read( bktr, ap->a_uio, ap->a_ioflag ) );
}
return( ENXIO );
}
static int
bktr_write(struct dev_write_args *ap)
{
return( EINVAL );
}
static int
bktr_ioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
u_long cmd = ap->a_cmd;
bktr_ptr_t bktr;
int unit;
unit = UNIT(minor(dev));
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
return (ENXIO);
}
#ifdef BKTR_GPIO_ACCESS
if (bktr->bigbuf == 0 && cmd != BT848_GPIO_GET_EN &&
cmd != BT848_GPIO_SET_EN && cmd != BT848_GPIO_GET_DATA &&
cmd != BT848_GPIO_SET_DATA)
return( ENOMEM );
#else
if (bktr->bigbuf == 0)
return( ENOMEM );
#endif
switch ( FUNCTION( minor(dev) ) ) {
case VIDEO_DEV:
return( video_ioctl( bktr, unit, cmd, ap->a_data, curthread ) );
case TUNER_DEV:
return( tuner_ioctl( bktr, unit, cmd, ap->a_data, curthread ) );
}
return( ENXIO );
}
static int
bktr_mmap(struct dev_mmap_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int unit;
bktr_ptr_t bktr;
unit = UNIT(minor(dev));
if (FUNCTION(minor(dev)) > 0)
return(EINVAL);
bktr = (struct bktr_softc*)devclass_get_softc(bktr_devclass, unit);
if (bktr == NULL) {
return (ENXIO);
}
if (ap->a_nprot & PROT_EXEC)
return(EINVAL);
if (ap->a_offset < 0)
return(EINVAL);
if (ap->a_offset >= bktr->alloc_pages * PAGE_SIZE)
return(EINVAL);
ap->a_result = atop(vtophys(bktr->bigbuf) + ap->a_offset);
return(0);
}
static struct filterops bktr_filterops =
{ FILTEROP_ISFD, NULL, bktr_filter_detach, bktr_filter };
static int
bktr_kqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
struct klist *klist;
bktr_ptr_t bktr;
int unit;
ap->a_result = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
if (FUNCTION(minor(dev)) == VBI_DEV) {
unit = UNIT(minor(dev));
bktr = (struct bktr_softc *)
devclass_get_softc(bktr_devclass, unit);
kn->kn_fop = &bktr_filterops;
kn->kn_hook = (caddr_t)bktr;
break;
}
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
klist = &bktr->vbi_kq.ki_note;
knote_insert(klist, kn);
return (0);
}
static void
bktr_filter_detach(struct knote *kn)
{
bktr_ptr_t bktr = (bktr_ptr_t)kn->kn_hook;
struct klist *klist;
klist = &bktr->vbi_kq.ki_note;
knote_insert(klist, kn);
}
static int
bktr_filter(struct knote *kn, long hint)
{
bktr_ptr_t bktr = (bktr_ptr_t)kn->kn_hook;
int ready = 0;
if (bktr == NULL) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
return (1);
}
LOCK_VBI(bktr);
crit_enter();
if (bktr->vbisize != 0)
ready = 1;
crit_exit();
UNLOCK_VBI(bktr);
return (ready);
}