video/edid/edid_sort.c: Add logic to sort video modes by how close they are to the preferred mode. This is part of the logic to pick the best possible video mode.

This commit is contained in:
Gregory Nutt 2019-07-07 17:59:51 -06:00
parent 69e215e083
commit 01ca208990
5 changed files with 234 additions and 5 deletions

View File

@ -601,10 +601,13 @@ int edid_parse(FAR const uint8_t *data, FAR struct edid_info_s *edid);
* Name: edid_sort_modes
*
* Description:
* Sort video modes by refresh rate, aspect ratio (*), then resolution.
* Sort video modes by refresh rate, aspect ratio, then resolution.
* Preferred mode or largest mode is first in the list and other modes
* are sorted on closest match to that mode.
*
* Note that the aspect ratio calculation treats "close" aspect ratios
* (within 12.5%) as the same for this purpose.
*
* Input Parameters:
* modes - A reference to the first entry in a list of video modes
* preferred - A pointer to the pointer to the preferred mode in the list

View File

@ -38,7 +38,7 @@ ifeq ($(CONFIG_VIDEO_EDID),y)
# Files required for EDID support
ASRCS +=
CSRCS += edid_parse.c edid_videomode.c # edid_sort.c
CSRCS += edid_parse.c edid_videomode.c edid_sort.c
# Include EDID build support

View File

@ -1,7 +1,7 @@
/****************************************************************************
* video/edid/edid_parse.c
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Derives from logic in FreeBSD which has an equivalent 3-clause BSD
@ -181,7 +181,7 @@ static bool edid_std_timing(FAR const uint8_t *stdtim,
}
else
{
#if 0 /* Not implemented */
#if 0 /* Not implemented. See FreeBSD sys/dev/videomode/vesagtf.c */
/* Failing that, calculate it using gtf
*
* Hmm. I'm not using alternate GTF timings, which

226
video/edid/edid_sort.c Normal file
View File

@ -0,0 +1,226 @@
/****************************************************************************
* video/edid/edid_sort.c
*
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Derives from logic in FreeBSD which has an compatible 2-clause BSD
* license:
*
* Copyright (c) 2006 The NetBSD Foundation. All rights reserved.
* Author: Michael Lorenz
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE NETBSD FOUNDATION BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
* GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <sys/types.h>
#include <string.h>
#include <nuttx/lcd/edid.h>
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
#define DIVIDE(x, y) (((x) + ((y) / 2)) / (y))
/****************************************************************************
* Private Functions
****************************************************************************/
static inline void swap_modes(FAR struct edid_videomode_s *left,
FAR struct edid_videomode_s *right)
{
struct edid_videomode_s temp;
temp = *left;
*left = *right;
*right = temp;
}
static inline int _abs(int a)
{
if (a < 0)
{
return -a;
}
else
{
return a;
}
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: edid_sort_modes
*
* Description:
* Sort video modes by refresh rate, aspect ratio, then resolution.
* Preferred mode or largest mode is first in the list and other modes
* are sorted on closest match to that mode.
*
* Note that the aspect ratio calculation treats "close" aspect ratios
* (within 12.5%) as the same for this purpose.
*
* Input Parameters:
* modes - A reference to the first entry in a list of video modes
* preferred - A pointer to the pointer to the preferred mode in the list
* nmodes - The number of modes in the list
*
* Returned Value:
* None
*
****************************************************************************/
void edid_sort_modes(FAR struct edid_videomode_s *modes,
FAR struct edid_videomode_s **preferred, unsigned int nmodes)
{
FAR struct edid_videomode_s *tmpmode = NULL;
int aspect;
int refresh;
int hbest;
int vbest;
int abest;
int atemp;
int rbest;
int rtemp;
int i;
int j;
if (nmodes < 2)
{
return;
}
if (*preferred != NULL)
{
/* Put the preferred mode first in the list */
aspect = (*preferred)->hdisplay * 100 / (*preferred)->vdisplay;
refresh = DIVIDE(DIVIDE((*preferred)->dotclock * 1000,
(*preferred)->htotal), (*preferred)->vtotal);
if (*preferred != modes)
{
swap_modes(*preferred, modes);
*preferred = modes;
}
}
else
{
/* Find the largest horizontal and vertical mode and put that
* first in the list. Preferred refresh rate is taken from
* the first mode of this size.
*/
hbest = 0;
vbest = 0;
for (i = 0; i < nmodes; i++)
{
if (modes[i].hdisplay > hbest)
{
hbest = modes[i].hdisplay;
vbest = modes[i].vdisplay;
tmpmode = &modes[i];
}
else if (modes[i].hdisplay == hbest && modes[i].vdisplay > vbest)
{
vbest = modes[i].vdisplay;
tmpmode = &modes[i];
}
}
aspect = tmpmode->hdisplay * 100 / tmpmode->vdisplay;
refresh = DIVIDE(DIVIDE(tmpmode->dotclock * 1000,
tmpmode->htotal), tmpmode->vtotal);
if (tmpmode != modes)
{
swap_modes(tmpmode, modes);
}
}
/* Sort other modes by refresh rate, aspect ratio, then resolution */
for (j = 1; j < nmodes - 1; j++)
{
rbest = 1000;
abest = 1000;
hbest = 0;
vbest = 0;
for (i = j; i < nmodes; i++)
{
rtemp = _abs(refresh -
DIVIDE(DIVIDE(modes[i].dotclock * 1000,
modes[i].htotal), modes[i].vtotal));
atemp = (modes[i].hdisplay * 100 / modes[i].vdisplay);
if (rtemp < rbest)
{
rbest = rtemp;
tmpmode = &modes[i];
}
if (rtemp == rbest)
{
/* Treat "close" aspect ratios as identical */
if (_abs(abest - atemp) > (abest / 8) &&
_abs(aspect - atemp) < _abs(aspect - abest))
{
abest = atemp;
tmpmode = &modes[i];
}
if (atemp == abest || _abs(abest - atemp) <= (abest / 8))
{
if (modes[i].hdisplay > hbest)
{
hbest = modes[i].hdisplay;
tmpmode = &modes[i];
}
if (modes[i].hdisplay == hbest && modes[i].vdisplay > vbest)
{
vbest = modes[i].vdisplay;
tmpmode = &modes[i];
}
}
}
}
if (tmpmode != &modes[j])
{
swap_modes(tmpmode, &modes[j]);
}
}
}

View File

@ -1,7 +1,7 @@
/****************************************************************************
* video/edid/edid_parse.c
*
* Copyright (C) 2018 Gregory Nutt. All rights reserved.
* Copyright (C) 2019 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Derives from logic in FreeBSD which has an equivalent 3-clause BSD