1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "matroxfb_base.h"
16#include "matroxfb_misc.h"
17#include "matroxfb_DAC1064.h"
18#include "g450_pll.h"
19#include <linux/matroxfb.h>
20#include <asm/div64.h>
21
22#include "matroxfb_g450.h"
23
24
25struct mctl {
26 struct v4l2_queryctrl desc;
27 size_t control;
28};
29
30#define BLMIN 0xF3
31#define WLMAX 0x3FF
32
33static const struct mctl g450_controls[] =
34{ { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
35 "brightness",
36 0, WLMAX-BLMIN, 1, 370-BLMIN,
37 0,
38 }, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
39 { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
40 "contrast",
41 0, 1023, 1, 127,
42 0,
43 }, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
44 { { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
45 "saturation",
46 0, 255, 1, 165,
47 0,
48 }, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
49 { { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
50 "hue",
51 0, 255, 1, 0,
52 0,
53 }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
54 { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
55 "test output",
56 0, 1, 1, 0,
57 0,
58 }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
59};
60
61#define G450CTRLS ARRAY_SIZE(g450_controls)
62
63
64
65
66static int get_ctrl_id(__u32 v4l2_id) {
67 int i;
68
69 for (i = 0; i < G450CTRLS; i++) {
70 if (v4l2_id < g450_controls[i].desc.id) {
71 if (g450_controls[i].desc.id == 0x08000000) {
72 return -EINVAL;
73 }
74 return -ENOENT;
75 }
76 if (v4l2_id == g450_controls[i].desc.id) {
77 return i;
78 }
79 }
80 return -EINVAL;
81}
82
83static inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx)
84{
85 return (int*)((char*)minfo + g450_controls[idx].control);
86}
87
88static void tvo_fill_defaults(struct matrox_fb_info *minfo)
89{
90 unsigned int i;
91
92 for (i = 0; i < G450CTRLS; i++) {
93 *get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value;
94 }
95}
96
97static int cve2_get_reg(struct matrox_fb_info *minfo, int reg)
98{
99 unsigned long flags;
100 int val;
101
102 matroxfb_DAC_lock_irqsave(flags);
103 matroxfb_DAC_out(minfo, 0x87, reg);
104 val = matroxfb_DAC_in(minfo, 0x88);
105 matroxfb_DAC_unlock_irqrestore(flags);
106 return val;
107}
108
109static void cve2_set_reg(struct matrox_fb_info *minfo, int reg, int val)
110{
111 unsigned long flags;
112
113 matroxfb_DAC_lock_irqsave(flags);
114 matroxfb_DAC_out(minfo, 0x87, reg);
115 matroxfb_DAC_out(minfo, 0x88, val);
116 matroxfb_DAC_unlock_irqrestore(flags);
117}
118
119static void cve2_set_reg10(struct matrox_fb_info *minfo, int reg, int val)
120{
121 unsigned long flags;
122
123 matroxfb_DAC_lock_irqsave(flags);
124 matroxfb_DAC_out(minfo, 0x87, reg);
125 matroxfb_DAC_out(minfo, 0x88, val >> 2);
126 matroxfb_DAC_out(minfo, 0x87, reg + 1);
127 matroxfb_DAC_out(minfo, 0x88, val & 3);
128 matroxfb_DAC_unlock_irqrestore(flags);
129}
130
131static void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl,
132 int *wl)
133{
134 const int b = minfo->altout.tvo_params.brightness + BLMIN;
135 const int c = minfo->altout.tvo_params.contrast;
136
137 *bl = max(b - c, BLMIN);
138 *wl = min(b + c, WLMAX);
139}
140
141static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) {
142 int i;
143
144 i = get_ctrl_id(p->id);
145 if (i >= 0) {
146 *p = g450_controls[i].desc;
147 return 0;
148 }
149 if (i == -ENOENT) {
150 static const struct v4l2_queryctrl disctrl =
151 { .flags = V4L2_CTRL_FLAG_DISABLED };
152
153 i = p->id;
154 *p = disctrl;
155 p->id = i;
156 sprintf(p->name, "Ctrl #%08X", i);
157 return 0;
158 }
159 return -EINVAL;
160}
161
162static int g450_set_ctrl(void* md, struct v4l2_control *p) {
163 int i;
164 struct matrox_fb_info *minfo = md;
165
166 i = get_ctrl_id(p->id);
167 if (i < 0) return -EINVAL;
168
169
170
171
172 if (p->value == *get_ctrl_ptr(minfo, i)) return 0;
173
174
175
176
177 if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
178 if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
179
180
181
182
183 *get_ctrl_ptr(minfo, i) = p->value;
184
185 switch (p->id) {
186 case V4L2_CID_BRIGHTNESS:
187 case V4L2_CID_CONTRAST:
188 {
189 int blacklevel, whitelevel;
190 g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
191 cve2_set_reg10(minfo, 0x0e, blacklevel);
192 cve2_set_reg10(minfo, 0x1e, whitelevel);
193 }
194 break;
195 case V4L2_CID_SATURATION:
196 cve2_set_reg(minfo, 0x20, p->value);
197 cve2_set_reg(minfo, 0x22, p->value);
198 break;
199 case V4L2_CID_HUE:
200 cve2_set_reg(minfo, 0x25, p->value);
201 break;
202 case MATROXFB_CID_TESTOUT:
203 {
204 unsigned char val = cve2_get_reg(minfo, 0x05);
205 if (p->value) val |= 0x02;
206 else val &= ~0x02;
207 cve2_set_reg(minfo, 0x05, val);
208 }
209 break;
210 }
211
212
213 return 0;
214}
215
216static int g450_get_ctrl(void* md, struct v4l2_control *p) {
217 int i;
218 struct matrox_fb_info *minfo = md;
219
220 i = get_ctrl_id(p->id);
221 if (i < 0) return -EINVAL;
222 p->value = *get_ctrl_ptr(minfo, i);
223 return 0;
224}
225
226struct output_desc {
227 unsigned int h_vis;
228 unsigned int h_f_porch;
229 unsigned int h_sync;
230 unsigned int h_b_porch;
231 unsigned long long int chromasc;
232 unsigned int burst;
233 unsigned int v_total;
234};
235
236static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r,
237 struct my_timming *mt, const struct output_desc *outd)
238{
239 u_int32_t chromasc;
240 u_int32_t hlen;
241 u_int32_t hsl;
242 u_int32_t hbp;
243 u_int32_t hfp;
244 u_int32_t hvis;
245 unsigned int pixclock;
246 unsigned long long piic;
247 int mnp;
248 int over;
249
250 r->regs[0x80] = 0x03;
251
252 hvis = ((mt->HDisplay << 1) + 3) & ~3;
253
254 if (hvis >= 2048) {
255 hvis = 2044;
256 }
257
258 piic = 1000000000ULL * hvis;
259 do_div(piic, outd->h_vis);
260
261 dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
262
263 mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL);
264
265 mt->mnp = mnp;
266 mt->pixclock = g450_mnp2f(minfo, mnp);
267
268 dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
269
270 pixclock = 1000000000U / mt->pixclock;
271
272 dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
273
274 piic = outd->chromasc;
275 do_div(piic, mt->pixclock);
276 chromasc = piic;
277
278 dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
279
280 r->regs[0] = piic >> 24;
281 r->regs[1] = piic >> 16;
282 r->regs[2] = piic >> 8;
283 r->regs[3] = piic >> 0;
284 hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
285 hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
286 hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
287 hlen = hvis + hfp + hsl + hbp;
288 over = hlen & 0x0F;
289
290 dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
291
292 if (over) {
293 hfp -= over;
294 hlen -= over;
295 if (over <= 2) {
296 } else if (over < 10) {
297 hfp += 4;
298 hlen += 4;
299 } else {
300 hfp += 16;
301 hlen += 16;
302 }
303 }
304
305
306 r->regs[0x08] = hsl;
307 r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock;
308 r->regs[0x0A] = hbp;
309 r->regs[0x2C] = hfp;
310 r->regs[0x31] = hvis / 8;
311 r->regs[0x32] = hvis & 7;
312
313 dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
314
315 r->regs[0x84] = 1;
316 r->regs[0x85] = 0;
317 hvis = hvis >> 1;
318 hlen = hlen >> 1;
319
320 dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
321
322 mt->interlaced = 1;
323
324 mt->HDisplay = hvis & ~7;
325 mt->HSyncStart = mt->HDisplay + 8;
326 mt->HSyncEnd = (hlen & ~7) - 8;
327 mt->HTotal = hlen;
328
329 {
330 int upper;
331 unsigned int vtotal;
332 unsigned int vsyncend;
333 unsigned int vdisplay;
334
335 vtotal = mt->VTotal;
336 vsyncend = mt->VSyncEnd;
337 vdisplay = mt->VDisplay;
338 if (vtotal < outd->v_total) {
339 unsigned int yovr = outd->v_total - vtotal;
340
341 vsyncend += yovr >> 1;
342 } else if (vtotal > outd->v_total) {
343 vdisplay = outd->v_total - 4;
344 vsyncend = outd->v_total;
345 }
346 upper = (outd->v_total - vsyncend) >> 1;
347 r->regs[0x17] = outd->v_total / 4;
348 r->regs[0x18] = outd->v_total & 3;
349 r->regs[0x33] = upper - 1;
350 r->regs[0x82] = upper;
351 r->regs[0x83] = upper >> 8;
352
353 mt->VDisplay = vdisplay;
354 mt->VSyncStart = outd->v_total - 2;
355 mt->VSyncEnd = outd->v_total;
356 mt->VTotal = outd->v_total;
357 }
358}
359
360static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
361 static const struct output_desc paloutd = {
362 .h_vis = 52148148,
363 .h_f_porch = 1407407,
364 .h_sync = 4666667,
365 .h_b_porch = 5777778,
366 .chromasc = 19042247534182ULL,
367 .burst = 2518518,
368 .v_total = 625,
369 };
370 static const struct output_desc ntscoutd = {
371 .h_vis = 52888889,
372 .h_f_porch = 1333333,
373 .h_sync = 4666667,
374 .h_b_porch = 4666667,
375 .chromasc = 15374030659475ULL,
376 .burst = 2418418,
377 .v_total = 525,
378 };
379
380 static const struct mavenregs palregs = { {
381 0x2A, 0x09, 0x8A, 0xCB,
382 0x00,
383 0x00,
384 0xF9,
385 0x00,
386 0x7E,
387 0x44,
388 0x9C,
389 0x2E,
390 0x21,
391 0x00,
392
393 0x3C, 0x03,
394 0x3C, 0x03,
395 0x1A,
396 0x2A,
397 0x1C, 0x3D, 0x14,
398 0x9C, 0x01,
399 0x00,
400 0xFE,
401 0x7E,
402 0x60,
403 0x05,
404
405 0xAD, 0x03,
406
407 0xA5,
408 0x07,
409
410 0xA5,
411 0x00,
412 0x00,
413 0x00,
414 0x08,
415 0x04,
416 0x00,
417 0x1A,
418 0x55, 0x01,
419 0x26,
420 0x07, 0x7E,
421 0x02, 0x54,
422 0xB0, 0x00,
423 0x14,
424 0x49,
425 0x00,
426 0x00,
427 0xA3,
428 0xC8,
429 0x22,
430 0x02,
431 0x22,
432 0x3F, 0x03,
433 0x00,
434 0x00,
435 } };
436 static const struct mavenregs ntscregs = { {
437 0x21, 0xF0, 0x7C, 0x1F,
438 0x00,
439 0x00,
440 0xF9,
441 0x00,
442 0x7E,
443 0x43,
444 0x7E,
445 0x3D,
446 0x00,
447 0x00,
448 0x41, 0x00,
449 0x3C, 0x00,
450 0x17,
451 0x21,
452 0x1B, 0x1B, 0x24,
453 0x83, 0x01,
454 0x00,
455 0x0F,
456 0x0F,
457 0x60,
458 0x05,
459
460 0xC0, 0x02,
461
462 0x9C,
463 0x04,
464
465 0x9C,
466 0x01,
467 0x02,
468 0x00,
469 0x0A,
470 0x05,
471 0x00,
472 0x10,
473 0xFF, 0x03,
474 0x24,
475 0x0F, 0x78,
476 0x00, 0x00,
477 0xB2, 0x04,
478 0x14,
479 0x02,
480 0x00,
481 0x00,
482 0xA3,
483 0xC8,
484 0x15,
485 0x05,
486 0x3B,
487 0x3C, 0x00,
488 0x00,
489 0x00,
490 } };
491
492 if (norm == MATROXFB_OUTPUT_MODE_PAL) {
493 *data = palregs;
494 *outd = &paloutd;
495 } else {
496 *data = ntscregs;
497 *outd = &ntscoutd;
498 }
499 return;
500}
501
502#define LR(x) cve2_set_reg(minfo, (x), m->regs[(x)])
503static void cve2_init_TV(struct matrox_fb_info *minfo,
504 const struct mavenregs *m)
505{
506 int i;
507
508 LR(0x80);
509 LR(0x82); LR(0x83);
510 LR(0x84); LR(0x85);
511
512 cve2_set_reg(minfo, 0x3E, 0x01);
513
514 for (i = 0; i < 0x3E; i++) {
515 LR(i);
516 }
517 cve2_set_reg(minfo, 0x3E, 0x00);
518}
519
520static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
521 struct matrox_fb_info *minfo = md;
522
523 dprintk(KERN_DEBUG "Computing, mode=%u\n", minfo->outputs[1].mode);
524
525 if (mt->crtc == MATROXFB_SRC_CRTC2 &&
526 minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
527 const struct output_desc* outd;
528
529 cve2_init_TVdata(minfo->outputs[1].mode, &minfo->hw.maven, &outd);
530 {
531 int blacklevel, whitelevel;
532 g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
533 minfo->hw.maven.regs[0x0E] = blacklevel >> 2;
534 minfo->hw.maven.regs[0x0F] = blacklevel & 3;
535 minfo->hw.maven.regs[0x1E] = whitelevel >> 2;
536 minfo->hw.maven.regs[0x1F] = whitelevel & 3;
537
538 minfo->hw.maven.regs[0x20] =
539 minfo->hw.maven.regs[0x22] = minfo->altout.tvo_params.saturation;
540
541 minfo->hw.maven.regs[0x25] = minfo->altout.tvo_params.hue;
542
543 if (minfo->altout.tvo_params.testout) {
544 minfo->hw.maven.regs[0x05] |= 0x02;
545 }
546 }
547 computeRegs(minfo, &minfo->hw.maven, mt, outd);
548 } else if (mt->mnp < 0) {
549
550
551 mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
552 mt->pixclock = g450_mnp2f(minfo, mt->mnp);
553 }
554 dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
555 return 0;
556}
557
558static int matroxfb_g450_program(void* md) {
559 struct matrox_fb_info *minfo = md;
560
561 if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
562 cve2_init_TV(minfo, &minfo->hw.maven);
563 }
564 return 0;
565}
566
567static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
568 switch (arg) {
569 case MATROXFB_OUTPUT_MODE_PAL:
570 case MATROXFB_OUTPUT_MODE_NTSC:
571 case MATROXFB_OUTPUT_MODE_MONITOR:
572 return 0;
573 }
574 return -EINVAL;
575}
576
577static int g450_dvi_compute(void* md, struct my_timming* mt) {
578 struct matrox_fb_info *minfo = md;
579
580 if (mt->mnp < 0) {
581 mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
582 mt->pixclock = g450_mnp2f(minfo, mt->mnp);
583 }
584 return 0;
585}
586
587static struct matrox_altout matroxfb_g450_altout = {
588 .name = "Secondary output",
589 .compute = matroxfb_g450_compute,
590 .program = matroxfb_g450_program,
591 .verifymode = matroxfb_g450_verify_mode,
592 .getqueryctrl = g450_query_ctrl,
593 .getctrl = g450_get_ctrl,
594 .setctrl = g450_set_ctrl,
595};
596
597static struct matrox_altout matroxfb_g450_dvi = {
598 .name = "DVI output",
599 .compute = g450_dvi_compute,
600};
601
602void matroxfb_g450_connect(struct matrox_fb_info *minfo)
603{
604 if (minfo->devflags.g450dac) {
605 down_write(&minfo->altout.lock);
606 tvo_fill_defaults(minfo);
607 minfo->outputs[1].src = minfo->outputs[1].default_src;
608 minfo->outputs[1].data = minfo;
609 minfo->outputs[1].output = &matroxfb_g450_altout;
610 minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
611 minfo->outputs[2].src = minfo->outputs[2].default_src;
612 minfo->outputs[2].data = minfo;
613 minfo->outputs[2].output = &matroxfb_g450_dvi;
614 minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
615 up_write(&minfo->altout.lock);
616 }
617}
618
619void matroxfb_g450_shutdown(struct matrox_fb_info *minfo)
620{
621 if (minfo->devflags.g450dac) {
622 down_write(&minfo->altout.lock);
623 minfo->outputs[1].src = MATROXFB_SRC_NONE;
624 minfo->outputs[1].output = NULL;
625 minfo->outputs[1].data = NULL;
626 minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
627 minfo->outputs[2].src = MATROXFB_SRC_NONE;
628 minfo->outputs[2].output = NULL;
629 minfo->outputs[2].data = NULL;
630 minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
631 up_write(&minfo->altout.lock);
632 }
633}
634
635EXPORT_SYMBOL(matroxfb_g450_connect);
636EXPORT_SYMBOL(matroxfb_g450_shutdown);
637
638MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
639MODULE_DESCRIPTION("Matrox G450/G550 output driver");
640MODULE_LICENSE("GPL");
641