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