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