summaryrefslogtreecommitdiff
path: root/hw
diff options
context:
space:
mode:
authorVladimir Dergachev <volodya@mindspring.com>2004-09-30 22:58:07 +0000
committerVladimir Dergachev <volodya@mindspring.com>2004-09-30 22:58:07 +0000
commit0d474149f1cb68a60927529f6eac611a12acf5e6 (patch)
tree5a33d2449547355d6fd9cee21f470c663c5cc14c /hw
parent1dfafe2aeec864a9bdfd6da3324243b2be8e3a62 (diff)
Initial code from GATOS. This needs to be cleaned up, for example the bt829
code is practically untouched since xatitv (which was a standalone test program). However, it all worked and was debugged over long period of time, so I prefer to to mess with these for now. New drivers: fi12xx (including MT2032 - this would be be split off later). tdaXXX msp34xx bt8xx
Diffstat (limited to 'hw')
-rw-r--r--hw/xfree86/i2c/bt829.c737
-rw-r--r--hw/xfree86/i2c/bt829.h115
-rw-r--r--hw/xfree86/i2c/bt829_module.c26
-rw-r--r--hw/xfree86/i2c/fi1236.c550
-rw-r--r--hw/xfree86/i2c/fi1236.h116
-rw-r--r--hw/xfree86/i2c/fi1236_module.c26
-rw-r--r--hw/xfree86/i2c/i2c_def.h29
-rw-r--r--hw/xfree86/i2c/msp3430.c718
-rw-r--r--hw/xfree86/i2c/msp3430.h126
-rw-r--r--hw/xfree86/i2c/msp3430_module.c26
-rw-r--r--hw/xfree86/i2c/tda8425.c74
-rw-r--r--hw/xfree86/i2c/tda8425.h53
-rw-r--r--hw/xfree86/i2c/tda8425_module.c26
-rw-r--r--hw/xfree86/i2c/tda9850.c108
-rw-r--r--hw/xfree86/i2c/tda9850.h52
-rw-r--r--hw/xfree86/i2c/tda9850_module.c26
-rw-r--r--hw/xfree86/i2c/tda9885.c98
-rw-r--r--hw/xfree86/i2c/tda9885.h72
-rw-r--r--hw/xfree86/i2c/tda9885_module.c26
19 files changed, 3004 insertions, 0 deletions
diff --git a/hw/xfree86/i2c/bt829.c b/hw/xfree86/i2c/bt829.c
new file mode 100644
index 000000000..2cb5e53a0
--- /dev/null
+++ b/hw/xfree86/i2c/bt829.c
@@ -0,0 +1,737 @@
+/* TODO: clean up/fix CC code */
+
+#include "xf86.h"
+#include "xf86i2c.h"
+#include "bt829.h"
+#include "i2c_def.h"
+
+/* Changing the following settings (especially VCROP) may */
+/* require modifying code that calls this driver. */
+#define HCROP 0 /* amount to crop from the left and right edges */
+#define VCROP 0 /* amount to crop from the top and bottom edges */
+
+#define BTVERSION (bt->id>>4)
+
+#define H(X) ( ((X)>>8) & 0xFF )
+#define L(X) ( (X) & 0xFF )
+
+#define LIMIT(X,A,B) (((X)<(A)) ? (A) : ((X)>(B)) ? (B) : (X) )
+
+/* Bt829 family chip ID's */
+#define BT815 0x02
+#define BT817 0x06
+#define BT819 0x07
+#define BT827 0x0C
+#define BT829 0x0E
+
+/* Bt829 registers */
+#define STATUS 0x00 /* Device Status */
+#define IFORM 0x01 /* Input Format */
+#define TDEC 0x02 /* Temporal Decimation */
+#define CROP 0x03 /* MSB Cropping */
+#define VDELAY_LO 0x04 /* Vertical Delay */
+#define VACTIVE_LO 0x05 /* Vertical Active */
+#define HDELAY_LO 0x06 /* Horizontal Delay */
+#define HACTIVE_LO 0x07 /* Horizontal Active */
+#define HSCALE_HI 0x08 /* Horizontal Scaling */
+#define HSCALE_LO 0x09 /* Horizontal Scaling */
+#define BRIGHT 0x0A /* Brightness Control */
+#define CONTROL 0x0B /* Miscellaneous Control */
+#define CONTRAST_LO 0x0C /* Luma Gain (Contrast) */
+#define SAT_U_LO 0x0D /* Chroma (U) Gain (Saturation) */
+#define SAT_V_LO 0x0E /* Chroma (V) Gain (Saturation) */
+#define HUE 0x0F /* Hue Control */
+#define SCLOOP 0x10 /* SC Loop Control */
+#define WC_UP 0x11 /* White Crush Up Count */
+#define OFORM 0x12 /* Output Format */
+#define VSCALE_HI 0x13 /* Vertical Scaling */
+#define VSCALE_LO 0x14 /* Vertical Scaling */
+#define TEST 0x15 /* Test Control */
+#define VPOLE 0x16 /* Video Timing Polarity */
+#define IDCODE 0x17 /* ID Code */
+#define ADELAY 0x18 /* AGC Delay */
+#define BDELAY 0x19 /* Burst Gate Delay */
+#define ADC 0x1A /* ADC Interface */
+#define VTC 0x1B /* Video Timing Control */
+#define CC_STATUS 0x1C /* Extended Data Services/Closed Capt Status */
+#define CC_DATA 0x1D /* Extended Data Services/Closed Capt Data */
+#define WC_DN 0x1E /* White Crush Down Count */
+#define SRESET 0x1F /* Software Reset */
+#define P_IO 0x3F /* Programmable I/O */
+
+static CARD8 btread(BT829Ptr bt, CARD8 reg)
+{
+ CARD8 v;
+
+ I2C_WriteRead(&(bt->d), &reg, 1, &v, 1);
+
+ return v;
+}
+
+static void btwrite(BT829Ptr bt, CARD8 reg, CARD8 val)
+{
+ CARD8 data[2];
+
+ data[0] = reg;
+ data[1] = val;
+ I2C_WriteRead(&(bt->d), data, 2, NULL, 0);
+}
+
+/*
+ * Register access
+ */
+static void btwrite_status(BT829Ptr bt) /* STATUS */
+{
+ btwrite(bt, STATUS, 0x00); /* clear */
+}
+
+static void btwrite_iform(BT829Ptr bt) /* IFORM */
+{
+ int xtsel;
+
+ switch (bt->format) {
+ case BT829_NTSC:
+ case BT829_NTSC_JAPAN:
+ case BT829_PAL_M:
+ case BT829_PAL_N_COMB: /* gatos says xtsel = 2 */
+ xtsel = 1;
+ break;
+ case BT829_PAL:
+ case BT829_PAL_N:
+ case BT829_SECAM:
+ xtsel = 2;
+ break;
+ default: /* shouldn't get here */
+ xtsel = 3; /* hardware default */
+ break;
+ }
+
+ btwrite(bt, IFORM, (bt->mux<<5) | (xtsel<<3) | bt->format);
+}
+
+static void btwrite_tdec(BT829Ptr bt) /* TDEC */
+{
+ /* use default */
+}
+
+static void btwrite_crop(BT829Ptr bt) /* CROP */
+{
+ btwrite(bt, CROP, (H(bt->vdelay)<<6) | (H(bt->vactive)<<4) |
+ (H(bt->hdelay)<<2) | H(bt->width));
+}
+
+static void btwrite_vdelay_lo(BT829Ptr bt) /* VDELAY_LO */
+{
+ btwrite(bt, VDELAY_LO, L(bt->vdelay));
+}
+
+static void btwrite_vactive_lo(BT829Ptr bt) /* VACTIVE_LO */
+{
+ btwrite(bt, VACTIVE_LO, L(bt->vactive));
+}
+
+static void btwrite_hdelay_lo(BT829Ptr bt) /* HDELAY_LO */
+{
+ btwrite(bt, HDELAY_LO, L(bt->hdelay));
+}
+
+static void btwrite_hactive_lo(BT829Ptr bt) /* HACTIVE_LO */
+{
+ btwrite(bt, HACTIVE_LO, L(bt->width));
+}
+
+static void btwrite_hscale_hi(BT829Ptr bt) /* HSCALE_HI */
+{
+ btwrite(bt, HSCALE_HI, H(bt->hscale));
+}
+
+static void btwrite_hscale_lo(BT829Ptr bt) /* HSCALE_LO */
+{
+ btwrite(bt, HSCALE_LO, L(bt->hscale));
+}
+
+static void btwrite_bright(BT829Ptr bt) /* BRIGHT */
+{
+ btwrite(bt, BRIGHT, bt->brightness);
+}
+
+static void btwrite_control(BT829Ptr bt) /* CONTROL */
+{
+ int ldec;
+
+ /* The data sheet says ldec should always be 0 for SECAM */
+ /* but the picture quality is better with ldec = 1 */
+ ldec = (bt->width > 360); /* gatos says 384 */
+
+ btwrite(bt, CONTROL,
+ ((bt->mux==bt->svideo_mux) ? 0xC0:0x00) | /* LNOTCH and COMP */
+ (ldec<<5) | (H(bt->contrast)<<2) | (H(bt->sat_u)<<1) | H(bt->sat_v));
+}
+
+static void btwrite_contrast_lo(BT829Ptr bt) /* CONTRAST_LO */
+{
+ btwrite(bt, CONTRAST_LO, L(bt->contrast));
+}
+
+static void btwrite_sat_u_lo(BT829Ptr bt) /* SAT_U_LO */
+{
+ btwrite(bt, SAT_U_LO, L(bt->sat_u));
+}
+
+static void btwrite_sat_v_lo(BT829Ptr bt) /* SAT_V_LO */
+{
+ btwrite(bt, SAT_V_LO, L(bt->sat_v));
+}
+
+static void btwrite_hue(BT829Ptr bt) /* HUE */
+{
+ btwrite(bt, HUE, bt->hue);
+}
+
+static void btwrite_scloop(BT829Ptr bt) /* SCLOOP */
+{
+ if (BTVERSION >= BT827) {
+ btwrite(bt, SCLOOP,
+ (bt->format==BT829_SECAM) ? 0x10:0x00 /* QCIF or AUTO */
+ );
+ }
+}
+
+static void btwrite_wc_up(BT829Ptr bt) /* WC_UP */
+{
+ if (BTVERSION >= BT827) {
+ /* use default */
+ }
+}
+
+static void btwrite_oform(BT829Ptr bt) /* OFORM */
+{
+ btwrite(bt, OFORM, (bt->code<<3) | (bt->len<<2) |
+ 0x02 /* RANGE = 0, CORE = 0, VBI_FRAME = 0, OES = 2 (default) */
+ );
+}
+
+static void btwrite_vscale_hi(BT829Ptr bt) /* VSCALE_HI */
+{
+ btwrite(bt, VSCALE_HI, H(bt->vscale) |
+ 0x60 /* YCOMB = 0, COMB = 1, INT = 1 (default) */
+ );
+}
+
+static void btwrite_vscale_lo(BT829Ptr bt) /* VSCALE_LO */
+{
+ btwrite(bt, VSCALE_LO, L(bt->vscale));
+}
+
+/* TEST should not be written to */
+
+static void btwrite_vpole(BT829Ptr bt) /* VPOLE */
+{
+ btwrite(bt, VPOLE, (bt->out_en<<7));
+}
+
+/* IDCODE is read only */
+
+static void btwrite_adelay(BT829Ptr bt) /* ADELAY */
+{
+ switch (bt->format) {
+ case BT829_NTSC:
+ case BT829_NTSC_JAPAN:
+ case BT829_PAL_M:
+ btwrite(bt, ADELAY, 104);
+ break;
+ case BT829_PAL:
+ case BT829_PAL_N:
+ case BT829_SECAM:
+ case BT829_PAL_N_COMB:
+ btwrite(bt, ADELAY, 127);
+ break;
+ default: /* shouldn't get here */
+ btwrite(bt, ADELAY, 104); /* hardware default */
+ break;
+ }
+}
+
+static void btwrite_bdelay(BT829Ptr bt) /* BDELAY */
+{
+ switch (bt->format) {
+ case BT829_NTSC:
+ case BT829_NTSC_JAPAN:
+ case BT829_PAL_M:
+ btwrite(bt, BDELAY, 93);
+ break;
+ case BT829_PAL:
+ case BT829_PAL_N:
+ case BT829_PAL_N_COMB:
+ btwrite(bt, BDELAY, 114);
+ break;
+ case BT829_SECAM:
+ btwrite(bt, BDELAY, 160);
+ break;
+ default: /* shouldn't get here */
+ btwrite(bt, BDELAY, 93); /* hardware default */
+ break;
+ }
+}
+
+static void btwrite_adc(BT829Ptr bt) /* ADC */
+{
+ btwrite(bt, ADC, bt->mux==bt->svideo_mux ? 0x80:0x82); /* CSLEEP = 0 or 1 */
+}
+
+static void btwrite_vtc(BT829Ptr bt) /* VTC */
+{
+ int vfilt = 0; /* hardware default */
+
+ if (BTVERSION > BT827) { /* gatos says >= BT827 */
+ switch (bt->format) {
+ case BT829_NTSC:
+ case BT829_NTSC_JAPAN:
+ case BT829_PAL_M:
+ case BT829_PAL_N_COMB: /* gatos groups with BT829_PAL */
+ if (bt->width <= 360) vfilt = 1; /* gatos says <= 240 */
+ if (bt->width <= 180) vfilt = 2; /* gatos says <= 120 */
+ if (bt->width <= 90) vfilt = 3; /* gatos says <= 60 */
+ break;
+ case BT829_PAL:
+ case BT829_PAL_N:
+ case BT829_SECAM:
+ if (bt->width <= 384) vfilt = 1;
+ if (bt->width <= 192) vfilt = 2;
+ if (bt->width<= 96) vfilt = 3;
+ break;
+ default: /* shouldn't get here */
+ break; /* use hardware default */
+ }
+ btwrite(bt, VTC, (bt->vbien<<4) | (bt->vbifmt<<3) | vfilt);
+ }
+}
+
+static void btwrite_cc_status(BT829Ptr bt) /* CC_STATUS */
+{ /* FIXME: ATI specific */
+ if (BTVERSION >= BT827) {
+ if (bt->ccmode == 0) btwrite(bt, CC_STATUS, 0x00);
+ /* 0x40 is activate to set the CCVALID line. Not required yet */
+ else btwrite(bt, CC_STATUS, (bt->ccmode<<4) | 0x40);
+ }
+}
+
+/* CC_DATA is read only */
+
+static void btwrite_wc_dn(BT829Ptr bt) /* WC_DN */
+{
+ if (BTVERSION >= BT827) {
+ /* use default */
+ }
+}
+
+static void bt_reset(BT829Ptr bt) { /* SRESET */
+ btwrite(bt, SRESET, 0x0); /* Reset all registers */
+}
+
+static void btwrite_p_io(BT829Ptr bt) /* P_IO */
+{
+ if (BTVERSION >= BT827) {
+ btwrite(bt, P_IO, bt->p_io);
+ }
+}
+
+/*
+ * Deal with dependencies
+ */
+static void propagate_changes(BT829Ptr bt)
+{
+ CARD16 hdelay, unscaled_hdelay, vdelay, hscale, vscale;
+ int htotal, vactive;
+
+ switch (bt->format) {
+ case BT829_NTSC:
+ case BT829_NTSC_JAPAN:
+ case BT829_PAL_M:
+ vdelay = 22;
+ htotal = 754;
+ vactive = 480;
+ unscaled_hdelay = 135;
+ break;
+ case BT829_PAL:
+ case BT829_PAL_N:
+ vdelay = (bt->tunertype==5) ? 34 : 22;
+ htotal = 922;
+ vactive = 576;
+ unscaled_hdelay = 186;
+ break;
+ case BT829_SECAM:
+ vdelay = 34;
+ htotal = 922;
+ vactive = 576;
+ unscaled_hdelay = 186;
+ break;
+ case BT829_PAL_N_COMB:
+ vdelay = (bt->tunertype==5) ? 34 : 22; /* windows says 22 */
+ htotal = 754; /* gatos and windows say 922 */
+ vactive = 576;
+ unscaled_hdelay = 135; /* gatos and windows say 186 */
+ break;
+ default: /* shouldn't get here */
+ vdelay = 22; /* hardware default */
+ htotal = 754;
+ vactive = 480; /* hardware default */
+ unscaled_hdelay = 135;
+ break;
+ }
+
+ bt->htotal = htotal; /* Used for error checking in bt829_SetCaptSize */
+
+ hscale = 4096 * htotal / (bt->width + 2 * HCROP)-4096;
+ hdelay = (
+ HCROP + (bt->width + 2 * HCROP) * unscaled_hdelay / htotal
+ ) & 0x3FE;
+
+ vactive = vactive - 2 * VCROP;
+ vdelay = vdelay + VCROP;
+ vscale = (0x10000 - (512*vactive/bt->height-512)) & 0x1FFF;
+
+ if ((hdelay != bt->hdelay) || (vdelay != bt->vdelay) ||
+ (vactive != bt->vactive) || (hscale != bt->hscale) ||
+ (vscale != bt->vscale)) {
+ bt->hdelay = hdelay;
+ bt->vdelay = vdelay;
+ bt->vactive = vactive;
+ bt->hscale = hscale;
+ bt->vscale = vscale;
+ btwrite_crop(bt);
+ btwrite_vdelay_lo(bt);
+ btwrite_vactive_lo(bt);
+ btwrite_hdelay_lo(bt);
+ btwrite_hscale_hi(bt);
+ btwrite_hscale_lo(bt);
+ btwrite_control(bt);
+ btwrite_vscale_hi(bt);
+ btwrite_vscale_lo(bt);
+ }
+}
+
+static void write_all(BT829Ptr bt)
+{
+ bt_reset(bt);
+ propagate_changes(bt); /* ensure consistency */
+ btwrite_iform(bt);
+ btwrite_tdec(bt);
+ btwrite_crop(bt);
+ btwrite_vdelay_lo(bt);
+ btwrite_vactive_lo(bt);
+ btwrite_hdelay_lo(bt);
+ btwrite_hactive_lo(bt);
+ btwrite_hscale_hi(bt);
+ btwrite_hscale_lo(bt);
+ btwrite_bright(bt);
+ btwrite_control(bt);
+ btwrite_contrast_lo(bt);
+ btwrite_sat_u_lo(bt);
+ btwrite_sat_v_lo(bt);
+ btwrite_hue(bt);
+ btwrite_scloop(bt);
+ btwrite_wc_up(bt);
+ btwrite_oform(bt);
+ btwrite_vscale_hi(bt);
+ btwrite_vscale_lo(bt);
+ btwrite_vpole(bt);
+ btwrite_adelay(bt);
+ btwrite_bdelay(bt);
+ btwrite_adc(bt);
+ btwrite_vtc(bt);
+/* btwrite_cc_status(bt); */ /* FIXME: CC code needs cleaning */
+ btwrite_wc_dn(bt);
+ btwrite_p_io(bt);
+}
+
+/*
+ * Public functions
+ */
+BT829Ptr bt829_Detect(I2CBusPtr b, I2CSlaveAddr addr)
+{
+ BT829Ptr bt;
+ I2CByte a;
+
+ bt = xcalloc(1, sizeof(BT829Rec));
+ if(bt == NULL) return NULL;
+ bt->d.DevName = strdup("BT829 video decoder");
+ bt->d.SlaveAddr = addr;
+ bt->d.pI2CBus = b;
+ bt->d.NextDev = NULL;
+ bt->d.StartTimeout = b->StartTimeout;
+ bt->d.BitTimeout = b->BitTimeout;
+ bt->d.AcknTimeout = b->AcknTimeout;
+ bt->d.ByteTimeout = b->ByteTimeout;
+
+
+ if(!I2C_WriteRead(&(bt->d), NULL, 0, &a, 1))
+ {
+ free(bt);
+ return NULL;
+ }
+
+ bt->id = btread(bt,IDCODE);
+
+ free(bt->d.DevName);
+ bt->d.DevName = xcalloc(200, sizeof(char));
+ switch(BTVERSION){
+ case BT815:
+ sprintf(bt->d.DevName, "bt815a video decoder, revision %d",bt->id & 0xf);
+ break;
+ case BT817:
+ sprintf(bt->d.DevName, "bt817a video decoder, revision %d",bt->id & 0xf);
+ break;
+ case BT819:
+ sprintf(bt->d.DevName, "bt819a video decoder, revision %d",bt->id & 0xf);
+ break;
+ case BT827:
+ sprintf(bt->d.DevName, "bt827a/b video decoder, revision %d",bt->id & 0xf);
+ break;
+ case BT829:
+ sprintf(bt->d.DevName, "bt829a/b video decoder, revision %d",bt->id & 0xf);
+ break;
+ default:
+ sprintf(bt->d.DevName, "bt8xx/unknown video decoder version %d, revision %d",bt->id >> 4,bt->id & 0xf);
+ break;
+ }
+
+ /* set default parameters */
+ if(!I2CDevInit(&(bt->d)))
+ {
+ free(bt);
+ return NULL;
+ }
+
+ bt->tunertype = 1;
+
+ bt->brightness = 0; /* hardware default */
+ bt->ccmode = 0;
+ bt->code = 0; /* hardware default */
+ bt->contrast = 216; /* hardware default */
+ bt->format = BT829_NTSC;
+ bt->height = 480; /* hardware default for vactive */
+ bt->hue = 0; /* hardware default */
+ bt->len = 1; /* hardware default */
+ bt->mux = BT829_MUX0; /* hardware default */
+ bt->out_en = 0; /* hardware default */
+ bt->p_io = 0; /* hardware default */
+ bt->sat_u = 254; /* hardware default */
+ bt->sat_v = 180; /* hardware default */
+ bt->vbien = 0; /* hardware default */
+ bt->vbifmt = 0; /* hardware default */
+ bt->width = 640; /* hardware default for hactive */
+
+ bt->hdelay = 120; /* hardware default */
+ bt->hscale = 684; /* hardware default */
+ bt->vactive = 480; /* hardware default */
+ bt->vdelay = 22; /* hardware default */
+ bt->vscale = 0; /* hardware default */
+
+ bt->htotal = 754; /* NTSC */
+ bt->svideo_mux = 0; /* no s-video */
+
+ return bt;
+}
+
+int bt829_ATIInit(BT829Ptr bt)
+{
+ bt->code = 1;
+ bt->len = 0;
+ bt->vbien = 1;
+ bt->vbifmt = 1;
+ bt->svideo_mux = BT829_MUX1;
+
+ write_all (bt);
+
+ return 0;
+}
+
+int bt829_SetFormat(BT829Ptr bt, CARD8 format)
+{
+ if ((format < 1) || (format > 7)) return -1;
+ if ((BTVERSION <= BT819) &&
+ (format != BT829_NTSC) && (format != BT829_PAL)) return -1;
+ if (format == bt->format) return 0;
+ bt->format = format;
+ propagate_changes(bt);
+ btwrite_iform(bt);
+ btwrite_scloop(bt);
+ btwrite_adelay(bt);
+ btwrite_bdelay(bt);
+ btwrite_vtc(bt);
+ return 0;
+}
+
+int bt829_SetMux(BT829Ptr bt, CARD8 mux)
+{
+ if ((mux < 1) || (mux > 3)) return -1;
+ if (mux == bt->mux) return 0;
+ bt->mux = mux;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_iform(bt);
+ btwrite_control(bt);
+ btwrite_adc(bt);
+ return 0;
+}
+
+void bt829_SetBrightness(BT829Ptr bt, int brightness)
+{
+ brightness = LIMIT(brightness,-1000,999); /* ensure -128 <= brightness <= 127 below */
+ brightness = (128*brightness)/1000;
+ if (brightness == bt->brightness) return;
+ bt->brightness = brightness;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_bright(bt);
+}
+
+void bt829_SetContrast(BT829Ptr bt, int contrast)
+{
+ contrast = LIMIT(contrast,-1000,1000);
+ contrast = (216*(contrast+1000))/1000;
+ if (contrast == bt->contrast) return;
+ bt->contrast = contrast;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_control(bt);
+ btwrite_contrast_lo(bt);
+}
+
+void bt829_SetSaturation(BT829Ptr bt, int saturation)
+{
+ CARD16 sat_u, sat_v;
+
+ saturation = LIMIT(saturation,-1000,1000);
+ sat_u = (254*(saturation+1000))/1000;
+ sat_v = (180*(saturation+1000))/1000;
+ if ((sat_u == bt->sat_u) && (sat_v == bt->sat_v)) return;
+ bt->sat_u = sat_u;
+ bt->sat_v = sat_v;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_control(bt);
+ btwrite_sat_u_lo(bt);
+ btwrite_sat_v_lo(bt);
+}
+
+void bt829_SetTint(BT829Ptr bt, int hue)
+{
+ hue = LIMIT(hue,-1000,999); /* ensure -128 <= hue <= 127 below */
+ hue = (128*hue)/1000;
+ if (hue == bt->hue) return;
+ bt->hue = hue;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_hue(bt);
+}
+
+int bt829_SetCaptSize(BT829Ptr bt, int width, int height)
+{
+ if ((width > bt->htotal - 2 * HCROP) ||
+ (16 * width < bt->htotal - 32 * HCROP)) return -1;
+ if ((height > bt->vactive) || (16 * height < bt->vactive)) return -1;
+ if ((width == bt->width) && (height == bt->height)) return 0;
+ bt->width = width;
+ bt->height = height;
+ propagate_changes(bt);
+ btwrite_crop(bt);
+ btwrite_hactive_lo(bt);
+ btwrite_control(bt);
+ btwrite_vtc(bt);
+ return 0;
+}
+
+int bt829_SetCC(BT829Ptr bt) /* FIXME: should take ccmode as a parameter */
+{
+ if (BTVERSION < BT827) return -1; /* can't do it */
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_cc_status(bt);
+ /* we write to STATUS to reset the CCVALID flag */
+ if (bt->ccmode != 0) btwrite_status(bt);
+ return 0;
+}
+
+void bt829_SetOUT_EN(BT829Ptr bt, BOOL out_en)
+{
+ out_en = (out_en != 0);
+ if (out_en == bt->out_en) return;
+ bt->out_en = out_en;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_vpole(bt);
+}
+
+void bt829_SetP_IO(BT829Ptr bt, CARD8 p_io)
+{
+ if (p_io == bt->p_io) return;
+ bt->p_io = p_io;
+ /* propagate_changes(bt); */ /* no dependencies */
+ btwrite_p_io(bt);
+}
+
+#define BTREAD(R) btread(bt,(R))
+
+#if 0
+
+void bt829_getCCdata(BT829Ptr bt,struct CCdata *data)
+{
+ CARD8 status;
+ data->num_valid=0;
+ /* wait for buffer to be half full (means 8/16 bytes)
+ * either 4 (one of CC/EDS) or 2 (both CC/EDS) frames */
+ if(!(BTREAD(STATUS)&0x04)) return; /* could comment this line */
+ for(;data->num_valid<CC_FIFO_SIZE;data->num_valid++) {
+ status=BTREAD(CC_STATUS);
+ if(!(status&0x04)) break;
+ data->data[data->num_valid]= BTREAD(CC_DATA)&0x7f;
+ /* stripped high bit (parity) */
+ data->status[data->num_valid]= (CCS_EDS*((status&0x02)>>1)) |
+ (CCS_HIGH*(status&0x01)) |
+ (CCS_OVER*((status&0x08)>>3)) |
+ (CCS_PAR*((status&0x80)>>7)) ; }
+ btwrite(bt,STATUS,0x00); /* Reset CCVALID status bit */
+ return;
+}
+
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* Debug and report routines */
+
+#define DUMPREG(REG) \
+ xf86DrvMsg(bt->d.pI2CBus->scrnIndex,X_INFO," %-12s (0x%02X) = 0x%02X\n", \
+ #REG,REG,BTREAD(REG))
+
+/*static void bt829_dumpregs(BT829Ptr bt)
+{
+ DUMPREG(STATUS);
+ DUMPREG(IFORM);
+ DUMPREG(TDEC);
+ DUMPREG(CROP);
+ DUMPREG(VDELAY_LO);
+ DUMPREG(VACTIVE_LO);
+ DUMPREG(HDELAY_LO);
+ DUMPREG(HACTIVE_LO);
+ DUMPREG(HSCALE_HI);
+ DUMPREG(HSCALE_LO);
+ DUMPREG(BRIGHT);
+ DUMPREG(CONTROL);
+ DUMPREG(CONTRAST_LO);
+ DUMPREG(SAT_U_LO);
+ DUMPREG(SAT_V_LO);
+ DUMPREG(HUE);
+ if (BTVERSION >= BT827) {
+ DUMPREG(SCLOOP);
+ DUMPREG(WC_UP) ; }
+ DUMPREG(OFORM);
+ DUMPREG(VSCALE_HI);
+ DUMPREG(VSCALE_LO);
+ DUMPREG(TEST);
+ DUMPREG(VPOLE);
+ DUMPREG(IDCODE);
+ DUMPREG(ADELAY);
+ DUMPREG(BDELAY);
+ DUMPREG(ADC);
+ if (BTVERSION >= BT827) {
+ DUMPREG(VTC);
+ DUMPREG(CC_STATUS);
+ DUMPREG(CC_DATA);
+ DUMPREG(WC_DN);
+ DUMPREG(P_IO) ; }
+}*/
diff --git a/hw/xfree86/i2c/bt829.h b/hw/xfree86/i2c/bt829.h
new file mode 100644
index 000000000..2c7c00d34
--- /dev/null
+++ b/hw/xfree86/i2c/bt829.h
@@ -0,0 +1,115 @@
+#ifndef __BT829_H__
+#define __BT829_H__
+
+#include "xf86i2c.h"
+
+typedef struct {
+ int tunertype; /* Must be set before init */
+ /* Private variables */
+ I2CDevRec d;
+
+ CARD8 brightness;
+ CARD8 ccmode;
+ CARD8 code;
+ CARD16 contrast;
+ CARD8 format;
+ int height;
+ CARD8 hue;
+ CARD8 len;
+ CARD8 mux;
+ CARD8 out_en;
+ CARD8 p_io;
+ CARD16 sat_u;
+ CARD16 sat_v;
+ CARD8 vbien;
+ CARD8 vbifmt;
+ int width;
+
+ CARD16 hdelay;
+ CARD16 hscale;
+ CARD16 vactive;
+ CARD16 vdelay;
+ CARD16 vscale;
+
+ CARD16 htotal;
+ CARD8 id;
+ CARD8 svideo_mux;
+} BT829Rec, *BT829Ptr;
+
+BT829Ptr bt829_Detect(I2CBusPtr b, I2CSlaveAddr addr);
+
+/* ATI card specific initialization */
+#define BT829_ATI_ADDR_1 0x8A
+#define BT829_ATI_ADDR_2 0x88
+int bt829_ATIInit(BT829Ptr bt);
+
+#define BT829_NTSC 1 /* NTSC-M */
+#define BT829_NTSC_JAPAN 2 /* NTSC-Japan */
+#define BT829_PAL 3 /* PAL-B,D,G,H,I */
+#define BT829_PAL_M 4 /* PAL-M */
+#define BT829_PAL_N 5 /* PAL-N */
+#define BT829_SECAM 6 /* SECAM */
+#define BT829_PAL_N_COMB 7 /* PAL-N combination */
+int bt829_SetFormat(BT829Ptr bt, CARD8 format);
+
+#define BT829_MUX2 1 /* ATI -> composite video */
+#define BT829_MUX0 2 /* ATI -> tv tuner */
+#define BT829_MUX1 3 /* ATI -> s-video */
+int bt829_SetMux(BT829Ptr bt, CARD8 mux);
+
+int bt829_SetCaptSize(BT829Ptr bt, int width, int height);
+
+void bt829_SetBrightness(BT829Ptr bt, int brightness);
+void bt829_SetContrast(BT829Ptr bt, int contrast);
+void bt829_SetSaturation(BT829Ptr bt, int saturation);
+void bt829_SetTint(BT829Ptr bt, int hue); /* Hue */
+
+void bt829_SetOUT_EN(BT829Ptr bt, BOOL out_en); /* VPOLE register */
+void bt829_SetP_IO(BT829Ptr bt, CARD8 p_io); /* P_IO register */
+
+int bt829_SetCC(BT829Ptr bt);
+
+#define BT829SymbolsList \
+ "bt829_Detect", \
+ "bt829_ATIInit", \
+ "bt829_SetFormat", \
+ "bt829_SetMux", \
+ "bt829_SetBrightness", \
+ "bt829_SetContrast", \
+ "bt829_SetSaturation", \
+ "bt829_SetTint", \
+ "bt829_SetCaptSize", \
+ "bt829_SetOUT_EN", \
+ "bt829_SetP_IO"
+
+#ifdef XFree86LOADER
+
+#define xf86_bt829_Detect ((BT829Ptr (*)(I2CBusPtr, I2CSlaveAddr))LoaderSymbol("bt829_Detect"))
+#define xf86_bt829_ATIInit ((int (*)(BT829Ptr))LoaderSymbol("bt829_ATIInit"))
+#define xf86_bt829_SetFormat ((int (*)(BT829Ptr, CARD8))LoaderSymbol("bt829_SetFormat"))
+#define xf86_bt829_SetMux ((int (*)(BT829Ptr, CARD8))LoaderSymbol("bt829_SetMux"))
+#define xf86_bt829_SetCaptSize ((int (*)(BT829Ptr, int, int))LoaderSymbol("bt829_SetCaptSize"))
+#define xf86_bt829_SetBrightness ((void (*)(BT829Ptr, int))LoaderSymbol("bt829_SetBrightness"))
+#define xf86_bt829_SetContrast ((void (*)(BT829Ptr, int))LoaderSymbol("bt829_SetContrast"))
+#define xf86_bt829_SetSaturation ((void (*)(BT829Ptr, int))LoaderSymbol("bt829_SetSaturation"))
+#define xf86_bt829_SetTint ((void (*)(BT829Ptr, int))LoaderSymbol("bt829_SetTint"))
+#define xf86_bt829_SetOUT_EN ((void (*)(BT829Ptr, Bool))LoaderSymbol("bt829_SetOUT_EN"))
+#define xf86_bt829_SetP_IO ((void (*)(BT829Ptr, CARD8))LoaderSymbol("bt829_SetP_IO"))
+
+#else
+
+#define xf86_bt829_Detect bt829_Detect
+#define xf86_bt829_ATIInit bt829_ATIInit
+#define xf86_bt829_SetFormat bt829_SetFormat
+#define xf86_bt829_SetMux bt829_SetMux
+#define xf86_bt829_SetCaptSize bt829_SetCaptSize
+#define xf86_bt829_SetBrightness bt829_SetBrightness
+#define xf86_bt829_SetContrast bt829_SetContrast
+#define xf86_bt829_SetSaturation bt829_SetSaturation
+#define xf86_bt829_SetTint bt829_SetTint
+#define xf86_bt829_SetOUT_EN bt829_SetOUT_EN
+#define xf86_bt829_SetP_IO bt829_SetP_IO
+
+#endif
+
+#endif
diff --git a/hw/xfree86/i2c/bt829_module.c b/hw/xfree86/i2c/bt829_module.c
new file mode 100644
index 000000000..7d13f69d7
--- /dev/null
+++ b/hw/xfree86/i2c/bt829_module.c
@@ -0,0 +1,26 @@
+
+#include "xf86Module.h"
+
+static MODULESETUPPROTO(bt829Setup);
+
+
+static XF86ModuleVersionInfo bt829VersRec =
+{
+ "bt829",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_VIDEODRV, /* This needs the video driver ABI */
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_NONE,
+ {0,0,0,0}
+};
+
+XF86ModuleData bt829ModuleData = { &bt829VersRec, bt829Setup, NULL };
+
+static pointer
+bt829Setup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ return (pointer)1;
+}
diff --git a/hw/xfree86/i2c/fi1236.c b/hw/xfree86/i2c/fi1236.c
new file mode 100644
index 000000000..f56716b38
--- /dev/null
+++ b/hw/xfree86/i2c/fi1236.c
@@ -0,0 +1,550 @@
+#include "xf86.h"
+#include "xf86i2c.h"
+#include "fi1236.h"
+#include "i2c_def.h"
+
+#define NUM_TUNERS 7
+
+const FI1236_parameters tuner_parms[NUM_TUNERS] =
+{
+ /* 0 - FI1236 */
+ { 733 ,884 ,12820 ,2516 ,7220 ,0xA2 ,0x94, 0x34, 0x8e },
+ /* !!!based on documentation - it should be:
+ {733 ,16*55.25 ,16*801.25 ,16*160 ,16*454 ,0xA0 ,0x90, 0x30, 0x8e},*/
+
+ /* 1 - FI1216 */
+ { 623 ,16*48.75 ,16*855.25 ,16*170 ,16*450 ,0xA0 ,0x90, 0x30, 0x8e },
+ /* 2 - TEMIC FN5AL */
+ { 623 ,16*45.75 ,16*855.25 ,16*169 ,16*454 ,0xA0 ,0x90, 0x30, 0x8e },
+ /* 3 - MT2032.. */
+ { 733 ,768 ,13760 , 0 , 0 , 0 , 0, 0, 0 },
+ /* 4 - FI1246 */
+ { 623 ,16*45.75 ,16*855.25 ,16*170 ,16*450 ,0xA0 ,0x90, 0x30, 0x8e },
+ /* 5 - FI1256 */
+ { 623 ,16*49.75 ,16*863.25 ,16*170 ,16*450 ,0xA0 ,0x90, 0x30, 0x8e },
+ /* 6 - FI1236W */
+ { 733 ,884 ,12820 ,2516 ,7220 ,0x1 ,0x2, 0x4, 0x8e }
+};
+
+
+FI1236Ptr Detect_FI1236(I2CBusPtr b, I2CSlaveAddr addr)
+{
+ FI1236Ptr f;
+ I2CByte a;
+
+ f = xcalloc(1,sizeof(FI1236Rec));
+ if(f == NULL) return NULL;
+ f->d.DevName = strdup("FI12xx Tuner");
+ f->d.SlaveAddr = addr;
+ f->d.pI2CBus = b;
+ f->d.NextDev = NULL;
+ f->d.StartTimeout = b->StartTimeout;
+ f->d.BitTimeout = b->BitTimeout;
+ f->d.AcknTimeout = b->AcknTimeout;
+ f->d.ByteTimeout = b->ByteTimeout;
+ f->type=TUNER_TYPE_FI1236;
+ f->afc_timer_installed=FALSE;
+ f->last_afc_hint=TUNER_OFF;
+ f->video_if=45.7812;
+
+ if(!I2C_WriteRead(&(f->d), NULL, 0, &a, 1))
+ {
+ free(f);
+ return NULL;
+ }
+ FI1236_set_tuner_type(f, TUNER_TYPE_FI1236);
+ if(!I2CDevInit(&(f->d)))
+ {
+ free(f);
+ return NULL;
+ }
+ return f;
+}
+
+static void MT2032_dump_parameters(FI1236Ptr f, MT2032_parameters *m)
+{
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: input f_rf=%g f_if1=%g f_if2=%g f_ref=%g f_ifbw=%g f_step=%g\n",
+ m->f_rf, m->f_if1, m->f_if2, m->f_ref, m->f_ifbw, m->f_step);
+
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: computed f_lo1=%g f_lo2=%g LO1I=%d LO2I=%d SEL=%d STEP=%d NUM=%d\n",
+ m->f_lo1, m->f_lo2, m->LO1I, m->LO2I, m->SEL, m->STEP, m->NUM);
+}
+
+
+static void MT2032_getid(FI1236Ptr f)
+{
+CARD8 out[4];
+CARD8 in;
+
+in=0x11;
+I2C_WriteRead(&(f->d), (I2CByte *)&in, 1, out, 4);
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: Company code 0x%02x%02x, part code 0x%02x, revision code 0x%02x\n",
+ out[0], out[1], out[2], out[3]);
+
+}
+
+/* might be buggy */
+static void MT2032_shutdown(FI1236Ptr f)
+{
+CARD8 data[10];
+CARD8 value;
+
+data[0]=0x00; /* start with register 0x00 */
+data[1]=0x1A;
+data[2]=0x44;
+data[3]=0x20;
+
+I2C_WriteRead(&(f->d), (I2CByte *)data, 4, NULL, 0);
+
+data[0]=0x05; /* now start with register 0x05 */
+data[1]=0xD7;
+data[2]=0x14;
+data[3]=0x05;
+I2C_WriteRead(&(f->d), (I2CByte *)data, 4, NULL, 0);
+
+data[0]=0x0B; /* now start with register 0x05 */
+data[1]=0x8F;
+data[2]=0x07;
+data[3]=0x43;
+I2C_WriteRead(&(f->d), (I2CByte *)data, 4, NULL, 0);
+
+usleep(15000);
+}
+
+
+static void MT2032_init(FI1236Ptr f)
+{
+CARD8 data[10];
+CARD8 value;
+CARD8 xogc;
+
+MT2032_getid(f);
+
+data[0]=0x02; /* start with register 0x02 */
+data[1]=0xFF;
+data[2]=0x0F;
+data[3]=0x1F;
+
+I2C_WriteRead(&(f->d), (I2CByte *)data, 4, NULL, 0);
+
+data[0]=0x06; /* now start with register 0x06 */
+data[1]=0xE4;
+data[2]=0x8F;
+data[3]=0xC3;
+data[4]=0x4E;
+data[5]=0xEC;
+I2C_WriteRead(&(f->d), (I2CByte *)data, 6, NULL, 0);
+
+data[0]=0x0d; /* now start with register 0x0d */
+data[1]=0x32;
+I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+
+while(1) {
+ usleep(15000); /* wait 15 milliseconds */
+
+ data[0]=0x0e; /* register number 7, status */
+ value=0xFF;
+ if(!I2C_WriteRead(&(f->d), (I2CByte *)data, 1, &value, 1))
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: failed to read XOK\n");
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: XOK=%d\n", value & 0x01);
+ if(value & 1) break;
+
+ data[0]=0x07;
+ if(!I2C_WriteRead(&(f->d), (I2CByte *)data, 1, &value, 1))
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: failed to read XOGC\n");
+
+ xogc=value & 0x7;
+ if(xogc==4){
+ break; /* XOGC has reached 4.. stop */
+ }
+ xogc--;
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: try XOGC=%d\n", xogc);
+ usleep(15000);
+ data[0]=0x07; /* register number 7, control byte 2 */
+ data[1]=0x08 | xogc;
+ I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+ }
+f->xogc=xogc;
+/* wait before continuing */
+usleep(15000); /* wait 50 milliseconds */
+MT2032_dump_status(f);
+}
+
+static int MT2032_no_spur_in_band(MT2032_parameters *m)
+{
+int n_max, n1, n2;
+double f_test;
+n_max=5;
+n1=1;
+while(1){
+ n2=-n1;
+ f_test=n1*(m->f_lo1-m->f_lo2);
+ while(1){
+ n2--;
+ f_test=f_test-m->f_lo2;
+ xf86DrvMsg(0, X_INFO, "testing f_test=%g n1=%d n2=%d f_lo1=%g f_lo2=%g f_if2=%g\n", f_test, n1, n2, m->f_lo1, m->f_lo2, m->f_if2);
+ xf86DrvMsg(0, X_INFO, "d_f=%g f_ifbw=%g\n",fabs(fabs(f_test)-m->f_if2), m->f_ifbw);
+ if((fabs(fabs(f_test)-m->f_if2)*2.0)<=m->f_ifbw)return 0;
+ if(n2<=-n_max)break;
+ /* this line in the manual is bogus. I say it is faster
+ and more correct to go over all harmonics.. */
+ #if 0
+ if(f_test<(m->f_lo2-m->f_if2-m->f_ifbw))break;
+ #endif
+ }
+ n1++;
+ if(n1>=n_max)return 1;
+ }
+
+}
+
+static void MT2032_calculate_register_settings(MT2032_parameters *m, double f_rf, double f_if1, double f_if2, double f_ref, double f_ifbw, double f_step)
+{
+int n;
+m->f_rf=f_rf;
+m->f_if1=f_if1;
+m->f_if2=f_if2;
+m->f_ref=f_ref;
+m->f_ifbw=f_ifbw;
+m->f_step=f_step;
+
+m->f_lo1=f_rf+f_if1;
+m->LO1I=(int)floor((m->f_lo1/f_ref)+0.5);
+m->f_lo1=f_ref*m->LO1I;
+
+m->f_lo2=m->f_lo1-f_rf-f_if2;
+
+/* check for spurs */
+n=1;
+while(n<3){
+ if(MT2032_no_spur_in_band(m))break;
+ if(m->f_lo1<(f_rf+f_if1)){
+ m->LO1I+=n;
+ } else {
+ m->LO1I-=n;
+ }
+ m->f_lo1=m->LO1I*f_ref;
+ m->f_lo2=m->f_lo1-f_rf-f_if2;
+ n++;
+ }
+/* xf86DrvMsg(0, X_INFO, "MT2032: n=%d\n", n); */
+/* select VCO */
+
+/* m->f_lo1>1100.0 */
+if(m->f_lo1<1370.0)m->SEL=4;
+ else
+if(m->f_lo1<1530.0)m->SEL=3;
+ else
+if(m->f_lo1<1720.0)m->SEL=2;
+ else
+if(m->f_lo1<1890.0)m->SEL=1;
+ else /* m->f_lo1 < 1958.0 */
+ m->SEL=0;
+
+/* calculate the rest of the registers */
+m->LO2I=(int)floor(m->f_lo2/f_ref);
+m->STEP=(int)floor(3780.0*f_step/f_ref);
+m->NUM=(int)floor(3780.0*(m->f_lo2/f_ref-m->LO2I));
+m->NUM=m->STEP*(int)floor((1.0*m->NUM)/(1.0*m->STEP)+0.5);
+}
+
+static int MT2032_wait_for_lock(FI1236Ptr f)
+{
+int n;
+CARD8 data[10];
+CARD8 value;
+
+n=12;
+while(1){
+ data[0]=0x0e; /* register number 7, status */
+ I2C_WriteRead(&(f->d), (I2CByte *)data, 1, &value, 1);
+/* xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: LO1LK=%d LO2LK=%d\n", (value & 0x04)>>2, (value & 0x02)>>1); */
+ if((value & 6)==6) break;
+ usleep(1500);
+ n--;
+ if(n<0)break;
+ }
+if(n<0){
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: failed to set frequency\n");
+ return 0;
+ }
+return 1;
+}
+
+static void MT2032_implement_settings(FI1236Ptr f, MT2032_parameters *m)
+{
+CARD8 data[10];
+CARD8 value;
+
+data[0]=0x00; /* start with register 0x00 */
+data[1]=(m->LO1I>>3)-1;
+data[2]=(m->SEL<<4)|(m->LO1I & 0x7);
+data[3]=0x86;
+I2C_WriteRead(&(f->d), (I2CByte *)data, 4, NULL, 0);
+
+data[0]=0x05; /* start with register 0x05 */
+data[1]=((m->LO2I & 0x7)<<5)|((m->LO2I>>3)-1);
+if(m->f_rf<400.0)data[2]=0xe4;
+ else data[2]=0xf4;
+I2C_WriteRead(&(f->d), (I2CByte *)data, 3, NULL, 0);
+
+data[0]=0x07; /* register number 7, control byte 2 */
+I2C_WriteRead(&(f->d), (I2CByte *)data, 1, &value, 1);
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: using XOGC=%d\n", (value & 0x07));
+data[1]=8 | (value & 0x7);
+I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+
+data[0]=0x0b; /* start with register 0x0b */
+data[1]=m->NUM & 0xff;
+data[2]=(1<<7)|((m->NUM >> 8) & 0x0f);
+I2C_WriteRead(&(f->d), (I2CByte *)data, 3, NULL, 0);
+
+MT2032_wait_for_lock(f);
+}
+
+static void MT2032_optimize_VCO(FI1236Ptr f, MT2032_parameters *m)
+{
+CARD8 data[10];
+CARD8 value;
+CARD8 TAD1;
+
+data[0]=0x0f; /* register number 7, status */
+I2C_WriteRead(&(f->d), (I2CByte *)data, 1, &value, 1);
+TAD1=value & 0x07;
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: TAD1=%d SEL=%d\n", TAD1, m->SEL);
+if(TAD1 < 2)return;
+if(TAD1==2){
+ if(m->SEL==0)return;
+ m->SEL--;
+ } else {
+ if(m->SEL>=4)return;
+ m->SEL++;
+ }
+data[0]=0x01; /* start with register 1 */
+data[1]=(m->SEL<<4)|(m->LO1I & 0x7);
+I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+
+}
+
+static int FI1236_get_afc_hint(FI1236Ptr f)
+{
+CARD8 out;
+CARD8 AFC;
+I2C_WriteRead(&(f->d), NULL, 0, &out, 1);
+AFC=out & 0x7;
+if(AFC==2)return TUNER_TUNED;
+if(AFC==3)return TUNER_JUST_BELOW;
+if(AFC==1)return TUNER_JUST_ABOVE;
+return TUNER_OFF;
+}
+
+
+static int MT2032_get_afc_hint(FI1236Ptr f)
+{
+CARD8 in;
+CARD8 out[2];
+CARD8 AFC;
+in=0x0e;
+I2C_WriteRead(&(f->d), (I2CByte *)&in, 1, out, 2);
+AFC=(out[0]>>4) & 0x7;
+#if 0
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "AFC=%d TAD1=%d TAD2=%d\n", AFC, out[1] & 0x7, (out[1]>>4)& 0x07);
+#endif
+if(AFC==2)return TUNER_TUNED;
+if(AFC==3)return TUNER_JUST_BELOW;
+if(AFC==1)return TUNER_JUST_ABOVE;
+return TUNER_OFF;
+}
+
+/* this function is for external use only */
+int TUNER_get_afc_hint(FI1236Ptr f)
+{
+if(f->afc_timer_installed)return TUNER_STILL_TUNING;
+return f->last_afc_hint;
+if(f->type==TUNER_TYPE_MT2032)
+ return MT2032_get_afc_hint(f);
+ else
+ return FI1236_get_afc_hint(f);
+}
+
+static void MT2032_dump_status(FI1236Ptr f)
+{
+CARD8 in;
+CARD8 out[2];
+CARD8 AFC;
+CARD8 LDONrb;
+CARD8 LO1LK, LO2LK, XOK;
+CARD8 TAD2, TAD1;
+
+in=0x0e;
+I2C_WriteRead(&(f->d), (I2CByte *)&in, 1, out, 2);
+XOK=out[0] & 1;
+LO1LK=(out[0]>>2) &1;
+LO2LK=(out[0]>>1) &1;
+LDONrb=(out[0]>>3) &1;
+
+AFC=(out[0]>>4) & 0x7;
+
+TAD1=(out[1] & 0x7);
+TAD2=(out[1]>>4) & 0x7;
+
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: status: XOK=%d LO1LK=%d LO2LK=%d LDONrb=%d AFC=%d TAD1=%d TAD2=%d\n",
+ XOK, LO1LK, LO2LK, LDONrb, AFC, TAD1, TAD2);
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: status: OSCILLATOR:%s PLL1:%s PLL2:%s\n",
+ XOK ? "ok":"off", LO1LK ? "locked" : "off" , LO2LK ? "locked" : "off");
+
+}
+
+static void MT2032_tune(FI1236Ptr f, double freq, double step)
+{
+MT2032_parameters m;
+CARD8 data[10];
+int i;
+/* NTSC IF is 44mhz.. but 733/16=45.8125 and all TDAXXXX docs mention
+ 45.75, 39, 58.75 and 30. */
+#if 0
+MT2032_calculate_register_settings(&m, freq, 1090.0, 45.125, 5.25, 6.0, step);
+MT2032_calculate_register_settings(&m, freq, 1090.0, 45.74, 5.25, 6.0, step);
+#endif
+MT2032_calculate_register_settings(&m, freq, 1090.0, f->video_if, 5.25, 3.0, step);
+MT2032_dump_parameters(f, &m);
+MT2032_implement_settings(f, &m);
+/* MT2032_dump_parameters(f, &m); */
+for(i=0;i<3;i++){
+ MT2032_optimize_VCO(f, &m);
+ if(MT2032_wait_for_lock(f)){
+ data[0]=0x02; /* LO Gain control register 0x02 */
+ data[1]=0x20;
+ I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+ return;
+ }
+ data[0]=0x07;
+ data[1]=0x88|f->xogc;
+ I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+ usleep(15000);
+ data[1]=0x08|f->xogc;
+ I2C_WriteRead(&(f->d), (I2CByte *)data, 2, NULL, 0);
+ }
+xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "MT2032: failed to set frequency\n");
+}
+
+void FI1236_set_tuner_type(FI1236Ptr f, int type)
+{
+f->type=type;
+if(type>=NUM_TUNERS)type = NUM_TUNERS-1;
+if(type<0)type = 0;
+memcpy(&(f->parm), &(tuner_parms[type]), sizeof(FI1236_parameters));
+f->original_frequency=f->parm.min_freq;
+f->afc_delta=0;
+if(type==TUNER_TYPE_MT2032){
+ MT2032_init(f);
+ return;
+ }
+}
+
+
+CARD32 AFC_TimerCallback(OsTimerPtr timer, CARD32 time, pointer data){
+FI1236Ptr f=(FI1236Ptr)data;
+if(FI1236_AFC(f))return 150;
+ else {
+ f->afc_timer_installed=FALSE;
+ f->afc_count=0;
+ return 0;
+ }
+}
+
+void FI1236_tune(FI1236Ptr f, CARD32 frequency)
+{
+ CARD16 divider;
+
+ if(frequency < f->parm.min_freq) frequency = f->parm.min_freq;
+ if(frequency > f->parm.max_freq) frequency = f->parm.max_freq;
+
+ divider = (f->parm.fcar+(CARD16)frequency) & 0x7fff;
+ f->tuner_data.div1 = (CARD8)((divider>>8)&0x7f);
+ f->tuner_data.div2 = (CARD8)(divider & 0xff);
+ f->tuner_data.control = f->parm.control;
+
+ if(frequency < f->parm.threshold1)
+ {
+ f->tuner_data.band = f->parm.band_low;
+ }
+ else if (frequency < f->parm.threshold2)
+ {
+ f->tuner_data.band = f->parm.band_mid;
+ }
+ else
+ {
+ f->tuner_data.band = f->parm.band_high;
+ }
+
+#if 0
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "Setting tuner frequency to %d\n", frequency);
+#endif
+ I2C_WriteRead(&(f->d), (I2CByte *)&(f->tuner_data), 4, NULL, 0);
+}
+
+void TUNER_set_frequency(FI1236Ptr f, CARD32 frequency)
+{
+ CARD16 divider;
+
+ if(frequency < f->parm.min_freq) frequency = f->parm.min_freq;
+ if(frequency > f->parm.max_freq) frequency = f->parm.max_freq;
+
+ f->afc_delta=0;
+ f->original_frequency=frequency;
+
+ if(f->type==TUNER_TYPE_MT2032){
+ MT2032_tune(f, (1.0*frequency)/16.0, 0.0625);
+ } else {
+ FI1236_tune(f, frequency);
+ }
+
+ if(!f->afc_timer_installed){
+ f->afc_timer_installed=TRUE;
+/* RegisterBlockAndWakeupHandlers(FI1236_BlockHandler, AFCWakeup, f); */
+ TimerSet(NULL, 0, 300, AFC_TimerCallback, f);
+ }
+
+}
+
+
+int FI1236_AFC(FI1236Ptr f)
+{
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "AFC: f=%p f->count=%d f->original_frequency=%d f->afc_delta=%d\n", f, f->afc_count, f->original_frequency, f->afc_delta);
+ f->afc_count++;
+ if(f->type==TUNER_TYPE_MT2032){
+ f->last_afc_hint=MT2032_get_afc_hint(f);
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "AFC: afc_hint=%d\n", f->last_afc_hint);
+ if(f->last_afc_hint==TUNER_TUNED)return 0;
+ if(f->afc_count>3)f->last_afc_hint=TUNER_OFF;
+ if(f->last_afc_hint==TUNER_OFF){
+ f->afc_delta=0;
+ } else
+ f->afc_delta+=f->last_afc_hint;
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "AFC: Setting tuner frequency to %g\n", (0.5*(2*f->original_frequency+f->afc_delta))/16.0);
+ MT2032_tune(f, (1.0*f->original_frequency+0.5*f->afc_delta)/16.0, 0.03125);
+ if(f->last_afc_hint==TUNER_OFF)return 0;
+ return 1; /* call me again */
+ } else {
+ f->last_afc_hint=FI1236_get_afc_hint(f);
+ if(f->last_afc_hint==TUNER_TUNED)return 0;
+ if(f->afc_count>3)f->last_afc_hint=TUNER_OFF;
+ if(f->last_afc_hint==TUNER_OFF){
+ f->afc_delta=0;
+ } else
+ f->afc_delta+=f->last_afc_hint;
+ xf86DrvMsg(f->d.pI2CBus->scrnIndex, X_INFO, "AFC: Setting tuner frequency to %g\n", (0.5*(2*f->original_frequency+f->afc_delta))/16.0);
+ FI1236_tune(f, f->original_frequency+f->afc_delta);
+ if(f->last_afc_hint==TUNER_OFF)return 0;
+ return 1; /* call me again */
+ }
+ return 0; /* done */
+}
+
+void fi1236_dump_status(FI1236Ptr f)
+{
+if(f->type==TUNER_TYPE_MT2032){
+ MT2032_dump_status(f);
+ }
+}
diff --git a/hw/xfree86/i2c/fi1236.h b/hw/xfree86/i2c/fi1236.h
new file mode 100644
index 000000000..8c9808f05
--- /dev/null
+++ b/hw/xfree86/i2c/fi1236.h
@@ -0,0 +1,116 @@
+#ifndef __FI1236_H__
+#define __FI1236_H__
+
+#include "xf86i2c.h"
+
+/* why someone has defined NUM someplace else is beyoung me.. */
+#undef NUM
+
+typedef struct {
+ CARD32 fcar; /* 16 * fcar_Mhz */
+ CARD32 min_freq; /* 16 * min_freq_Mhz */
+ CARD32 max_freq; /* 16 * max_freq_Mhz */
+
+ CARD32 threshold1; /* 16 * Value_Mhz */
+ CARD32 threshold2; /* 16 * Value_Mhz */
+
+ CARD8 band_low;
+ CARD8 band_mid;
+ CARD8 band_high;
+ CARD8 control;
+ } FI1236_parameters;
+
+
+typedef struct {
+ /* what we want */
+ /* all frequencies are in Mhz */
+ double f_rf; /* frequency to tune to */
+ double f_if1; /* first intermediate frequency */
+ double f_if2; /* second intermediate frequency */
+ double f_ref; /* reference frequency */
+ double f_ifbw; /* bandwidth */
+ double f_step; /* step */
+
+ /* what we compute */
+ double f_lo1;
+ double f_lo2;
+ int LO1I;
+ int LO2I;
+ int SEL;
+ int STEP;
+ int NUM;
+ } MT2032_parameters;
+
+typedef struct {
+ I2CDevRec d;
+ int type;
+
+ int afc_delta;
+ CARD32 original_frequency;
+ Bool afc_timer_installed;
+ int afc_count;
+ int last_afc_hint;
+
+ double video_if;
+ FI1236_parameters parm;
+ int xogc; /* for MT2032 */
+
+ struct {
+ CARD8 div1;
+ CARD8 div2;
+ CARD8 control;
+ CARD8 band;
+ } tuner_data;
+ } FI1236Rec, *FI1236Ptr;
+
+#define TUNER_TYPE_FI1236 0
+#define TUNER_TYPE_FI1216 1
+#define TUNER_TYPE_TEMIC_FN5AL 2
+#define TUNER_TYPE_MT2032 3
+#define TUNER_TYPE_FI1246 4
+#define TUNER_TYPE_FI1256 5
+#define TUNER_TYPE_FI1236W 6
+
+#define FI1236_ADDR(a) ((a)->d.SlaveAddr)
+
+#define FI1236_ADDR_1 0xC6
+#define FI1236_ADDR_2 0xC0
+
+#define TUNER_TUNED 0
+#define TUNER_JUST_BELOW 1
+#define TUNER_JUST_ABOVE -1
+#define TUNER_OFF 4
+#define TUNER_STILL_TUNING 5
+
+
+FI1236Ptr Detect_FI1236(I2CBusPtr b, I2CSlaveAddr addr);
+void FI1236_set_tuner_type(FI1236Ptr f, int type);
+void TUNER_set_frequency(FI1236Ptr f, CARD32 frequency);
+int FI1236_AFC(FI1236Ptr f);
+int TUNER_get_afc_hint(FI1236Ptr f);
+void fi1236_dump_status(FI1236Ptr f);
+
+#define FI1236SymbolsList \
+ "Detect_FI1236", \
+ "FI1236_set_tuner_type", \
+ "TUNER_set_frequency"
+
+#ifdef XFree86LOADER
+
+#define xf86_Detect_FI1236 ((FI1236Ptr (*)(I2CBusPtr, I2CSlaveAddr))LoaderSymbol("Detect_FI1236"))
+#define xf86_FI1236_set_tuner_type ((void (*)(FI1236Ptr, int))LoaderSymbol("FI1236_set_tuner_type"))
+#define xf86_TUNER_set_frequency ((void (*)(FI1236Ptr, CARD32))LoaderSymbol("TUNER_set_frequency"))
+#define xf86_FI1236_AFC ((int (*)(FI1236Ptr))LoaderSymbol("FI1236_AFC"))
+#define xf86_TUNER_get_afc_hint ((int (*)(FI1236Ptr))LoaderSymbol("TUNER_get_afc_hint"))
+
+#else
+
+#define xf86_Detect_FI1236 Detect_FI1236
+#define xf86_FI1236_set_tuner_type FI1236_set_tuner_type
+#define xf86_TUNER_set_frequency TUNER_set_frequency
+#define xf86_FI1236_AFC FI1236_AFC
+#define xf86_TUNER_get_afc_hint TUNER_get_afc_hint
+
+#endif
+
+#endif
diff --git a/hw/xfree86/i2c/fi1236_module.c b/hw/xfree86/i2c/fi1236_module.c
new file mode 100644
index 000000000..1363b60ba
--- /dev/null
+++ b/hw/xfree86/i2c/fi1236_module.c
@@ -0,0 +1,26 @@
+
+#include "xf86Module.h"
+
+static MODULESETUPPROTO(fi1236Setup);
+
+
+static XF86ModuleVersionInfo fi1236VersRec =
+{
+ "fi1236",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_VIDEODRV, /* This needs the video driver ABI */
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_NONE,
+ {0,0,0,0}
+};
+
+XF86ModuleData fi1236ModuleData = { &fi1236VersRec, fi1236Setup, NULL };
+
+static pointer
+fi1236Setup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ return (pointer)1;
+}
diff --git a/hw/xfree86/i2c/i2c_def.h b/hw/xfree86/i2c/i2c_def.h
new file mode 100644
index 000000000..84b9c5d91
--- /dev/null
+++ b/hw/xfree86/i2c/i2c_def.h
@@ -0,0 +1,29 @@
+#ifndef __I2C_DEF_H__
+#define __I2C_DEF_H__
+
+/* the following are a workaround for possible loader bug..
+ WATCH function types ! */
+#if XFree86LOADER
+
+#define CreateI2CBusRec ((pointer (*)(void))LoaderSymbol("xf86CreateI2CBusRec"))
+#define DestroyI2CBusRec ((pointer (*)(I2CBusPtr, Bool, Bool))LoaderSymbol("xf86DestroyI2CBusRec"))
+#define I2CBusInit ((Bool (*)(pointer))LoaderSymbol("xf86I2CBusInit"))
+#define I2C_WriteRead ((Bool (*)(I2CDevPtr, I2CByte *, int, I2CByte *, int))LoaderSymbol("xf86I2CWriteRead"))
+#define CreateI2CDevRec ((pointer (*)(void))LoaderSymbol("xf86CreateI2CDevRec"))
+#define I2CDevInit ((Bool (*)(I2CDevPtr))LoaderSymbol("xf86I2CDevInit"))
+#define I2CProbeAddress ((Bool (*)(I2CBusPtr,I2CSlaveAddr))LoaderSymbol("xf86I2CProbeAddress"))
+
+#else
+
+#define CreateI2CBusRec xf86CreateI2CBusRec
+#define DestroyI2CBusRec xf86DestroyI2CBusRec
+#define I2CBusInit xf86I2CBusInit
+#define I2C_WriteRead xf86I2CWriteRead
+#define CreateI2CDevRec xf86CreateI2CDevRec
+#define I2CDevInit xf86I2CDevInit
+#define I2CProbeAddress xf86I2CProbeAddress
+
+#endif
+
+
+#endif
diff --git a/hw/xfree86/i2c/msp3430.c b/hw/xfree86/i2c/msp3430.c
new file mode 100644
index 000000000..699cfa495
--- /dev/null
+++ b/hw/xfree86/i2c/msp3430.c
@@ -0,0 +1,718 @@
+#include "xf86.h"
+#include "xf86i2c.h"
+#include "msp3430.h"
+#include "i2c_def.h"
+
+#define CONTROL 0x00
+#define WR_DEM 0x10
+#define RD_DEM 0x11
+#define WR_DSP 0x12
+#define RD_DSP 0x13
+
+
+void InitMSP34xxG(MSP3430Ptr m);
+void InitMSP34x5D(MSP3430Ptr m);
+void CheckModeMSP34x5D(MSP3430Ptr m);
+char *MSP_getProductName (CARD16 product_id);
+void mpause(int milliseconds);
+
+#define __MSPDEBUG__ 0
+
+#if __MSPDEBUG__ > 3
+
+void MSPBeep(MSP3430Ptr m, CARD8 freq);
+#define __MSPBEEP MSPBeep(m,0x14);
+
+#else
+
+#define __MSPBEEP
+#endif
+
+static void SetMSP3430Control(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegValueHigh, CARD8 RegValueLow)
+{
+ I2CByte data[3];
+
+ data[0]=RegAddress;
+ data[1]=RegValueHigh;
+ data[2]=RegValueLow;
+
+ I2C_WriteRead(&(m->d),data,3,NULL,0);
+}
+
+static void SetMSP3430Data(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegSubAddressHigh, CARD8 RegSubAddressLow,
+ CARD8 RegValueHigh, CARD8 RegValueLow)
+{
+ I2CByte data[5];
+#ifdef MSP_DEBUG
+ if(!m->registers_present[RegSubAddressLow]){
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_ERROR, "Attempt to access non-existent register in MSP34xxX: 0x%02x 0x%02x 0x%02x <- 0x%02x 0x%02x\n",
+ RegAddress, RegSubAddressHigh, RegSubAddressLow, RegValueHigh, RegValueLow);
+ }
+#endif
+
+ data[0] = RegAddress;
+ data[1] = RegSubAddressHigh;
+ data[2] = RegSubAddressLow;
+ data[3] = RegValueHigh;
+ data[4] = RegValueLow;
+
+ I2C_WriteRead(&(m->d),data,5,NULL,0);
+}
+
+static void GetMSP3430Data(MSP3430Ptr m, CARD8 RegAddress, CARD8 RegSubAddressHigh, CARD8 RegSubAddressLow,
+ CARD8 *RegValueHigh, CARD8 *RegValueLow)
+{
+ I2CByte send[3];
+ I2CByte receive[2];
+
+ send[0] = RegAddress;
+ send[1] = RegSubAddressHigh;
+ send[2] = RegSubAddressLow;
+
+ I2C_WriteRead(&(m->d), send, 3, receive, 2);
+
+ *RegValueHigh = receive[0];
+ *RegValueLow = receive[1];
+}
+
+static void MSP3430DumpStatus(MSP3430Ptr m)
+{
+CARD8 status_hi, status_lo;
+CARD8 subaddr, data[2];
+
+GetMSP3430Data(m, RD_DEM, 0x02, 0x00, &status_hi, &status_lo);
+xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: SAP(8)=%d mono/NICAM(7)=%d stereo=%d %s O_1=%d O_0=%d 2nd car=%d 1st car=%d\n",
+ status_hi & 1, (status_lo>>7) & 1, (status_lo>>6)&1,
+ (status_lo>>5)? ( (status_hi>>1)&1? "bad NICAM reception" : "NICAM" ) :
+ ((status_hi>>1)&1 ? "bogus" : "ANALOG FM/AM") ,
+ (status_lo>>4)&1, (status_lo>>3)&1,!( (status_lo>>2)&1), !((status_lo>>1)&1));
+
+GetMSP3430Data(m, RD_DEM, 0x00, 0x7E, &status_hi, &status_lo);
+xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: standard result=0x%02x%02x\n",
+ status_hi, status_lo);
+subaddr=0x0;
+I2C_WriteRead(&(m->d), &subaddr, 1, data, 2);
+xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP34xx: control=0x%02x%02x\n",
+ data[1], data[0]);
+}
+
+/* wrapper */
+void InitMSP3430(MSP3430Ptr m)
+{
+ #if __MSPDEBUG__ > 1
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"InitMSP3430(m->connector=%d, m->standard=%d, m->chip_family=%d)\n",
+ m->connector, m->standard, m->chip_family);
+ #endif
+ switch (m->chip_family) {
+ case MSPFAMILY_34x0G:
+ InitMSP34xxG(m);
+ break;
+ case MSPFAMILY_34x5G:
+ InitMSP34xxG(m);
+ break;
+ case MSPFAMILY_34x5D:
+ InitMSP34x5D(m);
+ break;
+ }
+}
+
+/*-----------------------------------------------------------------
+| common functions for all MSP34xx chips
+|----------------------------------------------------------------*/
+
+MSP3430Ptr DetectMSP3430(I2CBusPtr b, I2CSlaveAddr addr)
+{
+ MSP3430Ptr m;
+ I2CByte a;
+ CARD8 hardware_version, major_revision, product_code, rom_version;
+ Bool supported;
+
+ m = xcalloc(1,sizeof(MSP3430Rec));
+ if(m == NULL)return NULL;
+ m->d.DevName = strdup("MSP34xx");
+ m->d.SlaveAddr = addr;
+ m->d.pI2CBus = b;
+ m->d.NextDev = NULL;
+ m->d.StartTimeout = b->StartTimeout;
+ m->d.BitTimeout = b->BitTimeout;
+ m->d.AcknTimeout = b->AcknTimeout;
+ m->d.ByteTimeout = b->ByteTimeout;
+
+ if(!I2C_WriteRead(&(m->d), NULL, 0, &a, 1))
+ {
+ xfree(m->d.DevName);
+ xfree(m);
+ return NULL;
+ }
+
+
+ m->standard=MSP3430_NTSC;
+ m->connector=MSP3430_CONNECTOR_1;
+ m->mode=MSPMODE_STEREO_A; /*stereo or chanel A if avail. */
+ m->c_format=MSPFORMAT_UNKNOWN;
+ m->c_standard=MSPSTANDARD_UNKNOWN;
+ m->c_matrix=m->c_fmmatrix=m->c_source=0;
+ m->volume=0;
+ m->recheck=FALSE;
+
+ GetMSP3430Data(m, RD_DSP, 0x00, 0x1E, &hardware_version, &major_revision);
+ GetMSP3430Data(m, RD_DSP, 0x00, 0x1F, &product_code, &rom_version);
+ m->hardware_version=hardware_version;
+ m->major_revision=major_revision;
+ m->product_code=product_code;
+ m->rom_version=rom_version;
+
+ m->chip_id=((major_revision << 8) | product_code);
+
+ supported=FALSE;
+ switch (major_revision) {
+ case 4: /* 34xxD */
+ switch (product_code) {
+ case 0x05: /* 3405D */
+ case 0x0A: /* 3410D */
+ case 0x0F: /* 3415D */
+ m->chip_family=MSPFAMILY_34x5D;
+ m->recheck=TRUE;
+ supported=TRUE;
+ break;
+ default:
+ m->chip_family=MSPFAMILY_34x0D;
+ }
+ break;
+ case 7: /* 34xxG */
+ switch(product_code){
+ case 0x00:
+ case 0x0A:
+ case 0x1E:
+ case 0x28:
+ case 0x32:
+ m->chip_family=MSPFAMILY_34x0G;
+ supported=TRUE;
+ break;
+ case 0x0f:
+ case 0x19:
+ case 0x2d:
+ case 0x37:
+ case 0x41:
+ m->chip_family=MSPFAMILY_34x5G;
+ supported=TRUE;
+ #ifdef MSP_DEBUG
+ memset(m->registers_present, 0, 256);
+ #define A(num) m->registers_present[(num)]=1;
+ #define B(num1, num2) memset(&(m->registers_present[num1]), 1, num2-num1);
+ A(0x20)
+ A(0x30)
+ A(0x40)
+ A(0x00)
+ B(0x01, 0x08)
+ B(0x0B, 0x0E)
+ A(0x10)
+ B(0x12,0x14)
+ A(0x16)
+ A(0x29)
+ #undef B
+ #undef A
+ #endif
+ break;
+ default:
+ m->chip_family=MSPFAMILY_UNKNOWN;
+ }
+ break;
+ default:
+ m->chip_family=MSPFAMILY_UNKNOWN;
+ }
+
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "Found %s%s, rom version 0x%02x, chip_id=0x%04x\n",
+ MSP_getProductName(m->chip_id), supported?"":" (unsupported)", rom_version, m->chip_id);
+
+ if (!supported) {
+ xfree(m->d.DevName);
+ xfree(m);
+ return NULL;
+ }
+ if(!I2CDevInit(&(m->d)))
+ {
+ xfree(m->d.DevName);
+ xfree(m);
+ return NULL;
+ }
+
+ return m;
+}
+
+void ResetMSP3430(MSP3430Ptr m)
+{
+ /* Reset the MSP3430 */
+ SetMSP3430Control(m, 0x00, 0x80, 0x00);
+ /* Set it back to normal operation */
+ SetMSP3430Control(m, 0x00, 0x00, 0x00);
+
+ m->c_format=MSPFORMAT_UNKNOWN;
+ m->c_standard=MSPSTANDARD_UNKNOWN;
+ m->c_matrix=m->c_fmmatrix=m->c_source=0;
+ m->volume=0;
+}
+
+void MSP3430SetVolume (MSP3430Ptr m, CARD8 value)
+{
+ CARD8 result;
+ CARD8 old_volume;
+
+#if 0
+ GetMSP3430Data(m, RD_DSP, 0x00, 0x00, &old_volume, &result);
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP3430 result 0x%02x\n", result);
+#endif
+ /* save an extra Get call */
+ result=0;
+
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x00, value, result);
+
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x07, value, 0);
+ m->volume=value;
+
+#if __MSPDEBUG__ > 2
+ MSP3430DumpStatus(m);
+ __MSPBEEP
+ GetMSP3430Data(m, RD_DSP, 0x00, 0x00, &old_volume, &result);
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "MSP3430 volume 0x%02x\n",value);
+#endif
+}
+
+
+void MSP3430SetSAP (MSP3430Ptr m, int mode)
+{
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex, X_INFO, "Put actual code to change SAP here\n");
+
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x08, mode & 0xff, 0x20);
+}
+
+
+#if 0
+void MSP3430SetSource(MSP3430Ptr m, CARD8 value)
+{
+ /* Write to DSP, register 0x0008, (loudspeaker channel source/matrix) */
+ /* This sets the source to the TV tuner, for stereo operation */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x08, value, 0x20);
+}
+#endif
+
+
+char *MSP_getProductName (CARD16 product_id)
+{
+ switch (product_id) {
+ case 0x0400: return "MSP3400D";
+ case 0x040a: return "MSP3410D";
+ case 0x0405: return "MSP3405D";
+ case 0x040f: return "MSP3415D";
+ case 0x0700: return "MSP3400G";
+ case 0x070a: return "MSP3410G";
+ case 0x071e: return "MSP3430G";
+ case 0x0728: return "MSP3440G";
+ case 0x0732: return "MSP3450G";
+ case 0x070f: return "MSP3415G";
+ case 0x0719: return "MSP3425G";
+ case 0x072d: return "MSP3445G";
+ case 0x0737: return "MSP3455G";
+ case 0x0741: return "MSP3465G";
+ }
+ return "MSP - unknown type";
+}
+
+#if __MSPDEBUG__ > 2
+/*puts beep in MSP output
+ freq = 0x01 - 16Hz ... 0x40 - 1kHz ... 0xff - 4kHz
+*/
+void MSPBeep(MSP3430Ptr m, CARD8 freq) {
+ SetMSP3430Data (m, WR_DSP, 0x00, freq, 0x7f, 0x40);
+ mpause(100);
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, 0x00);
+}
+#endif
+
+void mpause(int milliseconds) {
+ int i,m;
+ m=milliseconds/20;
+ for (i=0;i<m;i++) usleep(20000);
+}
+
+/*-----------------------------------------------------------------
+| specific functions for all MSP34xxG chips
+|----------------------------------------------------------------*/
+
+void InitMSP34xxG(MSP3430Ptr m)
+{
+
+ #if __MSPDEBUG__ > 1
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"InitMSP34xxG(m->connector=%d, m->standard=%d, m->chip_family=%d)\n",
+ m->connector, m->standard, m->chip_family);
+ #endif
+ /* Reset MSP3430 */
+ SetMSP3430Control(m, 0x00, 0x80, 0x00);
+ /* Set it back to normal operation */
+ SetMSP3430Control(m, 0x00, 0x00, 0x00);
+
+ /*set MODUS register */
+ /* bits: 0 - automatic sound detection */
+ /* 1 - enable STATUS change */
+ /* 12 - detect 6.5 Mhz carrier as D/K1, D/K2 or D/K NICAM (does not seem to work ) */
+ /* 13 - detect 4.5 Mhz carrier as BTSC */
+ if ( (m->standard & 0xff) == MSP3430_PAL )
+ {
+ SetMSP3430Data(m, WR_DEM, 0x00, 0x30, 0x30, 0x03|0x08); /* make O_ pins tristate */
+ /* PAL standard */
+ SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x01); /* possibly wrong */
+ } else {
+ SetMSP3430Data(m, WR_DEM, 0x00, 0x30, 0x20, 0x03|0x08);
+ /* standard selection is M-BTSC-Stereo */
+ SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x20);
+ }
+
+ switch(m->connector){
+ case MSP3430_CONNECTOR_1:
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x03, 0x20);
+ break;
+ case MSP3430_CONNECTOR_2:
+ /* this has not been checked yet.. could be bogus */
+ /* SCART Input Prescale: 0 dB gain */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x02, 0x20);
+ break;
+ case MSP3430_CONNECTOR_3:
+ default:
+ /* SCART Input Prescale: 0 dB gain */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
+
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x08, 0x02, 0x20);
+ break;
+ }
+
+ switch(m->standard){
+ case MSP3430_PAL:
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x10, 0x00, 0x5a);
+ SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x03);
+ /* Set volume to FAST_MUTE. */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
+ break;
+ case MSP3430_PAL_DK1:
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x10, 0x00, 0x5a);
+ SetMSP3430Data(m, WR_DEM, 0x00, 0x20, 0x00, 0x04);
+ /* Set volume to FAST_MUTE. */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
+ break;
+ case MSP3430_SECAM: /* is this right ? */
+ case MSP3430_NTSC:
+ /* Write to DSP, register 0x000E, (prescale FM/FM matrix) */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x0e, 0x24, 0x03);
+
+ /* Set volume to FAST_MUTE. */
+ SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);
+ break;
+ }
+
+}
+
+/*-----------------------------------------------------------------
+| specific functions for all MSP34x5D chips
+|----------------------------------------------------------------*/
+
+void InitMSP34x5D(MSP3430Ptr m)
+{
+int count;
+CARD8 high,low;
+CARD16 result,standard;
+CARD16 peak;
+
+
+if (m->c_format==MSPFORMAT_UNKNOWN) ResetMSP3430(m);
+else {
+ /*mute volume*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x00, 0x00, 0x00);
+}
+
+
+
+ switch(m->connector){
+ case MSP3430_CONNECTOR_2:
+ case MSP3430_CONNECTOR_3:
+ if (m->c_format!=MSPFORMAT_SCART) {
+ /* SCART Input Prescale: 0 dB gain */
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0d, 0x19, 0x00);
+ /* this has not been checked yet.. could be bogus */
+ m->c_format=MSPFORMAT_SCART; /*stereo*/
+ }
+ break;
+ case MSP3430_CONNECTOR_1:
+ default:
+
+ switch ( m->standard & 0x00ff ) {
+ case MSP3430_PAL:
+ switch( m->standard ) {
+ case MSP3430_PAL_DK1:
+ standard=MSPSTANDARD_FM_DK1;
+ break;
+/* case MSP3430_PAL_DK2:
+ standard=MSPSTANDARD_FM_DK2;
+ break;
+ case MSP3430_PAL_BG:
+ may be FM stereo (Germany) or FM NICAM (Scandinavia,spain)
+ standard=MSPSTANDARD_AUTO;
+ break;
+*/
+ default:
+ standard=MSPSTANDARD_AUTO;
+ }
+ break;
+ case MSP3430_SECAM:
+ standard=MSPSTANDARD_AUTO;
+ case MSP3430_NTSC:
+ /* Only MSP34x5 supported format - Korean NTSC-M*/
+ standard=MSPSTANDARD_FM_M;
+ default:
+ standard=MSPSTANDARD_AUTO;
+ }
+
+ /*no NICAM support in MSP3410D - force to autodetect*/
+ if ((m->chip_id==0x405) && (standard>=MSPSTANDARD_NICAM_BG))
+ standard=MSPSTANDARD_AUTO;
+
+ if (m->c_standard != standard) {
+
+ SetMSP3430Data (m, WR_DEM, 0x00, 0x20, standard>>8, standard & 0xFF);
+ if (standard==MSPSTANDARD_AUTO) {
+ count = 50; /* time shouldn't exceed 1s, just in case */
+ do {
+ usleep(20000);
+ GetMSP3430Data (m, RD_DEM, 0x00, 0x7e, &high, &low);
+ result = ( high << 8 ) | low;
+ --count;
+ } while( result > 0x07ff && count > 0 );
+
+ if ((result > MSPSTANDARD_AUTO))
+ standard=result;
+ else standard=MSPSTANDARD_UNKNOWN;
+#if __MSPDEBUG__ > 1
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Detected audio standard: %d\n",result);
+#endif
+ /* result = MSPSTANDARD_NICAM_L can be one of:
+ SECAM_L - MSPSTANDARD_NICAM_L
+ D/K1 - MSPSTANDARD_FM_DK1
+ D/K2 - MSPSTANDARD_FM_DK2
+ D/K-NICAM - MSPSTANDARD_NICAM_DK*/
+ if( standard == MSPSTANDARD_NICAM_L ) {
+ if ((m->standard & 0x00ff)==MSP3430_PAL) {
+ /* force PAL D/K */
+ standard=MSPSTANDARD_FM_DK1;
+ SetMSP3430Data (m, WR_DEM, 0x00, 0x20, standard>>8, standard & 0xFF);
+#if __MSPDEBUG__ > 1
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO, "Detected 6.5MHz carrier - forced to D/K1 !!!\n" );
+#endif
+ }
+ }
+ }
+ m->c_standard=standard;
+ } /*end - standard changed*/
+ else {
+ if (standard<MSPSTANDARD_NICAM_BG) {
+ /* get old value of ident. mode register*/
+ GetMSP3430Data (m, RD_DSP, 0x00, 0x15, &high, &low);
+ /* reset Ident-Filter */
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, 0x3F);
+ /* put back old value to ident. mode register*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x14, 0x00, low);
+ }
+ }
+
+ if (standard<=MSPSTANDARD_AUTO) {
+ m->c_format=MSPFORMAT_1xFM;
+ }
+ else if (standard<MSPSTANDARD_NICAM_BG) {
+ /* set FM prescale */
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x30, 0);
+ /* set FM deemphasis*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0f, ((standard==MSPSTANDARD_FM_M)?0:1), 0);
+
+ /* check if FM2 carrier is present */
+ /*turn off FM DC Notch*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x17, 0x00, 0x3f);
+ /*matrix source for Quasi-Peak Detector - stereo: ch2->L ch1->R*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0c, 0x00, 0x20);
+
+ mpause(250);
+ GetMSP3430Data (m, RD_DSP, 0x00, 0x1A, &high, &low);
+ peak = (high << 8) | low;
+#if __MSPDEBUG__ > 1
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Second carrier Quasi-Peak detection: %d\n",peak);
+#endif
+ /*turn on FM DC Notch*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x17, 0x00, 0x00);
+
+ if (peak<5) {
+ /* if second carrier not detected - only mono from first carrier*/
+ m->c_format=MSPFORMAT_1xFM;
+ }
+ else {
+ m->c_format=MSPFORMAT_2xFM;
+ /*start of FM identification process - FM_WAIT
+ wait at least 0.5s - used 1s - gives beter resolution*/
+ mpause(1000);
+ }
+ }
+ else {
+ if (standard==MSPSTANDARD_NICAM_L) {
+ m->c_format=MSPFORMAT_NICAM_AM;
+ /* set AM prescale */
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x7C, 0);
+ }
+ else {
+ m->c_format=MSPFORMAT_NICAM_FM;
+ /* set FM prescale */
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, 0x30, 0);
+ }
+ /* set FM deemphasis*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0f, 0x00, 0);
+ /* set NICAM prescale to 0dB */
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x10, 0x20, 0);
+ }
+
+ break;
+ } /*end - case conector*/
+
+ CheckModeMSP34x5D(m);
+
+ /* Set volume to FAST_MUTE. */
+ /*SetMSP3430Data(m, WR_DSP, 0x00, 0x00, 0xFF, 0x00);*/
+ /*set volume*/
+ MSP3430SetVolume(m,m->volume);
+
+ __MSPBEEP
+
+
+} /* EnableMSP34x5D ()... */
+
+
+
+
+void CheckModeMSP34x5D(MSP3430Ptr m) {
+ const char stereo_on=25;
+ const char stereo_off=20;
+ const char dual_on=-stereo_on;
+ const char dual_off=-stereo_off;
+ char detect;
+ CARD8 matrix, fmmatrix, source, high, low;
+ char *msg;
+
+ fmmatrix=0; /*no matrix*/
+ source=0; /*FM*/
+ switch (m->c_format) {
+ case MSPFORMAT_NICAM_FM:
+ case MSPFORMAT_NICAM_AM:
+ case MSPFORMAT_SCART:
+ source=( (m->c_format == MSPFORMAT_SCART)?2:1 );
+ switch (m->mode) {
+ case MSPMODE_MONO:
+ matrix=0x30; /*MONO*/
+ break;
+ case MSPMODE_A:
+ matrix=0x00; /*A*/
+ break;
+ case MSPMODE_B:
+ matrix=0x10; /*B*/
+ break;
+ default:
+ matrix=0x20; /*STEREO*/
+ break;
+ }
+ break;
+ default:
+ case MSPFORMAT_1xFM:
+ matrix=0x00; /*A*/
+ break;
+ case MSPFORMAT_2xFM:
+ switch (m->mode) {
+ case MSPMODE_MONO:
+ matrix=0x30; /*MONO*/
+ break;
+ case MSPMODE_STEREO:
+ matrix=0x20; /*STEREO*/
+ fmmatrix=((m->c_standard==MSPSTANDARD_FM_M)?2:1);
+ break;
+ case MSPMODE_AB:
+ matrix=0x20; /*STEREO*/
+ break;
+ case MSPMODE_A:
+ matrix=0x00; /*A*/
+ break;
+ case MSPMODE_B:
+ matrix=0x10; /*B*/
+ break;
+ default:
+ /*FM_IDENT_CHECK*/
+ GetMSP3430Data (m, RD_DSP, 0x00, 0x18, &high, &low);
+ detect=(char)high;
+#if __MSPDEBUG__ > 1
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Stereo Detection Register: %d\n",detect);
+#endif
+ if (detect>=((m->c_mode==MSPMODE_STEREO)?stereo_off:stereo_on)) {
+ m->c_mode=MSPMODE_STEREO;
+ matrix=0x20; /*STEREO*/
+ fmmatrix=((m->c_standard==MSPSTANDARD_FM_M)?2:1);
+ }
+ else if (detect<=((m->c_mode==MSPMODE_AB)?dual_off:dual_on)) {
+ m->c_mode=MSPMODE_AB;
+ switch (m->mode) {
+ case MSPMODE_STEREO_AB: matrix=0x20; break;
+ case MSPMODE_STEREO_B: matrix=0x10; break;
+ default:
+ case MSPMODE_A: matrix=0x00; break;
+ }
+ }
+ else {
+ m->c_mode=MSPMODE_MONO;
+ matrix=0x30; /*MONO*/
+ }
+ break;
+ } /* end - case mode*/
+ break;
+ }
+
+ if (m->c_fmmatrix != fmmatrix) {
+ GetMSP3430Data (m, RD_DSP, 0x00, 0x0e, &high, &low);
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x0e, high, fmmatrix);
+ m->c_fmmatrix = fmmatrix;
+ }
+
+ if ((m->c_matrix != matrix) || (m->c_source != source)) {
+ /*set chanel source and matrix for loudspeaker*/
+ SetMSP3430Data (m, WR_DSP, 0x00, 0x08, source, matrix);
+
+ m->c_matrix = matrix;
+ m->c_source = source;
+ }
+
+ if ( ((m->c_format) & 0xF0) == MSPFORMAT_NICAM)
+ SetMSP3430Data (m, WR_DEM, 0x00, 0x21, 0, 1);
+
+#if __MSPDEBUG__ > 0
+ switch (matrix) {
+ case 0x30: /*MONO*/
+ msg="MONO";
+ break;
+ case 0x00: /*LEFT*/
+ msg="MONO/CHANNEL_1";
+ break;
+ case 0x10: /*RIGHT*/
+ msg="MONO/CHANNEL_2";
+ break;
+ case 0x20: /*LEFT*/
+ msg="STEREO";
+ break;
+ default:
+ msg="unknown";
+ break;
+ }
+ xf86DrvMsg(m->d.pI2CBus->scrnIndex,X_INFO,"Audio mode set to: %s\n",msg);
+#endif
+}
+
diff --git a/hw/xfree86/i2c/msp3430.h b/hw/xfree86/i2c/msp3430.h
new file mode 100644
index 000000000..8726bacf5
--- /dev/null
+++ b/hw/xfree86/i2c/msp3430.h
@@ -0,0 +1,126 @@
+#ifndef __MSP3430_H__
+#define __MSP3430_H__
+
+#include "xf86i2c.h"
+
+typedef struct {
+ I2CDevRec d;
+
+ int standard;
+ int connector;
+ int mode;
+
+ CARD8 hardware_version, major_revision, product_code, rom_version;
+#ifdef MSP_DEBUG
+ CARD8 registers_present[256];
+#endif
+
+ CARD16 chip_id;
+ CARD8 chip_family;
+ Bool recheck; /*reinitialization needed after channel change */
+ CARD8 c_format; /*current state of audio format */
+ CARD16 c_standard; /*current state of standard register */
+ CARD8 c_source; /*current state of source register */
+ CARD8 c_matrix; /*current state of matrix register */
+ CARD8 c_fmmatrix; /*current state of fmmatrix register */
+ int c_mode; /* current state of mode for autoswitchimg */
+ CARD8 volume;
+ } MSP3430Rec, * MSP3430Ptr;
+
+
+#define MSP3430_ADDR_1 0x80
+#define MSP3430_ADDR_2 0x84
+#define MSP3430_ADDR_3 0x88
+
+#define MSP3430_PAL 1
+#define MSP3430_NTSC 2
+#define MSP3430_PAL_DK1 (0x100 | MSP3430_PAL)
+#define MSP3430_SECAM 3
+
+#define MSP3430_CONNECTOR_1 1 /* tuner on AIW cards */
+#define MSP3430_CONNECTOR_2 2 /* SVideo on AIW cards */
+#define MSP3430_CONNECTOR_3 3 /* composite on AIW cards */
+
+#define MSP3430_ADDR(a) ((a)->d.SlaveAddr)
+
+#define MSP3430_FAST_MUTE 0xFF
+/* a handy volume transform function, -1000..1000 -> 0x01..0x7F */
+#define MSP3430_VOLUME(value) (0x01+(0x7F-0x01)*log(value+1001)/log(2001))
+
+/*----------------------------------------------------------*/
+
+/* MSP chip families */
+#define MSPFAMILY_UNKNOWN 0
+#define MSPFAMILY_34x0D 1
+#define MSPFAMILY_34x5D 2
+#define MSPFAMILY_34x0G 3
+#define MSPFAMILY_34x5G 4
+
+/* values for MSP standard */
+#define MSPSTANDARD_UNKNOWN 0x00
+#define MSPSTANDARD_AUTO 0x01
+#define MSPSTANDARD_FM_M 0x02
+#define MSPSTANDARD_FM_BG 0x03
+#define MSPSTANDARD_FM_DK1 0x04
+#define MSPSTANDARD_FM_DK2 0x04
+#define MSPSTANDARD_NICAM_BG 0x08
+#define MSPSTANDARD_NICAM_L 0x09
+#define MSPSTANDARD_NICAM_I 0x0A
+#define MSPSTANDARD_NICAM_DK 0x0B
+
+/* values for MSP format */
+#define MSPFORMAT_UNKNOWN 0x00
+#define MSPFORMAT_FM 0x10
+#define MSPFORMAT_1xFM 0x00|MSPFORMAT_FM
+#define MSPFORMAT_2xFM 0x01|MSPFORMAT_FM
+#define MSPFORMAT_NICAM 0x20
+#define MSPFORMAT_NICAM_FM 0x00|MSPFORMAT_NICAM
+#define MSPFORMAT_NICAM_AM 0x01|MSPFORMAT_NICAM
+#define MSPFORMAT_SCART 0x30
+
+/* values for MSP mode */
+#define MSPMODE_UNKNOWN 0
+/* automatic modes */
+#define MSPMODE_STEREO_AB 1
+#define MSPMODE_STEREO_A 2
+#define MSPMODE_STEREO_B 3
+/* forced modes */
+#define MSPMODE_MONO 4
+#define MSPMODE_STEREO 5
+#define MSPMODE_AB 6
+#define MSPMODE_A 7
+#define MSPMODE_B 8
+/*----------------------------------------------------------*/
+
+void InitMSP3430(MSP3430Ptr m);
+MSP3430Ptr DetectMSP3430(I2CBusPtr b, I2CSlaveAddr addr);
+void ResetMSP3430(MSP3430Ptr m);
+void MSP3430SetVolume (MSP3430Ptr m, CARD8 value);
+void MSP3430SetSAP (MSP3430Ptr m, int mode);
+
+#define MSP3430SymbolsList \
+ "InitMSP3430", \
+ "DetectMSP3430", \
+ "ResetMSP3430", \
+ "MSP3430SetVolume", \
+ "MSP3430SetSAP"
+
+#ifdef XFree86LOADER
+
+#define xf86_DetectMSP3430 ((MSP3430Ptr (*)(I2CBusPtr, I2CSlaveAddr))LoaderSymbol("DetectMSP3430"))
+#define xf86_ResetMSP3430 ((void (*)(MSP3430Ptr))LoaderSymbol("ResetMSP3430"))
+#define xf86_MSP3430SetVolume ((void (*)(MSP3430Ptr, CARD8))LoaderSymbol("MSP3430SetVolume"))
+#define xf86_MSP3430SetSAP ((void (*)(MSP3430Ptr, int))LoaderSymbol("MSP3430SetSAP"))
+#define xf86_InitMSP3430 ((void (*)(MSP3430Ptr))LoaderSymbol("InitMSP3430"))
+
+#else
+
+#define xf86_DetectMSP3430 DetectMSP3430
+#define xf86_ResetMSP3430 ResetMSP3430
+#define xf86_MSP3430SetVolume MSP3430SetVolume
+#define xf86_MSP3430SetSAP MSP3430SetSAP
+#define xf86_InitMSP3430 InitMSP3430
+
+#endif
+
+#endif
diff --git a/hw/xfree86/i2c/msp3430_module.c b/hw/xfree86/i2c/msp3430_module.c
new file mode 100644
index 000000000..5d963e914
--- /dev/null
+++ b/hw/xfree86/i2c/msp3430_module.c
@@ -0,0 +1,26 @@
+
+#include "xf86Module.h"
+
+static MODULESETUPPROTO(msp3430Setup);
+
+
+static XF86ModuleVersionInfo msp3430VersRec =
+{
+ "msp3430",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_VIDEODRV, /* This needs the video driver ABI */
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_NONE,
+ {0,0,0,0}
+};
+
+XF86ModuleData msp3430ModuleData = { &msp3430VersRec, msp3430Setup, NULL };
+
+static pointer
+msp3430Setup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ return (pointer)1;
+}
diff --git a/hw/xfree86/i2c/tda8425.c b/hw/xfree86/i2c/tda8425.c
new file mode 100644
index 000000000..8ddf7267e
--- /dev/null
+++ b/hw/xfree86/i2c/tda8425.c
@@ -0,0 +1,74 @@
+#include "xf86.h"
+#include "xf86i2c.h"
+#include "tda8425.h"
+#include "i2c_def.h"
+
+#define TDA8425(a,b) { \
+ data[0]=a; \
+ data[1]=b; \
+ I2C_WriteRead(&(t->d), data, 2, NULL, 0); \
+ }
+
+TDA8425Ptr Detect_tda8425(I2CBusPtr b, I2CSlaveAddr addr, Bool force)
+{
+ TDA8425Ptr t;
+
+ t = xcalloc(1, sizeof(TDA8425Rec));
+ if(t == NULL) return NULL;
+ t->d.DevName = "TDA8425 BTSC Stereo Audio Processor";
+ t->d.SlaveAddr = addr;
+ t->d.pI2CBus = b;
+ t->d.NextDev = NULL;
+ t->d.StartTimeout = b->StartTimeout;
+ t->d.BitTimeout = b->BitTimeout;
+ t->d.AcknTimeout = b->AcknTimeout;
+ t->d.ByteTimeout = b->ByteTimeout;
+
+ if(!force && !I2CProbeAddress(b, addr))
+ {
+ xfree(t);
+ return NULL;
+ }
+
+ /* set default parameters */
+ if(!I2CDevInit(&(t->d)))
+ {
+ xfree(t);
+ return NULL;
+ }
+
+ return t;
+}
+
+Bool tda8425_init(TDA8425Ptr t)
+{
+ t->stereo = 3; /* 3 = Spacial 2 = Linear 1 = Pseudo 0 = Forced mono */
+ t->v_left = 0xFF; /* FF - C0 */
+ t->v_right = 0xFF; /* FF - C0 */
+ t->bass = 0xF6; /* 0xFF - 0xF0 */
+ t->treble = 0xF6; /* 0xFF - 0xF0 */
+ t->src_sel = 3; /* 3 - stereo */
+ t->mute = TRUE;
+ t->mux = 0; /* 0 - source one, 1 -source 2 */
+
+ tda8425_setaudio(t);
+ return TRUE;
+}
+
+void tda8425_setaudio(TDA8425Ptr t)
+{
+ I2CByte data[2];
+
+ TDA8425(0x00, t->v_left );
+ TDA8425(0x01, t->v_right );
+ TDA8425(0x02, t->bass );
+ TDA8425(0x03, t->treble );
+ TDA8425(0x08, 0xC0 | (t->mute ? 0x20 : 0x0) | (t->stereo << 3) | (t->src_sel << 1) |
+ t->mux);
+}
+
+void tda8425_mute(TDA8425Ptr t, Bool mute)
+{
+ t->mute = mute;
+ tda8425_setaudio(t);
+}
diff --git a/hw/xfree86/i2c/tda8425.h b/hw/xfree86/i2c/tda8425.h
new file mode 100644
index 000000000..ec1908ba9
--- /dev/null
+++ b/hw/xfree86/i2c/tda8425.h
@@ -0,0 +1,53 @@
+#ifndef __TDA8425_H__
+#define __TDA8425_H__
+
+#include "xf86i2c.h"
+
+typedef struct {
+ I2CDevRec d;
+
+ int mux;
+ int stereo;
+ int v_left;
+ int v_right;
+ int bass;
+ int treble;
+ int src_sel;
+ Bool mute;
+ } TDA8425Rec, *TDA8425Ptr;
+
+#define TDA8425_ADDR_1 0x82
+
+/* the third parameter is meant to force detection of tda8425.
+ This is because tda8425 is write-only and complete implementation
+ of I2C protocol is not always available. Besides address there is no good
+ way to autodetect it so we have to _know_ it is there anyway */
+
+TDA8425Ptr Detect_tda8425(I2CBusPtr b, I2CSlaveAddr addr,Bool force);
+Bool tda8425_init(TDA8425Ptr t);
+void tda8425_setaudio(TDA8425Ptr t);
+void tda8425_mute(TDA8425Ptr t, Bool mute);
+
+#define TDA8425SymbolsList \
+ "Detect_tda8425", \
+ "tda8425_init", \
+ "tda8425_setaudio", \
+ "tda8425_mute"
+
+#ifdef XFree86LOADER
+
+#define xf86_Detect_tda8425 ((TDA8425Ptr (*)(I2CBusPtr, I2CSlaveAddr,Bool))LoaderSymbol("Detect_tda8425"))
+#define xf86_tda8425_init ((Bool (*)(TDA8425Ptr))LoaderSymbol("tda8425_init"))
+#define xf86_tda8425_setaudio ((void (*)(TDA8425Ptr))LoaderSymbol("tda8425_setaudio"))
+#define xf86_tda8425_mute ((void (*)(TDA8425Ptr, Bool))LoaderSymbol("tda8425_mute"))
+
+#else
+
+#define xf86_Detect_tda8425 Detect_tda8425
+#define xf86_tda8425_init tda8425_init
+#define xf86_tda8425_setaudio tda8425_setaudio
+#define xf86_tda8425_mute tda8425_mute
+
+#endif
+
+#endif
diff --git a/hw/xfree86/i2c/tda8425_module.c b/hw/xfree86/i2c/tda8425_module.c
new file mode 100644
index 000000000..8ae222268
--- /dev/null
+++ b/hw/xfree86/i2c/tda8425_module.c
@@ -0,0 +1,26 @@
+
+#include "xf86Module.h"
+
+static MODULESETUPPROTO(tda8425Setup);
+
+
+static XF86ModuleVersionInfo tda8425VersRec =
+{
+ "tda8425",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_VIDEODRV, /* This needs the video driver ABI */
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_NONE,
+ {0,0,0,0}
+};
+
+XF86ModuleData tda8425ModuleData = { &tda8425VersRec, tda8425Setup, NULL };
+
+static pointer
+tda8425Setup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ return (pointer)1;
+}
diff --git a/hw/xfree86/i2c/tda9850.c b/hw/xfree86/i2c/tda9850.c
new file mode 100644
index 000000000..708dcccbb
--- /dev/null
+++ b/hw/xfree86/i2c/tda9850.c
@@ -0,0 +1,108 @@
+#include "xf86.h"
+#include "xf86i2c.h"
+#include "tda9850.h"
+#include "i2c_def.h"
+
+#define TDA9850(a,b) { \
+ data[0]=a; \
+ data[1]=b; \
+ I2C_WriteRead(&(t->d), data, 2, NULL, 0); \
+ }
+
+TDA9850Ptr Detect_tda9850(I2CBusPtr b, I2CSlaveAddr addr)
+{
+ TDA9850Ptr t;
+ I2CByte a;
+
+ t = xcalloc(1, sizeof(TDA9850Rec));
+ if(t == NULL) return NULL;
+ switch(addr)
+ {
+ case TDA9850_ADDR_1:
+ t->d.DevName = "TDA9850 BTSC Stereo+SAP Audio Processor";
+ break;
+ default:
+ t->d.DevName = "Generic TDAxxxx";
+ break;
+ }
+ t->d.SlaveAddr = addr;
+ t->d.pI2CBus = b;
+ t->d.NextDev = NULL;
+ t->d.StartTimeout = b->StartTimeout;
+ t->d.BitTimeout = b->BitTimeout;
+ t->d.AcknTimeout = b->AcknTimeout;
+ t->d.ByteTimeout = b->ByteTimeout;
+
+ if(!I2C_WriteRead(&(t->d), NULL, 0, &a, 1))
+ {
+ xfree(t);
+ return NULL;
+ }
+
+ /* set default parameters */
+ if(!I2CDevInit(&(t->d)))
+ {
+ xfree(t);
+ return NULL;
+ }
+
+ return t;
+}
+
+Bool tda9850_init(TDA9850Ptr t)
+{
+ t->stereo = 1;
+ t->sap = 0;
+ t->mute = TRUE;
+ t->sap_mute = TRUE;
+ tda9850_setaudio(t);
+ return TRUE;
+}
+
+void tda9850_setaudio(TDA9850Ptr t)
+{
+CARD8 data[2];
+
+if(t->mux==2)
+{
+ TDA9850(0x04,0x0F); TDA9850(0x05,0x0F); TDA9850(0x06, 0x58);
+ TDA9850(0x07,0x07); TDA9850(0x08,0x00);
+ TDA9850(0x09,0x00); TDA9850(0x0A,0x03);
+} else
+{
+ TDA9850(0x04,0x07); TDA9850(0x05,0x07);
+ TDA9850(0x06,0x58); TDA9850(0x07,0x07);
+ TDA9850(0x08,0x10); TDA9850(0x09,0x10);
+ TDA9850(0x0A,0x03);
+}
+
+TDA9850(0x06,(t->stereo<<6)|(t->sap<<7)|(t->mute?0x8:0)|(t->sap_mute?0x10:0x0));
+}
+
+void tda9850_mute(TDA9850Ptr t, Bool mute)
+{
+CARD8 data[2];
+
+xf86DrvMsg(t->d.pI2CBus->scrnIndex, X_INFO, "tda9850_mute %s\n", mute ? "on" : "off");
+t->mute = mute;
+
+TDA9850(0x06,(t->stereo<<6)|(t->sap<<7)|(t->mute?0x8:0x0)|(t->sap_mute?0x10:0x0));
+}
+
+void tda9850_sap_mute(TDA9850Ptr t, Bool sap_mute)
+{
+CARD8 data[2];
+
+xf86DrvMsg(t->d.pI2CBus->scrnIndex, X_INFO, "tda9850_sap_mute %s\n", sap_mute ? "on" : "off");
+t->sap_mute = sap_mute;
+
+TDA9850(0x06,(t->stereo<<6)|(t->sap<<7)|(t->mute?0x8:0x0)|(t->sap_mute?0x10:0x0));
+}
+
+CARD16 tda9850_getstatus(TDA9850Ptr t)
+{
+CARD16 status;
+
+I2C_WriteRead(&(t->d), NULL, 0, &status, 2);
+return status;
+}
diff --git a/hw/xfree86/i2c/tda9850.h b/hw/xfree86/i2c/tda9850.h
new file mode 100644
index 000000000..2c8ddcc6e
--- /dev/null
+++ b/hw/xfree86/i2c/tda9850.h
@@ -0,0 +1,52 @@
+#ifndef __TDA9850_H__
+#define __TDA9850_H__
+
+#include "xf86i2c.h"
+
+typedef struct {
+ I2CDevRec d;
+
+ int mux;
+ int stereo;
+ int sap;
+ Bool mute;
+ Bool sap_mute;
+ } TDA9850Rec, *TDA9850Ptr;
+
+#define TDA9850_ADDR_1 0xB4
+
+TDA9850Ptr Detect_tda9850(I2CBusPtr b, I2CSlaveAddr addr);
+Bool tda9850_init(TDA9850Ptr t);
+void tda9850_setaudio(TDA9850Ptr t);
+void tda9850_mute(TDA9850Ptr t, Bool mute);
+void tda9850_sap_mute(TDA9850Ptr t, Bool sap_mute);
+CARD16 tda9850_getstatus(TDA9850Ptr t);
+
+#define TDA9850SymbolsList \
+ "Detect_tda9850", \
+ "tda9850_init", \
+ "tda9850_setaudio", \
+ "tda9850_mute", \
+ "tda9850_sap_mute"
+
+#ifdef XFree86LOADER
+
+#define xf86_Detect_tda9850 ((TDA9850Ptr (*)(I2CBusPtr, I2CSlaveAddr))LoaderSymbol("Detect_tda9850"))
+#define xf86_tda9850_init ((Bool (*)(TDA9850Ptr))LoaderSymbol("tda9850_init"))
+#define xf86_tda9850_setaudio ((void (*)(TDA9850Ptr))LoaderSymbol("tda9850_setaudio"))
+#define xf86_tda9850_mute ((void (*)(TDA9850Ptr, Bool))LoaderSymbol("tda9850_mute"))
+#define xf86_tda9850_sap_mute ((void (*)(TDA9850Ptr, Bool))LoaderSymbol("tda9850_sap_mute"))
+#define xf86_tda9850_getstatus ((CARD16 (*)(TDA9850Ptr))LoaderSymbol("tda9850_getstatus"))
+
+#else
+
+#define xf86_Detect_tda9850 Detect_tda9850
+#define xf86_tda9850_init tda9850_init
+#define xf86_tda9850_setaudio tda9850_setaudio
+#define xf86_tda9850_mute tda9850_mute
+#define xf86_tda9850_sap_mute tda9850_sap_mute
+#define xf86_tda9850_getstatus tda9850_getstatus
+
+#endif
+
+#endif
diff --git a/hw/xfree86/i2c/tda9850_module.c b/hw/xfree86/i2c/tda9850_module.c
new file mode 100644
index 000000000..ec57a8a2d
--- /dev/null
+++ b/hw/xfree86/i2c/tda9850_module.c
@@ -0,0 +1,26 @@
+
+#include "xf86Module.h"
+
+static MODULESETUPPROTO(tda9850Setup);
+
+
+static XF86ModuleVersionInfo tda9850VersRec =
+{
+ "tda9850",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_VIDEODRV, /* This needs the video driver ABI */
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_NONE,
+ {0,0,0,0}
+};
+
+XF86ModuleData tda9850ModuleData = { &tda9850VersRec, tda9850Setup, NULL };
+
+static pointer
+tda9850Setup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ return (pointer)1;
+}
diff --git a/hw/xfree86/i2c/tda9885.c b/hw/xfree86/i2c/tda9885.c
new file mode 100644
index 000000000..f91d0fdfe
--- /dev/null
+++ b/hw/xfree86/i2c/tda9885.c
@@ -0,0 +1,98 @@
+#include "xf86.h"
+#include "xf86i2c.h"
+#include "tda9885.h"
+#include "i2c_def.h"
+
+
+TDA9885Ptr Detect_tda9885(I2CBusPtr b, I2CSlaveAddr addr)
+{
+ TDA9885Ptr t;
+ I2CByte a;
+
+ t = xcalloc(1, sizeof(TDA9885Rec));
+ if(t == NULL) return NULL;
+ switch(addr)
+ {
+ case TDA9885_ADDR_1:
+ case TDA9885_ADDR_2:
+ case TDA9885_ADDR_3:
+ case TDA9885_ADDR_4:
+ t->d.DevName = "TDA9885 Alignment-free IF-PLL";
+ break;
+ default:
+ t->d.DevName = "Generic TDAxxxx";
+ break;
+ }
+ t->d.SlaveAddr = addr;
+ t->d.pI2CBus = b;
+ t->d.NextDev = NULL;
+ t->d.StartTimeout = b->StartTimeout;
+ t->d.BitTimeout = b->BitTimeout;
+ t->d.AcknTimeout = b->AcknTimeout;
+ t->d.ByteTimeout = b->ByteTimeout;
+
+ if(!I2C_WriteRead(&(t->d), NULL, 0, &a, 1))
+ {
+ xfree(t);
+ return NULL;
+ }
+
+ /* set default parameters */
+ if(!I2CDevInit(&(t->d)))
+ {
+ xfree(t);
+ return NULL;
+ }
+
+ return t;
+}
+
+Bool tda9885_init(TDA9885Ptr t)
+{
+ t->forced_mute_audio=1;
+ return TRUE;
+}
+
+void tda9885_getstatus(TDA9885Ptr t)
+{
+CARD8 value;
+
+I2C_WriteRead(&(t->d), NULL, 0, &value, 1);
+t->after_reset=value & 1;
+t->afc_status=(value >> 1) & 0xf;
+t->fm_carrier=(value>>5)& 1;
+t->vif_level=(value >>6) & 1;
+t->afc_win=(value >> 7)&1;
+}
+
+void tda9885_setparameters(TDA9885Ptr t)
+{
+CARD8 data[4];
+
+data[0]=0; /* start with subaddress 0 */
+data[1]=(t->sound_trap & 1) |
+ ((t->auto_mute_fm &1)<<1) |
+ ((t->carrier_mode &1)<<2) |
+ ((t->modulation &3)<<3) |
+ ((t->forced_mute_audio &1)<<5) |
+ ((t->port1 & 1)<<6) |
+ ((t->port2 &1)<<7); /* B data */
+data[2]=(t->top_adjustment & 0x1f) |
+ ((t->deemphasis & 0x3)<<5) |
+ ((t->audio_gain & 1) << 7); /* C data */
+data[3]=(t->standard_sound_carrier & 0x3) |
+ ((t->standard_video_if & 0x07)<<2) |
+ ((t->minimum_gain & 0x01)<<5) |
+ ((t->gating & 0x01)<<6) |
+ ((t->vif_agc & 0x01)<<7); /* E data */
+
+I2C_WriteRead(&(t->d), data, 4, NULL, 0);
+}
+
+void tda9885_dumpstatus(TDA9885Ptr t)
+{
+xf86DrvMsg(t->d.pI2CBus->scrnIndex,X_INFO,"TDA9885 status: after_reset=%d afc_status=%d (%3.1f kHz off) fm_carrier=%d vif_level=%d afc_win=%d %s\n",
+ t->after_reset, t->afc_status,
+ (t->afc_status<8)?-12.5-t->afc_status*25.0:-12.5+(16-t->afc_status)*25.0,
+ t->fm_carrier, t->vif_level, t->afc_win, t->afc_win?"VCO in": "VCO out");
+}
diff --git a/hw/xfree86/i2c/tda9885.h b/hw/xfree86/i2c/tda9885.h
new file mode 100644
index 000000000..a63b34764
--- /dev/null
+++ b/hw/xfree86/i2c/tda9885.h
@@ -0,0 +1,72 @@
+#ifndef __TDA9885_H__
+#define __TDA9885_H__
+
+#include "xf86i2c.h"
+
+typedef struct {
+ I2CDevRec d;
+
+ /* write-only parameters */
+ /* B DATA */
+ CARD8 sound_trap;
+ CARD8 auto_mute_fm;
+ CARD8 carrier_mode;
+ CARD8 modulation;
+ CARD8 forced_mute_audio;
+ CARD8 port1;
+ CARD8 port2;
+ /* C DATA */
+ CARD8 top_adjustment;
+ CARD8 deemphasis;
+ CARD8 audio_gain;
+ /* E DATA */
+ CARD8 standard_sound_carrier;
+ CARD8 standard_video_if;
+ CARD8 minimum_gain;
+ CARD8 gating;
+ CARD8 vif_agc;
+ /* read-only values */
+
+ CARD8 after_reset;
+ CARD8 afc_status;
+ CARD8 vif_level;
+ CARD8 afc_win;
+ CARD8 fm_carrier;
+ } TDA9885Rec, *TDA9885Ptr;
+
+#define TDA9885_ADDR_1 0x86
+#define TDA9885_ADDR_2 0x84
+#define TDA9885_ADDR_3 0x96
+#define TDA9885_ADDR_4 0x94
+
+TDA9885Ptr Detect_tda9885(I2CBusPtr b, I2CSlaveAddr addr);
+Bool tda9885_init(TDA9885Ptr t);
+void tda9885_setparameters(TDA9885Ptr t);
+void tda9885_getstatus(TDA9885Ptr t);
+void tda9885_dumpstatus(TDA9885Ptr t);
+
+#define TDA9885SymbolsList \
+ "Detect_tda9885", \
+ "tda9885_init", \
+ "tda9885_setaudio", \
+ "tda9885_mute"
+
+#ifdef XFree86LOADER
+
+#define xf86_Detect_tda9885 ((TDA9885Ptr (*)(I2CBusPtr, I2CSlaveAddr))LoaderSymbol("Detect_tda9885"))
+#define xf86_tda9885_init ((Bool (*)(TDA9885Ptr))LoaderSymbol("tda9885_init"))
+#define xf86_tda9885_setparameters ((void (*)(TDA9885Ptr))LoaderSymbol("tda9885_setparameters"))
+#define xf86_tda9885_getstatus ((void (*)(TDA9885Ptr))LoaderSymbol("tda9885_getstatus"))
+#define xf86_tda9885_dumpstatus ((void (*)(TDA9885Ptr))LoaderSymbol("tda9885_dumpstatus"))
+
+#else
+
+#define xf86_Detect_tda9885 Detect_tda9885
+#define xf86_tda9885_init tda9885_init
+#define xf86_tda9885_setparameters tda9885_setparameters
+#define xf86_tda9885_getstatus tda9885_getstatus
+#define xf86_tda9885_dumpstatus tda9885_dumpstatus
+
+#endif
+
+#endif
diff --git a/hw/xfree86/i2c/tda9885_module.c b/hw/xfree86/i2c/tda9885_module.c
new file mode 100644
index 000000000..b66a0176b
--- /dev/null
+++ b/hw/xfree86/i2c/tda9885_module.c
@@ -0,0 +1,26 @@
+
+#include "xf86Module.h"
+
+static MODULESETUPPROTO(tda9885Setup);
+
+
+static XF86ModuleVersionInfo tda9885VersRec =
+{
+ "tda9885",
+ MODULEVENDORSTRING,
+ MODINFOSTRING1,
+ MODINFOSTRING2,
+ XF86_VERSION_CURRENT,
+ 1, 0, 0,
+ ABI_CLASS_VIDEODRV, /* This needs the video driver ABI */
+ ABI_VIDEODRV_VERSION,
+ MOD_CLASS_NONE,
+ {0,0,0,0}
+};
+
+XF86ModuleData tda9885ModuleData = { &tda9885VersRec, tda9885Setup, NULL };
+
+static pointer
+tda9885Setup(pointer module, pointer opts, int *errmaj, int *errmin) {
+ return (pointer)1;
+}