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