summaryrefslogtreecommitdiff
path: root/hw/xfree86/i2c/bt829.c
blob: 3963ecf47025008e634f07791fa5ed1a8f198a7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
/* TODO: clean up/fix CC code */

#ifdef HAVE_XORG_CONFIG_H
#include <xorg-config.h>
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#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) ; }
}*/