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