18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2006 Intel Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Authors:
68c2ecf20Sopenharmony_ci *    Eric Anholt <eric@anholt.net>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci#include <drm/drm.h>
98c2ecf20Sopenharmony_ci#include <drm/drm_dp_helper.h>
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include "intel_bios.h"
128c2ecf20Sopenharmony_ci#include "psb_drv.h"
138c2ecf20Sopenharmony_ci#include "psb_intel_drv.h"
148c2ecf20Sopenharmony_ci#include "psb_intel_reg.h"
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#define	SLAVE_ADDR1	0x70
178c2ecf20Sopenharmony_ci#define	SLAVE_ADDR2	0x72
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistatic void *find_section(struct bdb_header *bdb, int section_id)
208c2ecf20Sopenharmony_ci{
218c2ecf20Sopenharmony_ci	u8 *base = (u8 *)bdb;
228c2ecf20Sopenharmony_ci	int index = 0;
238c2ecf20Sopenharmony_ci	u16 total, current_size;
248c2ecf20Sopenharmony_ci	u8 current_id;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	/* skip to first section */
278c2ecf20Sopenharmony_ci	index += bdb->header_size;
288c2ecf20Sopenharmony_ci	total = bdb->bdb_size;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	/* walk the sections looking for section_id */
318c2ecf20Sopenharmony_ci	while (index < total) {
328c2ecf20Sopenharmony_ci		current_id = *(base + index);
338c2ecf20Sopenharmony_ci		index++;
348c2ecf20Sopenharmony_ci		current_size = *((u16 *)(base + index));
358c2ecf20Sopenharmony_ci		index += 2;
368c2ecf20Sopenharmony_ci		if (current_id == section_id)
378c2ecf20Sopenharmony_ci			return base + index;
388c2ecf20Sopenharmony_ci		index += current_size;
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	return NULL;
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic void
458c2ecf20Sopenharmony_ciparse_edp(struct drm_psb_private *dev_priv, struct bdb_header *bdb)
468c2ecf20Sopenharmony_ci{
478c2ecf20Sopenharmony_ci	struct bdb_edp *edp;
488c2ecf20Sopenharmony_ci	struct edp_power_seq *edp_pps;
498c2ecf20Sopenharmony_ci	struct edp_link_params *edp_link_params;
508c2ecf20Sopenharmony_ci	uint8_t	panel_type;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	edp = find_section(bdb, BDB_EDP);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	dev_priv->edp.bpp = 18;
558c2ecf20Sopenharmony_ci	if (!edp) {
568c2ecf20Sopenharmony_ci		if (dev_priv->edp.support) {
578c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported, assume %dbpp panel color depth.\n",
588c2ecf20Sopenharmony_ci				      dev_priv->edp.bpp);
598c2ecf20Sopenharmony_ci		}
608c2ecf20Sopenharmony_ci		return;
618c2ecf20Sopenharmony_ci	}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ci	panel_type = dev_priv->panel_type;
648c2ecf20Sopenharmony_ci	switch ((edp->color_depth >> (panel_type * 2)) & 3) {
658c2ecf20Sopenharmony_ci	case EDP_18BPP:
668c2ecf20Sopenharmony_ci		dev_priv->edp.bpp = 18;
678c2ecf20Sopenharmony_ci		break;
688c2ecf20Sopenharmony_ci	case EDP_24BPP:
698c2ecf20Sopenharmony_ci		dev_priv->edp.bpp = 24;
708c2ecf20Sopenharmony_ci		break;
718c2ecf20Sopenharmony_ci	case EDP_30BPP:
728c2ecf20Sopenharmony_ci		dev_priv->edp.bpp = 30;
738c2ecf20Sopenharmony_ci		break;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/* Get the eDP sequencing and link info */
778c2ecf20Sopenharmony_ci	edp_pps = &edp->power_seqs[panel_type];
788c2ecf20Sopenharmony_ci	edp_link_params = &edp->link_params[panel_type];
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	dev_priv->edp.pps = *edp_pps;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("EDP timing in vbt t1_t3 %d t8 %d t9 %d t10 %d t11_t12 %d\n",
838c2ecf20Sopenharmony_ci				dev_priv->edp.pps.t1_t3, dev_priv->edp.pps.t8,
848c2ecf20Sopenharmony_ci				dev_priv->edp.pps.t9, dev_priv->edp.pps.t10,
858c2ecf20Sopenharmony_ci				dev_priv->edp.pps.t11_t12);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	dev_priv->edp.rate = edp_link_params->rate ? DP_LINK_BW_2_7 :
888c2ecf20Sopenharmony_ci		DP_LINK_BW_1_62;
898c2ecf20Sopenharmony_ci	switch (edp_link_params->lanes) {
908c2ecf20Sopenharmony_ci	case 0:
918c2ecf20Sopenharmony_ci		dev_priv->edp.lanes = 1;
928c2ecf20Sopenharmony_ci		break;
938c2ecf20Sopenharmony_ci	case 1:
948c2ecf20Sopenharmony_ci		dev_priv->edp.lanes = 2;
958c2ecf20Sopenharmony_ci		break;
968c2ecf20Sopenharmony_ci	case 3:
978c2ecf20Sopenharmony_ci	default:
988c2ecf20Sopenharmony_ci		dev_priv->edp.lanes = 4;
998c2ecf20Sopenharmony_ci		break;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VBT reports EDP: Lane_count %d, Lane_rate %d, Bpp %d\n",
1028c2ecf20Sopenharmony_ci			dev_priv->edp.lanes, dev_priv->edp.rate, dev_priv->edp.bpp);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	switch (edp_link_params->preemphasis) {
1058c2ecf20Sopenharmony_ci	case 0:
1068c2ecf20Sopenharmony_ci		dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_0;
1078c2ecf20Sopenharmony_ci		break;
1088c2ecf20Sopenharmony_ci	case 1:
1098c2ecf20Sopenharmony_ci		dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_1;
1108c2ecf20Sopenharmony_ci		break;
1118c2ecf20Sopenharmony_ci	case 2:
1128c2ecf20Sopenharmony_ci		dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_2;
1138c2ecf20Sopenharmony_ci		break;
1148c2ecf20Sopenharmony_ci	case 3:
1158c2ecf20Sopenharmony_ci		dev_priv->edp.preemphasis = DP_TRAIN_PRE_EMPH_LEVEL_3;
1168c2ecf20Sopenharmony_ci		break;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	switch (edp_link_params->vswing) {
1198c2ecf20Sopenharmony_ci	case 0:
1208c2ecf20Sopenharmony_ci		dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_0;
1218c2ecf20Sopenharmony_ci		break;
1228c2ecf20Sopenharmony_ci	case 1:
1238c2ecf20Sopenharmony_ci		dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_1;
1248c2ecf20Sopenharmony_ci		break;
1258c2ecf20Sopenharmony_ci	case 2:
1268c2ecf20Sopenharmony_ci		dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_2;
1278c2ecf20Sopenharmony_ci		break;
1288c2ecf20Sopenharmony_ci	case 3:
1298c2ecf20Sopenharmony_ci		dev_priv->edp.vswing = DP_TRAIN_VOLTAGE_SWING_LEVEL_3;
1308c2ecf20Sopenharmony_ci		break;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("VBT reports EDP: VSwing  %d, Preemph %d\n",
1338c2ecf20Sopenharmony_ci			dev_priv->edp.vswing, dev_priv->edp.preemphasis);
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic u16
1378c2ecf20Sopenharmony_ciget_blocksize(void *p)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	u16 *block_ptr, block_size;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	block_ptr = (u16 *)((char *)p - 2);
1428c2ecf20Sopenharmony_ci	block_size = *block_ptr;
1438c2ecf20Sopenharmony_ci	return block_size;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_cistatic void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode,
1478c2ecf20Sopenharmony_ci			struct lvds_dvo_timing *dvo_timing)
1488c2ecf20Sopenharmony_ci{
1498c2ecf20Sopenharmony_ci	panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) |
1508c2ecf20Sopenharmony_ci		dvo_timing->hactive_lo;
1518c2ecf20Sopenharmony_ci	panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay +
1528c2ecf20Sopenharmony_ci		((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo);
1538c2ecf20Sopenharmony_ci	panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start +
1548c2ecf20Sopenharmony_ci		dvo_timing->hsync_pulse_width;
1558c2ecf20Sopenharmony_ci	panel_fixed_mode->htotal = panel_fixed_mode->hdisplay +
1568c2ecf20Sopenharmony_ci		((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo);
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci	panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) |
1598c2ecf20Sopenharmony_ci		dvo_timing->vactive_lo;
1608c2ecf20Sopenharmony_ci	panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay +
1618c2ecf20Sopenharmony_ci		dvo_timing->vsync_off;
1628c2ecf20Sopenharmony_ci	panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start +
1638c2ecf20Sopenharmony_ci		dvo_timing->vsync_pulse_width;
1648c2ecf20Sopenharmony_ci	panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay +
1658c2ecf20Sopenharmony_ci		((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo);
1668c2ecf20Sopenharmony_ci	panel_fixed_mode->clock = dvo_timing->clock * 10;
1678c2ecf20Sopenharmony_ci	panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (dvo_timing->hsync_positive)
1708c2ecf20Sopenharmony_ci		panel_fixed_mode->flags |= DRM_MODE_FLAG_PHSYNC;
1718c2ecf20Sopenharmony_ci	else
1728c2ecf20Sopenharmony_ci		panel_fixed_mode->flags |= DRM_MODE_FLAG_NHSYNC;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	if (dvo_timing->vsync_positive)
1758c2ecf20Sopenharmony_ci		panel_fixed_mode->flags |= DRM_MODE_FLAG_PVSYNC;
1768c2ecf20Sopenharmony_ci	else
1778c2ecf20Sopenharmony_ci		panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC;
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_ci	/* Some VBTs have bogus h/vtotal values */
1808c2ecf20Sopenharmony_ci	if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal)
1818c2ecf20Sopenharmony_ci		panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1;
1828c2ecf20Sopenharmony_ci	if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal)
1838c2ecf20Sopenharmony_ci		panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	drm_mode_set_name(panel_fixed_mode);
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic void parse_backlight_data(struct drm_psb_private *dev_priv,
1898c2ecf20Sopenharmony_ci				struct bdb_header *bdb)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	struct bdb_lvds_backlight *vbt_lvds_bl = NULL;
1928c2ecf20Sopenharmony_ci	struct bdb_lvds_backlight *lvds_bl;
1938c2ecf20Sopenharmony_ci	u8 p_type = 0;
1948c2ecf20Sopenharmony_ci	void *bl_start = NULL;
1958c2ecf20Sopenharmony_ci	struct bdb_lvds_options *lvds_opts
1968c2ecf20Sopenharmony_ci				= find_section(bdb, BDB_LVDS_OPTIONS);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	dev_priv->lvds_bl = NULL;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	if (lvds_opts)
2018c2ecf20Sopenharmony_ci		p_type = lvds_opts->panel_type;
2028c2ecf20Sopenharmony_ci	else
2038c2ecf20Sopenharmony_ci		return;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT);
2068c2ecf20Sopenharmony_ci	vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	lvds_bl = kmemdup(vbt_lvds_bl, sizeof(*vbt_lvds_bl), GFP_KERNEL);
2098c2ecf20Sopenharmony_ci	if (!lvds_bl) {
2108c2ecf20Sopenharmony_ci		dev_err(dev_priv->dev->dev, "out of memory for backlight data\n");
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	dev_priv->lvds_bl = lvds_bl;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/* Try to find integrated panel data */
2178c2ecf20Sopenharmony_cistatic void parse_lfp_panel_data(struct drm_psb_private *dev_priv,
2188c2ecf20Sopenharmony_ci			    struct bdb_header *bdb)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	struct bdb_lvds_options *lvds_options;
2218c2ecf20Sopenharmony_ci	struct bdb_lvds_lfp_data *lvds_lfp_data;
2228c2ecf20Sopenharmony_ci	struct bdb_lvds_lfp_data_entry *entry;
2238c2ecf20Sopenharmony_ci	struct lvds_dvo_timing *dvo_timing;
2248c2ecf20Sopenharmony_ci	struct drm_display_mode *panel_fixed_mode;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	/* Defaults if we can't find VBT info */
2278c2ecf20Sopenharmony_ci	dev_priv->lvds_dither = 0;
2288c2ecf20Sopenharmony_ci	dev_priv->lvds_vbt = 0;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	lvds_options = find_section(bdb, BDB_LVDS_OPTIONS);
2318c2ecf20Sopenharmony_ci	if (!lvds_options)
2328c2ecf20Sopenharmony_ci		return;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	dev_priv->lvds_dither = lvds_options->pixel_dither;
2358c2ecf20Sopenharmony_ci	dev_priv->panel_type = lvds_options->panel_type;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	if (lvds_options->panel_type == 0xff)
2388c2ecf20Sopenharmony_ci		return;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
2418c2ecf20Sopenharmony_ci	if (!lvds_lfp_data)
2428c2ecf20Sopenharmony_ci		return;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	entry = &lvds_lfp_data->data[lvds_options->panel_type];
2468c2ecf20Sopenharmony_ci	dvo_timing = &entry->dvo_timing;
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci	panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode),
2498c2ecf20Sopenharmony_ci				      GFP_KERNEL);
2508c2ecf20Sopenharmony_ci	if (panel_fixed_mode == NULL) {
2518c2ecf20Sopenharmony_ci		dev_err(dev_priv->dev->dev, "out of memory for fixed panel mode\n");
2528c2ecf20Sopenharmony_ci		return;
2538c2ecf20Sopenharmony_ci	}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	dev_priv->lvds_vbt = 1;
2568c2ecf20Sopenharmony_ci	fill_detail_timing_data(panel_fixed_mode, dvo_timing);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (panel_fixed_mode->htotal > 0 && panel_fixed_mode->vtotal > 0) {
2598c2ecf20Sopenharmony_ci		dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
2608c2ecf20Sopenharmony_ci		drm_mode_debug_printmodeline(panel_fixed_mode);
2618c2ecf20Sopenharmony_ci	} else {
2628c2ecf20Sopenharmony_ci		dev_dbg(dev_priv->dev->dev, "ignoring invalid LVDS VBT\n");
2638c2ecf20Sopenharmony_ci		dev_priv->lvds_vbt = 0;
2648c2ecf20Sopenharmony_ci		kfree(panel_fixed_mode);
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	return;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci/* Try to find sdvo panel data */
2708c2ecf20Sopenharmony_cistatic void parse_sdvo_panel_data(struct drm_psb_private *dev_priv,
2718c2ecf20Sopenharmony_ci		      struct bdb_header *bdb)
2728c2ecf20Sopenharmony_ci{
2738c2ecf20Sopenharmony_ci	struct bdb_sdvo_lvds_options *sdvo_lvds_options;
2748c2ecf20Sopenharmony_ci	struct lvds_dvo_timing *dvo_timing;
2758c2ecf20Sopenharmony_ci	struct drm_display_mode *panel_fixed_mode;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	dev_priv->sdvo_lvds_vbt_mode = NULL;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS);
2808c2ecf20Sopenharmony_ci	if (!sdvo_lvds_options)
2818c2ecf20Sopenharmony_ci		return;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS);
2848c2ecf20Sopenharmony_ci	if (!dvo_timing)
2858c2ecf20Sopenharmony_ci		return;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	if (!panel_fixed_mode)
2908c2ecf20Sopenharmony_ci		return;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	fill_detail_timing_data(panel_fixed_mode,
2938c2ecf20Sopenharmony_ci			dvo_timing + sdvo_lvds_options->panel_type);
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	return;
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic void parse_general_features(struct drm_psb_private *dev_priv,
3018c2ecf20Sopenharmony_ci		       struct bdb_header *bdb)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct bdb_general_features *general;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	/* Set sensible defaults in case we can't find the general block */
3068c2ecf20Sopenharmony_ci	dev_priv->int_tv_support = 1;
3078c2ecf20Sopenharmony_ci	dev_priv->int_crt_support = 1;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	general = find_section(bdb, BDB_GENERAL_FEATURES);
3108c2ecf20Sopenharmony_ci	if (general) {
3118c2ecf20Sopenharmony_ci		dev_priv->int_tv_support = general->int_tv_support;
3128c2ecf20Sopenharmony_ci		dev_priv->int_crt_support = general->int_crt_support;
3138c2ecf20Sopenharmony_ci		dev_priv->lvds_use_ssc = general->enable_ssc;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci		if (dev_priv->lvds_use_ssc) {
3168c2ecf20Sopenharmony_ci			dev_priv->lvds_ssc_freq
3178c2ecf20Sopenharmony_ci				= general->ssc_freq ? 100 : 96;
3188c2ecf20Sopenharmony_ci		}
3198c2ecf20Sopenharmony_ci	}
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic void
3238c2ecf20Sopenharmony_ciparse_sdvo_device_mapping(struct drm_psb_private *dev_priv,
3248c2ecf20Sopenharmony_ci			  struct bdb_header *bdb)
3258c2ecf20Sopenharmony_ci{
3268c2ecf20Sopenharmony_ci	struct sdvo_device_mapping *p_mapping;
3278c2ecf20Sopenharmony_ci	struct bdb_general_definitions *p_defs;
3288c2ecf20Sopenharmony_ci	struct child_device_config *p_child;
3298c2ecf20Sopenharmony_ci	int i, child_device_num, count;
3308c2ecf20Sopenharmony_ci	u16	block_size;
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
3338c2ecf20Sopenharmony_ci	if (!p_defs) {
3348c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("No general definition block is found, unable to construct sdvo mapping.\n");
3358c2ecf20Sopenharmony_ci		return;
3368c2ecf20Sopenharmony_ci	}
3378c2ecf20Sopenharmony_ci	/* judge whether the size of child device meets the requirements.
3388c2ecf20Sopenharmony_ci	 * If the child device size obtained from general definition block
3398c2ecf20Sopenharmony_ci	 * is different with sizeof(struct child_device_config), skip the
3408c2ecf20Sopenharmony_ci	 * parsing of sdvo device info
3418c2ecf20Sopenharmony_ci	 */
3428c2ecf20Sopenharmony_ci	if (p_defs->child_dev_size != sizeof(*p_child)) {
3438c2ecf20Sopenharmony_ci		/* different child dev size . Ignore it */
3448c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("different child size is found. Invalid.\n");
3458c2ecf20Sopenharmony_ci		return;
3468c2ecf20Sopenharmony_ci	}
3478c2ecf20Sopenharmony_ci	/* get the block size of general definitions */
3488c2ecf20Sopenharmony_ci	block_size = get_blocksize(p_defs);
3498c2ecf20Sopenharmony_ci	/* get the number of child device */
3508c2ecf20Sopenharmony_ci	child_device_num = (block_size - sizeof(*p_defs)) /
3518c2ecf20Sopenharmony_ci				sizeof(*p_child);
3528c2ecf20Sopenharmony_ci	count = 0;
3538c2ecf20Sopenharmony_ci	for (i = 0; i < child_device_num; i++) {
3548c2ecf20Sopenharmony_ci		p_child = &(p_defs->devices[i]);
3558c2ecf20Sopenharmony_ci		if (!p_child->device_type) {
3568c2ecf20Sopenharmony_ci			/* skip the device block if device type is invalid */
3578c2ecf20Sopenharmony_ci			continue;
3588c2ecf20Sopenharmony_ci		}
3598c2ecf20Sopenharmony_ci		if (p_child->slave_addr != SLAVE_ADDR1 &&
3608c2ecf20Sopenharmony_ci			p_child->slave_addr != SLAVE_ADDR2) {
3618c2ecf20Sopenharmony_ci			/*
3628c2ecf20Sopenharmony_ci			 * If the slave address is neither 0x70 nor 0x72,
3638c2ecf20Sopenharmony_ci			 * it is not a SDVO device. Skip it.
3648c2ecf20Sopenharmony_ci			 */
3658c2ecf20Sopenharmony_ci			continue;
3668c2ecf20Sopenharmony_ci		}
3678c2ecf20Sopenharmony_ci		if (p_child->dvo_port != DEVICE_PORT_DVOB &&
3688c2ecf20Sopenharmony_ci			p_child->dvo_port != DEVICE_PORT_DVOC) {
3698c2ecf20Sopenharmony_ci			/* skip the incorrect SDVO port */
3708c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("Incorrect SDVO port. Skip it\n");
3718c2ecf20Sopenharmony_ci			continue;
3728c2ecf20Sopenharmony_ci		}
3738c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on"
3748c2ecf20Sopenharmony_ci				" %s port\n",
3758c2ecf20Sopenharmony_ci				p_child->slave_addr,
3768c2ecf20Sopenharmony_ci				(p_child->dvo_port == DEVICE_PORT_DVOB) ?
3778c2ecf20Sopenharmony_ci					"SDVOB" : "SDVOC");
3788c2ecf20Sopenharmony_ci		p_mapping = &(dev_priv->sdvo_mappings[p_child->dvo_port - 1]);
3798c2ecf20Sopenharmony_ci		if (!p_mapping->initialized) {
3808c2ecf20Sopenharmony_ci			p_mapping->dvo_port = p_child->dvo_port;
3818c2ecf20Sopenharmony_ci			p_mapping->slave_addr = p_child->slave_addr;
3828c2ecf20Sopenharmony_ci			p_mapping->dvo_wiring = p_child->dvo_wiring;
3838c2ecf20Sopenharmony_ci			p_mapping->ddc_pin = p_child->ddc_pin;
3848c2ecf20Sopenharmony_ci			p_mapping->i2c_pin = p_child->i2c_pin;
3858c2ecf20Sopenharmony_ci			p_mapping->initialized = 1;
3868c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("SDVO device: dvo=%x, addr=%x, wiring=%d, ddc_pin=%d, i2c_pin=%d\n",
3878c2ecf20Sopenharmony_ci				      p_mapping->dvo_port,
3888c2ecf20Sopenharmony_ci				      p_mapping->slave_addr,
3898c2ecf20Sopenharmony_ci				      p_mapping->dvo_wiring,
3908c2ecf20Sopenharmony_ci				      p_mapping->ddc_pin,
3918c2ecf20Sopenharmony_ci				      p_mapping->i2c_pin);
3928c2ecf20Sopenharmony_ci		} else {
3938c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
3948c2ecf20Sopenharmony_ci					 "two SDVO device.\n");
3958c2ecf20Sopenharmony_ci		}
3968c2ecf20Sopenharmony_ci		if (p_child->slave2_addr) {
3978c2ecf20Sopenharmony_ci			/* Maybe this is a SDVO device with multiple inputs */
3988c2ecf20Sopenharmony_ci			/* And the mapping info is not added */
3998c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("there exists the slave2_addr. Maybe this"
4008c2ecf20Sopenharmony_ci				" is a SDVO device with multiple inputs.\n");
4018c2ecf20Sopenharmony_ci		}
4028c2ecf20Sopenharmony_ci		count++;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	if (!count) {
4068c2ecf20Sopenharmony_ci		/* No SDVO device info is found */
4078c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("No SDVO device info is found in VBT\n");
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	return;
4108c2ecf20Sopenharmony_ci}
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic void
4148c2ecf20Sopenharmony_ciparse_driver_features(struct drm_psb_private *dev_priv,
4158c2ecf20Sopenharmony_ci		      struct bdb_header *bdb)
4168c2ecf20Sopenharmony_ci{
4178c2ecf20Sopenharmony_ci	struct bdb_driver_features *driver;
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	driver = find_section(bdb, BDB_DRIVER_FEATURES);
4208c2ecf20Sopenharmony_ci	if (!driver)
4218c2ecf20Sopenharmony_ci		return;
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	if (driver->lvds_config == BDB_DRIVER_FEATURE_EDP)
4248c2ecf20Sopenharmony_ci		dev_priv->edp.support = 1;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci	dev_priv->lvds_enabled_in_vbt = driver->lvds_config != 0;
4278c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("LVDS VBT config bits: 0x%x\n", driver->lvds_config);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci	/* This bit means to use 96Mhz for DPLL_A or not */
4308c2ecf20Sopenharmony_ci	if (driver->primary_lfp_id)
4318c2ecf20Sopenharmony_ci		dev_priv->dplla_96mhz = true;
4328c2ecf20Sopenharmony_ci	else
4338c2ecf20Sopenharmony_ci		dev_priv->dplla_96mhz = false;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_cistatic void
4378c2ecf20Sopenharmony_ciparse_device_mapping(struct drm_psb_private *dev_priv,
4388c2ecf20Sopenharmony_ci		       struct bdb_header *bdb)
4398c2ecf20Sopenharmony_ci{
4408c2ecf20Sopenharmony_ci	struct bdb_general_definitions *p_defs;
4418c2ecf20Sopenharmony_ci	struct child_device_config *p_child, *child_dev_ptr;
4428c2ecf20Sopenharmony_ci	int i, child_device_num, count;
4438c2ecf20Sopenharmony_ci	u16	block_size;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
4468c2ecf20Sopenharmony_ci	if (!p_defs) {
4478c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n");
4488c2ecf20Sopenharmony_ci		return;
4498c2ecf20Sopenharmony_ci	}
4508c2ecf20Sopenharmony_ci	/* judge whether the size of child device meets the requirements.
4518c2ecf20Sopenharmony_ci	 * If the child device size obtained from general definition block
4528c2ecf20Sopenharmony_ci	 * is different with sizeof(struct child_device_config), skip the
4538c2ecf20Sopenharmony_ci	 * parsing of sdvo device info
4548c2ecf20Sopenharmony_ci	 */
4558c2ecf20Sopenharmony_ci	if (p_defs->child_dev_size != sizeof(*p_child)) {
4568c2ecf20Sopenharmony_ci		/* different child dev size . Ignore it */
4578c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("different child size is found. Invalid.\n");
4588c2ecf20Sopenharmony_ci		return;
4598c2ecf20Sopenharmony_ci	}
4608c2ecf20Sopenharmony_ci	/* get the block size of general definitions */
4618c2ecf20Sopenharmony_ci	block_size = get_blocksize(p_defs);
4628c2ecf20Sopenharmony_ci	/* get the number of child device */
4638c2ecf20Sopenharmony_ci	child_device_num = (block_size - sizeof(*p_defs)) /
4648c2ecf20Sopenharmony_ci				sizeof(*p_child);
4658c2ecf20Sopenharmony_ci	count = 0;
4668c2ecf20Sopenharmony_ci	/* get the number of child devices that are present */
4678c2ecf20Sopenharmony_ci	for (i = 0; i < child_device_num; i++) {
4688c2ecf20Sopenharmony_ci		p_child = &(p_defs->devices[i]);
4698c2ecf20Sopenharmony_ci		if (!p_child->device_type) {
4708c2ecf20Sopenharmony_ci			/* skip the device block if device type is invalid */
4718c2ecf20Sopenharmony_ci			continue;
4728c2ecf20Sopenharmony_ci		}
4738c2ecf20Sopenharmony_ci		count++;
4748c2ecf20Sopenharmony_ci	}
4758c2ecf20Sopenharmony_ci	if (!count) {
4768c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("no child dev is parsed from VBT\n");
4778c2ecf20Sopenharmony_ci		return;
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci	dev_priv->child_dev = kcalloc(count, sizeof(*p_child), GFP_KERNEL);
4808c2ecf20Sopenharmony_ci	if (!dev_priv->child_dev) {
4818c2ecf20Sopenharmony_ci		DRM_DEBUG_KMS("No memory space for child devices\n");
4828c2ecf20Sopenharmony_ci		return;
4838c2ecf20Sopenharmony_ci	}
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci	dev_priv->child_dev_num = count;
4868c2ecf20Sopenharmony_ci	count = 0;
4878c2ecf20Sopenharmony_ci	for (i = 0; i < child_device_num; i++) {
4888c2ecf20Sopenharmony_ci		p_child = &(p_defs->devices[i]);
4898c2ecf20Sopenharmony_ci		if (!p_child->device_type) {
4908c2ecf20Sopenharmony_ci			/* skip the device block if device type is invalid */
4918c2ecf20Sopenharmony_ci			continue;
4928c2ecf20Sopenharmony_ci		}
4938c2ecf20Sopenharmony_ci		child_dev_ptr = dev_priv->child_dev + count;
4948c2ecf20Sopenharmony_ci		count++;
4958c2ecf20Sopenharmony_ci		memcpy((void *)child_dev_ptr, (void *)p_child,
4968c2ecf20Sopenharmony_ci					sizeof(*p_child));
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	return;
4998c2ecf20Sopenharmony_ci}
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci/**
5038c2ecf20Sopenharmony_ci * psb_intel_init_bios - initialize VBIOS settings & find VBT
5048c2ecf20Sopenharmony_ci * @dev: DRM device
5058c2ecf20Sopenharmony_ci *
5068c2ecf20Sopenharmony_ci * Loads the Video BIOS and checks that the VBT exists.  Sets scratch registers
5078c2ecf20Sopenharmony_ci * to appropriate values.
5088c2ecf20Sopenharmony_ci *
5098c2ecf20Sopenharmony_ci * VBT existence is a sanity check that is relied on by other i830_bios.c code.
5108c2ecf20Sopenharmony_ci * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may
5118c2ecf20Sopenharmony_ci * feed an updated VBT back through that, compared to what we'll fetch using
5128c2ecf20Sopenharmony_ci * this method of groping around in the BIOS data.
5138c2ecf20Sopenharmony_ci *
5148c2ecf20Sopenharmony_ci * Returns 0 on success, nonzero on failure.
5158c2ecf20Sopenharmony_ci */
5168c2ecf20Sopenharmony_ciint psb_intel_init_bios(struct drm_device *dev)
5178c2ecf20Sopenharmony_ci{
5188c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
5198c2ecf20Sopenharmony_ci	struct pci_dev *pdev = dev->pdev;
5208c2ecf20Sopenharmony_ci	struct vbt_header *vbt = NULL;
5218c2ecf20Sopenharmony_ci	struct bdb_header *bdb = NULL;
5228c2ecf20Sopenharmony_ci	u8 __iomem *bios = NULL;
5238c2ecf20Sopenharmony_ci	size_t size;
5248c2ecf20Sopenharmony_ci	int i;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci
5278c2ecf20Sopenharmony_ci	dev_priv->panel_type = 0xff;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	/* XXX Should this validation be moved to intel_opregion.c? */
5308c2ecf20Sopenharmony_ci	if (dev_priv->opregion.vbt) {
5318c2ecf20Sopenharmony_ci		struct vbt_header *vbt = dev_priv->opregion.vbt;
5328c2ecf20Sopenharmony_ci		if (memcmp(vbt->signature, "$VBT", 4) == 0) {
5338c2ecf20Sopenharmony_ci			DRM_DEBUG_KMS("Using VBT from OpRegion: %20s\n",
5348c2ecf20Sopenharmony_ci					 vbt->signature);
5358c2ecf20Sopenharmony_ci			bdb = (struct bdb_header *)((char *)vbt + vbt->bdb_offset);
5368c2ecf20Sopenharmony_ci		} else
5378c2ecf20Sopenharmony_ci			dev_priv->opregion.vbt = NULL;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci	if (bdb == NULL) {
5418c2ecf20Sopenharmony_ci		bios = pci_map_rom(pdev, &size);
5428c2ecf20Sopenharmony_ci		if (!bios)
5438c2ecf20Sopenharmony_ci			return -1;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci		/* Scour memory looking for the VBT signature */
5468c2ecf20Sopenharmony_ci		for (i = 0; i + 4 < size; i++) {
5478c2ecf20Sopenharmony_ci			if (!memcmp(bios + i, "$VBT", 4)) {
5488c2ecf20Sopenharmony_ci				vbt = (struct vbt_header *)(bios + i);
5498c2ecf20Sopenharmony_ci				break;
5508c2ecf20Sopenharmony_ci			}
5518c2ecf20Sopenharmony_ci		}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci		if (!vbt) {
5548c2ecf20Sopenharmony_ci			dev_err(dev->dev, "VBT signature missing\n");
5558c2ecf20Sopenharmony_ci			pci_unmap_rom(pdev, bios);
5568c2ecf20Sopenharmony_ci			return -1;
5578c2ecf20Sopenharmony_ci		}
5588c2ecf20Sopenharmony_ci		bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset);
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	/* Grab useful general dxefinitions */
5628c2ecf20Sopenharmony_ci	parse_general_features(dev_priv, bdb);
5638c2ecf20Sopenharmony_ci	parse_driver_features(dev_priv, bdb);
5648c2ecf20Sopenharmony_ci	parse_lfp_panel_data(dev_priv, bdb);
5658c2ecf20Sopenharmony_ci	parse_sdvo_panel_data(dev_priv, bdb);
5668c2ecf20Sopenharmony_ci	parse_sdvo_device_mapping(dev_priv, bdb);
5678c2ecf20Sopenharmony_ci	parse_device_mapping(dev_priv, bdb);
5688c2ecf20Sopenharmony_ci	parse_backlight_data(dev_priv, bdb);
5698c2ecf20Sopenharmony_ci	parse_edp(dev_priv, bdb);
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (bios)
5728c2ecf20Sopenharmony_ci		pci_unmap_rom(pdev, bios);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	return 0;
5758c2ecf20Sopenharmony_ci}
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci/**
5788c2ecf20Sopenharmony_ci * Destroy and free VBT data
5798c2ecf20Sopenharmony_ci */
5808c2ecf20Sopenharmony_civoid psb_intel_destroy_bios(struct drm_device *dev)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	struct drm_psb_private *dev_priv = dev->dev_private;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	kfree(dev_priv->sdvo_lvds_vbt_mode);
5858c2ecf20Sopenharmony_ci	kfree(dev_priv->lfp_lvds_vbt_mode);
5868c2ecf20Sopenharmony_ci	kfree(dev_priv->lvds_bl);
5878c2ecf20Sopenharmony_ci}
588