Skip to content

Commit

Permalink
drm/i915/bios: Avoid temporary allocation whilst searching for downclock
Browse files Browse the repository at this point in the history
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: Chris Wilson <chris@chris-wilson.co.uk>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>
Cc: Keith Packard <keithp@keithp.com>
Signed-off-by: Keith Packard <keithp@keithp.com>
  • Loading branch information
Chris Wilson authored and Keith Packard committed Jul 13, 2011
1 parent 9573672 commit 99834ea
Showing 1 changed file with 83 additions and 59 deletions.
142 changes: 83 additions & 59 deletions drivers/gpu/drm/i915/intel_bios.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ get_blocksize(void *p)

static void
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) |
dvo_timing->hactive_lo;
Expand Down Expand Up @@ -115,20 +115,75 @@ fill_detail_timing_data(struct drm_display_mode *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 */
static void
parse_lfp_panel_data(struct drm_i915_private *dev_priv,
struct bdb_header *bdb)
{
struct bdb_lvds_options *lvds_options;
struct bdb_lvds_lfp_data *lvds_lfp_data;
struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
struct bdb_lvds_lfp_data_entry *entry;
struct lvds_dvo_timing *dvo_timing;
const struct bdb_lvds_options *lvds_options;
const struct bdb_lvds_lfp_data *lvds_lfp_data;
const struct bdb_lvds_lfp_data_ptrs *lvds_lfp_data_ptrs;
const struct lvds_dvo_timing *panel_dvo_timing;
struct drm_display_mode *panel_fixed_mode;
int lfp_data_size, dvo_timing_offset;
int i, temp_downclock;
struct drm_display_mode *temp_mode;
int i, downclock;

lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
if (!lvds_options)
Expand All @@ -150,75 +205,44 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,

dev_priv->lvds_vbt = 1;

lfp_data_size = lvds_lfp_data_ptrs->ptr[1].dvo_timing_offset -
lvds_lfp_data_ptrs->ptr[0].dvo_timing_offset;
entry = (struct bdb_lvds_lfp_data_entry *)
((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_dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
lvds_lfp_data_ptrs,
lvds_options->panel_type);

panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
if (!panel_fixed_mode)
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;

DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
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
* the LVDS downclock is found.
* Iterate over the LVDS panel timing info to find the lowest clock
* for the native resolution.
*/
downclock = panel_dvo_timing->clock;
for (i = 0; i < 16; i++) {
entry = (struct bdb_lvds_lfp_data_entry *)
((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
dvo_timing = (struct lvds_dvo_timing *)
((unsigned char *)entry + dvo_timing_offset);

fill_detail_timing_data(temp_mode, dvo_timing);

if (temp_mode->hdisplay == panel_fixed_mode->hdisplay &&
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));
const struct lvds_dvo_timing *dvo_timing;

dvo_timing = get_lvds_dvo_timing(lvds_lfp_data,
lvds_lfp_data_ptrs,
i);
if (lvds_dvo_timing_equal_size(dvo_timing, panel_dvo_timing) &&
dvo_timing->clock < downclock)
downclock = dvo_timing->clock;
}
kfree(temp_mode);
if (temp_downclock < panel_fixed_mode->clock &&
i915_lvds_downclock) {

if (downclock < panel_dvo_timing->clock && i915_lvds_downclock) {
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. "
"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 */
Expand Down

0 comments on commit 99834ea

Please sign in to comment.