0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include "matroxfb_base.h"
0017 #include "matroxfb_misc.h"
0018 #include "matroxfb_DAC1064.h"
0019 #include "g450_pll.h"
0020 #include <linux/matroxfb.h>
0021 #include <asm/div64.h>
0022
0023 #include "matroxfb_g450.h"
0024
0025
0026 struct mctl {
0027 struct v4l2_queryctrl desc;
0028 size_t control;
0029 };
0030
0031 #define BLMIN 0xF3
0032 #define WLMAX 0x3FF
0033
0034 static const struct mctl g450_controls[] =
0035 { { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER,
0036 "brightness",
0037 0, WLMAX-BLMIN, 1, 370-BLMIN,
0038 0,
0039 }, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) },
0040 { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER,
0041 "contrast",
0042 0, 1023, 1, 127,
0043 0,
0044 }, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) },
0045 { { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER,
0046 "saturation",
0047 0, 255, 1, 165,
0048 0,
0049 }, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) },
0050 { { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER,
0051 "hue",
0052 0, 255, 1, 0,
0053 0,
0054 }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) },
0055 { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN,
0056 "test output",
0057 0, 1, 1, 0,
0058 0,
0059 }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) },
0060 };
0061
0062 #define G450CTRLS ARRAY_SIZE(g450_controls)
0063
0064
0065
0066
0067 static int get_ctrl_id(__u32 v4l2_id) {
0068 int i;
0069
0070 for (i = 0; i < G450CTRLS; i++) {
0071 if (v4l2_id < g450_controls[i].desc.id) {
0072 if (g450_controls[i].desc.id == 0x08000000) {
0073 return -EINVAL;
0074 }
0075 return -ENOENT;
0076 }
0077 if (v4l2_id == g450_controls[i].desc.id) {
0078 return i;
0079 }
0080 }
0081 return -EINVAL;
0082 }
0083
0084 static inline int *get_ctrl_ptr(struct matrox_fb_info *minfo, unsigned int idx)
0085 {
0086 return (int*)((char*)minfo + g450_controls[idx].control);
0087 }
0088
0089 static void tvo_fill_defaults(struct matrox_fb_info *minfo)
0090 {
0091 unsigned int i;
0092
0093 for (i = 0; i < G450CTRLS; i++) {
0094 *get_ctrl_ptr(minfo, i) = g450_controls[i].desc.default_value;
0095 }
0096 }
0097
0098 static int cve2_get_reg(struct matrox_fb_info *minfo, int reg)
0099 {
0100 unsigned long flags;
0101 int val;
0102
0103 matroxfb_DAC_lock_irqsave(flags);
0104 matroxfb_DAC_out(minfo, 0x87, reg);
0105 val = matroxfb_DAC_in(minfo, 0x88);
0106 matroxfb_DAC_unlock_irqrestore(flags);
0107 return val;
0108 }
0109
0110 static void cve2_set_reg(struct matrox_fb_info *minfo, int reg, int val)
0111 {
0112 unsigned long flags;
0113
0114 matroxfb_DAC_lock_irqsave(flags);
0115 matroxfb_DAC_out(minfo, 0x87, reg);
0116 matroxfb_DAC_out(minfo, 0x88, val);
0117 matroxfb_DAC_unlock_irqrestore(flags);
0118 }
0119
0120 static void cve2_set_reg10(struct matrox_fb_info *minfo, int reg, int val)
0121 {
0122 unsigned long flags;
0123
0124 matroxfb_DAC_lock_irqsave(flags);
0125 matroxfb_DAC_out(minfo, 0x87, reg);
0126 matroxfb_DAC_out(minfo, 0x88, val >> 2);
0127 matroxfb_DAC_out(minfo, 0x87, reg + 1);
0128 matroxfb_DAC_out(minfo, 0x88, val & 3);
0129 matroxfb_DAC_unlock_irqrestore(flags);
0130 }
0131
0132 static void g450_compute_bwlevel(const struct matrox_fb_info *minfo, int *bl,
0133 int *wl)
0134 {
0135 const int b = minfo->altout.tvo_params.brightness + BLMIN;
0136 const int c = minfo->altout.tvo_params.contrast;
0137
0138 *bl = max(b - c, BLMIN);
0139 *wl = min(b + c, WLMAX);
0140 }
0141
0142 static int g450_query_ctrl(void* md, struct v4l2_queryctrl *p) {
0143 int i;
0144
0145 i = get_ctrl_id(p->id);
0146 if (i >= 0) {
0147 *p = g450_controls[i].desc;
0148 return 0;
0149 }
0150 if (i == -ENOENT) {
0151 static const struct v4l2_queryctrl disctrl =
0152 { .flags = V4L2_CTRL_FLAG_DISABLED };
0153
0154 i = p->id;
0155 *p = disctrl;
0156 p->id = i;
0157 sprintf(p->name, "Ctrl #%08X", i);
0158 return 0;
0159 }
0160 return -EINVAL;
0161 }
0162
0163 static int g450_set_ctrl(void* md, struct v4l2_control *p) {
0164 int i;
0165 struct matrox_fb_info *minfo = md;
0166
0167 i = get_ctrl_id(p->id);
0168 if (i < 0) return -EINVAL;
0169
0170
0171
0172
0173 if (p->value == *get_ctrl_ptr(minfo, i)) return 0;
0174
0175
0176
0177
0178 if (p->value > g450_controls[i].desc.maximum) return -EINVAL;
0179 if (p->value < g450_controls[i].desc.minimum) return -EINVAL;
0180
0181
0182
0183
0184 *get_ctrl_ptr(minfo, i) = p->value;
0185
0186 switch (p->id) {
0187 case V4L2_CID_BRIGHTNESS:
0188 case V4L2_CID_CONTRAST:
0189 {
0190 int blacklevel, whitelevel;
0191 g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
0192 cve2_set_reg10(minfo, 0x0e, blacklevel);
0193 cve2_set_reg10(minfo, 0x1e, whitelevel);
0194 }
0195 break;
0196 case V4L2_CID_SATURATION:
0197 cve2_set_reg(minfo, 0x20, p->value);
0198 cve2_set_reg(minfo, 0x22, p->value);
0199 break;
0200 case V4L2_CID_HUE:
0201 cve2_set_reg(minfo, 0x25, p->value);
0202 break;
0203 case MATROXFB_CID_TESTOUT:
0204 {
0205 unsigned char val = cve2_get_reg(minfo, 0x05);
0206 if (p->value) val |= 0x02;
0207 else val &= ~0x02;
0208 cve2_set_reg(minfo, 0x05, val);
0209 }
0210 break;
0211 }
0212
0213
0214 return 0;
0215 }
0216
0217 static int g450_get_ctrl(void* md, struct v4l2_control *p) {
0218 int i;
0219 struct matrox_fb_info *minfo = md;
0220
0221 i = get_ctrl_id(p->id);
0222 if (i < 0) return -EINVAL;
0223 p->value = *get_ctrl_ptr(minfo, i);
0224 return 0;
0225 }
0226
0227 struct output_desc {
0228 unsigned int h_vis;
0229 unsigned int h_f_porch;
0230 unsigned int h_sync;
0231 unsigned int h_b_porch;
0232 unsigned long long int chromasc;
0233 unsigned int burst;
0234 unsigned int v_total;
0235 };
0236
0237 static void computeRegs(struct matrox_fb_info *minfo, struct mavenregs *r,
0238 struct my_timming *mt, const struct output_desc *outd)
0239 {
0240 u_int32_t chromasc;
0241 u_int32_t hlen;
0242 u_int32_t hsl;
0243 u_int32_t hbp;
0244 u_int32_t hfp;
0245 u_int32_t hvis;
0246 unsigned int pixclock;
0247 unsigned long long piic;
0248 int mnp;
0249 int over;
0250
0251 r->regs[0x80] = 0x03;
0252
0253 hvis = ((mt->HDisplay << 1) + 3) & ~3;
0254
0255 if (hvis >= 2048) {
0256 hvis = 2044;
0257 }
0258
0259 piic = 1000000000ULL * hvis;
0260 do_div(piic, outd->h_vis);
0261
0262 dprintk(KERN_DEBUG "Want %u kHz pixclock\n", (unsigned int)piic);
0263
0264 mnp = matroxfb_g450_setclk(minfo, piic, M_VIDEO_PLL);
0265
0266 mt->mnp = mnp;
0267 mt->pixclock = g450_mnp2f(minfo, mnp);
0268
0269 dprintk(KERN_DEBUG "MNP=%08X\n", mnp);
0270
0271 pixclock = 1000000000U / mt->pixclock;
0272
0273 dprintk(KERN_DEBUG "Got %u ps pixclock\n", pixclock);
0274
0275 piic = outd->chromasc;
0276 do_div(piic, mt->pixclock);
0277 chromasc = piic;
0278
0279 dprintk(KERN_DEBUG "Chroma is %08X\n", chromasc);
0280
0281 r->regs[0] = piic >> 24;
0282 r->regs[1] = piic >> 16;
0283 r->regs[2] = piic >> 8;
0284 r->regs[3] = piic >> 0;
0285 hbp = (((outd->h_b_porch + pixclock) / pixclock)) & ~1;
0286 hfp = (((outd->h_f_porch + pixclock) / pixclock)) & ~1;
0287 hsl = (((outd->h_sync + pixclock) / pixclock)) & ~1;
0288 hlen = hvis + hfp + hsl + hbp;
0289 over = hlen & 0x0F;
0290
0291 dprintk(KERN_DEBUG "WL: vis=%u, hf=%u, hs=%u, hb=%u, total=%u\n", hvis, hfp, hsl, hbp, hlen);
0292
0293 if (over) {
0294 hfp -= over;
0295 hlen -= over;
0296 if (over <= 2) {
0297 } else if (over < 10) {
0298 hfp += 4;
0299 hlen += 4;
0300 } else {
0301 hfp += 16;
0302 hlen += 16;
0303 }
0304 }
0305
0306
0307 r->regs[0x08] = hsl;
0308 r->regs[0x09] = (outd->burst + pixclock - 1) / pixclock;
0309 r->regs[0x0A] = hbp;
0310 r->regs[0x2C] = hfp;
0311 r->regs[0x31] = hvis / 8;
0312 r->regs[0x32] = hvis & 7;
0313
0314 dprintk(KERN_DEBUG "PG: vis=%04X, hf=%02X, hs=%02X, hb=%02X, total=%04X\n", hvis, hfp, hsl, hbp, hlen);
0315
0316 r->regs[0x84] = 1;
0317 r->regs[0x85] = 0;
0318 hvis = hvis >> 1;
0319 hlen = hlen >> 1;
0320
0321 dprintk(KERN_DEBUG "hlen=%u hvis=%u\n", hlen, hvis);
0322
0323 mt->interlaced = 1;
0324
0325 mt->HDisplay = hvis & ~7;
0326 mt->HSyncStart = mt->HDisplay + 8;
0327 mt->HSyncEnd = (hlen & ~7) - 8;
0328 mt->HTotal = hlen;
0329
0330 {
0331 int upper;
0332 unsigned int vtotal;
0333 unsigned int vsyncend;
0334 unsigned int vdisplay;
0335
0336 vtotal = mt->VTotal;
0337 vsyncend = mt->VSyncEnd;
0338 vdisplay = mt->VDisplay;
0339 if (vtotal < outd->v_total) {
0340 unsigned int yovr = outd->v_total - vtotal;
0341
0342 vsyncend += yovr >> 1;
0343 } else if (vtotal > outd->v_total) {
0344 vdisplay = outd->v_total - 4;
0345 vsyncend = outd->v_total;
0346 }
0347 upper = (outd->v_total - vsyncend) >> 1;
0348 r->regs[0x17] = outd->v_total / 4;
0349 r->regs[0x18] = outd->v_total & 3;
0350 r->regs[0x33] = upper - 1;
0351 r->regs[0x82] = upper;
0352 r->regs[0x83] = upper >> 8;
0353
0354 mt->VDisplay = vdisplay;
0355 mt->VSyncStart = outd->v_total - 2;
0356 mt->VSyncEnd = outd->v_total;
0357 mt->VTotal = outd->v_total;
0358 }
0359 }
0360
0361 static void cve2_init_TVdata(int norm, struct mavenregs* data, const struct output_desc** outd) {
0362 static const struct output_desc paloutd = {
0363 .h_vis = 52148148,
0364 .h_f_porch = 1407407,
0365 .h_sync = 4666667,
0366 .h_b_porch = 5777778,
0367 .chromasc = 19042247534182ULL,
0368 .burst = 2518518,
0369 .v_total = 625,
0370 };
0371 static const struct output_desc ntscoutd = {
0372 .h_vis = 52888889,
0373 .h_f_porch = 1333333,
0374 .h_sync = 4666667,
0375 .h_b_porch = 4666667,
0376 .chromasc = 15374030659475ULL,
0377 .burst = 2418418,
0378 .v_total = 525,
0379 };
0380
0381 static const struct mavenregs palregs = { {
0382 0x2A, 0x09, 0x8A, 0xCB,
0383 0x00,
0384 0x00,
0385 0xF9,
0386 0x00,
0387 0x7E,
0388 0x44,
0389 0x9C,
0390 0x2E,
0391 0x21,
0392 0x00,
0393
0394 0x3C, 0x03,
0395 0x3C, 0x03,
0396 0x1A,
0397 0x2A,
0398 0x1C, 0x3D, 0x14,
0399 0x9C, 0x01,
0400 0x00,
0401 0xFE,
0402 0x7E,
0403 0x60,
0404 0x05,
0405
0406 0xAD, 0x03,
0407
0408 0xA5,
0409 0x07,
0410
0411 0xA5,
0412 0x00,
0413 0x00,
0414 0x00,
0415 0x08,
0416 0x04,
0417 0x00,
0418 0x1A,
0419 0x55, 0x01,
0420 0x26,
0421 0x07, 0x7E,
0422 0x02, 0x54,
0423 0xB0, 0x00,
0424 0x14,
0425 0x49,
0426 0x00,
0427 0x00,
0428 0xA3,
0429 0xC8,
0430 0x22,
0431 0x02,
0432 0x22,
0433 0x3F, 0x03,
0434 0x00,
0435 0x00,
0436 } };
0437 static const struct mavenregs ntscregs = { {
0438 0x21, 0xF0, 0x7C, 0x1F,
0439 0x00,
0440 0x00,
0441 0xF9,
0442 0x00,
0443 0x7E,
0444 0x43,
0445 0x7E,
0446 0x3D,
0447 0x00,
0448 0x00,
0449 0x41, 0x00,
0450 0x3C, 0x00,
0451 0x17,
0452 0x21,
0453 0x1B, 0x1B, 0x24,
0454 0x83, 0x01,
0455 0x00,
0456 0x0F,
0457 0x0F,
0458 0x60,
0459 0x05,
0460
0461 0xC0, 0x02,
0462
0463 0x9C,
0464 0x04,
0465
0466 0x9C,
0467 0x01,
0468 0x02,
0469 0x00,
0470 0x0A,
0471 0x05,
0472 0x00,
0473 0x10,
0474 0xFF, 0x03,
0475 0x24,
0476 0x0F, 0x78,
0477 0x00, 0x00,
0478 0xB2, 0x04,
0479 0x14,
0480 0x02,
0481 0x00,
0482 0x00,
0483 0xA3,
0484 0xC8,
0485 0x15,
0486 0x05,
0487 0x3B,
0488 0x3C, 0x00,
0489 0x00,
0490 0x00,
0491 } };
0492
0493 if (norm == MATROXFB_OUTPUT_MODE_PAL) {
0494 *data = palregs;
0495 *outd = &paloutd;
0496 } else {
0497 *data = ntscregs;
0498 *outd = &ntscoutd;
0499 }
0500 return;
0501 }
0502
0503 #define LR(x) cve2_set_reg(minfo, (x), m->regs[(x)])
0504 static void cve2_init_TV(struct matrox_fb_info *minfo,
0505 const struct mavenregs *m)
0506 {
0507 int i;
0508
0509 LR(0x80);
0510 LR(0x82); LR(0x83);
0511 LR(0x84); LR(0x85);
0512
0513 cve2_set_reg(minfo, 0x3E, 0x01);
0514
0515 for (i = 0; i < 0x3E; i++) {
0516 LR(i);
0517 }
0518 cve2_set_reg(minfo, 0x3E, 0x00);
0519 }
0520
0521 static int matroxfb_g450_compute(void* md, struct my_timming* mt) {
0522 struct matrox_fb_info *minfo = md;
0523
0524 dprintk(KERN_DEBUG "Computing, mode=%u\n", minfo->outputs[1].mode);
0525
0526 if (mt->crtc == MATROXFB_SRC_CRTC2 &&
0527 minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
0528 const struct output_desc* outd;
0529
0530 cve2_init_TVdata(minfo->outputs[1].mode, &minfo->hw.maven, &outd);
0531 {
0532 int blacklevel, whitelevel;
0533 g450_compute_bwlevel(minfo, &blacklevel, &whitelevel);
0534 minfo->hw.maven.regs[0x0E] = blacklevel >> 2;
0535 minfo->hw.maven.regs[0x0F] = blacklevel & 3;
0536 minfo->hw.maven.regs[0x1E] = whitelevel >> 2;
0537 minfo->hw.maven.regs[0x1F] = whitelevel & 3;
0538
0539 minfo->hw.maven.regs[0x20] =
0540 minfo->hw.maven.regs[0x22] = minfo->altout.tvo_params.saturation;
0541
0542 minfo->hw.maven.regs[0x25] = minfo->altout.tvo_params.hue;
0543
0544 if (minfo->altout.tvo_params.testout) {
0545 minfo->hw.maven.regs[0x05] |= 0x02;
0546 }
0547 }
0548 computeRegs(minfo, &minfo->hw.maven, mt, outd);
0549 } else if (mt->mnp < 0) {
0550
0551
0552 mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
0553 mt->pixclock = g450_mnp2f(minfo, mt->mnp);
0554 }
0555 dprintk(KERN_DEBUG "Pixclock = %u\n", mt->pixclock);
0556 return 0;
0557 }
0558
0559 static int matroxfb_g450_program(void* md) {
0560 struct matrox_fb_info *minfo = md;
0561
0562 if (minfo->outputs[1].mode != MATROXFB_OUTPUT_MODE_MONITOR) {
0563 cve2_init_TV(minfo, &minfo->hw.maven);
0564 }
0565 return 0;
0566 }
0567
0568 static int matroxfb_g450_verify_mode(void* md, u_int32_t arg) {
0569 switch (arg) {
0570 case MATROXFB_OUTPUT_MODE_PAL:
0571 case MATROXFB_OUTPUT_MODE_NTSC:
0572 case MATROXFB_OUTPUT_MODE_MONITOR:
0573 return 0;
0574 }
0575 return -EINVAL;
0576 }
0577
0578 static int g450_dvi_compute(void* md, struct my_timming* mt) {
0579 struct matrox_fb_info *minfo = md;
0580
0581 if (mt->mnp < 0) {
0582 mt->mnp = matroxfb_g450_setclk(minfo, mt->pixclock, (mt->crtc == MATROXFB_SRC_CRTC1) ? M_PIXEL_PLL_C : M_VIDEO_PLL);
0583 mt->pixclock = g450_mnp2f(minfo, mt->mnp);
0584 }
0585 return 0;
0586 }
0587
0588 static struct matrox_altout matroxfb_g450_altout = {
0589 .name = "Secondary output",
0590 .compute = matroxfb_g450_compute,
0591 .program = matroxfb_g450_program,
0592 .verifymode = matroxfb_g450_verify_mode,
0593 .getqueryctrl = g450_query_ctrl,
0594 .getctrl = g450_get_ctrl,
0595 .setctrl = g450_set_ctrl,
0596 };
0597
0598 static struct matrox_altout matroxfb_g450_dvi = {
0599 .name = "DVI output",
0600 .compute = g450_dvi_compute,
0601 };
0602
0603 void matroxfb_g450_connect(struct matrox_fb_info *minfo)
0604 {
0605 if (minfo->devflags.g450dac) {
0606 down_write(&minfo->altout.lock);
0607 tvo_fill_defaults(minfo);
0608 minfo->outputs[1].src = minfo->outputs[1].default_src;
0609 minfo->outputs[1].data = minfo;
0610 minfo->outputs[1].output = &matroxfb_g450_altout;
0611 minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
0612 minfo->outputs[2].src = minfo->outputs[2].default_src;
0613 minfo->outputs[2].data = minfo;
0614 minfo->outputs[2].output = &matroxfb_g450_dvi;
0615 minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
0616 up_write(&minfo->altout.lock);
0617 }
0618 }
0619
0620 void matroxfb_g450_shutdown(struct matrox_fb_info *minfo)
0621 {
0622 if (minfo->devflags.g450dac) {
0623 down_write(&minfo->altout.lock);
0624 minfo->outputs[1].src = MATROXFB_SRC_NONE;
0625 minfo->outputs[1].output = NULL;
0626 minfo->outputs[1].data = NULL;
0627 minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR;
0628 minfo->outputs[2].src = MATROXFB_SRC_NONE;
0629 minfo->outputs[2].output = NULL;
0630 minfo->outputs[2].data = NULL;
0631 minfo->outputs[2].mode = MATROXFB_OUTPUT_MODE_MONITOR;
0632 up_write(&minfo->altout.lock);
0633 }
0634 }
0635
0636 EXPORT_SYMBOL(matroxfb_g450_connect);
0637 EXPORT_SYMBOL(matroxfb_g450_shutdown);
0638
0639 MODULE_AUTHOR("(c) 2000-2002 Petr Vandrovec <vandrove@vc.cvut.cz>");
0640 MODULE_DESCRIPTION("Matrox G450/G550 output driver");
0641 MODULE_LICENSE("GPL");