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 this = &videomode_list[i]; 81 mref = this->dot_clock * 1000 / (this->htotal * this->vtotal); 82 diff = abs(mref - refresh); 83 if ((this->hdisplay != width) || (this->vdisplay != height)) 84 continue; 85 DPRINTF("%s in %d hz, diff %d\n", this->name, mref, diff); 86 if (best != NULL) { 87 if (diff < closest) { 88 best = this; 89 closest = diff; 90 } 91 } else { 92 best = this; 93 closest = diff; 94 } 95 } 96 if (best != NULL) 97 DPRINTF("found %s %d\n", best->name, best->dot_clock); 98 99 return best; 100 } 101 102 static inline void 103 swap_modes(struct videomode *left, struct videomode *right) 104 { 105 struct videomode temp; 106 107 temp = *left; 108 *left = *right; 109 *right = temp; 110 } 111 112 /* 113 * Sort modes by refresh rate, aspect ratio (*), then resolution. 114 * Preferred mode or largest mode is first in the list and other modes 115 * are sorted on closest match to that mode. 116 * (*) Note that the aspect ratio calculation treats "close" aspect ratios 117 * (within 12.5%) as the same for this purpose. 118 */ 119 #define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) 120 void 121 sort_modes(struct videomode *modes, struct videomode **preferred, int nmodes) 122 { 123 int aspect, refresh, hbest, vbest, abest, atemp, rbest, rtemp; 124 int i, j; 125 struct videomode *mtemp = NULL; 126 127 if (nmodes < 2) 128 return; 129 130 if (*preferred != NULL) { 131 /* Put the preferred mode first in the list */ 132 aspect = (*preferred)->hdisplay * 100 / (*preferred)->vdisplay; 133 refresh = DIVIDE(DIVIDE((*preferred)->dot_clock * 1000, 134 (*preferred)->htotal), (*preferred)->vtotal); 135 if (*preferred != modes) { 136 swap_modes(*preferred, modes); 137 *preferred = modes; 138 } 139 } else { 140 /* 141 * Find the largest horizontal and vertical mode and put that 142 * first in the list. Preferred refresh rate is taken from 143 * the first mode of this size. 144 */ 145 hbest = 0; 146 vbest = 0; 147 for (i = 0; i < nmodes; i++) { 148 if (modes[i].hdisplay > hbest) { 149 hbest = modes[i].hdisplay; 150 vbest = modes[i].vdisplay; 151 mtemp = &modes[i]; 152 } else if (modes[i].hdisplay == hbest && 153 modes[i].vdisplay > vbest) { 154 vbest = modes[i].vdisplay; 155 mtemp = &modes[i]; 156 } 157 } 158 aspect = mtemp->hdisplay * 100 / mtemp->vdisplay; 159 refresh = DIVIDE(DIVIDE(mtemp->dot_clock * 1000, 160 mtemp->htotal), mtemp->vtotal); 161 if (mtemp != modes) 162 swap_modes(mtemp, modes); 163 } 164 165 /* Sort other modes by refresh rate, aspect ratio, then resolution */ 166 for (j = 1; j < nmodes - 1; j++) { 167 rbest = 1000; 168 abest = 1000; 169 hbest = 0; 170 vbest = 0; 171 for (i = j; i < nmodes; i++) { 172 rtemp = abs(refresh - 173 DIVIDE(DIVIDE(modes[i].dot_clock * 1000, 174 modes[i].htotal), modes[i].vtotal)); 175 atemp = (modes[i].hdisplay * 100 / modes[i].vdisplay); 176 if (rtemp < rbest) { 177 rbest = rtemp; 178 mtemp = &modes[i]; 179 } 180 if (rtemp == rbest) { 181 /* Treat "close" aspect ratios as identical */ 182 if (abs(abest - atemp) > (abest / 8) && 183 abs(aspect - atemp) < abs(aspect - abest)) { 184 abest = atemp; 185 mtemp = &modes[i]; 186 } 187 if (atemp == abest || 188 abs(abest - atemp) <= (abest / 8)) { 189 if (modes[i].hdisplay > hbest) { 190 hbest = modes[i].hdisplay; 191 mtemp = &modes[i]; 192 } 193 if (modes[i].hdisplay == hbest && 194 modes[i].vdisplay > vbest) { 195 vbest = modes[i].vdisplay; 196 mtemp = &modes[i]; 197 } 198 } 199 } 200 } 201 if (mtemp != &modes[j]) 202 swap_modes(mtemp, &modes[j]); 203 } 204 } 205