Commit 99834ea4 authored by Chris Wilson's avatar Chris Wilson Committed by Keith Packard

drm/i915/bios: Avoid temporary allocation whilst searching for downclock

Alan Cox reported a missing check on the kmalloc return value for the
allocation of a temporary mode used for searching for the LVDS downlock
frequency. This allocation is roughly 200 bytes, a little too large to
friviously place on the stack. However, we can simply use the few bytes
we need stored within the original DVO timing data, skip the translation
and do the compare directly between the timing data rather than on a
mode, thus avoiding the need for any temporary allocations.
Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Keith Packard <keithp@keithp.com>
Signed-off-by: default avatarKeith Packard <keithp@keithp.com>
parent 95736720
...@@ -74,7 +74,7 @@ get_blocksize(void *p) ...@@ -74,7 +74,7 @@ get_blocksize(void *p)
static void static void
fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
struct lvds_dvo_timing *dvo_timing) const struct lvds_dvo_timing *dvo_timing)
{ {
panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
dvo_timing->hactive_lo; dvo_timing->hactive_lo;
...@@ -115,20 +115,75 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, ...@@ -115,20 +115,75 @@ fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
drm_mode_set_name(panel_fixed_mode); drm_mode_set_name(panel_fixed_mode);
} }
static bool
lvds_dvo_timing_equal_size(const struct lvds_dvo_timing *a,
const struct lvds_dvo_timing *b)
{
if (a->hactive_hi != b->hactive_hi ||
a->hactive_lo != b->hactive_lo)
return false;
if (a->hsync_off_hi != b->hsync_off_hi ||
a->hsync_off_lo != b->hsync_off_lo)
return false;
if (a->hsync_pulse_width != b->hsync_pulse_width)
return false;
if (a->hblank_hi != b->hblank_hi ||
a->hblank_lo != b->hblank_lo)
return false;
if (a->vactive_hi != b->vactive_hi ||
a->vactive_lo != b->vactive_lo)
return false;
if (a->vsync_off != b->vsync_off)
return false;
if (a->vsync_pulse_width != b->vsync_pulse_width)
return false;
if (a->vblank_hi != b->vblank_hi ||
a->vblank_lo != b->vblank_lo)
return false;
return true;
}
static const struct lvds_dvo_timing *
get_lvds_dvo_timing(const struct bdb_lvds_lfp_data *lvds_lfp_data,
const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs,
int index)
{
/*
* the size of fp_timing varies on the different platform.
* So calculate the DVO timing relative offset in LVDS data
* entry to get the DVO timing entry
*/
int lfp_data_size =
lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
int dvo_timing_offset =
lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
char *entry = (char *)lvds_lfp_data->data + lfp_data_size * index;
return (struct lvds_dvo_timing *)(entry + dvo_timing_offset);
}
/* Try to find integrated panel data */ /* Try to find integrated panel data */
static void static void
parse_lfp_panel_data(struct drm_i915_private *dev_priv, parse_lfp_panel_data(struct drm_i915_private *dev_priv,
struct bdb_header *bdb) struct bdb_header *bdb)
{ {
struct bdb_lvds_options *lvds_options; const struct bdb_lvds_options *lvds_options;
struct bdb_lvds_lfp_data *lvds_lfp_data; const struct bdb_lvds_lfp_data *lvds_lfp_data;
struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs; const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
struct bdb_lvds_lfp_data_entry *entry; const struct lvds_dvo_timing *panel_dvo_timing;
struct lvds_dvo_timing *dvo_timing;
struct drm_display_mode *panel_fixed_mode; struct drm_display_mode *panel_fixed_mode;
int lfp_data_size, dvo_timing_offset; int i, downclock;
int i, temp_downclock;
struct drm_display_mode *temp_mode;
lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
if (!lvds_options) if (!lvds_options)
...@@ -150,75 +205,44 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv, ...@@ -150,75 +205,44 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
dev_priv->lvds_vbt = 1; dev_priv->lvds_vbt = 1;
lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset - panel_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset; lvds_lfp_data_ptrs,
entry = (struct bdb_lvds_lfp_data_entry *) lvds_options->panel_type);
((uint8_t *)lvds_lfp_data->data + (lfp_data_size *
lvds_options->panel_type));
dvo_timing_offset = lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset -
lvds_lfp_data_ptrs->ptr[0].fp_timing_offset;
/*
* the size of fp_timing varies on the different platform.
* So calculate the DVO timing relative offset in LVDS data
* entry to get the DVO timing entry
*/
dvo_timing = (struct lvds_dvo_timing *)
((unsigned char *)entry + dvo_timing_offset);
panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
if (!panel_fixed_mode) if (!panel_fixed_mode)
return; return;
fill_detail_timing_data(panel_fixed_mode, dvo_timing); fill_detail_timing_data(panel_fixed_mode, panel_dvo_timing);
dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n"); DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
drm_mode_debug_printmodeline(panel_fixed_mode); drm_mode_debug_printmodeline(panel_fixed_mode);
temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL);
temp_downclock = panel_fixed_mode->clock;
/* /*
* enumerate the LVDS panel timing info entry in VBT to check whether * Iterate over the LVDS panel timing info to find the lowest clock
* the LVDS downclock is found. * for the native resolution.
*/ */
downclock = panel_dvo_timing->clock;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
entry = (struct bdb_lvds_lfp_data_entry *) const struct lvds_dvo_timing *dvo_timing;
((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
dvo_timing = (struct lvds_dvo_timing *) dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
((unsigned char *)entry + dvo_timing_offset); lvds_lfp_data_ptrs,
i);
fill_detail_timing_data(temp_mode, dvo_timing); if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) &&
dvo_timing->clock < downclock)
if (temp_mode->hdisplay == panel_fixed_mode->hdisplay && downclock = dvo_timing->clock;
temp_mode->hsync_start == panel_fixed_mode->hsync_start &&
temp_mode->hsync_end == panel_fixed_mode->hsync_end &&
temp_mode->htotal == panel_fixed_mode->htotal &&
temp_mode->vdisplay == panel_fixed_mode->vdisplay &&
temp_mode->vsync_start == panel_fixed_mode->vsync_start &&
temp_mode->vsync_end == panel_fixed_mode->vsync_end &&
temp_mode->vtotal == panel_fixed_mode->vtotal &&
temp_mode->clock < temp_downclock) {
/*
* downclock is already found. But we expect
* to find the lower downclock.
*/
temp_downclock = temp_mode->clock;
}
/* clear it to zero */
memset(temp_mode, 0, sizeof(*temp_mode));
} }
kfree(temp_mode);
if (temp_downclock < panel_fixed_mode->clock && if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
i915_lvds_downclock) {
dev_priv->lvds_downclock_avail = 1; dev_priv->lvds_downclock_avail = 1;
dev_priv->lvds_downclock = temp_downclock; dev_priv->lvds_downclock = downclock * 10;
DRM_DEBUG_KMS("LVDS downclock is found in VBT. " DRM_DEBUG_KMS("LVDS downclock is found in VBT. "
"Normal Clock %dKHz, downclock %dKHz\n", "Normal Clock %dKHz, downclock %dKHz\n",
temp_downclock, panel_fixed_mode->clock); panel_fixed_mode->clock, 10*downclock);
} }
return;
} }
/* Try to find sdvo panel data */ /* Try to find sdvo panel data */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment