1278798Shselasky/* $NetBSD: pickmode.c,v 1.3 2011/04/09 18:22:31 jdc Exp $ */ 2278798Shselasky/* $FreeBSD$ */ 3278798Shselasky 4278798Shselasky/*- 5278798Shselasky * Copyright (c) 2006 The NetBSD Foundation 6278798Shselasky * All rights reserved. 7278798Shselasky * 8278798Shselasky * this code was contributed to The NetBSD Foundation by Michael Lorenz 9278798Shselasky * 10278798Shselasky * Redistribution and use in source and binary forms, with or without 11278798Shselasky * modification, are permitted provided that the following conditions 12278798Shselasky * are met: 13278798Shselasky * 1. Redistributions of source code must retain the above copyright 14278798Shselasky * notice, this list of conditions and the following disclaimer. 15278798Shselasky * 2. Redistributions in binary form must reproduce the above copyright 16278798Shselasky * notice, this list of conditions and the following disclaimer in the 17278798Shselasky * documentation and/or other materials provided with the distribution. 18278798Shselasky * 19278798Shselasky * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS 20278798Shselasky * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21278798Shselasky * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22278798Shselasky * ARE DISCLAIMED. IN NO EVENT SHALL THE NETBSD FOUNDATION BE LIABLE 23278798Shselasky * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24278798Shselasky * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 25278798Shselasky * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26278798Shselasky * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 27278798Shselasky * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28278798Shselasky * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29278798Shselasky * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30278798Shselasky */ 31278798Shselasky 32278798Shselasky#include <sys/cdefs.h> 33278798Shselasky__FBSDID("$FreeBSD$"); 34278798Shselasky 35278798Shselasky#include <sys/param.h> 36278798Shselasky#include <sys/libkern.h> 37278798Shselasky#include <dev/videomode/videomode.h> 38278798Shselasky#include "opt_videomode.h" 39278798Shselasky 40278798Shselasky#ifdef PICKMODE_DEBUG 41278798Shselasky#define DPRINTF printf 42278798Shselasky#else 43278798Shselasky#define DPRINTF while (0) printf 44278798Shselasky#endif 45278798Shselasky 46278798Shselaskyconst struct videomode * 47278798Shselaskypick_mode_by_dotclock(int width, int height, int dotclock) 48278798Shselasky{ 49278798Shselasky const struct videomode *this, *best = NULL; 50278798Shselasky int i; 51278798Shselasky 52278798Shselasky DPRINTF("%s: looking for %d x %d at up to %d kHz\n", __func__, width, 53278798Shselasky height, dotclock); 54278798Shselasky for (i = 0; i < videomode_count; i++) { 55278798Shselasky this = &videomode_list[i]; 56278798Shselasky if ((this->hdisplay != width) || (this->vdisplay != height) || 57278798Shselasky (this->dot_clock > dotclock)) 58278798Shselasky continue; 59278798Shselasky if (best != NULL) { 60278798Shselasky if (this->dot_clock > best->dot_clock) 61278798Shselasky best = this; 62278798Shselasky } else 63278798Shselasky best = this; 64278798Shselasky } 65278798Shselasky if (best != NULL) 66278798Shselasky DPRINTF("found %s\n", best->name); 67278798Shselasky 68278798Shselasky return best; 69278798Shselasky} 70278798Shselasky 71278798Shselaskyconst struct videomode * 72278798Shselaskypick_mode_by_ref(int width, int height, int refresh) 73278798Shselasky{ 74278798Shselasky const struct videomode *this, *best = NULL; 75278798Shselasky int mref, closest = 1000, i, diff; 76278798Shselasky 77278798Shselasky DPRINTF("%s: looking for %d x %d at up to %d Hz\n", __func__, width, 78278798Shselasky height, refresh); 79278798Shselasky for (i = 0; i < videomode_count; i++) { 80278798Shselasky 81278798Shselasky this = &videomode_list[i]; 82278798Shselasky mref = this->dot_clock * 1000 / (this->htotal * this->vtotal); 83278798Shselasky diff = abs(mref - refresh); 84278798Shselasky if ((this->hdisplay != width) || (this->vdisplay != height)) 85278798Shselasky continue; 86278798Shselasky DPRINTF("%s in %d hz, diff %d\n", this->name, mref, diff); 87278798Shselasky if (best != NULL) { 88278798Shselasky if (diff < closest) { 89278798Shselasky best = this; 90278798Shselasky closest = diff; 91278798Shselasky } 92278798Shselasky } else { 93278798Shselasky best = this; 94278798Shselasky closest = diff; 95278798Shselasky } 96278798Shselasky } 97278798Shselasky if (best != NULL) 98278798Shselasky DPRINTF("found %s %d\n", best->name, best->dot_clock); 99278798Shselasky 100278798Shselasky return best; 101278798Shselasky} 102278798Shselasky 103278798Shselaskystatic inline void 104278798Shselaskyswap_modes(struct videomode *left, struct videomode *right) 105278798Shselasky{ 106278798Shselasky struct videomode temp; 107278798Shselasky 108278798Shselasky temp = *left; 109278798Shselasky *left = *right; 110278798Shselasky *right = temp; 111278798Shselasky} 112278798Shselasky 113278798Shselasky/* 114278798Shselasky * Sort modes by refresh rate, aspect ratio (*), then resolution. 115278798Shselasky * Preferred mode or largest mode is first in the list and other modes 116278798Shselasky * are sorted on closest match to that mode. 117278798Shselasky * (*) Note that the aspect ratio calculation treats "close" aspect ratios 118278798Shselasky * (within 12.5%) as the same for this purpose. 119278798Shselasky */ 120278798Shselasky#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y)) 121278798Shselaskyvoid 122278798Shselaskysort_modes(struct videomode *modes, struct videomode **preferred, int nmodes) 123278798Shselasky{ 124278798Shselasky int aspect, refresh, hbest, vbest, abest, atemp, rbest, rtemp; 125278798Shselasky int i, j; 126278798Shselasky struct videomode *mtemp = NULL; 127278798Shselasky 128278798Shselasky if (nmodes < 2) 129278798Shselasky return; 130278798Shselasky 131278798Shselasky if (*preferred != NULL) { 132278798Shselasky /* Put the preferred mode first in the list */ 133278798Shselasky aspect = (*preferred)->hdisplay * 100 / (*preferred)->vdisplay; 134278798Shselasky refresh = DIVIDE(DIVIDE((*preferred)->dot_clock * 1000, 135278798Shselasky (*preferred)->htotal), (*preferred)->vtotal); 136278798Shselasky if (*preferred != modes) { 137278798Shselasky swap_modes(*preferred, modes); 138278798Shselasky *preferred = modes; 139278798Shselasky } 140278798Shselasky } else { 141278798Shselasky /* 142278798Shselasky * Find the largest horizontal and vertical mode and put that 143278798Shselasky * first in the list. Preferred refresh rate is taken from 144278798Shselasky * the first mode of this size. 145278798Shselasky */ 146278798Shselasky hbest = 0; 147278798Shselasky vbest = 0; 148278798Shselasky for (i = 0; i < nmodes; i++) { 149278798Shselasky if (modes[i].hdisplay > hbest) { 150278798Shselasky hbest = modes[i].hdisplay; 151278798Shselasky vbest = modes[i].vdisplay; 152278798Shselasky mtemp = &modes[i]; 153278798Shselasky } else if (modes[i].hdisplay == hbest && 154278798Shselasky modes[i].vdisplay > vbest) { 155278798Shselasky vbest = modes[i].vdisplay; 156278798Shselasky mtemp = &modes[i]; 157278798Shselasky } 158278798Shselasky } 159278798Shselasky aspect = mtemp->hdisplay * 100 / mtemp->vdisplay; 160278798Shselasky refresh = DIVIDE(DIVIDE(mtemp->dot_clock * 1000, 161278798Shselasky mtemp->htotal), mtemp->vtotal); 162278798Shselasky if (mtemp != modes) 163278798Shselasky swap_modes(mtemp, modes); 164278798Shselasky } 165278798Shselasky 166278798Shselasky /* Sort other modes by refresh rate, aspect ratio, then resolution */ 167278798Shselasky for (j = 1; j < nmodes - 1; j++) { 168278798Shselasky rbest = 1000; 169278798Shselasky abest = 1000; 170278798Shselasky hbest = 0; 171278798Shselasky vbest = 0; 172278798Shselasky for (i = j; i < nmodes; i++) { 173278798Shselasky rtemp = abs(refresh - 174278798Shselasky DIVIDE(DIVIDE(modes[i].dot_clock * 1000, 175278798Shselasky modes[i].htotal), modes[i].vtotal)); 176278798Shselasky atemp = (modes[i].hdisplay * 100 / modes[i].vdisplay); 177278798Shselasky if (rtemp < rbest) { 178278798Shselasky rbest = rtemp; 179278798Shselasky mtemp = &modes[i]; 180278798Shselasky } 181278798Shselasky if (rtemp == rbest) { 182278798Shselasky /* Treat "close" aspect ratios as identical */ 183278798Shselasky if (abs(abest - atemp) > (abest / 8) && 184278798Shselasky abs(aspect - atemp) < abs(aspect - abest)) { 185278798Shselasky abest = atemp; 186278798Shselasky mtemp = &modes[i]; 187278798Shselasky } 188278798Shselasky if (atemp == abest || 189278798Shselasky abs(abest - atemp) <= (abest / 8)) { 190278798Shselasky if (modes[i].hdisplay > hbest) { 191278798Shselasky hbest = modes[i].hdisplay; 192278798Shselasky mtemp = &modes[i]; 193278798Shselasky } 194278798Shselasky if (modes[i].hdisplay == hbest && 195278798Shselasky modes[i].vdisplay > vbest) { 196278798Shselasky vbest = modes[i].vdisplay; 197278798Shselasky mtemp = &modes[i]; 198278798Shselasky } 199278798Shselasky } 200278798Shselasky } 201278798Shselasky } 202278798Shselasky if (mtemp != &modes[j]) 203278798Shselasky swap_modes(mtemp, &modes[j]); 204278798Shselasky } 205278798Shselasky} 206