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