Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright © 2006-2011 Intel Corporation |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or modify it |
| 5 | * under the terms and conditions of the GNU General Public License, |
| 6 | * version 2, as published by the Free Software Foundation. |
| 7 | * |
| 8 | * This program is distributed in the hope it will be useful, but WITHOUT |
| 9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 11 | * more details. |
| 12 | * |
| 13 | * You should have received a copy of the GNU General Public License along with |
| 14 | * this program; if not, write to the Free Software Foundation, Inc., |
| 15 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. |
| 16 | * |
| 17 | * Authors: |
| 18 | * Eric Anholt <eric@anholt.net> |
| 19 | */ |
| 20 | |
| 21 | #include <linux/i2c.h> |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 22 | |
| 23 | #include <drm/drmP.h> |
Daniel Vetter | 3cb9ae4 | 2014-10-29 10:03:57 +0100 | [diff] [blame] | 24 | #include <drm/drm_plane_helper.h> |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 25 | #include "framebuffer.h" |
| 26 | #include "psb_drv.h" |
| 27 | #include "psb_intel_drv.h" |
| 28 | #include "psb_intel_reg.h" |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 29 | #include "gma_display.h" |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 30 | #include "power.h" |
| 31 | |
Patrik Jakobsson | 4e6bb70 | 2013-03-13 23:24:08 +0100 | [diff] [blame] | 32 | #define INTEL_LIMIT_I9XX_SDVO_DAC 0 |
| 33 | #define INTEL_LIMIT_I9XX_LVDS 1 |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 34 | |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 35 | static const struct gma_limit_t psb_intel_limits[] = { |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 36 | { /* INTEL_LIMIT_I9XX_SDVO_DAC */ |
Patrik Jakobsson | 06da491 | 2013-03-14 00:14:06 +0100 | [diff] [blame] | 37 | .dot = {.min = 20000, .max = 400000}, |
| 38 | .vco = {.min = 1400000, .max = 2800000}, |
| 39 | .n = {.min = 1, .max = 6}, |
| 40 | .m = {.min = 70, .max = 120}, |
| 41 | .m1 = {.min = 8, .max = 18}, |
| 42 | .m2 = {.min = 3, .max = 7}, |
| 43 | .p = {.min = 5, .max = 80}, |
| 44 | .p1 = {.min = 1, .max = 8}, |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 45 | .p2 = {.dot_limit = 200000, .p2_slow = 10, .p2_fast = 5}, |
| 46 | .find_pll = gma_find_best_pll, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 47 | }, |
| 48 | { /* INTEL_LIMIT_I9XX_LVDS */ |
Patrik Jakobsson | 06da491 | 2013-03-14 00:14:06 +0100 | [diff] [blame] | 49 | .dot = {.min = 20000, .max = 400000}, |
| 50 | .vco = {.min = 1400000, .max = 2800000}, |
| 51 | .n = {.min = 1, .max = 6}, |
| 52 | .m = {.min = 70, .max = 120}, |
| 53 | .m1 = {.min = 8, .max = 18}, |
| 54 | .m2 = {.min = 3, .max = 7}, |
| 55 | .p = {.min = 7, .max = 98}, |
| 56 | .p1 = {.min = 1, .max = 8}, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 57 | /* The single-channel range is 25-112Mhz, and dual-channel |
| 58 | * is 80-224Mhz. Prefer single channel as much as possible. |
| 59 | */ |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 60 | .p2 = {.dot_limit = 112000, .p2_slow = 14, .p2_fast = 7}, |
| 61 | .find_pll = gma_find_best_pll, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 62 | }, |
| 63 | }; |
| 64 | |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 65 | static const struct gma_limit_t *psb_intel_limit(struct drm_crtc *crtc, |
| 66 | int refclk) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 67 | { |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 68 | const struct gma_limit_t *limit; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 69 | |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 70 | if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 71 | limit = &psb_intel_limits[INTEL_LIMIT_I9XX_LVDS]; |
| 72 | else |
| 73 | limit = &psb_intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; |
| 74 | return limit; |
| 75 | } |
| 76 | |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 77 | static void psb_intel_clock(int refclk, struct gma_clock_t *clock) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 78 | { |
| 79 | clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); |
| 80 | clock->p = clock->p1 * clock->p2; |
| 81 | clock->vco = refclk * clock->m / (clock->n + 2); |
| 82 | clock->dot = clock->vco / clock->p; |
| 83 | } |
| 84 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 85 | /** |
| 86 | * Return the pipe currently connected to the panel fitter, |
| 87 | * or -1 if the panel fitter is not present or not in use |
| 88 | */ |
| 89 | static int psb_intel_panel_fitter_pipe(struct drm_device *dev) |
| 90 | { |
| 91 | u32 pfit_control; |
| 92 | |
| 93 | pfit_control = REG_READ(PFIT_CONTROL); |
| 94 | |
| 95 | /* See if the panel fitter is in use */ |
| 96 | if ((pfit_control & PFIT_ENABLE) == 0) |
| 97 | return -1; |
| 98 | /* Must be on PIPE 1 for PSB */ |
| 99 | return 1; |
| 100 | } |
| 101 | |
| 102 | static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, |
| 103 | struct drm_display_mode *mode, |
| 104 | struct drm_display_mode *adjusted_mode, |
| 105 | int x, int y, |
| 106 | struct drm_framebuffer *old_fb) |
| 107 | { |
| 108 | struct drm_device *dev = crtc->dev; |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 109 | struct drm_psb_private *dev_priv = dev->dev_private; |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 110 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
Jani Nikula | 45fe734 | 2015-03-11 11:51:01 +0200 | [diff] [blame] | 111 | const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 112 | int pipe = gma_crtc->pipe; |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 113 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 114 | int refclk; |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 115 | struct gma_clock_t clock; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 116 | u32 dpll = 0, fp = 0, dspcntr, pipeconf; |
Kirill A. Shutemov | 2e33d6b | 2012-03-08 16:15:20 +0000 | [diff] [blame] | 117 | bool ok, is_sdvo = false; |
| 118 | bool is_lvds = false, is_tv = false; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 119 | struct drm_mode_config *mode_config = &dev->mode_config; |
| 120 | struct drm_connector *connector; |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 121 | const struct gma_limit_t *limit; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 122 | |
| 123 | /* No scan out no play */ |
Matt Roper | f4510a2 | 2014-04-01 15:22:40 -0700 | [diff] [blame] | 124 | if (crtc->primary->fb == NULL) { |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 125 | crtc_funcs->mode_set_base(crtc, x, y, old_fb); |
| 126 | return 0; |
| 127 | } |
| 128 | |
| 129 | list_for_each_entry(connector, &mode_config->connector_list, head) { |
Patrik Jakobsson | 367e440 | 2013-07-22 17:45:26 +0200 | [diff] [blame] | 130 | struct gma_encoder *gma_encoder = gma_attached_encoder(connector); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 131 | |
| 132 | if (!connector->encoder |
| 133 | || connector->encoder->crtc != crtc) |
| 134 | continue; |
| 135 | |
Patrik Jakobsson | 367e440 | 2013-07-22 17:45:26 +0200 | [diff] [blame] | 136 | switch (gma_encoder->type) { |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 137 | case INTEL_OUTPUT_LVDS: |
| 138 | is_lvds = true; |
| 139 | break; |
| 140 | case INTEL_OUTPUT_SDVO: |
| 141 | is_sdvo = true; |
| 142 | break; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 143 | case INTEL_OUTPUT_TVOUT: |
| 144 | is_tv = true; |
| 145 | break; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 146 | } |
| 147 | } |
| 148 | |
| 149 | refclk = 96000; |
| 150 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 151 | limit = gma_crtc->clock_funcs->limit(crtc, refclk); |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 152 | |
| 153 | ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 154 | &clock); |
| 155 | if (!ok) { |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 156 | DRM_ERROR("Couldn't find PLL settings for mode! target: %d, actual: %d", |
| 157 | adjusted_mode->clock, clock.dot); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 158 | return 0; |
| 159 | } |
| 160 | |
| 161 | fp = clock.n << 16 | clock.m1 << 8 | clock.m2; |
| 162 | |
| 163 | dpll = DPLL_VGA_MODE_DIS; |
| 164 | if (is_lvds) { |
| 165 | dpll |= DPLLB_MODE_LVDS; |
| 166 | dpll |= DPLL_DVO_HIGH_SPEED; |
| 167 | } else |
| 168 | dpll |= DPLLB_MODE_DAC_SERIAL; |
| 169 | if (is_sdvo) { |
| 170 | int sdvo_pixel_multiply = |
| 171 | adjusted_mode->clock / mode->clock; |
| 172 | dpll |= DPLL_DVO_HIGH_SPEED; |
| 173 | dpll |= |
| 174 | (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES; |
| 175 | } |
| 176 | |
| 177 | /* compute bitmask from p1 value */ |
| 178 | dpll |= (1 << (clock.p1 - 1)) << 16; |
| 179 | switch (clock.p2) { |
| 180 | case 5: |
| 181 | dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_5; |
| 182 | break; |
| 183 | case 7: |
| 184 | dpll |= DPLLB_LVDS_P2_CLOCK_DIV_7; |
| 185 | break; |
| 186 | case 10: |
| 187 | dpll |= DPLL_DAC_SERIAL_P2_CLOCK_DIV_10; |
| 188 | break; |
| 189 | case 14: |
| 190 | dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14; |
| 191 | break; |
| 192 | } |
| 193 | |
| 194 | if (is_tv) { |
| 195 | /* XXX: just matching BIOS for now */ |
| 196 | /* dpll |= PLL_REF_INPUT_TVCLKINBC; */ |
| 197 | dpll |= 3; |
| 198 | } |
| 199 | dpll |= PLL_REF_INPUT_DREFCLK; |
| 200 | |
| 201 | /* setup pipeconf */ |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 202 | pipeconf = REG_READ(map->conf); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 203 | |
| 204 | /* Set up the display plane register */ |
| 205 | dspcntr = DISPPLANE_GAMMA_ENABLE; |
| 206 | |
| 207 | if (pipe == 0) |
| 208 | dspcntr |= DISPPLANE_SEL_PIPE_A; |
| 209 | else |
| 210 | dspcntr |= DISPPLANE_SEL_PIPE_B; |
| 211 | |
| 212 | dspcntr |= DISPLAY_PLANE_ENABLE; |
| 213 | pipeconf |= PIPEACONF_ENABLE; |
| 214 | dpll |= DPLL_VCO_ENABLE; |
| 215 | |
| 216 | |
| 217 | /* Disable the panel fitter if it was on our pipe */ |
| 218 | if (psb_intel_panel_fitter_pipe(dev) == pipe) |
| 219 | REG_WRITE(PFIT_CONTROL, 0); |
| 220 | |
| 221 | drm_mode_debug_printmodeline(mode); |
| 222 | |
| 223 | if (dpll & DPLL_VCO_ENABLE) { |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 224 | REG_WRITE(map->fp0, fp); |
| 225 | REG_WRITE(map->dpll, dpll & ~DPLL_VCO_ENABLE); |
| 226 | REG_READ(map->dpll); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 227 | udelay(150); |
| 228 | } |
| 229 | |
| 230 | /* The LVDS pin pair needs to be on before the DPLLs are enabled. |
| 231 | * This is an exception to the general rule that mode_set doesn't turn |
| 232 | * things on. |
| 233 | */ |
| 234 | if (is_lvds) { |
| 235 | u32 lvds = REG_READ(LVDS); |
| 236 | |
| 237 | lvds &= ~LVDS_PIPEB_SELECT; |
| 238 | if (pipe == 1) |
| 239 | lvds |= LVDS_PIPEB_SELECT; |
| 240 | |
| 241 | lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP; |
| 242 | /* Set the B0-B3 data pairs corresponding to |
| 243 | * whether we're going to |
| 244 | * set the DPLLs for dual-channel mode or not. |
| 245 | */ |
| 246 | lvds &= ~(LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP); |
| 247 | if (clock.p2 == 7) |
| 248 | lvds |= LVDS_B0B3_POWER_UP | LVDS_CLKB_POWER_UP; |
| 249 | |
| 250 | /* It would be nice to set 24 vs 18-bit mode (LVDS_A3_POWER_UP) |
| 251 | * appropriately here, but we need to look more |
| 252 | * thoroughly into how panels behave in the two modes. |
| 253 | */ |
| 254 | |
| 255 | REG_WRITE(LVDS, lvds); |
| 256 | REG_READ(LVDS); |
| 257 | } |
| 258 | |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 259 | REG_WRITE(map->fp0, fp); |
| 260 | REG_WRITE(map->dpll, dpll); |
| 261 | REG_READ(map->dpll); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 262 | /* Wait for the clocks to stabilize. */ |
| 263 | udelay(150); |
| 264 | |
| 265 | /* write it again -- the BIOS does, after all */ |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 266 | REG_WRITE(map->dpll, dpll); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 267 | |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 268 | REG_READ(map->dpll); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 269 | /* Wait for the clocks to stabilize. */ |
| 270 | udelay(150); |
| 271 | |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 272 | REG_WRITE(map->htotal, (adjusted_mode->crtc_hdisplay - 1) | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 273 | ((adjusted_mode->crtc_htotal - 1) << 16)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 274 | REG_WRITE(map->hblank, (adjusted_mode->crtc_hblank_start - 1) | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 275 | ((adjusted_mode->crtc_hblank_end - 1) << 16)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 276 | REG_WRITE(map->hsync, (adjusted_mode->crtc_hsync_start - 1) | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 277 | ((adjusted_mode->crtc_hsync_end - 1) << 16)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 278 | REG_WRITE(map->vtotal, (adjusted_mode->crtc_vdisplay - 1) | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 279 | ((adjusted_mode->crtc_vtotal - 1) << 16)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 280 | REG_WRITE(map->vblank, (adjusted_mode->crtc_vblank_start - 1) | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 281 | ((adjusted_mode->crtc_vblank_end - 1) << 16)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 282 | REG_WRITE(map->vsync, (adjusted_mode->crtc_vsync_start - 1) | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 283 | ((adjusted_mode->crtc_vsync_end - 1) << 16)); |
| 284 | /* pipesrc and dspsize control the size that is scaled from, |
| 285 | * which should always be the user's requested size. |
| 286 | */ |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 287 | REG_WRITE(map->size, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 288 | ((mode->vdisplay - 1) << 16) | (mode->hdisplay - 1)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 289 | REG_WRITE(map->pos, 0); |
| 290 | REG_WRITE(map->src, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 291 | ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1)); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 292 | REG_WRITE(map->conf, pipeconf); |
| 293 | REG_READ(map->conf); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 294 | |
Patrik Jakobsson | d1fa08f | 2013-07-10 01:20:19 +0200 | [diff] [blame] | 295 | gma_wait_for_vblank(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 296 | |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 297 | REG_WRITE(map->cntr, dspcntr); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 298 | |
| 299 | /* Flush the plane changes */ |
| 300 | crtc_funcs->mode_set_base(crtc, x, y, old_fb); |
| 301 | |
Patrik Jakobsson | d1fa08f | 2013-07-10 01:20:19 +0200 | [diff] [blame] | 302 | gma_wait_for_vblank(dev); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 303 | |
| 304 | return 0; |
| 305 | } |
| 306 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 307 | /* Returns the clock of the currently programmed mode of the given pipe. */ |
| 308 | static int psb_intel_crtc_clock_get(struct drm_device *dev, |
| 309 | struct drm_crtc *crtc) |
| 310 | { |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 311 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 312 | struct drm_psb_private *dev_priv = dev->dev_private; |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 313 | int pipe = gma_crtc->pipe; |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 314 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 315 | u32 dpll; |
| 316 | u32 fp; |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 317 | struct gma_clock_t clock; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 318 | bool is_lvds; |
Alan Cox | 6256304 | 2012-05-11 11:30:16 +0100 | [diff] [blame] | 319 | struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 320 | |
| 321 | if (gma_power_begin(dev, false)) { |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 322 | dpll = REG_READ(map->dpll); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 323 | if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 324 | fp = REG_READ(map->fp0); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 325 | else |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 326 | fp = REG_READ(map->fp1); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 327 | is_lvds = (pipe == 1) && (REG_READ(LVDS) & LVDS_PORT_EN); |
| 328 | gma_power_end(dev); |
| 329 | } else { |
Alan Cox | 6256304 | 2012-05-11 11:30:16 +0100 | [diff] [blame] | 330 | dpll = p->dpll; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 331 | |
| 332 | if ((dpll & DISPLAY_RATE_SELECT_FPA1) == 0) |
Alan Cox | 6256304 | 2012-05-11 11:30:16 +0100 | [diff] [blame] | 333 | fp = p->fp0; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 334 | else |
Alan Cox | 6256304 | 2012-05-11 11:30:16 +0100 | [diff] [blame] | 335 | fp = p->fp1; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 336 | |
Alan Cox | c6265ff | 2012-03-08 16:02:05 +0000 | [diff] [blame] | 337 | is_lvds = (pipe == 1) && (dev_priv->regs.psb.saveLVDS & |
Alan Cox | 648a8e3 | 2012-03-08 16:00:31 +0000 | [diff] [blame] | 338 | LVDS_PORT_EN); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 339 | } |
| 340 | |
| 341 | clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT; |
| 342 | clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT; |
| 343 | clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT; |
| 344 | |
| 345 | if (is_lvds) { |
| 346 | clock.p1 = |
| 347 | ffs((dpll & |
| 348 | DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS) >> |
| 349 | DPLL_FPA01_P1_POST_DIV_SHIFT); |
| 350 | clock.p2 = 14; |
| 351 | |
| 352 | if ((dpll & PLL_REF_INPUT_MASK) == |
| 353 | PLLB_REF_INPUT_SPREADSPECTRUMIN) { |
| 354 | /* XXX: might not be 66MHz */ |
Patrik Jakobsson | 1548060 | 2013-03-13 23:32:36 +0100 | [diff] [blame] | 355 | psb_intel_clock(66000, &clock); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 356 | } else |
Patrik Jakobsson | 1548060 | 2013-03-13 23:32:36 +0100 | [diff] [blame] | 357 | psb_intel_clock(48000, &clock); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 358 | } else { |
| 359 | if (dpll & PLL_P1_DIVIDE_BY_TWO) |
| 360 | clock.p1 = 2; |
| 361 | else { |
| 362 | clock.p1 = |
| 363 | ((dpll & |
| 364 | DPLL_FPA01_P1_POST_DIV_MASK_I830) >> |
| 365 | DPLL_FPA01_P1_POST_DIV_SHIFT) + 2; |
| 366 | } |
| 367 | if (dpll & PLL_P2_DIVIDE_BY_4) |
| 368 | clock.p2 = 4; |
| 369 | else |
| 370 | clock.p2 = 2; |
| 371 | |
Patrik Jakobsson | 1548060 | 2013-03-13 23:32:36 +0100 | [diff] [blame] | 372 | psb_intel_clock(48000, &clock); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 373 | } |
| 374 | |
| 375 | /* XXX: It would be nice to validate the clocks, but we can't reuse |
| 376 | * i830PllIsValid() because it relies on the xf86_config connector |
| 377 | * configuration being accurate, which it isn't necessarily. |
| 378 | */ |
| 379 | |
| 380 | return clock.dot; |
| 381 | } |
| 382 | |
| 383 | /** Returns the currently programmed mode of the given pipe. */ |
| 384 | struct drm_display_mode *psb_intel_crtc_mode_get(struct drm_device *dev, |
| 385 | struct drm_crtc *crtc) |
| 386 | { |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 387 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
| 388 | int pipe = gma_crtc->pipe; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 389 | struct drm_display_mode *mode; |
| 390 | int htot; |
| 391 | int hsync; |
| 392 | int vtot; |
| 393 | int vsync; |
| 394 | struct drm_psb_private *dev_priv = dev->dev_private; |
Alan Cox | 6256304 | 2012-05-11 11:30:16 +0100 | [diff] [blame] | 395 | struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 396 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 397 | |
| 398 | if (gma_power_begin(dev, false)) { |
Alan Cox | 213a843 | 2012-05-11 11:31:22 +0100 | [diff] [blame] | 399 | htot = REG_READ(map->htotal); |
| 400 | hsync = REG_READ(map->hsync); |
| 401 | vtot = REG_READ(map->vtotal); |
| 402 | vsync = REG_READ(map->vsync); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 403 | gma_power_end(dev); |
| 404 | } else { |
Alan Cox | 6256304 | 2012-05-11 11:30:16 +0100 | [diff] [blame] | 405 | htot = p->htotal; |
| 406 | hsync = p->hsync; |
| 407 | vtot = p->vtotal; |
| 408 | vsync = p->vsync; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 409 | } |
| 410 | |
| 411 | mode = kzalloc(sizeof(*mode), GFP_KERNEL); |
| 412 | if (!mode) |
| 413 | return NULL; |
| 414 | |
| 415 | mode->clock = psb_intel_crtc_clock_get(dev, crtc); |
| 416 | mode->hdisplay = (htot & 0xffff) + 1; |
| 417 | mode->htotal = ((htot & 0xffff0000) >> 16) + 1; |
| 418 | mode->hsync_start = (hsync & 0xffff) + 1; |
| 419 | mode->hsync_end = ((hsync & 0xffff0000) >> 16) + 1; |
| 420 | mode->vdisplay = (vtot & 0xffff) + 1; |
| 421 | mode->vtotal = ((vtot & 0xffff0000) >> 16) + 1; |
| 422 | mode->vsync_start = (vsync & 0xffff) + 1; |
| 423 | mode->vsync_end = ((vsync & 0xffff0000) >> 16) + 1; |
| 424 | |
| 425 | drm_mode_set_name(mode); |
| 426 | drm_mode_set_crtcinfo(mode, 0); |
| 427 | |
| 428 | return mode; |
| 429 | } |
| 430 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 431 | const struct drm_crtc_helper_funcs psb_intel_helper_funcs = { |
Patrik Jakobsson | 42568dd | 2013-07-10 18:44:25 +0200 | [diff] [blame] | 432 | .dpms = gma_crtc_dpms, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 433 | .mode_set = psb_intel_crtc_mode_set, |
Patrik Jakobsson | 00b1fe7 | 2013-07-10 18:37:03 +0200 | [diff] [blame] | 434 | .mode_set_base = gma_pipe_set_base, |
Patrik Jakobsson | 4855177 | 2013-07-10 17:40:54 +0200 | [diff] [blame] | 435 | .prepare = gma_crtc_prepare, |
| 436 | .commit = gma_crtc_commit, |
| 437 | .disable = gma_crtc_disable, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 438 | }; |
| 439 | |
| 440 | const struct drm_crtc_funcs psb_intel_crtc_funcs = { |
Patrik Jakobsson | 561573b | 2013-07-10 23:48:13 +0200 | [diff] [blame] | 441 | .cursor_set = gma_crtc_cursor_set, |
| 442 | .cursor_move = gma_crtc_cursor_move, |
Patrik Jakobsson | 6443ea1 | 2013-07-10 18:39:58 +0200 | [diff] [blame] | 443 | .gamma_set = gma_crtc_gamma_set, |
Patrik Jakobsson | 43a8302 | 2013-07-12 15:41:36 +0200 | [diff] [blame] | 444 | .set_config = gma_crtc_set_config, |
Patrik Jakobsson | b1255b8 | 2013-07-10 23:24:22 +0200 | [diff] [blame] | 445 | .destroy = gma_crtc_destroy, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 446 | }; |
| 447 | |
Patrik Jakobsson | 7f67c06 | 2013-07-02 17:07:59 +0200 | [diff] [blame] | 448 | const struct gma_clock_funcs psb_clock_funcs = { |
| 449 | .clock = psb_intel_clock, |
| 450 | .limit = psb_intel_limit, |
| 451 | .pll_is_valid = gma_pll_is_valid, |
| 452 | }; |
| 453 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 454 | /* |
| 455 | * Set the default value of cursor control and base register |
| 456 | * to zero. This is a workaround for h/w defect on Oaktrail |
| 457 | */ |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 458 | static void psb_intel_cursor_init(struct drm_device *dev, |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 459 | struct gma_crtc *gma_crtc) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 460 | { |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 461 | struct drm_psb_private *dev_priv = dev->dev_private; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 462 | u32 control[3] = { CURACNTR, CURBCNTR, CURCCNTR }; |
| 463 | u32 base[3] = { CURABASE, CURBBASE, CURCBASE }; |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 464 | struct gtt_range *cursor_gt; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 465 | |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 466 | if (dev_priv->ops->cursor_needs_phys) { |
| 467 | /* Allocate 4 pages of stolen mem for a hardware cursor. That |
| 468 | * is enough for the 64 x 64 ARGB cursors we support. |
| 469 | */ |
Patrik Jakobsson | c269c68 | 2014-01-06 02:39:10 +0100 | [diff] [blame] | 470 | cursor_gt = psb_gtt_alloc_range(dev, 4 * PAGE_SIZE, "cursor", 1, |
| 471 | PAGE_SIZE); |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 472 | if (!cursor_gt) { |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 473 | gma_crtc->cursor_gt = NULL; |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 474 | goto out; |
| 475 | } |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 476 | gma_crtc->cursor_gt = cursor_gt; |
| 477 | gma_crtc->cursor_addr = dev_priv->stolen_base + |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 478 | cursor_gt->offset; |
| 479 | } else { |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 480 | gma_crtc->cursor_gt = NULL; |
Patrik Jakobsson | bc79482 | 2012-05-21 15:27:30 +0100 | [diff] [blame] | 481 | } |
| 482 | |
| 483 | out: |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 484 | REG_WRITE(control[gma_crtc->pipe], 0); |
| 485 | REG_WRITE(base[gma_crtc->pipe], 0); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 486 | } |
| 487 | |
| 488 | void psb_intel_crtc_init(struct drm_device *dev, int pipe, |
| 489 | struct psb_intel_mode_device *mode_dev) |
| 490 | { |
| 491 | struct drm_psb_private *dev_priv = dev->dev_private; |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 492 | struct gma_crtc *gma_crtc; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 493 | int i; |
| 494 | uint16_t *r_base, *g_base, *b_base; |
| 495 | |
| 496 | /* We allocate a extra array of drm_connector pointers |
| 497 | * for fbdev after the crtc */ |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 498 | gma_crtc = kzalloc(sizeof(struct gma_crtc) + |
| 499 | (INTELFB_CONN_LIMIT * sizeof(struct drm_connector *)), |
| 500 | GFP_KERNEL); |
| 501 | if (gma_crtc == NULL) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 502 | return; |
| 503 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 504 | gma_crtc->crtc_state = |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 505 | kzalloc(sizeof(struct psb_intel_crtc_state), GFP_KERNEL); |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 506 | if (!gma_crtc->crtc_state) { |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 507 | dev_err(dev->dev, "Crtc state error: No memory\n"); |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 508 | kfree(gma_crtc); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 509 | return; |
| 510 | } |
| 511 | |
| 512 | /* Set the CRTC operations from the chip specific data */ |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 513 | drm_crtc_init(dev, &gma_crtc->base, dev_priv->ops->crtc_funcs); |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 514 | |
Patrik Jakobsson | 5ea75e0 | 2013-06-30 21:39:00 +0200 | [diff] [blame] | 515 | /* Set the CRTC clock functions from chip specific data */ |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 516 | gma_crtc->clock_funcs = dev_priv->ops->clock_funcs; |
Patrik Jakobsson | 5ea75e0 | 2013-06-30 21:39:00 +0200 | [diff] [blame] | 517 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 518 | drm_mode_crtc_set_gamma_size(&gma_crtc->base, 256); |
| 519 | gma_crtc->pipe = pipe; |
| 520 | gma_crtc->plane = pipe; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 521 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 522 | r_base = gma_crtc->base.gamma_store; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 523 | g_base = r_base + 256; |
| 524 | b_base = g_base + 256; |
| 525 | for (i = 0; i < 256; i++) { |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 526 | gma_crtc->lut_r[i] = i; |
| 527 | gma_crtc->lut_g[i] = i; |
| 528 | gma_crtc->lut_b[i] = i; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 529 | r_base[i] = i << 8; |
| 530 | g_base[i] = i << 8; |
| 531 | b_base[i] = i << 8; |
| 532 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 533 | gma_crtc->lut_adj[i] = 0; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 534 | } |
| 535 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 536 | gma_crtc->mode_dev = mode_dev; |
| 537 | gma_crtc->cursor_addr = 0; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 538 | |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 539 | drm_crtc_helper_add(&gma_crtc->base, |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 540 | dev_priv->ops->crtc_helper); |
| 541 | |
| 542 | /* Setup the array of drm_connector pointer array */ |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 543 | gma_crtc->mode_set.crtc = &gma_crtc->base; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 544 | BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) || |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 545 | dev_priv->plane_to_crtc_mapping[gma_crtc->plane] != NULL); |
| 546 | dev_priv->plane_to_crtc_mapping[gma_crtc->plane] = &gma_crtc->base; |
| 547 | dev_priv->pipe_to_crtc_mapping[gma_crtc->pipe] = &gma_crtc->base; |
| 548 | gma_crtc->mode_set.connectors = (struct drm_connector **)(gma_crtc + 1); |
| 549 | gma_crtc->mode_set.num_connectors = 0; |
| 550 | psb_intel_cursor_init(dev, gma_crtc); |
Forest Bond | 4a68a74 | 2012-08-13 16:31:24 +0000 | [diff] [blame] | 551 | |
| 552 | /* Set to true so that the pipe is forced off on initial config. */ |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 553 | gma_crtc->active = true; |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 554 | } |
| 555 | |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 556 | struct drm_crtc *psb_intel_get_crtc_from_pipe(struct drm_device *dev, int pipe) |
| 557 | { |
| 558 | struct drm_crtc *crtc = NULL; |
| 559 | |
| 560 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
Patrik Jakobsson | 6306865 | 2013-07-22 01:31:23 +0200 | [diff] [blame] | 561 | struct gma_crtc *gma_crtc = to_gma_crtc(crtc); |
| 562 | if (gma_crtc->pipe == pipe) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 563 | break; |
| 564 | } |
| 565 | return crtc; |
| 566 | } |
| 567 | |
Patrik Jakobsson | a3d5d75 | 2013-07-22 17:05:25 +0200 | [diff] [blame] | 568 | int gma_connector_clones(struct drm_device *dev, int type_mask) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 569 | { |
| 570 | int index_mask = 0; |
| 571 | struct drm_connector *connector; |
| 572 | int entry = 0; |
| 573 | |
| 574 | list_for_each_entry(connector, &dev->mode_config.connector_list, |
| 575 | head) { |
Patrik Jakobsson | 367e440 | 2013-07-22 17:45:26 +0200 | [diff] [blame] | 576 | struct gma_encoder *gma_encoder = gma_attached_encoder(connector); |
| 577 | if (type_mask & (1 << gma_encoder->type)) |
Alan Cox | 89c7813 | 2011-11-03 18:22:15 +0000 | [diff] [blame] | 578 | index_mask |= (1 << entry); |
| 579 | entry++; |
| 580 | } |
| 581 | return index_mask; |
| 582 | } |