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