# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	dev/cxm
#	dev/cxm/Patch.iicbb-fbsd4
#	dev/cxm/Patch.iicbb-fbsd5
#	dev/cxm/cxm.c
#	dev/cxm/cxm.h
#	dev/cxm/cxm_audio.c
#	dev/cxm/cxm_eeprom.c
#	dev/cxm/cxm_i2c.c
#	dev/cxm/cxm_ir.c
#	dev/cxm/cxm_tuner.c
#	dev/cxm/cxm_video.c
#	dev/cxm/cxm_extract_fw.c
#	modules/cxm
#	modules/cxm/Makefile
#	modules/cxm/cxm
#	modules/cxm/cxm/Makefile
#	modules/cxm/cxm_iic
#	modules/cxm/cxm_iic/Makefile
#
echo c - dev/cxm
mkdir -p dev/cxm > /dev/null 2>&1
echo x - dev/cxm/Patch.iicbb-fbsd4
sed 's/^X//' >dev/cxm/Patch.iicbb-fbsd4 << 'END-of-dev/cxm/Patch.iicbb-fbsd4'
X*** dev/iicbus/iicbb.c.ORIGINAL	Sat Apr 20 12:38:43 2002
X--- dev/iicbus/iicbb.c	Thu Jan 29 01:46:55 2004
X*************** struct iicbb_softc {
X*** 71,76 ****
X--- 71,77 ----
X  static int iicbb_probe(device_t);
X  static int iicbb_attach(device_t);
X  static int iicbb_detach(device_t);
X+ static void iicbb_child_detached(device_t, device_t);
X  static int iicbb_print_child(device_t, device_t);
X  
X  static int iicbb_callback(device_t, int, caddr_t);
X*************** static device_method_t iicbb_methods[] =
X*** 87,92 ****
X--- 88,94 ----
X  	DEVMETHOD(device_detach,	iicbb_detach),
X  
X  	/* bus interface */
X+ 	DEVMETHOD(bus_child_detached,	iicbb_child_detached),
X  	DEVMETHOD(bus_print_child,	iicbb_print_child),
X  
X  	/* iicbus interface */
X*************** static int iicbb_attach(device_t dev)
X*** 135,149 ****
X  static int iicbb_detach(device_t dev)
X  {
X  	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
X  
X! 	if (sc->iicbus) {
X! 		bus_generic_detach(dev);
X! 		device_delete_child(dev, sc->iicbus);
X! 	}
X  
X  	return (0);
X  }
X  
X  static int
X  iicbb_print_child(device_t bus, device_t dev)
X  {
X--- 137,170 ----
X  static int iicbb_detach(device_t dev)
X  {
X  	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
X+ 	device_t child;
X  
X! 	/*
X! 	 * Detach the children before recursively deleting
X! 	 * in case a child has a pointer to a grandchild
X! 	 * which is used by the child's detach routine.
X! 	 *
X! 	 * Remember the child before detaching so we can
X! 	 * delete it (bus_generic_detach indirectly zeroes
X! 	 * sc->child_dev).
X! 	 */
X! 	child = sc->iicbus;
X! 	bus_generic_detach(dev);
X! 	if (child)
X! 		device_delete_child(dev, child);
X  
X  	return (0);
X  }
X  
X+ static void
X+ iicbb_child_detached( device_t dev, device_t child )
X+ {
X+ 	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
X+ 
X+ 	if (child == sc->iicbus)
X+ 		sc->iicbus = NULL;
X+ }
X+ 
X  static int
X  iicbb_print_child(device_t bus, device_t dev)
X  {
X*************** static int iicbb_read(device_t dev, char
X*** 345,349 ****
X--- 366,371 ----
X  }
X  
X  DRIVER_MODULE(iicbb, bti2c, iicbb_driver, iicbb_devclass, 0, 0);
X+ DRIVER_MODULE(iicbb, cxm_iic, iicbb_driver, iicbb_devclass, 0, 0);
X  DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
X  DRIVER_MODULE(iicbb, viapm, iicbb_driver, iicbb_devclass, 0, 0);
END-of-dev/cxm/Patch.iicbb-fbsd4
echo x - dev/cxm/Patch.iicbb-fbsd5
sed 's/^X//' >dev/cxm/Patch.iicbb-fbsd5 << 'END-of-dev/cxm/Patch.iicbb-fbsd5'
X*** dev/iicbus/iicbb.c.ORIGINAL	Sun Aug 24 13:49:13 2003
X--- dev/iicbus/iicbb.c	Fri Jul  1 15:55:21 2005
X*************** struct iicbb_softc {
X*** 66,71 ****
X--- 66,72 ----
X  static int iicbb_probe(device_t);
X  static int iicbb_attach(device_t);
X  static int iicbb_detach(device_t);
X+ static void iicbb_child_detached(device_t, device_t);
X  static int iicbb_print_child(device_t, device_t);
X  
X  static int iicbb_callback(device_t, int, caddr_t);
X*************** static device_method_t iicbb_methods[] =
X*** 82,87 ****
X--- 83,89 ----
X  	DEVMETHOD(device_detach,	iicbb_detach),
X  
X  	/* bus interface */
X+ 	DEVMETHOD(bus_child_detached,	iicbb_child_detached),
X  	DEVMETHOD(bus_print_child,	iicbb_print_child),
X  
X  	/* iicbus interface */
X*************** static int iicbb_attach(device_t dev)
X*** 130,144 ****
X  static int iicbb_detach(device_t dev)
X  {
X  	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
X  
X! 	if (sc->iicbus) {
X! 		bus_generic_detach(dev);
X! 		device_delete_child(dev, sc->iicbus);
X! 	}
X  
X  	return (0);
X  }
X  
X  static int
X  iicbb_print_child(device_t bus, device_t dev)
X  {
X--- 132,165 ----
X  static int iicbb_detach(device_t dev)
X  {
X  	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
X+ 	device_t child;
X  
X! 	/*
X! 	 * Detach the children before recursively deleting
X! 	 * in case a child has a pointer to a grandchild
X! 	 * which is used by the child's detach routine.
X! 	 *
X! 	 * Remember the child before detaching so we can
X! 	 * delete it (bus_generic_detach indirectly zeroes
X! 	 * sc->child_dev).
X! 	 */
X! 	child = sc->iicbus;
X! 	bus_generic_detach(dev);
X! 	if (child)
X! 		device_delete_child(dev, child);
X  
X  	return (0);
X  }
X  
X+ static void
X+ iicbb_child_detached( device_t dev, device_t child )
X+ {
X+ 	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
X+ 
X+ 	if (child == sc->iicbus)
X+ 		sc->iicbus = NULL;
X+ }
X+ 
X  static int
X  iicbb_print_child(device_t bus, device_t dev)
X  {
X*************** static int iicbb_read(device_t dev, char
X*** 383,388 ****
X--- 404,410 ----
X  }
X  
X  DRIVER_MODULE(iicbb, bktr, iicbb_driver, iicbb_devclass, 0, 0);
X+ DRIVER_MODULE(iicbb, cxm_iic, iicbb_driver, iicbb_devclass, 0, 0);
X  DRIVER_MODULE(iicbb, lpbb, iicbb_driver, iicbb_devclass, 0, 0);
X  DRIVER_MODULE(iicbb, viapm, iicbb_driver, iicbb_devclass, 0, 0);
X  
X*** dev/iicbus/iicbus_if.m.ORIGINAL	Wed Apr 13 14:25:01 2005
X--- dev/iicbus/iicbus_if.m	Mon Oct 24 00:51:05 2005
X*************** METHOD int read {
X*** 90,96 ****
X  #
X  METHOD int write {
X  	device_t dev;
X! 	char *buf;
X  	int len;
X  	int *bytes;
X  	int timeout;
X--- 90,96 ----
X  #
X  METHOD int write {
X  	device_t dev;
X! 	const char *buf;
X  	int len;
X  	int *bytes;
X  	int timeout;
X*** dev/iicbus/iiconf.h.ORIGINAL	Wed Jun 16 22:51:57 2004
X--- dev/iicbus/iiconf.h	Mon Oct 24 00:43:41 2005
X*************** extern int iicbus_started(device_t);
X*** 114,120 ****
X  extern int iicbus_start(device_t, u_char, int);
X  extern int iicbus_stop(device_t);
X  extern int iicbus_repeated_start(device_t, u_char, int);
X! extern int iicbus_write(device_t, char *, int, int *, int);
X  extern int iicbus_read(device_t, char *, int, int *, int, int);
X  
X  /* single byte read/write functions, start/stop not managed */
X--- 114,120 ----
X  extern int iicbus_start(device_t, u_char, int);
X  extern int iicbus_stop(device_t);
X  extern int iicbus_repeated_start(device_t, u_char, int);
X! extern int iicbus_write(device_t, const char *, int, int *, int);
X  extern int iicbus_read(device_t, char *, int, int *, int, int);
X  
X  /* single byte read/write functions, start/stop not managed */
X*** dev/iicbus/iiconf.c.ORIGINAL	Wed Jun 16 22:51:57 2004
X--- dev/iicbus/iiconf.c	Mon Oct 24 00:47:32 2005
X*************** iicbus_stop(device_t bus)
X*** 234,240 ****
X   * iicbus_start() call
X   */
X  int
X! iicbus_write(device_t bus, char *buf, int len, int *sent, int timeout)
X  {
X  	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
X  	
X--- 234,240 ----
X   * iicbus_start() call
X   */
X  int
X! iicbus_write(device_t bus, const char *buf, int len, int *sent, int timeout)
X  {
X  	struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
X  	
END-of-dev/cxm/Patch.iicbb-fbsd5
echo x - dev/cxm/cxm.c
sed 's/^X//' >dev/cxm/cxm.c << 'END-of-dev/cxm/cxm.c'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Conexant MPEG-2 Codec driver. Supports the CX23415 / CX23416
X * chips that are on the Hauppauge PVR-250 and PVR-350 video
X * capture cards.  Currently only the encoder is supported.
X *
X * This driver was written using the invaluable information
X * compiled by The IvyTV Project (ivtv.sourceforge.net).
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/mman.h>
X#include <sys/module.h>
X#include <sys/poll.h>
X#include <sys/proc.h>
X#include <sys/signalvar.h>
X#include <sys/vnode.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#if __FreeBSD_version < 600000
X#  include <machine/bus_memio.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#if __FreeBSD_version < 503001
X#  include <machine/ioctl_meteor.h>
X#  include <machine/ioctl_bt848.h>
X#else
X#  include <dev/bktr/ioctl_meteor.h>
X#  include <dev/bktr/ioctl_bt848.h>
X#endif
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#if __FreeBSD_version < 500000
X#  include <pci/pcireg.h>
X#  include <pci/pcivar.h>
X#else
X#  include <dev/pci/pcireg.h>
X#  include <dev/pci/pcivar.h>
X#endif
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X
X
X/*
X * Various supported device vendors/types and their names.
X */
Xstatic struct cxm_dev cxm_devs[] = {
X	{ CXM_VENDORID_CONEXANT, CXM_DEVICEID_CONEXANT_iTVC15,
X		"Conexant iTVC15 MPEG Coder" },
X	{ CXM_VENDORID_CONEXANT, CXM_DEVICEID_CONEXANT_iTVC16,
X		"Conexant iTVC16 MPEG Coder" },
X	{ 0, 0, NULL }
X};
X
X
Xstatic int	cxm_probe( device_t dev );
Xstatic int	cxm_attach( device_t dev );
Xstatic int	cxm_detach( device_t dev );
Xstatic int	cxm_shutdown( device_t dev );
Xstatic void	cxm_intr( void *arg );
X
Xstatic void	cxm_child_detached( device_t dev, device_t child );
Xstatic int	cxm_read_ivar( device_t bus, device_t dev,
X			       int index, uintptr_t* val);
Xstatic int	cxm_write_ivar( device_t bus, device_t dev,
X				int index, uintptr_t val);
X
X
Xstatic device_method_t cxm_methods[] = {
X	/* Device interface */
X	DEVMETHOD(device_probe,         cxm_probe),
X	DEVMETHOD(device_attach,        cxm_attach),
X	DEVMETHOD(device_detach,        cxm_detach),
X	DEVMETHOD(device_shutdown,      cxm_shutdown),
X
X	/* bus interface */
X	DEVMETHOD(bus_child_detached,   cxm_child_detached),
X	DEVMETHOD(bus_print_child,      bus_generic_print_child),
X	DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
X	DEVMETHOD(bus_read_ivar,        cxm_read_ivar),
X	DEVMETHOD(bus_write_ivar,       cxm_write_ivar),
X
X	{ 0, 0 }
X};
X
Xstatic driver_t cxm_driver = {
X	"cxm",
X	cxm_methods,
X	sizeof(struct cxm_softc),
X};
X
Xstatic devclass_t cxm_devclass;
X
Xstatic	d_open_t	cxm_open;
Xstatic	d_close_t	cxm_close;
Xstatic	d_read_t	cxm_read;
Xstatic	d_ioctl_t	cxm_ioctl;
Xstatic	d_poll_t	cxm_poll;
X
X#if __FreeBSD_version < 500000
X#  define CDEV_MAJOR 92 
X#else
X#  define CDEV_MAJOR MAJOR_AUTO
X#endif
X
Xstatic struct cdevsw cxm_cdevsw = {
X#if __FreeBSD_version < 500000
X	.d_bmaj =	-1,
X#elif __FreeBSD_version >= 502103
X	.d_version =	D_VERSION,
X	.d_flags =	D_NEEDGIANT,
X#endif
X#if __FreeBSD_version < 600000
X	.d_maj =	CDEV_MAJOR,
X#endif
X	.d_name =	"cxm",
X	.d_open =	cxm_open,
X	.d_close =	cxm_close,
X	.d_read =	cxm_read,
X	.d_ioctl =	cxm_ioctl,
X	.d_poll =	cxm_poll
X};
X
XMODULE_DEPEND(cxm, cxm_iic, 1, 1, 1);
X#if __FreeBSD_version >= 500000
XMODULE_DEPEND(cxm, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
X#endif
XDRIVER_MODULE(cxm, pci, cxm_driver, cxm_devclass, 0, 0);
X
X
Xstatic struct cxm_codec_audio_format codec_audio_formats[] = {
X	{ 44100, 0xb8 }, /* 44.1 Khz, MPEG-1 Layer II, 224 kb/s */
X	{ 48000, 0xe9 }  /* 48 Khz, MPEG-1 Layer II, 384 kb/s */
X};
X
X
X/*
X * Various profiles.
X */
Xstatic struct cxm_codec_profile vcd_ntsc_profile = {
X	"MPEG-1 VideoCD NTSC video and MPEG audio",
X	CXM_FW_STREAM_TYPE_VCD,
X	30,
X	352, 240, 480,
X	{ 10, 12, 21 },
X	12,
X	0,
X	{ 1, 1150000, 0 },
X	{ 1, 15, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	44100
X};
X
Xstatic struct cxm_codec_profile vcd_pal_profile = {
X	"MPEG-1 VideoCD PAL video and MPEG audio",
X	CXM_FW_STREAM_TYPE_VCD,
X	25,
X	352, 288, 576,
X	{ 6, 17, 22 },
X	8,
X	0,
X	{ 1, 1150000, 0 },
X	{ 1, 12, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	44100
X};
X
Xstatic struct cxm_codec_profile svcd_ntsc_profile = {
X	"MPEG-2 SuperVCD NTSC video and MPEG audio",
X	CXM_FW_STREAM_TYPE_SVCD,
X	30,
X	480, 480, 480,
X	{ 10, 12, 21 },
X	2,
X	0,
X	/* 2.5 Mb/s peak limit to keep bbdmux followed by mplex -f 4 happy */
X	{ 0, 1150000, 2500000 },
X	{ 1, 15, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	44100
X};
X
Xstatic struct cxm_codec_profile svcd_pal_profile = {
X	"MPEG-2 SuperVCD PAL video and MPEG audio",
X	CXM_FW_STREAM_TYPE_SVCD,
X	25,
X	480, 576, 576,
X	{ 6, 17, 22 },
X	2,
X	0,
X	/* 2.5 Mb/s peak limit to keep bbdmux followed by mplex -f 4 happy */
X	{ 0, 1150000, 2500000 },
X	{ 1, 12, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	44100
X};
X
Xstatic struct cxm_codec_profile dvd_half_d1_ntsc_profile = {
X	"MPEG-2 DVD NTSC video and MPEG audio",
X	CXM_FW_STREAM_TYPE_DVD,
X	30,
X	352, 480, 480,
X	{ 10, 12, 21 },
X	2,
X	0,
X	{ 0, 4000000, 4520000 }, /* 4 hours on 8.54 GB media */
X	{ 1, 15, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	48000
X};
X
Xstatic struct cxm_codec_profile dvd_half_d1_pal_profile = {
X	"MPEG-2 DVD PAL video and MPEG audio",
X	CXM_FW_STREAM_TYPE_DVD,
X	25,
X	352, 576, 576,
X	{ 6, 17, 22 },
X	2,
X	0,
X	{ 0, 4000000, 4520000 }, /* 4 hours on 8.54 GB media */
X	{ 1, 12, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	48000
X};
X
Xstatic struct cxm_codec_profile dvd_full_d1_ntsc_profile = {
X	"MPEG-2 DVD NTSC video and MPEG audio",
X	CXM_FW_STREAM_TYPE_DVD,
X	30,
X	720, 480, 480,
X	{ 10, 12, 21 },
X	2,
X	0,
X	/* 9.52 Mb/s peak limit to keep bbdmux followed by mplex -f 8 happy */
X	{ 0, 9000000, 9520000 }, /* 1 hour on 4.7 GB media */
X	{ 1, 15, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	48000
X};
X
Xstatic struct cxm_codec_profile dvd_full_d1_pal_profile = {
X	"MPEG-2 DVD PAL video and MPEG audio",
X	CXM_FW_STREAM_TYPE_DVD,
X	25,
X	720, 576, 576,
X	{ 6, 17, 22 },
X	2,
X	0,
X	/* 9.52 Mb/s peak limit to keep bbdmux followed by mplex -f 8 happy */
X	{ 0, 9000000, 9520000 }, /* 1 hour on 4.7 GB media */
X	{ 1, 12, 3},
X	/*
X	 * Spatial filter = Manual, Temporal filter = Manual
X	 * Median filter = Horizontal / Vertical
X	 * Spatial filter value = 1, Temporal filter value = 4
X	 */
X	{ 0, 3, 1, 4 },
X	48000
X};
X
X
Xstatic const struct cxm_codec_profile
X*codec_profiles[] = {
X	&vcd_ntsc_profile,
X	&vcd_pal_profile,
X	&svcd_ntsc_profile,
X	&svcd_pal_profile,
X	&dvd_half_d1_ntsc_profile,
X	&dvd_half_d1_pal_profile,
X	&dvd_full_d1_ntsc_profile,
X	&dvd_full_d1_pal_profile
X};
X
X
Xstatic unsigned int
Xcxm_queue_firmware_command( struct cxm_softc *sc,
X			    enum cxm_mailbox_name mbx_name, u_int32_t cmd,
X			    u_int32_t *parameters, unsigned int nparameters )
X{
X	unsigned int i;
X	unsigned int mailbox;
X	u_int32_t completed_command;
X	u_int32_t flags;
X	intrmask_t s;
X
X	if (nparameters > CXM_MBX_MAX_PARAMETERS) {
X		printf("%s: too many parameters for mailbox\n", sc->name);
X		return -1;
X	}
X
X	mailbox = 0;
X
X	switch (mbx_name) {
X	case cxm_dec_mailbox:
X		mailbox = sc->dec_mbx
X			  + CXM_MBX_FW_CMD_MAILBOX *sizeof(struct cxm_mailbox);
X		break;
X
X	case cxm_enc_mailbox:
X		mailbox = sc->enc_mbx
X			  + CXM_MBX_FW_CMD_MAILBOX *sizeof(struct cxm_mailbox);
X		break;
X
X	default:
X		return -1;
X	}
X
X	s = spltty();
X	for (i = 0; i < CXM_MBX_FW_CMD_MAILBOXES; i++) {
X		flags = CSR_READ_4(sc,
X				   mailbox
X				   + offsetof(struct cxm_mailbox, flags));
X		if (! (flags & CXM_MBX_FLAG_IN_USE) )
X			break;
X
X		/*
X		 * Mail boxes containing certain completed commands
X		 * for which the results are never needed can be reused.
X		 */
X
X		if ((flags & (CXM_MBX_FLAG_DRV_DONE | CXM_MBX_FLAG_FW_DONE))
X		    == (CXM_MBX_FLAG_DRV_DONE | CXM_MBX_FLAG_FW_DONE)) {
X			completed_command
X			 = CSR_READ_4(sc,
X				      mailbox
X				      + offsetof(struct cxm_mailbox, command));
X
X			/*
X			 * DMA results are always check by reading the
X			 * DMA status register ... never by checking
X			 * the mailbox after the command has completed.
X			 */
X
X			if (completed_command == CXM_FW_CMD_SCHED_DMA_TO_HOST)
X				break;
X		}
X
X		mailbox += sizeof(struct cxm_mailbox);
X	}
X
X	if (i >= CXM_MBX_FW_CMD_MAILBOXES) {
X		splx(s);
X		return -1;
X	}
X
X	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags),
X		    CXM_MBX_FLAG_IN_USE);
X
X	/*
X	 * PCI writes may be buffered so force the
X	 * write to complete by reading the last
X	 * location written.
X	 */
X
X	(void)CSR_READ_4(sc, mailbox + offsetof(struct cxm_mailbox, flags));
X
X	splx(s);
X
X	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, command), cmd);
X	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, timeout),
X		    CXM_FW_STD_TIMEOUT);
X
X	for (i = 0; i < nparameters; i++)
X		CSR_WRITE_4(sc,
X			    mailbox
X			    + offsetof(struct cxm_mailbox, parameters)
X			    + i * sizeof(u_int32_t),
X			    *(parameters + i));
X
X	for ( ; i < CXM_MBX_MAX_PARAMETERS; i++)
X		CSR_WRITE_4(sc,
X			    mailbox
X			    + offsetof(struct cxm_mailbox, parameters)
X			    + i * sizeof(u_int32_t), 0);
X
X	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags),
X		    CXM_MBX_FLAG_IN_USE | CXM_MBX_FLAG_DRV_DONE);
X
X	return mailbox;
X}
X
X
Xstatic int
Xcxm_firmware_command( struct cxm_softc *sc,
X		      enum cxm_mailbox_name mbx_name, u_int32_t cmd,
X		      u_int32_t *parameters, unsigned int nparameters )
X{
X	const char *wmesg;
X	unsigned int *bmp;
X	unsigned int i;
X	unsigned int mailbox;
X	u_int32_t flags;
X	u_int32_t result;
X
X	bmp = NULL;
X	wmesg = "";
X
X	switch (mbx_name) {
X	case cxm_dec_mailbox:
X		bmp = &sc->dec_mbx;
X		wmesg = "cxmdfw";
X		break;
X
X	case cxm_enc_mailbox:
X		bmp = &sc->enc_mbx;
X		wmesg = "cxmefw";
X		break;
X
X	default:
X		return -1;
X	}
X
X	mailbox = cxm_queue_firmware_command(sc, mbx_name, cmd,
X					     parameters, nparameters);
X	if (mailbox == -1) {
X		printf("%s: no free mailboxes\n", sc->name);
X		return -1;
X	}
X
X	/* Give the firmware a chance to start processing the request */
X	(void)tsleep(bmp, PWAIT, wmesg, hz / 100);
X
X	for (i = 0; i < 100; i++) {
X		flags = CSR_READ_4(sc,
X				   mailbox
X				   + offsetof(struct cxm_mailbox, flags));
X		if ( (flags & CXM_MBX_FLAG_FW_DONE) )
X			break;
X
X		/* Wait for 10ms */
X		(void)tsleep(bmp, PWAIT, wmesg, hz / 100);
X	}
X
X	if (i >= 100) {
X		printf("%s: timeout\n", sc->name);
X		return -1;
X	}
X
X	result = CSR_READ_4(sc,
X			    mailbox
X			    + offsetof(struct cxm_mailbox, result));
X
X	for (i = 0; i < nparameters; i++)
X		*(parameters + i)
X		  = CSR_READ_4(sc,
X			       mailbox
X			       + offsetof(struct cxm_mailbox, parameters)
X			       + i * sizeof(u_int32_t));
X
X	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags), 0);
X
X	return result == 0 ? 0 : -1;
X}
X
X
Xstatic int
Xcxm_firmware_command_nosleep( struct cxm_softc *sc,
X			      enum cxm_mailbox_name mbx_name, u_int32_t cmd,
X			      u_int32_t *parameters, unsigned int nparameters )
X{
X	unsigned int i;
X	unsigned int mailbox;
X	u_int32_t flags;
X	u_int32_t result;
X
X	for (i = 0; i < 100; i++) {
X		mailbox = cxm_queue_firmware_command(sc, mbx_name, cmd,
X						     parameters, nparameters);
X		if (mailbox != -1)
X			break;
X
X		/* Wait for 10ms */
X		DELAY(10000);
X		}
X
X	if (i >= 100) {
X		printf("%s: no free mailboxes\n", sc->name);
X		return -1;
X	}
X
X	/* Give the firmware a chance to start processing the request */
X	DELAY(10000);
X
X	for (i = 0; i < 100; i++) {
X		flags = CSR_READ_4(sc,
X				   mailbox
X				   + offsetof(struct cxm_mailbox, flags));
X		if ( (flags & CXM_MBX_FLAG_FW_DONE) )
X			break;
X
X		/* Wait for 10ms */
X		DELAY(10000);
X	}
X
X	if (i >= 100) {
X		printf("%s: timeout\n", sc->name);
X		return -1;
X	}
X
X	result = CSR_READ_4(sc,
X			    mailbox
X			    + offsetof(struct cxm_mailbox, result));
X
X	for (i = 0; i < nparameters; i++)
X		*(parameters + i)
X		  = CSR_READ_4(sc,
X			       mailbox
X			       + offsetof(struct cxm_mailbox, parameters)
X			       + i * sizeof(u_int32_t));
X
X	CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags), 0);
X
X	return result == 0 ? 0 : -1;
X}
X
X
Xstatic int
Xcxm_stop_firmware( struct cxm_softc *sc )
X{
X
X	if (cxm_firmware_command_nosleep(sc, cxm_enc_mailbox,
X					 CXM_FW_CMD_ENC_HALT_FW, NULL, 0) < 0)
X		return -1;
X
X	if (sc->type == cxm_iTVC15_type
X	    && cxm_firmware_command_nosleep(sc, cxm_dec_mailbox,
X					    CXM_FW_CMD_DEC_HALT_FW,
X					    NULL, 0) < 0)
X		return -1;
X
X	/* Wait for 10ms */
X	DELAY(10000);
X
X	return 0;
X}
X
X
Xstatic void
Xcxm_set_irq_mask( struct cxm_softc *sc, u_int32_t mask )
X{
X	intrmask_t s;
X
X	s = spltty();
X
X	CSR_WRITE_4(sc, CXM_REG_IRQ_MASK, mask);
X
X	/*
X	 * PCI writes may be buffered so force the
X	 * write to complete by reading the last
X	 * location written.
X	 */
X
X	(void)CSR_READ_4(sc, CXM_REG_IRQ_MASK);
X
X	sc->irq_mask = mask;
X
X	splx(s);
X}
X
X
Xstatic void
Xcxm_set_irq_status( struct cxm_softc *sc, u_int32_t status )
X{
X
X	CSR_WRITE_4(sc, CXM_REG_IRQ_STATUS, status);
X
X	/*
X	 * PCI writes may be buffered so force the
X	 * write to complete by reading the last
X	 * location written.
X	 */
X
X	(void)CSR_READ_4(sc, CXM_REG_IRQ_STATUS);
X}
X
X
Xstatic int
Xcxm_stop_hardware( struct cxm_softc *sc )
X{
X
X	if (sc->cxm_iic) {
X		if (cxm_saa7115_mute(sc) < 0)
X			return -1;
X		if (cxm_msp_mute(sc) < 0)
X			return -1;
X	}
X
X	/* Halt the firmware */
X	if (sc->enc_mbx != -1) {
X		if (cxm_stop_firmware(sc) < 0)
X			return -1;
X	}
X
X	/* Mask all interrupts */
X	cxm_set_irq_mask(sc, 0xffffffff);
X
X	/* Stop VDM */
X	CSR_WRITE_4(sc, CXM_REG_VDM, CXM_CMD_VDM_STOP);
X
X	/* Stop AO */
X	CSR_WRITE_4(sc, CXM_REG_AO, CXM_CMD_AO_STOP);
X
X	/* Ping (?) APU */
X	CSR_WRITE_4(sc, CXM_REG_APU, CXM_CMD_APU_PING);
X
X	/* Stop VPU */
X	CSR_WRITE_4(sc, CXM_REG_VPU, sc->type == cxm_iTVC15_type
X					? CXM_CMD_VPU_STOP15
X					: CXM_CMD_VPU_STOP16);
X
X	/* Reset Hw Blocks */
X	CSR_WRITE_4(sc, CXM_REG_HW_BLOCKS, CXM_CMD_HW_BLOCKS_RST);
X
X	/* Stop SPU */
X	CSR_WRITE_4(sc, CXM_REG_SPU, CXM_CMD_SPU_STOP);
X
X	/* Wait for 10ms */
X	DELAY(10000);
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_download_firmware( struct cxm_softc *sc )
X{
X	unsigned int i;
X	const u_int32_t *fw;
X
X	/* Download the encoder firmware */
X	fw = (const u_int32_t *)cxm_enc_fw;
X	for (i = 0; i < CXM_FW_SIZE; i += sizeof(*fw))
X		CSR_WRITE_4(sc, CXM_MEM_ENC + i, *fw++);
X
X	/* Download the decoder firmware */
X	if (sc->type == cxm_iTVC15_type) {
X		fw = (const u_int32_t *)cxm_dec_fw;
X		for (i = 0; i < CXM_FW_SIZE; i += sizeof(*fw))
X			CSR_WRITE_4(sc, CXM_MEM_DEC + i, *fw++);
X	}
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_init_hardware( struct cxm_softc *sc )
X{
X	unsigned int i;
X	unsigned int mailbox;
X	u_int32_t parameter;
X
X	if (cxm_stop_hardware(sc) < 0)
X		return -1;
X
X	/* Initialize encoder SDRAM pre-charge */
X	CSR_WRITE_4(sc, CXM_REG_ENC_SDRAM_PRECHARGE,
X			CXM_CMD_SDRAM_PRECHARGE_INIT);
X
X	/* Initialize encoder SDRAM refresh to 1us */
X	CSR_WRITE_4(sc, CXM_REG_ENC_SDRAM_REFRESH,
X			CXM_CMD_SDRAM_REFRESH_INIT);
X
X	/* Initialize decoder SDRAM pre-charge */
X	CSR_WRITE_4(sc, CXM_REG_DEC_SDRAM_PRECHARGE,
X			CXM_CMD_SDRAM_PRECHARGE_INIT);
X
X	/* Initialize decoder SDRAM refresh to 1us */
X	CSR_WRITE_4(sc, CXM_REG_DEC_SDRAM_REFRESH,
X			CXM_CMD_SDRAM_REFRESH_INIT);
X
X	/* Wait for 600ms */
X	DELAY(600000);
X
X	if (cxm_download_firmware(sc) < 0)
X		return -1;
X
X	/* Enable SPU */
X	CSR_WRITE_4(sc, CXM_REG_SPU,
X			CSR_READ_4(sc, CXM_REG_SPU) & CXM_MASK_SPU_ENABLE);
X
X	/* Wait for 1 second */
X	DELAY(1000000);
X
X	/* Enable VPU */
X	CSR_WRITE_4(sc, CXM_REG_VPU,
X			CSR_READ_4(sc, CXM_REG_VPU)
X			& (sc->type == cxm_iTVC15_type
X				? CXM_MASK_VPU_ENABLE15
X				: CXM_MASK_VPU_ENABLE16));
X
X	/* Wait for 1 second */
X	DELAY(1000000);
X
X	/* Locate encoder mailbox */
X	mailbox = CXM_MEM_ENC;
X	for (i = 0; i < CXM_MEM_ENC_SIZE; i += 0x100)
X		if (CSR_READ_4(sc, mailbox + i) == 0x12345678
X		    && CSR_READ_4(sc, mailbox + i + 4) == 0x34567812
X		    && CSR_READ_4(sc, mailbox + i + 8) == 0x56781234
X		    && CSR_READ_4(sc, mailbox + i + 12) == 0x78123456)
X			break;
X
X	if (i >= CXM_MEM_ENC_SIZE)
X		return -1;
X
X	sc->enc_mbx = mailbox + i + 16;
X
X	/* Locate decoder mailbox */
X	if (sc->type == cxm_iTVC15_type) {
X		mailbox = CXM_MEM_DEC;
X		for (i = 0; i < CXM_MEM_DEC_SIZE; i += 0x100)
X			if (CSR_READ_4(sc, mailbox + i) == 0x12345678
X			    && CSR_READ_4(sc, mailbox + i + 4) == 0x34567812
X			    && CSR_READ_4(sc, mailbox + i + 8) == 0x56781234
X			    && CSR_READ_4(sc, mailbox + i + 12) == 0x78123456)
X				break;
X
X		if (i >= CXM_MEM_DEC_SIZE)
X			return -1;
X
X		sc->dec_mbx = mailbox + i + 16;
X	}
X
X	/* Get encoder firmware version */
X	parameter = 0;
X	if (cxm_firmware_command_nosleep(sc, cxm_enc_mailbox,
X					 CXM_FW_CMD_ENC_GET_FW_VER,
X					 &parameter, 1) < 0)
X		return -1;
X
X	printf("%s: encoder firmware version %#x\n",
X	       sc->name, (unsigned int)parameter);
X
X	/* Get decoder firmware version */
X	if (sc->type == cxm_iTVC15_type) {
X		parameter = 0;
X		if (cxm_firmware_command_nosleep(sc, cxm_dec_mailbox,
X						 CXM_FW_CMD_DEC_GET_FW_VER,
X						 &parameter, 1) < 0)
X			return -1;
X
X		printf("%s: decoder firmware version %#x\n",
X		       sc->name, (unsigned int)parameter);
X	}
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_configure_encoder( struct cxm_softc *sc )
X{
X	int fps;
X	unsigned int i;
X	u_int32_t parameters[12];
X	const struct cxm_codec_profile *cpp;
X
X	if (sc->source == cxm_fm_source)
X		switch (cxm_tuner_selected_channel_set(sc)) {
X		case CHNLSET_NABCST:
X		case CHNLSET_CABLEIRC:
X		case CHNLSET_JPNBCST:
X		case CHNLSET_JPNCABLE:
X			fps = 30;
X			break;
X
X		default:
X			fps = 25;
X			break;
X		}
X	else
X		fps = cxm_saa7115_detected_fps(sc);
X
X	if (fps < 0)
X		return -1;
X
X	if (sc->profile->fps != fps) {
X
X		/*
X		 * Pick a profile with the correct fps using the
X		 * chosen stream type and width to decide between
X		 * the VCD, SVCD, or DVD profiles.
X		 */
X
X		for (i = 0; i < NUM_ELEMENTS(codec_profiles); i++)
X			if (codec_profiles[i]->fps == fps
X			    && codec_profiles[i]->stream_type
X			       == sc->profile->stream_type
X			    && codec_profiles[i]->width == sc->profile->width)
X				break;
X
X		if (i >= NUM_ELEMENTS(codec_profiles))
X			return -1;
X
X		sc->profile = codec_profiles[i];
X	}
X
X	cpp = sc->profile;
X
X	if (cxm_saa7115_configure(sc,
X				  cpp->width, cpp->source_height, fps,
X				  cpp->audio_sample_rate) < 0)
X		return -1;
X
X	/* assign dma block len */
X	parameters[0] = 1; /* Transfer block size = 1 */
X	parameters[1] = 1; /* Units = 1 (frames) */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_DMA_BLOCKLEN,
X				 parameters, 2) != 0)
X		return -1;
X
X
X	/* assign program index info */
X	parameters[0] = 0; /* Picture mask = 0 (don't generate index) */
X	parameters[1] = 0; /* Num_req = 0 */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_PGM_INDEX_INFO,
X				 parameters, 2) != 0)
X		return -1;
X
X	/* assign stream type */
X	parameters[0] = cpp->stream_type;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_STREAM_TYPE,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign output port */
X	parameters[0] = 0; /* 0 (Memory) */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_OUTPUT_PORT,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign framerate */
X	parameters[0] = cpp->fps == 30 ? 0 : 1;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_FRAME_RATE,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign frame size */
X	parameters[0] = cpp->height;
X	parameters[1] = cpp->width;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_FRAME_SIZE,
X				 parameters, 2) != 0)
X		return -1;
X
X	/* assign aspect ratio */
X	parameters[0] = cpp->aspect;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_ASPECT_RATIO,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign bitrates */
X	parameters[0] = cpp->bitrate.mode;
X	parameters[1] = cpp->bitrate.average;
X	parameters[2] = cpp->bitrate.peak / 400;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_BITRATES,
X				 parameters, 3) != 0)
X		return -1;
X
X	/* assign gop closure */
X	parameters[0] = cpp->gop.closure;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_GOP_CLOSURE,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign gop properties */
X	parameters[0] = cpp->gop.frames;
X	parameters[1] = cpp->gop.bframes;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_GOP_PROPERTIES,
X				 parameters, 2) != 0)
X		return -1;
X
X	/* assign 3 2 pulldown */
X	parameters[0] = cpp->pulldown;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_3_2_PULLDOWN,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign dnr filter mode */
X	parameters[0] = cpp->dnr.mode;
X	parameters[1] = cpp->dnr.type;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_DNR_FILTER_MODE,
X				 parameters, 2) != 0)
X		return -1;
X
X	/* assign dnr filter props */
X	parameters[0] = cpp->dnr.spatial;
X	parameters[1] = cpp->dnr.temporal;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_DNR_FILTER_PROPERTIES,
X				 parameters, 2) != 0)
X		return -1;
X
X	/*
X	 * assign audio properties
X	 */
X
X	for (i = 0; i < NUM_ELEMENTS(codec_audio_formats); i++)
X		if (codec_audio_formats[i].sample_rate
X		    == cpp->audio_sample_rate)
X			break;
X
X	if (i >= NUM_ELEMENTS(codec_audio_formats))
X		return -1;
X
X	parameters[0] = codec_audio_formats[i].format;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_AUDIO_PROPERTIES,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign coring levels */
X	parameters[0] = 0; /* luma_h */
X	parameters[1] = 255; /* luma_l */
X	parameters[2] = 0; /* chroma_h */
X	parameters[3] = 255; /* chroma_l */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_CORING_LEVELS,
X				 parameters, 4) != 0)
X		return -1;
X
X	/* assign spatial filter type */
X	parameters[0] = 3; /* Luminance filter = 3 (2D H/V Separable) */
X	parameters[1] = 1; /* Chrominance filter = 1 (1D Horizontal) */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_SPATIAL_FILTER_TYPE,
X				 parameters, 2) != 0)
X		return -1;
X
X	/* assign frame drop rate */
X	parameters[0] = 0;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_FRAME_DROP_RATE,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* assign placeholder */
X	parameters[0] = 0; /* type = 0 (Extension / UserData) */
X	parameters[1] = 0; /* period */
X	parameters[2] = 0; /* size_t */
X	parameters[3] = 0; /* arg0 */
X	parameters[4] = 0; /* arg1 */
X	parameters[5] = 0; /* arg2 */
X	parameters[6] = 0; /* arg3 */
X	parameters[7] = 0; /* arg4 */
X	parameters[8] = 0; /* arg5 */
X	parameters[9] = 0; /* arg6 */
X	parameters[10] = 0; /* arg7 */
X	parameters[11] = 0; /* arg8 */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_PLACEHOLDER,
X				 parameters, 12) != 0)
X		return -1;
X
X	/* assign VBI properties */
X	parameters[0] = 0xbd04; /* mode = 0 (sliced), stream and user data */
X	parameters[1] = 0; /* frames per interrupt (only valid in raw mode) */
X	parameters[2] = 0; /* total raw VBI frames (only valid in raw mode) */
X	parameters[3] = 0x25256262; /* ITU 656 start codes (saa7115 table 24)*/
X	parameters[4] = 0x38387f7f; /* ITU 656 stop codes (saa7115 table 24) */
X	parameters[5] = cpp->vbi.nlines; /* lines per frame */
X	parameters[6] = 1440; /* bytes per line = 720 pixels */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_VBI_PROPERTIES,
X				 parameters, 7) != 0)
X		return -1;
X
X	/* assign VBI lines */
X	parameters[0] = 0xffffffff; /* all lines */
X	parameters[1] = 0; /* disable VBI features */
X	parameters[2] = 0;
X	parameters[3] = 0;
X	parameters[4] = 0;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_VBI_LINE,
X				 parameters, 5) != 0)
X		return -1;
X
X	/* assign number of lines in fields 1 and 2 */
X	parameters[0] = cpp->source_height / 2 + cpp->vbi.nlines;
X	parameters[1] = cpp->source_height / 2 + cpp->vbi.nlines;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ASSIGN_NUM_VSYNC_LINES,
X				 parameters, 2) != 0)
X		return -1;
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_start_encoder( struct cxm_softc *sc )
X{
X	u_int32_t parameters[4];
X	u_int32_t subtype;
X	u_int32_t type;
X
X	if (sc->encoding)
X		return 0;
X
X	if (cxm_configure_encoder(sc) < 0)
X		return -1;
X
X	/* Mute the video input if necessary. */
X	parameters[0] = sc->source == cxm_fm_source ? 1 : 0;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_MUTE_VIDEO_INPUT,
X				 parameters, 1) != 0)
X		return -1;
X
X	/* Clear pending encoder interrupts (which are currently masked) */
X	cxm_set_irq_status(sc, CXM_IRQ_ENC);
X
X	/* Enable event notification */
X	parameters[0] = 0; /* Event = 0 (refresh encoder input) */
X	parameters[1] = 1; /* Notification = 1 (enable) */
X	parameters[2] = 0x10000000; /* Interrupt bit */
X	parameters[3] = -1; /* Mailbox = -1 (no mailbox) */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ENC_EVENT_NOTIFICATION,
X				 parameters, 4) != 0)
X		return -1;
X
X	if (cxm_saa7115_mute(sc) < 0)
X		return -1;
X	if (cxm_msp_mute(sc) < 0)
X		return -1;
X
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_INITIALIZE_VIDEO_INPUT,
X				 NULL, 0) < 0)
X		return -1;
X
X	if (cxm_saa7115_unmute(sc) < 0)
X		return -1;
X	if (cxm_msp_unmute(sc) < 0)
X		return -1;
X
X	/* Wait for 100ms */
X	(void)tsleep(&sc->encoding, PWAIT, "cxmce", hz / 10);
X
X	type = sc->mpeg ? CXM_FW_CAPTURE_STREAM_TYPE_MPEG
X			: CXM_FW_CAPTURE_STREAM_TYPE_RAW;
X	subtype = ((sc->mpeg || sc->source == cxm_fm_source)
X		   ? CXM_FW_CAPTURE_STREAM_PCM_AUDIO : 0)
X		  | ((sc->mpeg || sc->source != cxm_fm_source)
X		     ? CXM_FW_CAPTURE_STREAM_YUV : 0);
X
X	/* Start the encoder */
X	parameters[0] = type;
X	parameters[1] = subtype;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_BEGIN_CAPTURE, parameters, 2) != 0)
X		return -1;
X
X	sc->enc_pool.offset = 0;
X	sc->enc_pool.read = 0;
X	sc->enc_pool.write = 0;
X
X	sc->encoding_eos = 0;
X
X	sc->encoding = 1;
X
X	/* Enable interrupts */
X	cxm_set_irq_mask(sc, sc->irq_mask & ~CXM_IRQ_ENC);
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_stop_encoder( struct cxm_softc *sc )
X{
X	u_int32_t parameters[4];
X	u_int32_t subtype;
X	u_int32_t type;
X	intrmask_t s;
X
X	if (! sc->encoding )
X		return 0;
X
X	type = sc->mpeg ? CXM_FW_CAPTURE_STREAM_TYPE_MPEG
X			: CXM_FW_CAPTURE_STREAM_TYPE_RAW;
X	subtype = ((sc->mpeg || sc->source == cxm_fm_source)
X		   ? CXM_FW_CAPTURE_STREAM_PCM_AUDIO : 0)
X		  | ((sc->mpeg || sc->source != cxm_fm_source)
X		     ? CXM_FW_CAPTURE_STREAM_YUV : 0);
X
X	/* Stop the encoder */
X	parameters[0] = sc->mpeg ? 0 : 1; /* When = 0 (end of GOP) */
X	parameters[1] = type;
X	parameters[2] = subtype;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_END_CAPTURE, parameters, 3) != 0)
X		return -1;
X
X	/* Wait for up to 1 second */
X	s = spltty();
X	if (! sc->encoding_eos )
X		(void)tsleep(&sc->encoding_eos, PWAIT, "cxmeos", hz);
X	splx(s);
X
X	if (sc->mpeg && ! sc->encoding_eos )
X		printf("%s: missing encoder EOS\n", sc->name);
X
X	/* Disable event notification */
X	parameters[0] = 0; /* Event = 0 (refresh encoder input) */
X	parameters[1] = 0; /* Notification = 0 (disable) */
X	parameters[2] = 0x10000000; /* Interrupt bit */
X	parameters[3] = -1; /* Mailbox = -1 (no mailbox) */
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_ENC_EVENT_NOTIFICATION,
X				 parameters, 4) != 0)
X		return -1;
X
X	/* Disable interrupts */
X	cxm_set_irq_mask(sc, sc->irq_mask | CXM_IRQ_ENC);
X
X	sc->encoding = 0;
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_pause_encoder( struct cxm_softc *sc )
X{
X	u_int32_t parameter;
X
X	/* Pause the encoder */
X	parameter = 0;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_PAUSE_ENCODER, &parameter, 1) != 0)
X		return -1;
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_unpause_encoder( struct cxm_softc *sc )
X{
X	u_int32_t parameter;
X
X	/* Unpause the encoder */
X	parameter = 1;
X	if (cxm_firmware_command(sc, cxm_enc_mailbox,
X				 CXM_FW_CMD_PAUSE_ENCODER, &parameter, 1) != 0)
X		return -1;
X
X	return 0;
X}
X
X
Xstatic unsigned int
Xcxm_encoder_fixup_byte_order( struct cxm_softc *sc,
X			      unsigned int current, size_t offset)
X{
X	unsigned int	strips;
X	unsigned int	i;
X	unsigned int	j;
X	unsigned int	k;
X	unsigned int	macroblocks_per_line;
X	unsigned int	scratch;
X	unsigned int	words_per_line;
X	u_int32_t	*ptr;
X	u_int32_t	*src;
X	size_t		nbytes;
X
X	switch (sc->enc_pool.bufs[current].byte_order) {
X	case cxm_device_mpeg_byte_order:
X
X		/*
X		 * Convert each 32 bit word to the proper byte ordering.
X		 */
X
X		for (nbytes = 0,
X		     ptr = (u_int32_t *)sc->enc_pool.bufs[current].vaddr;
X		     nbytes != sc->enc_pool.bufs[current].size;
X		     nbytes += sizeof(*ptr), ptr++)
X			*ptr = bswap32(*ptr);
X
X		break;
X
X	case cxm_device_yuv12_byte_order:
X
X		/*
X		 * Convert each macro block to planar using
X		 * a scratch buffer (the buffer prior to the
X		 * current buffer is always free since it marks
X		 * the end of the ring buffer).
X		 */
X
X		scratch = (current + (CXM_SG_BUFFERS - 1)) % CXM_SG_BUFFERS;
X
X		if (offset) {
X			current = scratch;
X			break;
X		}
X
X		src = (u_int32_t *)sc->enc_pool.bufs[current].vaddr;
X		words_per_line = sc->profile->width / sizeof(*ptr);
X		macroblocks_per_line
X		  = sc->profile->width / CXM_MACROBLOCK_WIDTH;
X		strips = sc->enc_pool.bufs[current].size
X			   / (macroblocks_per_line * CXM_MACROBLOCK_SIZE);
X
X		for (i = 0; i < strips; i++) {
X			ptr = (u_int32_t *)sc->enc_pool.bufs[scratch].vaddr
X			      + i * macroblocks_per_line * CXM_MACROBLOCK_SIZE
X				/ sizeof(*ptr);
X			for (j = 0; j < macroblocks_per_line; j++) {
X				for (k = 0; k < CXM_MACROBLOCK_HEIGHT; k++) {
X#if CXM_MACROBLOCK_WIDTH != 16
X#  error CXM_MACROBLOCK_WIDTH != 16
X#endif
X					*(ptr + k * words_per_line)
X					  = *src++;
X					*(ptr + k * words_per_line + 1)
X					  = *src++;
X					*(ptr + k * words_per_line + 2)
X					  = *src++;
X					*(ptr + k * words_per_line + 3)
X					  = *src++;
X				}
X				ptr += CXM_MACROBLOCK_WIDTH / sizeof(*ptr);
X			}
X		}
X
X		sc->enc_pool.bufs[scratch].size
X		  = sc->enc_pool.bufs[current].size;
X
X		current = scratch;
X		break;
X
X	default:
X		break;
X	}
X
X	sc->enc_pool.bufs[current].byte_order = cxm_host_byte_order;
X
X	return current;
X}
X
X
Xstatic void
Xcxm_encoder_dma_discard( struct cxm_softc *sc )
X{
X	u_int32_t parameters[3];
X
X	/* Discard the DMA request */
X	parameters[0] = 0;
X	parameters[1] = 0;
X	parameters[2] = 0;
X	if (cxm_queue_firmware_command(sc, cxm_enc_mailbox,
X				       CXM_FW_CMD_SCHED_DMA_TO_HOST,
X				       parameters, 3) == -1) {
X		printf("%s: failed to discard encoder dma request\n",
X		       sc->name);
X		return;
X	}
X
X	sc->encoding_dma = -1;
X}
X
X
Xstatic void
Xcxm_encoder_dma_done( struct cxm_softc *sc )
X{
X	int buffers_pending;
X	u_int32_t status;
X	intrmask_t s;
X
X	if (! sc->encoding_dma) {
X		printf("%s: encoder dma not already in progress\n",
X		       sc->name);
X		return;
X	}
X
X	buffers_pending = sc->encoding_dma;
X	sc->encoding_dma = 0;
X
X	if (buffers_pending < 0)
X		return;
X
X	status = CSR_READ_4(sc, CXM_REG_DMA_STATUS) & 0x0000000f;
X
X	if ((status
X	     & (CXM_DMA_ERROR_LIST | CXM_DMA_ERROR_WRITE | CXM_DMA_SUCCESS))
X	    != CXM_DMA_SUCCESS) {
X		printf("%s: encoder dma status %#x\n",
X		       sc->name, (unsigned int)status);
X		return;
X	}
X
X	/* Update the books (spl is used since mutex is not available) */
X	s = spltty();
X	sc->enc_pool.write = (sc->enc_pool.write + buffers_pending)
X				   % CXM_SG_BUFFERS;
X	splx(s);
X
X	/* signal anyone requesting notification */
X	if (sc->enc_proc)
X		psignal (sc->enc_proc, sc->enc_signal);
X
X	/* wakeup anyone waiting for data */
X	wakeup(&sc->enc_pool.read);
X
X	/* wakeup anyone polling for data */
X	selwakeup(&sc->enc_sel);
X}
X
X
Xstatic void
Xcxm_encoder_dma_request( struct cxm_softc *sc )
X{
X	enum cxm_byte_order byte_order;
X	int buffers_free;
X	int buffers_pending;
X	unsigned int current;
X	unsigned int i;
X	unsigned int mailbox;
X	unsigned int macroblocks_per_line;
X	unsigned int nrequests;
X	unsigned int strips;
X	u_int32_t parameters[CXM_MBX_MAX_PARAMETERS];
X	u_int32_t type;
X	size_t max_sg_segment;
X	struct {
X		size_t offset;
X		size_t size;
X	} requests[2];
X	intrmask_t s;
X
X	if (sc->encoding_dma) {
X		printf("%s: encoder dma already in progress\n",
X		       sc->name);
X		cxm_encoder_dma_discard(sc);
X		return;
X	}
X
X	mailbox = sc->enc_mbx
X		  + CXM_MBX_FW_DMA_MAILBOX * sizeof(struct cxm_mailbox);
X
X	for (i = 0; i < CXM_MBX_MAX_PARAMETERS; i++)
X		parameters[i]
X		  = CSR_READ_4(sc,
X			       mailbox
X			       + offsetof(struct cxm_mailbox, parameters)
X			       + i * sizeof(u_int32_t));
X
X	byte_order = cxm_device_mpeg_byte_order;
X	max_sg_segment = CXM_SG_SEGMENT;
X	nrequests = 0;
X	type = parameters[0];
X
X	switch (type) {
X	case 0: /* MPEG */
X		requests[nrequests].offset = parameters[1];
X		requests[nrequests++].size = parameters[2];
X		break;
X
X	case 1: /* YUV */
X		byte_order = cxm_device_yuv12_byte_order;
X
X		/*
X		 * Simplify macroblock unpacking by ensuring
X		 * that strips don't span buffers.
X		 */
X
X#if CXM_MACROBLOCK_SIZE % 256
X#  error CXM_MACROBLOCK_SIZE not a multiple of 256
X#endif
X
X		macroblocks_per_line = sc->profile->width
X				       / CXM_MACROBLOCK_WIDTH;
X		strips = CXM_SG_SEGMENT
X			 / (macroblocks_per_line * CXM_MACROBLOCK_SIZE);
X		max_sg_segment = strips
X				 * macroblocks_per_line * CXM_MACROBLOCK_SIZE;
X
X		requests[nrequests].offset = parameters[1]; /* Y */
X		requests[nrequests++].size = parameters[2];
X		requests[nrequests].offset = parameters[3]; /* UV */
X		requests[nrequests++].size = parameters[4];
X		break;
X
X	case 2: /* PCM (audio) */
X	case 3: /* VBI */
X	default:
X		printf("%s: encoder dma type %#x unsupported\n",
X		       sc->name, (unsigned int)type);
X		cxm_encoder_dma_discard(sc);
X		return;
X	}
X
X	/*
X	 * Determine the number of buffers free at this * instant *
X	 * taking into consideration that the ring buffer wraps.
X	 */
X	s = spltty();
X	buffers_free = sc->enc_pool.read - sc->enc_pool.write;
X	if (buffers_free <= 0)
X		buffers_free += CXM_SG_BUFFERS;
X	splx(s);
X
X	/*
X	 * Build the scatter / gather list taking in
X	 * consideration that the ring buffer wraps,
X	 * at least one free buffer must always be
X	 * present to mark the end of the ring buffer,
X	 * and each transfer must be a multiple of 256.
X	 */
X
X	buffers_pending = 0;
X	current = sc->enc_pool.write;
X
X	for (i = 0; i < nrequests; i++) {
X		if (! requests[i].size ) {
X			printf("%s: encoder dma size is zero\n", sc->name);
X			cxm_encoder_dma_discard(sc);
X			return;
X		}
X
X		while (requests[i].size) {
X			sc->enc_pool.bufs[current].size
X			  = requests[i].size > max_sg_segment
X			    ? max_sg_segment : requests[i].size;
X			sc->enc_pool.bufs[current].byte_order = byte_order;
X
X			sc->enc_sg.vaddr[buffers_pending].src
X			  = requests[i].offset;
X			sc->enc_sg.vaddr[buffers_pending].dst
X			  = sc->enc_pool.bufs[current].baddr;
X			sc->enc_sg.vaddr[buffers_pending].size
X			  = (sc->enc_pool.bufs[current].size + 0x000000ff)
X			    & 0xffffff00;
X
X			requests[i].offset += sc->enc_pool.bufs[current].size;
X			requests[i].size -= sc->enc_pool.bufs[current].size;
X			buffers_pending++;
X			current = (current + 1) % CXM_SG_BUFFERS;
X
X			if (buffers_pending >= buffers_free) {
X				printf(
X			      "%s: encoder dma not enough buffer space free\n",
X				       sc->name);
X				cxm_encoder_dma_discard(sc);
X				return;
X			}
X		}
X	}
X
X	/* Mark the last transfer in the list */
X	sc->enc_sg.vaddr[buffers_pending - 1].size |= 0x80000000;
X
X	/* Schedule the DMA */
X	parameters[0] = sc->enc_sg.baddr;
X	parameters[1] = buffers_pending * sizeof(sc->enc_sg.vaddr[0]);
X	parameters[2] = type;
X	if (cxm_queue_firmware_command(sc, cxm_enc_mailbox,
X				       CXM_FW_CMD_SCHED_DMA_TO_HOST,
X				       parameters, 3) == -1) {
X		printf("%s: failed to schedule encoder dma request\n",
X		       sc->name);
X		return;
X	}
X
X	/*
X	 * Record the number of pending buffers for the
X	 * benefit of cxm_encoder_dma_done.  Doing this
X	 * after queuing the command doesn't introduce
X	 * a race condition since we're already in the
X	 * interrupt handler.
X	 */
X
X	sc->encoding_dma = buffers_pending;
X}
X
X
Xstatic int
Xcxm_encoder_wait_for_lock( struct cxm_softc *sc )
X{
X	int muted;
X	int locked;
X	int result;
X
X	locked = 1;
X
X	/*
X	 * Wait for the tuner to lock.
X	 */
X	if (sc->source == cxm_fm_source || sc->source == cxm_tuner_source) {
X		result = cxm_tuner_wait_for_lock(sc);
X		if (result <= 0)
X			return result;
X		}
X
X	/*
X	 * Wait for the video decoder to lock.
X	 */
X	if (sc->source != cxm_fm_source) {
X		result = cxm_saa7115_wait_for_lock(sc);
X		if (result < 0)
X			return result;
X		else if (result == 0)
X			locked = 0;
X		}
X
X	/*
X	 * Wait for the audio decoder to lock.
X	 */
X	if (sc->source == cxm_tuner_source) {
X		muted = cxm_msp_is_muted(sc);
X
X		result = cxm_msp_autodetect_standard(sc);
X		if (result < 0)
X			return result;
X		else if (result == 0)
X			locked = 0;
X
X		if (muted == 0 && cxm_msp_unmute(sc) < 0)
X			return -1;
X	}
X
X	return locked;
X}
X
X
Xstatic void
Xcxm_mapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error)
X{
X	bus_addr_t *busaddrp;
X 
X	/*
X	 * Only the first bus space address is needed
X	 * since it's known that the memory is physically
X	 * contiguous due to bus_dmamem_alloc.
X	 */
X
X	busaddrp = (bus_addr_t *)arg;
X	*busaddrp = segs->ds_addr;
X}
X
X
X/*
X * the boot time probe routine.
X */
Xstatic int
Xcxm_probe( device_t dev )
X{
X	struct cxm_dev		*t;
X
X	t = cxm_devs;
X
X	while(t->name != NULL) {
X		if ((pci_get_vendor(dev) == t->vid) &&
X		    (pci_get_device(dev) == t->did)) {
X			device_set_desc(dev, t->name);
X			return 0;
X		}
X		t++;
X	}
X
X	return ENXIO;
X}
X
X
X/*
X * the attach routine.
X */
Xstatic int
Xcxm_attach( device_t dev )
X{
X	int		error;
X	int		rid;
X	int		unit;
X	unsigned int	i;
X	u_int32_t	command;
X	struct cxm_softc *sc;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X	unit = device_get_unit(dev);
X
X	sc->type = cxm_iTVC15_type;
X	switch(pci_get_device(dev)) {
X	case CXM_DEVICEID_CONEXANT_iTVC16:
X		sc->type = cxm_iTVC16_type;
X		break;
X
X	default:
X		break;
X	}
X	
X	/* build the device name */
X	snprintf(sc->name, sizeof(sc->name), "cxm%d",unit);
X
X	/*
X	 * Enable bus mastering and memory mapped I/O.
X	 */
X	pci_enable_busmaster(dev);
X	pci_enable_io(dev, SYS_RES_MEMORY);
X	command = pci_read_config(dev, PCIR_COMMAND, 4);
X
X	if (! (command & PCIM_CMD_MEMEN) ) {
X		device_printf(dev, "failed to enable memory mappings\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	/*
X	 * Map control/status registers.
X	 */
X	rid = CXM_RID;
X	sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
X					0, ~0, 1, RF_ACTIVE);
X
X	if (! sc->mem_res ) {
X		device_printf(dev, "could not map memory\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	sc->btag = rman_get_bustag(sc->mem_res);
X	sc->bhandle = rman_get_bushandle(sc->mem_res);
X
X	/*
X	 * Attach the I2C bus.
X	 */
X	sc->cxm_iic = device_add_child(dev, "cxm_iic", unit);
X
X	if (! sc->cxm_iic ) {
X		device_printf(dev, "could not add cxm_iic\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	error = device_probe_and_attach(sc->cxm_iic);
X
X	if (error) {
X		device_printf(dev, "could not attach cxm_iic\n");
X		goto fail;
X	}
X
X	/*
X	 * Initialize the tuner.
X	 */
X	if (cxm_tuner_init(sc) < 0) {
X		device_printf(dev, "could not initialize tuner\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	/*
X	 * Initialize the SAA7115.
X	 */
X	if (cxm_saa7115_init(sc) < 0) {
X		device_printf(dev, "could not initialize video decoder\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	/*
X	 * Initialize the MSP3400.
X	 */
X	if (cxm_msp_init(sc) < 0) {
X		device_printf(dev, "could not initialize audio decoder\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	/*
X	 * Initialize the IR Remote.
X	 */
X	if (cxm_ir_init(sc) < 0) {
X		device_printf(dev, "could not initialize IR remote\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	sc->dec_mbx = -1;
X	sc->enc_mbx = -1;
X
X	/*
X	 * Disable the Conexant device.
X	 *
X	 * This is done * after * attaching the I2C bus so
X	 * cxm_stop_hardware can mute the video and audio
X	 * decoders.
X	 */
X	cxm_stop_hardware(sc);
X
X	/*
X	 * Allocate our interrupt.
X	 */
X	rid = 0;
X	sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
X				0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
X
X	if (sc->irq_res == NULL) {
X		device_printf(dev, "could not map interrupt\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_TTY,
X			       cxm_intr, sc, &sc->ih_cookie);
X	if (error) {
X		device_printf(dev, "could not setup irq\n");
X		goto fail;
X
X	}
X
X	/*
X	 * Allocate a DMA tag for the parent bus.
X	 */
X	error = bus_dma_tag_create(NULL, 1, 0,
X				   BUS_SPACE_MAXADDR_32BIT,
X				   BUS_SPACE_MAXADDR, NULL, NULL,
X				   BUS_SPACE_MAXSIZE_32BIT, 1,
X				   BUS_SPACE_MAXSIZE_32BIT, 0,
X#if __FreeBSD_version >= 501102
X				   busdma_lock_mutex, &Giant,
X#endif
X				   &sc->parent_dmat);
X	if (error) {
X		device_printf(dev, "could not create parent bus DMA tag\n");
X		goto fail;
X	}
X
X	/*
X	 * Allocate a DMA tag for the encoder buffers.
X	 */
X	error = bus_dma_tag_create(sc->parent_dmat, 256, 0,
X				   BUS_SPACE_MAXADDR_32BIT,
X				   BUS_SPACE_MAXADDR, NULL, NULL,
X				   CXM_SG_SEGMENT, 1,
X				   BUS_SPACE_MAXSIZE_32BIT, 0,
X#if __FreeBSD_version >= 501102
X				   busdma_lock_mutex, &Giant,
X#endif
X				   &sc->enc_pool.dmat);
X	if (error) {
X		device_printf(dev,
X			      "could not create encoder buffer DMA tag\n");
X		goto fail;
X	}
X
X	for (i = 0; i < CXM_SG_BUFFERS; i++) {
X
X		/*
X		 * Allocate the encoder buffer.
X		 */
X		error = bus_dmamem_alloc(sc->enc_pool.dmat,
X					 (void **)&sc->enc_pool.bufs[i].vaddr,
X					 BUS_DMA_NOWAIT,
X					 &sc->enc_pool.bufs[i].dmamap);
X		if (error) {
X			device_printf(dev,
X				      "could not allocate encoder buffer\n");
X			goto fail;
X		}
X
X		/*
X		 * Map the encoder buffer.
X		 */
X		error = bus_dmamap_load(sc->enc_pool.dmat,
X					sc->enc_pool.bufs[i].dmamap,
X					sc->enc_pool.bufs[i].vaddr,
X					CXM_SG_SEGMENT,
X					cxm_mapmem,
X					&sc->enc_pool.bufs[i].baddr, 0);
X		if (error) {
X			device_printf(dev, "could not map encoder buffer\n");
X			goto fail;
X		}
X	}
X
X	/*
X	 * Allocate a DMA tag for the scatter / gather list.
X	 */
X	error = bus_dma_tag_create(sc->parent_dmat, 1, 0,
X				   BUS_SPACE_MAXADDR_32BIT,
X				   BUS_SPACE_MAXADDR, NULL, NULL,
X				   CXM_SG_BUFFERS
X				   * sizeof(struct cxm_sg_entry), 1,
X				   BUS_SPACE_MAXSIZE_32BIT, 0,
X#if __FreeBSD_version >= 501102
X				   busdma_lock_mutex, &Giant,
X#endif
X				   &sc->enc_sg.dmat);
X	if (error) {
X		device_printf(dev,
X			      "could not create scatter / gather DMA tag\n");
X		goto fail;
X	}
X
X	/*
X	 * Allocate the scatter / gather list.
X	 */
X	error = bus_dmamem_alloc(sc->enc_sg.dmat, (void **)&sc->enc_sg.vaddr,
X				 BUS_DMA_NOWAIT, &sc->enc_sg.dmamap);
X	if (error) {
X		device_printf(dev,
X			      "could not allocate scatter / gather list\n");
X		goto fail;
X	}
X
X	/*
X	 * Map the scatter / gather list.
X	 */
X	error = bus_dmamap_load(sc->enc_sg.dmat, sc->enc_sg.dmamap,
X				sc->enc_sg.vaddr,
X				CXM_SG_BUFFERS * sizeof(struct cxm_sg_entry),
X				cxm_mapmem, &sc->enc_sg.baddr, 0);
X	if (error) {
X		device_printf(dev, "could not map scatter / gather list\n");
X		goto fail;
X	}
X
X	/*
X	 * Initialize the hardware.
X	 */
X	if (cxm_init_hardware(sc) < 0) {
X		device_printf(dev, "could not initialize hardware\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	sc->profile = &dvd_full_d1_ntsc_profile;
X
X	sc->source = cxm_tuner_source;
X
X	/* make the device entries */
X	sc->cxm_dev = make_dev(&cxm_cdevsw, unit,    
X				0, 0, 0444, "cxm%d",  unit);
X
X	return 0;
X
Xfail:
X	if (sc->enc_sg.baddr)
X		bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
X	if (sc->enc_sg.vaddr)
X		bus_dmamem_free(sc->enc_sg.dmat, sc->enc_sg.vaddr,
X				sc->enc_sg.dmamap);
X	if (sc->enc_sg.dmat)
X		bus_dma_tag_destroy(sc->enc_sg.dmat);
X
X	for (i = 0; i < CXM_SG_BUFFERS; i++) {
X		if (sc->enc_pool.bufs[i].baddr)
X			bus_dmamap_unload(sc->enc_pool.dmat,
X					  sc->enc_pool.bufs[i].dmamap);
X		if (sc->enc_pool.bufs[i].vaddr)
X			bus_dmamem_free(sc->enc_pool.dmat,
X					sc->enc_pool.bufs[i].vaddr,
X					sc->enc_pool.bufs[i].dmamap);
X	}
X
X	if (sc->enc_pool.dmat)
X		bus_dma_tag_destroy(sc->enc_pool.dmat);
X
X	if (sc->parent_dmat)
X		bus_dma_tag_destroy(sc->parent_dmat);
X
X	/*
X	 * Detach the I2C bus.
X	 *
X	 * This is done * after * deallocating the scatter / gather
X	 * list and buffers so the kernel has a better chance of
X	 * gracefully handling a memory shortage.
X	 *
X	 * Detach the children before recursively deleting
X	 * in case a child has a pointer to a grandchild
X	 * which is used by the child's detach routine.
X	 */
X	bus_generic_detach(dev);
X	if (sc->cxm_iic)
X		device_delete_child(dev, sc->cxm_iic);
X
X	if (sc->ih_cookie)
X		bus_teardown_intr(dev, sc->irq_res, sc->ih_cookie);
X	if (sc->irq_res)
X		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
X	if (sc->mem_res)
X		bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
X
X	return error;
X}
X
X/*
X * the detach routine.
X */
Xstatic int
Xcxm_detach( device_t dev )
X{
X	unsigned int i;
X	struct cxm_softc *sc;
X	device_t child;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X
X	/* Disable the Conexant device. */
X	cxm_stop_hardware(sc);
X
X	/* Unregister the /dev/cxmN device. */
X	destroy_dev(sc->cxm_dev);
X
X	/*
X	 * Deallocate scatter / gather list and buffers.
X	 */
X	bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
X	bus_dmamem_free(sc->enc_sg.dmat, sc->enc_sg.vaddr, sc->enc_sg.dmamap);
X
X	bus_dma_tag_destroy(sc->enc_sg.dmat);
X
X	for (i = 0; i < CXM_SG_BUFFERS; i++) {
X		bus_dmamap_unload(sc->enc_pool.dmat,
X				  sc->enc_pool.bufs[i].dmamap);
X		bus_dmamem_free(sc->enc_pool.dmat, sc->enc_pool.bufs[i].vaddr,
X				sc->enc_pool.bufs[i].dmamap);
X	}
X
X	bus_dma_tag_destroy(sc->enc_pool.dmat);
X
X	bus_dma_tag_destroy(sc->parent_dmat);
X
X	/*
X	 * Detach the I2C bus.
X	 *
X	 * This is done * after * deallocating the scatter / gather
X	 * list and buffers so the kernel has a better chance of
X	 * gracefully handling a memory shortage.
X	 *
X	 * Detach the children before recursively deleting
X	 * in case a child has a pointer to a grandchild
X	 * which is used by the child's detach routine.
X	 *
X	 * Remember the child before detaching so we can
X	 * delete it (bus_generic_detach indirectly zeroes
X	 * sc->child_dev).
X	 */
X	child = sc->cxm_iic;
X	bus_generic_detach(dev);
X	if (child)
X		device_delete_child(dev, child);
X
X	/* Deallocate resources. */
X	bus_teardown_intr(dev, sc->irq_res, sc->ih_cookie);
X	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
X	bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
X	 
X	return 0;
X}
X
X/*
X * the shutdown routine.
X */
Xstatic int
Xcxm_shutdown( device_t dev )
X{
X	struct cxm_softc *sc = device_get_softc(dev);
X
X	/* Disable the Conexant device. */
X	cxm_stop_hardware(sc);
X
X	return 0;
X}
X
X/*
X * the interrupt routine.
X */
Xstatic void
Xcxm_intr( void *arg )
X{
X	u_int32_t status;
X	struct cxm_softc *sc;
X
X	/* Get the device data */
X	sc = (struct cxm_softc *)arg;
X
X	status = CSR_READ_4(sc, CXM_REG_IRQ_STATUS);
X
X	status &= ~sc->irq_mask;
X
X	if (! status )
X		return;
X
X	/* Process DMA done before handling a new DMA request or EOS */
X	if (status & CXM_IRQ_ENC_DMA_DONE)
X		cxm_encoder_dma_done(sc);
X
X	if (status & CXM_IRQ_ENC_DMA_REQUEST)
X		cxm_encoder_dma_request(sc);
X
X	if (status & CXM_IRQ_ENC_EOS) {
X		sc->encoding_eos = 1;
X		wakeup(&sc->encoding_eos);
X	}
X
X	cxm_set_irq_status(sc, status);
X}
X
X
X/*
X * the child detached routine.
X */
Xstatic void
Xcxm_child_detached( device_t dev, device_t child )
X{
X	struct cxm_softc *sc;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X
X	if (child == sc->cxm_iic)
X		sc->cxm_iic = NULL;
X}
X
X
Xstatic int
Xcxm_read_ivar( device_t dev, device_t child, int index, uintptr_t* val)
X{
X	struct cxm_softc *sc;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X
X	switch (index) {
X	case CXM_IVAR_BHANDLE:
X		*(bus_space_handle_t **)val = &sc->bhandle;
X		break;
X
X	case CXM_IVAR_BTAG:
X		*(bus_space_tag_t **)val = &sc->btag;
X		break;
X
X	case CXM_IVAR_IICBUS:
X		*(device_t **)val = &sc->iicbus;
X		break;
X
X	default:
X		return ENOENT;
X	}
X
X	return 0;
X}
X
X
Xstatic int
Xcxm_write_ivar( device_t dev, device_t child, int index, uintptr_t val)
X{
X	struct cxm_softc *sc;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X
X	switch (index) {
X	case CXM_IVAR_BHANDLE:
X		return EINVAL;
X
X	case CXM_IVAR_BTAG:
X		return EINVAL;
X
X	case CXM_IVAR_IICBUS:
X		if (sc->iicbus)
X			return EINVAL;
X		sc->iicbus = val ? *(device_t *)val : NULL;
X		break;
X
X	default:
X		return ENOENT;
X	}
X
X	return 0;
X}
X
X
X/*---------------------------------------------------------
X**
X**	Conexant iTVC15 / iTVC16 character device driver routines
X**
X**---------------------------------------------------------
X*/
X
X#define UNIT(x)		((x) & 0x0f)
X#define FUNCTION(x)	(x >> 4)
X
X/*
X * 
X */
Xint
Xcxm_open( dev_t dev, int flags, int fmt, d_thread_t *td )
X{
X	int		unit;
X	struct cxm_softc *sc;
X
X	unit = UNIT( minor(dev) );
X
X	/* Get the device data */
X	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
X	if (sc == NULL) {
X		/* the device is no longer valid/functioning */
X		return ENXIO;
X	}
X
X	if (sc->is_opened)
X		return EBUSY;
X
X	sc->is_opened = 1;
X	sc->mpeg = 1;
X
X	/* Record that the device is now busy */
X	device_busy(devclass_get_device(cxm_devclass, unit)); 
X
X	return 0;
X}
X
X
X/*
X * 
X */
Xint
Xcxm_close( dev_t dev, int flags, int fmt, d_thread_t *td )
X{
X	int		unit;
X	struct cxm_softc *sc;
X
X	unit = UNIT( minor(dev) );
X
X	/* Get the device data */
X	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
X	if (sc == NULL) {
X		/* the device is no longer valid/functioning */
X		return ENXIO;
X	}
X
X	if (cxm_stop_encoder(sc) < 0)
X		return ENXIO;
X
X	sc->enc_pool.offset = 0;
X	sc->enc_pool.read = 0;
X	sc->enc_pool.write = 0;
X
X	sc->enc_proc = NULL;
X	sc->enc_signal = 0;
X
X	device_unbusy(devclass_get_device(cxm_devclass, unit)); 
X
X	sc->is_opened = 0;
X
X	return 0;
X}
X
X
X/*
X * 
X */
Xint
Xcxm_read( dev_t dev, struct uio *uio, int flag )
X{
X	int		buffers_available;
X	int		buffers_read;
X	int		error;
X	int		unit;
X	unsigned int	current;
X	unsigned int	i;
X	size_t		nbytes;
X	size_t		offset;
X	struct cxm_softc *sc;
X	intrmask_t	s;
X
X	unit = UNIT( minor(dev) );
X
X	/* Get the device data */
X	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
X	if (sc == NULL) {
X		/* the device is no longer valid/functioning */
X		return ENXIO;
X	}
X
X	/* Only trigger the encoder if the ring buffer is empty */
X	if (! sc->encoding && sc->enc_pool.read == sc->enc_pool.write) {
X		if (cxm_start_encoder(sc) < 0)
X			return ENXIO;
X		if (flag & IO_NDELAY)
X			return EWOULDBLOCK;
X	}
X
X	buffers_available = 0;
X
X	s = spltty();
X	while (sc->enc_pool.read == sc->enc_pool.write) {
X		error = tsleep(&sc->enc_pool.read, PZERO | PCATCH, "cmxrd", 0);
X		if (error) {
X			splx(s);
X			return error;
X		}
X	}
X
X	/*
X	 * Determine the number of buffers available at this * instant *
X	 * taking in consideration that the ring buffer wraps.
X	 */
X	buffers_available = sc->enc_pool.write - sc->enc_pool.read;
X	if (buffers_available < 0)
X		buffers_available += CXM_SG_BUFFERS;
X	splx(s);
X
X	offset = sc->enc_pool.offset;
X
X	for (buffers_read = 0, i = sc->enc_pool.read;
X	     buffers_read != buffers_available && uio->uio_resid;
X	     buffers_read++, i = (i + 1) % CXM_SG_BUFFERS) {
X
X		current = cxm_encoder_fixup_byte_order (sc, i, offset);
X
X		nbytes = sc->enc_pool.bufs[current].size - offset;
X
X		/* Don't transfer more than requested */
X		if (nbytes > uio->uio_resid)
X			nbytes = uio->uio_resid;
X
X		error = uiomove(sc->enc_pool.bufs[current].vaddr + offset,
X				nbytes, uio);
X		if (error)
X			return error;
X
X		offset += nbytes;
X
X		/* Handle a partial read of a buffer */
X		if (! uio->uio_resid && offset != sc->enc_pool.bufs[i].size)
X			break;
X
X		offset = 0;
X	}
X
X	sc->enc_pool.offset = offset;
X
X	/* Update the books (spl is used since mutex is not available) */
X	s = spltty();
X	sc->enc_pool.read = (sc->enc_pool.read + buffers_read)
X			    % CXM_SG_BUFFERS;
X	splx(s);
X
X	return 0;
X}
X
X
X/*
X * 
X */
Xint
Xcxm_ioctl( dev_t dev, unsigned long cmd, caddr_t arg,
X	   int flag, d_thread_t *td )
X{
X	int		brightness;
X	int		chroma_saturation;
X	int		contrast;
X	int		fps;
X	int		hue;
X	int		result;
X	int		status;
X	int		unit;
X	unsigned int	i;
X	unsigned int	sig;
X	unsigned long	freq;
X	struct cxm_softc *sc;
X	enum cxm_source	source;
X	struct bktr_capture_area *cap;
X	struct bktr_remote *remote;
X	intrmask_t	s;
X
X	unit = UNIT( minor(dev) );
X
X	/* Get the device data */
X	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
X	if (sc == NULL) {
X		/* the device is no longer valid/functioning */
X		return ENXIO;
X	}
X
X	switch (cmd) {
X	case BT848_GAUDIO:
X		switch (cxm_msp_selected_source(sc)) {
X		case cxm_tuner_source:
X			*(int *) arg = AUDIO_TUNER;
X			break;
X
X		case cxm_line_in_source_composite:
X		case cxm_line_in_source_svideo:
X			*(int *) arg = AUDIO_EXTERN;
X			break;
X
X		case cxm_fm_source:
X			*(int *) arg = AUDIO_INTERN;
X			break;
X
X		default:
X			return ENXIO;
X		}
X
X		if (cxm_msp_is_muted(sc) == 1)
X			*(int *) arg |= AUDIO_MUTE;
X		break;
X
X	case BT848_SAUDIO:
X		source = cxm_unknown_source;
X
X		switch (*(int *) arg) {
X		case AUDIO_TUNER:
X			source = cxm_tuner_source;
X			break;
X
X		case AUDIO_EXTERN:
X			source = cxm_line_in_source_composite;
X			break;
X
X		case AUDIO_INTERN:
X			source = cxm_fm_source;
X			break;
X
X		case AUDIO_MUTE:
X			if (cxm_msp_mute(sc) < 0)
X				return ENXIO;
X			return 0;
X
X		case AUDIO_UNMUTE:
X			if (cxm_msp_unmute(sc) < 0)
X				return ENXIO;
X			return 0;
X
X		default:
X			return EINVAL;
X		}
X
X		if (sc->encoding) {
X
X			/*
X			 * Switching between audio + video and audio only
X			 * subtypes isn't supported while encoding.
X			 */
X
X			if (source != sc->source
X			    && (source == cxm_fm_source
X				|| sc->source == cxm_fm_source))
X				return EBUSY;
X		}
X
X		if (cxm_pause_encoder(sc) < 0)
X			return ENXIO;
X
X		if (cxm_msp_select_source(sc, source) < 0)
X			return ENXIO;
X
X		if (source == cxm_fm_source)
X			sc->source = source;
X
X		result = cxm_encoder_wait_for_lock(sc);
X		if (result < 0)
X			return ENXIO;
X		else if (result == 0)
X			return EINVAL;
X
X		if (cxm_unpause_encoder(sc) < 0)
X			return ENXIO;
X		break;
X
X	case BT848_GBRIG:
X		brightness = cxm_saa7115_get_brightness(sc);
X
X		if (brightness < 0)
X			return ENXIO;
X
X		/*
X		 * Brooktree brightness:
X		 * 0x80 = -50.0%, 0x00 = +0.0%, 0x7f = +49.6%
X		 */
X		*(int *)arg = (int)(unsigned char)brightness - 128;
X		break;
X
X	case BT848_SBRIG:
X
X		/*
X		 * Brooktree brightness:
X		 * 0x80 = -50.0%, 0x00 = +0.0%, 0x7f = +49.6%
X		 */
X		brightness = *(int *)arg + 128;
X
X		if (cxm_saa7115_set_brightness(sc, brightness) < 0)
X			return ENXIO;
X		break;
X
X	case METEORGBRIG:
X		brightness = cxm_saa7115_get_brightness(sc);
X
X		if (brightness < 0)
X			return ENXIO;
X
X		*(unsigned char *)arg = (unsigned char)brightness;
X		break;
X
X	case METEORSBRIG:
X		brightness = *(unsigned char *)arg;
X
X		if (cxm_saa7115_set_brightness(sc, brightness) < 0)
X			return ENXIO;
X		break;
X
X	case BT848_GCSAT:
X		chroma_saturation = cxm_saa7115_get_chroma_saturation(sc);
X
X		if (chroma_saturation < 0)
X			return ENXIO;
X
X		/*
X		 * Brooktree chroma saturation:
X		 * 0x000 = 0%, 0x0fe = 100%, 0x1ff = 201.18%
X		 */
X		*(int *)arg = ((signed char)chroma_saturation > 0)
X				? (chroma_saturation * 4 - 2) : 0;
X		break;
X
X	case BT848_SCSAT:
X
X		/*
X		 * Brooktree chroma saturation:
X		 * 0x000 = 0%, 0x0fe = 100%, 0x1ff = 201.18%
X		 */
X		chroma_saturation = (*(int *)arg & 0x1ff) < 510
X				      ? ((*(int *)arg & 0x1ff) + 2) / 4 : 127;
X
X		if (cxm_saa7115_set_chroma_saturation(sc, chroma_saturation)
X		    < 0)
X			return ENXIO;
X
X		break;
X
X	case METEORGCSAT:
X		chroma_saturation = cxm_saa7115_get_chroma_saturation(sc);
X
X		if (chroma_saturation < 0)
X			return ENXIO;
X
X		*(unsigned char *)arg = (unsigned char)chroma_saturation;
X		break;
X
X	case METEORSCSAT:
X		chroma_saturation = *(unsigned char *)arg;
X
X		if (cxm_saa7115_set_chroma_saturation(sc, chroma_saturation)
X		    < 0)
X			return ENXIO;
X		break;
X
X	case METEORGCONT:
X		contrast = cxm_saa7115_get_contrast(sc);
X
X		if (contrast < 0)
X			return ENXIO;
X
X		*(unsigned char *)arg = (unsigned char)contrast;
X		break;
X
X	case METEORSCONT:
X		contrast = *(unsigned char *)arg;
X
X		if (cxm_saa7115_set_contrast(sc, contrast) < 0)
X			return ENXIO;
X		break;
X
X	case BT848_GHUE:
X		hue = cxm_saa7115_get_hue(sc);
X
X		if (hue < 0)
X			return ENXIO;
X
X		*(int *)arg = (signed char)hue;
X		break;
X
X	case BT848_SHUE:
X		hue = *(int *)arg;
X
X		if (cxm_saa7115_set_hue(sc, hue) < 0)
X			return ENXIO;
X		break;
X
X	case METEORGHUE:
X		hue = cxm_saa7115_get_hue(sc);
X
X		if (hue < 0)
X			return ENXIO;
X
X		*(signed char *)arg = (signed char)hue;
X		break;
X
X	case METEORSHUE:
X		hue = *(signed char *)arg;
X
X		if (cxm_saa7115_set_hue(sc, hue) < 0)
X			return ENXIO;
X		break;
X
X	case METEORCAPTUR:
X		switch (*(int *) arg) {
X		case METEOR_CAP_CONTINOUS:
X			if (cxm_start_encoder(sc) < 0)
X				return ENXIO;
X			break;
X
X		case METEOR_CAP_STOP_CONT:
X			if (cxm_stop_encoder(sc) < 0)
X				return ENXIO;
X			break;
X
X		default:
X			return EINVAL;
X		}
X		break;
X
X	case BT848_GCAPAREA:
X		cap = (struct bktr_capture_area *)arg;
X		memset (cap, 0, sizeof (*cap));
X		cap->x_offset = 0;
X		cap->y_offset = 0;
X		cap->x_size = sc->profile->width;
X		cap->y_size = sc->profile->height;
X		break;
X
X	case BT848_SCAPAREA:
X		if (sc->encoding)
X			return EBUSY;
X
X		cap = (struct bktr_capture_area *)arg;
X		if (cap->x_offset || cap->y_offset
X		    || (cap->x_size % CXM_MACROBLOCK_WIDTH)
X		    || (cap->y_size % CXM_MACROBLOCK_HEIGHT))
X			return EINVAL;
X
X		/*
X		 * Setting the width and height has the side effect of
X		 * chosing between the VCD, SVCD, and DVD profiles.
X		 */
X
X		for (i = 0; i < NUM_ELEMENTS(codec_profiles); i++)
X			if (codec_profiles[i]->width == cap->x_size
X			    && codec_profiles[i]->height == cap->y_size)
X				break;
X
X		if (i >= NUM_ELEMENTS(codec_profiles))
X			return EINVAL;
X
X		sc->profile = codec_profiles[i];
X		break;
X
X	case BT848GFMT:
X		switch (cxm_saa7115_detected_format(sc)) {
X		case cxm_ntsc_60hz_source_format:
X			*(unsigned long *)arg = BT848_IFORM_F_NTSCM;
X			break;
X
X		case cxm_pal_50hz_source_format:
X			*(unsigned long *)arg = BT848_IFORM_F_PALBDGHI;
X			break;
X
X		case cxm_secam_50hz_source_format:
X			*(unsigned long *)arg = BT848_IFORM_F_SECAM;
X			break;
X
X		case cxm_pal_60hz_source_format:
X			*(unsigned long *)arg = BT848_IFORM_F_PALM;
X			break;
X
X		case cxm_bw_50hz_source_format:
X		case cxm_bw_60hz_source_format:
X		case cxm_ntsc_50hz_source_format:
X			*(unsigned long *)arg = BT848_IFORM_F_AUTO;
X			break;
X
X		default:
X			return ENXIO;
X		}
X		break;
X
X	case METEORGFMT:
X		switch (cxm_saa7115_detected_format(sc)) {
X		case cxm_ntsc_60hz_source_format:
X			*(unsigned long *)arg = METEOR_FMT_NTSC;
X			break;
X
X		case cxm_pal_50hz_source_format:
X			*(unsigned long *)arg = METEOR_FMT_PAL;
X			break;
X
X		case cxm_secam_50hz_source_format:
X			*(unsigned long *)arg = METEOR_FMT_SECAM;
X			break;
X
X		case cxm_bw_50hz_source_format:
X		case cxm_bw_60hz_source_format:
X		case cxm_ntsc_50hz_source_format:
X		case cxm_pal_60hz_source_format:
X			*(unsigned long *)arg = METEOR_FMT_AUTOMODE;
X			break;
X
X		default:
X			return ENXIO;
X		}
X		break;
X
X	case METEORGFPS:
X		fps = cxm_saa7115_detected_fps(sc);
X
X		if (fps < 0)
X			return ENXIO;
X
X		*(unsigned short *)arg = fps;
X		break;
X
X	case METEORGINPUT:
X		switch (sc->source) {
X		case cxm_tuner_source:
X			*(unsigned long *)arg = METEOR_INPUT_DEV1;
X			break;
X
X		case cxm_line_in_source_composite:
X			*(unsigned long *)arg = METEOR_INPUT_DEV2;
X			break;
X
X		case cxm_line_in_source_svideo:
X			*(unsigned long *)arg = METEOR_INPUT_DEV_SVIDEO;
X			break;
X
X		default:
X			return ENXIO;
X		}
X		break;
X
X	case METEORSINPUT:
X		source = cxm_unknown_source;
X
X		switch (*(unsigned long *)arg & 0xf000) {
X		case METEOR_INPUT_DEV1:
X			source = cxm_tuner_source;
X			break;
X
X		case METEOR_INPUT_DEV2:
X			source = cxm_line_in_source_composite;
X			break;
X
X		case METEOR_INPUT_DEV_SVIDEO:
X			source = cxm_line_in_source_svideo;
X			break;
X
X		default:
X			 return EINVAL;
X		}
X
X		if (sc->encoding) {
X
X			/*
X			 * Switching between audio + video and audio only
X			 * subtypes isn't supported while encoding.
X			 */
X
X			if (source != sc->source
X			    && (source == cxm_fm_source
X				|| sc->source == cxm_fm_source))
X				return EBUSY;
X		}
X
X		if (cxm_pause_encoder(sc) < 0)
X			return ENXIO;
X
X		if (cxm_saa7115_select_source(sc, source) < 0)
X			return ENXIO;
X		if (cxm_msp_select_source(sc, source) < 0)
X			return ENXIO;
X
X		sc->source = source;
X
X		result = cxm_encoder_wait_for_lock(sc);
X		if (result < 0)
X			return ENXIO;
X		else if (result == 0)
X			return EINVAL;
X
X		if (cxm_unpause_encoder(sc) < 0)
X			return ENXIO;
X		break;
X
X	case METEORGSIGNAL:
X		*(unsigned int *)arg = sc->enc_signal;
X		break;
X
X	case METEORSSIGNAL:
X		sig = *(unsigned int *)arg;
X
X		if (! _SIG_VALID(sig) )
X			return EINVAL;
X
X		/*
X		 * Historically, applications used METEOR_SIG_MODE_MASK
X		 * to reset signal delivery.
X		 */
X		if (sig == METEOR_SIG_MODE_MASK)
X			sig = 0;
X
X		s = spltty();
X#if __FreeBSD_version < 500000
X		sc->enc_proc = sig ? td : NULL;
X#else
X		sc->enc_proc = sig ? td->td_proc : NULL;
X#endif
X		sc->enc_signal = sig;
X		splx (s);
X		break;
X
X	case RADIO_GETFREQ:
X		/* Convert from kHz to MHz * 100 */
X		freq = sc->tuner_freq / 10;
X
X		*(unsigned int *)arg = freq;
X		break;
X
X	case RADIO_SETFREQ:
X		if (sc->source == cxm_fm_source)
X			if (cxm_pause_encoder(sc) < 0)
X				return ENXIO;
X
X		/* Convert from MHz * 100 to kHz */
X		freq = *(unsigned int *)arg * 10;
X
X		if (cxm_tuner_select_frequency(sc, cxm_tuner_fm_freq_type,
X					       freq) < 0)
X			return ENXIO;
X
X		/*
X		 * Explicitly wait for the tuner lock so we
X		 * can indicate if there's a station present.
X		 */
X		if (cxm_tuner_wait_for_lock(sc) < 0)
X			return EINVAL;
X
X		result = cxm_encoder_wait_for_lock(sc);
X		if (result < 0)
X			return ENXIO;
X		else if (result == 0)
X			return EINVAL;
X
X		if (sc->source == cxm_fm_source)
X			if (cxm_unpause_encoder(sc) < 0)
X				return ENXIO;
X		break;
X
X	case TVTUNER_GETAFC:
X		*(int *)arg = sc->tuner_afc;
X		break;
X
X	case TVTUNER_SETAFC:
X		sc->tuner_afc = (*(int *)arg != 0);
X		break;
X
X	case TVTUNER_GETTYPE:
X		*(unsigned int *)arg = cxm_tuner_selected_channel_set(sc);
X		break;
X
X	case TVTUNER_SETTYPE:
X		if (cxm_tuner_select_channel_set(sc, *(unsigned int *)arg) < 0)
X			return EINVAL;
X		break;
X
X	case TVTUNER_SETCHNL:
X		if (sc->source == cxm_tuner_source)
X			if (cxm_pause_encoder(sc) < 0)
X				return ENXIO;
X
X		if (cxm_tuner_select_channel(sc, *(unsigned int *)arg) < 0)
X			return ENXIO;
X
X		if (sc->tuner_afc)
X			if (cxm_tuner_apply_afc(sc) < 0)
X				return EINVAL;
X
X		/*
X		 * Explicitly wait for the tuner lock so we
X		 * can indicate if there's a station present.
X		 */
X		if (cxm_tuner_wait_for_lock(sc) < 0)
X			return EINVAL;
X
X		result = cxm_encoder_wait_for_lock(sc);
X		if (result < 0)
X			return ENXIO;
X		else if (result == 0)
X			return EINVAL;
X
X		if (sc->source == cxm_tuner_source)
X			if (cxm_unpause_encoder(sc) < 0)
X				return ENXIO;
X		break;
X
X	case TVTUNER_GETFREQ:
X		/* Convert from kHz to MHz * 16 */
X		freq = (sc->tuner_freq * 16) / 1000;
X
X		*(unsigned int *)arg = freq;
X		break;
X
X	case TVTUNER_SETFREQ:
X		if (sc->source == cxm_tuner_source)
X			if (cxm_pause_encoder(sc) < 0)
X				return ENXIO;
X
X		/* Convert from MHz * 16 to kHz */
X		freq = (*(unsigned int *)arg * 1000) / 16;
X
X		if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
X					       freq) < 0)
X			return ENXIO;
X
X		/*
X		 * Explicitly wait for the tuner lock so we
X		 * can indicate if there's a station present.
X		 */
X		if (cxm_tuner_wait_for_lock(sc) < 0)
X			return EINVAL;
X
X		result = cxm_encoder_wait_for_lock(sc);
X		if (result < 0)
X			return ENXIO;
X		else if (result == 0)
X			return EINVAL;
X
X		if (sc->source == cxm_tuner_source)
X			if (cxm_unpause_encoder(sc) < 0)
X				return ENXIO;
X		break;
X
X	case TVTUNER_GETSTATUS:
X		status = cxm_tuner_status(sc);
X		if (status < 0)
X			return ENXIO;
X		*(unsigned long *)arg = status & 0xff;
X		break;
X
X	case REMOTE_GETKEY:
X		remote = (struct bktr_remote *)arg;
X		if (cxm_ir_key(sc, (char *)remote, sizeof(*remote)) < 0)
X			return ENXIO;
X		break;
X
X	default:
X		return ENOTTY;
X	}
X
X	return 0;
X}
X
X
Xint cxm_poll( dev_t dev, int events, d_thread_t *td)
X{
X	int		revents;
X	int		unit;
X	struct cxm_softc *sc;
X	intrmask_t	s;
X
X	unit = UNIT( minor(dev) );
X
X	/* Get the device data */
X	sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
X	if (sc == NULL) {
X		/* the device is no longer valid/functioning */
X		return POLLHUP;
X	}
X
X	revents = 0;
X
X	s = spltty();
X	if (events & (POLLIN | POLLRDNORM)) {
X		if (sc->enc_pool.read == sc->enc_pool.write)
X			selrecord(td, &sc->enc_sel);
X		else
X			revents = events & (POLLIN | POLLRDNORM);
X	}
X	splx(s);
X
X	return revents;
X}
END-of-dev/cxm/cxm.c
echo x - dev/cxm/cxm.h
sed 's/^X//' >dev/cxm/cxm.h << 'END-of-dev/cxm/cxm.h'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Header file for the Conexant MPEG-2 Codec driver.
X */
X
X#if __FreeBSD_version >= 503001
X#  define dev_t struct cdev *
X#endif
X
X#define bswap32(X) ntohl(X)
X
X#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(*array))
X
X/*
X * For simplicity several large buffers allocate during
X * driver attachment which normally occurs early on
X * (when large areas of memory are available) are used
X * to move data to / from the card.  It's not unusual
X * for the memory allocation to fail due to fragmentation
X * if the driver is loaded after the system has been
X * running for a while.  One solution is to allocate
X * several PAGE_SIZE buffers instead, however it doesn't
X * seem worth the trouble.
X */
Xenum cxm_byte_order { cxm_unknown_byte_order,
X		      cxm_device_mpeg_byte_order, cxm_device_yuv12_byte_order,
X		      cxm_host_byte_order };
X
Xstruct cxm_buffer {
X	char		*vaddr;
X	bus_addr_t	baddr;
X	bus_dmamap_t	dmamap;
X	size_t		size;
X	enum cxm_byte_order byte_order;
X};
X
X#define CXM_SG_BUFFERS 50
X
Xstruct cxm_buffer_pool {
X	bus_dma_tag_t		dmat;
X	size_t			offset;
X	unsigned int		read;
X	volatile unsigned int	write;
X	struct cxm_buffer	bufs[CXM_SG_BUFFERS];
X};
X
X/*
X * Audio format encoding
X *
X * 7 6 5 4 3 2 1 0
X *
X *             0 0  44.1 kHz
X *             0 1  48 kHz
X *             1 0  32 kHz
X *
X *         0 1  Layer 1
X *         1 0  Layer 2
X *         1 1  Layer 3
X *
X *          L1 / L2
X * 0 0 0 0  Free fmt
X * 0 0 0 1  32k / 32k
X * 0 0 1 0  64k / 48k
X * 0 0 1 1  96k / 56k
X * 0 1 0 0  128k / 64k
X * 0 1 0 1  160k / 80k
X * 0 1 1 0  192k / 96k
X * 0 1 1 1  224k / 112k
X * 1 0 0 0  256k / 128k
X * 1 0 0 1  288k / 160k
X * 1 0 1 0  320k / 192k
X * 1 0 1 1  352k / 224k
X * 1 1 0 0  384k / 256k
X * 1 1 0 1  416k / 320k
X * 1 1 1 0  448k / 384k
X */
Xstruct cxm_codec_audio_format {
X	unsigned int	sample_rate;
X	u_int32_t	format;
X};
X
Xstruct cxm_codec_profile {
X	const char	*name;
X	u_int32_t	stream_type;
X	u_int32_t	fps;
X	u_int32_t	width;
X	u_int32_t	height;
X	u_int32_t	source_height;
X	struct {
X		u_int32_t	start;
X		u_int32_t	nlines;
X		u_int32_t	cc;
X	} vbi;
X	u_int32_t	aspect;
X	u_int32_t	pulldown;
X	struct {
X		u_int32_t	mode;
X		u_int32_t	average;
X		u_int32_t	peak;
X	} bitrate;
X	struct {
X		u_int32_t	closure;
X		u_int32_t	frames;
X		u_int32_t	bframes;
X	} gop;
X	struct {
X		u_int32_t	mode;
X		u_int32_t	type;
X		u_int32_t	spatial;
X		u_int32_t	temporal;
X	} dnr;
X
X	unsigned int	audio_sample_rate;
X};
X
X#define CXM_VENDORID_CONEXANT        0x4444
X#define CXM_DEVICEID_CONEXANT_iTVC15 0x0803
X#define CXM_DEVICEID_CONEXANT_iTVC16 0x0016
X
X#define CXM_VENDORID_HAUPPAUGE       0x0070
X
Xstruct cxm_dev {
X	u_int16_t	vid;
X	u_int16_t	did;
X	char		*name;
X};
X
X#define CXM_MBX_FW_CMD_MAILBOX   0
X#define CXM_MBX_FW_CMD_MAILBOXES 6
X
X#define CXM_MBX_FW_DMA_MAILBOX   9
X
X#define CXM_MBX_MAX_PARAMETERS   16
X
X/* Mailbox flags bit definitions */
X#define CXM_MBX_FLAG_DRV_DONE 0x00000002
X#define CXM_MBX_FLAG_FW_DONE  0x00000004
X#define CXM_MBX_FLAG_IN_USE   0x00000001
X
Xstruct cxm_mailbox {
X	u_int32_t	flags;
X	u_int32_t	command;
X	u_int32_t	result;
X	u_int32_t	timeout;
X	u_int32_t	parameters[CXM_MBX_MAX_PARAMETERS];
X} __attribute__ ((packed));
X
Xenum cxm_mailbox_name { cxm_unknown_mailbox,
X			cxm_dec_mailbox, cxm_enc_mailbox };
X
X/*
X * Scatter / gather is supported with the restriction
X * that the size of each piece must be a multiple of
X * 256 and less than 64k.
X */
X#define CXM_SG_SEGMENT  (0xff00 & ~(PAGE_SIZE - 1))
X
Xstruct cxm_sg_entry {
X	u_int32_t	src;
X	u_int32_t	dst;
X	u_int32_t	size;
X} __attribute__ ((packed));
X
Xstruct cxm_sg_list {
X	bus_dma_tag_t	dmat;
X	struct cxm_sg_entry *vaddr;
X	bus_addr_t	baddr;
X	bus_dmamap_t	dmamap;
X
X};
X
Xenum cxm_source { cxm_unknown_source, cxm_fm_source, cxm_tuner_source,
X		  cxm_line_in_source_composite, cxm_line_in_source_svideo };
X
Xenum cxm_source_format { cxm_unknown_source_format,
X			 cxm_bw_50hz_source_format,
X			 cxm_bw_60hz_source_format,
X			 cxm_ntsc_50hz_source_format,
X			 cxm_ntsc_60hz_source_format,
X			 cxm_pal_50hz_source_format,
X			 cxm_pal_60hz_source_format,
X			 cxm_secam_50hz_source_format };
X
Xextern const struct cxm_tuner cxm_tuners[];
X
Xenum cxm_type { cxm_unknown_type, cxm_iTVC15_type, cxm_iTVC16_type };
X
X/*
X * Conexant iTVC15 / iTVC16 info structure, one per card installed.
X */
Xstruct cxm_softc {
X	enum cxm_type	type;
X	char		name[10];	/* Device name and unit number */
X
X	struct resource	*mem_res;	/* Resource descriptor for registers */
X	bus_space_tag_t	btag;		/* Bus space access functions */
X	bus_space_handle_t bhandle;	/* Bus space access functions */
X
X	struct resource *irq_res;	/* Resource descriptor for interrupt */
X	void            *ih_cookie;	/* Newbus interrupt handler cookie */
X
X	u_int32_t	irq_mask;
X
X	bus_dma_tag_t	parent_dmat;
X
X	struct cxm_buffer_pool	enc_pool;
X	struct cxm_sg_list enc_sg;
X
X	struct selinfo	enc_sel;
X
X	struct proc	*enc_proc;
X	int		enc_signal;
X
X	unsigned int	dec_mbx;
X	unsigned int	enc_mbx;
X
X	device_t	cxm_iic;
X	device_t	iicbus;
X
X	const struct cxm_tuner *tuner;
X	const struct cxm_tuner_channels *tuner_channels;
X	int		tuner_afc;
X	unsigned long	tuner_freq;
X
X	char		msp_name[10];
X
X	const struct cxm_codec_profile *profile;
X
X	enum cxm_source	source;
X
X	dev_t		cxm_dev;	/* Device entry for /dev/cxmN */
X
X	int		is_opened;
X	int		mpeg;
X
X	int		encoding;
X	int		encoding_dma;
X	int		encoding_eos;
X};
X
X/*
X * Conexant iTVC15 / iTVC16 I2C info structure, one per card installed.
X */
Xstruct cxm_iic_softc {
X	char		name[10];	/* Device name and unit number */
X	bus_space_tag_t	btag;		/* Bus space access functions */
X	bus_space_handle_t bhandle;	/* Bus space access functions */
X
X	device_t	iicbb;
X
X};
X
X/*
X * List of IVARS available to the I2C device driver
X */
X#define CXM_IVAR_BHANDLE 0
X#define CXM_IVAR_BTAG    1
X#define CXM_IVAR_IICBUS  2
X
X/*
X * Bus resource id
X */
X#if __FreeBSD_version >= 503001
X#  define CXM_RID PCIR_BAR(0)
X#else
X#  define CXM_RID PCIR_MAPS
X#endif
X
X/*
X * Access macros
X */
X#define CSR_WRITE_4(sc, reg, val)       \
X	bus_space_write_4((sc)->btag, (sc)->bhandle, (reg), (val))
X#define CSR_WRITE_2(sc, reg, val)       \
X	bus_space_write_2((sc)->btag, (sc)->bhandle, (reg), val))
X#define CSR_WRITE_1(sc, reg, val)       \
X	bus_space_write_1((sc)->btag, (sc)->bhandle, (reg), val))
X#define CSR_READ_4(sc, reg)             \
X	bus_space_read_4((sc)->btag, (sc)->bhandle, (reg))
X#define CSR_READ_2(sc, reg)             \
X	bus_space_read_2((sc)->btag, (sc)->bhandle, (reg))
X#define CSR_READ_1(sc, reg)             \
X	bus_space_read_1((sc)->btag, (sc)->bhandle, (reg))
X
X/*
X * Decoder / encoder firmware
X */
Xextern const char cxm_dec_fw[];
Xextern const char cxm_enc_fw[];
X
X#define CXM_FW_SIZE (256 * 1024)
X
X/*
X * Decoder / encoder memory offsets
X */
X#define CXM_MEM_DEC 0x01000000
X#define CXM_MEM_ENC 0x00000000
X
X#define CXM_MEM_DEC_SIZE 0x01000000
X#define CXM_MEM_ENC_SIZE 0x01000000
X
X/*
X * Register offsets
X */
X#define CXM_REG_AO                  0x2002d00
X#define CXM_REG_APU                 0x200a064
X#define CXM_REG_DEC_SDRAM_PRECHARGE 0x20008fc
X#define CXM_REG_DEC_SDRAM_REFRESH   0x20008f8
X#define CXM_REG_DMA_STATUS          0x2000004
X#define CXM_REG_ENC_SDRAM_PRECHARGE 0x20007fc
X#define CXM_REG_ENC_SDRAM_REFRESH   0x20007f8
X#define CXM_REG_HW_BLOCKS           0x2009054
X#define CXM_REG_I2C_GETSCL          0x2007008
X#define CXM_REG_I2C_GETSDA          0x200700c
X#define CXM_REG_I2C_SETSCL          0x2007000
X#define CXM_REG_I2C_SETSDA          0x2007004
X#define CXM_REG_IRQ_MASK            0x2000048
X#define CXM_REG_IRQ_STATUS          0x2000040
X#define CXM_REG_SPU                 0x2009050
X#define CXM_REG_VDM                 0x2002800
X#define CXM_REG_VPU                 0x2009058
X
X/*
X * Register values
X */
X#define CXM_CMD_AO_STOP              0x00000005
X#define CXM_CMD_APU_PING             0x00000000
X#define CXM_CMD_HW_BLOCKS_RST        0xffffffff
X#define CXM_CMD_SDRAM_PRECHARGE_INIT 0x0000001a
X#define CXM_CMD_SDRAM_REFRESH_INIT   0x80000640
X#define CXM_CMD_SPU_STOP             0x00000001
X#define CXM_CMD_VDM_STOP             0x00000000
X#define CXM_CMD_VPU_STOP15           0xfffffffe
X#define CXM_CMD_VPU_STOP16           0xffffffee
X
X#define CXM_DMA_ERROR_LIST           0x00000008
X#define CXM_DMA_ERROR_READ           0x00000002
X#define CXM_DMA_ERROR_WRITE          0x00000004
X#define CXM_DMA_SUCCESS              0x00000001
X
X#define CXM_IRQ_DEC_DMA_DONE         (1 << 20)
X#define CXM_IRQ_DEC_DMA_REQUEST      (1 << 22)
X#define CXM_IRQ_DEC_VSYNC            (1 << 10)
X#define CXM_IRQ_ENC_DMA_DONE         (1 << 27)
X#define CXM_IRQ_ENC_DMA_REQUEST      (1 << 31)
X#define CXM_IRQ_ENC_EOS              (1 << 30)
X#define CXM_IRQ_ENC_EVENT            (1 << 28)
X
X#define CXM_IRQ_ENC (CXM_IRQ_ENC_DMA_REQUEST | CXM_IRQ_ENC_DMA_DONE \
X		     | CXM_IRQ_ENC_EOS | CXM_IRQ_ENC_EVENT)
X
X/*
X * Register masks
X */
X#define CXM_MASK_SPU_ENABLE          0xfffffffe
X#define CXM_MASK_VPU_ENABLE15        0xfffffff6
X#define CXM_MASK_VPU_ENABLE16        0xfffffffb
X
X/*
X * Firmware commands
X */
X#define CXM_FW_CMD_ASSIGN_3_2_PULLDOWN          0x000000b1
X#define CXM_FW_CMD_ASSIGN_ASPECT_RATIO          0x00000099
X#define CXM_FW_CMD_ASSIGN_AUDIO_PROPERTIES      0x000000bd
X#define CXM_FW_CMD_ASSIGN_BITRATES              0x00000095
X#define CXM_FW_CMD_ASSIGN_CORING_LEVELS         0x0000009f
X#define CXM_FW_CMD_ASSIGN_DMA_BLOCKLEN          0x000000c9
X#define CXM_FW_CMD_ASSIGN_DNR_FILTER_MODE       0x0000009b
X#define CXM_FW_CMD_ASSIGN_DNR_FILTER_PROPERTIES 0x0000009d
X#define CXM_FW_CMD_ASSIGN_FRAME_DROP_RATE       0x000000d0
X#define CXM_FW_CMD_ASSIGN_FRAME_RATE            0x0000008f
X#define CXM_FW_CMD_ASSIGN_FRAME_SIZE            0x00000091
X#define CXM_FW_CMD_ASSIGN_GOP_CLOSURE           0x000000c5
X#define CXM_FW_CMD_ASSIGN_GOP_PROPERTIES        0x00000097
X#define CXM_FW_CMD_ASSIGN_NUM_VSYNC_LINES       0x000000d6
X#define CXM_FW_CMD_ASSIGN_OUTPUT_PORT           0x000000bb
X#define CXM_FW_CMD_ASSIGN_PGM_INDEX_INFO        0x000000c7
X#define CXM_FW_CMD_ASSIGN_PLACEHOLDER           0x000000d8
X#define CXM_FW_CMD_ASSIGN_SPATIAL_FILTER_TYPE   0x000000a1
X#define CXM_FW_CMD_ASSIGN_STREAM_TYPE           0x000000b9
X#define CXM_FW_CMD_ASSIGN_VBI_LINE              0x000000b7
X#define CXM_FW_CMD_ASSIGN_VBI_PROPERTIES        0x000000c8
X#define CXM_FW_CMD_BEGIN_CAPTURE                0x00000081
X#define CXM_FW_CMD_DEC_EVENT_NOTIFICATION       0x00000017
X#define CXM_FW_CMD_DEC_GET_FW_VER               0x00000011
X#define CXM_FW_CMD_DEC_HALT_FW                  0x0000000e
X#define CXM_FW_CMD_ENC_EVENT_NOTIFICATION       0x000000d5
X#define CXM_FW_CMD_ENC_GET_FW_VER               0x000000c4
X#define CXM_FW_CMD_ENC_HALT_FW                  0x000000c3
X#define CXM_FW_CMD_END_CAPTURE                  0x00000082
X#define CXM_FW_CMD_INITIALIZE_VIDEO_INPUT       0x000000cd
X#define CXM_FW_CMD_MUTE_VIDEO_INPUT             0x000000d9
X#define CXM_FW_CMD_PAUSE_ENCODER                0x000000d2
X#define CXM_FW_CMD_SCHED_DMA_TO_HOST            0x000000cc
X
X#define CXM_FW_STD_TIMEOUT                          0x00010000
X
X#define CXM_FW_CAPTURE_STREAM_TYPE_MPEG             0x00000000
X#define CXM_FW_CAPTURE_STREAM_TYPE_RAW              0x00000001
X#define CXM_FW_CAPTURE_STREAM_TYPE_RAW_PASSTHROUGH  0x00000002
X#define CXM_FW_CAPTURE_STREAM_TYPE_VBI              0x00000003
X
X#define CXM_FW_CAPTURE_STREAM_YUV                   0x00000001
X#define CXM_FW_CAPTURE_STREAM_PCM_AUDIO             0x00000002
X#define CXM_FW_CAPTURE_STREAM_VBI                   0x00000004
X
X#define CXM_FW_STREAM_TYPE_DVD                      0x0000000a
X#define CXM_FW_STREAM_TYPE_MPEG1                    0x00000002
X#define CXM_FW_STREAM_TYPE_MPEG2_PROGRAM            0x00000000
X#define CXM_FW_STREAM_TYPE_SVCD                     0x0000000c
X#define CXM_FW_STREAM_TYPE_VCD                      0x0000000b
X
X#define CXM_MACROBLOCK_HEIGHT 16
X#define CXM_MACROBLOCK_WIDTH  16
X#define CXM_MACROBLOCK_SIZE   (CXM_MACROBLOCK_HEIGHT * CXM_MACROBLOCK_WIDTH)
X
X/*
X * I2C addresses
X */
X#define CXM_I2C_EEPROM   0xa0
X#define CXM_I2C_IR       0x30
X#define CXM_I2C_MSP3400  0x80
X#define CXM_I2C_SAA7115  0x42
X#define CXM_I2C_TUNER    0xc2
X#define CXM_I2C_TUNER_IF 0x86
X
X/* The maximum wait time of the MSP
X   is 1 ms during normal operations. */
X#define CXM_I2C_TIMEOUT  1000
X
X/*
X * EEPROM 
X */
Xint cxm_eeprom_init( struct cxm_softc *sc );
Xint cxm_eeprom_tuner_type( struct cxm_softc *sc );
X
X/*
X * Infrared remote 
X */
Xint cxm_ir_init( struct cxm_softc *sc );
Xint cxm_ir_key( struct cxm_softc *sc, char *buf, int len );
X
X/*
X * Audio decoder
X */
X#define CXM_MSP3400C_DEM 0x10
X#define CXM_MSP3400C_DFP 0x12
X
Xstruct cxm_msp_setting {
X	unsigned char	dev;
X	unsigned int	addr;
X	char		value[2];
X};
X
Xstruct cxm_msp_command {
X	unsigned int nsettings;
X	struct cxm_msp_setting settings[5];
X};
X
Xint cxm_msp_init( struct cxm_softc *sc );
Xint cxm_msp_mute( struct cxm_softc *sc );
Xint cxm_msp_unmute( struct cxm_softc *sc );
Xint cxm_msp_is_muted( struct cxm_softc *sc );
Xint cxm_msp_select_source( struct cxm_softc *sc, enum cxm_source source );
Xenum cxm_source cxm_msp_selected_source( struct cxm_softc *sc );
Xint cxm_msp_autodetect_standard( struct cxm_softc *sc );
Xint cxm_msp_is_locked( struct cxm_softc *sc );
Xint cxm_msp_wait_for_lock( struct cxm_softc *sc );
X
X/*
X * Tuner
X */
X#define CXM_TUNER_PHILIPS_FI1216_MK2   0
X#define CXM_TUNER_PHILIPS_FM1216       1
X#define CXM_TUNER_PHILIPS_FQ1216ME     2
X#define CXM_TUNER_PHILIPS_FQ1216ME_MK3 3
X#define CXM_TUNER_PHILIPS_FM1216ME_MK3 4
X#define CXM_TUNER_PHILIPS_FI1236_MK2   5
X#define CXM_TUNER_PHILIPS_FM1236       6
X#define CXM_TUNER_PHILIPS_FI1246_MK2   7
X#define CXM_TUNER_PHILIPS_FM1246       8
X#define CXM_TUNER_TEMIC_4006_FH5       9
X#define CXM_TUNER_TEMIC_4009_FR5      10
X#define CXM_TUNER_TEMIC_4036_FY5      11
X#define CXM_TUNER_TEMIC_4039_FR5      12
X#define CXM_TUNER_TEMIC_4066_FY5      13
X#define CXM_TUNER_LG_TPI8PSB11D       14
X#define CXM_TUNER_LG_TPI8PSB01N       15
X#define CXM_TUNER_LG_TAPC_H701F       16
X#define CXM_TUNER_LG_TAPC_H001F       17
X#define CXM_TUNER_LG_TAPE_H001F       18
X#define CXM_TUNER_MICROTUNE_4049_FM5  19
X#define CXM_TUNER_TCL_2002N_6A        20
X
X#define CXM_TUNER_TYPES               21
X
X#define CXM_TUNER_AFC_MASK           0x07
X
X#define CXM_TUNER_AFC_FREQ_MINUS_125 0x00
X#define CXM_TUNER_AFC_FREQ_MINUS_62  0x01
X#define CXM_TUNER_AFC_FREQ_CENTERED  0x02
X#define CXM_TUNER_AFC_FREQ_PLUS_62   0x03
X#define CXM_TUNER_AFC_FREQ_PLUS_125  0x04
X
X#define CXM_TUNER_PHASE_LOCKED       0x40
X
X#define CXM_TUNER_FM_SYSTEM          0x01
X#define CXM_TUNER_TV_SYSTEM_BG       0x02
X#define CXM_TUNER_TV_SYSTEM_DK       0x04
X#define CXM_TUNER_TV_SYSTEM_I        0x08
X#define CXM_TUNER_TV_SYSTEM_MN       0x10
X#define CXM_TUNER_TV_SYSTEM_L        0x20
X#define CXM_TUNER_TV_SYSTEM_L_PRIME  0x40
X
Xstruct cxm_tuner_band_code {
X	unsigned long	freq;
X	unsigned char	codes[2];
X};
X
Xstruct cxm_tuner_channel_assignment {
X	unsigned int	channel;
X	unsigned long	freq;
X	unsigned long	step;
X};
X
Xstruct cxm_tuner_channels {
X	const char	*name;
X	unsigned int	chnlset;
X	unsigned int	system;
X	unsigned int	min_channel;
X	unsigned int	max_channel;
X	unsigned long	if_freq;
X	struct cxm_tuner_channel_assignment assignments[17];
X};
X
Xstruct cxm_tuner_system_code {
X	unsigned int	system;
X	unsigned char	codes[4];
X};
X
Xenum cxm_tuner_system_code_style { cxm_unknown_system_code_style,
X				   cxm_none_system_code_style,
X				   cxm_port_system_code_style,
X				   cxm_if_system_code_style,
X				   cxm_if_system_with_aux_code_style };
X
Xstruct cxm_tuner_system {
X	unsigned int				supported;
X	enum cxm_tuner_system_code_style	code_style;
X	struct cxm_tuner_system_code		codes[6];
X};
X
Xstruct cxm_tuner {
X	const char	*name;
X	struct cxm_tuner_system	systems;
X	unsigned long	min_freq;
X	unsigned long	max_freq;
X	struct cxm_tuner_band_code band_codes[3];
X	unsigned long fm_min_freq;
X	unsigned long fm_max_freq;
X	struct cxm_tuner_band_code fm_band_code;
X	const struct cxm_tuner_channels *default_channels;
X};
X
Xenum cxm_tuner_freq_type { cxm_tuner_unknown_freq_type, cxm_tuner_fm_freq_type,
X			   cxm_tuner_tv_freq_type };
X
Xint cxm_tuner_init( struct cxm_softc *sc );
Xint cxm_tuner_select_channel_set( struct cxm_softc *sc,
X				  unsigned int channel_set );
Xunsigned int cxm_tuner_selected_channel_set( struct cxm_softc *sc );
Xint cxm_tuner_select_frequency( struct cxm_softc *sc,
X				enum cxm_tuner_freq_type type,
X				unsigned long freq );
Xint cxm_tuner_select_channel( struct cxm_softc *sc, unsigned int channel );
Xint cxm_tuner_apply_afc( struct cxm_softc *sc );
Xint cxm_tuner_is_locked( struct cxm_softc *sc );
Xint cxm_tuner_wait_for_lock( struct cxm_softc *sc );
Xint cxm_tuner_status( struct cxm_softc *sc );
X
X/*
X * Video decoder
X */
Xstruct cxm_saa7115_setting {
X	unsigned char	addr;
X	unsigned int	nvalues;
X	char		values[32];
X};
X
Xstruct cxm_saa7115_command {
X	unsigned int nsettings;
X	struct cxm_saa7115_setting settings[20];
X};
X
Xstruct cxm_saa7115_audio_clock {
X	unsigned int sample_rate;
X	unsigned int fps;
X	const struct cxm_saa7115_command *clock;
X};
X
Xstruct cxm_saa7115_scaling {
X	unsigned int width;
X	unsigned int height;
X	unsigned int fps;
X	const struct cxm_saa7115_command *scaling;
X};
X
Xint cxm_saa7115_init( struct cxm_softc *sc );
Xint cxm_saa7115_mute( struct cxm_softc *sc );
Xint cxm_saa7115_unmute( struct cxm_softc *sc );
Xint cxm_saa7115_select_source( struct cxm_softc *sc, enum cxm_source source );
Xint cxm_saa7115_configure( struct cxm_softc *sc,
X			   unsigned int width, unsigned int height,
X			   unsigned int fps, unsigned int audio_sample_rate);
Xenum cxm_source_format cxm_saa7115_detected_format( struct cxm_softc *sc );
Xint cxm_saa7115_detected_fps( struct cxm_softc *sc );
Xint cxm_saa7115_get_brightness( struct cxm_softc *sc );
Xint cxm_saa7115_set_brightness( struct cxm_softc *sc,
X				unsigned char brightness );
Xint cxm_saa7115_get_chroma_saturation( struct cxm_softc *sc );
Xint cxm_saa7115_set_chroma_saturation( struct cxm_softc *sc,
X				       unsigned char chroma_saturation);
Xint cxm_saa7115_get_contrast( struct cxm_softc *sc );
Xint cxm_saa7115_set_contrast( struct cxm_softc *sc, unsigned char contrast );
Xint cxm_saa7115_get_hue( struct cxm_softc *sc );
Xint cxm_saa7115_set_hue( struct cxm_softc *sc, unsigned char hue );
Xint cxm_saa7115_is_locked( struct cxm_softc *sc );
Xint cxm_saa7115_wait_for_lock( struct cxm_softc *sc );
END-of-dev/cxm/cxm.h
echo x - dev/cxm/cxm_audio.c
sed 's/^X//' >dev/cxm/cxm_audio.c << 'END-of-dev/cxm/cxm_audio.c'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Audio decoder routines for the Conexant MPEG-2 Codec driver.
X *
X * Ideally these routines should be implemented as a separate
X * driver which has a generic audio decoder interface so that
X * it's not necessary for each multimedia driver to re-invent
X * the wheel.
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/poll.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X#include <dev/iicbus/iicbus.h>
X
X#include "iicbb_if.h"
X
X
Xstatic const struct cxm_msp_command
Xmsp34x5G_init = {
X	5,
X	{
X		/* Enable Automatic Sound Select */
X		{ CXM_MSP3400C_DEM, 0x0030, { 0x20, 0x03 } },
X		/* SCART Prescale = 0 dB */
X		{ CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } },
X		/* FM / AM Prescale = 100 Khz and FM Matrix = Sound A Mono  */
X		{ CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x03 } },
X		/* NICAM Prescale = 9 dB  */
X		{ CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } },
X		/* Enable Automatic Standard Select */
X		{ CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } },
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34x5G_select_tuner = {
X	3,
X	{
X		/* Loudspeaker Source = demodulator (St or A), Matrix = St */
X		{ CXM_MSP3400C_DFP, 0x0008, { 0x03, 0x20 } },
X		/* SCART1_L/R Source = demodulator (St or A), Matrix = St */
X		{ CXM_MSP3400C_DFP, 0x000a, { 0x03, 0x20 } },
X		/* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */
X		{ CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } }
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34x5D_init = {
X	4,
X	{
X		/* Enable Automatic NICAM-FM/AM Switching */
X		{ CXM_MSP3400C_DEM, 0x0021, { 0x00, 0x01 } },
X		/* SCART Prescale = 0 dB */
X		{ CXM_MSP3400C_DFP, 0x000d, { 0x19, 0x00 } },
X		/* NICAM Prescale = 9 dB  */
X		{ CXM_MSP3400C_DFP, 0x0010, { 0x5a, 0x00 } },
X		/* Enable Automatic Standard Select */
X		{ CXM_MSP3400C_DEM, 0x0020, { 0x00, 0x01 } },
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34x5D_select_tuner = {
X	5,
X	{
X		/* Loudspeaker Source = demodulator (NICAM), Matrix = St */
X		{ CXM_MSP3400C_DFP, 0x0008, { 0x01, 0x20 } },
X		/* SCART1_L/R Source = demodulator (NICAM), Matrix = St */
X		{ CXM_MSP3400C_DFP, 0x000a, { 0x01, 0x20 } },
X		/* FM / AM Prescale = 100 Khz and FM Matrix = No Matrix  */
X		{ CXM_MSP3400C_DFP, 0x000e, { 0x24, 0x00 } },
X		/* FM Deemphasis = 50 us */
X		{ CXM_MSP3400C_DFP, 0x000f, { 0x00, 0x00 } },
X		/* DSP In = mute, SC1_OUT_L/R Source = SCART1_L/R */
X		{ CXM_MSP3400C_DFP, 0x0013, { 0x0f, 0x20 } }
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34xxx_mute = {
X	2,
X	{
X		/* Loudspeaker volume = mute */
X		{ CXM_MSP3400C_DFP, 0x0000, { 0x00, 0x00 } },
X		/* SC1_OUT_L/R volume = mute */
X		{ CXM_MSP3400C_DFP, 0x0007, { 0x00, 0x01 } }
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34xxx_unmute = {
X	2,
X	{
X		/* Loudspeaker volume = 0 db */
X		{ CXM_MSP3400C_DFP, 0x0000, { 0x73, 0x00 } },
X		/* SC1_OUT_L/R volume = 0 db */
X		{ CXM_MSP3400C_DFP, 0x0007, { 0x73, 0x01 } }
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34xxx_select_fm = {
X	3,
X	{
X		/* Loudspeaker Source = SCART, Matrix = STEREO */
X		{ CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } },
X		/* SCART1_L/R Source = SCART, Matrix = STEREO */
X		{ CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } },
X		/* DSP In = SC2_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */
X		{ CXM_MSP3400C_DFP, 0x0013, { 0x0e, 0x00 } }
X	}
X};
X
Xstatic const struct cxm_msp_command
Xmsp34xxx_select_line_in = {
X	3,
X	{
X		/* Loudspeaker Source = SCART, Matrix = STEREO */
X		{ CXM_MSP3400C_DFP, 0x0008, { 0x02, 0x20 } },
X		/* SCART1_L/R Source = SCART, Matrix = STEREO */
X		{ CXM_MSP3400C_DFP, 0x000a, { 0x02, 0x20 } },
X		/* DSP In = SC1_IN_L/R, SC1_OUT_L/R Source = SCART1_L/R */
X		{ CXM_MSP3400C_DFP, 0x0013, { 0x0c, 0x00 } }
X	}
X};
X
X
X/* Reset the MSP or DPL chip */
Xstatic int
Xcxm_msp_dpl_reset( device_t iicbus, int i2c_addr )
X{
X	unsigned char msg[3];
X	int sent;
X
X	/* put into reset mode */
X	msg[0] = 0x00;
X	msg[1] = 0x80;
X	msg[2] = 0x00;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	/* put back to operational mode */
X	msg[0] = 0x00;
X	msg[1] = 0x00;
X	msg[2] = 0x00;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return 0;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
X/* Read from the MSP or DPL registers */
Xstatic int
Xcxm_msp_dpl_read( device_t iicbus, int i2c_addr,
X		  unsigned char dev, unsigned int addr,
X		  char *buf, int len )
X{
X	unsigned char msg[3];
X	int received;
X	int sent;
X
X	msg[0] = (unsigned char)(dev + 1);
X	msg[1] = (unsigned char)(addr >> 8);
X	msg[2] = (unsigned char)addr;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
X		goto fail;
X
X	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return received;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
X/* Write to the MSP or DPL registers */
Xstatic int
Xcxm_msp_dpl_write( device_t iicbus, int i2c_addr,
X		   unsigned char dev, unsigned int addr,
X		   const char *buf, int len )
X{
X	unsigned char msg[3];
X	int sent;
X
X	msg[0] = (unsigned char)dev;
X	msg[1] = (unsigned char)(addr >> 8);
X	msg[2] = (unsigned char)addr;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return sent;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
Xint
Xcxm_msp_init( struct cxm_softc *sc )
X{
X	unsigned char rev1[2];
X	unsigned char rev2[2];
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_msp_setting *settings;
X
X	if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
X		return -1;
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
X			     0x001e, rev1, sizeof(rev1)) != sizeof(rev1))
X		return -1;
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
X			     0x001f, rev2, sizeof(rev2)) != sizeof(rev2))
X		return -1;
X
X	snprintf(sc->msp_name, sizeof(sc->msp_name), "%c4%02d%c-%c%d",
X		 ((rev1[1] >> 4) & 0x0f) + '3', rev2[0],
X		 (rev1[1] & 0x0f) + '@', rev1[0] + '@', rev2[1] & 0x1f);
X
X	/*
X	 * MSP 34x5D, 34x5G, and MSP 44x8G are the
X	 * only audio decoders currently supported.
X	 */
X
X	if (strncmp(&sc->msp_name[0], "34", 2) == 0
X	    && strncmp(&sc->msp_name[3], "5D", 2) == 0)
X	  ;
X	else if (strncmp(&sc->msp_name[0], "34", 2) == 0
X		 && strncmp(&sc->msp_name[3], "5G", 2) == 0)
X	  ;
X	else if (strncmp(&sc->msp_name[0], "44", 2) == 0
X		 && strncmp(&sc->msp_name[3], "8G", 2) == 0)
X	  ;
X	else {
X		printf("%s: unknown audio decoder MSP%s\n",
X		       sc->name, sc->msp_name);
X		return -1;
X	}
X
X	nsettings = msp34x5G_init.nsettings;
X	settings = msp34x5G_init.settings;
X	if (sc->msp_name[4] == 'D') {
X		nsettings = msp34x5D_init.nsettings;
X		settings = msp34x5D_init.settings;
X	  }
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
X				      settings[i].dev, settings[i].addr,
X				      settings[i].value,
X				      sizeof(settings[i].value))
X		    != sizeof(settings[i].value))
X			return -1;
X
X	if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
X		return -1;
X
X	printf("%s: MSP%s audio decoder\n", sc->name, sc->msp_name);
X
X	return 0;
X}
X
X
Xint
Xcxm_msp_mute( struct cxm_softc *sc )
X{
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_msp_setting *settings;
X
X	nsettings = msp34xxx_mute.nsettings;
X	settings = msp34xxx_mute.settings;
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
X				      settings[i].dev, settings[i].addr,
X				      settings[i].value,
X				      sizeof(settings[i].value))
X		    != sizeof(settings[i].value))
X			return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_msp_unmute( struct cxm_softc *sc )
X{
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_msp_setting *settings;
X
X	nsettings = msp34xxx_unmute.nsettings;
X	settings = msp34xxx_unmute.settings;
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
X				      settings[i].dev, settings[i].addr,
X				      settings[i].value,
X				      sizeof(settings[i].value))
X		    != sizeof(settings[i].value))
X			return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_msp_is_muted( struct cxm_softc *sc )
X{
X	unsigned char volume[2];
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
X			     0x0000, volume, sizeof(volume)) != sizeof(volume))
X		return -1;
X
X	return volume[0] == 0x00 || volume[0] == 0xff ? 1 : 0;
X}
X
X
Xint
Xcxm_msp_select_source( struct cxm_softc *sc, enum cxm_source source )
X{
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_msp_setting *settings;
X
X	switch (source) {
X	case cxm_fm_source:
X		nsettings = msp34xxx_select_fm.nsettings;
X		settings = msp34xxx_select_fm.settings;
X		break;
X
X	case cxm_line_in_source_composite:
X	case cxm_line_in_source_svideo:
X		nsettings = msp34xxx_select_line_in.nsettings;
X		settings = msp34xxx_select_line_in.settings;
X		break;
X
X	case cxm_tuner_source:
X		nsettings = msp34x5G_select_tuner.nsettings;
X		settings = msp34x5G_select_tuner.settings;
X		if (sc->msp_name[4] == 'D') {
X			nsettings = msp34x5D_select_tuner.nsettings;
X			settings = msp34x5D_select_tuner.settings;
X		  }
X		break;
X
X	default:
X		return -1;
X	}
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
X				      settings[i].dev, settings[i].addr,
X				      settings[i].value,
X				      sizeof(settings[i].value))
X		    != sizeof(settings[i].value))
X			return -1;
X
X	return 0;
X}
X
X
Xenum cxm_source
Xcxm_msp_selected_source( struct cxm_softc *sc )
X{
X	unsigned char dsp[2];
X	unsigned char source[2];
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
X			     0x0008, source, sizeof(source)) != sizeof(source))
X		return cxm_unknown_source;
X
X	switch (source[0]) {
X	case 0: /* FM / AM mono signal */
X	case 1: /* Stereo or A / B */
X	case 3: /* Stereo or A */
X	case 4: /* Stereo or B */
X		return cxm_tuner_source;
X
X	case 2: /* SCART */
X		break;
X
X	default:
X		return cxm_unknown_source;
X	}
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
X			     0x0013, dsp, sizeof(dsp)) != sizeof(dsp))
X		return cxm_unknown_source;
X
X	if (dsp[1] & 0x20)
X		return cxm_unknown_source;
X
X	switch (dsp[0] & 0x03) {
X	case 0:
X		return cxm_line_in_source_composite;
X
X	case 2:
X		return cxm_fm_source;
X
X	default:
X		 return cxm_unknown_source;
X	}
X}
X
X
Xint
Xcxm_msp_autodetect_standard( struct cxm_softc *sc )
X{
X	unsigned int i;
X	int locked;
X	unsigned int nsettings;
X	const struct cxm_msp_setting *settings;
X
X	switch (cxm_msp_selected_source(sc)) {
X	case cxm_tuner_source:
X		break;
X
X	case cxm_fm_source:
X	case cxm_line_in_source_composite:
X	case cxm_line_in_source_svideo:
X		return 1;
X
X	default:
X		return -1;
X	}
X
X	/*
X	 * Section 3.3.2.2 of the data sheet states:
X	 *
X	 *   A general refresh of the STANDARD SELECT
X	 *   register is not allowed.
X	 */
X
X	if (cxm_msp_dpl_reset (sc->iicbus, CXM_I2C_MSP3400) < 0)
X		return -1;
X
X	nsettings = msp34x5G_init.nsettings;
X	settings = msp34x5G_init.settings;
X	if (sc->msp_name[4] == 'D') {
X		nsettings = msp34x5D_init.nsettings;
X		settings = msp34x5D_init.settings;
X	  }
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_msp_dpl_write(sc->iicbus, CXM_I2C_MSP3400,
X				      settings[i].dev, settings[i].addr,
X				      settings[i].value,
X				      sizeof(settings[i].value))
X		    != sizeof(settings[i].value))
X			return -1;
X
X	locked = cxm_msp_wait_for_lock(sc);
X
X	if (cxm_msp_select_source(sc, cxm_tuner_source) < 0)
X		return -1;
X
X	return locked;
X}
X
X
Xint
Xcxm_msp_is_locked( struct cxm_softc *sc )
X{
X	unsigned char source[2];
X	unsigned char standard[2];
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DFP,
X			     0x0008, source, sizeof(source)) != sizeof(source))
X		return -1;
X
X	switch (source[0]) {
X	case 0: /* FM / AM mono signal */
X	case 1: /* Stereo or A / B */
X	case 3: /* Stereo or A */
X	case 4: /* Stereo or B */
X		break;
X
X	default:
X		return 1;
X	}
X
X	if (cxm_msp_dpl_read(sc->iicbus, CXM_I2C_MSP3400, CXM_MSP3400C_DEM,
X			     0x007e, standard, sizeof(standard))
X	    != sizeof(standard))
X		return -1;
X
X	if (standard[0] >= 8 || (standard[0] == 0 && standard[1] == 0))
X		return 0;
X
X	return 1;
X}
X
X
Xint
Xcxm_msp_wait_for_lock( struct cxm_softc *sc )
X{
X	unsigned int i;
X
X	/*
X	 * Section 3.3.2.1 of the data sheet states:
X	 *
X	 *   Within 0.5 s the detection and setup of the actual
X	 *   TV sound standard is performed.  The detected result
X	 *   can be read out of the STANDARD RESULT register by
X	 *   the control processor.
X	 */
X
X	for (i = 0; i < 10; i++) {
X
X		/*
X		 * The input may have just changed (prior to
X		 * cxm_msp_wait_for_lock) so start with the
X		 * delay to give the audio decoder a chance
X		 * to update its status.
X		 */
X
X		tsleep(&sc->iicbus, PZERO, "audio", hz / 20 );
X
X		switch (cxm_msp_is_locked(sc)) {
X		case 1:
X			return 1;
X
X		case 0:
X			break;
X
X		default:
X			return -1;
X		}
X	}
X
X	printf("%s: audio decoder failed to lock\n", sc->name);
X
X	return 0;
X}
END-of-dev/cxm/cxm_audio.c
echo x - dev/cxm/cxm_eeprom.c
sed 's/^X//' >dev/cxm/cxm_eeprom.c << 'END-of-dev/cxm/cxm_eeprom.c'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * EEPROM routines for the Conexant MPEG-2 Codec driver.
X *
X * Ideally these routines should be implemented as a separate
X * driver which has a generic EEPROM interface so that it's
X * not necessary for each multimedia driver to re-invent the
X * wheel.
X */
X
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/poll.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X#include <dev/iicbus/iicbus.h>
X
X#include "iicbb_if.h"
X
X
Xstatic int
Xcxm_eeprom_read( device_t iicbus, int i2c_addr,
X		 char *buf, int len, unsigned int offset )
X{
X	char msg[1];
X	int received;
X	int sent;
X
X	msg[0] = (unsigned char)offset;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
X		goto fail;
X
X	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X        return received;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
Xint
Xcxm_eeprom_init( struct cxm_softc *sc )
X{
X	unsigned char eeprom[1];
X
X	if (cxm_eeprom_read(sc->iicbus, CXM_I2C_EEPROM,
X			    eeprom, sizeof(eeprom), 0) != sizeof(eeprom))
X		return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_eeprom_tuner_type( struct cxm_softc *sc )
X{
X	unsigned char eeprom[256];
X	unsigned int i;
X	unsigned int len;
X	unsigned int subsystem_vendor_id;
X	unsigned int tuner_code;
X	int tuner_type;
X
X	if (cxm_eeprom_read(sc->iicbus, CXM_I2C_EEPROM,
X			    eeprom, sizeof(eeprom), 0) != sizeof(eeprom))
X		return -1;
X
X	subsystem_vendor_id = (unsigned int)eeprom[254] << 8 | eeprom[255];
X	tuner_type = -1;
X
X	switch (subsystem_vendor_id) {
X	case CXM_VENDORID_HAUPPAUGE:
X
X		/*
X		 * The Hauppauge eeprom format is tagged.
X		 */
X
X		if (eeprom[0] != 0x84) {
X			printf("%s: unknown Hauppauge eeprom format %#x\n",
X			       sc->name, (unsigned int)eeprom[0]);
X			break;
X		}
X
X		tuner_code = -1;
X
X		for (i = 0; i < sizeof(eeprom); i += len) {
X			len = 0;
X			if (eeprom[i] == 0x84) {
X				len = (unsigned int)eeprom[i + 2] << 8
X				      | eeprom[i + 1];
X				i += 3;
X			}
X			else if ((eeprom[i] & 0xf0) == 0x70) {
X				if (eeprom[i] & 0x08)
X					break;
X				len = eeprom[i] & 0x07;
X				i++;
X			}
X			else {
X				printf(
X				  "%s: unknown Hauppauge eeprom packet %#x\n",
X				       sc->name, (unsigned int)eeprom[i]);
X				return -1;
X			}
X
X			if (i >= sizeof(eeprom)
X			    || (i + len) > sizeof(eeprom)) {
X				printf("%s: corrupt Hauppauge eeprom packet\n",
X				       sc->name);
X				return -1;
X			}
X
X			switch (eeprom[i]) {
X			case 0x00:
X				tuner_code = eeprom[i + 6];
X				break;
X
X			case 0x0a:
X				tuner_code = eeprom[i + 2];
X				break;
X
X			default:
X				break;
X			}
X		}
X
X		switch (tuner_code) {
X		case 0x03: /* Philips FI1216 */
X		case 0x08: /* Philips FI1216 MK2 */
X			tuner_type = CXM_TUNER_PHILIPS_FI1216_MK2;
X			break;
X
X		case 0x22: /* Philips FQ1216ME */
X			tuner_type = CXM_TUNER_PHILIPS_FQ1216ME;
X			break;
X
X		case 0x37: /* Philips FQ1216ME MK3 */
X			tuner_type = CXM_TUNER_PHILIPS_FQ1216ME_MK3;
X			break;
X
X		case 0x1d: /* Temic 4006FH5 */
X			tuner_type = CXM_TUNER_TEMIC_4006_FH5;
X			break;
X
X		case 0x30: /* LG Innotek TPI8PSB11D */
X			tuner_type = CXM_TUNER_LG_TPI8PSB11D;
X			break;
X
X		case 0x34: /* Microtune 4049FM5 */
X			tuner_type = CXM_TUNER_MICROTUNE_4049_FM5;
X			break;
X
X		case 0x05: /* Philips FI1236 */
X		case 0x0a: /* Philips FI1236 MK2 */
X			tuner_type = CXM_TUNER_PHILIPS_FI1236_MK2;
X			break;
X
X		case 0x1a: /* Temic 4036FY5 */
X			tuner_type = CXM_TUNER_TEMIC_4036_FY5;
X			break;
X
X		case 0x52: /* LG Innotek TAPC-H701F */
X			tuner_type = CXM_TUNER_LG_TAPC_H701F;
X			break;
X
X		case 0x55: /* TCL 2002N-6A */
X			tuner_type = CXM_TUNER_TCL_2002N_6A;
X			break;
X
X		case 0x06: /* Philips FI1246 */
X		case 0x0b: /* Philips FI1246 MK2 */
X			tuner_type = CXM_TUNER_PHILIPS_FI1246_MK2;
X			break;
X
X		case 0x23: /* Temic 4066FY5 */
X			tuner_type = CXM_TUNER_TEMIC_4066_FY5;
X			break;
X
X		case 0x10: /* Philips FR1216 MK2 */
X		case 0x15: /* Philips FM1216 */
X			tuner_type = CXM_TUNER_PHILIPS_FM1216;
X			break;
X
X		case 0x39: /* Philips FM1216ME MK3 */
X			tuner_type = CXM_TUNER_PHILIPS_FM1216ME_MK3;
X			break;
X
X		case 0x2a: /* Temic 4009FR5 */
X			tuner_type = CXM_TUNER_TEMIC_4009_FR5;
X			break;
X
X		case 0x2f: /* LG Innotek TPI8PSB01N */
X			tuner_type = CXM_TUNER_LG_TPI8PSB01N;
X			break;
X
X		case 0x12: /* Philips FR1236 MK2 */
X		case 0x17: /* Philips FM1236 */
X			tuner_type = CXM_TUNER_PHILIPS_FM1236;
X			break;
X
X		case 0x21: /* Temic 4039FR5 */
X			tuner_type = CXM_TUNER_TEMIC_4039_FR5;
X			break;
X
X		case 0x44: /* LG Innotek TAPE-H001F */
X			tuner_type = CXM_TUNER_LG_TAPE_H001F;
X			break;
X
X		case 0x13: /* Philips FR1246 MK2 */
X		case 0x18: /* Philips FM1246 */
X			tuner_type = CXM_TUNER_PHILIPS_FM1246;
X			break;
X
X		default:
X			printf("%s: unknown tuner code %#x\n",
X			       sc->name, tuner_code);
X			break;
X		}
X		break;
X
X	default:
X		printf("%s: unknown subsystem vendor id %#x\n",
X		       sc->name, subsystem_vendor_id);
X		break;
X	}
X
X	return tuner_type;
X}
END-of-dev/cxm/cxm_eeprom.c
echo x - dev/cxm/cxm_i2c.c
sed 's/^X//' >dev/cxm/cxm_i2c.c << 'END-of-dev/cxm/cxm_i2c.c'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * I2c routines for the Conexant MPEG-2 Codec driver.
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/malloc.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/module.h>
X#include <sys/poll.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#if __FreeBSD_version < 600000
X#  include <machine/bus_memio.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#if __FreeBSD_version < 500000
X#  include <pci/pcireg.h>
X#  include <pci/pcivar.h>
X#else
X#  include <dev/pci/pcireg.h>
X#  include <dev/pci/pcivar.h>
X#endif
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X
X#include "iicbb_if.h"
X
X
Xstatic int	cxm_iic_probe( device_t dev );
Xstatic int	cxm_iic_attach( device_t dev );
Xstatic int	cxm_iic_detach( device_t dev );
Xstatic void	cxm_iic_child_detached( device_t dev, device_t child );
X
Xstatic int	cxm_iic_callback( device_t, int, caddr_t * );
Xstatic int	cxm_iic_reset( device_t, u_char, u_char, u_char * );
Xstatic int	cxm_iic_getscl( device_t );
Xstatic int	cxm_iic_getsda( device_t );
Xstatic void	cxm_iic_setscl( device_t, int );
Xstatic void	cxm_iic_setsda( device_t, int );
X
X#if __FreeBSD_version < 500000
Xstatic void	cxm_iic_setlines( device_t, int, int );
X#endif
X
Xstatic device_method_t cxm_iic_methods[] = {
X	/* Device interface */
X	DEVMETHOD(device_probe,         cxm_iic_probe),
X	DEVMETHOD(device_attach,        cxm_iic_attach),
X	DEVMETHOD(device_detach,        cxm_iic_detach),
X
X	/* bus interface */
X	DEVMETHOD(bus_child_detached,   cxm_iic_child_detached),
X	DEVMETHOD(bus_print_child,      bus_generic_print_child),
X	DEVMETHOD(bus_driver_added,     bus_generic_driver_added),
X
X	/* iicbb interface */
X	DEVMETHOD(iicbb_callback,       cxm_iic_callback),
X	DEVMETHOD(iicbb_reset,          cxm_iic_reset),
X
X#if __FreeBSD_version < 500000
X	DEVMETHOD(iicbb_getdataline,    cxm_iic_getsda),
X	DEVMETHOD(iicbb_setlines,       cxm_iic_setlines),
X#else
X	DEVMETHOD(iicbb_getscl,         cxm_iic_getscl),
X	DEVMETHOD(iicbb_getsda,         cxm_iic_getsda),
X	DEVMETHOD(iicbb_setscl,         cxm_iic_setscl),
X	DEVMETHOD(iicbb_setsda,         cxm_iic_setsda),
X#endif
X
X	{ 0, 0 }
X};
X
Xstatic driver_t cxm_iic_driver = {
X	"cxm_iic",
X	cxm_iic_methods,
X	sizeof(struct cxm_iic_softc),
X};
X
Xstatic devclass_t cxm_iic_devclass;
X
X#if __FreeBSD_version >= 500000
XMODULE_DEPEND(cxm_iic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
X#endif
XMODULE_VERSION(cxm_iic, 1);
XDRIVER_MODULE(cxm_iic, cxm, cxm_iic_driver, cxm_iic_devclass, 0, 0);
X
X
X/*
X * the boot time probe routine.
X *
X * The cxm_iic device is only probed after it has
X * been established that the cxm device is present
X * which means that the cxm_iic device * must *
X * be present since it's built into the cxm hardware.
X */
Xstatic int
Xcxm_iic_probe( device_t dev )
X{
X	device_set_desc(dev, "Conexant iTVC15 / iTVC16 I2C controller");
X
X	return 0;
X}
X
X
X/*
X * the attach routine.
X */
Xstatic int
Xcxm_iic_attach( device_t dev )
X{
X	device_t *kids;
X	device_t iicbus;
X	int error;
X	int numkids;
X	int i;
X	int unit;
X	bus_space_handle_t *bhandlep;
X	bus_space_tag_t *btagp;
X	struct cxm_iic_softc *sc;
X	device_t child;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X	unit = device_get_unit(dev);
X
X	/* build the device name */
X	snprintf(sc->name, sizeof(sc->name), "cxm_iic%d",unit);
X
X	/* retrieve the cxm btag and bhandle */
X	if (BUS_READ_IVAR(device_get_parent(dev), dev,
X			  CXM_IVAR_BTAG, (uintptr_t *)&btagp)
X	    || BUS_READ_IVAR(device_get_parent(dev), dev,
X			     CXM_IVAR_BHANDLE, (uintptr_t *)&bhandlep)) {
X		device_printf(dev,
X			      "could not retrieve bus space information\n");
X		return ENXIO;
X	}
X
X	sc->btag = *btagp;
X	sc->bhandle = *bhandlep;
X
X	/* add bit-banging generic code onto cxm_iic interface */
X	sc->iicbb = device_add_child(dev, "iicbb", -1);
X
X	if (! sc->iicbb) {
X		device_printf(dev, "could not add iicbb\n");
X		return ENXIO;
X	}
X
X	/* probed and attached the bit-banging code */
X	error = device_probe_and_attach(sc->iicbb);
X
X	if (error) {
X		device_printf(dev, "could not attach iicbb\n");
X		goto fail;
X	}
X
X	/* locate iicbus which was attached by the bit-banging code */
X	iicbus = NULL;
X	device_get_children(sc->iicbb, &kids, &numkids);
X	for (i = 0; i < numkids; i++)
X		if (strcmp(device_get_name(kids[i]), "iicbus") == 0) {
X			iicbus = kids[i];
X			break;
X		}
X	free(kids, M_TEMP);
X
X	if (! iicbus) {
X		device_printf(dev, "could not find iicbus\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	if (BUS_WRITE_IVAR(device_get_parent(dev), dev,
X			   CXM_IVAR_IICBUS, (uintptr_t)&iicbus)) {
X		device_printf(dev, "could not store iicbus information\n");
X		error = ENXIO;
X		goto fail;
X	}
X
X	return 0;
X
Xfail:
X	/*
X	 * Detach the children before recursively deleting
X	 * in case a child has a pointer to a grandchild
X	 * which is used by the child's detach routine.
X	 *
X	 * Remember the child before detaching so we can
X	 * delete it (bus_generic_detach indirectly zeroes
X	 * sc->child_dev).
X	 */
X	child = sc->iicbb;
X	bus_generic_detach(dev);
X	if (child)
X		device_delete_child(dev, child);
X
X	return error;
X}
X
X
X/*
X * the detach routine.
X */
Xstatic int
Xcxm_iic_detach( device_t dev )
X{
X	struct cxm_iic_softc *sc;
X	device_t child;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X
X	(void)BUS_WRITE_IVAR(device_get_parent(dev), dev, CXM_IVAR_IICBUS, 0);
X
X	/*
X	 * Detach the children before recursively deleting
X	 * in case a child has a pointer to a grandchild
X	 * which is used by the child's detach routine.
X	 *
X	 * Remember the child before detaching so we can
X	 * delete it (bus_generic_detach indirectly zeroes
X	 * sc->child_dev).
X	 */
X	child = sc->iicbb;
X	bus_generic_detach(dev);
X	if (child)
X		device_delete_child(dev, child);
X
X	return 0;
X}
X
X
X/*
X * the child detached routine.
X */
Xstatic void
Xcxm_iic_child_detached( device_t dev, device_t child )
X{
X	struct cxm_iic_softc *sc;
X
X	/* Get the device data */
X	sc = device_get_softc(dev);
X
X	if (child == sc->iicbb)
X		sc->iicbb = NULL;
X}
X
X
Xstatic int
Xcxm_iic_callback( device_t dev, int index, caddr_t *data )
X{
X	return 0;
X}
X
X
Xstatic int
Xcxm_iic_reset( device_t dev, u_char speed, u_char addr, u_char * oldaddr )
X{
X	struct cxm_iic_softc *sc;
X
X	/* Get the device data */
X	sc = (struct cxm_iic_softc *)device_get_softc(dev);
X
X	/* Set scl to 1 */
X	CSR_WRITE_4(sc, CXM_REG_I2C_SETSCL, ~(int)1);
X
X	/* Set sda to 1 */
X	CSR_WRITE_4(sc, CXM_REG_I2C_SETSDA, ~(int)1);
X
X	/*
X	 * PCI writes may be buffered so force the
X	 * write to complete by reading the last
X	 * location written.
X	 */
X
X	(void)CSR_READ_4(sc, CXM_REG_I2C_SETSDA);
X
X	/* Wait for 10 usec */
X	DELAY(10);
X
X	return IIC_ENOADDR;
X}
X
X
Xstatic int
Xcxm_iic_getscl( device_t dev )
X{
X	struct cxm_iic_softc *sc;
X
X	/* Get the device data */
X	sc = (struct cxm_iic_softc *)device_get_softc(dev);
X
X	/* Get sda */
X	return CSR_READ_1(sc, CXM_REG_I2C_GETSCL);
X}
X
X
Xstatic int
Xcxm_iic_getsda( device_t dev )
X{
X	struct cxm_iic_softc *sc;
X
X	/* Get the device data */
X	sc = (struct cxm_iic_softc *)device_get_softc(dev);
X
X	/* Get sda */
X	return CSR_READ_1(sc, CXM_REG_I2C_GETSDA);
X}
X
X
Xstatic void
Xcxm_iic_setscl( device_t dev, int val )
X{
X	struct cxm_iic_softc *sc;
X
X	/* Get the device data */
X	sc = (struct cxm_iic_softc *)device_get_softc(dev);
X
X	/* Set scl to the requested value */
X	CSR_WRITE_4(sc, CXM_REG_I2C_SETSCL, ~(int)(val ? 1 : 0));
X
X	/*
X	 * PCI writes may be buffered so force the
X	 * write to complete by reading the last
X	 * location written.
X	 */
X
X	(void)CSR_READ_4(sc, CXM_REG_I2C_SETSCL);
X}
X
X
Xstatic void
Xcxm_iic_setsda( device_t dev, int val )
X{
X	struct cxm_iic_softc *sc;
X
X	/* Get the device data */
X	sc = (struct cxm_iic_softc *)device_get_softc(dev);
X
X	/* Set sda to the requested value */
X	CSR_WRITE_4(sc, CXM_REG_I2C_SETSDA, ~(int)(val ? 1 : 0));
X
X	/*
X	 * PCI writes may be buffered so force the
X	 * write to complete by reading the last
X	 * location written.
X	 */
X
X	(void)CSR_READ_4(sc, CXM_REG_I2C_SETSDA);
X}
X
X
X#if __FreeBSD_version < 500000
Xstatic void
Xcxm_iic_setlines( device_t dev, int ctrl, int data )
X{
X
X	cxm_iic_setscl(dev, ctrl);
X	cxm_iic_setsda(dev, data);
X
X	/* Wait for 10 usec */
X	DELAY(10);
X}
X#endif
END-of-dev/cxm/cxm_i2c.c
echo x - dev/cxm/cxm_ir.c
sed 's/^X//' >dev/cxm/cxm_ir.c << 'END-of-dev/cxm/cxm_ir.c'
X/*
X * Copyright (c) 2003, 2004
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Infrared remote routines for the Conexant MPEG-2 Codec driver.
X *
X * Ideally these routines should be implemented as a separate
X * driver which has a generic infrared remote interface so that
X * it's not necessary for each multimedia driver to re-invent
X * the wheel.
X */
X
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/poll.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X#include <dev/iicbus/iicbus.h>
X
X#include "iicbb_if.h"
X
X
Xstatic int
Xcxm_ir_read( device_t iicbus, int i2c_addr, char *buf, int len )
X{
X	int received;
X
X	if (iicbus_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X        return received;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
Xint
Xcxm_ir_init( struct cxm_softc *sc )
X{
X	unsigned char key[1];
X
X	if (cxm_ir_read(sc->iicbus, CXM_I2C_IR,
X			    key, sizeof(key)) != sizeof(key))
X		return -1;
X
X	printf("%s: IR Remote\n", sc->name);
X
X	return 0;
X}
X
X
Xint
Xcxm_ir_key( struct cxm_softc *sc, char *buf, int len )
X{
X	int result;
X
X	result = cxm_ir_read(sc->iicbus, CXM_I2C_IR, buf, len);
X
X	if (result >= 0)
X		return result;
X
X	/*
X	 * If the IR receiver didn't respond,
X	 * then wait 50 ms and try again.
X	 */
X
X	tsleep(&sc->iicbus, PZERO, "IR", hz / 20 );
X
X	return cxm_ir_read(sc->iicbus, CXM_I2C_IR, buf, len);
X}
END-of-dev/cxm/cxm_ir.c
echo x - dev/cxm/cxm_tuner.c
sed 's/^X//' >dev/cxm/cxm_tuner.c << 'END-of-dev/cxm/cxm_tuner.c'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Tuner routines for the Conexant MPEG-2 Codec driver.
X *
X * Ideally these routines should be implemented as a separate
X * driver which has a generic tuner interface so that it's
X * not necessary for each multimedia driver to re-invent the
X * wheel.
X */
X
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/poll.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#if __FreeBSD_version < 503001
X#  include <machine/ioctl_meteor.h>
X#  include <machine/ioctl_bt848.h>
X#else
X#  include <dev/bktr/ioctl_meteor.h>
X#  include <dev/bktr/ioctl_bt848.h>
X#endif
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X#include <dev/iicbus/iicbus.h>
X
X#include "iicbb_if.h"
X
X
X/*
X * Channel mappings derived from
X * http://developer.apple.com/technotes/tn/tn1012.html
X *
X * B/G Cable mapping based the bktr Western European mapping
X */
X
Xstatic const struct cxm_tuner_channels
Xus_air_channels = {
X	"US Broadcast",
X	CHNLSET_NABCST,
X	CXM_TUNER_TV_SYSTEM_MN,
X	2,
X	69,
X	45750,
X	{ { 14, 471250, 6000 },
X	  { 7, 175250, 6000 },
X	  { 5, 77250, 6000 },
X	  { 2, 55250, 6000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xus_cable_channels = {
X	"US Cable",
X	CHNLSET_CABLEIRC,
X	CXM_TUNER_TV_SYSTEM_MN,
X	1,
X	125,
X	45750,
X	{ { 100, 649250, 6000 },
X	  { 95, 91250, 6000 },
X	  { 23, 217250, 6000 },
X	  { 14, 121250, 6000 },
X	  { 7, 175250, 6000 },
X	  { 5, 77250, 6000 },
X	  { 2, 55250, 6000 },
X	  { 1, 73250, 6000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xbg_air_channels = {
X	"B/G Broadcast",
X	0,
X	CXM_TUNER_TV_SYSTEM_BG,
X	2,
X	89,
X	38900,
X	{ { 82, 175250, 7000 },
X	  { 80, 55250, 7000 },
X	  { 79, 217250, 7000 },
X	  { 77, 209250, 7000 },
X	  { 76, 138250, 9000 },
X	  { 75, 102250, 9000 },
X	  { 73, 86250, 9000 },
X	  { 72, 64250, 7500 },
X	  { 70, 49750, 7500 },
X	  { 21, 471250, 8000 },
X	  { 20, 210250, 8500 },
X	  { 18, 192750, 8500 },
X	  { 16, 175250, 8000 },
X	  { 15, 82250, 8500 },
X	  { 13, 53750, 8500 },
X	  { 5, 175250, 7000 },
X	  { 2, 48250, 7000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xbg_cable_channels = {
X	"B/G Cable",
X	CHNLSET_WEUROPE,
X	CXM_TUNER_TV_SYSTEM_BG,
X	2,
X	120,
X	38900,
X	{ { 100, 303250, 8000 },
X	  { 90, 231250, 7000 },
X	  { 80, 105250, 7000 },
X	  { 79, 0, 0 },
X	  { 78, 0, 0 },
X	  { 77, 0, 0 },
X	  { 74, 69250, 7000 },
X	  { 73, 0, 0 },
X	  { 70, 45750, 8000 },
X	  { 21, 471250, 8000 },
X	  { 20, 210250, 8500 },
X	  { 18, 192750, 8500 },
X	  { 16, 175250, 8000 },
X	  { 15, 82250, 8500 },
X	  { 13, 53750, 8500 },
X	  { 5, 175250, 7000 },
X	  { 2, 48250, 7000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xbg_australia_channels = {
X	"B/G Australia",
X	CHNLSET_AUSTRALIA,
X	CXM_TUNER_TV_SYSTEM_BG,
X	1,
X	83,
X	38900,
X	{ { 28, 527250, 7000 },
X	  { 10, 209250, 7000 },
X	  { 6, 175250, 7000 },
X	  { 4, 95250, 7000 },
X	  { 3, 86250, 7000 },
X	  { 1, 57250, 7000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xi_air_channels = {
X	"I Broadcast",
X	0,
X	CXM_TUNER_TV_SYSTEM_I,
X	1,
X	83,
X	38900,
X	{ { 75, 179750, 5000 },
X	  { 71, 51750, 5000 },
X	  { 70, 45000, 5000 },
X	  { 21, 471250, 8000 },
X	  { 20, 0, 0 },
X	  { 19, 0, 0 },
X	  { 18, 0, 0 },
X	  { 17, 0, 0 },
X	  { 16, 0, 0 },
X	  { 15, 0, 0 },
X	  { 14, 0, 0 },
X	  { 13, 247250, 8000 },
X	  { 12, 0, 0 },
X	  { 4, 175250, 8000 },
X	  { 1, 45750, 8000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xl_air_channels = {
X	"L Broadcast",
X	CHNLSET_FRANCE,
X	CXM_TUNER_TV_SYSTEM_L,
X	1,
X	69,
X	38900,
X	{ { 21, 471250, 8000 },
X	  { 20, 0, 0 },
X	  { 19, 0, 0 },
X	  { 18, 0, 0 },
X	  { 17, 0, 0 },
X	  { 16, 0, 0 },
X	  { 15, 0, 0 },
X	  { 14, 0, 0 },
X	  { 8, 176000, 8000 },
X	  { 5, 57250, 3250 },
X	  { 4, 55750, 1500 },
X	  { 3, 54000, 1750 },
X	  { 2, 49250, 4750 },
X	  { 1, 47750, 1500 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xjapan_air_channels = {
X	"Japan Broadcast",
X	CHNLSET_JPNBCST,
X	CXM_TUNER_TV_SYSTEM_MN,
X	1,
X	62,
X	45750,
X	{ { 13, 471250, 6000 },
X	  { 8, 193250, 6000 },
X	  { 4, 171250, 6000 },
X	  { 1, 91250, 6000 } }
X};
X
Xstatic const struct cxm_tuner_channels
Xjapan_cable_channels = {
X	"Japan Cable",
X	CHNLSET_JPNCABLE,
X	CXM_TUNER_TV_SYSTEM_MN,
X	1,
X	63,
X	45750,
X	{ { 23, 223250, 6000 },
X	  { 22, 165250, 6000 },
X	  { 13, 109250, 6000 },
X	  { 8, 193250, 6000 },
X	  { 4, 171250, 6000 },
X	  { 1, 91250, 6000 } }
X};
X
X
Xstatic const struct cxm_tuner_channels
X*channel_sets[] = {
X	&us_air_channels,
X	&us_cable_channels,
X	&bg_air_channels,
X	&bg_cable_channels,
X	&bg_australia_channels,
X	&i_air_channels,
X	&l_air_channels,
X	&japan_air_channels,
X	&japan_cable_channels
X};
X
X
Xconst struct cxm_tuner
Xcxm_tuners[CXM_TUNER_TYPES] = {
X	{ "Philips FI1216 MK2",
X		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
X		48250, 855250,
X		{ { 450000, { 0x8e, 0x30 } },
X		  { 170000, { 0x8e, 0x90 } },
X		  { 48250, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&bg_air_channels },
X	{ "Philips FM1216",
X		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
X		48250, 855250,
X		{ { 450000, { 0xce, 0x30 } },
X		  { 170000, { 0xce, 0x90 } },
X		  { 48250, { 0xce, 0xa0 } } },
X		87500, 108000,
X		{ 87500, { 0x88, 0xa5 } },
X		&bg_air_channels },
X	{ "Philips FQ1216ME",
X		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
X		  | CXM_TUNER_TV_SYSTEM_I
X		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
X		  cxm_port_system_code_style,
X		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0x09 } },
X		    { CXM_TUNER_TV_SYSTEM_DK,      { 0x09 } },
X		    { CXM_TUNER_TV_SYSTEM_I,       { 0x01 } },
X		    { CXM_TUNER_TV_SYSTEM_L,       { 0x0b } },
X		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x0a } } } },
X		48250, 855250,
X		{ { 450000, { 0x8e, 0x30 } },
X		  { 170000, { 0x8e, 0x90 } },
X		  { 48250, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&l_air_channels },
X	{ "Philips FQ1216ME MK3",
X		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
X		  | CXM_TUNER_TV_SYSTEM_I
X		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
X		  cxm_if_system_with_aux_code_style,
X		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0x16, 0x70, 0x49, 0x40 }},
X		    { CXM_TUNER_TV_SYSTEM_DK,      { 0x16, 0x70, 0x4b, 0x40 }},
X		    { CXM_TUNER_TV_SYSTEM_I,       { 0x16, 0x70, 0x4a, 0x40 }},
X		    { CXM_TUNER_TV_SYSTEM_L,       { 0x06, 0x50, 0x4b, 0x50 }},
X		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x86, 0x50, 0x53, 0x50 }}
X		    } },
X		48250, 863250,
X		{ { 442000, { 0xce, 0x04 } },
X		  { 160000, { 0xce, 0x02 } },
X		  { 48250, { 0xce, 0x01 } } },
X		0, 0,
X		{ 0 },
X		&l_air_channels },
X	{ "Philips FM1216ME MK3",
X		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
X		  | CXM_TUNER_TV_SYSTEM_I
X		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
X		  cxm_if_system_with_aux_code_style,
X		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0x16, 0x70, 0x49, 0x40 }},
X		    { CXM_TUNER_TV_SYSTEM_DK,      { 0x16, 0x70, 0x4b, 0x40 }},
X		    { CXM_TUNER_TV_SYSTEM_I,       { 0x16, 0x70, 0x4a, 0x40 }},
X		    { CXM_TUNER_TV_SYSTEM_L,       { 0x06, 0x50, 0x4b, 0x50 }},
X		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x86, 0x50, 0x53, 0x50 }},
X		    { CXM_TUNER_FM_SYSTEM,         { 0x0a, 0x90, 0x20, 0x40 }}
X		    } },
X		48250, 863250,
X		{ { 442000, { 0xce, 0x04 } },
X		  { 160000, { 0xce, 0x02 } },
X		  { 48250, { 0xce, 0x01 } } },
X		87500, 108000,
X		{ 87500, { 0x88, 0x19 } },
X		&l_air_channels },
X	{ "Philips FI1236 MK2",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 454000, { 0x8e, 0x30 } },
X		  { 160000, { 0x8e, 0x90 } },
X		  { 55250, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&us_cable_channels },
X	{ "Philips FM1236",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 454000, { 0xce, 0x30 } },
X		  { 160000, { 0xce, 0x90 } },
X		  { 55250, { 0xce, 0xa0 } } },
X		76000, 108000,
X		{ 76000, { 0x88, 0xa5 } },
X		&us_cable_channels },
X	{ "Philips FI1246 MK2",
X		{ CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
X		45750, 855250,
X		{ { 450000, { 0x8e, 0x30 } },
X		  { 170000, { 0x8e, 0x90 } },
X		  { 45750, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&i_air_channels },
X	{ "Philips FM1246",
X		{ CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
X		45750, 855250,
X		{ { 450000, { 0xce, 0x30 } },
X		  { 170000, { 0xce, 0x90 } },
X		  { 45750, { 0xce, 0xa0 } } },
X		87500, 108000,
X		{ 87500, { 0x88, 0xa5 } },
X		&i_air_channels },
X	{ "Temic 4006 FH5",
X		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
X		48250, 855250,
X		{ { 454000, { 0x8e, 0x30 } },
X		  { 169000, { 0x8e, 0x90 } },
X		  { 48250, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&bg_air_channels },
X	{ "Temic 4009 FR5",
X		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
X		48250, 855250,
X		{ { 464000, { 0x8e, 0x30 } },
X		  { 141000, { 0x8e, 0x90 } },
X		  { 48250, { 0x8e, 0xa0 } } },
X		87500, 108100,
X		{ 87500, { 0x88, 0xa5 } },
X		&bg_air_channels },
X	{ "Temic 4036 FY5",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 453000, { 0x8e, 0x30 } },
X		  { 158000, { 0x8e, 0x90 } },
X		  { 55250, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&us_cable_channels },
X	{ "Temic 4039 FR5",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 453000, { 0x8e, 0x30 } },
X		  { 158000, { 0x8e, 0x90 } },
X		  { 55250, { 0x8e, 0xa0 } } },
X		75900, 108100,
X		{ 75900, { 0x88, 0xa5 } },
X		&us_cable_channels },
X	{ "Temic 4066 FY5",
X		{ CXM_TUNER_TV_SYSTEM_I, cxm_none_system_code_style },
X		45750, 855250,
X		{ { 454000, { 0x8e, 0x30 } },
X		  { 169000, { 0x8e, 0x90 } },
X		  { 45750, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&i_air_channels },
X	{ "LG Innotek TPI8PSB11D",
X		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
X		48250, 855250,
X		{ { 450000, { 0x8e, 0x30 } },
X		  { 170000, { 0x8e, 0x90 } },
X		  { 48250, { 0x8e, 0xa0 } } },
X		0, 0,
X		{ 0 },
X		&bg_air_channels },
X	{ "LG Innotek TPI8PSB01N",
X		{ CXM_TUNER_TV_SYSTEM_BG, cxm_none_system_code_style },
X		48250, 855250,
X		{ { 450000, { 0x8e, 0x30 } },
X		  { 170000, { 0x8e, 0x90 } },
X		  { 48250, { 0x8e, 0xa0 } } },
X		87500, 108000,
X		{ 87500, { 0x88, 0xa5 } },
X		&bg_air_channels },
X	{ "LG Innotek TAPC-H701F",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 450000, { 0xce, 0x08 } },
X		  { 165000, { 0xce, 0x02 } },
X		  { 55250, { 0xce, 0x01 } } },
X		0, 0,
X		{ 0 },
X		&us_cable_channels },
X	{ "LG Innotek TAPC-H001F",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 450000, { 0xce, 0x08 } },
X		  { 165000, { 0xce, 0x02 } },
X		  { 55250, { 0xce, 0x01 } } },
X		76000, 108000,
X		{ 76000, { 0x88, 0x04 } },
X		&us_cable_channels },
X	{ "LG Innotek TAPE-H001F",
X		{ CXM_TUNER_TV_SYSTEM_MN,
X		  cxm_if_system_with_aux_code_style,
X		  { { CXM_TUNER_TV_SYSTEM_MN,      { 0x16, 0x30, 0x44, 0x30 }},
X		    { CXM_TUNER_FM_SYSTEM,         { 0x0a, 0x90, 0x20, 0x30 }}
X		    } },
X		48250, 801250,
X		{ { 442000, { 0xce, 0x04 } },
X		  { 160000, { 0xce, 0x02 } },
X		  { 48250, { 0xce, 0x01 } } },
X		88000, 108000,
X		{ 88000, { 0x88, 0x19 } },
X		&us_cable_channels },
X	{ "Microtune 4049 FM5",
X		{ CXM_TUNER_TV_SYSTEM_BG | CXM_TUNER_TV_SYSTEM_DK
X		  | CXM_TUNER_TV_SYSTEM_I
X		  | CXM_TUNER_TV_SYSTEM_L | CXM_TUNER_TV_SYSTEM_L_PRIME,
X		  cxm_if_system_code_style,
X		  { { CXM_TUNER_TV_SYSTEM_BG,      { 0xd4, 0x70, 0x09 } },
X		    { CXM_TUNER_TV_SYSTEM_DK,      { 0xd4, 0x70, 0x0b } },
X		    { CXM_TUNER_TV_SYSTEM_I,       { 0xd4, 0x70, 0x0a } },
X		    { CXM_TUNER_TV_SYSTEM_L,       { 0xc4, 0x10, 0x0b } },
X		    { CXM_TUNER_TV_SYSTEM_L_PRIME, { 0x84, 0x10, 0x13 } },
X		    { CXM_TUNER_FM_SYSTEM,         { 0xdc, 0x10, 0x1d } } } },
X		45750, 855250,
X		{ { 464000, { 0x8e, 0x30 } },
X		  { 141000, { 0x8e, 0x90 } },
X		  { 45750, { 0x8e, 0xa0 } } },
X		87500, 108100,
X		{ 87500, { 0x88, 0xa4 } },
X		&l_air_channels },
X	{ "TCL 2002N-6A",
X		{ CXM_TUNER_TV_SYSTEM_MN, cxm_none_system_code_style },
X		55250, 801250,
X		{ { 446000, { 0x8e, 0x08 } },
X		  { 170000, { 0x8e, 0x02 } },
X		  { 55250, { 0x8e, 0x01 } } },
X		0, 0,
X		{ 0 },
X		&us_cable_channels }
X};
X
X
X/* Read from the tuner registers */
Xstatic int
Xcxm_tuner_read( device_t iicbus, int i2c_addr, char *buf, int len )
X{
X	int received;
X
X	if (iicbus_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return received;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
X/* Write to the tuner registers */
Xstatic int
Xcxm_tuner_write( device_t iicbus, int i2c_addr, const char *buf, int len )
X{
X	int sent;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return sent;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
Xint
Xcxm_tuner_init( struct cxm_softc *sc )
X{
X	unsigned char status;
X	int tuner_type;
X
X	if (cxm_eeprom_init(sc) < 0)
X		return -1;
X
X	tuner_type = cxm_eeprom_tuner_type(sc);
X
X	if (tuner_type < 0 || tuner_type >= NUM_ELEMENTS(cxm_tuners))
X		return -1;
X
X	sc->tuner = &cxm_tuners[tuner_type];
X	sc->tuner_channels = sc->tuner->default_channels;
X	sc->tuner_freq = 0;
X
X	if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER, &status, sizeof(status))
X	    != sizeof(status))
X		return -1;
X
X	if (cxm_tuner_select_channel(sc, 4) < 0)
X		return -1;
X
X	printf("%s: %s tuner\n", sc->name, sc->tuner->name);
X
X	return 0;
X}
X
X
Xint
Xcxm_tuner_select_channel_set( struct cxm_softc *sc, unsigned int channel_set )
X{
X	unsigned int i;
X
X	if (! channel_set) {
X		sc->tuner_channels = sc->tuner->default_channels;
X		return 0;
X	}
X
X	for (i = 0; i < NUM_ELEMENTS(channel_sets); i++)
X		if (channel_sets[i]->chnlset == channel_set)
X			break;
X
X	if (i >= NUM_ELEMENTS(channel_sets))
X		return -1;
X
X	if (! (sc->tuner->systems.supported & channel_sets[i]->system) )
X		return -1;
X
X	sc->tuner_channels = channel_sets[i];
X
X	return 0;
X}
X
X
Xunsigned int
Xcxm_tuner_selected_channel_set( struct cxm_softc *sc )
X{
X	return sc->tuner_channels->chnlset;
X}
X
X
Xint
Xcxm_tuner_select_frequency( struct cxm_softc *sc,
X			    enum cxm_tuner_freq_type freq_type,
X			    unsigned long freq )
X{
X	unsigned char msg[5];
X	unsigned char aux;
X	unsigned char pb;
X	unsigned int i;
X	unsigned int system;
X	unsigned int tuner_msg_len;
X	unsigned long N;
X	unsigned long osc_freq;
X	const struct cxm_tuner_band_code *band_code;
X	const struct cxm_tuner_system_code *system_code;
X
X	N = 0;
X	aux = 0;
X	pb = 0;
X
X	system_code = NULL;
X
X	tuner_msg_len = 4;
X
X	if (sc->tuner->systems.code_style != cxm_none_system_code_style) {
X		system = freq_type == cxm_tuner_fm_freq_type
X			 ? CXM_TUNER_FM_SYSTEM : sc->tuner_channels->system;
X
X		for (i = 0; i < NUM_ELEMENTS (sc->tuner->systems.codes); i++)
X			if (sc->tuner->systems.codes[i].system & system)
X				break;
X
X		if (i >= NUM_ELEMENTS (sc->tuner->systems.codes))
X			return -1;
X
X		switch (sc->tuner->systems.code_style) {
X		case cxm_port_system_code_style:
X			pb = sc->tuner->systems.codes[i].codes[0];
X			break;
X
X		case cxm_if_system_with_aux_code_style:
X			aux = sc->tuner->systems.codes[i].codes[3];
X			tuner_msg_len = 5;
X
X			/* FALLTHROUGH */
X
X		case cxm_if_system_code_style:
X			system_code = &sc->tuner->systems.codes[i];
X			break;
X
X		default:
X			return -1;
X		}
X	}
X
X	switch (freq_type) {
X	case cxm_tuner_fm_freq_type:
X
X		if (freq < sc->tuner->fm_min_freq
X		    || freq > sc->tuner->fm_max_freq
X		    || ! sc->tuner->fm_band_code.freq)
X			return -1;
X
X		/*
X		 * The Philips FM1216ME MK3 data sheet recommends
X		 * first setting the tuner to TV mode at a high
X		 * frequency (e.g. 150 MHz), then selecting the
X		 * desired FM station in order to ensure that the
X		 * tuning voltage does not stay locked at 0V.
X		 */
X
X		if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
X					       150000) < 0)
X			return -1;
X
X		/*
X		 * N = { fRF(pc) + fIF(pc) } / step_size
X		 *
X		 * fRF = RF frequency in MHz
X		 * fIF = Intermediate frequency in MHz (FM = 10.70 MHz)
X		 * step_size = Step size in MHz (FM = 50 kHz)
X		 */
X
X		osc_freq = freq + 10700;
X
X		N = (20 * osc_freq) / 1000;
X
X		msg[0] = (unsigned char)(N >> 8);
X		msg[1] = (unsigned char)N;
X		msg[2] = sc->tuner->fm_band_code.codes[0];
X		msg[3] = sc->tuner->fm_band_code.codes[1] | pb;
X		msg[4] = aux;
X		break;
X
X	case cxm_tuner_tv_freq_type:
X
X		if (freq < sc->tuner->min_freq
X		    || freq > sc->tuner->max_freq)
X			return -1;
X
X		/*
X		 * N = 16 * { fRF(pc) + fIF(pc) }
X		 *
X		 * fRF = RF frequency in MHz
X		 * fIF = Intermediate frequency in MHz
X		 *
X		 * The data sheet doesn't state it, however
X		 * this is probably the same equation as
X		 * FM simply with 62.5 kHz as the step size.
X		 */
X
X		osc_freq = freq + sc->tuner_channels->if_freq;
X
X		N = (16 * osc_freq) / 1000;
X
X		for (band_code = sc->tuner->band_codes;
X		     band_code->freq > freq; band_code++)
X			;
X
X		if (freq >= sc->tuner_freq) {
X			msg[0] = (unsigned char)(N >> 8);
X			msg[1] = (unsigned char)N;
X			msg[2] = band_code->codes[0];
X			msg[3] = band_code->codes[1] | pb;
X		}
X		else {
X			msg[0] = band_code->codes[0];
X			msg[1] = band_code->codes[1] | pb;
X			msg[2] = (unsigned char)(N >> 8);
X			msg[3] = (unsigned char)N;
X		}
X		msg[4] = aux;
X		break;
X
X	default:
X		return -1;
X	}
X
X	if (N > 32767)
X		return -1;
X
X	if (cxm_tuner_write(sc->iicbus, CXM_I2C_TUNER, msg, tuner_msg_len)
X			    != tuner_msg_len)
X		return -1;
X
X	/*
X	 * Program the IF processing after the tuner since some tuners
X	 * use the control byte to set the address of the IF.
X	 */
X
X	if (system_code) {
X		msg[0] = 0x00;
X		msg[1] = system_code->codes[0];
X		msg[2] = system_code->codes[1];
X		msg[3] = system_code->codes[2];
X
X		if (cxm_tuner_write(sc->iicbus, CXM_I2C_TUNER_IF, msg, 4) != 4)
X			return -1;
X	}
X
X	sc->tuner_freq = freq;
X
X	return 0;
X}
X
X
Xint
Xcxm_tuner_select_channel( struct cxm_softc *sc, unsigned int channel )
X{
X	unsigned long freq;
X	const struct cxm_tuner_channel_assignment *assignments;
X	const struct cxm_tuner_channels *channels;
X
X	channels = sc->tuner_channels;
X
X	if (! channels
X	    || channel < channels->min_channel
X	    || channel > channels->max_channel)
X		return -1;
X
X	for (assignments = channels->assignments;
X	     assignments->channel > channel; assignments++)
X		;
X
X	if (! assignments->freq)
X		return -1;
X
X	freq = assignments->freq
X	       + (channel - assignments->channel) * assignments->step;
X
X	return cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type, freq);
X}
X
X
Xint
Xcxm_tuner_apply_afc( struct cxm_softc *sc )
X{
X	unsigned int i;
X	int status;
X	unsigned long freq;
X	unsigned long max_offset;
X	unsigned long original_freq;
X	unsigned long prev_freq;
X	unsigned long step_size;
X
X	if (cxm_tuner_wait_for_lock(sc) != 1)
X		return -1;
X
X	original_freq = sc->tuner_freq;
X
X	freq = sc->tuner_freq;
X	prev_freq = 0;
X	max_offset = 2000;
X	step_size = 63;
X
X	for (i = 0; i < (max_offset / step_size); i++) {
X		status = cxm_tuner_status(sc);
X
X		if (status == -1)
X			break;
X
X		if (! (status & CXM_TUNER_PHASE_LOCKED) )
X			break;
X
X		switch (status & CXM_TUNER_AFC_MASK) {
X		case CXM_TUNER_AFC_FREQ_CENTERED:
X			return 0;
X
X		case CXM_TUNER_AFC_FREQ_MINUS_125:
X		case CXM_TUNER_AFC_FREQ_MINUS_62:
X			freq -= step_size;
X			break;
X
X		case CXM_TUNER_AFC_FREQ_PLUS_62:
X		case CXM_TUNER_AFC_FREQ_PLUS_125:
X			freq += step_size;
X			break;
X
X		default:
X			goto fail;
X		}
X
X		if (freq == prev_freq)
X			return 0;
X		prev_freq = sc->tuner_freq;
X
X		if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
X					       freq) < 0)
X			break;
X
X		/*
X		 * Delay long enough for the tuner to update its status.
X		 */
X
X		tsleep(&sc->iicbus, PZERO, "afc", hz / 10 );
X	}
X
Xfail:
X	(void)cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
X					 original_freq);
X	return -1;
X}
X
X
Xint
Xcxm_tuner_is_locked( struct cxm_softc *sc )
X{
X	int status;
X
X	status = cxm_tuner_status(sc);
X
X	if (status == -1)
X		return -1;
X
X	return (status & CXM_TUNER_PHASE_LOCKED) ? 1 : 0;
X}
X
X
Xint
Xcxm_tuner_wait_for_lock( struct cxm_softc *sc )
X{
X	unsigned int i;
X
X	/*
X	 * The data sheet states the maximum lock-in time
X	 * is 150 ms using fast tuning ... unfortunately
X	 * it doesn't state the maximum lock-in time using
X	 * moderate tuning.  Hopefully 300 ms is enough.
X	 */
X
X	for (i = 0; i < 3; i++) {
X
X		/*
X		 * The frequency may have just changed (prior to
X		 * cxm_tuner_wait_for_lock) so start with the delay
X		 * to give the tuner a chance to update its status.
X		 */
X
X		tsleep(&sc->iicbus, PZERO, "tuner", hz / 10);
X
X		switch (cxm_tuner_is_locked(sc)) {
X		case 1:
X			return 1;
X
X		case 0:
X			break;
X
X		default:
X			return -1;
X		}
X	}
X
X	printf("%s: tuner failed to lock\n", sc->name);
X
X	return 0;
X}
X
X
Xstatic const unsigned char afcmap[16] = {
X	CXM_TUNER_AFC_FREQ_CENTERED,
X	CXM_TUNER_AFC_FREQ_MINUS_62,
X	CXM_TUNER_AFC_FREQ_MINUS_62,
X	CXM_TUNER_AFC_FREQ_MINUS_62,
X	CXM_TUNER_AFC_FREQ_MINUS_125,
X	CXM_TUNER_AFC_FREQ_MINUS_125,
X	CXM_TUNER_AFC_FREQ_MINUS_125,
X	CXM_TUNER_AFC_FREQ_MINUS_125,
X	CXM_TUNER_AFC_FREQ_PLUS_125,
X	CXM_TUNER_AFC_FREQ_PLUS_125,
X	CXM_TUNER_AFC_FREQ_PLUS_125,
X	CXM_TUNER_AFC_FREQ_PLUS_125,
X	CXM_TUNER_AFC_FREQ_PLUS_62,
X	CXM_TUNER_AFC_FREQ_PLUS_62,
X	CXM_TUNER_AFC_FREQ_PLUS_62,
X	CXM_TUNER_AFC_FREQ_CENTERED
X};
X
Xint
Xcxm_tuner_status( struct cxm_softc *sc )
X{
X	unsigned char status;
X
X	if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER, &status, sizeof(status))
X	    != sizeof(status))
X		return -1;
X
X	if (sc->tuner->systems.code_style == cxm_if_system_code_style
X	    || sc->tuner->systems.code_style
X	       == cxm_if_system_with_aux_code_style) {
X		unsigned char if_status;
X
X		if (cxm_tuner_read(sc->iicbus, CXM_I2C_TUNER_IF,
X				   &if_status, sizeof(if_status))
X		    != sizeof(if_status))
X			return -1;
X
X		status &= ~CXM_TUNER_AFC_MASK;
X		status |= afcmap[(if_status >> 1) & 0x0f];
X	}
X
X	return status;
X}
END-of-dev/cxm/cxm_tuner.c
echo x - dev/cxm/cxm_video.c
sed 's/^X//' >dev/cxm/cxm_video.c << 'END-of-dev/cxm/cxm_video.c'
X/*
X * Copyright (c) 2003, 2004, 2005
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Video decoder routines for the Conexant MPEG-2 Codec driver.
X *
X * Ideally these routines should be implemented as a separate
X * driver which has a generic video decoder interface so that
X * it's not necessary for each multimedia driver to re-invent
X * the wheel.
X */
X 
X#include <sys/param.h>
X#include <sys/systm.h>
X#include <sys/conf.h>
X#include <sys/uio.h>
X#include <sys/kernel.h>
X#include <sys/poll.h>
X
X#if __FreeBSD_version >= 500014
X#  include <sys/selinfo.h>
X#else
X#  include <sys/select.h>
X#endif
X
X#include <machine/bus.h>
X#include <machine/clock.h>
X#include <machine/resource.h>
X
X#include <sys/bus.h>
X#include <sys/rman.h>
X
X#include <dev/cxm/cxm.h>
X
X#include <dev/iicbus/iiconf.h>
X#include <dev/iicbus/iicbus.h>
X
X#include "iicbb_if.h"
X
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_init = {
X	19,
X	{
X		/* Full auto mode for CVBS */
X		{ 0x01, 1, { 0x08 } },
X		{ 0x03, 18, { 0x20, 0x90, 0x90, 0xeb, 0xe0, 0xb0, 0x40, 0x80,
X			      0x44, 0x40, 0x00, 0x03, 0x2a, 0x06, 0x00, 0x9d,
X			      0x80, 0x01 } },
X		{ 0x17, 7, { 0x99, 0x40, 0x80, 0x77, 0x42, 0xa9, 0x01 } },
X
X		/*
X		 * VBI data slicer
X		 *
X		 * NTSC raw VBI data on lines 10 through 21
X		 * PAL raw VBI data on lines 6 through 22
X		 *
X		 * Actually lines 21 and 22 are set by the
X		 * NTSC and PAL specific configurations.
X		 */
X		{ 0x40, 20, { 0x40, 0x00, 0x00, 0x00, 0x00, 0xdd, 0xdd, 0xdd,
X			      0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
X			      0xdd, 0xdd, 0xdd, 0xdd } },
X		{ 0x56, 4, { 0x00, 0x00, 0x00, 0x47 } },
X		{ 0x5c, 3, { 0x00, 0x1f, 0x35 } },
X
X		/* I-port and X-port configuration */
X		{ 0x80, 2, { 0x00, 0x01 } },
X		{ 0x83, 5, { 0x00, 0x20, 0x21, 0xc5, 0x01 } },
X
X		/* Scaler input configuration and output format settings */
X		{ 0xc0, 4, { 0x00, 0x08, 0x00, 0x80 } },
X
X		/* VBI scaler configuration */
X		{ 0x90, 4, { 0x80, 0x48, 0x00, 0x84 } },
X		{ 0xa0, 3, { 0x01, 0x00, 0x00 } },
X		{ 0xa4, 3, { 0x80, 0x40, 0x40 } },
X		{ 0xa8, 3, { 0x00, 0x02, 0x00 } },
X		{ 0xac, 3, { 0x00, 0x01, 0x00 } },
X		{ 0xb0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X		{ 0xb8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/* Audio Master Clock to Audio Serial Clock ratio */
X		{ 0x38, 3, { 0x03, 0x10, 0x00 } },
X
X		/* PLL2 target clock 27 MHz (using a 32.11 MHz crystal) */
X		{ 0xf1, 4, { 0x05, 0xd0, 0x35, 0x00 } },
X
X		/* Pulse generator */
X		{ 0xf6, 10, { 0x61, 0x0e, 0x60, 0x0e, 0x60, 0x0e, 0x00,
X			      0x00, 0x00, 0x88 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_mute = {
X	1,
X	{
X		/* Disable I-port */
X		{ 0x87, 1, { 0x00 } },
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_unmute = {
X	1,
X	{
X		/* Enable I-port */
X		{ 0x87, 1, { 0x01 } },
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_select_fm = {
X	1,
X	{
X		/* Enable audio clock */
X		{ 0x88, 1, { 0x33 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_select_line_in_composite = {
X	3,
X	{
X		/* Amp plus anti-alias filter, CVBS from AI11 */
X		{ 0x02, 1, { 0xc0 } },
X		/* Adaptive luminance comb filter */
X		{ 0x09, 1, { 0x40 } },
X
X		/* Enable AD1, audio clock, scaler, decoder */
X		{ 0x88, 1, { 0x70 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_select_line_in_svideo = {
X	3,
X	{
X		/* Amp plus anti-alias filter, Y / C from AI11 / AI21 */
X		{ 0x02, 1, { 0xc8 } },
X		/* Bypass chrominance trap / comb filter */
X		{ 0x09, 1, { 0x80 } },
X
X		/* Enable AD1 & 2, audio clock, scaler, decoder */
X		{ 0x88, 1, { 0xf0 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_select_tuner = {
X	3,
X	{
X		/* Amp plus anti-alias filter, CVBS (auto gain) from AI23 */
X		{ 0x02, 1, { 0xc4 } },
X		/* Adaptive luminance comb filter */
X		{ 0x09, 1, { 0x40 } },
X
X		/* Enable AD2, audio clock, scaler, decoder */
X		{ 0x88, 1, { 0xb0 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_audio_clock_44100_ntsc = {
X	2,
X	{
X		/* Audio clock 44.1 kHz NTSC (using a 32.11 MHz crystal) */
X		{ 0x30, 3, { 0xbc, 0xdf, 0x02 } },
X		{ 0x34, 3, { 0xf2, 0x00, 0x2d } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_audio_clock_44100_pal = {
X	2,
X	{
X		/* Audio clock 44.1 kHz PAL (using a 32.11 MHz crystal) */
X		{ 0x30, 3, { 0x00, 0x72, 0x03 } },
X		{ 0x34, 3, { 0xf2, 0x00, 0x2d } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_audio_clock_48000_ntsc = {
X	2,
X	{
X		/* Audio clock 48 kHz NTSC (using a 32.11 MHz crystal) */
X		{ 0x30, 3, { 0xcd, 0x20, 0x03 } },
X		{ 0x34, 3, { 0xce, 0xfb, 0x30 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_audio_clock_48000_pal = {
X	2,
X	{
X		/* Audio clock 48 kHz PAL (using a 32.11 MHz crystal) */
X		{ 0x30, 3, { 0x00, 0xc0, 0x03 } },
X		{ 0x34, 3, { 0xce, 0xfb, 0x30 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_scaler_vcd_ntsc_double_lines = {
X	13,
X	{
X		/*
X		 * Input window = 720 x 240, output window = 352 x 240 with
X		 * YS extended by 2 as per section 17.4 of the data sheet
X		 * and YO accounting for scaler processing triggering at
X		 * line 5 and active video starting at line 23 (see section
X		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
X		 * NTSC active video should actually start at line 22, however
X		 * not all channels / programs do.
X		 */
X		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
X			      0x60, 0x01, 0xf0, 0x00 } },
X
X		/* Prefiltering and prescaling */
X		{ 0xd0, 3, { 0x02, 0x02, 0xaa } },
X
X		/* Brightness, contrast, and saturation */
X		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
X
X		/* Horizontal phase scaling */
X		{ 0xd8, 3, { 0x18, 0x04, 0x00 } },
X		{ 0xdc, 3, { 0x0c, 0x02, 0x00 } },
X
X		/* Vertical scaling */
X		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X
X		/* Vertical phase offsets */
X		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/*
X		 * VBI input window = 720 x 12, output window = 1440 x 12.
X		 */
X		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
X			      0xa0, 0x05, 0x0c, 0x00 } },
X
X		/* Inverted VGATE start at line 23, stop after line 263 */
X		{ 0x15, 2, { 0x02, 0x12 } },
X
X		/* VBI data slicer 525 lines, line 21 is closed caption */
X		{ 0x54, 2, { 0x4d, 0x00 } },
X		{ 0x5a, 2, { 0x06, 0x83 } },
X
X		/* PLL2 525 lines, 27 Mhz target clock */
X		{ 0xf0, 1, { 0xad } },
X
X		/* Pulse generator 525 lines, 27 Mhz target clock */
X		{ 0xf5, 1, { 0xad } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_scaler_vcd_pal_double_lines = {
X	13,
X	{
X		/*
X		 * Input window = 720 x 288, output window = 352 x 288 with
X		 * YS extended by 2 as per section 17.4 of the data sheet
X		 * and YO accounting for scaler processing triggering at
X		 * line 2 and active video starting at line 25 (see section
X		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
X		 * PAL active video should actually start at line 24, however
X		 * not all channels / programs do.
X		 */
X		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
X			      0x60, 0x01, 0x20, 0x01 } },
X
X		/* Prefiltering and prescaling */
X		{ 0xd0, 3, { 0x02, 0x02, 0xaa } },
X
X		/* Brightness, contrast, and saturation */
X		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
X
X		/* Horizontal phase scaling */
X		{ 0xd8, 3, { 0x18, 0x04, 0x00 } },
X		{ 0xdc, 3, { 0x0c, 0x02, 0x00 } },
X
X		/* Vertical scaling */
X		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X
X		/* Vertical phase offsets */
X		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/*
X		 * VBI input window = 720 x 17, output window = 1440 x 17.
X		 */
X		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
X			      0xa0, 0x05, 0x11, 0x00 } },
X
X		/* Inverted VGATE start at line 25, stop after line 313 */
X		{ 0x15, 2, { 0x37, 0x17 } },
X
X		/* VBI data slicer 625 lines, line 22 is closed caption */
X		{ 0x54, 2, { 0xdd, 0x4d } },
X		{ 0x5a, 2, { 0x03, 0x03 } },
X
X		/* PLL2 625 lines, 27 Mhz target clock */
X		{ 0xf0, 1, { 0xb0 } },
X
X		/* Pulse generator 625 lines, 27 Mhz target clock */
X		{ 0xf5, 1, { 0xb0 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_scaler_svcd_ntsc = {
X	13,
X	{
X		/*
X		 * Input window = 720 x 240, output window = 480 x 240 with
X		 * YS extended by 2 as per section 17.4 of the data sheet
X		 * and YO accounting for scaler processing triggering at
X		 * line 5 and active video starting at line 23 (see section
X		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
X		 * NTSC active video should actually start at line 22, however
X		 * not all channels / programs do.
X		 */
X		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
X			      0xe0, 0x01, 0xf0, 0x00 } },
X
X		/* Prefiltering and prescaling */
X		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
X
X		/* Brightness, contrast, and saturation */
X		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
X
X		/* Horizontal phase scaling */
X		{ 0xd8, 3, { 0x00, 0x06, 0x00 } },
X		{ 0xdc, 3, { 0x00, 0x03, 0x00 } },
X
X		/* Vertical scaling */
X		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X
X		/* Vertical phase offsets */
X		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/*
X		 * VBI input window = 720 x 12, output window = 1440 x 12.
X		 */
X		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
X			      0xa0, 0x05, 0x0c, 0x00 } },
X
X		/* Inverted VGATE start at line 23, stop after line 263 */
X		{ 0x15, 2, { 0x02, 0x12 } },
X
X		/* VBI data slicer 525 lines, line 21 is closed caption */
X		{ 0x54, 2, { 0x4d, 0x00 } },
X		{ 0x5a, 2, { 0x06, 0x83 } },
X
X		/* PLL2 525 lines, 27 Mhz target clock */
X		{ 0xf0, 1, { 0xad } },
X
X		/* Pulse generator 525 lines, 27 Mhz target clock */
X		{ 0xf5, 1, { 0xad } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_scaler_svcd_pal = {
X	13,
X	{
X		/*
X		 * Input window = 720 x 288, output window = 480 x 288 with
X		 * YS extended by 2 as per section 17.4 of the data sheet
X		 * and YO accounting for scaler processing triggering at
X		 * line 2 and active video starting at line 25 (see section
X		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
X		 * PAL active video should actually start at line 24, however
X		 * not all channels / programs do.
X		 */
X		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
X			      0xe0, 0x01, 0x20, 0x01 } },
X
X		/* Prefiltering and prescaling */
X		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
X
X		/* Brightness, contrast, and saturation */
X		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
X
X		/* Horizontal phase scaling */
X		{ 0xd8, 3, { 0x00, 0x06, 0x00 } },
X		{ 0xdc, 3, { 0x00, 0x03, 0x00 } },
X
X		/* Vertical scaling */
X		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X
X		/* Vertical phase offsets */
X		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/*
X		 * VBI input window = 720 x 17, output window = 1440 x 17.
X		 */
X		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
X			      0xa0, 0x05, 0x11, 0x00 } },
X
X		/* Inverted VGATE start at line 25, stop after line 313 */
X		{ 0x15, 2, { 0x37, 0x17 } },
X
X		/* VBI data slicer 625 lines, line 22 is closed caption */
X		{ 0x54, 2, { 0xdd, 0x4d } },
X		{ 0x5a, 2, { 0x03, 0x03 } },
X
X		/* PLL2 625 lines, 27 Mhz target clock */
X		{ 0xf0, 1, { 0xb0 } },
X
X		/* Pulse generator 625 lines, 27 Mhz target clock */
X		{ 0xf5, 1, { 0xb0 } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_scaler_dvd_ntsc = {
X	13,
X	{
X		/*
X		 * Input window = 720 x 240, output window = 720 x 240 with
X		 * YS extended by 2 as per section 17.4 of the data sheet
X		 * and YO accounting for scaler processing triggering at
X		 * line 5 and active video starting at line 23 (see section
X		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
X		 * NTSC active video should actually start at line 22, however
X		 * not all channels / programs do.
X		 */
X		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x12, 0x00, 0xf2, 0x00,
X			      0xd0, 0x02, 0xf0, 0x00 } },
X
X		/* Prefiltering and prescaling */
X		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
X
X		/* Brightness, contrast, and saturation */
X		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
X
X		/* Horizontal phase scaling */
X		{ 0xd8, 3, { 0x00, 0x04, 0x00 } },
X		{ 0xdc, 3, { 0x00, 0x02, 0x00 } },
X
X		/* Vertical scaling */
X		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X
X		/* Vertical phase offsets */
X		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/*
X		 * VBI input window = 720 x 12, output window = 1440 x 12.
X		 */
X		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x05, 0x00, 0x0c, 0x00,
X			      0xa0, 0x05, 0x0c, 0x00 } },
X
X		/* Inverted VGATE start at line 23, stop after line 263 */
X		{ 0x15, 2, { 0x02, 0x12 } },
X
X		/* VBI data slicer 525 lines, line 21 is closed caption */
X		{ 0x54, 2, { 0x4d, 0x00 } },
X		{ 0x5a, 2, { 0x06, 0x83 } },
X
X		/* PLL2 525 lines, 27 Mhz target clock */
X		{ 0xf0, 1, { 0xad } },
X
X		/* Pulse generator 525 lines, 27 Mhz target clock */
X		{ 0xf5, 1, { 0xad } }
X	}
X};
X
Xstatic const struct cxm_saa7115_command
Xsaa7115_scaler_dvd_pal = {
X	13,
X	{
X		/*
X		 * Input window = 720 x 288, output window = 720 x 288 with
X		 * YS extended by 2 as per section 17.4 of the data sheet
X		 * and YO accounting for scaler processing triggering at
X		 * line 2 and active video starting at line 25 (see section
X		 * 8.2 table 8 and section 8.3.1.1 table 11 of the data sheet).
X		 * PAL active video should actually start at line 24, however
X		 * not all channels / programs do.
X		 */
X		{ 0xc4, 12, { 0x02, 0x00, 0xd0, 0x02, 0x17, 0x00, 0x22, 0x01,
X			      0xd0, 0x02, 0x20, 0x01 } },
X
X		/* Prefiltering and prescaling */
X		{ 0xd0, 3, { 0x01, 0x00, 0x00 } },
X
X		/* Brightness, contrast, and saturation */
X		{ 0xd4, 3, { 0x80, 0x40, 0x40 } },
X
X		/* Horizontal phase scaling */
X		{ 0xd8, 3, { 0x00, 0x04, 0x00 } },
X		{ 0xdc, 3, { 0x00, 0x02, 0x00 } },
X
X		/* Vertical scaling */
X		{ 0xe0, 5, { 0x00, 0x04, 0x00, 0x04, 0x00 } },
X
X		/* Vertical phase offsets */
X		{ 0xe8, 8, { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }},
X
X		/*
X		 * VBI input window = 720 x 17, output window = 1440 x 17.
X		 */
X		{ 0x94, 12, { 0x02, 0x00, 0xd0, 0x02, 0x04, 0x00, 0x11, 0x00,
X			      0xa0, 0x05, 0x11, 0x00 } },
X
X		/* Inverted VGATE start at line 25, stop after line 313 */
X		{ 0x15, 2, { 0x37, 0x17 } },
X
X		/* VBI data slicer 625 lines, line 22 is closed caption */
X		{ 0x54, 2, { 0xdd, 0x4d } },
X		{ 0x5a, 2, { 0x03, 0x03 } },
X
X		/* PLL2 625 lines, 27 Mhz target clock */
X		{ 0xf0, 1, { 0xb0 } },
X
X		/* Pulse generator 625 lines, 27 Mhz target clock */
X		{ 0xf5, 1, { 0xb0 } }
X	}
X};
X
X
Xstatic const struct cxm_saa7115_audio_clock
Xsaa7115_audio_clock[] = {
X	{ 44100, 30, &saa7115_audio_clock_44100_ntsc },
X	{ 44100, 25, &saa7115_audio_clock_44100_pal },
X	{ 48000, 30, &saa7115_audio_clock_48000_ntsc },
X	{ 48000, 25, &saa7115_audio_clock_48000_pal }
X};
X
Xstatic const struct cxm_saa7115_scaling
Xsaa7115_scalings[] = {
X	{ 352, 480, 30, &saa7115_scaler_vcd_ntsc_double_lines },
X	{ 352, 576, 25, &saa7115_scaler_vcd_pal_double_lines },
X	{ 480, 480, 30, &saa7115_scaler_svcd_ntsc },
X	{ 480, 576, 25, &saa7115_scaler_svcd_pal },
X	{ 720, 480, 30, &saa7115_scaler_dvd_ntsc },
X	{ 720, 576, 25, &saa7115_scaler_dvd_pal }
X};
X
X
X/* Reset the SAA7115 chip */
Xstatic int
Xcxm_saa7115_reset( device_t iicbus, int i2c_addr )
X{
X	unsigned char msg[2];
X	int sent;
X
X	/* put into reset mode */
X	msg[0] = 0x88;
X	msg[1] = 0x0b;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	/* put back to operational mode */
X	msg[0] = 0x88;
X	msg[1] = 0x2b;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return 0;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
X/* Read from the SAA7115 registers */
Xstatic int
Xcxm_saa7115_read( device_t iicbus, int i2c_addr,
X		  unsigned char addr, char *buf, int len )
X{
X	unsigned char msg[1];
X	int received;
X	int sent;
X
X	msg[0] = addr;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	if (iicbus_repeated_start(iicbus, i2c_addr + 1, CXM_I2C_TIMEOUT) != 0)
X		goto fail;
X
X	if (iicbus_read(iicbus, buf, len, &received, IIC_LAST_READ, 0) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return received;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
X/* Write to the SAA7115 registers */
Xstatic int
Xcxm_saa7115_write( device_t iicbus, int i2c_addr,
X		   unsigned char addr, const char *buf, int len )
X{
X	unsigned char msg[1];
X	int sent;
X
X	msg[0] = addr;
X
X	if (iicbus_start(iicbus, i2c_addr, CXM_I2C_TIMEOUT) != 0)
X		return -1;
X
X	if (iicbus_write(iicbus, msg, sizeof(msg), &sent, CXM_I2C_TIMEOUT) != 0
X	    || sent != sizeof(msg))
X		goto fail;
X
X	if (iicbus_write(iicbus, buf, len, &sent, CXM_I2C_TIMEOUT) != 0)
X		goto fail;
X
X	iicbus_stop(iicbus);
X
X	return sent;
X
Xfail:
X	iicbus_stop(iicbus);
X	return -1;
X}
X
X
Xint
Xcxm_saa7115_init( struct cxm_softc *sc )
X{
X	char name[5];
X	unsigned char id[1];
X	unsigned char rev;
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_saa7115_setting *settings;
X
X	if (cxm_saa7115_reset (sc->iicbus, CXM_I2C_SAA7115) < 0)
X		return -1;
X
X	name[4] = '\0';
X	for (i = 0; i < 4; i++) {
X		id[0] = 2 + i;
X
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x00,
X				      id, sizeof(id)) != sizeof(id))
X			return -1;
X
X		if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x00,
X				     id, sizeof(id)) != sizeof(id))
X			return -1;
X
X		name[i] = '0' + (id[0] & 0x0f);
X		rev = id[0] >> 4;
X	}
X
X	/*
X	 * SAA 7115 is the only video decoder currently supported.
X	 */
X
X	nsettings = 0;
X	settings = NULL;
X
X	if (strcmp(name, "7115") == 0) {
X		nsettings = saa7115_init.nsettings;
X		settings = saa7115_init.settings;
X	  }
X	else {
X		printf("%s: unknown video decoder SAA%s\n",
X		       sc->name, name);
X		return -1;
X	}
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
X				      settings[i].addr,
X				      settings[i].values, settings[i].nvalues)
X		    != settings[i].nvalues)
X			return -1;
X
X	if (cxm_saa7115_select_source(sc, cxm_tuner_source) < 0)
X		return -1;
X
X	printf("%s: SAA%s rev %u video decoder\n",
X	       sc->name, name, (unsigned int)rev);
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_mute( struct cxm_softc *sc )
X{
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_saa7115_setting *settings;
X
X	nsettings = saa7115_mute.nsettings;
X	settings = saa7115_mute.settings;
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
X				      settings[i].addr,
X				      settings[i].values, settings[i].nvalues)
X		    != settings[i].nvalues)
X			return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_unmute( struct cxm_softc *sc )
X{
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_saa7115_setting *settings;
X
X	nsettings = saa7115_unmute.nsettings;
X	settings = saa7115_unmute.settings;
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
X				      settings[i].addr,
X				      settings[i].values, settings[i].nvalues)
X		    != settings[i].nvalues)
X			return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_select_source( struct cxm_softc *sc, enum cxm_source source )
X{
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_saa7115_setting *settings;
X
X	switch (source) {
X	case cxm_fm_source:
X		nsettings = saa7115_select_fm.nsettings;
X		settings = saa7115_select_fm.settings;
X		break;
X
X	case cxm_line_in_source_composite:
X		nsettings = saa7115_select_line_in_composite.nsettings;
X		settings = saa7115_select_line_in_composite.settings;
X		break;
X
X	case cxm_line_in_source_svideo:
X		nsettings = saa7115_select_line_in_svideo.nsettings;
X		settings = saa7115_select_line_in_svideo.settings;
X		break;
X
X	case cxm_tuner_source:
X		nsettings = saa7115_select_tuner.nsettings;
X		settings = saa7115_select_tuner.settings;
X		break;
X
X	default:
X		return -1;
X	}
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
X				      settings[i].addr,
X				      settings[i].values, settings[i].nvalues)
X		    != settings[i].nvalues)
X			return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_configure( struct cxm_softc *sc,
X		       unsigned int width, unsigned int height,
X		       unsigned int fps, unsigned int audio_sample_rate )
X{
X	unsigned char power[1];
X	unsigned char task[1];
X	unsigned int i;
X	unsigned int nsettings;
X	const struct cxm_saa7115_setting *settings;
X
X	for (i = 0; NUM_ELEMENTS(saa7115_scalings); i++)
X		if (saa7115_scalings[i].width == width
X		    && saa7115_scalings[i].height == height
X		    && saa7115_scalings[i].fps == fps)
X			break;
X
X	if (i >= NUM_ELEMENTS(saa7115_scalings))
X		return -1;
X
X	nsettings = saa7115_scalings[i].scaling->nsettings;
X	settings = saa7115_scalings[i].scaling->settings;
X
X	/*
X	 * Reset the scaler.
X	 */
X
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x88,
X			     power, sizeof(power)) != sizeof(power))
X		return -1;
X
X	power[0] &= ~0x20;
X
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x88,
X			      power, sizeof(power)) != sizeof(power))
X		return -1;
X
X	/*
X	 * Configure the scaler.
X	 */
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
X				      settings[i].addr,
X				      settings[i].values, settings[i].nvalues)
X		    != settings[i].nvalues)
X			return -1;
X
X	/*
X	 * Enable task register set A and B.
X	 */
X
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x80,
X			     task, sizeof(task)) != sizeof(task))
X		return -1;
X
X	task[0] |= 0x30;
X
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x80,
X			      task, sizeof(task)) != sizeof(task))
X		return -1;
X
X	/*
X	 * Enable the scaler.
X	 */
X
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x88,
X			     power, sizeof(power)) != sizeof(power))
X		return -1;
X
X	power[0] |= 0x20;
X
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x88,
X			      power, sizeof(power)) != sizeof(power))
X		return -1;
X
X	/*
X	 * Configure the audio clock.
X	 */
X
X	for (i = 0; NUM_ELEMENTS(saa7115_audio_clock); i++)
X		if (saa7115_audio_clock[i].sample_rate == audio_sample_rate
X		    && saa7115_audio_clock[i].fps == fps)
X			break;
X
X	if (i >= NUM_ELEMENTS(saa7115_audio_clock))
X		return -1;
X
X	nsettings = saa7115_audio_clock[i].clock->nsettings;
X	settings = saa7115_audio_clock[i].clock->settings;
X
X	for (i = 0; i < nsettings; i++)
X		if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115,
X				      settings[i].addr,
X				      settings[i].values, settings[i].nvalues)
X		    != settings[i].nvalues)
X			return -1;
X
X	return 0;
X}
X
X
Xenum cxm_source_format
Xcxm_saa7115_detected_format( struct cxm_softc *sc )
X{
X	unsigned char status[2];
X	enum cxm_source_format source_format;
X
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1e,
X			     status, sizeof(status)) != sizeof(status))
X		return cxm_unknown_source_format;
X
X	if (! (status[1] & 0x01) ) {
X		printf("%s: video decoder isn't locked\n", sc->name);
X		return cxm_unknown_source_format;
X	}
X
X	source_format = cxm_unknown_source_format;
X
X	if (! (status[1] & 0x20) )
X		switch (status[0] & 0x03) {
X		case 0:
X			source_format = cxm_bw_50hz_source_format;
X			break;
X
X		case 1:
X			source_format = cxm_ntsc_50hz_source_format;
X			break;
X
X		case 2:
X			source_format = cxm_pal_50hz_source_format;
X			break;
X
X		case 3:
X			source_format = cxm_secam_50hz_source_format;
X			break;
X
X		default:
X			break;
X		}
X	else
X		switch (status[0] & 0x03) {
X		case 0:
X			source_format = cxm_bw_60hz_source_format;
X			break;
X
X		case 1:
X			source_format = cxm_ntsc_60hz_source_format;
X			break;
X
X		case 2:
X			source_format = cxm_pal_60hz_source_format;
X			break;
X
X		default:
X			break;
X		}
X
X	return source_format;
X}
X
X
Xint
Xcxm_saa7115_detected_fps( struct cxm_softc *sc )
X{
X	unsigned char status[1];
X
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1f,
X			     status, sizeof(status)) != sizeof(status))
X		return -1;
X
X	if (! (status[0] & 0x01) ) {
X		printf("%s: video decoder isn't locked\n", sc->name);
X		return -1;
X	}
X
X	return (status[0] & 0x20) ? 30 : 25;
X}
X
X
Xint
Xcxm_saa7115_get_brightness( struct cxm_softc *sc )
X{
X	unsigned char brightness;
X
X	/*
X	 * Brightness is treated as an unsigned value by the decoder.
X	 * 0 = dark, 128 = ITU level, 255 = bright
X	 */
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0a,
X			     &brightness, sizeof(brightness))
X	    != sizeof(brightness))
X		return -1;
X
X	return brightness;
X}
X
X
Xint
Xcxm_saa7115_set_brightness( struct cxm_softc *sc, unsigned char brightness )
X{
X
X	/*
X	 * Brightness is treated as an unsigned value by the decoder.
X	 * 0 = dark, 128 = ITU level, 255 = bright
X	 */
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0a,
X			      &brightness, sizeof(brightness))
X	    != sizeof(brightness))
X		return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_get_chroma_saturation( struct cxm_softc *sc )
X{
X	unsigned char chroma_saturation;
X
X	/*
X	 * Chroma saturation is treated as a signed value by the decoder.
X	 * -128 = -2.0 (inverse chrominance), -64 = 1.0 (inverse chrominance),
X	 * 0 = 0 (color off), 64 = 1.0 (ITU level), 127 = 1.984 (maximum)
X	 */
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0c,
X			     &chroma_saturation, sizeof(chroma_saturation))
X	    != sizeof(chroma_saturation))
X		return -1;
X
X	return chroma_saturation;
X}
X
X
Xint
Xcxm_saa7115_set_chroma_saturation( struct cxm_softc *sc,
X				   unsigned char chroma_saturation )
X{
X
X	/*
X	 * Chroma saturation is treated as a signed value by the decoder.
X	 * -128 = -2.0 (inverse chrominance), -64 = 1.0 (inverse chrominance),
X	 * 0 = 0 (color off), 64 = 1.0 (ITU level), 127 = 1.984 (maximum)
X	 */
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0c,
X			      &chroma_saturation, sizeof(chroma_saturation))
X	    != sizeof(chroma_saturation))
X		return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_get_contrast( struct cxm_softc *sc )
X{
X	unsigned char contrast;
X
X	/*
X	 * Contrast is treated as a signed value by the decoder.
X	 * -128 = -2.0 (inverse luminance), -64 = 1.0 (inverse luminance),
X	 * 0 = 0 (luminance off), 64 = 1.0, 68 = 1.063 (ITU level),
X	 * 127 = 1.984 (maximum)
X	 */
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0b,
X			     &contrast, sizeof(contrast)) != sizeof(contrast))
X		return -1;
X
X	return contrast;
X}
X
X
Xint
Xcxm_saa7115_set_contrast( struct cxm_softc *sc, unsigned char contrast )
X{
X
X	/*
X	 * Contrast is treated as a signed value by the decoder.
X	 * -128 = -2.0 (inverse luminance), -64 = 1.0 (inverse luminance),
X	 * 0 = 0 (luminance off), 64 = 1.0, 68 = 1.063 (ITU level),
X	 * 127 = 1.984 (maximum)
X	 */
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0b,
X			      &contrast, sizeof(contrast)) != sizeof(contrast))
X		return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_get_hue( struct cxm_softc *sc )
X{
X	unsigned char hue;
X
X	/*
X	 * Hue is treated as a signed value by the decoder.
X	 * -128 = -180.0, 0 = 0.0, 127 = +178.6
X	 */
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x0d,
X			     &hue, sizeof(hue))
X	    != sizeof(hue))
X		return -1;
X
X	return hue;
X}
X
X
Xint
Xcxm_saa7115_set_hue( struct cxm_softc *sc, unsigned char hue )
X{
X
X	/*
X	 * Hue is treated as a signed value by the decoder.
X	 * -128 = -180.0, 0 = 0.0, 127 = +178.6
X	 */
X	if (cxm_saa7115_write(sc->iicbus, CXM_I2C_SAA7115, 0x0d,
X			      &hue, sizeof(hue))
X	    != sizeof(hue))
X		return -1;
X
X	return 0;
X}
X
X
Xint
Xcxm_saa7115_is_locked( struct cxm_softc *sc )
X{
X	unsigned char status[1];
X
X	if (cxm_saa7115_read(sc->iicbus, CXM_I2C_SAA7115, 0x1f,
X			     status, sizeof(status)) != sizeof(status))
X		return -1;
X
X	return (status[0] & 0x01) ? 1 : 0;
X}
X
X
Xint
Xcxm_saa7115_wait_for_lock( struct cxm_softc *sc )
X{
X	unsigned int i;
X
X	/*
X	 * Section 2.7 of the data sheet states:
X	 *
X	 *   Ultra-fast frame lock (almost 1 field)
X	 *
X	 * so hopefully 500 ms is enough (the lock
X	 * sometimes takes a long time to occur ...
X	 * possibly due to the time it takes to
X	 * autodetect the format).
X	 */
X
X	for (i = 0; i < 10; i++) {
X
X		/*
X		 * The input may have just changed (prior to
X		 * cxm_saa7115_wait_for_lock) so start with
X		 * the delay to give the video decoder a
X		 * chance to update its status.
X		 */
X
X		tsleep(&sc->iicbus, PZERO, "video", hz / 20 );
X
X		switch (cxm_saa7115_is_locked(sc)) {
X		case 1:
X			return 1;
X
X		case 0:
X			break;
X
X		default:
X			return -1;
X		}
X	}
X
X	printf("%s: video decoder failed to lock\n", sc->name);
X
X	return 0;
X}
END-of-dev/cxm/cxm_video.c
echo x - dev/cxm/cxm_extract_fw.c
sed 's/^X//' >dev/cxm/cxm_extract_fw.c << 'END-of-dev/cxm/cxm_extract_fw.c'
X/*
X * Copyright (c) 2003
X *	John Wehle <john@feith.com>.  All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *	This product includes software developed by John Wehle.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission.
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.	IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
X * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
X * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
X * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
X * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
X * POSSIBILITY OF SUCH DAMAGE.
X */
X
X/*
X * Conexant MPEG-2 Codec firmware extraction program.
X *
X * Generates cxm_dec_fw.c and cxm_enc_fw.c from the
X * Hauppauge PVR-250 / PVR-350 Microsoft Windows driver
X * (i.e. hcwpvrp2.sys).
X *
X * This was written using the invaluable information
X * compiled by The IvyTV Project (ivtv.sourceforge.net).
X */
X
X#include <fcntl.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <sys/types.h>
X#include <sys/mman.h>
X#include <sys/param.h>
X#include <sys/stat.h>
X
X
Xconst char *MyName = "cxm_extract_fw";
X
Xconst u_int8_t decoder_magic[] = {
X  0xa7, 0x03, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa };
Xconst u_int8_t encoder_magic[] = {
X  0xa7, 0x0d, 0x00, 0x00, 0x66, 0xbb, 0x55, 0xaa };
X
X
Xstatic int
Xsave_firmware (const char *name, const u_int8_t *buf, size_t nbytes)
X  {
X  FILE *ofp;
X  char outfile[MAXPATHLEN];
X  size_t i;
X
X  if (nbytes < (256 * 1024)) {
X    fprintf (stderr, "%s: save_firmware -- firmware image isn't long enough\n",
X             MyName);
X    return -1;
X    }
X
X  if (snprintf (outfile, sizeof (outfile), "%s.c", name) >= sizeof (outfile)) {
X    fprintf (stderr, "%s: save_firmware -- firmware name is too long\n",
X             MyName);
X    return -1;
X    }
X
X  if (! (ofp = fopen (outfile, "w")) ) {
X    fprintf (stderr, "%s: save_firmware -- can't open output file <%s>\n",
X             MyName, outfile);
X    perror (MyName);
X    return -1;
X    }
X
X  fprintf (ofp, "#include <sys/types.h>\n"
X                "\n"
X                "const u_int8_t %s[] __attribute__ ((aligned(4))) = {",
X           name);
X
X  for (i = 0; i < (256 * 1024); i++) {
X    if (i)
X      fputc (',', ofp);
X    if ((i % 8) == 0)
X      fputs ("\n\t", ofp);
X    else
X      fputc (' ', ofp);
X    fprintf (ofp, "0x%.2x", buf[i]);
X    }
X
X  fprintf (ofp, "\n};\n");
X
X  if (ferror (ofp)) {
X    fclose (ofp);
X    return -1;
X    }
X
X  fclose (ofp);
X  return 0;
X  }
X
X
Xint
Xmain (int argc, char **argv)
X  {
X  const char *infile;
X  const u_int8_t *end;
X  const u_int8_t *ptr;
X  const u_int8_t *start;
X  int decoder_fw_saved;
X  int encoder_fw_saved;
X  int fd;
X  struct stat statbuf;
X
X  if (argc != 2) {
X    fprintf (stderr, "Usage: %s <firmware file> \n", MyName);
X    exit (1);
X    }
X
X  infile = argv[1];
X
X  /*
X   * Open the file.
X   */
X
X  if ((fd = open (infile, O_RDONLY)) < 0) {
X    fprintf (stderr, "%s: can't open %s for reading\n", MyName, infile);
X    perror (MyName);
X    exit (1);
X    }
X
X  /*
X   * Determine how big it is.
X   */
X
X  if (fstat (fd, &statbuf) < 0) {
X    fprintf (stderr, "%s: can't fstat %s\n", MyName, infile);
X    perror (MyName);
X    close (fd);
X    exit (1);
X    }
X
X  /*
X   * Map it into memory.
X   */
X
X  if ( !(start = (u_int8_t *)mmap (NULL, (size_t)statbuf.st_size,
X                                   PROT_READ, MAP_SHARED, fd, (off_t)0)) ) {
X    fprintf (stderr, "%s: can't mmap %s\n", MyName, infile);
X    perror (MyName);
X    close (fd);
X    exit (1);
X    }
X
X  end = start + statbuf.st_size;
X
X  close (fd);
X
X  decoder_fw_saved = 0;
X  encoder_fw_saved = 0;
X
X  for (ptr = start; ptr != end; ptr++) {
X    if ((end - ptr) >= sizeof (decoder_magic)
X        && memcmp (ptr, decoder_magic, sizeof (decoder_magic)) == 0)
X      if (! decoder_fw_saved) {
X        if (save_firmware ("cxm_dec_fw", ptr, end - ptr) < 0) {
X          fprintf (stderr, "%s: save_firmware failed\n", MyName);
X          exit (1);
X          }
X        decoder_fw_saved = 1;
X        }
X      else {
X        fprintf (stderr, "%s: multiple decoder images present\n", MyName);
X        exit (1);
X        }
X
X    if ((end - ptr) >= sizeof (encoder_magic)
X        && memcmp (ptr, encoder_magic, sizeof (encoder_magic)) == 0)
X      if (! encoder_fw_saved) {
X        if (save_firmware ("cxm_enc_fw", ptr, end - ptr) < 0) {
X          fprintf (stderr, "%s: save_firmware failed\n", MyName);
X          exit (1);
X          }
X        encoder_fw_saved = 1;
X        }
X      else {
X        fprintf (stderr, "%s: multiple encoder images present\n", MyName);
X        exit (1);
X        }
X    }
X
X  munmap ((caddr_t)start, (size_t)statbuf.st_size);
X
X  if (! decoder_fw_saved)
X    fprintf (stderr, "%s: decoder image not present\n", MyName);
X
X  if (! encoder_fw_saved)
X    fprintf (stderr, "%s: encoder image not present\n", MyName);
X
X  if (! decoder_fw_saved || ! encoder_fw_saved)
X    exit (1);
X
X  exit (0);
X  }
END-of-dev/cxm/cxm_extract_fw.c
echo c - modules/cxm
mkdir -p modules/cxm > /dev/null 2>&1
echo x - modules/cxm/Makefile
sed 's/^X//' >modules/cxm/Makefile << 'END-of-modules/cxm/Makefile'
XSUBDIR =
XSUBDIR += cxm
XSUBDIR += cxm_iic
X
X.include <bsd.subdir.mk>
END-of-modules/cxm/Makefile
echo c - modules/cxm/cxm
mkdir -p modules/cxm/cxm > /dev/null 2>&1
echo x - modules/cxm/cxm/Makefile
sed 's/^X//' >modules/cxm/cxm/Makefile << 'END-of-modules/cxm/cxm/Makefile'
X.PATH:	${.CURDIR}/../../../dev/cxm
XKMOD	= cxm
XSRCS	= cxm.c cxm.h cxm_dec_fw.c cxm_enc_fw.c cxm_audio.c cxm_eeprom.c \
X	  cxm_ir.c cxm_tuner.c cxm_video.c opt_cxm.h \
X	  bus_if.h device_if.h iicbb_if.h pci_if.h vnode_if.h
X
X.include <bsd.kmod.mk>
END-of-modules/cxm/cxm/Makefile
echo c - modules/cxm/cxm_iic
mkdir -p modules/cxm/cxm_iic > /dev/null 2>&1
echo x - modules/cxm/cxm_iic/Makefile
sed 's/^X//' >modules/cxm/cxm_iic/Makefile << 'END-of-modules/cxm/cxm_iic/Makefile'
X.PATH:	${.CURDIR}/../../../dev/cxm
XKMOD	= cxm_iic
XSRCS	= cxm_i2c.c cxm.h \
X	  opt_cxm.h bus_if.h device_if.h iicbb_if.h pci_if.h
X
X.include <bsd.kmod.mk>
END-of-modules/cxm/cxm_iic/Makefile
exit
