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