1 /* $NetBSD: pickmode.c,v 1.3 2011/04/09 18:22:31 jdc Exp $ */ 2 3 /*- 4 * Copyright (c) 2006 The NetBSD Foundation 5 * All rights reserved. 6 * 7 * this code was contributed to The NetBSD Foundation by Michael Lorenz 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS 19 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE NETBSD FOUNDATION BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 24 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 26 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/cdefs.h> 32 #include <sys/param.h> 33 #include <sys/libkern.h> 34 #include <dev/videomode/videomode.h> 35 #include "opt_videomode.h" 36 37 #ifdef PICKMODE_DEBUG 38 #define DPRINTF printf 39 #else 40 #define DPRINTF while (0) printf 41 #endif 42 43 const struct videomode * 44 pick_mode_by_dotclock(int width, int height, int dotclock) 45 { 46 const struct videomode *this, *best = NULL; 47 int i; 48 49 DPRINTF("%s: looking for %d x %d at up to %d kHz\n", __func__, width, 50 height, dotclock); 51 for (i = 0; i < videomode_count; i++) { 52 this = &videomode_list[i]; 53 if ((this->hdisplay != width) || (this->vdisplay != height) || 54 (this->dot_clock > dotclock)) 55 continue; 56 if (best != NULL) { 57 if (this->dot_clock > best->dot_clock) 58 best = this; 59 } else 60 best = this; 61 } 62 if (best != NULL) 63 DPRINTF("found %s\n", best->name); 64 65 return best; 66 } 67 68 const struct videomode * 69 pick_mode_by_ref(int width, int height, int refresh) 70 { 71 const struct videomode *this, *best = NULL; 72 int mref, closest = 1000, i, diff; 73 74 DPRINTF("%s: looking for %d x %d at up to %d Hz\n", __func__, width, 75 height, refresh); 76 for (i = 0; i < videomode_count; i++) { 77 this = &videomode_list[i]; 78 mref = this->dot_clock * 1000 / (this->htotal * this->vtotal); 79 diff = abs(mref - refresh); 80 if ((this->hdisplay != width) || (this->vdisplay != height)) 81 continue; 82 DPRINTF("%s in %d hz, diff %d\n", this->name, mref, diff); 83 if (best != NULL) { 84 if (diff < closest) { 85 best = this; 86 closest = diff; 87 } 88 } else { 89 best = this; 90 closest = diff; 91 } 92 } 93 if (best != NULL) 94 DPRINTF("found %s %d\n", best->name, best->dot_clock); 95 96 return best; 97 } 98 99 static inline void 100 swap_modes(struct videomode *left, struct videomode *right) 101 { 102 struct videomode temp; 103 104 temp = *left; 105 *left = *right; 106 *right = temp; 107 } 108 109 /* 110 * Sort modes by refresh rate, aspect ratio (*), then resolution. 111 * Preferred mode or largest mode is first in the list and other modes 112 * are sorted on closest match to that mode. 113 * (*) Note that the aspect ratio calculation treats "close" aspect ratios 114 * (within 12.5%) as the same for this purpose. 115 */ 116 #define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) 117 void 118 sort_modes(struct videomode *modes, struct videomode **preferred, int nmodes) 119 { 120 int aspect, refresh, hbest, vbest, abest, atemp, rbest, rtemp; 121 int i, j; 122 struct videomode *mtemp = NULL; 123 124 if (nmodes < 2) 125 return; 126 127 if (*preferred != NULL) { 128 /* Put the preferred mode first in the list */ 129 aspect = (*preferred)->hdisplay * 100 / (*preferred)->vdisplay; 130 refresh = DIVIDE(DIVIDE((*preferred)->dot_clock * 1000, 131 (*preferred)->htotal), (*preferred)->vtotal); 132 if (*preferred != modes) { 133 swap_modes(*preferred, modes); 134 *preferred = modes; 135 } 136 } else { 137 /* 138 * Find the largest horizontal and vertical mode and put that 139 * first in the list. Preferred refresh rate is taken from 140 * the first mode of this size. 141 */ 142 hbest = 0; 143 vbest = 0; 144 for (i = 0; i < nmodes; i++) { 145 if (modes[i].hdisplay > hbest) { 146 hbest = modes[i].hdisplay; 147 vbest = modes[i].vdisplay; 148 mtemp = &modes[i]; 149 } else if (modes[i].hdisplay == hbest && 150 modes[i].vdisplay > vbest) { 151 vbest = modes[i].vdisplay; 152 mtemp = &modes[i]; 153 } 154 } 155 aspect = mtemp->hdisplay * 100 / mtemp->vdisplay; 156 refresh = DIVIDE(DIVIDE(mtemp->dot_clock * 1000, 157 mtemp->htotal), mtemp->vtotal); 158 if (mtemp != modes) 159 swap_modes(mtemp, modes); 160 } 161 162 /* Sort other modes by refresh rate, aspect ratio, then resolution */ 163 for (j = 1; j < nmodes - 1; j++) { 164 rbest = 1000; 165 abest = 1000; 166 hbest = 0; 167 vbest = 0; 168 for (i = j; i < nmodes; i++) { 169 rtemp = abs(refresh - 170 DIVIDE(DIVIDE(modes[i].dot_clock * 1000, 171 modes[i].htotal), modes[i].vtotal)); 172 atemp = (modes[i].hdisplay * 100 / modes[i].vdisplay); 173 if (rtemp < rbest) { 174 rbest = rtemp; 175 mtemp = &modes[i]; 176 } 177 if (rtemp == rbest) { 178 /* Treat "close" aspect ratios as identical */ 179 if (abs(abest - atemp) > (abest / 8) && 180 abs(aspect - atemp) < abs(aspect - abest)) { 181 abest = atemp; 182 mtemp = &modes[i]; 183 } 184 if (atemp == abest || 185 abs(abest - atemp) <= (abest / 8)) { 186 if (modes[i].hdisplay > hbest) { 187 hbest = modes[i].hdisplay; 188 mtemp = &modes[i]; 189 } 190 if (modes[i].hdisplay == hbest && 191 modes[i].vdisplay > vbest) { 192 vbest = modes[i].vdisplay; 193 mtemp = &modes[i]; 194 } 195 } 196 } 197 } 198 if (mtemp != &modes[j]) 199 swap_modes(mtemp, &modes[j]); 200 } 201 } 202