/* Authors: Mark Watson 2000, Rudolf Cornelissen 1/2003-5/2006 Thanx to Petr Vandrovec for writing matroxfb. */ #define MODULE_BIT 0x00100000 #include "mga_std.h" typedef struct { uint32 h_total; uint32 h_display; uint32 h_sync_length; uint32 front_porch; uint32 back_porch; uint32 color_burst; uint32 v_total; float chroma_subcarrier; } gx50_maven_timing; //fixme: try to implement 'fast' and 'slow' settings for all modes, // so buffer duplication or skipping won't be neccesary for realtime video. //fixme: try to setup the CRTC2 in interlaced mode for the video modes on <= G400MAX cards. /* find 'exact' valid video PLL setting */ status_t g100_g400max_maventv_vid_pll_find( display_mode target, unsigned int * ht_new, unsigned int * ht_last_line, uint8 * m_result, uint8 * n_result, uint8 * p_result) { int m = 0, n = 0, p = 0, m_max; float diff, diff_smallest = 999999999; int best[5], h_total_mod; float fields_sec, f_vco; /* We need to be exact, so work with clockperiods per field instead of with frequency. * Make sure however we truncate these clocks to be integers! * (The NTSC field frequency would otherwise prevent the 'whole number of clocks per field' * check done in this routine later on...) */ uint32 vco_clks_field, max_pclks_field, req_pclks_field; /* We need this variable to be a float, because we need to be able to verify if this * represents a whole number of clocks per field later on! */ float calc_pclks_field; LOG(2,("MAVENTV: searching for EXACT videoclock match\n")); /* determine the max. reference-frequency postscaler setting for the current card */ //fixme: check G100 and G200 m_max if exist and possible... switch(si->ps.card_type) { case G100: LOG(2,("MAVENTV: G100 restrictions apply\n")); m_max = 32; break; case G200: LOG(2,("MAVENTV: G200 restrictions apply\n")); m_max = 32; break; default: LOG(2,("MAVENTV: G400/G400MAX restrictions apply\n")); m_max = 32; break; } /* set number of fields per second to generate */ if ((target.flags & TV_BITS) == TV_PAL) fields_sec = 50.0; else fields_sec = 59.94; /* determine the max. pixelclock for the current videomode */ switch (target.space) { case B_RGB16_LITTLE: max_pclks_field = (si->ps.max_dac2_clock_16 * 1000000) / fields_sec; break; case B_RGB32_LITTLE: max_pclks_field = (si->ps.max_dac2_clock_32 * 1000000) / fields_sec; break; default: /* use fail-safe value */ max_pclks_field = (si->ps.max_dac2_clock_32 * 1000000) / fields_sec; break; } /* if some dualhead mode is active, an extra restriction might apply */ if ((target.flags & DUALHEAD_BITS) && (target.space == B_RGB32_LITTLE)) max_pclks_field = (si->ps.max_dac2_clock_32dh * 1000000) / fields_sec; /* Checkout all possible Htotal settings within the current granularity step * of CRTC2 to get a real close videoclock match! * (The MAVEN apparantly has a granularity of 1 pixel, while CRTC2 has 8 pixels) */ for (h_total_mod = 0; h_total_mod < 8; h_total_mod++) { LOG(2,("MAVENTV: trying h_total modification of +%d...\n", h_total_mod)); /* Calculate videoclock to be a bit to high so we can compensate for an exact * match via h_total_lastline.. */ *ht_new = target.timing.h_total + h_total_mod + 2; /* Make sure the requested pixelclock is within the PLL's operational limits */ /* lower limit is min_video_vco divided by highest postscaler-factor */ req_pclks_field = *ht_new * target.timing.v_total; if (req_pclks_field < (((si->ps.min_video_vco * 1000000) / fields_sec) / 8.0)) { req_pclks_field = (((si->ps.min_video_vco * 1000000) / fields_sec) / 8.0); LOG(4,("MAVENTV: WARNING, clamping at lowest possible videoclock\n")); } /* upper limit is given by pins in combination with current active mode */ if (req_pclks_field > max_pclks_field) { req_pclks_field = max_pclks_field; LOG(4,("MAVENTV: WARNING, clamping at highest possible videoclock\n")); } /* iterate through all valid PLL postscaler settings */ for (p=0x01; p < 0x10; p = p<<1) { /* calc the needed number of VCO clocks per field for this postscaler setting */ vco_clks_field = req_pclks_field * p; /* check if this is within range of the VCO specs */ if ((vco_clks_field >= ((si->ps.min_video_vco * 1000000) / fields_sec)) && (vco_clks_field <= ((si->ps.max_video_vco * 1000000) / fields_sec))) { /* iterate trough all valid reference-frequency postscaler settings */ for (m = 2; m <= m_max; m++) { /* calculate VCO postscaler setting for current setup.. */ n = (int)(((vco_clks_field * m) / ((si->ps.f_ref * 1000000) / fields_sec)) + 0.5); /* ..and check for validity */ if ((n < 8) || (n > 128)) continue; /* special TVmode stuff starts here (rest is in fact standard): */ /* calculate number of videoclocks per field */ calc_pclks_field = (((uint32)((si->ps.f_ref * 1000000) / fields_sec)) * n) / ((float)(m * p)); /* we need a whole number of clocks per field, otherwise it won't work correctly. * (TVout will flicker, green fields will occur) */ if (calc_pclks_field != (uint32)calc_pclks_field) continue; /* check if we have the min. needed number of clocks per field for a sync lock */ if (calc_pclks_field < ((*ht_new * (target.timing.v_total - 1)) + 2)) continue; /* calc number of clocks we have for the last field line */ *ht_last_line = calc_pclks_field - (*ht_new * (target.timing.v_total - 1)); /* check if we haven't got too much clocks in the last field line for a sync lock */ if (*ht_last_line > *ht_new) continue; /* we have a match! */ /* calculate the difference between a full line and the last line */ diff = *ht_new - *ht_last_line; /* if this last_line comes closer to a full line than earlier 'hits' then use it */ if (diff < diff_smallest) { /* log results */ if (diff_smallest == 999999999) LOG(2,("MAVENTV: MATCH, ")); else LOG(2,("MAVENTV: better MATCH,")); f_vco = (si->ps.f_ref / m) * n; LOG(2,("found vid VCO freq %fMhz, pixclk %fMhz\n", f_vco, (f_vco / p))); LOG(2,("MAVENTV: mnp(ex. filter) 0x%02x 0x%02x 0x%02x, h_total %d, ht_lastline %d\n", (m - 1), (n - 1), (p - 1), (*ht_new - 2), (*ht_last_line - 2))); /* remember this best match */ diff_smallest = diff; best[0] = m; best[1] = n; best[2] = p; /* h_total to use for this setting: * exclude the 'calculate clock a bit too high' trick */ best[3] = *ht_new - 2; /* ht_last_line to use for this setting: * exclude the 'calculate clock a bit too high' trick */ best[4] = *ht_last_line - 2; } } } } } LOG(2,("MAVENTV: search completed.\n")); /* setup the scalers programming values for found optimum setting */ m = best[0] - 1; n = best[1] - 1; p = best[2] - 1; /* if no match was found set fixed PLL frequency so we have something valid at least */ if (diff_smallest == 999999999) { LOG(4,("MAVENTV: WARNING, no MATCH found!\n")); if (si->ps.f_ref == 27.000) { /* set 13.5Mhz */ m = 0x03; n = 0x07; p = 0x03; } else { /* set 14.31818Mhz */ m = 0x01; n = 0x07; p = 0x03; } best[3] = target.timing.h_total; best[4] = target.timing.h_total; } /* calc the needed PLL loopbackfilter setting belonging to current VCO speed */ f_vco = (si->ps.f_ref / (m + 1)) * (n + 1); LOG(2,("MAVENTV: using vid VCO frequency %fMhz\n", f_vco)); switch(si->ps.card_type) { case G100: case G200: for(;;) { if (f_vco >= 180) {p |= (0x03 << 3); break;}; if (f_vco >= 140) {p |= (0x02 << 3); break;}; if (f_vco >= 100) {p |= (0x01 << 3); break;}; break; } break; default: for(;;) { if (f_vco >= 240) {p |= (0x03 << 3); break;}; if (f_vco >= 170) {p |= (0x02 << 3); break;}; if (f_vco >= 110) {p |= (0x01 << 3); break;}; break; } break; } /* return results */ *m_result = m; *n_result = n; *p_result = p; *ht_new = best[3]; *ht_last_line = best[4]; /* display the found pixelclock values */ LOG(2,("MAVENTV: vid PLL check: got %fMHz, mnp 0x%02x 0x%02x 0x%02x\n", (f_vco / ((p & 0x07) + 1)), m, n, p)); LOG(2,("MAVENTV: new h_total %d, ht_lastline %d\n", *ht_new, *ht_last_line)); /* return status */ if (diff_smallest == 999999999) return B_ERROR; return B_OK; } /* Notes about timing: * Note: * all horizontal timing is measured in pixelclock periods; * all? vertical timing is measured in field? lines. */ /* Note: * <= G400MAX cards have a fixed 27Mhz(?) clock for TV timing register values, * while on G450/G550 these need to be calculated based on the video pixelclock. */ /* Notes about signal strengths: * Note: * G400 and earlier cards have a fixed reference voltage of +2.6 Volt; * G450 and G550 cards MAVEN DACs have a switchable ref voltage of +1.5/+2.0 Volt. * * This voltage is used to feed the videosignals: * - Hsync pulse level; * - Lowest active video output level; * - Highest active video output level. * These actual voltages are set via 10bit DACs. * * G450/G550: * The color burst amplitude videosignal is fed by 80% of the above mentioned * ref. voltage, and is set via an 8bit DAC. * On G400 and earlier cards the ref. voltage is different, and also differs * for PAL and NTSC mode. */ /* Note: * Increasing the distance between the highest and lowest active video output * level increases contrast; decreasing it decreases contrast. */ /* Note: * Increasing both the highest and lowest active video output level with the * same amount increases brightness; decreasing it decreases brightness. */ /* Note: * Increasing the Hsync pulse level increases the black level, so decreases * brightness and contrast. */ /* Preset maven PAL output (625lines, 50Hz mode) */ static void gxx0_maventv_PAL_init(uint8* buffer) { uint16 value; /* Chroma subcarrier divider */ buffer[0x00] = 0x2A; buffer[0x01] = 0x09; buffer[0x02] = 0x8A; buffer[0x03] = 0xCB; buffer[0x04] = 0x00; buffer[0x05] = 0x00; buffer[0x06] = 0xF9; buffer[0x07] = 0x00; /* Hsync pulse length */ buffer[0x08] = 0x7E; /* color burst length */ buffer[0x09] = 0x44; /* back porch length */ buffer[0x0a] = 0x9C; /* color burst amplitude */ if (si->ps.card_type <= G400MAX) { buffer[0x0b] = 0x3e; } else { buffer[0x0b] = 0x48; } buffer[0x0c] = 0x21; buffer[0x0d] = 0x00; if (si->ps.card_type <= G400MAX) { /* Lowest active video output level. * Warning: make sure this stays above (or equals) the sync pulse level! */ value = 0x0ea; buffer[0x0e] = ((value >> 2) & 0xff); buffer[0x0f] = (value & 0x03); /* horizontal sync pulse level */ buffer[0x10] = ((value >> 2) & 0xff); buffer[0x11] = (value & 0x03); } else { /* Lowest active video output level. * Warning: make sure this stays above (or equals) the sync pulse level! */ value = 0x130; buffer[0x0e] = ((value >> 2) & 0xff); buffer[0x0f] = (value & 0x03); /* horizontal sync pulse level */ buffer[0x10] = ((value >> 2) & 0xff); buffer[0x11] = (value & 0x03); } buffer[0x12] = 0x1A; buffer[0x13] = 0x2A; /* functional unit */ buffer[0x14] = 0x1C; buffer[0x15] = 0x3D; buffer[0x16] = 0x14; /* vertical total */ //(=625) /* b9-2 */ buffer[0x17] = 0x9C; /* b1-0 in b1-0 */ buffer[0x18] = 0x01; buffer[0x19] = 0x00; buffer[0x1a] = 0xFE; buffer[0x1b] = 0x7E; buffer[0x1c] = 0x60; buffer[0x1d] = 0x05; /* Highest active video output level. * Warning: make sure this stays above the lowest active video output level! */ if (si->ps.card_type <= G400MAX) { value = 0x24f; buffer[0x1e] = ((value >> 2) & 0xff); buffer[0x1f] = (value & 0x03); } else { value = 0x300; buffer[0x1e] = ((value >> 2) & 0xff); buffer[0x1f] = (value & 0x03); } /* saturation (field?) #1 */ if (si->ps.card_type <= G400MAX) buffer[0x20] = 0x72; else buffer[0x20] = 0xA5; buffer[0x21] = 0x07; /* saturation (field?) #2 */ if (si->ps.card_type <= G400MAX) buffer[0x22] = 0x72; else buffer[0x22] = 0xA5; buffer[0x23] = 0x00; buffer[0x24] = 0x00; /* hue? */ buffer[0x25] = 0x00; buffer[0x26] = 0x08; buffer[0x27] = 0x04; buffer[0x28] = 0x00; buffer[0x29] = 0x1A; /* functional unit */ buffer[0x2a] = 0x55; buffer[0x2b] = 0x01; /* front porch length */ buffer[0x2c] = 0x26; /* functional unit */ buffer[0x2d] = 0x07; buffer[0x2e] = 0x7E; /* functional unit */ buffer[0x2f] = 0x02; buffer[0x30] = 0x54; /* horizontal visible */ value = 0x580; buffer[0x31] = ((value >> 3) & 0xff); buffer[0x32] = (value & 0x07); /* upper blanking (in field lines) */ buffer[0x33] = 0x14; //=((v_total - v_sync_end)/2) -1 buffer[0x34] = 0x49; buffer[0x35] = 0x00; buffer[0x36] = 0x00; buffer[0x37] = 0xA3; buffer[0x38] = 0xC8; buffer[0x39] = 0x22; buffer[0x3a] = 0x02; buffer[0x3b] = 0x22; /* functional unit */ buffer[0x3c] = 0x3F; buffer[0x3d] = 0x03; } /* Preset maven NTSC output (525lines, 59.94Hz mode) */ static void gxx0_maventv_NTSC_init(uint8* buffer) { uint16 value; /* Chroma subcarrier frequency */ buffer[0x00] = 0x21; buffer[0x01] = 0xF0; buffer[0x02] = 0x7C; buffer[0x03] = 0x1F; buffer[0x04] = 0x00; buffer[0x05] = 0x00;//b1 = ON enables colorbar testimage buffer[0x06] = 0xF9;//b0 = ON enables MAVEN TV output buffer[0x07] = 0x00;//influences the colorburst signal amplitude somehow /* Hsync pulse length */ buffer[0x08] = 0x7E; /* color burst length */ buffer[0x09] = 0x43; /* back porch length */ buffer[0x0a] = 0x7E; /* color burst amplitude */ if (si->ps.card_type <= G400MAX) { buffer[0x0b] = 0x46; } else { buffer[0x0b] = 0x48; } buffer[0x0c] = 0x00; buffer[0x0d] = 0x00; if (si->ps.card_type <= G400MAX) { /* Lowest active video output level. * Warning: make sure this stays above (or equals) the sync pulse level! */ value = 0x0ea; buffer[0x0e] = ((value >> 2) & 0xff); buffer[0x0f] = (value & 0x03); /* horizontal sync pulse level */ buffer[0x10] = ((value >> 2) & 0xff); buffer[0x11] = (value & 0x03); } else { /* Lowest active video output level. * Warning: make sure this stays above (or equals) the sync pulse level! */ value = 0x130; buffer[0x0e] = ((value >> 2) & 0xff); buffer[0x0f] = (value & 0x03); /* horizontal sync pulse level */ buffer[0x10] = ((value >> 2) & 0xff); buffer[0x11] = (value & 0x03); } buffer[0x12] = 0x17; buffer[0x13] = 0x21; /* functional unit */ buffer[0x14] = 0x1B; buffer[0x15] = 0x1B; buffer[0x16] = 0x24; /* vertical total */ /* b9-2 */ buffer[0x17] = 0x83; /* b1-0 in b1-0 */ buffer[0x18] = 0x01; buffer[0x19] = 0x00;//mv register? buffer[0x1a] = 0x0F; buffer[0x1b] = 0x0F; buffer[0x1c] = 0x60; buffer[0x1d] = 0x05; /* Highest active video output level. * Warning: make sure this stays above the lowest active video output level! */ if (si->ps.card_type <= G400MAX) { value = 0x24f; buffer[0x1e] = ((value >> 2) & 0xff); buffer[0x1f] = (value & 0x03); } else { value = 0x300; buffer[0x1e] = ((value >> 2) & 0xff); buffer[0x1f] = (value & 0x03); } /* color saturation #1 (Y-B ?) */ if (si->ps.card_type <= G400MAX) buffer[0x20] = 0x5F; else buffer[0x20] = 0x9C; buffer[0x21] = 0x04; /* color saturation #2 (Y-R ?) */ if (si->ps.card_type <= G400MAX) buffer[0x22] = 0x5F; else buffer[0x22] = 0x9C; buffer[0x23] = 0x01; buffer[0x24] = 0x02; /* hue: preset at 0 degrees */ buffer[0x25] = 0x00; buffer[0x26] = 0x0A; buffer[0x27] = 0x05;//sync stuff buffer[0x28] = 0x00; buffer[0x29] = 0x10;//field line-length stuff /* functional unit */ buffer[0x2a] = 0xFF; buffer[0x2b] = 0x03; /* front porch length */ buffer[0x2c] = 0x24; /* functional unit */ buffer[0x2d] = 0x0F; buffer[0x2e] = 0x78; /* functional unit */ buffer[0x2f] = 0x00; buffer[0x30] = 0x00; /* horizontal visible */ /* b10-3 */ buffer[0x31] = 0xB2; /* b2-0 in b2-0 */ buffer[0x32] = 0x04; /* upper blanking (in field lines) */ buffer[0x33] = 0x14; buffer[0x34] = 0x02;//colorphase or so stuff. buffer[0x35] = 0x00; buffer[0x36] = 0x00; buffer[0x37] = 0xA3; buffer[0x38] = 0xC8; buffer[0x39] = 0x15; buffer[0x3a] = 0x05; buffer[0x3b] = 0x3B; /* functional unit */ buffer[0x3c] = 0x3C; buffer[0x3d] = 0x00; } static void gx50_maventv_PAL_timing(gx50_maven_timing *m_timing) { /* values are given in picoseconds */ m_timing->h_total = 64000000; /* the sum of the signal duration below should match h_total! */ m_timing->h_display = 52148148; m_timing->h_sync_length = 4666667; m_timing->front_porch = 1407407; m_timing->back_porch = 5777778; /* colorburst is 'superimposed' on the above timing */ m_timing->color_burst = 2518518; /* number of lines per frame */ m_timing->v_total = 625; /* color carrier frequency in Mhz */ m_timing->chroma_subcarrier = 4.43361875; } static void gx50_maventv_NTSC_timing(gx50_maven_timing *m_timing) { /* values are given in picoseconds */ m_timing->h_total = 63555556; /* the sum of the signal duration below should match h_total! */ m_timing->h_display = 52888889; m_timing->h_sync_length = 4666667; m_timing->front_porch = 1333333; m_timing->back_porch = 4666667; /* colorburst is 'superimposed' on the above timing */ m_timing->color_burst = 2418418; /* number of lines per frame */ m_timing->v_total = 525; /* color carrier frequency in Mhz */ m_timing->chroma_subcarrier = 3.579545454; } int maventv_init(display_mode target) { uint8 val; uint8 m_result, n_result, p_result; unsigned int ht_new, ht_last_line; float calc_pclk; /* use a display_mode copy because we might tune it for TVout compatibility */ display_mode tv_target = target; /* used as buffer for TVout signal to generate */ uint8 maventv_regs[64]; /* used in G450/G550 to calculate TVout signal timing dependant on pixelclock; * <= G400MAX use fixed settings because base-clock here is the fixed crystal * frequency. */ //fixme: //if <=G400 cards with MAVEN and crystal of 14.31818Mhz exist, modify!?! //only saw 27Mhz versions of G100 and G200 that (can) hold a MAVEN upto now... gx50_maven_timing m_timing; /* preset new TVout mode */ if ((tv_target.flags & TV_BITS) == TV_PAL) { LOG(4, ("MAVENTV: PAL TVout\n")); gxx0_maventv_PAL_init(maventv_regs); gx50_maventv_PAL_timing(&m_timing); } else { LOG(4, ("MAVENTV: NTSC TVout\n")); gxx0_maventv_NTSC_init(maventv_regs); gx50_maventv_NTSC_timing(&m_timing); } /* enter mode-program mode */ if (si->ps.card_type <= G400MAX) MAVW(PGM, 0x01); else { DXIW(TVO_IDX, MGAMAV_PGM); DXIW(TVO_DATA, 0x01); } /* tune new TVout mode */ if (si->ps.card_type <= G400MAX) { /* setup TV-mode 'copy' of CRTC2, setup PLL, inputs, outputs and sync-locks */ MAVW(MONSET, 0x00); MAVW(MONEN, 0xA2); /* xmiscctrl */ //unknown regs: MAVWW(WREG_0X8E_L, 0x1EFF); MAVW(BREG_0XC6, 0x01); MAVW(LOCK, 0x01); MAVW(OUTMODE, 0x08); /* set high contrast/brightness range for TVout */ /* Note: * b4-5 have contrast/brightness function during TVout, while these bits * are used to set sync polarity in a serial fashion during monitor modes. * Setting both these bits here will 'increase' the sync polarity offset * by one! */ MAVW(LUMA, 0x78); /* We just made a sync polarity programming step for monitor modes, so: * note the offset from 'reset position' we will have now. * Note: * Not applicable for singlehead cards with a MAVEN, since it's only used * for TVout there. */ si->maven_syncpol_offset += 1; if (si->maven_syncpol_offset > 3) si->maven_syncpol_offset = 0; //unknown regs: MAVW(STABLE, 0x02); MAVW(MONEN, 0xB3); /* modify mode to center and size correctly on TV */ { int diff; float uscan_fact; bool tweak = false; /* needed for NTSC VCD mode */ if (!(tv_target.flags & TV_VIDEO)) /* Desktop modes */ { LOG(4,("MAVENTV: setting underscanning ('downscaled') desktop mode\n")); /* adapt mode to underscan correctly */ if ((tv_target.timing.h_display < 704) && ((tv_target.flags & TV_BITS) == TV_PAL)) { /* can't be higher because of scaling limitations in MAVEN! */ uscan_fact = 0.76; } else { /* optimal setting for desktop modes.. */ uscan_fact = 0.80; } /* horizontal */ tv_target.timing.h_total = (tv_target.timing.h_display / uscan_fact); /* adhere to CRTC constraints */ tv_target.timing.h_total &= ~0x0007; /* vertical */ tv_target.timing.v_total = (tv_target.timing.v_display / uscan_fact); /* now do vertical centering */ if ((tv_target.flags & TV_BITS) == TV_PAL) { diff = tv_target.timing.v_total - tv_target.timing.v_display; tv_target.timing.v_sync_start = tv_target.timing.v_display + ((diff * 7) / 20); /* sync all the way 'to the end' to prevent vertical overscanning * rubbish on top of screen due to MAVEN hardware design fault */ tv_target.timing.v_sync_end = tv_target.timing.v_total; } else { diff = tv_target.timing.v_total - tv_target.timing.v_display; tv_target.timing.v_sync_start = tv_target.timing.v_display + ((diff * 5) / 20); /* sync all the way 'to the end' to prevent vertical overscanning * rubbish on top of screen due to MAVEN hardware design fault */ tv_target.timing.v_sync_end = tv_target.timing.v_total; } } else /* Video modes */ { uint16 mode = (((tv_target.flags & TV_BITS) << (14 - 9)) | tv_target.timing.h_display); LOG(4,("MAVENTV: setting overscanning ('unscaled') video mode\n")); /* adapt standard modes to be displayed 1:1 */ switch (mode) { case ((TV_NTSC << (14 - 9)) | 640): /* NTSC VCD mode */ /* horizontal: adhere to CRTC granularity (8) */ /* Note: h_total = 704 has no PLL match! */ tv_target.timing.h_total = 696; /* because this 'low' horizontal resolution cannot be scaled up * for overscanning use (MAVEN restriction) we need to do some * tweaking to get a mode we can work with that still has the * correct aspect ratio. * This mode has just a little bit horizontal overscanning. */ tweak = true; break; case ((TV_NTSC << (14 - 9)) | 720): /* NTSC DVD mode */ /* horizontal: adhere to CRTC granularity (8) */ tv_target.timing.h_total = 784; /* MGA_TVOs need additional tweaking */ if (!si->ps.secondary_head) si->crtc_delay += 12; break; case ((TV_PAL << (14 - 9)) | 768): /* PAL VCD mode */ /* horizontal: adhere to CRTC granularity (8) */ tv_target.timing.h_total = 832; break; case ((TV_PAL << (14 - 9)) | 720): /* PAL DVD mode */ /* horizontal: adhere to CRTC granularity (8) */ tv_target.timing.h_total = 784; break; default: /* non-standard mode: just hope for he best. */ break; } /* now do vertical centering and clipping */ if ((tv_target.flags & TV_BITS) == TV_PAL) { /* defined by the PAL standard */ tv_target.timing.v_total = m_timing.v_total; /* we need to center the image on TV vertically. * note that 576 is the maximum supported resolution for the PAL standard, * this is already overscanning by approx 8-10% */ diff = 576 - tv_target.timing.v_display; /* if we cannot display the current vertical resolution fully, clip it */ if (diff < 0) { tv_target.timing.v_display = 576; diff = 0; } /* now center the image on TV */ tv_target.timing.v_sync_start = tv_target.timing.v_display + (diff / 2); /* sync all the way 'to the end' to prevent vertical overscanning * rubbish on top of screen due to MAVEN hardware design fault */ /* note: probably invisible in these Video modes */ tv_target.timing.v_sync_end = tv_target.timing.v_total; } else { /* defined by the NTSC standard */ tv_target.timing.v_total = m_timing.v_total; /* NTSC VCD mode needs to be scaled down vertically to get correct * aspect ratio... */ if (tweak) tv_target.timing.v_total += 32; /* we need to center the image on TV vertically. * note that 480 is the maximum supported resolution for the NTSC standard, * this is already overscanning by approx 8-10% */ diff = 480 - tv_target.timing.v_display; /* if we cannot display the current vertical resolution fully, clip it */ if (diff < 0) { tv_target.timing.v_display = 480; diff = 0; } /* now center the image on TV */ tv_target.timing.v_sync_start = tv_target.timing.v_display + (diff / 2); /* ...NTSC VCD mode needs to be moved up to center the tweaked mode * correcty... */ if (tweak) tv_target.timing.v_sync_start += 9; /* sync all the way 'to the end' to prevent vertical overscanning * rubbish on top of screen due to MAVEN hardware design fault */ /* note: might be visible in the NTSC VCD Video mode */ tv_target.timing.v_sync_end = tv_target.timing.v_total; } } /* finally do horizontal centering */ if ((tv_target.flags & TV_BITS) == TV_PAL) { diff = tv_target.timing.h_total - tv_target.timing.h_display; if (!si->ps.secondary_head) { tv_target.timing.h_sync_start = tv_target.timing.h_display - 16 + (diff / 2); /* keep adhering to CRTC constraints */ tv_target.timing.h_sync_start &= ~0x0007; tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 32; } else { tv_target.timing.h_sync_start = tv_target.timing.h_display - 0 + (diff / 2); /* keep adhering to CRTC constraints */ tv_target.timing.h_sync_start &= ~0x0007; tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 16; } } else { diff = tv_target.timing.h_total - tv_target.timing.h_display; tv_target.timing.h_sync_start = tv_target.timing.h_display - 16 + (diff / 2); /* ...and finally the NTSC VCD mode needs to be moved to the right to * center the tweaked mode correctly. */ if (tweak) tv_target.timing.h_sync_start -= 16; /* keep adhering to CRTC constraints */ tv_target.timing.h_sync_start &= ~0x0007; tv_target.timing.h_sync_end = tv_target.timing.h_sync_start + 16; } } /* tune crtc delay */ if ((tv_target.timing.h_display >= 1000) && (((tv_target.flags & TV_BITS) != TV_PAL))) { si->crtc_delay += 1; } else { si->crtc_delay -= 3; } /* setup video PLL */ g100_g400max_maventv_vid_pll_find( tv_target, &ht_new, &ht_last_line, &m_result, &n_result, &p_result); MAVW(PIXPLLM, m_result); MAVW(PIXPLLN, n_result); MAVW(PIXPLLP, (p_result | 0x80)); MAVW(MONSET, 0x20); MAVW(TEST, 0x10); /* htotal - 2 */ MAVWW(HTOTALL, ht_new); /* last line in field can have different length */ /* hlen - 2 */ MAVWW(LASTLINEL, ht_last_line); /* horizontal vidrst pos: 0 <= vidrst pos <= htotal - 2 */ MAVWW(HVIDRSTL, (ht_last_line - si->crtc_delay - (tv_target.timing.h_sync_end - tv_target.timing.h_sync_start))); //ORG (does the same but with limit checks but these limits should never occur!): // slen = tv_target.timing.h_sync_end - tv_target.timing.h_sync_start; // hcrt = tv_target.timing.h_total - slen - si->crtc_delay; // if (ht_last_line < tv_target.timing.h_total) hcrt += ht_last_line; // if (hcrt > tv_target.timing.h_total) hcrt -= tv_target.timing.h_total; // if (hcrt + 2 > tv_target.timing.h_total) hcrt = 0; /* or issue warning? */ // MAVWW(HVIDRSTL, hcrt); /* who knows */ MAVWW(HSYNCSTRL, 0x0004);//must be 4!! /* hblanking end: 100% */ MAVWW(HSYNCLENL, (tv_target.timing.h_total - tv_target.timing.h_sync_end)); /* vertical line count - 1 */ MAVWW(VTOTALL, (tv_target.timing.v_total - 1)); /* vertical vidrst pos */ MAVWW(VVIDRSTL, (tv_target.timing.v_total - 2)); /* something end... [A6]+1..[A8] */ MAVWW(VSYNCSTRL, 0x0001); /* vblanking end: stop vblanking */ MAVWW(VSYNCLENL, (tv_target.timing.v_sync_end - tv_target.timing.v_sync_start - 1)); //org: no visible diff: //MAVWW(VSYNCLENL, (tv_target.timing.v_total - tv_target.timing.v_sync_start - 1)); /* something start... 0..[A4]-1 */ MAVWW(VDISPLAYL, 0x0000); //std setmode (no visible difference) //MAVWW(VDISPLAYL, (tv_target.timing.v_total - 1)); /* ... */ MAVWW(WREG_0X98_L, 0x0000); /* moves picture up/down and so on... */ MAVWW(VSOMETHINGL, 0x0001); /* Fix this... 0..VTotal */ { uint32 h_display_tv; uint8 h_scale_tv; unsigned int ib_min_length; unsigned int ib_length; int index; /* calc hor scale-back factor from input to output picture (in 1.7 format) * the MAVEN has 736 pixels fixed total outputline length for TVout */ h_scale_tv = (736 << 7) / tv_target.timing.h_total;//should be PLL corrected LOG(4,("MAVENTV: horizontal scale-back factor is: %f\n", (h_scale_tv / 128.0))); /* limit values to MAVEN capabilities (scale-back factor is 0.5-1.0) */ if (h_scale_tv > 0x80) { h_scale_tv = 0x80; LOG(4,("MAVENTV: limiting horizontal scale-back factor to: %f\n", (h_scale_tv / 128.0))); } if (h_scale_tv < 0x40) { h_scale_tv = 0x40; LOG(4,("MAVENTV: limiting horizontal scale-back factor to: %f\n", (h_scale_tv / 128.0))); } /* make sure we get no round-off error artifacts on screen */ h_scale_tv--; /* calc difference in (wanted output picture width (excl. hsync_length)) and * (fixed total output line length (=768)), * based on input picture and scaling factor */ /* (MAVEN trick (part 1) to get output picture width to fit into just 8 bits) */ h_display_tv = ((768 - 1) << 7) - (((tv_target.timing.h_total - tv_target.timing.h_sync_end) /* is left margin */ + tv_target.timing.h_display - 8) * h_scale_tv); /* convert result from 25.7 to 32.0 format */ h_display_tv = h_display_tv >> 7; LOG(4,("MAVENTV: displaying output on %d picture pixels\n", ((768 - 1) - h_display_tv))); /* half result: MAVEN trick (part 2) * (258 - 768 pixels, only even number per line is possible) */ h_display_tv = h_display_tv >> 1; /* limit value to register contraints */ if (h_display_tv > 0xFF) h_display_tv = 0xFF; MAVW(HSCALETV, h_scale_tv); MAVW(HDISPLAYTV, h_display_tv); /* calculate line inputbuffer length */ /* It must be between (including): * ((input picture left margin) + (input picture hor. resolution) + 4) * AND * (input picture total line length) (PLL corrected) */ /* calculate minimal line input buffer length */ ib_min_length = ((tv_target.timing.h_total - tv_target.timing.h_sync_end) + tv_target.timing.h_display + 4); /* calculate optimal line input buffer length (so top of picture is OK too) */ /* The following formula applies: * optimal buffer length = ((((0x78 * i) - R) / hor. scaling factor) + Q) * * where (in 4.8 format!) * R Qmin Qmax * 0x0E0 0x5AE 0x5BF * 0x100 0x5CF 0x5FF * 0x180 0x653 0x67F * 0x200 0x6F8 0x6FF */ index = 1; do { ib_length = ((((((0x7800 << 7) * index) - (0x100 << 7)) / h_scale_tv) + 0x05E7) >> 8); index++; } while (ib_length < ib_min_length); LOG(4,("MAVENTV: optimal line inputbuffer length: %d\n", ib_length)); if (ib_length >= ht_new + 2) { ib_length = ib_min_length; LOG(4,("MAVENTV: limiting line inputbuffer length, setting minimal usable: %d\n", ib_length)); } MAVWW(HDISPLAYL, ib_length); } { uint16 t_scale_tv; uint32 v_display_tv; /* calc total scale-back factor from input to output picture */ { uint32 out_clocks; uint32 in_clocks; //takes care of green stripes: /* calc output clocks per frame */ out_clocks = m_timing.v_total * (ht_new + 2); /* calc input clocks per frame */ in_clocks = (tv_target.timing.v_total - 1) * (ht_new + 2) + ht_last_line + 2; /* calc total scale-back factor from input to output picture in 1.15 format */ t_scale_tv = ((((uint64)out_clocks) << 15) / in_clocks); LOG(4,("MAVENTV: total scale-back factor is: %f\n", (t_scale_tv / 32768.0))); /* min. scale-back factor is 1.0 for 1:1 output */ if (t_scale_tv > 0x8000) { t_scale_tv = 0x8000; LOG(4,("MAVENTV: limiting total scale-back factor to: %f\n", (t_scale_tv / 32768.0))); } } /*calc output picture height based on input picture and scaling factor */ //warning: v_display was 'one' lower originally! v_display_tv = ((tv_target.timing.v_sync_end - tv_target.timing.v_sync_start) /* is sync length */ + (tv_target.timing.v_total - tv_target.timing.v_sync_end) /* is upper margin */ + tv_target.timing.v_display) * t_scale_tv; /* convert result from 17.15 to 32.0 format */ v_display_tv = (v_display_tv >> 15); LOG(4,("MAVENTV: displaying output on %d picture frame-lines\n", v_display_tv)); /* half result, and compensate for internal register offset * (MAVEN trick to get it to fit into just 8 bits). * (allowed output frame height is 292 - 802 lines, only even numbers) */ v_display_tv = (v_display_tv >> 1) - 146; /* limit value to register contraints */ if (v_display_tv > 0xFF) v_display_tv = 0xFF; /* make sure we get no round-off error artifacts on screen */ t_scale_tv--; MAVWW(TSCALETVL, t_scale_tv); MAVW(VDISPLAYTV, v_display_tv); } MAVW(TEST, 0x00); /* gamma correction registers */ MAVW(GAMMA1, 0x00); MAVW(GAMMA2, 0x00); MAVW(GAMMA3, 0x00); MAVW(GAMMA4, 0x1F); MAVW(GAMMA5, 0x10); MAVW(GAMMA6, 0x10); MAVW(GAMMA7, 0x10); MAVW(GAMMA8, 0x64); /* 100 */ MAVW(GAMMA9, 0xC8); /* 200 */ /* set flickerfilter */ if (!(tv_target.flags & TV_VIDEO)) { /* Desktop modes (those are scaled): filter on to prevent artifacts */ MAVW(FFILTER, 0xa2); LOG(4,("MAVENTV: enabling flicker filter\n")); } else { /* Video modes (those are unscaled): filter off to increase sharpness */ //fixme? OFF is dependant on MAVEN version(?): MGA_TVO_B = $40, else $00. MAVW(FFILTER, 0x00); LOG(4,("MAVENTV: disabling flicker filter\n")); } /* 0x10 or anything ored with it */ //fixme? linux uses 0x14... MAVW(TEST, (MAVR(TEST) & 0x10)); /* output: SVideo/Composite */ MAVW(OUTMODE, 0x08); } else /* card_type is >= G450 */ { //fixme: setup an intermediate buffer if vertical res is different than settings below! //fixme: setup 2D or 3D engine to do screen_to_screen_scaled_filtered_blit between the buffers // during vertical retrace! if ((tv_target.flags & TV_BITS) == TV_PAL) { int diff; /* defined by the PAL standard */ tv_target.timing.v_total = m_timing.v_total; /* we need to center the image on TV vertically. * note that 576 is the maximum supported resolution for the PAL standard, * this is already overscanning by approx 8-10% */ diff = 576 - tv_target.timing.v_display; /* if we cannot display the current vertical resolution fully, clip it */ if (diff < 0) { tv_target.timing.v_display = 576; diff = 0; } /* now center the image on TV by centering the vertical sync pulse */ tv_target.timing.v_sync_start = tv_target.timing.v_display + 1 + (diff / 2); tv_target.timing.v_sync_end = tv_target.timing.v_sync_start + 1; } else { int diff; /* defined by the NTSC standard */ tv_target.timing.v_total = m_timing.v_total; /* we need to center the image on TV vertically. * note that 480 is the maximum supported resolution for the NTSC standard, * this is already overscanning by approx 8-10% */ diff = 480 - tv_target.timing.v_display; /* if we cannot display the current vertical resolution fully, clip it */ if (diff < 0) { tv_target.timing.v_display = 480; diff = 0; } /* now center the image on TV by centering the vertical sync pulse */ tv_target.timing.v_sync_start = tv_target.timing.v_display + 1 + (diff / 2); tv_target.timing.v_sync_end = tv_target.timing.v_sync_start + 1; } /* setup video PLL for G450/G550: * this can be done in the normal way because the MAVEN works in slave mode! * NOTE: must be done before programming CRTC2, or interlaced startup may fail. */ //fixme: make sure videoPLL is powered up: XPWRCTRL b1=1 { uint16 front_porch, back_porch, h_sync_length, burst_length, h_total, h_display; uint32 chromasc; uint64 pix_period; uint16 h_total_wanted, leftover; /* calculate tv_h_display in 'half pixelclocks' and adhere to MAVEN restrictions. * ('half pixelclocks' exist because the MAVEN uses them...) */ h_display = (((tv_target.timing.h_display << 1) + 3) & ~0x03); if (h_display > 2044) h_display = 2044; /* copy result to MAVEN TV mode */ maventv_regs[0x31] = (h_display >> 3); maventv_regs[0x32] = (h_display & 0x07); /* calculate needed video pixelclock in kHz. * NOTE: * The clock calculated is based on MAVEN output, so each pixelclock period * is in fact a 'half pixelclock' period compared to monitor mode use. */ tv_target.timing.pixel_clock = ((((uint64)h_display) * 1000000000) / m_timing.h_display); /* tune display_mode adhering to CRTC2 restrictions */ /* (truncate h_display to 'whole pixelclocks') */ tv_target.timing.h_display = ((h_display >> 1) & ~0x07); tv_target.timing.h_sync_start = tv_target.timing.h_display + 8; g450_g550_maven_vid_pll_find(tv_target, &calc_pclk, &m_result, &n_result, &p_result, 1); /* adjust mode to actually used pixelclock */ tv_target.timing.pixel_clock = (calc_pclk * 1000); /* program videoPLL */ DXIW(VIDPLLM, m_result); DXIW(VIDPLLN, n_result); DXIW(VIDPLLP, p_result); /* calculate videoclock 'half' period duration in picoseconds */ pix_period = (1000000000 / ((float)tv_target.timing.pixel_clock)) + 0.5; LOG(4,("MAVENTV: TV videoclock period is %d picoseconds\n", pix_period)); /* calculate number of 'half' clocks per line according to pixelclock set */ /* fixme: try to setup the modes in such a way that * (h_total_clk % 16) == 0 because of the CRTC2 restrictions: * we want to loose the truncating h_total trick below if possible! */ /* Note: * This is here so we can see the wanted and calc'd timing difference. */ h_total_wanted = ((m_timing.h_total / ((float)pix_period)) + 0.5); LOG(4,("MAVENTV: TV h_total should be %d units\n", h_total_wanted)); /* calculate chroma subcarrier value to setup: * do this as exact as possible because this signal is very sensitive.. */ chromasc = ((((uint64)0x100000000) * (m_timing.chroma_subcarrier / calc_pclk)) + 0.5); /* copy result to MAVEN TV mode */ maventv_regs[0] = ((chromasc >> 24) & 0xff); maventv_regs[1] = ((chromasc >> 16) & 0xff); maventv_regs[2] = ((chromasc >> 8) & 0xff); maventv_regs[3] = ((chromasc >> 0) & 0xff); LOG(4,("MAVENTV: TV chroma subcarrier divider set is $%08x\n", chromasc)); /* calculate front porch in 'half pixelclocks' */ /* we always round up because of the h_total truncating 'trick' below, * which works in combination with the existing difference between * h_total_clk and h_total */ //fixme: prevent this if possible! front_porch = ((m_timing.front_porch / ((float)pix_period)) + 1); /* value must be even */ front_porch &= ~0x01; /* calculate back porch in 'half pixelclocks' */ /* we always round up because of the h_total truncating 'trick' below, * which works in combination with the existing difference between * h_total_clk and h_total */ //fixme: prevent this if possible! back_porch = ((m_timing.back_porch / ((float)pix_period)) + 1); /* value must be even */ back_porch &= ~0x01; /* calculate h_sync length in 'half pixelclocks' */ /* we always round up because of the h_total truncating 'trick' below, * which works in combination with the existing difference between * h_total_clk and h_total */ //fixme: prevent this if possible! h_sync_length = ((m_timing.h_sync_length / ((float)pix_period)) + 1); /* value must be even */ h_sync_length &= ~0x01; /* calculate h_total in 'half pixelclocks' */ h_total = h_display + front_porch + back_porch + h_sync_length; LOG(4,("MAVENTV: TV front_porch is %d clocks\n", front_porch)); LOG(4,("MAVENTV: TV back_porch is %d clocks\n", back_porch)); LOG(4,("MAVENTV: TV h_sync_length is %d clocks\n", h_sync_length)); LOG(4,("MAVENTV: TV h_display is %d clocks \n", h_display)); LOG(4,("MAVENTV: TV h_total is %d clocks\n", h_total)); /* calculate color_burst length in 'half pixelclocks' */ burst_length = (((m_timing.color_burst /*- 1*/) / ((float)pix_period)) + 0.5); LOG(4,("MAVENTV: TV color_burst is %d clocks.\n", burst_length)); /* copy result to MAVEN TV mode */ maventv_regs[0x09] = burst_length; /* Calculate line length 'rest' that remains after truncating * h_total to adhere to the CRTC2 timing restrictions. */ leftover = h_total & 0x0F; /* if some 'rest' exists, we need to compensate for it... */ /* Note: * It's much better to prevent this from happening because this * 'trick' will decay TVout timing! (timing is nolonger official) */ if (leftover) { /* truncate line length to adhere to CRTC2 restrictions */ front_porch -= leftover; h_total -= leftover; /* now set line length to closest CRTC2 valid match */ if (leftover < 3) { /* 1 <= old rest <= 2: * Truncated line length is closest match. */ LOG(4,("MAVENTV: CRTC2 h_total leftover discarded (< 3)\n")); } else { if (leftover < 10) { /* 3 <= old rest <= 9: * We use the NTSC killer circuitry to get closest match. * (The 'g400_crtc2_set_timing' routine will enable it * because of the illegal h_total timing we create here.) */ front_porch += 4; h_total += 4; LOG(4,("MAVENTV: CRTC2 h_total leftover corrected via killer (> 2, < 10)\n")); } else { /* 10 <= old rest <= 15: * Set closest valid CRTC2 match. */ front_porch += 16; h_total += 16; LOG(4,("MAVENTV: CRTC2 h_total leftover corrected via increase (> 9, < 16)\n")); } } } /* (linux) fixme: maybe MAVEN has requirement 800 < h_total < 1184 */ maventv_regs[0x2C] = front_porch; maventv_regs[0x0A] = back_porch; maventv_regs[0x08] = h_sync_length; /* change h_total to represent 'whole pixelclocks' */ h_total = h_total >> 1; /* tune display_mode adhering to CRTC2 restrictions */ tv_target.timing.h_sync_end = (h_total & ~0x07) - 8; /* h_total is checked before being programmed! (NTSC killer circuitry) */ tv_target.timing.h_total = h_total; } /* output Y/C and CVBS signals (| $40 needed for SCART) */ DXIW(TVO_IDX, 0x80); DXIW(TVO_DATA, 0x03); /* select input colorspace */ //fixme?: has no effect on output picture on monitor or TV... //DXIW(TVO_IDX, 0x81); //DXIW(TVO_DATA, 0x00); /* calculate vertical sync point */ { int upper; /* set 625 lines for PAL or 525 lines for NTSC */ maventv_regs[0x17] = m_timing.v_total / 4; maventv_regs[0x18] = m_timing.v_total & 3; /* calculate upper blanking range in field lines */ upper = (m_timing.v_total - tv_target.timing.v_sync_end) >> 1; /* blank TVout above the line number calculated */ maventv_regs[0x33] = upper - 1; /* set calculated vertical sync point */ DXIW(TVO_IDX, 0x82); DXIW(TVO_DATA, (upper & 0xff)); DXIW(TVO_IDX, 0x83); DXIW(TVO_DATA, ((upper >> 8) & 0xff)); LOG(4,("MAVENTV: TV upper blanking range set is %d\n", upper)); } /* set fized horizontal sync point */ DXIW(TVO_IDX, 0x84); DXIW(TVO_DATA, 0x01); DXIW(TVO_IDX, 0x85); DXIW(TVO_DATA, 0x00); /* connect DAC1 to CON1, CRTC2/'DAC2' to CON2 (TVout mode) */ DXIW(OUTPUTCONN,0x0d); } /* program new TVout mode */ for (val = 0x00; val <= 0x3D; val++) { if (si->ps.card_type <= G400MAX) { i2c_maven_write(val, maventv_regs[val]); } else { DXIW(TVO_IDX, val); DXIW(TVO_DATA, maventv_regs[val]); } } /* leave mode-program mode */ if (si->ps.card_type <= G400MAX) MAVW(PGM, 0x00); else { DXIW(TVO_IDX, MGAMAV_PGM); DXIW(TVO_DATA, 0x00); /* Select 2.0 Volt MAVEN DAC ref. so we have enough contrast/brightness range */ DXIW(GENIOCTRL, DXIR(GENIOCTRL) | 0x40); DXIW(GENIODATA, 0x00); } /* setup CRTC timing */ if (si->ps.secondary_head) { /* on dualhead cards TVout is always used on CRTC2 */ g400_crtc2_set_timing(tv_target); } else { /* on singlehead cards TVout is always used on CRTC1 */ gx00_crtc_set_timing(tv_target); } /* start whole thing if needed */ if (si->ps.card_type <= G400MAX) MAVW(RESYNC, 0x20); return 0; }