1 /* 2 * 3 * Hardware accelerated Matrox Millennium I, II, Mystique, G100, G200, G400 and G450. 4 * 5 * (c) 1998-2002 Petr Vandrovec <vandrove@vc.cvut.cz> 6 * 7 * Portions Copyright (c) 2001 Matrox Graphics Inc. 8 * 9 * Version: 1.65 2002/08/14 10 * 11 * See matroxfb_base.c for contributors. 12 * 13 */ 14 15 #include "matroxfb_maven.h" 16 #include "matroxfb_misc.h" 17 #include "matroxfb_DAC1064.h" 18 #include <linux/i2c.h> 19 #include <linux/matroxfb.h> 20 #include <linux/slab.h> 21 #include <asm/div64.h> 22 23 #define MGATVO_B 1 24 #define MGATVO_C 2 25 26 static const struct maven_gamma { 27 unsigned char reg83; 28 unsigned char reg84; 29 unsigned char reg85; 30 unsigned char reg86; 31 unsigned char reg87; 32 unsigned char reg88; 33 unsigned char reg89; 34 unsigned char reg8a; 35 unsigned char reg8b; 36 } maven_gamma[] = { 37 { 131, 57, 223, 15, 117, 212, 251, 91, 156}, 38 { 133, 61, 128, 63, 180, 147, 195, 100, 180}, 39 { 131, 19, 63, 31, 50, 66, 171, 64, 176}, 40 { 0, 0, 0, 31, 16, 16, 16, 100, 200}, 41 { 8, 23, 47, 73, 147, 244, 220, 80, 195}, 42 { 22, 43, 64, 80, 147, 115, 58, 85, 168}, 43 { 34, 60, 80, 214, 147, 212, 188, 85, 167}, 44 { 45, 77, 96, 216, 147, 99, 91, 85, 159}, 45 { 56, 76, 112, 107, 147, 212, 148, 64, 144}, 46 { 65, 91, 128, 137, 147, 196, 17, 69, 148}, 47 { 72, 104, 136, 138, 147, 180, 245, 73, 147}, 48 { 87, 116, 143, 126, 16, 83, 229, 77, 144}, 49 { 95, 119, 152, 254, 244, 83, 221, 77, 151}, 50 { 100, 129, 159, 156, 244, 148, 197, 77, 160}, 51 { 105, 141, 167, 247, 244, 132, 181, 84, 166}, 52 { 105, 147, 168, 247, 244, 245, 181, 90, 170}, 53 { 120, 153, 175, 248, 212, 229, 165, 90, 180}, 54 { 119, 156, 176, 248, 244, 229, 84, 74, 160}, 55 { 119, 158, 183, 248, 244, 229, 149, 78, 165} 56 }; 57 58 /* Definition of the various controls */ 59 struct mctl { 60 struct v4l2_queryctrl desc; 61 size_t control; 62 }; 63 64 #define BLMIN 0x0FF 65 #define WLMAX 0x3FF 66 67 static const struct mctl maven_controls[] = 68 { { { V4L2_CID_BRIGHTNESS, V4L2_CTRL_TYPE_INTEGER, 69 "brightness", 70 0, WLMAX - BLMIN, 1, 379 - BLMIN, 71 0, 72 }, offsetof(struct matrox_fb_info, altout.tvo_params.brightness) }, 73 { { V4L2_CID_CONTRAST, V4L2_CTRL_TYPE_INTEGER, 74 "contrast", 75 0, 1023, 1, 127, 76 0, 77 }, offsetof(struct matrox_fb_info, altout.tvo_params.contrast) }, 78 { { V4L2_CID_SATURATION, V4L2_CTRL_TYPE_INTEGER, 79 "saturation", 80 0, 255, 1, 155, 81 0, 82 }, offsetof(struct matrox_fb_info, altout.tvo_params.saturation) }, 83 { { V4L2_CID_HUE, V4L2_CTRL_TYPE_INTEGER, 84 "hue", 85 0, 255, 1, 0, 86 0, 87 }, offsetof(struct matrox_fb_info, altout.tvo_params.hue) }, 88 { { V4L2_CID_GAMMA, V4L2_CTRL_TYPE_INTEGER, 89 "gamma", 90 0, ARRAY_SIZE(maven_gamma) - 1, 1, 3, 91 0, 92 }, offsetof(struct matrox_fb_info, altout.tvo_params.gamma) }, 93 { { MATROXFB_CID_TESTOUT, V4L2_CTRL_TYPE_BOOLEAN, 94 "test output", 95 0, 1, 1, 0, 96 0, 97 }, offsetof(struct matrox_fb_info, altout.tvo_params.testout) }, 98 { { MATROXFB_CID_DEFLICKER, V4L2_CTRL_TYPE_INTEGER, 99 "deflicker mode", 100 0, 2, 1, 0, 101 0, 102 }, offsetof(struct matrox_fb_info, altout.tvo_params.deflicker) }, 103 104 }; 105 106 #define MAVCTRLS ARRAY_SIZE(maven_controls) 107 108 /* Return: positive number: id found 109 -EINVAL: id not found, return failure 110 -ENOENT: id not found, create fake disabled control */ 111 static int get_ctrl_id(__u32 v4l2_id) { 112 int i; 113 114 for (i = 0; i < MAVCTRLS; i++) { 115 if (v4l2_id < maven_controls[i].desc.id) { 116 if (maven_controls[i].desc.id == 0x08000000) { 117 return -EINVAL; 118 } 119 return -ENOENT; 120 } 121 if (v4l2_id == maven_controls[i].desc.id) { 122 return i; 123 } 124 } 125 return -EINVAL; 126 } 127 128 struct maven_data { 129 struct matrox_fb_info* primary_head; 130 struct i2c_client *client; 131 int version; 132 }; 133 134 static int* get_ctrl_ptr(struct maven_data* md, int idx) { 135 return (int*)((char*)(md->primary_head) + maven_controls[idx].control); 136 } 137 138 static int maven_get_reg(struct i2c_client* c, char reg) { 139 char dst; 140 struct i2c_msg msgs[] = { 141 { 142 .addr = c->addr, 143 .flags = I2C_M_REV_DIR_ADDR, 144 .len = sizeof(reg), 145 .buf = ® 146 }, 147 { 148 .addr = c->addr, 149 .flags = I2C_M_RD | I2C_M_NOSTART, 150 .len = sizeof(dst), 151 .buf = &dst 152 } 153 }; 154 s32 err; 155 156 err = i2c_transfer(c->adapter, msgs, 2); 157 if (err < 0) 158 printk(KERN_INFO "ReadReg(%d) failed\n", reg); 159 return dst & 0xFF; 160 } 161 162 static int maven_set_reg(struct i2c_client* c, int reg, int val) { 163 s32 err; 164 165 err = i2c_smbus_write_byte_data(c, reg, val); 166 if (err) 167 printk(KERN_INFO "WriteReg(%d) failed\n", reg); 168 return err; 169 } 170 171 static int maven_set_reg_pair(struct i2c_client* c, int reg, int val) { 172 s32 err; 173 174 err = i2c_smbus_write_word_data(c, reg, val); 175 if (err) 176 printk(KERN_INFO "WriteRegPair(%d) failed\n", reg); 177 return err; 178 } 179 180 static const struct matrox_pll_features maven_pll = { 181 50000, 182 27000, 183 4, 127, 184 2, 31, 185 3 186 }; 187 188 struct matrox_pll_features2 { 189 unsigned int vco_freq_min; 190 unsigned int vco_freq_max; 191 unsigned int feed_div_min; 192 unsigned int feed_div_max; 193 unsigned int in_div_min; 194 unsigned int in_div_max; 195 unsigned int post_shift_max; 196 }; 197 198 struct matrox_pll_ctl { 199 unsigned int ref_freq; 200 unsigned int den; 201 }; 202 203 static const struct matrox_pll_features2 maven1000_pll = { 204 50000000, 205 300000000, 206 5, 128, 207 3, 32, 208 3 209 }; 210 211 static const struct matrox_pll_ctl maven_PAL = { 212 540000, 213 50 214 }; 215 216 static const struct matrox_pll_ctl maven_NTSC = { 217 450450, /* 27027000/60 == 27000000/59.94005994 */ 218 60 219 }; 220 221 static int matroxfb_PLL_mavenclock(const struct matrox_pll_features2* pll, 222 const struct matrox_pll_ctl* ctl, 223 unsigned int htotal, unsigned int vtotal, 224 unsigned int* in, unsigned int* feed, unsigned int* post, 225 unsigned int* h2) { 226 unsigned int besth2 = 0; 227 unsigned int fxtal = ctl->ref_freq; 228 unsigned int fmin = pll->vco_freq_min / ctl->den; 229 unsigned int fwant; 230 unsigned int p; 231 unsigned int scrlen; 232 unsigned int fmax; 233 234 DBG(__func__) 235 236 scrlen = htotal * (vtotal - 1); 237 fwant = htotal * vtotal; 238 fmax = pll->vco_freq_max / ctl->den; 239 240 dprintk(KERN_DEBUG "want: %u, xtal: %u, h: %u, v: %u, fmax: %u\n", 241 fwant, fxtal, htotal, vtotal, fmax); 242 for (p = 1; p <= pll->post_shift_max; p++) { 243 if (fwant * 2 > fmax) 244 break; 245 fwant *= 2; 246 } 247 if (fwant > fmax) 248 return 0; 249 for (; p-- > 0; fwant >>= 1) { 250 unsigned int m; 251 252 if (fwant < fmin) break; 253 for (m = pll->in_div_min; m <= pll->in_div_max; m++) { 254 unsigned int n; 255 unsigned int dvd; 256 unsigned int ln; 257 258 n = (fwant * m) / fxtal; 259 if (n < pll->feed_div_min) 260 continue; 261 if (n > pll->feed_div_max) 262 break; 263 264 ln = fxtal * n; 265 dvd = m << p; 266 267 if (ln % dvd) 268 continue; 269 ln = ln / dvd; 270 271 if (ln < scrlen + 2) 272 continue; 273 ln = ln - scrlen; 274 if (ln > htotal) 275 continue; 276 dprintk(KERN_DEBUG "Match: %u / %u / %u / %u\n", n, m, p, ln); 277 if (ln > besth2) { 278 dprintk(KERN_DEBUG "Better...\n"); 279 *h2 = besth2 = ln; 280 *post = p; 281 *in = m; 282 *feed = n; 283 } 284 } 285 } 286 287 /* if h2/post/in/feed have not been assigned, return zero (error) */ 288 if (besth2 < 2) 289 return 0; 290 291 dprintk(KERN_ERR "clk: %02X %02X %02X %d %d\n", *in, *feed, *post, fxtal, fwant); 292 return fxtal * (*feed) / (*in) * ctl->den; 293 } 294 295 static int matroxfb_mavenclock(const struct matrox_pll_ctl *ctl, 296 unsigned int htotal, unsigned int vtotal, 297 unsigned int* in, unsigned int* feed, unsigned int* post, 298 unsigned int* htotal2) { 299 unsigned int fvco; 300 unsigned int uninitialized_var(p); 301 302 fvco = matroxfb_PLL_mavenclock(&maven1000_pll, ctl, htotal, vtotal, in, feed, &p, htotal2); 303 if (!fvco) 304 return -EINVAL; 305 p = (1 << p) - 1; 306 if (fvco <= 100000000) 307 ; 308 else if (fvco <= 140000000) 309 p |= 0x08; 310 else if (fvco <= 180000000) 311 p |= 0x10; 312 else 313 p |= 0x18; 314 *post = p; 315 return 0; 316 } 317 318 static void DAC1064_calcclock(unsigned int freq, unsigned int fmax, 319 unsigned int* in, unsigned int* feed, unsigned int* post) { 320 unsigned int fvco; 321 unsigned int p; 322 323 fvco = matroxfb_PLL_calcclock(&maven_pll, freq, fmax, in, feed, &p); 324 p = (1 << p) - 1; 325 if (fvco <= 100000) 326 ; 327 else if (fvco <= 140000) 328 p |= 0x08; 329 else if (fvco <= 180000) 330 p |= 0x10; 331 else 332 p |= 0x18; 333 *post = p; 334 return; 335 } 336 337 static unsigned char maven_compute_deflicker (const struct maven_data* md) { 338 unsigned char df; 339 340 df = (md->version == MGATVO_B?0x40:0x00); 341 switch (md->primary_head->altout.tvo_params.deflicker) { 342 case 0: 343 /* df |= 0x00; */ 344 break; 345 case 1: 346 df |= 0xB1; 347 break; 348 case 2: 349 df |= 0xA2; 350 break; 351 } 352 return df; 353 } 354 355 static void maven_compute_bwlevel (const struct maven_data* md, 356 int *bl, int *wl) { 357 const int b = md->primary_head->altout.tvo_params.brightness + BLMIN; 358 const int c = md->primary_head->altout.tvo_params.contrast; 359 360 *bl = max(b - c, BLMIN); 361 *wl = min(b + c, WLMAX); 362 } 363 364 static const struct maven_gamma* maven_compute_gamma (const struct maven_data* md) { 365 return maven_gamma + md->primary_head->altout.tvo_params.gamma; 366 } 367 368 369 static void maven_init_TVdata(const struct maven_data* md, struct mavenregs* data) { 370 static struct mavenregs palregs = { { 371 0x2A, 0x09, 0x8A, 0xCB, /* 00: chroma subcarrier */ 372 0x00, 373 0x00, /* ? not written */ 374 0x00, /* modified by code (F9 written...) */ 375 0x00, /* ? not written */ 376 0x7E, /* 08 */ 377 0x44, /* 09 */ 378 0x9C, /* 0A */ 379 0x2E, /* 0B */ 380 0x21, /* 0C */ 381 0x00, /* ? not written */ 382 0x3F, 0x03, /* 0E-0F */ 383 0x3F, 0x03, /* 10-11 */ 384 0x1A, /* 12 */ 385 0x2A, /* 13 */ 386 0x1C, 0x3D, 0x14, /* 14-16 */ 387 0x9C, 0x01, /* 17-18 */ 388 0x00, /* 19 */ 389 0xFE, /* 1A */ 390 0x7E, /* 1B */ 391 0x60, /* 1C */ 392 0x05, /* 1D */ 393 0x89, 0x03, /* 1E-1F */ 394 0x72, /* 20 */ 395 0x07, /* 21 */ 396 0x72, /* 22 */ 397 0x00, /* 23 */ 398 0x00, /* 24 */ 399 0x00, /* 25 */ 400 0x08, /* 26 */ 401 0x04, /* 27 */ 402 0x00, /* 28 */ 403 0x1A, /* 29 */ 404 0x55, 0x01, /* 2A-2B */ 405 0x26, /* 2C */ 406 0x07, 0x7E, /* 2D-2E */ 407 0x02, 0x54, /* 2F-30 */ 408 0xB0, 0x00, /* 31-32 */ 409 0x14, /* 33 */ 410 0x49, /* 34 */ 411 0x00, /* 35 written multiple times */ 412 0x00, /* 36 not written */ 413 0xA3, /* 37 */ 414 0xC8, /* 38 */ 415 0x22, /* 39 */ 416 0x02, /* 3A */ 417 0x22, /* 3B */ 418 0x3F, 0x03, /* 3C-3D */ 419 0x00, /* 3E written multiple times */ 420 0x00, /* 3F not written */ 421 }, MATROXFB_OUTPUT_MODE_PAL, 625, 50 }; 422 static struct mavenregs ntscregs = { { 423 0x21, 0xF0, 0x7C, 0x1F, /* 00: chroma subcarrier */ 424 0x00, 425 0x00, /* ? not written */ 426 0x00, /* modified by code (F9 written...) */ 427 0x00, /* ? not written */ 428 0x7E, /* 08 */ 429 0x43, /* 09 */ 430 0x7E, /* 0A */ 431 0x3D, /* 0B */ 432 0x00, /* 0C */ 433 0x00, /* ? not written */ 434 0x41, 0x00, /* 0E-0F */ 435 0x3C, 0x00, /* 10-11 */ 436 0x17, /* 12 */ 437 0x21, /* 13 */ 438 0x1B, 0x1B, 0x24, /* 14-16 */ 439 0x83, 0x01, /* 17-18 */ 440 0x00, /* 19 */ 441 0x0F, /* 1A */ 442 0x0F, /* 1B */ 443 0x60, /* 1C */ 444 0x05, /* 1D */ 445 0x89, 0x02, /* 1E-1F */ 446 0x5F, /* 20 */ 447 0x04, /* 21 */ 448 0x5F, /* 22 */ 449 0x01, /* 23 */ 450 0x02, /* 24 */ 451 0x00, /* 25 */ 452 0x0A, /* 26 */ 453 0x05, /* 27 */ 454 0x00, /* 28 */ 455 0x10, /* 29 */ 456 0xFF, 0x03, /* 2A-2B */ 457 0x24, /* 2C */ 458 0x0F, 0x78, /* 2D-2E */ 459 0x00, 0x00, /* 2F-30 */ 460 0xB2, 0x04, /* 31-32 */ 461 0x14, /* 33 */ 462 0x02, /* 34 */ 463 0x00, /* 35 written multiple times */ 464 0x00, /* 36 not written */ 465 0xA3, /* 37 */ 466 0xC8, /* 38 */ 467 0x15, /* 39 */ 468 0x05, /* 3A */ 469 0x3B, /* 3B */ 470 0x3C, 0x00, /* 3C-3D */ 471 0x00, /* 3E written multiple times */ 472 0x00, /* never written */ 473 }, MATROXFB_OUTPUT_MODE_NTSC, 525, 60 }; 474 struct matrox_fb_info *minfo = md->primary_head; 475 476 if (minfo->outputs[1].mode == MATROXFB_OUTPUT_MODE_PAL) 477 *data = palregs; 478 else 479 *data = ntscregs; 480 481 /* Set deflicker */ 482 data->regs[0x93] = maven_compute_deflicker(md); 483 484 /* set gamma */ 485 { 486 const struct maven_gamma* g; 487 g = maven_compute_gamma(md); 488 data->regs[0x83] = g->reg83; 489 data->regs[0x84] = g->reg84; 490 data->regs[0x85] = g->reg85; 491 data->regs[0x86] = g->reg86; 492 data->regs[0x87] = g->reg87; 493 data->regs[0x88] = g->reg88; 494 data->regs[0x89] = g->reg89; 495 data->regs[0x8A] = g->reg8a; 496 data->regs[0x8B] = g->reg8b; 497 } 498 499 /* Set contrast / brightness */ 500 { 501 int bl, wl; 502 maven_compute_bwlevel (md, &bl, &wl); 503 data->regs[0x0e] = bl >> 2; 504 data->regs[0x0f] = bl & 3; 505 data->regs[0x1e] = wl >> 2; 506 data->regs[0x1f] = wl & 3; 507 } 508 509 /* Set saturation */ 510 { 511 data->regs[0x20] = 512 data->regs[0x22] = minfo->altout.tvo_params.saturation; 513 } 514 515 /* Set HUE */ 516 data->regs[0x25] = minfo->altout.tvo_params.hue; 517 return; 518 } 519 520 #define LR(x) maven_set_reg(c, (x), m->regs[(x)]) 521 #define LRP(x) maven_set_reg_pair(c, (x), m->regs[(x)] | (m->regs[(x)+1] << 8)) 522 static void maven_init_TV(struct i2c_client* c, const struct mavenregs* m) { 523 int val; 524 525 526 maven_set_reg(c, 0x3E, 0x01); 527 maven_get_reg(c, 0x82); /* fetch oscillator state? */ 528 maven_set_reg(c, 0x8C, 0x00); 529 maven_get_reg(c, 0x94); /* get 0x82 */ 530 maven_set_reg(c, 0x94, 0xA2); 531 /* xmiscctrl */ 532 533 maven_set_reg_pair(c, 0x8E, 0x1EFF); 534 maven_set_reg(c, 0xC6, 0x01); 535 536 /* removed code... */ 537 538 maven_get_reg(c, 0x06); 539 maven_set_reg(c, 0x06, 0xF9); /* or read |= 0xF0 ? */ 540 541 /* removed code here... */ 542 543 /* real code begins here? */ 544 /* chroma subcarrier */ 545 LR(0x00); LR(0x01); LR(0x02); LR(0x03); 546 547 LR(0x04); 548 549 LR(0x2C); 550 LR(0x08); 551 LR(0x0A); 552 LR(0x09); 553 LR(0x29); 554 LRP(0x31); 555 LRP(0x17); 556 LR(0x0B); 557 LR(0x0C); 558 if (m->mode == MATROXFB_OUTPUT_MODE_PAL) { 559 maven_set_reg(c, 0x35, 0x10); /* ... */ 560 } else { 561 maven_set_reg(c, 0x35, 0x0F); /* ... */ 562 } 563 564 LRP(0x10); 565 566 LRP(0x0E); 567 LRP(0x1E); 568 569 LR(0x20); /* saturation #1 */ 570 LR(0x22); /* saturation #2 */ 571 LR(0x25); /* hue */ 572 LR(0x34); 573 LR(0x33); 574 LR(0x19); 575 LR(0x12); 576 LR(0x3B); 577 LR(0x13); 578 LR(0x39); 579 LR(0x1D); 580 LR(0x3A); 581 LR(0x24); 582 LR(0x14); 583 LR(0x15); 584 LR(0x16); 585 LRP(0x2D); 586 LRP(0x2F); 587 LR(0x1A); 588 LR(0x1B); 589 LR(0x1C); 590 LR(0x23); 591 LR(0x26); 592 LR(0x28); 593 LR(0x27); 594 LR(0x21); 595 LRP(0x2A); 596 if (m->mode == MATROXFB_OUTPUT_MODE_PAL) 597 maven_set_reg(c, 0x35, 0x1D); /* ... */ 598 else 599 maven_set_reg(c, 0x35, 0x1C); 600 601 LRP(0x3C); 602 LR(0x37); 603 LR(0x38); 604 maven_set_reg(c, 0xB3, 0x01); 605 606 maven_get_reg(c, 0xB0); /* read 0x80 */ 607 maven_set_reg(c, 0xB0, 0x08); /* ugh... */ 608 maven_get_reg(c, 0xB9); /* read 0x7C */ 609 maven_set_reg(c, 0xB9, 0x78); 610 maven_get_reg(c, 0xBF); /* read 0x00 */ 611 maven_set_reg(c, 0xBF, 0x02); 612 maven_get_reg(c, 0x94); /* read 0x82 */ 613 maven_set_reg(c, 0x94, 0xB3); 614 615 LR(0x80); /* 04 1A 91 or 05 21 91 */ 616 LR(0x81); 617 LR(0x82); 618 619 maven_set_reg(c, 0x8C, 0x20); 620 maven_get_reg(c, 0x8D); 621 maven_set_reg(c, 0x8D, 0x10); 622 623 LR(0x90); /* 4D 50 52 or 4E 05 45 */ 624 LR(0x91); 625 LR(0x92); 626 627 LRP(0x9A); /* 0049 or 004F */ 628 LRP(0x9C); /* 0004 or 0004 */ 629 LRP(0x9E); /* 0458 or 045E */ 630 LRP(0xA0); /* 05DA or 051B */ 631 LRP(0xA2); /* 00CC or 00CF */ 632 LRP(0xA4); /* 007D or 007F */ 633 LRP(0xA6); /* 007C or 007E */ 634 LRP(0xA8); /* 03CB or 03CE */ 635 LRP(0x98); /* 0000 or 0000 */ 636 LRP(0xAE); /* 0044 or 003A */ 637 LRP(0x96); /* 05DA or 051B */ 638 LRP(0xAA); /* 04BC or 046A */ 639 LRP(0xAC); /* 004D or 004E */ 640 641 LR(0xBE); 642 LR(0xC2); 643 644 maven_get_reg(c, 0x8D); 645 maven_set_reg(c, 0x8D, 0x04); 646 647 LR(0x20); /* saturation #1 */ 648 LR(0x22); /* saturation #2 */ 649 LR(0x93); /* whoops */ 650 LR(0x20); /* oh, saturation #1 again */ 651 LR(0x22); /* oh, saturation #2 again */ 652 LR(0x25); /* hue */ 653 LRP(0x0E); 654 LRP(0x1E); 655 LRP(0x0E); /* problems with memory? */ 656 LRP(0x1E); /* yes, matrox must have problems in memory area... */ 657 658 /* load gamma correction stuff */ 659 LR(0x83); 660 LR(0x84); 661 LR(0x85); 662 LR(0x86); 663 LR(0x87); 664 LR(0x88); 665 LR(0x89); 666 LR(0x8A); 667 LR(0x8B); 668 669 val = maven_get_reg(c, 0x8D); 670 val &= 0x14; /* 0x10 or anything ored with it */ 671 maven_set_reg(c, 0x8D, val); 672 673 LR(0x33); 674 LR(0x19); 675 LR(0x12); 676 LR(0x3B); 677 LR(0x13); 678 LR(0x39); 679 LR(0x1D); 680 LR(0x3A); 681 LR(0x24); 682 LR(0x14); 683 LR(0x15); 684 LR(0x16); 685 LRP(0x2D); 686 LRP(0x2F); 687 LR(0x1A); 688 LR(0x1B); 689 LR(0x1C); 690 LR(0x23); 691 LR(0x26); 692 LR(0x28); 693 LR(0x27); 694 LR(0x21); 695 LRP(0x2A); 696 if (m->mode == MATROXFB_OUTPUT_MODE_PAL) 697 maven_set_reg(c, 0x35, 0x1D); 698 else 699 maven_set_reg(c, 0x35, 0x1C); 700 LRP(0x3C); 701 LR(0x37); 702 LR(0x38); 703 704 maven_get_reg(c, 0xB0); 705 LR(0xB0); /* output mode */ 706 LR(0x90); 707 LR(0xBE); 708 LR(0xC2); 709 710 LRP(0x9A); 711 LRP(0xA2); 712 LRP(0x9E); 713 LRP(0xA6); 714 LRP(0xAA); 715 LRP(0xAC); 716 maven_set_reg(c, 0x3E, 0x00); 717 maven_set_reg(c, 0x95, 0x20); 718 } 719 720 static int maven_find_exact_clocks(unsigned int ht, unsigned int vt, 721 struct mavenregs* m) { 722 unsigned int x; 723 unsigned int err = ~0; 724 725 /* 1:1 */ 726 m->regs[0x80] = 0x0F; 727 m->regs[0x81] = 0x07; 728 m->regs[0x82] = 0x81; 729 730 for (x = 0; x < 8; x++) { 731 unsigned int c; 732 unsigned int uninitialized_var(a), uninitialized_var(b), 733 uninitialized_var(h2); 734 unsigned int h = ht + 2 + x; 735 736 if (!matroxfb_mavenclock((m->mode == MATROXFB_OUTPUT_MODE_PAL) ? &maven_PAL : &maven_NTSC, h, vt, &a, &b, &c, &h2)) { 737 unsigned int diff = h - h2; 738 739 if (diff < err) { 740 err = diff; 741 m->regs[0x80] = a - 1; 742 m->regs[0x81] = b - 1; 743 m->regs[0x82] = c | 0x80; 744 m->hcorr = h2 - 2; 745 m->htotal = h - 2; 746 } 747 } 748 } 749 return err != ~0U; 750 } 751 752 static inline int maven_compute_timming(struct maven_data* md, 753 struct my_timming* mt, 754 struct mavenregs* m) { 755 unsigned int tmpi; 756 unsigned int a, bv, c; 757 struct matrox_fb_info *minfo = md->primary_head; 758 759 m->mode = minfo->outputs[1].mode; 760 if (m->mode != MATROXFB_OUTPUT_MODE_MONITOR) { 761 unsigned int lmargin; 762 unsigned int umargin; 763 unsigned int vslen; 764 unsigned int hcrt; 765 unsigned int slen; 766 767 maven_init_TVdata(md, m); 768 769 if (maven_find_exact_clocks(mt->HTotal, mt->VTotal, m) == 0) 770 return -EINVAL; 771 772 lmargin = mt->HTotal - mt->HSyncEnd; 773 slen = mt->HSyncEnd - mt->HSyncStart; 774 hcrt = mt->HTotal - slen - mt->delay; 775 umargin = mt->VTotal - mt->VSyncEnd; 776 vslen = mt->VSyncEnd - mt->VSyncStart; 777 778 if (m->hcorr < mt->HTotal) 779 hcrt += m->hcorr; 780 if (hcrt > mt->HTotal) 781 hcrt -= mt->HTotal; 782 if (hcrt + 2 > mt->HTotal) 783 hcrt = 0; /* or issue warning? */ 784 785 /* last (first? middle?) line in picture can have different length */ 786 /* hlen - 2 */ 787 m->regs[0x96] = m->hcorr; 788 m->regs[0x97] = m->hcorr >> 8; 789 /* ... */ 790 m->regs[0x98] = 0x00; m->regs[0x99] = 0x00; 791 /* hblanking end */ 792 m->regs[0x9A] = lmargin; /* 100% */ 793 m->regs[0x9B] = lmargin >> 8; /* 100% */ 794 /* who knows */ 795 m->regs[0x9C] = 0x04; 796 m->regs[0x9D] = 0x00; 797 /* htotal - 2 */ 798 m->regs[0xA0] = m->htotal; 799 m->regs[0xA1] = m->htotal >> 8; 800 /* vblanking end */ 801 m->regs[0xA2] = mt->VTotal - mt->VSyncStart - 1; /* stop vblanking */ 802 m->regs[0xA3] = (mt->VTotal - mt->VSyncStart - 1) >> 8; 803 /* something end... [A6]+1..[A8] */ 804 if (md->version == MGATVO_B) { 805 m->regs[0xA4] = 0x04; 806 m->regs[0xA5] = 0x00; 807 } else { 808 m->regs[0xA4] = 0x01; 809 m->regs[0xA5] = 0x00; 810 } 811 /* something start... 0..[A4]-1 */ 812 m->regs[0xA6] = 0x00; 813 m->regs[0xA7] = 0x00; 814 /* vertical line count - 1 */ 815 m->regs[0xA8] = mt->VTotal - 1; 816 m->regs[0xA9] = (mt->VTotal - 1) >> 8; 817 /* horizontal vidrst pos */ 818 m->regs[0xAA] = hcrt; /* 0 <= hcrt <= htotal - 2 */ 819 m->regs[0xAB] = hcrt >> 8; 820 /* vertical vidrst pos */ 821 m->regs[0xAC] = mt->VTotal - 2; 822 m->regs[0xAD] = (mt->VTotal - 2) >> 8; 823 /* moves picture up/down and so on... */ 824 m->regs[0xAE] = 0x01; /* Fix this... 0..VTotal */ 825 m->regs[0xAF] = 0x00; 826 { 827 int hdec; 828 int hlen; 829 unsigned int ibmin = 4 + lmargin + mt->HDisplay; 830 unsigned int ib; 831 int i; 832 833 /* Verify! */ 834 /* Where 94208 came from? */ 835 if (mt->HTotal) 836 hdec = 94208 / (mt->HTotal); 837 else 838 hdec = 0x81; 839 if (hdec > 0x81) 840 hdec = 0x81; 841 if (hdec < 0x41) 842 hdec = 0x41; 843 hdec--; 844 hlen = 98304 - 128 - ((lmargin + mt->HDisplay - 8) * hdec); 845 if (hlen < 0) 846 hlen = 0; 847 hlen = hlen >> 8; 848 if (hlen > 0xFF) 849 hlen = 0xFF; 850 /* Now we have to compute input buffer length. 851 If you want any picture, it must be between 852 4 + lmargin + xres 853 and 854 94208 / hdec 855 If you want perfect picture even on the top 856 of screen, it must be also 857 0x3C0000 * i / hdec + Q - R / hdec 858 where 859 R Qmin Qmax 860 0x07000 0x5AE 0x5BF 861 0x08000 0x5CF 0x5FF 862 0x0C000 0x653 0x67F 863 0x10000 0x6F8 0x6FF 864 */ 865 i = 1; 866 do { 867 ib = ((0x3C0000 * i - 0x8000)/ hdec + 0x05E7) >> 8; 868 i++; 869 } while (ib < ibmin); 870 if (ib >= m->htotal + 2) { 871 ib = ibmin; 872 } 873 874 m->regs[0x90] = hdec; /* < 0x40 || > 0x80 is bad... 0x80 is questionable */ 875 m->regs[0xC2] = hlen; 876 /* 'valid' input line length */ 877 m->regs[0x9E] = ib; 878 m->regs[0x9F] = ib >> 8; 879 } 880 { 881 int vdec; 882 int vlen; 883 884 #define MATROX_USE64BIT_DIVIDE 885 if (mt->VTotal) { 886 #ifdef MATROX_USE64BIT_DIVIDE 887 u64 f1; 888 u32 a; 889 u32 b; 890 891 a = m->vlines * (m->htotal + 2); 892 b = (mt->VTotal - 1) * (m->htotal + 2) + m->hcorr + 2; 893 894 f1 = ((u64)a) << 15; /* *32768 */ 895 do_div(f1, b); 896 vdec = f1; 897 #else 898 vdec = m->vlines * 32768 / mt->VTotal; 899 #endif 900 } else 901 vdec = 0x8000; 902 if (vdec > 0x8000) 903 vdec = 0x8000; 904 vlen = (vslen + umargin + mt->VDisplay) * vdec; 905 vlen = (vlen >> 16) - 146; /* FIXME: 146?! */ 906 if (vlen < 0) 907 vlen = 0; 908 if (vlen > 0xFF) 909 vlen = 0xFF; 910 vdec--; 911 m->regs[0x91] = vdec; 912 m->regs[0x92] = vdec >> 8; 913 m->regs[0xBE] = vlen; 914 } 915 m->regs[0xB0] = 0x08; /* output: SVideo/Composite */ 916 return 0; 917 } 918 919 DAC1064_calcclock(mt->pixclock, 450000, &a, &bv, &c); 920 m->regs[0x80] = a; 921 m->regs[0x81] = bv; 922 m->regs[0x82] = c | 0x80; 923 924 m->regs[0xB3] = 0x01; 925 m->regs[0x94] = 0xB2; 926 927 /* htotal... */ 928 m->regs[0x96] = mt->HTotal; 929 m->regs[0x97] = mt->HTotal >> 8; 930 /* ?? */ 931 m->regs[0x98] = 0x00; 932 m->regs[0x99] = 0x00; 933 /* hsync len */ 934 tmpi = mt->HSyncEnd - mt->HSyncStart; 935 m->regs[0x9A] = tmpi; 936 m->regs[0x9B] = tmpi >> 8; 937 /* hblank end */ 938 tmpi = mt->HTotal - mt->HSyncStart; 939 m->regs[0x9C] = tmpi; 940 m->regs[0x9D] = tmpi >> 8; 941 /* hblank start */ 942 tmpi += mt->HDisplay; 943 m->regs[0x9E] = tmpi; 944 m->regs[0x9F] = tmpi >> 8; 945 /* htotal + 1 */ 946 tmpi = mt->HTotal + 1; 947 m->regs[0xA0] = tmpi; 948 m->regs[0xA1] = tmpi >> 8; 949 /* vsync?! */ 950 tmpi = mt->VSyncEnd - mt->VSyncStart - 1; 951 m->regs[0xA2] = tmpi; 952 m->regs[0xA3] = tmpi >> 8; 953 /* ignored? */ 954 tmpi = mt->VTotal - mt->VSyncStart; 955 m->regs[0xA4] = tmpi; 956 m->regs[0xA5] = tmpi >> 8; 957 /* ignored? */ 958 tmpi = mt->VTotal - 1; 959 m->regs[0xA6] = tmpi; 960 m->regs[0xA7] = tmpi >> 8; 961 /* vtotal - 1 */ 962 m->regs[0xA8] = tmpi; 963 m->regs[0xA9] = tmpi >> 8; 964 /* hor vidrst */ 965 tmpi = mt->HTotal - mt->delay; 966 m->regs[0xAA] = tmpi; 967 m->regs[0xAB] = tmpi >> 8; 968 /* vert vidrst */ 969 tmpi = mt->VTotal - 2; 970 m->regs[0xAC] = tmpi; 971 m->regs[0xAD] = tmpi >> 8; 972 /* ignored? */ 973 m->regs[0xAE] = 0x00; 974 m->regs[0xAF] = 0x00; 975 976 m->regs[0xB0] = 0x03; /* output: monitor */ 977 m->regs[0xB1] = 0xA0; /* ??? */ 978 m->regs[0x8C] = 0x20; /* must be set... */ 979 m->regs[0x8D] = 0x04; /* defaults to 0x10: test signal */ 980 m->regs[0xB9] = 0x1A; /* defaults to 0x2C: too bright */ 981 m->regs[0xBF] = 0x22; /* makes picture stable */ 982 983 return 0; 984 } 985 986 static int maven_program_timming(struct maven_data* md, 987 const struct mavenregs* m) { 988 struct i2c_client *c = md->client; 989 990 if (m->mode == MATROXFB_OUTPUT_MODE_MONITOR) { 991 LR(0x80); 992 LR(0x81); 993 LR(0x82); 994 995 LR(0xB3); 996 LR(0x94); 997 998 LRP(0x96); 999 LRP(0x98); 1000 LRP(0x9A); 1001 LRP(0x9C); 1002 LRP(0x9E); 1003 LRP(0xA0); 1004 LRP(0xA2); 1005 LRP(0xA4); 1006 LRP(0xA6); 1007 LRP(0xA8); 1008 LRP(0xAA); 1009 LRP(0xAC); 1010 LRP(0xAE); 1011 1012 LR(0xB0); /* output: monitor */ 1013 LR(0xB1); /* ??? */ 1014 LR(0x8C); /* must be set... */ 1015 LR(0x8D); /* defaults to 0x10: test signal */ 1016 LR(0xB9); /* defaults to 0x2C: too bright */ 1017 LR(0xBF); /* makes picture stable */ 1018 } else { 1019 maven_init_TV(c, m); 1020 } 1021 return 0; 1022 } 1023 1024 static inline int maven_resync(struct maven_data* md) { 1025 struct i2c_client *c = md->client; 1026 maven_set_reg(c, 0x95, 0x20); /* start whole thing */ 1027 return 0; 1028 } 1029 1030 static int maven_get_queryctrl (struct maven_data* md, 1031 struct v4l2_queryctrl *p) { 1032 int i; 1033 1034 i = get_ctrl_id(p->id); 1035 if (i >= 0) { 1036 *p = maven_controls[i].desc; 1037 return 0; 1038 } 1039 if (i == -ENOENT) { 1040 static const struct v4l2_queryctrl disctrl = 1041 { .flags = V4L2_CTRL_FLAG_DISABLED }; 1042 1043 i = p->id; 1044 *p = disctrl; 1045 p->id = i; 1046 sprintf(p->name, "Ctrl #%08X", i); 1047 return 0; 1048 } 1049 return -EINVAL; 1050 } 1051 1052 static int maven_set_control (struct maven_data* md, 1053 struct v4l2_control *p) { 1054 int i; 1055 1056 i = get_ctrl_id(p->id); 1057 if (i < 0) return -EINVAL; 1058 1059 /* 1060 * Check if changed. 1061 */ 1062 if (p->value == *get_ctrl_ptr(md, i)) return 0; 1063 1064 /* 1065 * Check limits. 1066 */ 1067 if (p->value > maven_controls[i].desc.maximum) return -EINVAL; 1068 if (p->value < maven_controls[i].desc.minimum) return -EINVAL; 1069 1070 /* 1071 * Store new value. 1072 */ 1073 *get_ctrl_ptr(md, i) = p->value; 1074 1075 switch (p->id) { 1076 case V4L2_CID_BRIGHTNESS: 1077 case V4L2_CID_CONTRAST: 1078 { 1079 int blacklevel, whitelevel; 1080 maven_compute_bwlevel(md, &blacklevel, &whitelevel); 1081 blacklevel = (blacklevel >> 2) | ((blacklevel & 3) << 8); 1082 whitelevel = (whitelevel >> 2) | ((whitelevel & 3) << 8); 1083 maven_set_reg_pair(md->client, 0x0e, blacklevel); 1084 maven_set_reg_pair(md->client, 0x1e, whitelevel); 1085 } 1086 break; 1087 case V4L2_CID_SATURATION: 1088 { 1089 maven_set_reg(md->client, 0x20, p->value); 1090 maven_set_reg(md->client, 0x22, p->value); 1091 } 1092 break; 1093 case V4L2_CID_HUE: 1094 { 1095 maven_set_reg(md->client, 0x25, p->value); 1096 } 1097 break; 1098 case V4L2_CID_GAMMA: 1099 { 1100 const struct maven_gamma* g; 1101 g = maven_compute_gamma(md); 1102 maven_set_reg(md->client, 0x83, g->reg83); 1103 maven_set_reg(md->client, 0x84, g->reg84); 1104 maven_set_reg(md->client, 0x85, g->reg85); 1105 maven_set_reg(md->client, 0x86, g->reg86); 1106 maven_set_reg(md->client, 0x87, g->reg87); 1107 maven_set_reg(md->client, 0x88, g->reg88); 1108 maven_set_reg(md->client, 0x89, g->reg89); 1109 maven_set_reg(md->client, 0x8a, g->reg8a); 1110 maven_set_reg(md->client, 0x8b, g->reg8b); 1111 } 1112 break; 1113 case MATROXFB_CID_TESTOUT: 1114 { 1115 unsigned char val 1116 = maven_get_reg(md->client, 0x8d); 1117 if (p->value) val |= 0x10; 1118 else val &= ~0x10; 1119 maven_set_reg(md->client, 0x8d, val); 1120 } 1121 break; 1122 case MATROXFB_CID_DEFLICKER: 1123 { 1124 maven_set_reg(md->client, 0x93, maven_compute_deflicker(md)); 1125 } 1126 break; 1127 } 1128 1129 1130 return 0; 1131 } 1132 1133 static int maven_get_control (struct maven_data* md, 1134 struct v4l2_control *p) { 1135 int i; 1136 1137 i = get_ctrl_id(p->id); 1138 if (i < 0) return -EINVAL; 1139 p->value = *get_ctrl_ptr(md, i); 1140 return 0; 1141 } 1142 1143 /******************************************************/ 1144 1145 static int maven_out_compute(void* md, struct my_timming* mt) { 1146 #define mdinfo ((struct maven_data*)md) 1147 #define minfo (mdinfo->primary_head) 1148 return maven_compute_timming(md, mt, &minfo->hw.maven); 1149 #undef minfo 1150 #undef mdinfo 1151 } 1152 1153 static int maven_out_program(void* md) { 1154 #define mdinfo ((struct maven_data*)md) 1155 #define minfo (mdinfo->primary_head) 1156 return maven_program_timming(md, &minfo->hw.maven); 1157 #undef minfo 1158 #undef mdinfo 1159 } 1160 1161 static int maven_out_start(void* md) { 1162 return maven_resync(md); 1163 } 1164 1165 static int maven_out_verify_mode(void* md, u_int32_t arg) { 1166 switch (arg) { 1167 case MATROXFB_OUTPUT_MODE_PAL: 1168 case MATROXFB_OUTPUT_MODE_NTSC: 1169 case MATROXFB_OUTPUT_MODE_MONITOR: 1170 return 0; 1171 } 1172 return -EINVAL; 1173 } 1174 1175 static int maven_out_get_queryctrl(void* md, struct v4l2_queryctrl* p) { 1176 return maven_get_queryctrl(md, p); 1177 } 1178 1179 static int maven_out_get_ctrl(void* md, struct v4l2_control* p) { 1180 return maven_get_control(md, p); 1181 } 1182 1183 static int maven_out_set_ctrl(void* md, struct v4l2_control* p) { 1184 return maven_set_control(md, p); 1185 } 1186 1187 static struct matrox_altout maven_altout = { 1188 .name = "Secondary output", 1189 .compute = maven_out_compute, 1190 .program = maven_out_program, 1191 .start = maven_out_start, 1192 .verifymode = maven_out_verify_mode, 1193 .getqueryctrl = maven_out_get_queryctrl, 1194 .getctrl = maven_out_get_ctrl, 1195 .setctrl = maven_out_set_ctrl, 1196 }; 1197 1198 static int maven_init_client(struct i2c_client* clnt) { 1199 struct maven_data* md = i2c_get_clientdata(clnt); 1200 struct matrox_fb_info *minfo = container_of(clnt->adapter, 1201 struct i2c_bit_adapter, 1202 adapter)->minfo; 1203 1204 md->primary_head = minfo; 1205 md->client = clnt; 1206 down_write(&minfo->altout.lock); 1207 minfo->outputs[1].output = &maven_altout; 1208 minfo->outputs[1].src = minfo->outputs[1].default_src; 1209 minfo->outputs[1].data = md; 1210 minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; 1211 up_write(&minfo->altout.lock); 1212 if (maven_get_reg(clnt, 0xB2) < 0x14) { 1213 md->version = MGATVO_B; 1214 /* Tweak some things for this old chip */ 1215 } else { 1216 md->version = MGATVO_C; 1217 } 1218 /* 1219 * Set all parameters to its initial values. 1220 */ 1221 { 1222 unsigned int i; 1223 1224 for (i = 0; i < MAVCTRLS; ++i) { 1225 *get_ctrl_ptr(md, i) = maven_controls[i].desc.default_value; 1226 } 1227 } 1228 1229 return 0; 1230 } 1231 1232 static int maven_shutdown_client(struct i2c_client* clnt) { 1233 struct maven_data* md = i2c_get_clientdata(clnt); 1234 1235 if (md->primary_head) { 1236 struct matrox_fb_info *minfo = md->primary_head; 1237 1238 down_write(&minfo->altout.lock); 1239 minfo->outputs[1].src = MATROXFB_SRC_NONE; 1240 minfo->outputs[1].output = NULL; 1241 minfo->outputs[1].data = NULL; 1242 minfo->outputs[1].mode = MATROXFB_OUTPUT_MODE_MONITOR; 1243 up_write(&minfo->altout.lock); 1244 md->primary_head = NULL; 1245 } 1246 return 0; 1247 } 1248 1249 static int maven_probe(struct i2c_client *client, 1250 const struct i2c_device_id *id) 1251 { 1252 struct i2c_adapter *adapter = client->adapter; 1253 int err = -ENODEV; 1254 struct maven_data* data; 1255 1256 if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_WORD_DATA | 1257 I2C_FUNC_SMBUS_BYTE_DATA | 1258 I2C_FUNC_NOSTART | 1259 I2C_FUNC_PROTOCOL_MANGLING)) 1260 goto ERROR0; 1261 if (!(data = kzalloc(sizeof(*data), GFP_KERNEL))) { 1262 err = -ENOMEM; 1263 goto ERROR0; 1264 } 1265 i2c_set_clientdata(client, data); 1266 err = maven_init_client(client); 1267 if (err) 1268 goto ERROR4; 1269 return 0; 1270 ERROR4:; 1271 kfree(data); 1272 ERROR0:; 1273 return err; 1274 } 1275 1276 static int maven_remove(struct i2c_client *client) 1277 { 1278 maven_shutdown_client(client); 1279 kfree(i2c_get_clientdata(client)); 1280 return 0; 1281 } 1282 1283 static const struct i2c_device_id maven_id[] = { 1284 { "maven", 0 }, 1285 { } 1286 }; 1287 MODULE_DEVICE_TABLE(i2c, maven_id); 1288 1289 static struct i2c_driver maven_driver={ 1290 .driver = { 1291 .name = "maven", 1292 }, 1293 .probe = maven_probe, 1294 .remove = maven_remove, 1295 .id_table = maven_id, 1296 }; 1297 1298 module_i2c_driver(maven_driver); 1299 MODULE_AUTHOR("(c) 1999-2002 Petr Vandrovec <vandrove@vc.cvut.cz>"); 1300 MODULE_DESCRIPTION("Matrox G200/G400 Matrox MGA-TVO driver"); 1301 MODULE_LICENSE("GPL"); 1302