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 __FBSDID("$FreeBSD$"); 33 34 #include <sys/param.h> 35 #include <sys/libkern.h> 36 #include <dev/videomode/videomode.h> 37 #include "opt_videomode.h" 38 39 #ifdef PICKMODE_DEBUG 40 #define DPRINTF printf 41 #else 42 #define DPRINTF while (0) printf 43 #endif 44 45 const struct videomode * 46 pick_mode_by_dotclock(int width, int height, int dotclock) 47 { 48 const struct videomode *this, *best = NULL; 49 int i; 50 51 DPRINTF("%s: looking for %d x %d at up to %d kHz\n", __func__, width, 52 height, dotclock); 53 for (i = 0; i < videomode_count; i++) { 54 this = &videomode_list[i]; 55 if ((this->hdisplay != width) || (this->vdisplay != height) || 56 (this->dot_clock > dotclock)) 57 continue; 58 if (best != NULL) { 59 if (this->dot_clock > best->dot_clock) 60 best = this; 61 } else 62 best = this; 63 } 64 if (best != NULL) 65 DPRINTF("found %s\n", best->name); 66 67 return best; 68 } 69 70 const struct videomode * 71 pick_mode_by_ref(int width, int height, int refresh) 72 { 73 const struct videomode *this, *best = NULL; 74 int mref, closest = 1000, i, diff; 75 76 DPRINTF("%s: looking for %d x %d at up to %d Hz\n", __func__, width, 77 height, refresh); 78 for (i = 0; i < videomode_count; i++) { 79 this = &videomode_list[i]; 80 mref = this->dot_clock * 1000 / (this->htotal * this->vtotal); 81 diff = abs(mref - refresh); 82 if ((this->hdisplay != width) || (this->vdisplay != height)) 83 continue; 84 DPRINTF("%s in %d hz, diff %d\n", this->name, mref, diff); 85 if (best != NULL) { 86 if (diff < closest) { 87 best = this; 88 closest = diff; 89 } 90 } else { 91 best = this; 92 closest = diff; 93 } 94 } 95 if (best != NULL) 96 DPRINTF("found %s %d\n", best->name, best->dot_clock); 97 98 return best; 99 } 100 101 static inline void 102 swap_modes(struct videomode *left, struct videomode *right) 103 { 104 struct videomode temp; 105 106 temp = *left; 107 *left = *right; 108 *right = temp; 109 } 110 111 /* 112 * Sort modes by refresh rate, aspect ratio (*), then resolution. 113 * Preferred mode or largest mode is first in the list and other modes 114 * are sorted on closest match to that mode. 115 * (*) Note that the aspect ratio calculation treats "close" aspect ratios 116 * (within 12.5%) as the same for this purpose. 117 */ 118 #define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) 119 void 120 sort_modes(struct videomode *modes, struct videomode **preferred, int nmodes) 121 { 122 int aspect, refresh, hbest, vbest, abest, atemp, rbest, rtemp; 123 int i, j; 124 struct videomode *mtemp = NULL; 125 126 if (nmodes < 2) 127 return; 128 129 if (*preferred != NULL) { 130 /* Put the preferred mode first in the list */ 131 aspect = (*preferred)->hdisplay * 100 / (*preferred)->vdisplay; 132 refresh = DIVIDE(DIVIDE((*preferred)->dot_clock * 1000, 133 (*preferred)->htotal), (*preferred)->vtotal); 134 if (*preferred != modes) { 135 swap_modes(*preferred, modes); 136 *preferred = modes; 137 } 138 } else { 139 /* 140 * Find the largest horizontal and vertical mode and put that 141 * first in the list. Preferred refresh rate is taken from 142 * the first mode of this size. 143 */ 144 hbest = 0; 145 vbest = 0; 146 for (i = 0; i < nmodes; i++) { 147 if (modes[i].hdisplay > hbest) { 148 hbest = modes[i].hdisplay; 149 vbest = modes[i].vdisplay; 150 mtemp = &modes[i]; 151 } else if (modes[i].hdisplay == hbest && 152 modes[i].vdisplay > vbest) { 153 vbest = modes[i].vdisplay; 154 mtemp = &modes[i]; 155 } 156 } 157 aspect = mtemp->hdisplay * 100 / mtemp->vdisplay; 158 refresh = DIVIDE(DIVIDE(mtemp->dot_clock * 1000, 159 mtemp->htotal), mtemp->vtotal); 160 if (mtemp != modes) 161 swap_modes(mtemp, modes); 162 } 163 164 /* Sort other modes by refresh rate, aspect ratio, then resolution */ 165 for (j = 1; j < nmodes - 1; j++) { 166 rbest = 1000; 167 abest = 1000; 168 hbest = 0; 169 vbest = 0; 170 for (i = j; i < nmodes; i++) { 171 rtemp = abs(refresh - 172 DIVIDE(DIVIDE(modes[i].dot_clock * 1000, 173 modes[i].htotal), modes[i].vtotal)); 174 atemp = (modes[i].hdisplay * 100 / modes[i].vdisplay); 175 if (rtemp < rbest) { 176 rbest = rtemp; 177 mtemp = &modes[i]; 178 } 179 if (rtemp == rbest) { 180 /* Treat "close" aspect ratios as identical */ 181 if (abs(abest - atemp) > (abest / 8) && 182 abs(aspect - atemp) < abs(aspect - abest)) { 183 abest = atemp; 184 mtemp = &modes[i]; 185 } 186 if (atemp == abest || 187 abs(abest - atemp) <= (abest / 8)) { 188 if (modes[i].hdisplay > hbest) { 189 hbest = modes[i].hdisplay; 190 mtemp = &modes[i]; 191 } 192 if (modes[i].hdisplay == hbest && 193 modes[i].vdisplay > vbest) { 194 vbest = modes[i].vdisplay; 195 mtemp = &modes[i]; 196 } 197 } 198 } 199 } 200 if (mtemp != &modes[j]) 201 swap_modes(mtemp, &modes[j]); 202 } 203 } 204