diff options
| author | Patrik Jakobsson <patrik.r.jakobsson@gmail.com> | 2013-07-02 11:07:59 -0400 |
|---|---|---|
| committer | Patrik Jakobsson <patrik.r.jakobsson@gmail.com> | 2013-07-23 19:47:18 -0400 |
| commit | 7f67c06721641df12ed68249218d1c2118517f78 (patch) | |
| tree | 83119496ad04e8bc8b4dc034580ad4e1386e601a | |
| parent | fe477cc1b09ecd957c8c201b4f9c84e9d03621d4 (diff) | |
drm/gma500/psb: Make use of generic clock code
Add chip specific callbacks for the generic and non-generic clock
calculation code. Also remove as much dupilicated code as possible.
Signed-off-by: Patrik Jakobsson <patrik.r.jakobsson@gmail.com>
| -rw-r--r-- | drivers/gpu/drm/gma500/psb_device.c | 3 | ||||
| -rw-r--r-- | drivers/gpu/drm/gma500/psb_device.h | 24 | ||||
| -rw-r--r-- | drivers/gpu/drm/gma500/psb_intel_display.c | 189 | ||||
| -rw-r--r-- | drivers/gpu/drm/gma500/psb_intel_display.h | 2 |
4 files changed, 51 insertions, 167 deletions
diff --git a/drivers/gpu/drm/gma500/psb_device.c b/drivers/gpu/drm/gma500/psb_device.c index f6f534b4197e..697678619bd1 100644 --- a/drivers/gpu/drm/gma500/psb_device.c +++ b/drivers/gpu/drm/gma500/psb_device.c | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | #include "psb_reg.h" | 25 | #include "psb_reg.h" |
| 26 | #include "psb_intel_reg.h" | 26 | #include "psb_intel_reg.h" |
| 27 | #include "intel_bios.h" | 27 | #include "intel_bios.h" |
| 28 | 28 | #include "psb_device.h" | |
| 29 | 29 | ||
| 30 | static int psb_output_init(struct drm_device *dev) | 30 | static int psb_output_init(struct drm_device *dev) |
| 31 | { | 31 | { |
| @@ -380,6 +380,7 @@ const struct psb_ops psb_chip_ops = { | |||
| 380 | 380 | ||
| 381 | .crtc_helper = &psb_intel_helper_funcs, | 381 | .crtc_helper = &psb_intel_helper_funcs, |
| 382 | .crtc_funcs = &psb_intel_crtc_funcs, | 382 | .crtc_funcs = &psb_intel_crtc_funcs, |
| 383 | .clock_funcs = &psb_clock_funcs, | ||
| 383 | 384 | ||
| 384 | .output_init = psb_output_init, | 385 | .output_init = psb_output_init, |
| 385 | 386 | ||
diff --git a/drivers/gpu/drm/gma500/psb_device.h b/drivers/gpu/drm/gma500/psb_device.h new file mode 100644 index 000000000000..35e304c7f85a --- /dev/null +++ b/drivers/gpu/drm/gma500/psb_device.h | |||
| @@ -0,0 +1,24 @@ | |||
| 1 | /* | ||
| 2 | * Copyright © 2013 Patrik Jakobsson | ||
| 3 | * Copyright © 2011 Intel Corporation | ||
| 4 | * | ||
| 5 | * This program is free software; you can redistribute it and/or modify it | ||
| 6 | * under the terms and conditions of the GNU General Public License, | ||
| 7 | * version 2, as published by the Free Software Foundation. | ||
| 8 | * | ||
| 9 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
| 10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
| 11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
| 12 | * more details. | ||
| 13 | * | ||
| 14 | * You should have received a copy of the GNU General Public License along with | ||
| 15 | * this program; if not, write to the Free Software Foundation, Inc., | ||
| 16 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
| 17 | */ | ||
| 18 | |||
| 19 | #ifndef _PSB_DEVICE_H_ | ||
| 20 | #define _PSB_DEVICE_H_ | ||
| 21 | |||
| 22 | extern const struct gma_clock_funcs psb_clock_funcs; | ||
| 23 | |||
| 24 | #endif | ||
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c index 0f1d069afa11..89be7a3632ef 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.c +++ b/drivers/gpu/drm/gma500/psb_intel_display.c | |||
| @@ -26,39 +26,13 @@ | |||
| 26 | #include "psb_drv.h" | 26 | #include "psb_drv.h" |
| 27 | #include "psb_intel_drv.h" | 27 | #include "psb_intel_drv.h" |
| 28 | #include "psb_intel_reg.h" | 28 | #include "psb_intel_reg.h" |
| 29 | #include "psb_intel_display.h" | 29 | #include "gma_display.h" |
| 30 | #include "power.h" | 30 | #include "power.h" |
| 31 | 31 | ||
| 32 | struct psb_intel_clock_t { | ||
| 33 | /* given values */ | ||
| 34 | int n; | ||
| 35 | int m1, m2; | ||
| 36 | int p1, p2; | ||
| 37 | /* derived values */ | ||
| 38 | int dot; | ||
| 39 | int vco; | ||
| 40 | int m; | ||
| 41 | int p; | ||
| 42 | }; | ||
| 43 | |||
| 44 | struct psb_intel_range_t { | ||
| 45 | int min, max; | ||
| 46 | }; | ||
| 47 | |||
| 48 | struct psb_intel_p2_t { | ||
| 49 | int dot_limit; | ||
| 50 | int p2_slow, p2_fast; | ||
| 51 | }; | ||
| 52 | |||
| 53 | struct psb_intel_limit_t { | ||
| 54 | struct psb_intel_range_t dot, vco, n, m, m1, m2, p, p1; | ||
| 55 | struct psb_intel_p2_t p2; | ||
| 56 | }; | ||
| 57 | |||
| 58 | #define INTEL_LIMIT_I9XX_SDVO_DAC 0 | 32 | #define INTEL_LIMIT_I9XX_SDVO_DAC 0 |
| 59 | #define INTEL_LIMIT_I9XX_LVDS 1 | 33 | #define INTEL_LIMIT_I9XX_LVDS 1 |
| 60 | 34 | ||
| 61 | static const struct psb_intel_limit_t psb_intel_limits[] = { | 35 | static const struct gma_limit_t psb_intel_limits[] = { |
| 62 | { /* INTEL_LIMIT_I9XX_SDVO_DAC */ | 36 | { /* INTEL_LIMIT_I9XX_SDVO_DAC */ |
| 63 | .dot = {.min = 20000, .max = 400000}, | 37 | .dot = {.min = 20000, .max = 400000}, |
| 64 | .vco = {.min = 1400000, .max = 2800000}, | 38 | .vco = {.min = 1400000, .max = 2800000}, |
| @@ -68,8 +42,8 @@ static const struct psb_intel_limit_t psb_intel_limits[] = { | |||
| 68 | .m2 = {.min = 3, .max = 7}, | 42 | .m2 = {.min = 3, .max = 7}, |
| 69 | .p = {.min = 5, .max = 80}, | 43 | .p = {.min = 5, .max = 80}, |
| 70 | .p1 = {.min = 1, .max = 8}, | 44 | .p1 = {.min = 1, .max = 8}, |
| 71 | .p2 = {.dot_limit = 200000, | 45 | .p2 = {.dot_limit = 200000, .p2_slow = 10, .p2_fast = 5}, |
| 72 | .p2_slow = 10, .p2_fast = 5}, | 46 | .find_pll = gma_find_best_pll, |
| 73 | }, | 47 | }, |
| 74 | { /* INTEL_LIMIT_I9XX_LVDS */ | 48 | { /* INTEL_LIMIT_I9XX_LVDS */ |
| 75 | .dot = {.min = 20000, .max = 400000}, | 49 | .dot = {.min = 20000, .max = 400000}, |
| @@ -83,23 +57,24 @@ static const struct psb_intel_limit_t psb_intel_limits[] = { | |||
| 83 | /* The single-channel range is 25-112Mhz, and dual-channel | 57 | /* The single-channel range is 25-112Mhz, and dual-channel |
| 84 | * is 80-224Mhz. Prefer single channel as much as possible. | 58 | * is 80-224Mhz. Prefer single channel as much as possible. |
| 85 | */ | 59 | */ |
| 86 | .p2 = {.dot_limit = 112000, | 60 | .p2 = {.dot_limit = 112000, .p2_slow = 14, .p2_fast = 7}, |
| 87 | .p2_slow = 14, .p2_fast = 7}, | 61 | .find_pll = gma_find_best_pll, |
| 88 | }, | 62 | }, |
| 89 | }; | 63 | }; |
| 90 | 64 | ||
| 91 | static const struct psb_intel_limit_t *psb_intel_limit(struct drm_crtc *crtc) | 65 | static const struct gma_limit_t *psb_intel_limit(struct drm_crtc *crtc, |
| 66 | int refclk) | ||
| 92 | { | 67 | { |
| 93 | const struct psb_intel_limit_t *limit; | 68 | const struct gma_limit_t *limit; |
| 94 | 69 | ||
| 95 | if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) | 70 | if (gma_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) |
| 96 | limit = &psb_intel_limits[INTEL_LIMIT_I9XX_LVDS]; | 71 | limit = &psb_intel_limits[INTEL_LIMIT_I9XX_LVDS]; |
| 97 | else | 72 | else |
| 98 | limit = &psb_intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; | 73 | limit = &psb_intel_limits[INTEL_LIMIT_I9XX_SDVO_DAC]; |
| 99 | return limit; | 74 | return limit; |
| 100 | } | 75 | } |
| 101 | 76 | ||
| 102 | static void psb_intel_clock(int refclk, struct psb_intel_clock_t *clock) | 77 | static void psb_intel_clock(int refclk, struct gma_clock_t *clock) |
| 103 | { | 78 | { |
| 104 | clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); | 79 | clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2); |
| 105 | clock->p = clock->p1 * clock->p2; | 80 | clock->p = clock->p1 * clock->p2; |
| @@ -107,130 +82,6 @@ static void psb_intel_clock(int refclk, struct psb_intel_clock_t *clock) | |||
| 107 | clock->dot = clock->vco / clock->p; | 82 | clock->dot = clock->vco / clock->p; |
| 108 | } | 83 | } |
| 109 | 84 | ||
| 110 | /** | ||
| 111 | * Returns whether any output on the specified pipe is of the specified type | ||
| 112 | */ | ||
| 113 | bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type) | ||
| 114 | { | ||
| 115 | struct drm_device *dev = crtc->dev; | ||
| 116 | struct drm_mode_config *mode_config = &dev->mode_config; | ||
| 117 | struct drm_connector *l_entry; | ||
| 118 | |||
| 119 | list_for_each_entry(l_entry, &mode_config->connector_list, head) { | ||
| 120 | if (l_entry->encoder && l_entry->encoder->crtc == crtc) { | ||
| 121 | struct psb_intel_encoder *psb_intel_encoder = | ||
| 122 | psb_intel_attached_encoder(l_entry); | ||
| 123 | if (psb_intel_encoder->type == type) | ||
| 124 | return true; | ||
| 125 | } | ||
| 126 | } | ||
| 127 | return false; | ||
| 128 | } | ||
| 129 | |||
| 130 | #define INTELPllInvalid(s) { /* ErrorF (s) */; return false; } | ||
| 131 | /** | ||
| 132 | * Returns whether the given set of divisors are valid for a given refclk with | ||
| 133 | * the given connectors. | ||
| 134 | */ | ||
| 135 | |||
| 136 | static bool psb_intel_PLL_is_valid(struct drm_crtc *crtc, | ||
| 137 | struct psb_intel_clock_t *clock) | ||
| 138 | { | ||
| 139 | const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); | ||
| 140 | |||
| 141 | if (clock->p1 < limit->p1.min || limit->p1.max < clock->p1) | ||
| 142 | INTELPllInvalid("p1 out of range\n"); | ||
| 143 | if (clock->p < limit->p.min || limit->p.max < clock->p) | ||
| 144 | INTELPllInvalid("p out of range\n"); | ||
| 145 | if (clock->m2 < limit->m2.min || limit->m2.max < clock->m2) | ||
| 146 | INTELPllInvalid("m2 out of range\n"); | ||
| 147 | if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1) | ||
| 148 | INTELPllInvalid("m1 out of range\n"); | ||
| 149 | if (clock->m1 <= clock->m2) | ||
| 150 | INTELPllInvalid("m1 <= m2\n"); | ||
| 151 | if (clock->m < limit->m.min || limit->m.max < clock->m) | ||
| 152 | INTELPllInvalid("m out of range\n"); | ||
| 153 | if (clock->n < limit->n.min || limit->n.max < clock->n) | ||
| 154 | INTELPllInvalid("n out of range\n"); | ||
| 155 | if (clock->vco < limit->vco.min || limit->vco.max < clock->vco) | ||
| 156 | INTELPllInvalid("vco out of range\n"); | ||
| 157 | /* XXX: We may need to be checking "Dot clock" | ||
| 158 | * depending on the multiplier, connector, etc., | ||
| 159 | * rather than just a single range. | ||
| 160 | */ | ||
| 161 | if (clock->dot < limit->dot.min || limit->dot.max < clock->dot) | ||
| 162 | INTELPllInvalid("dot out of range\n"); | ||
| 163 | |||
| 164 | return true; | ||
| 165 | } | ||
| 166 | |||
| 167 | /** | ||
| 168 | * Returns a set of divisors for the desired target clock with the given | ||
| 169 | * refclk, or FALSE. The returned values represent the clock equation: | ||
| 170 | * reflck * (5 * (m1 + 2) + (m2 + 2)) / (n + 2) / p1 / p2. | ||
| 171 | */ | ||
| 172 | static bool psb_intel_find_best_PLL(struct drm_crtc *crtc, int target, | ||
| 173 | int refclk, | ||
| 174 | struct psb_intel_clock_t *best_clock) | ||
| 175 | { | ||
| 176 | struct drm_device *dev = crtc->dev; | ||
| 177 | struct psb_intel_clock_t clock; | ||
| 178 | const struct psb_intel_limit_t *limit = psb_intel_limit(crtc); | ||
| 179 | int err = target; | ||
| 180 | |||
| 181 | if (psb_intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS) && | ||
| 182 | (REG_READ(LVDS) & LVDS_PORT_EN) != 0) { | ||
| 183 | /* | ||
| 184 | * For LVDS, if the panel is on, just rely on its current | ||
| 185 | * settings for dual-channel. We haven't figured out how to | ||
| 186 | * reliably set up different single/dual channel state, if we | ||
| 187 | * even can. | ||
| 188 | */ | ||
| 189 | if ((REG_READ(LVDS) & LVDS_CLKB_POWER_MASK) == | ||
| 190 | LVDS_CLKB_POWER_UP) | ||
| 191 | clock.p2 = limit->p2.p2_fast; | ||
| 192 | else | ||
| 193 | clock.p2 = limit->p2.p2_slow; | ||
| 194 | } else { | ||
| 195 | if (target < limit->p2.dot_limit) | ||
| 196 | clock.p2 = limit->p2.p2_slow; | ||
| 197 | else | ||
| 198 | clock.p2 = limit->p2.p2_fast; | ||
| 199 | } | ||
| 200 | |||
| 201 | memset(best_clock, 0, sizeof(*best_clock)); | ||
| 202 | |||
| 203 | for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; | ||
| 204 | clock.m1++) { | ||
| 205 | for (clock.m2 = limit->m2.min; | ||
| 206 | clock.m2 < clock.m1 && clock.m2 <= limit->m2.max; | ||
| 207 | clock.m2++) { | ||
| 208 | for (clock.n = limit->n.min; | ||
| 209 | clock.n <= limit->n.max; clock.n++) { | ||
| 210 | for (clock.p1 = limit->p1.min; | ||
| 211 | clock.p1 <= limit->p1.max; | ||
| 212 | clock.p1++) { | ||
| 213 | int this_err; | ||
| 214 | |||
| 215 | psb_intel_clock(refclk, &clock); | ||
| 216 | |||
| 217 | if (!psb_intel_PLL_is_valid | ||
| 218 | (crtc, &clock)) | ||
| 219 | continue; | ||
| 220 | |||
| 221 | this_err = abs(clock.dot - target); | ||
| 222 | if (this_err < err) { | ||
| 223 | *best_clock = clock; | ||
| 224 | err = this_err; | ||
| 225 | } | ||
| 226 | } | ||
| 227 | } | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | return err != target; | ||
| 232 | } | ||
| 233 | |||
| 234 | void psb_intel_wait_for_vblank(struct drm_device *dev) | 85 | void psb_intel_wait_for_vblank(struct drm_device *dev) |
| 235 | { | 86 | { |
| 236 | /* Wait for 20ms, i.e. one cycle at 50hz. */ | 87 | /* Wait for 20ms, i.e. one cycle at 50hz. */ |
| @@ -484,12 +335,13 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, | |||
| 484 | int pipe = psb_intel_crtc->pipe; | 335 | int pipe = psb_intel_crtc->pipe; |
| 485 | const struct psb_offset *map = &dev_priv->regmap[pipe]; | 336 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
| 486 | int refclk; | 337 | int refclk; |
| 487 | struct psb_intel_clock_t clock; | 338 | struct gma_clock_t clock; |
| 488 | u32 dpll = 0, fp = 0, dspcntr, pipeconf; | 339 | u32 dpll = 0, fp = 0, dspcntr, pipeconf; |
| 489 | bool ok, is_sdvo = false; | 340 | bool ok, is_sdvo = false; |
| 490 | bool is_lvds = false, is_tv = false; | 341 | bool is_lvds = false, is_tv = false; |
| 491 | struct drm_mode_config *mode_config = &dev->mode_config; | 342 | struct drm_mode_config *mode_config = &dev->mode_config; |
| 492 | struct drm_connector *connector; | 343 | struct drm_connector *connector; |
| 344 | const struct gma_limit_t *limit; | ||
| 493 | 345 | ||
| 494 | /* No scan out no play */ | 346 | /* No scan out no play */ |
| 495 | if (crtc->fb == NULL) { | 347 | if (crtc->fb == NULL) { |
| @@ -520,10 +372,13 @@ static int psb_intel_crtc_mode_set(struct drm_crtc *crtc, | |||
| 520 | 372 | ||
| 521 | refclk = 96000; | 373 | refclk = 96000; |
| 522 | 374 | ||
| 523 | ok = psb_intel_find_best_PLL(crtc, adjusted_mode->clock, refclk, | 375 | limit = psb_intel_crtc->clock_funcs->limit(crtc, refclk); |
| 376 | |||
| 377 | ok = limit->find_pll(limit, crtc, adjusted_mode->clock, refclk, | ||
| 524 | &clock); | 378 | &clock); |
| 525 | if (!ok) { | 379 | if (!ok) { |
| 526 | dev_err(dev->dev, "Couldn't find PLL settings for mode!\n"); | 380 | DRM_ERROR("Couldn't find PLL settings for mode! target: %d, actual: %d", |
| 381 | adjusted_mode->clock, clock.dot); | ||
| 527 | return 0; | 382 | return 0; |
| 528 | } | 383 | } |
| 529 | 384 | ||
| @@ -1022,7 +877,7 @@ static int psb_intel_crtc_clock_get(struct drm_device *dev, | |||
| 1022 | const struct psb_offset *map = &dev_priv->regmap[pipe]; | 877 | const struct psb_offset *map = &dev_priv->regmap[pipe]; |
| 1023 | u32 dpll; | 878 | u32 dpll; |
| 1024 | u32 fp; | 879 | u32 fp; |
| 1025 | struct psb_intel_clock_t clock; | 880 | struct gma_clock_t clock; |
| 1026 | bool is_lvds; | 881 | bool is_lvds; |
| 1027 | struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; | 882 | struct psb_pipe *p = &dev_priv->regs.pipe[pipe]; |
| 1028 | 883 | ||
| @@ -1190,6 +1045,12 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = { | |||
| 1190 | .destroy = psb_intel_crtc_destroy, | 1045 | .destroy = psb_intel_crtc_destroy, |
| 1191 | }; | 1046 | }; |
| 1192 | 1047 | ||
| 1048 | const struct gma_clock_funcs psb_clock_funcs = { | ||
| 1049 | .clock = psb_intel_clock, | ||
| 1050 | .limit = psb_intel_limit, | ||
| 1051 | .pll_is_valid = gma_pll_is_valid, | ||
| 1052 | }; | ||
| 1053 | |||
| 1193 | /* | 1054 | /* |
| 1194 | * Set the default value of cursor control and base register | 1055 | * Set the default value of cursor control and base register |
| 1195 | * to zero. This is a workaround for h/w defect on Oaktrail | 1056 | * to zero. This is a workaround for h/w defect on Oaktrail |
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.h b/drivers/gpu/drm/gma500/psb_intel_display.h index 3724b971e91c..6e9007d86f72 100644 --- a/drivers/gpu/drm/gma500/psb_intel_display.h +++ b/drivers/gpu/drm/gma500/psb_intel_display.h | |||
| @@ -20,6 +20,4 @@ | |||
| 20 | #ifndef _INTEL_DISPLAY_H_ | 20 | #ifndef _INTEL_DISPLAY_H_ |
| 21 | #define _INTEL_DISPLAY_H_ | 21 | #define _INTEL_DISPLAY_H_ |
| 22 | 22 | ||
| 23 | bool psb_intel_pipe_has_type(struct drm_crtc *crtc, int type); | ||
| 24 | |||
| 25 | #endif | 23 | #endif |
