Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2007  Luc Verhaegen <lverhaegen@novell.com>
  3.  * Copyright 2007  Matthias Hopf <mhopf@novell.com>
  4.  * Copyright 2007  Egbert Eich   <eich@novell.com>
  5.  * Copyright 2007  Advanced Micro Devices, Inc.
  6.  *
  7.  * Permission is hereby granted, free of charge, to any person obtaining a
  8.  * copy of this software and associated documentation files (the "Software"),
  9.  * to deal in the Software without restriction, including without limitation
  10.  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  11.  * and/or sell copies of the Software, and to permit persons to whom the
  12.  * Software is furnished to do so, subject to the following conditions:
  13.  *
  14.  * The above copyright notice and this permission notice shall be included in
  15.  * all copies or substantial portions of the Software.
  16.  *
  17.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
  20.  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
  21.  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
  22.  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  23.  * OTHER DEALINGS IN THE SOFTWARE.
  24.  */
  25.  
  26. #ifdef HAVE_CONFIG_H
  27. #include "config.h"
  28. #endif
  29.  
  30. #include "xf86.h"
  31.  
  32. /* for usleep */
  33. #if HAVE_XF86_ANSIC_H
  34. # include "xf86_ansic.h"
  35. #else
  36. # include <unistd.h>
  37. #endif
  38.  
  39. #include "rhd.h"
  40. #include "rhd_crtc.h"
  41. #include "rhd_pll.h"
  42. #include "rhd_regs.h"
  43. #include "rhd_atombios.h"
  44.  
  45.  
  46. #define PLL_CALIBRATE_WAIT 0x100000
  47.  
  48. /*
  49.  * Get gain, charge pump, loop filter and current bias.
  50.  * For R500, this is done in atombios by ASIC_RegistersInit
  51.  * Some data table in atom should've provided this information.
  52.  */
  53.  
  54. struct PLL_Control {
  55.     CARD16 FeedbackDivider; /* 0xFFFF/-1 is the endmarker here */
  56.     CARD32 Control;
  57. };
  58.  
  59. /* From hardcoded values. */
  60. static struct PLL_Control RV610PLLControl[] =
  61. {
  62.     { 0x0049, 0x159F8704 },
  63.     { 0x006C, 0x159B8704 },
  64.     { 0xFFFF, 0x159EC704 }
  65. };
  66.  
  67. /* Some tables are provided by atombios,
  68.  * it's just that they are hidden away deliberately and not exposed */
  69. static struct PLL_Control RV670PLLControl[] =
  70. {
  71.     { 0x004A, 0x159FC704 },
  72.     { 0x0067, 0x159BC704 },
  73.     { 0x00C4, 0x159EC704 },
  74.     { 0x00F4, 0x1593A704 },
  75.     { 0x0136, 0x1595A704 },
  76.     { 0x01A4, 0x1596A704 },
  77.     { 0x022C, 0x159CE504 },
  78.     { 0xFFFF, 0x1591E404 }
  79. };
  80.  
  81. /*
  82.  * Used by PLLElectrical() for r5xx+ and by rv620/35 code.
  83.  */
  84. static CARD32
  85. PLLControlTableRetrieve(struct PLL_Control *Table, CARD16 FeedbackDivider)
  86. {
  87.     int i;
  88.  
  89.     for (i = 0; Table[i].FeedbackDivider < 0xFFFF ; i++)
  90.         if (Table[i].FeedbackDivider >= FeedbackDivider)
  91.             break;
  92.  
  93.     return Table[i].Control;
  94. }
  95.  
  96. /*
  97.  * Not used by rv620/35 code.
  98.  */
  99. static CARD32
  100. PLLElectrical(RHDPtr rhdPtr, CARD16 FeedbackDivider)
  101. {
  102.     switch (rhdPtr->ChipSet) {
  103.     case RHD_RV515:
  104.         if (rhdPtr->PciDeviceID == 0x7146)
  105.             return 0x00120704;
  106.         else
  107.             return 0;
  108.     case RHD_RV535:
  109.         if (rhdPtr->PciDeviceID == 0x71C1)
  110.             return 0x00230704;
  111.         else
  112.             return 0;
  113.     case RHD_RS600:
  114.     case RHD_RS690:
  115.     case RHD_RS740:
  116.         /* depending on MiscInfo also 0x00120004 */
  117.         return 0x00120704;
  118.     case RHD_R600:
  119.         return 0x01130704;
  120.     case RHD_RV610:
  121.     case RHD_RV630:
  122.     case RHD_M72:
  123.     case RHD_M74:
  124.     case RHD_M76:
  125.         return PLLControlTableRetrieve(RV610PLLControl, FeedbackDivider);
  126.     case RHD_RV670:
  127.     case RHD_R680:
  128.         return PLLControlTableRetrieve(RV670PLLControl, FeedbackDivider);
  129.     default:
  130.         return 0;
  131.     }
  132. }
  133.  
  134. /*
  135.  * All R500s, RS6x0, R600, RV610 and RV630.
  136.  */
  137.  
  138. /*
  139.  *
  140.  */
  141. static void
  142. PLL1Calibrate(struct rhdPLL *PLL)
  143. {
  144.     int i;
  145.  
  146.     RHDFUNC(PLL);
  147.  
  148.     RHDRegMask(PLL, P1PLL_CNTL, 1, 0x01); /* Reset */
  149.     usleep(2);
  150.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x01); /* Set */
  151.     for (i = 0; i < PLL_CALIBRATE_WAIT; i++)
  152.         if (((RHDRegRead(PLL, P1PLL_CNTL) >> 20) & 0x03) == 0x03)
  153.             break;
  154.  
  155.     if (i == PLL_CALIBRATE_WAIT) {
  156.         if (RHDRegRead(PLL, P1PLL_CNTL) & 0x00100000) /* Calibration done? */
  157.             xf86DrvMsg(PLL->scrnIndex, X_ERROR,
  158.                        "%s: Calibration failed.\n", __func__);
  159.         if (RHDRegRead(PLL, P1PLL_CNTL) & 0x00200000) /* PLL locked? */
  160.             xf86DrvMsg(PLL->scrnIndex, X_ERROR,
  161.                        "%s: Locking failed.\n", __func__);
  162.     } else
  163.         RHDDebug(PLL->scrnIndex, "%s: lock in %d loops\n", __func__, i);
  164. }
  165.  
  166. /*
  167.  *
  168.  */
  169. static void
  170. PLL2Calibrate(struct rhdPLL *PLL)
  171. {
  172.     int i;
  173.  
  174.     RHDFUNC(PLL);
  175.  
  176.     RHDRegMask(PLL, P2PLL_CNTL, 1, 0x01); /* Reset */
  177.     usleep(2);
  178.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x01); /* Set */
  179.  
  180.     for (i = 0; i < PLL_CALIBRATE_WAIT; i++)
  181.         if (((RHDRegRead(PLL, P2PLL_CNTL) >> 20) & 0x03) == 0x03)
  182.             break;
  183.  
  184.     if (i == PLL_CALIBRATE_WAIT) {
  185.         if (RHDRegRead(PLL, P2PLL_CNTL) & 0x00100000) /* Calibration done? */
  186.             xf86DrvMsg(PLL->scrnIndex, X_ERROR,
  187.                        "%s: Calibration failed.\n", __func__);
  188.         if (RHDRegRead(PLL, P2PLL_CNTL) & 0x00200000) /* PLL locked? */
  189.             xf86DrvMsg(PLL->scrnIndex, X_ERROR,
  190.                        "%s: Locking failed.\n", __func__);
  191.     } else
  192.         RHDDebug(PLL->scrnIndex, "%s: lock in %d loops\n", __func__, i);
  193. }
  194.  
  195. /*
  196.  *
  197.  */
  198. static void
  199. R500PLL1Power(struct rhdPLL *PLL, int Power)
  200. {
  201.     RHDFUNC(PLL);
  202.  
  203.     switch (Power) {
  204.     case RHD_POWER_ON:
  205.         RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */
  206.         usleep(2);
  207.  
  208.         PLL1Calibrate(PLL);
  209.  
  210.         return;
  211.     case RHD_POWER_RESET:
  212.         RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */
  213.         usleep(2);
  214.  
  215.         RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */
  216.         usleep(2);
  217.  
  218.         return;
  219.     case RHD_POWER_SHUTDOWN:
  220.     default:
  221.         RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */
  222.         usleep(2);
  223.  
  224.         RHDRegMask(PLL, P1PLL_CNTL, 0x02, 0x02); /* Power down */
  225.         usleep(200);
  226.  
  227.         return;
  228.     }
  229. }
  230.  
  231. /*
  232.  *
  233.  */
  234. static void
  235. R500PLL2Power(struct rhdPLL *PLL, int Power)
  236. {
  237.     RHDFUNC(PLL);
  238.  
  239.     switch (Power) {
  240.     case RHD_POWER_ON:
  241.         RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */
  242.         usleep(2);
  243.  
  244.         PLL2Calibrate(PLL);
  245.  
  246.         return;
  247.     case RHD_POWER_RESET:
  248.         RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */
  249.         usleep(2);
  250.  
  251.         RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */
  252.         usleep(2);
  253.  
  254.         return;
  255.     case RHD_POWER_SHUTDOWN:
  256.     default:
  257.         RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */
  258.         usleep(2);
  259.  
  260.         RHDRegMask(PLL, P2PLL_CNTL, 0x02, 0x02); /* Power down */
  261.         usleep(200);
  262.  
  263.         return;
  264.     }
  265. }
  266.  
  267. /*
  268.  *
  269.  */
  270. static void
  271. R500PLL1SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv,
  272.                CARD32 Control)
  273. {
  274.     RHDFUNC(PLL);
  275.     RHDRegWrite(PLL, EXT1_PPLL_REF_DIV_SRC, 0x01); /* XTAL */
  276.     RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x00); /* source = reference */
  277.  
  278.     RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 0x01); /* lock */
  279.  
  280.     RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, RefDiv);
  281.     RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, FBDiv);
  282.     RHDRegWrite(PLL, EXT1_PPLL_POST_DIV, PostDiv);
  283.     RHDRegWrite(PLL, EXT1_PPLL_CNTL, Control);
  284.  
  285.     RHDRegMask(PLL, EXT1_PPLL_UPDATE_CNTL, 0x00010000, 0x00010000); /* no autoreset */
  286.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x04); /* don't bypass calibration */
  287.  
  288.     /* We need to reset the anti glitch logic */
  289.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00000002); /* power up */
  290.  
  291.     /* reset anti glitch logic */
  292.     RHDRegMask(PLL, P1PLL_CNTL, 0x00002000, 0x00002000);
  293.     usleep(2);
  294.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00002000);
  295.  
  296.     /* powerdown and reset */
  297.     RHDRegMask(PLL, P1PLL_CNTL, 0x00000003, 0x00000003);
  298.     usleep(2);
  299.  
  300.     RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 0); /* unlock */
  301.     RHDRegMask(PLL, EXT1_PPLL_UPDATE_CNTL, 0, 0x01); /* we're done updating! */
  302.  
  303.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */
  304.     usleep(2);
  305.  
  306.     PLL1Calibrate(PLL);
  307.  
  308.     RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x01); /* source is PLL itself */
  309. }
  310.  
  311. /*
  312.  *
  313.  */
  314. static void
  315. R500PLL2SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv,
  316.                CARD32 Control)
  317. {
  318.     RHDRegWrite(PLL, EXT2_PPLL_REF_DIV_SRC, 0x01); /* XTAL */
  319.     RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x00); /* source = reference */
  320.  
  321.     RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 0x01); /* lock */
  322.  
  323.     RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, RefDiv);
  324.     RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, FBDiv);
  325.     RHDRegWrite(PLL, EXT2_PPLL_POST_DIV, PostDiv);
  326.     RHDRegWrite(PLL, EXT2_PPLL_CNTL, Control);
  327.  
  328.     RHDRegMask(PLL, EXT2_PPLL_UPDATE_CNTL, 0x00010000, 0x00010000); /* no autoreset */
  329.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x04); /* don't bypass calibration */
  330.  
  331.     /* We need to reset the anti glitch logic */
  332.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00000002); /* power up */
  333.  
  334.     /* reset anti glitch logic */
  335.     RHDRegMask(PLL, P2PLL_CNTL, 0x00002000, 0x00002000);
  336.     usleep(2);
  337.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00002000);
  338.  
  339.     /* powerdown and reset */
  340.     RHDRegMask(PLL, P2PLL_CNTL, 0x00000003, 0x00000003);
  341.     usleep(2);
  342.  
  343.     RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 0); /* unlock */
  344.     RHDRegMask(PLL, EXT2_PPLL_UPDATE_CNTL, 0, 0x01); /* we're done updating! */
  345.  
  346.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */
  347.     usleep(2);
  348.  
  349.     PLL2Calibrate(PLL);
  350.  
  351.     RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x01); /* source is PLL itself */
  352. }
  353.  
  354. /*
  355.  * The CRTC ownership of each PLL is multiplexed on the PLL blocks, and the
  356.  * ownership can only be switched when the currently referenced PLL is active.
  357.  * This makes handling a slight bit more complex.
  358.  */
  359. static void
  360. R500PLLCRTCGrab(struct rhdPLL *PLL, Bool Crtc2)
  361. {
  362.     CARD32 Stored;
  363.     Bool PLL2IsCurrent;
  364.  
  365.     if (!Crtc2) {
  366.         PLL2IsCurrent = RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000;
  367.  
  368.         if (PLL->Id == PLL_ID_PLL1)
  369.             RHDRegMask(PLL, PCLK_CRTC1_CNTL, 0, 0x00010000);
  370.         else
  371.             RHDRegMask(PLL, PCLK_CRTC1_CNTL, 0x00010000, 0x00010000);
  372.     } else {
  373.         PLL2IsCurrent = RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000;
  374.  
  375.         if (PLL->Id == PLL_ID_PLL1)
  376.             RHDRegMask(PLL, PCLK_CRTC2_CNTL, 0, 0x00010000);
  377.         else
  378.             RHDRegMask(PLL, PCLK_CRTC2_CNTL, 0x00010000, 0x00010000);
  379.     }
  380.  
  381.     /* if the current pll is not active, then poke it just enough to flip
  382.      * owners */
  383.     if (!PLL2IsCurrent) {
  384.         Stored = RHDRegRead(PLL, P1PLL_CNTL);
  385.  
  386.         if (Stored & 0x03) {
  387.             RHDRegMask(PLL, P1PLL_CNTL, 0, 0x03);
  388.             usleep(10);
  389.             RHDRegMask(PLL, P1PLL_CNTL, Stored, 0x03);
  390.         }
  391.     } else {
  392.         Stored = RHDRegRead(PLL, P2PLL_CNTL);
  393.  
  394.         if (Stored & 0x03) {
  395.             RHDRegMask(PLL, P2PLL_CNTL, 0, 0x03);
  396.             usleep(10);
  397.             RHDRegMask(PLL, P2PLL_CNTL, Stored, 0x03);
  398.         }
  399.     }
  400. }
  401.  
  402. /*
  403.  *
  404.  */
  405. static void
  406. R500PLL1Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider,
  407.             CARD16 FeedbackDivider, CARD8 PostDivider)
  408. {
  409.     RHDPtr rhdPtr = RHDPTRI(PLL);
  410.     CARD32 RefDiv, FBDiv, PostDiv, Control;
  411.  
  412.     RHDFUNC(PLL);
  413.  
  414.     RefDiv = ReferenceDivider;
  415.  
  416.     FBDiv = FeedbackDivider << 16;
  417.  
  418.     if (rhdPtr->ChipSet > RHD_R600) { /* set up Feedbackdivider slip */
  419.         if (FeedbackDivider <= 0x24)
  420.             FBDiv |= 0x00000030;
  421.         else if (FeedbackDivider <= 0x3F)
  422.             FBDiv |= 0x00000020;
  423.     } else if (rhdPtr->ChipSet >= RHD_RS600) /* RS600, RS690, R600 */
  424.         FBDiv |= 0x00000030;
  425.     else
  426.         FBDiv |= RHDRegRead(PLL, EXT1_PPLL_FB_DIV) & 0x00000030;
  427.  
  428.     PostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV) & ~0x0000007F;
  429.     PostDiv |= PostDivider & 0x0000007F;
  430.  
  431.     Control = PLLElectrical(rhdPtr, FeedbackDivider);
  432.     if (!Control)
  433.         Control = RHDRegRead(PLL, EXT1_PPLL_CNTL);
  434.  
  435.     /* Disable Spread Spectrum */
  436.     RHDRegMask(PLL, P1PLL_INT_SS_CNTL, 0, 0x00000001);
  437.  
  438.     R500PLL1SetLow(PLL, RefDiv, FBDiv, PostDiv, Control);
  439.  
  440.     if (rhdPtr->Crtc[0]->PLL == PLL)
  441.         R500PLLCRTCGrab(PLL, FALSE);
  442.     if (rhdPtr->Crtc[1]->PLL == PLL)
  443.         R500PLLCRTCGrab(PLL, TRUE);
  444. }
  445.  
  446. /*
  447.  *
  448.  */
  449. static void
  450. R500PLL2Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider,
  451.             CARD16 FeedbackDivider, CARD8 PostDivider)
  452. {
  453.     RHDPtr rhdPtr = RHDPTRI(PLL);
  454.     CARD32 RefDiv, FBDiv, PostDiv, Control;
  455.  
  456.     RHDFUNC(PLL);
  457.  
  458.     RefDiv = ReferenceDivider;
  459.  
  460.     FBDiv = FeedbackDivider << 16;
  461.  
  462.     if (rhdPtr->ChipSet > RHD_R600) { /* set up Feedbackdivider slip */
  463.         if (FeedbackDivider <= 0x24)
  464.             FBDiv |= 0x00000030;
  465.         else if (FeedbackDivider <= 0x3F)
  466.             FBDiv |= 0x00000020;
  467.     } else if (rhdPtr->ChipSet >= RHD_RS600) /* RS600, RS690, R600 */
  468.         FBDiv |= 0x00000030;
  469.     else
  470.         FBDiv |= RHDRegRead(PLL, EXT2_PPLL_FB_DIV) & 0x00000030;
  471.  
  472.     PostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV) & ~0x0000007F;
  473.     PostDiv |= PostDivider & 0x0000007F;
  474.  
  475.     Control = PLLElectrical(rhdPtr, FeedbackDivider);
  476.     if (!Control)
  477.         Control = RHDRegRead(PLL, EXT2_PPLL_CNTL);
  478.  
  479.     /* Disable Spread Spectrum */
  480.     RHDRegMask(PLL, P2PLL_INT_SS_CNTL, 0, 0x00000001);
  481.  
  482.     R500PLL2SetLow(PLL, RefDiv, FBDiv, PostDiv, Control);
  483.  
  484.     if (rhdPtr->Crtc[0]->PLL == PLL)
  485.         R500PLLCRTCGrab(PLL, FALSE);
  486.     if (rhdPtr->Crtc[1]->PLL == PLL)
  487.         R500PLLCRTCGrab(PLL, TRUE);
  488. }
  489.  
  490. /*
  491.  *
  492.  */
  493. static void
  494. R500PLL1Save(struct rhdPLL *PLL)
  495. {
  496.     RHDFUNC(PLL);
  497.  
  498.     PLL->StoreActive = !(RHDRegRead(PLL, P1PLL_CNTL) & 0x03);
  499.     PLL->StoreRefDiv = RHDRegRead(PLL, EXT1_PPLL_REF_DIV);
  500.     PLL->StoreFBDiv = RHDRegRead(PLL, EXT1_PPLL_FB_DIV);
  501.     PLL->StorePostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV);
  502.     PLL->StoreControl = RHDRegRead(PLL, EXT1_PPLL_CNTL);
  503.     PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P1PLL_INT_SS_CNTL);
  504.     PLL->StoreCrtc1Owner = !(RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000);
  505.     PLL->StoreCrtc2Owner = !(RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000);
  506.  
  507.     PLL->Stored = TRUE;
  508. }
  509.  
  510. /*
  511.  *
  512.  */
  513. static void
  514. R500PLL2Save(struct rhdPLL *PLL)
  515. {
  516.     RHDFUNC(PLL);
  517.  
  518.     PLL->StoreActive = !(RHDRegRead(PLL, P2PLL_CNTL) & 0x03);
  519.     PLL->StoreRefDiv = RHDRegRead(PLL, EXT2_PPLL_REF_DIV);
  520.     PLL->StoreFBDiv = RHDRegRead(PLL, EXT2_PPLL_FB_DIV);
  521.     PLL->StorePostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV);
  522.     PLL->StoreControl = RHDRegRead(PLL, EXT2_PPLL_CNTL);
  523.     PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P2PLL_INT_SS_CNTL);
  524.     PLL->StoreCrtc1Owner = RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000;
  525.     PLL->StoreCrtc2Owner = RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000;
  526.  
  527.     PLL->Stored = TRUE;
  528. }
  529.  
  530. /*
  531.  *
  532.  */
  533. static void
  534. R500PLL1Restore(struct rhdPLL *PLL)
  535. {
  536.     RHDFUNC(PLL);
  537.  
  538.     if (!PLL->Stored) {
  539.         xf86DrvMsg(PLL->scrnIndex, X_ERROR, "%s: %s: trying to restore "
  540.                    "uninitialized values.\n", __func__, PLL->Name);
  541.         return;
  542.     }
  543.  
  544.     if (PLL->StoreActive) {
  545.         R500PLL1SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv,
  546.                        PLL->StorePostDiv, PLL->StoreControl);
  547.  
  548.         /* HotFix: always keep spread spectrum disabled on restore */
  549.         if (0 && RHDPTRI(PLL)->ChipSet != RHD_M54)
  550.             RHDRegMask(PLL, P1PLL_INT_SS_CNTL,
  551.                        PLL->StoreSpreadSpectrum, 0x00000001);
  552.     } else {
  553.         PLL->Power(PLL, RHD_POWER_SHUTDOWN);
  554.  
  555.         /* lame attempt at at least restoring the old values */
  556.         RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, PLL->StoreRefDiv);
  557.         RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, PLL->StoreFBDiv);
  558.         RHDRegWrite(PLL, EXT1_PPLL_POST_DIV, PLL->StorePostDiv);
  559.         RHDRegWrite(PLL, EXT1_PPLL_CNTL, PLL->StoreControl);
  560.         RHDRegWrite(PLL, P1PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum);
  561.     }
  562.  
  563.     if (PLL->StoreCrtc1Owner)
  564.         R500PLLCRTCGrab(PLL, FALSE);
  565.     if (PLL->StoreCrtc2Owner)
  566.         R500PLLCRTCGrab(PLL, TRUE);
  567. }
  568.  
  569. /*
  570.  *
  571.  */
  572. static void
  573. R500PLL2Restore(struct rhdPLL *PLL)
  574. {
  575.     RHDFUNC(PLL);
  576.  
  577.     if (!PLL->Stored) {
  578.         xf86DrvMsg(PLL->scrnIndex, X_ERROR, "%s: %s: trying to restore "
  579.                    "uninitialized values.\n", __func__, PLL->Name);
  580.         return;
  581.     }
  582.  
  583.     if (PLL->StoreActive) {
  584.         R500PLL2SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv,
  585.                        PLL->StorePostDiv, PLL->StoreControl);
  586.  
  587.         if (RHDPTRI(PLL)->ChipSet != RHD_M54)
  588.             RHDRegMask(PLL, P2PLL_INT_SS_CNTL,
  589.                        PLL->StoreSpreadSpectrum, 0x00000001);
  590.     } else {
  591.         PLL->Power(PLL, RHD_POWER_SHUTDOWN);
  592.  
  593.         /* lame attempt at at least restoring the old values */
  594.         RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, PLL->StoreRefDiv);
  595.         RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, PLL->StoreFBDiv);
  596.         RHDRegWrite(PLL, EXT2_PPLL_POST_DIV, PLL->StorePostDiv);
  597.         RHDRegWrite(PLL, EXT2_PPLL_CNTL, PLL->StoreControl);
  598.         RHDRegWrite(PLL, P2PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum);
  599.     }
  600.  
  601.     if (PLL->StoreCrtc1Owner)
  602.         R500PLLCRTCGrab(PLL, FALSE);
  603.     if (PLL->StoreCrtc2Owner)
  604.         R500PLLCRTCGrab(PLL, TRUE);
  605. }
  606.  
  607. /*
  608.  * RV620 and up
  609.  */
  610.  
  611. /*
  612.  *
  613.  */
  614. #define RV620_DCCGCLK_RESET   0
  615. #define RV620_DCCGCLK_GRAB    1
  616. #define RV620_DCCGCLK_RELEASE 2
  617.  
  618. /*
  619.  * I still have no idea what DCCG stands for and why it needs to hook off some
  620.  * pixelclock...
  621.  */
  622. static void
  623. RV620DCCGCLKSet(struct rhdPLL *PLL, int set)
  624. {
  625.     CARD32 tmp;
  626.  
  627.     RHDFUNC(PLL);
  628.  
  629.     switch(set) {
  630.     case RV620_DCCGCLK_GRAB:
  631.         if (PLL->Id == PLL_ID_PLL1)
  632.             RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003);
  633.         else if (PLL->Id == PLL_ID_PLL2)
  634.             RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003);
  635.         else
  636.             RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
  637.         break;
  638.     case RV620_DCCGCLK_RELEASE:
  639.         tmp = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03;
  640.  
  641.         if ((PLL->Id == PLL_ID_PLL1) && (tmp == 0)) {
  642.             /* set to other PLL or external */
  643.             tmp = RHDRegRead(PLL, P2PLL_CNTL);
  644.             if (!(tmp & 0x03) && /* powered and not in reset */
  645.                 ((tmp & 0x00300000) == 0x00300000)) /* calibrated and locked */
  646.                 RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 1, 0x00000003);
  647.             else
  648.                 RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
  649.         } else if ((PLL->Id == PLL_ID_PLL2) && (tmp == 1)) {
  650.             /* set to other PLL or external */
  651.             tmp = RHDRegRead(PLL, P1PLL_CNTL);
  652.             if (!(tmp & 0x03) && /* powered and not in reset */
  653.                 ((tmp & 0x00300000) == 0x00300000)) /* calibrated and locked */
  654.                 RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0, 0x00000003);
  655.             else
  656.                 RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
  657.  
  658.         } /* no other action needs to be taken */
  659.         break;
  660.     case RV620_DCCGCLK_RESET:
  661.         tmp = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03;
  662.  
  663.         if (((PLL->Id == PLL_ID_PLL1) && (tmp == 0)) ||
  664.             ((PLL->Id == PLL_ID_PLL2) && (tmp == 1)))
  665.             RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 3, 0x00000003);
  666.         break;
  667.     default:
  668.         break;
  669.     }
  670. }
  671.  
  672. /*
  673.  *
  674.  */
  675. static Bool
  676. RV620DCCGCLKAvailable(struct rhdPLL *PLL)
  677. {
  678.     CARD32 Dccg = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL) & 0x03;
  679.  
  680.     RHDFUNC(PLL);
  681.  
  682.     if (Dccg & 0x02)
  683.         return TRUE;
  684.  
  685.     if ((PLL->Id == PLL_ID_PLL1) && (Dccg == 0))
  686.         return TRUE;
  687.     if ((PLL->Id == PLL_ID_PLL2) && (Dccg == 1))
  688.         return TRUE;
  689.  
  690.     return FALSE;
  691. }
  692.  
  693. /*
  694.  *
  695.  */
  696. static void
  697. RV620PLL1Power(struct rhdPLL *PLL, int Power)
  698. {
  699.     RHDFUNC(PLL);
  700.  
  701.     switch (Power) {
  702.     case RHD_POWER_ON:
  703.     {
  704.         Bool HasDccg = RV620DCCGCLKAvailable(PLL);
  705.  
  706.         if (HasDccg)
  707.             RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET);
  708.  
  709.         RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */
  710.         usleep(2);
  711.  
  712.         PLL1Calibrate(PLL);
  713.  
  714.         if (HasDccg)
  715.             RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB);
  716.         return;
  717.     }
  718.     case RHD_POWER_RESET:
  719.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE);
  720.  
  721.         RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */
  722.         usleep(2);
  723.  
  724.         RHDRegMask(PLL, P1PLL_CNTL, 0, 0x02); /* Powah */
  725.         usleep(2);
  726.  
  727.         return;
  728.     case RHD_POWER_SHUTDOWN:
  729.     default:
  730.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE);
  731.  
  732.         RHDRegMask(PLL, P1PLL_CNTL, 0x01, 0x01); /* Reset */
  733.         usleep(2);
  734.  
  735.         RHDRegMask(PLL, P1PLL_CNTL, 0x02, 0x02); /* Power down */
  736.         usleep(200);
  737.  
  738.         return;
  739.     }
  740. }
  741.  
  742. /*
  743.  *
  744.  */
  745. static void
  746. RV620PLL2Power(struct rhdPLL *PLL, int Power)
  747. {
  748.     RHDFUNC(PLL);
  749.  
  750.     switch (Power) {
  751.     case RHD_POWER_ON:
  752.     {
  753.         Bool HasDccg = RV620DCCGCLKAvailable(PLL);
  754.  
  755.         if (HasDccg)
  756.             RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET);
  757.  
  758.         RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */
  759.         usleep(2);
  760.  
  761.         PLL2Calibrate(PLL);
  762.  
  763.         if (HasDccg)
  764.             RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB);
  765.         return;
  766.     }
  767.     case RHD_POWER_RESET:
  768.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE);
  769.  
  770.         RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */
  771.         usleep(2);
  772.  
  773.         RHDRegMask(PLL, P2PLL_CNTL, 0, 0x02); /* Powah */
  774.         usleep(2);
  775.  
  776.         return;
  777.     case RHD_POWER_SHUTDOWN:
  778.     default:
  779.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RELEASE);
  780.  
  781.         RHDRegMask(PLL, P2PLL_CNTL, 0x01, 0x01); /* Reset */
  782.         usleep(2);
  783.  
  784.         RHDRegMask(PLL, P2PLL_CNTL, 0x02, 0x02); /* Power down */
  785.         usleep(200);
  786.  
  787.         return;
  788.     }
  789. }
  790.  
  791. /*
  792.  *
  793.  */
  794. static void
  795. RV620PLL1SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv,
  796.                 CARD8 ScalerDiv, CARD8 SymPostDiv, CARD32 Control)
  797. {
  798.     RHDFUNC(PLL);
  799.  
  800.     /* switch to external */
  801.     RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0);
  802.     RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, 0x00000200, 0x00000300);
  803.     RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, 0, 0x00000100);
  804.  
  805.     RHDRegMask(PLL, P1PLL_CNTL, 0x00000001, 0x00000001); /* reset */
  806.     usleep(2);
  807.     RHDRegMask(PLL, P1PLL_CNTL, 0x00000002, 0x00000002); /* power down */
  808.     usleep(10);
  809.     RHDRegMask(PLL, P1PLL_CNTL, 0x00002000, 0x00002000); /* reset anti-glitch */
  810.  
  811.     RHDRegWrite(PLL, EXT1_PPLL_CNTL, Control);
  812.  
  813.     RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, ScalerDiv, 0x0000003F);
  814.  
  815.     RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 1); /* lock */
  816.  
  817.     RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x00000001);
  818.  
  819.     RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, RefDiv);
  820.     RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, FBDiv);
  821.     RHDRegMask(PLL, EXT1_PPLL_POST_DIV, PostDiv, 0x0000007F);
  822.     RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, SymPostDiv, 0x0000007F);
  823.  
  824.     usleep(10);
  825.     RHDRegWrite(PLL, EXT1_PPLL_UPDATE_LOCK, 0); /* unlock */
  826.  
  827.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00000002); /* power up */
  828.     usleep(10);
  829.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00002000); /* undo reset anti-glitch */
  830.  
  831.     PLL1Calibrate(PLL);
  832.  
  833.     /* switch back to the pll */
  834.     RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, 0, 0x00000300);
  835.     RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, 0x00000100, 0x00000100);
  836.     RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, 0x00000001);
  837.  
  838.     RHDRegMask(PLL, P1PLL_CNTL, 0, 0x80000000); /* new and undocumented */
  839. }
  840.  
  841. /*
  842.  *
  843.  */
  844. static void
  845. RV620PLL2SetLow(struct rhdPLL *PLL, CARD32 RefDiv, CARD32 FBDiv, CARD32 PostDiv,
  846.                 CARD8 ScalerDiv, CARD8 SymPostDiv, CARD32 Control)
  847. {
  848.     RHDFUNC(PLL);
  849.  
  850.     /* switch to external */
  851.     RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0);
  852.     RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, 0x00000200, 0x00000300);
  853.     RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, 0, 0x00000100);
  854.  
  855.     RHDRegMask(PLL, P2PLL_CNTL, 0x00000001, 0x00000001); /* reset */
  856.     usleep(2);
  857.     RHDRegMask(PLL, P2PLL_CNTL, 0x00000002, 0x00000002); /* power down */
  858.     usleep(10);
  859.     RHDRegMask(PLL, P2PLL_CNTL, 0x00002000, 0x00002000); /* reset anti-glitch */
  860.  
  861.     RHDRegWrite(PLL, EXT2_PPLL_CNTL, Control);
  862.  
  863.     RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, ScalerDiv, 0x0000003F);
  864.  
  865.     RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 1); /* lock */
  866.  
  867.     RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x00000001);
  868.  
  869.     RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, RefDiv);
  870.     RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, FBDiv);
  871.     RHDRegMask(PLL, EXT2_PPLL_POST_DIV, PostDiv, 0x0000007F);
  872.     RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, SymPostDiv, 0x0000007F);
  873.  
  874.     usleep(10);
  875.     RHDRegWrite(PLL, EXT2_PPLL_UPDATE_LOCK, 0); /* unlock */
  876.  
  877.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00000002); /* power up */
  878.     usleep(10);
  879.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00002000); /* undo reset anti-glitch */
  880.  
  881.     PLL2Calibrate(PLL);
  882.  
  883.     /* switch back to the pll */
  884.     RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, 0, 0x00000300);
  885.     RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, 0x00000100, 0x00000100);
  886.     RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, 0x00000001);
  887.  
  888.     RHDRegMask(PLL, P2PLL_CNTL, 0, 0x80000000); /* new and undocumented */
  889. }
  890.  
  891. /*
  892.  *
  893.  */
  894. static void
  895. RV620PLL1Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider,
  896.              CARD16 FeedbackDivider, CARD8 PostDivider)
  897. {
  898.     RHDPtr rhdPtr = RHDPTRI(PLL);
  899.     Bool HasDccg = RV620DCCGCLKAvailable(PLL);
  900.     CARD32 RefDiv, FBDiv, PostDiv, Control;
  901.     CARD8 ScalerDiv, SymPostDiv;
  902.  
  903.     RHDFUNC(PLL);
  904.  
  905.     if (HasDccg)
  906.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET);
  907.  
  908.     /* Disable Spread Spectrum */
  909.     RHDRegMask(PLL, P1PLL_INT_SS_CNTL, 0, 0x00000001);
  910.  
  911.     RefDiv = ReferenceDivider;
  912.  
  913.     FBDiv = RHDRegRead(PLL, EXT1_PPLL_FB_DIV) & ~0x07FF003F;
  914.     FBDiv |= ((FeedbackDivider << 16) | 0x0030) & 0x07FF003F;
  915.  
  916.     PostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV) & ~0x0000007F;
  917.     PostDiv |= PostDivider & 0x0000007F;
  918.  
  919.     /* introduce flags for this, like on unichrome */
  920.     ScalerDiv = 2; /* scaler post divider, 4 for UPDP */
  921.  
  922.     SymPostDiv = PostDivider & 0x0000007F;
  923.  
  924.     Control = PLLControlTableRetrieve(RV670PLLControl, FeedbackDivider);
  925.  
  926.     RV620PLL1SetLow(PLL, RefDiv, FBDiv, PostDiv, ScalerDiv, SymPostDiv,
  927.                     Control);
  928.  
  929.     if (rhdPtr->Crtc[0]->PLL == PLL)
  930.         R500PLLCRTCGrab(PLL, FALSE);
  931.     if (rhdPtr->Crtc[1]->PLL == PLL)
  932.         R500PLLCRTCGrab(PLL, TRUE);
  933.  
  934.     if (HasDccg)
  935.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB);
  936. }
  937.  
  938. /*
  939.  *
  940.  */
  941. static void
  942. RV620PLL2Set(struct rhdPLL *PLL, int PixelClock, CARD16 ReferenceDivider,
  943.              CARD16 FeedbackDivider, CARD8 PostDivider)
  944. {
  945.     RHDPtr rhdPtr = RHDPTRI(PLL);
  946.     Bool HasDccg = RV620DCCGCLKAvailable(PLL);
  947.     CARD32 RefDiv, FBDiv, PostDiv, Control;
  948.     CARD8 ScalerDiv, SymPostDiv;
  949.  
  950.     RHDFUNC(PLL);
  951.  
  952.     if (HasDccg)
  953.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_RESET);
  954.  
  955.     /* Disable Spread Spectrum */
  956.     RHDRegMask(PLL, P2PLL_INT_SS_CNTL, 0, 0x00000001);
  957.  
  958.     RefDiv = ReferenceDivider;
  959.  
  960.     FBDiv = RHDRegRead(PLL, EXT2_PPLL_FB_DIV) & ~0x07FF003F;
  961.     FBDiv |= ((FeedbackDivider << 16) | 0x0030) & 0x07FF003F;
  962.  
  963.     PostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV) & ~0x0000007F;
  964.     PostDiv |= PostDivider & 0x0000007F;
  965.  
  966.     /* introduce flags for this, like on unichrome */
  967.     ScalerDiv = 2; /* scaler post divider, 4 for UPDP */
  968.  
  969.     SymPostDiv = PostDivider & 0x0000007F;
  970.  
  971.     Control = PLLControlTableRetrieve(RV670PLLControl, FeedbackDivider);
  972.  
  973.     RV620PLL2SetLow(PLL, RefDiv, FBDiv, PostDiv, ScalerDiv, SymPostDiv,
  974.                     Control);
  975.  
  976.     if (rhdPtr->Crtc[0]->PLL == PLL)
  977.         R500PLLCRTCGrab(PLL, FALSE);
  978.     if (rhdPtr->Crtc[1]->PLL == PLL)
  979.         R500PLLCRTCGrab(PLL, TRUE);
  980.  
  981.     if (HasDccg)
  982.         RV620DCCGCLKSet(PLL, RV620_DCCGCLK_GRAB);
  983. }
  984.  
  985. /*
  986.  *
  987.  */
  988. static void
  989. RV620PLL1Save(struct rhdPLL *PLL)
  990. {
  991.     RHDFUNC(PLL);
  992.  
  993.     PLL->StoreActive = !(RHDRegRead(PLL, P1PLL_CNTL) & 0x03);
  994.     PLL->StoreRefDiv = RHDRegRead(PLL, EXT1_PPLL_REF_DIV);
  995.     PLL->StoreFBDiv = RHDRegRead(PLL, EXT1_PPLL_FB_DIV);
  996.     PLL->StorePostDiv = RHDRegRead(PLL, EXT1_PPLL_POST_DIV);
  997.     PLL->StorePostDivSrc = RHDRegRead(PLL, EXT1_PPLL_POST_DIV_SRC);
  998.     PLL->StoreControl = RHDRegRead(PLL, EXT1_PPLL_CNTL);
  999.     PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P1PLL_INT_SS_CNTL);
  1000.  
  1001.     PLL->StoreGlitchReset = RHDRegRead(PLL, P1PLL_CNTL) & 0x00002000;
  1002.  
  1003.     PLL->StoreScalerPostDiv = RHDRegRead(PLL, P1PLL_DISP_CLK_CNTL) & 0x003F;
  1004.     PLL->StoreSymPostDiv = RHDRegRead(PLL, EXT1_SYM_PPLL_POST_DIV) & 0x007F;
  1005.  
  1006.     PLL->StoreCrtc1Owner = !(RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000);
  1007.     PLL->StoreCrtc2Owner = !(RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000);
  1008.  
  1009.     PLL->StoreDCCGCLKOwner = RV620DCCGCLKAvailable(PLL);
  1010.     if (PLL->StoreDCCGCLKOwner)
  1011.         PLL->StoreDCCGCLK = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL);
  1012.     else
  1013.         PLL->StoreDCCGCLK = 0;
  1014.  
  1015.     PLL->Stored = TRUE;
  1016. }
  1017.  
  1018. /*
  1019.  *
  1020.  */
  1021. static void
  1022. RV620PLL2Save(struct rhdPLL *PLL)
  1023. {
  1024.     RHDFUNC(PLL);
  1025.  
  1026.     PLL->StoreActive = !(RHDRegRead(PLL, P2PLL_CNTL) & 0x03);
  1027.     PLL->StoreRefDiv = RHDRegRead(PLL, EXT2_PPLL_REF_DIV);
  1028.     PLL->StoreFBDiv = RHDRegRead(PLL, EXT2_PPLL_FB_DIV);
  1029.     PLL->StorePostDiv = RHDRegRead(PLL, EXT2_PPLL_POST_DIV);
  1030.     PLL->StorePostDivSrc = RHDRegRead(PLL, EXT2_PPLL_POST_DIV_SRC);
  1031.     PLL->StoreControl = RHDRegRead(PLL, EXT2_PPLL_CNTL);
  1032.     PLL->StoreSpreadSpectrum = RHDRegRead(PLL, P2PLL_INT_SS_CNTL);
  1033.  
  1034.     PLL->StoreGlitchReset = RHDRegRead(PLL, P2PLL_CNTL) & 0x00002000;
  1035.  
  1036.     PLL->StoreScalerPostDiv = RHDRegRead(PLL, P2PLL_DISP_CLK_CNTL) & 0x003F;
  1037.     PLL->StoreSymPostDiv = RHDRegRead(PLL, EXT2_SYM_PPLL_POST_DIV) & 0x007F;
  1038.  
  1039.     PLL->StoreCrtc1Owner = RHDRegRead(PLL, PCLK_CRTC1_CNTL) & 0x00010000;
  1040.     PLL->StoreCrtc2Owner = RHDRegRead(PLL, PCLK_CRTC2_CNTL) & 0x00010000;
  1041.  
  1042.     PLL->StoreDCCGCLKOwner = RV620DCCGCLKAvailable(PLL);
  1043.     if (PLL->StoreDCCGCLKOwner)
  1044.         PLL->StoreDCCGCLK = RHDRegRead(PLL, DCCG_DISP_CLK_SRCSEL);
  1045.     else
  1046.         PLL->StoreDCCGCLK = 0;
  1047.  
  1048.     PLL->Stored = TRUE;
  1049. }
  1050.  
  1051. /*
  1052.  * Notice how we handle the DCCG ownership here. There is a difference between
  1053.  * currently holding the DCCG and what was held when in the VT. With the
  1054.  * solution here we no longer hardlock, but we do have the danger of keeping
  1055.  * the DCCG in external mode for too long a time, if both PLL restores are
  1056.  * too far apart. This is currently not an issue as VT restoration goes over
  1057.  * the whole device in one go anyway; no partial restoration going on
  1058.  */
  1059. static void
  1060. RV620PLL1Restore(struct rhdPLL *PLL)
  1061. {
  1062.     RHDFUNC(PLL);
  1063.  
  1064.     if (RV620DCCGCLKAvailable(PLL))
  1065.         RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0x03, 0x00000003);
  1066.  
  1067.     if (PLL->StoreActive) {
  1068.         RV620PLL1SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv,
  1069.                         PLL->StorePostDiv, PLL->StoreScalerPostDiv,
  1070.                         PLL->StoreSymPostDiv, PLL->StoreControl);
  1071.         RHDRegMask(PLL, P1PLL_INT_SS_CNTL,
  1072.                    PLL->StoreSpreadSpectrum, 0x00000001);
  1073.  
  1074.         if (PLL->StoreDCCGCLKOwner)
  1075.             RHDRegWrite(PLL, DCCG_DISP_CLK_SRCSEL, PLL->StoreDCCGCLK);
  1076.  
  1077.     } else {
  1078.         PLL->Power(PLL, RHD_POWER_SHUTDOWN);
  1079.  
  1080.         /* lame attempt at at least restoring the old values */
  1081.         RHDRegWrite(PLL, EXT1_PPLL_REF_DIV, PLL->StoreRefDiv);
  1082.         RHDRegWrite(PLL, EXT1_PPLL_FB_DIV, PLL->StoreFBDiv);
  1083.         RHDRegWrite(PLL, EXT1_PPLL_POST_DIV, PLL->StorePostDiv);
  1084.         RHDRegWrite(PLL, EXT1_PPLL_POST_DIV_SRC, PLL->StorePostDivSrc);
  1085.         RHDRegWrite(PLL, EXT1_PPLL_CNTL, PLL->StoreControl);
  1086.         RHDRegMask(PLL, P1PLL_DISP_CLK_CNTL, PLL->StoreScalerPostDiv, 0x003F);
  1087.         RHDRegMask(PLL, EXT1_SYM_PPLL_POST_DIV, PLL->StoreSymPostDiv, 0x007F);
  1088.         RHDRegWrite(PLL, P1PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum);
  1089.  
  1090.         if (PLL->StoreGlitchReset)
  1091.             RHDRegMask(PLL, P1PLL_CNTL, 0x00002000, 0x00002000);
  1092.         else
  1093.             RHDRegMask(PLL, P1PLL_CNTL, 0, 0x00002000);
  1094.     }
  1095.  
  1096.     if (PLL->StoreCrtc1Owner)
  1097.         R500PLLCRTCGrab(PLL, FALSE);
  1098.     if (PLL->StoreCrtc2Owner)
  1099.         R500PLLCRTCGrab(PLL, TRUE);
  1100.  
  1101.     if (PLL->StoreDCCGCLKOwner)
  1102.         RHDRegWrite(PLL, DCCG_DISP_CLK_SRCSEL, PLL->StoreDCCGCLK);
  1103. }
  1104.  
  1105. /*
  1106.  *
  1107.  */
  1108. static void
  1109. RV620PLL2Restore(struct rhdPLL *PLL)
  1110. {
  1111.     RHDFUNC(PLL);
  1112.  
  1113.     if (RV620DCCGCLKAvailable(PLL))
  1114.         RHDRegMask(PLL, DCCG_DISP_CLK_SRCSEL, 0x03, 0x00000003);
  1115.  
  1116.     if (PLL->StoreActive) {
  1117.         RV620PLL2SetLow(PLL, PLL->StoreRefDiv, PLL->StoreFBDiv,
  1118.                         PLL->StorePostDiv, PLL->StoreScalerPostDiv,
  1119.                         PLL->StoreSymPostDiv, PLL->StoreControl);
  1120.         RHDRegMask(PLL, P2PLL_INT_SS_CNTL,
  1121.                    PLL->StoreSpreadSpectrum, 0x00000001);
  1122.     } else {
  1123.         PLL->Power(PLL, RHD_POWER_SHUTDOWN);
  1124.  
  1125.         /* lame attempt at at least restoring the old values */
  1126.         RHDRegWrite(PLL, EXT2_PPLL_REF_DIV, PLL->StoreRefDiv);
  1127.         RHDRegWrite(PLL, EXT2_PPLL_FB_DIV, PLL->StoreFBDiv);
  1128.         RHDRegWrite(PLL, EXT2_PPLL_POST_DIV, PLL->StorePostDiv);
  1129.         RHDRegWrite(PLL, EXT2_PPLL_POST_DIV_SRC, PLL->StorePostDivSrc);
  1130.         RHDRegWrite(PLL, EXT2_PPLL_CNTL, PLL->StoreControl);
  1131.         RHDRegMask(PLL, P2PLL_DISP_CLK_CNTL, PLL->StoreScalerPostDiv, 0x003F);
  1132.         RHDRegMask(PLL, EXT2_SYM_PPLL_POST_DIV, PLL->StoreSymPostDiv, 0x007F);
  1133.         RHDRegWrite(PLL, P2PLL_INT_SS_CNTL, PLL->StoreSpreadSpectrum);
  1134.  
  1135.         if (PLL->StoreGlitchReset)
  1136.             RHDRegMask(PLL, P2PLL_CNTL, 0x00002000, 0x00002000);
  1137.         else
  1138.             RHDRegMask(PLL, P2PLL_CNTL, 0, 0x00002000);
  1139.     }
  1140.  
  1141.     if (PLL->StoreCrtc1Owner)
  1142.         R500PLLCRTCGrab(PLL, FALSE);
  1143.     if (PLL->StoreCrtc2Owner)
  1144.         R500PLLCRTCGrab(PLL, TRUE);
  1145.  
  1146.     if (PLL->StoreDCCGCLKOwner)
  1147.         RHDRegWrite(PLL, DCCG_DISP_CLK_SRCSEL, PLL->StoreDCCGCLK);
  1148. }
  1149.  
  1150. /* Some defaults for when we don't have this info */
  1151. /* XTAL is visible on the cards */
  1152. #define RHD_PLL_REFERENCE_DEFAULT            27000
  1153. /* these required quite some testing */
  1154. #define RHD_R500_PLL_INTERNAL_MIN_DEFAULT   648000
  1155. #define RHD_RV620_PLL_INTERNAL_MIN_DEFAULT  702000
  1156. /* Lowest value seen so far */
  1157. #define RHD_PLL_INTERNAL_MAX_DEFAULT       1100000
  1158. #define RHD_PLL_MIN_DEFAULT                  16000 /* guess */
  1159. #define RHD_PLL_MAX_DEFAULT                 400000 /* 400Mhz modes... hrm */
  1160.  
  1161. enum pllComp {
  1162.     PLL_NONE,
  1163.     PLL_MIN,
  1164.     PLL_MAX
  1165. };
  1166.  
  1167. /*
  1168.  *
  1169.  */
  1170. #ifdef ATOM_BIOS
  1171. static Bool
  1172. getPLLValuesFromAtomBIOS(RHDPtr rhdPtr,
  1173.                          AtomBiosRequestID func, char *msg, CARD32 *val, enum pllComp comp)
  1174. {
  1175.     AtomBiosArgRec arg;
  1176.     AtomBiosResult ret;
  1177.  
  1178.     if (rhdPtr->atomBIOS) {
  1179.         ret = RHDAtomBiosFunc(rhdPtr->scrnIndex, rhdPtr->atomBIOS,
  1180.                               func, &arg);
  1181.         if (ret == ATOM_SUCCESS) {
  1182.             if (arg.val) {
  1183.                 switch (comp) {
  1184.                     case PLL_MAX:
  1185.                         if (arg.val < *val)
  1186.                             xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING,
  1187.                                        "Lower %s detected than the default: %lu %lu.\n"
  1188.                                        "Please contact the authors ASAP.\n", msg,
  1189.                                        (unsigned long)*val, (unsigned long)arg.val * 10);
  1190.                         break;
  1191.                     case PLL_MIN:
  1192.                         if (arg.val > *val)
  1193.                             xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING,
  1194.                                        "Higher %s detected than the default: %lu %lu.\n"
  1195.                                        "Please contact the authors ASAP.\n", msg,
  1196.                                        (unsigned long)*val, (unsigned long)arg.val * 10);
  1197.                         break;
  1198.                     default:
  1199.                         break;
  1200.                 }
  1201.                 *val = arg.val;
  1202.             }
  1203.         }
  1204.         return TRUE;
  1205.     } else
  1206.         xf86DrvMsg(rhdPtr->scrnIndex, X_ERROR, "Failed to retrieve the %s"
  1207.                    " clock from ATOM.\n",msg);
  1208.     return FALSE;
  1209. }
  1210. #endif
  1211.  
  1212. /*
  1213.  *
  1214.  */
  1215. void
  1216. RHDSetupLimits(RHDPtr rhdPtr, CARD32 *RefClock,
  1217.                CARD32 *IntMin, CARD32 *IntMax,
  1218.                CARD32 *PixMin, CARD32 *PixMax)
  1219. {
  1220.     /* Retrieve the internal PLL frequency limits*/
  1221.     *RefClock = RHD_PLL_REFERENCE_DEFAULT;
  1222.     if (rhdPtr->ChipSet < RHD_RV620)
  1223.         *IntMin = RHD_R500_PLL_INTERNAL_MIN_DEFAULT;
  1224.     else
  1225.         *IntMin = RHD_RV620_PLL_INTERNAL_MIN_DEFAULT;
  1226.  
  1227.     *IntMax = RHD_PLL_INTERNAL_MAX_DEFAULT;
  1228.  
  1229.     /* keep the defaults */
  1230.     *PixMin = RHD_PLL_MIN_DEFAULT;
  1231.     *PixMax = RHD_PLL_MAX_DEFAULT;
  1232.  
  1233. #ifdef ATOM_BIOS
  1234.     getPLLValuesFromAtomBIOS(rhdPtr, GET_MIN_PIXEL_CLOCK_PLL_OUTPUT, "minimum PLL output",
  1235.                              IntMin,  PLL_MIN);
  1236.     getPLLValuesFromAtomBIOS(rhdPtr, GET_MAX_PIXEL_CLOCK_PLL_OUTPUT, "maximum PLL output",
  1237.                              IntMax, PLL_MAX);
  1238.     getPLLValuesFromAtomBIOS(rhdPtr, GET_MAX_PIXEL_CLK, "Pixel Clock",
  1239.                              PixMax, PLL_MAX);
  1240.     getPLLValuesFromAtomBIOS(rhdPtr, GET_REF_CLOCK, "reference clock",
  1241.                              RefClock, PLL_NONE);
  1242.     if (*IntMax == 0) {
  1243.         if (rhdPtr->ChipSet < RHD_RV620)
  1244.             *IntMax = RHD_R500_PLL_INTERNAL_MIN_DEFAULT;
  1245.         else
  1246.             *IntMax = RHD_RV620_PLL_INTERNAL_MIN_DEFAULT;
  1247.  
  1248.         xf86DrvMsg(rhdPtr->scrnIndex, X_WARNING, "AtomBIOS reports maximum VCO freq 0. "
  1249.                    "Using %lu instead\n",(unsigned long)*IntMax);
  1250.     }
  1251. #endif
  1252. }
  1253.  
  1254. /*
  1255.  *
  1256.  */
  1257. Bool
  1258. RHDPLLsInit(RHDPtr rhdPtr)
  1259. {
  1260.     struct rhdPLL *PLL;
  1261.     CARD32 RefClock, IntMin, IntMax, PixMin, PixMax;
  1262.  
  1263.     RHDFUNC(rhdPtr);
  1264.  
  1265.     if (RHDUseAtom(rhdPtr, NULL, atomUsagePLL))
  1266.         return FALSE;
  1267.  
  1268.     RHDSetupLimits(rhdPtr, &RefClock, &IntMin, &IntMax, &PixMin, &PixMax);
  1269.  
  1270.     /* PLL1 */
  1271.     PLL = (struct rhdPLL *) xnfcalloc(sizeof(struct rhdPLL), 1);
  1272.  
  1273.     PLL->scrnIndex = rhdPtr->scrnIndex;
  1274.     PLL->Name = PLL_NAME_PLL1;
  1275.     PLL->Id = PLL_ID_PLL1;
  1276.  
  1277.     PLL->RefClock = RefClock;
  1278.     PLL->IntMin = IntMin;
  1279.     PLL->IntMax = IntMax;
  1280.     PLL->PixMin = PixMin;
  1281.     PLL->PixMax = PixMax;
  1282.  
  1283.     PLL->Valid = NULL;
  1284.     if (rhdPtr->ChipSet < RHD_RV620) {
  1285.         PLL->Set = R500PLL1Set;
  1286.         PLL->Power = R500PLL1Power;
  1287.         PLL->Save = R500PLL1Save;
  1288.         PLL->Restore = R500PLL1Restore;
  1289.     } else {
  1290.         PLL->Set = RV620PLL1Set;
  1291.         PLL->Power = RV620PLL1Power;
  1292.         PLL->Save = RV620PLL1Save;
  1293.         PLL->Restore = RV620PLL1Restore;
  1294.     }
  1295.  
  1296.     rhdPtr->PLLs[0] = PLL;
  1297.  
  1298.     /* PLL2 */
  1299.     PLL = (struct rhdPLL *) xnfcalloc(sizeof(struct rhdPLL), 1);
  1300.  
  1301.     PLL->scrnIndex = rhdPtr->scrnIndex;
  1302.     PLL->Name = PLL_NAME_PLL2;
  1303.     PLL->Id = PLL_ID_PLL2;
  1304.  
  1305.     PLL->RefClock = RefClock;
  1306.     PLL->IntMin = IntMin;
  1307.     PLL->IntMax = IntMax;
  1308.     PLL->PixMin = PixMin;
  1309.     PLL->PixMax = PixMax;
  1310.  
  1311.     PLL->Valid = NULL;
  1312.     if (rhdPtr->ChipSet < RHD_RV620) {
  1313.         PLL->Set = R500PLL2Set;
  1314.         PLL->Power = R500PLL2Power;
  1315.         PLL->Save = R500PLL2Save;
  1316.         PLL->Restore = R500PLL2Restore;
  1317.     } else {
  1318.         PLL->Set = RV620PLL2Set;
  1319.         PLL->Power = RV620PLL2Power;
  1320.         PLL->Save = RV620PLL2Save;
  1321.         PLL->Restore = RV620PLL2Restore;
  1322.     }
  1323.  
  1324.     rhdPtr->PLLs[1] = PLL;
  1325.  
  1326.     return TRUE;
  1327. }
  1328.  
  1329. /*
  1330.  *
  1331.  */
  1332. ModeStatus
  1333. RHDPLLValid(struct rhdPLL *PLL, CARD32 Clock)
  1334. {
  1335.     RHDFUNC(PLL);
  1336.  
  1337.     if (Clock < PLL->PixMin)
  1338.         return MODE_CLOCK_LOW;
  1339.     if (Clock > PLL->PixMax)
  1340.         return MODE_CLOCK_HIGH;
  1341.  
  1342.     if (PLL->Valid)
  1343.         return PLL->Valid(PLL, Clock);
  1344.     else
  1345.         return MODE_OK;
  1346. }
  1347.  
  1348.  
  1349. /*
  1350.  * Calculate the PLL parameters for a given dotclock.
  1351.  *
  1352.  * This calculation uses a linear approximation of an experimentally found
  1353.  * curve that delimits reference versus feedback dividers on rv610. This curve
  1354.  * can be shifted towards higher feedback divider through increasing the gain
  1355.  * control, but the effect of this is rather limited.
  1356.  *
  1357.  * Since this upper limit still provides a wide enough range with enough
  1358.  * granularity, we use it for all r5xx and r6xx devices.
  1359.  */
  1360. static Bool
  1361. PLLCalculate(struct rhdPLL *PLL, CARD32 PixelClock,
  1362.              CARD16 *RefDivider, CARD16 *FBDivider, CARD8 *PostDivider)
  1363. {
  1364. /* limited by the number of bits available */
  1365. #define FB_DIV_LIMIT 2048
  1366. #define REF_DIV_LIMIT 1024
  1367. #define POST_DIV_LIMIT 128
  1368.  
  1369.     CARD32 FBDiv, RefDiv, PostDiv, BestDiff = 0xFFFFFFFF;
  1370.     float Ratio;
  1371.  
  1372.     Ratio = ((float) PixelClock) / ((float) PLL->RefClock);
  1373.  
  1374.     for (PostDiv = 2; PostDiv < POST_DIV_LIMIT; PostDiv++) {
  1375.         CARD32 VCOOut = PixelClock * PostDiv;
  1376.  
  1377.         /* we are conservative and avoid the limits */
  1378.         if (VCOOut <= PLL->IntMin)
  1379.             continue;
  1380.         if (VCOOut >= PLL->IntMax)
  1381.             break;
  1382.  
  1383.         for (RefDiv = 1; RefDiv <= REF_DIV_LIMIT; RefDiv++) {
  1384.             CARD32 Diff;
  1385.  
  1386.             FBDiv = (CARD32) ((Ratio * PostDiv * RefDiv) + 0.5);
  1387.  
  1388.             if (FBDiv >= FB_DIV_LIMIT)
  1389.                 break;
  1390.             if (FBDiv > (500 + (13 * RefDiv))) /* rv6x0 limit */
  1391.                 break;
  1392.  
  1393.             Diff = abs( PixelClock - (FBDiv * PLL->RefClock) / (PostDiv * RefDiv) );
  1394.  
  1395.             if (Diff < BestDiff) {
  1396.                 *FBDivider = FBDiv;
  1397.                 *RefDivider = RefDiv;
  1398.                 *PostDivider = PostDiv;
  1399.                 BestDiff = Diff;
  1400.             }
  1401.  
  1402.             if (BestDiff == 0)
  1403.                 break;
  1404.         }
  1405.         if (BestDiff == 0)
  1406.             break;
  1407.     }
  1408.  
  1409.     if (BestDiff != 0xFFFFFFFF) {
  1410.         RHDDebug(PLL->scrnIndex, "PLL Calculation: %dkHz = "
  1411.                    "(((%i / 0x%X) * 0x%X) / 0x%X) (%dkHz off)\n",
  1412.                    (int) PixelClock, (unsigned int) PLL->RefClock, *RefDivider,
  1413.                    *FBDivider, *PostDivider, (int) BestDiff);
  1414.         return TRUE;
  1415.     } else { /* Should never happen */
  1416.         xf86DrvMsg(PLL->scrnIndex, X_ERROR,
  1417.                    "%s: Failed to get a valid PLL setting for %dkHz\n",
  1418.                    __func__, (int) PixelClock);
  1419.         return FALSE;
  1420.     }
  1421. }
  1422.  
  1423. /*
  1424.  *
  1425.  */
  1426. void
  1427. RHDPLLSet(struct rhdPLL *PLL, CARD32 Clock)
  1428. {
  1429.     CARD16 RefDivider = 0, FBDivider = 0;
  1430.     CARD8 PostDivider = 0;
  1431.  
  1432.     RHDDebug(PLL->scrnIndex, "%s: Setting %s to %dkHz\n", __func__,
  1433.              PLL->Name, Clock);
  1434.  
  1435.     if (PLLCalculate(PLL, Clock, &RefDivider, &FBDivider, &PostDivider)) {
  1436.         PLL->Set(PLL, Clock, RefDivider, FBDivider, PostDivider);
  1437.  
  1438.         PLL->CurrentClock = Clock;
  1439.         PLL->Active = TRUE;
  1440.     } else
  1441.         xf86DrvMsg(PLL->scrnIndex, X_WARNING,
  1442.                    "%s: Not altering any settings.\n", __func__);
  1443. }
  1444.  
  1445. /*
  1446.  *
  1447.  */
  1448. void
  1449. RHDPLLPower(struct rhdPLL *PLL, int Power)
  1450. {
  1451.     RHDFUNC(PLL);
  1452.  
  1453.     if (PLL->Power)
  1454.         PLL->Power(PLL, Power);
  1455. }
  1456.  
  1457. /*
  1458.  *
  1459.  */
  1460. void
  1461. RHDPLLsPowerAll(RHDPtr rhdPtr, int Power)
  1462. {
  1463.     struct rhdPLL *PLL;
  1464.  
  1465.     RHDFUNC(rhdPtr);
  1466.  
  1467.     PLL = rhdPtr->PLLs[0];
  1468.     if (PLL->Power)
  1469.         PLL->Power(PLL, Power);
  1470.  
  1471.     PLL = rhdPtr->PLLs[1];
  1472.     if (PLL->Power)
  1473.         PLL->Power(PLL, Power);
  1474. }
  1475.  
  1476. /*
  1477.  *
  1478.  */
  1479. void
  1480. RHDPLLsShutdownInactive(RHDPtr rhdPtr)
  1481. {
  1482.     struct rhdPLL *PLL;
  1483.  
  1484.     RHDFUNC(rhdPtr);
  1485.  
  1486.     PLL = rhdPtr->PLLs[0];
  1487.     if (PLL->Power && !PLL->Active)
  1488.         PLL->Power(PLL, RHD_POWER_SHUTDOWN);
  1489.  
  1490.     PLL = rhdPtr->PLLs[1];
  1491.     if (PLL->Power && !PLL->Active)
  1492.         PLL->Power(PLL, RHD_POWER_SHUTDOWN);
  1493. }
  1494.  
  1495. /*
  1496.  *
  1497.  */
  1498. void
  1499. RHDPLLsSave(RHDPtr rhdPtr)
  1500. {
  1501.     struct rhdPLL *PLL;
  1502.  
  1503.     RHDFUNC(rhdPtr);
  1504.  
  1505.     PLL = rhdPtr->PLLs[0];
  1506.     if (PLL->Save)
  1507.         PLL->Save(PLL);
  1508.  
  1509.     PLL = rhdPtr->PLLs[1];
  1510.     if (PLL->Save)
  1511.         PLL->Save(PLL);
  1512. }
  1513.  
  1514. /*
  1515.  *
  1516.  */
  1517. void
  1518. RHDPLLsRestore(RHDPtr rhdPtr)
  1519. {
  1520.     struct rhdPLL *PLL;
  1521.  
  1522.     RHDFUNC(rhdPtr);
  1523.  
  1524.     PLL = rhdPtr->PLLs[0];
  1525.     if (PLL->Restore)
  1526.         PLL->Restore(PLL);
  1527.  
  1528.     PLL = rhdPtr->PLLs[1];
  1529.     if (PLL->Restore)
  1530.         PLL->Restore(PLL);
  1531. }
  1532.  
  1533. /*
  1534.  *
  1535.  */
  1536. void
  1537. RHDPLLsDestroy(RHDPtr rhdPtr)
  1538. {
  1539.     RHDFUNC(rhdPtr);
  1540.  
  1541.     if (rhdPtr->PLLs[0] && rhdPtr->PLLs[0]->Private)
  1542.         xfree(rhdPtr->PLLs[0]->Private);
  1543.     xfree(rhdPtr->PLLs[0]);
  1544.     if (rhdPtr->PLLs[1] && rhdPtr->PLLs[1]->Private)
  1545.         xfree(rhdPtr->PLLs[1]->Private);
  1546.     xfree(rhdPtr->PLLs[1]);
  1547. }
  1548.