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