1f910b411SAlan Cox /* 2f910b411SAlan Cox * Copyright (c) 2006 Intel Corporation 3f910b411SAlan Cox * 4f910b411SAlan Cox * This program is free software; you can redistribute it and/or modify it 5f910b411SAlan Cox * under the terms and conditions of the GNU General Public License, 6f910b411SAlan Cox * version 2, as published by the Free Software Foundation. 7f910b411SAlan Cox * 8f910b411SAlan Cox * This program is distributed in the hope it will be useful, but WITHOUT 9f910b411SAlan Cox * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 10f910b411SAlan Cox * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 11f910b411SAlan Cox * more details. 12f910b411SAlan Cox * 13f910b411SAlan Cox * You should have received a copy of the GNU General Public License along with 14f910b411SAlan Cox * this program; if not, write to the Free Software Foundation, Inc., 15f910b411SAlan Cox * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 16f910b411SAlan Cox * 17f910b411SAlan Cox * Authors: 18f910b411SAlan Cox * Eric Anholt <eric@anholt.net> 19f910b411SAlan Cox * 20f910b411SAlan Cox */ 21f910b411SAlan Cox #include <drm/drmP.h> 22f910b411SAlan Cox #include <drm/drm.h> 23838fa588SAlan Cox #include "gma_drm.h" 24f910b411SAlan Cox #include "psb_drv.h" 25f910b411SAlan Cox #include "psb_intel_drv.h" 26f910b411SAlan Cox #include "psb_intel_reg.h" 27f910b411SAlan Cox #include "intel_bios.h" 28f910b411SAlan Cox 29f910b411SAlan Cox 30f910b411SAlan Cox static void *find_section(struct bdb_header *bdb, int section_id) 31f910b411SAlan Cox { 32f910b411SAlan Cox u8 *base = (u8 *)bdb; 33f910b411SAlan Cox int index = 0; 34f910b411SAlan Cox u16 total, current_size; 35f910b411SAlan Cox u8 current_id; 36f910b411SAlan Cox 37f910b411SAlan Cox /* skip to first section */ 38f910b411SAlan Cox index += bdb->header_size; 39f910b411SAlan Cox total = bdb->bdb_size; 40f910b411SAlan Cox 41f910b411SAlan Cox /* walk the sections looking for section_id */ 42f910b411SAlan Cox while (index < total) { 43f910b411SAlan Cox current_id = *(base + index); 44f910b411SAlan Cox index++; 45f910b411SAlan Cox current_size = *((u16 *)(base + index)); 46f910b411SAlan Cox index += 2; 47f910b411SAlan Cox if (current_id == section_id) 48f910b411SAlan Cox return base + index; 49f910b411SAlan Cox index += current_size; 50f910b411SAlan Cox } 51f910b411SAlan Cox 52f910b411SAlan Cox return NULL; 53f910b411SAlan Cox } 54f910b411SAlan Cox 55f910b411SAlan Cox static void fill_detail_timing_data(struct drm_display_mode *panel_fixed_mode, 56f910b411SAlan Cox struct lvds_dvo_timing *dvo_timing) 57f910b411SAlan Cox { 58f910b411SAlan Cox panel_fixed_mode->hdisplay = (dvo_timing->hactive_hi << 8) | 59f910b411SAlan Cox dvo_timing->hactive_lo; 60f910b411SAlan Cox panel_fixed_mode->hsync_start = panel_fixed_mode->hdisplay + 61f910b411SAlan Cox ((dvo_timing->hsync_off_hi << 8) | dvo_timing->hsync_off_lo); 62f910b411SAlan Cox panel_fixed_mode->hsync_end = panel_fixed_mode->hsync_start + 63f910b411SAlan Cox dvo_timing->hsync_pulse_width; 64f910b411SAlan Cox panel_fixed_mode->htotal = panel_fixed_mode->hdisplay + 65f910b411SAlan Cox ((dvo_timing->hblank_hi << 8) | dvo_timing->hblank_lo); 66f910b411SAlan Cox 67f910b411SAlan Cox panel_fixed_mode->vdisplay = (dvo_timing->vactive_hi << 8) | 68f910b411SAlan Cox dvo_timing->vactive_lo; 69f910b411SAlan Cox panel_fixed_mode->vsync_start = panel_fixed_mode->vdisplay + 70f910b411SAlan Cox dvo_timing->vsync_off; 71f910b411SAlan Cox panel_fixed_mode->vsync_end = panel_fixed_mode->vsync_start + 72f910b411SAlan Cox dvo_timing->vsync_pulse_width; 73f910b411SAlan Cox panel_fixed_mode->vtotal = panel_fixed_mode->vdisplay + 74f910b411SAlan Cox ((dvo_timing->vblank_hi << 8) | dvo_timing->vblank_lo); 75f910b411SAlan Cox panel_fixed_mode->clock = dvo_timing->clock * 10; 76f910b411SAlan Cox panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED; 77f910b411SAlan Cox 78f910b411SAlan Cox /* Some VBTs have bogus h/vtotal values */ 79f910b411SAlan Cox if (panel_fixed_mode->hsync_end > panel_fixed_mode->htotal) 80f910b411SAlan Cox panel_fixed_mode->htotal = panel_fixed_mode->hsync_end + 1; 81f910b411SAlan Cox if (panel_fixed_mode->vsync_end > panel_fixed_mode->vtotal) 82f910b411SAlan Cox panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end + 1; 83f910b411SAlan Cox 84f910b411SAlan Cox drm_mode_set_name(panel_fixed_mode); 85f910b411SAlan Cox } 86f910b411SAlan Cox 87f910b411SAlan Cox static void parse_backlight_data(struct drm_psb_private *dev_priv, 88f910b411SAlan Cox struct bdb_header *bdb) 89f910b411SAlan Cox { 90f910b411SAlan Cox struct bdb_lvds_backlight *vbt_lvds_bl = NULL; 91f910b411SAlan Cox struct bdb_lvds_backlight *lvds_bl; 92f910b411SAlan Cox u8 p_type = 0; 93f910b411SAlan Cox void *bl_start = NULL; 94f910b411SAlan Cox struct bdb_lvds_options *lvds_opts 95f910b411SAlan Cox = find_section(bdb, BDB_LVDS_OPTIONS); 96f910b411SAlan Cox 97f910b411SAlan Cox dev_priv->lvds_bl = NULL; 98f910b411SAlan Cox 99f910b411SAlan Cox if (lvds_opts) 100f910b411SAlan Cox p_type = lvds_opts->panel_type; 101f910b411SAlan Cox else 102f910b411SAlan Cox return; 103f910b411SAlan Cox 104f910b411SAlan Cox bl_start = find_section(bdb, BDB_LVDS_BACKLIGHT); 105f910b411SAlan Cox vbt_lvds_bl = (struct bdb_lvds_backlight *)(bl_start + 1) + p_type; 106f910b411SAlan Cox 107f910b411SAlan Cox lvds_bl = kzalloc(sizeof(*vbt_lvds_bl), GFP_KERNEL); 108f910b411SAlan Cox if (!lvds_bl) { 109f910b411SAlan Cox dev_err(dev_priv->dev->dev, "out of memory for backlight data\n"); 110f910b411SAlan Cox return; 111f910b411SAlan Cox } 112f910b411SAlan Cox memcpy(lvds_bl, vbt_lvds_bl, sizeof(*vbt_lvds_bl)); 113f910b411SAlan Cox dev_priv->lvds_bl = lvds_bl; 114f910b411SAlan Cox } 115f910b411SAlan Cox 116f910b411SAlan Cox /* Try to find integrated panel data */ 117f910b411SAlan Cox static void parse_lfp_panel_data(struct drm_psb_private *dev_priv, 118f910b411SAlan Cox struct bdb_header *bdb) 119f910b411SAlan Cox { 120f910b411SAlan Cox struct bdb_lvds_options *lvds_options; 121f910b411SAlan Cox struct bdb_lvds_lfp_data *lvds_lfp_data; 122f910b411SAlan Cox struct bdb_lvds_lfp_data_entry *entry; 123f910b411SAlan Cox struct lvds_dvo_timing *dvo_timing; 124f910b411SAlan Cox struct drm_display_mode *panel_fixed_mode; 125f910b411SAlan Cox 126f910b411SAlan Cox /* Defaults if we can't find VBT info */ 127f910b411SAlan Cox dev_priv->lvds_dither = 0; 128f910b411SAlan Cox dev_priv->lvds_vbt = 0; 129f910b411SAlan Cox 130f910b411SAlan Cox lvds_options = find_section(bdb, BDB_LVDS_OPTIONS); 131f910b411SAlan Cox if (!lvds_options) 132f910b411SAlan Cox return; 133f910b411SAlan Cox 134f910b411SAlan Cox dev_priv->lvds_dither = lvds_options->pixel_dither; 135f910b411SAlan Cox if (lvds_options->panel_type == 0xff) 136f910b411SAlan Cox return; 137f910b411SAlan Cox 138f910b411SAlan Cox lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA); 139f910b411SAlan Cox if (!lvds_lfp_data) 140f910b411SAlan Cox return; 141f910b411SAlan Cox 142f910b411SAlan Cox 143f910b411SAlan Cox entry = &lvds_lfp_data->data[lvds_options->panel_type]; 144f910b411SAlan Cox dvo_timing = &entry->dvo_timing; 145f910b411SAlan Cox 146f910b411SAlan Cox panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), 147f910b411SAlan Cox GFP_KERNEL); 148f910b411SAlan Cox if (panel_fixed_mode == NULL) { 149f910b411SAlan Cox dev_err(dev_priv->dev->dev, "out of memory for fixed panel mode\n"); 150f910b411SAlan Cox return; 151f910b411SAlan Cox } 152f910b411SAlan Cox 153f910b411SAlan Cox dev_priv->lvds_vbt = 1; 154f910b411SAlan Cox fill_detail_timing_data(panel_fixed_mode, dvo_timing); 155f910b411SAlan Cox 156f910b411SAlan Cox if (panel_fixed_mode->htotal > 0 && panel_fixed_mode->vtotal > 0) { 157f910b411SAlan Cox dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode; 158f910b411SAlan Cox drm_mode_debug_printmodeline(panel_fixed_mode); 159f910b411SAlan Cox } else { 160f910b411SAlan Cox dev_dbg(dev_priv->dev->dev, "ignoring invalid LVDS VBT\n"); 161f910b411SAlan Cox dev_priv->lvds_vbt = 0; 162f910b411SAlan Cox kfree(panel_fixed_mode); 163f910b411SAlan Cox } 164f910b411SAlan Cox return; 165f910b411SAlan Cox } 166f910b411SAlan Cox 167f910b411SAlan Cox /* Try to find sdvo panel data */ 168f910b411SAlan Cox static void parse_sdvo_panel_data(struct drm_psb_private *dev_priv, 169f910b411SAlan Cox struct bdb_header *bdb) 170f910b411SAlan Cox { 171f910b411SAlan Cox struct bdb_sdvo_lvds_options *sdvo_lvds_options; 172f910b411SAlan Cox struct lvds_dvo_timing *dvo_timing; 173f910b411SAlan Cox struct drm_display_mode *panel_fixed_mode; 174f910b411SAlan Cox 175f910b411SAlan Cox dev_priv->sdvo_lvds_vbt_mode = NULL; 176f910b411SAlan Cox 177f910b411SAlan Cox sdvo_lvds_options = find_section(bdb, BDB_SDVO_LVDS_OPTIONS); 178f910b411SAlan Cox if (!sdvo_lvds_options) 179f910b411SAlan Cox return; 180f910b411SAlan Cox 181f910b411SAlan Cox dvo_timing = find_section(bdb, BDB_SDVO_PANEL_DTDS); 182f910b411SAlan Cox if (!dvo_timing) 183f910b411SAlan Cox return; 184f910b411SAlan Cox 185f910b411SAlan Cox panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL); 186f910b411SAlan Cox 187f910b411SAlan Cox if (!panel_fixed_mode) 188f910b411SAlan Cox return; 189f910b411SAlan Cox 190f910b411SAlan Cox fill_detail_timing_data(panel_fixed_mode, 191f910b411SAlan Cox dvo_timing + sdvo_lvds_options->panel_type); 192f910b411SAlan Cox 193f910b411SAlan Cox dev_priv->sdvo_lvds_vbt_mode = panel_fixed_mode; 194f910b411SAlan Cox 195f910b411SAlan Cox return; 196f910b411SAlan Cox } 197f910b411SAlan Cox 198f910b411SAlan Cox static void parse_general_features(struct drm_psb_private *dev_priv, 199f910b411SAlan Cox struct bdb_header *bdb) 200f910b411SAlan Cox { 201f910b411SAlan Cox struct bdb_general_features *general; 202f910b411SAlan Cox 203f910b411SAlan Cox /* Set sensible defaults in case we can't find the general block */ 204f910b411SAlan Cox dev_priv->int_tv_support = 1; 205f910b411SAlan Cox dev_priv->int_crt_support = 1; 206f910b411SAlan Cox 207f910b411SAlan Cox general = find_section(bdb, BDB_GENERAL_FEATURES); 208f910b411SAlan Cox if (general) { 209f910b411SAlan Cox dev_priv->int_tv_support = general->int_tv_support; 210f910b411SAlan Cox dev_priv->int_crt_support = general->int_crt_support; 211f910b411SAlan Cox dev_priv->lvds_use_ssc = general->enable_ssc; 212f910b411SAlan Cox 213f910b411SAlan Cox if (dev_priv->lvds_use_ssc) { 214f910b411SAlan Cox dev_priv->lvds_ssc_freq 215f910b411SAlan Cox = general->ssc_freq ? 100 : 96; 216f910b411SAlan Cox } 217f910b411SAlan Cox } 218f910b411SAlan Cox } 219f910b411SAlan Cox 220f910b411SAlan Cox /** 221f910b411SAlan Cox * psb_intel_init_bios - initialize VBIOS settings & find VBT 222f910b411SAlan Cox * @dev: DRM device 223f910b411SAlan Cox * 224f910b411SAlan Cox * Loads the Video BIOS and checks that the VBT exists. Sets scratch registers 225f910b411SAlan Cox * to appropriate values. 226f910b411SAlan Cox * 227f910b411SAlan Cox * VBT existence is a sanity check that is relied on by other i830_bios.c code. 228f910b411SAlan Cox * Note that it would be better to use a BIOS call to get the VBT, as BIOSes may 229f910b411SAlan Cox * feed an updated VBT back through that, compared to what we'll fetch using 230f910b411SAlan Cox * this method of groping around in the BIOS data. 231f910b411SAlan Cox * 232f910b411SAlan Cox * Returns 0 on success, nonzero on failure. 233f910b411SAlan Cox */ 234f910b411SAlan Cox bool psb_intel_init_bios(struct drm_device *dev) 235f910b411SAlan Cox { 236f910b411SAlan Cox struct drm_psb_private *dev_priv = dev->dev_private; 237f910b411SAlan Cox struct pci_dev *pdev = dev->pdev; 238f910b411SAlan Cox struct vbt_header *vbt = NULL; 239f910b411SAlan Cox struct bdb_header *bdb; 240f910b411SAlan Cox u8 __iomem *bios; 241f910b411SAlan Cox size_t size; 242f910b411SAlan Cox int i; 243f910b411SAlan Cox 244f910b411SAlan Cox bios = pci_map_rom(pdev, &size); 245f910b411SAlan Cox if (!bios) 246f910b411SAlan Cox return -1; 247f910b411SAlan Cox 248f910b411SAlan Cox /* Scour memory looking for the VBT signature */ 249f910b411SAlan Cox for (i = 0; i + 4 < size; i++) { 250f910b411SAlan Cox if (!memcmp(bios + i, "$VBT", 4)) { 251f910b411SAlan Cox vbt = (struct vbt_header *)(bios + i); 252f910b411SAlan Cox break; 253f910b411SAlan Cox } 254f910b411SAlan Cox } 255f910b411SAlan Cox 256f910b411SAlan Cox if (!vbt) { 257f910b411SAlan Cox dev_err(dev->dev, "VBT signature missing\n"); 258f910b411SAlan Cox pci_unmap_rom(pdev, bios); 259f910b411SAlan Cox return -1; 260f910b411SAlan Cox } 261f910b411SAlan Cox 262f910b411SAlan Cox bdb = (struct bdb_header *)(bios + i + vbt->bdb_offset); 263f910b411SAlan Cox 264f910b411SAlan Cox /* Grab useful general definitions */ 265f910b411SAlan Cox parse_general_features(dev_priv, bdb); 266f910b411SAlan Cox parse_lfp_panel_data(dev_priv, bdb); 267f910b411SAlan Cox parse_sdvo_panel_data(dev_priv, bdb); 268f910b411SAlan Cox parse_backlight_data(dev_priv, bdb); 269f910b411SAlan Cox 270f910b411SAlan Cox pci_unmap_rom(pdev, bios); 271f910b411SAlan Cox 272f910b411SAlan Cox return 0; 273f910b411SAlan Cox } 274f910b411SAlan Cox 275f910b411SAlan Cox /** 276f910b411SAlan Cox * Destroy and free VBT data 277f910b411SAlan Cox */ 278f910b411SAlan Cox void psb_intel_destroy_bios(struct drm_device *dev) 279f910b411SAlan Cox { 280f910b411SAlan Cox struct drm_psb_private *dev_priv = dev->dev_private; 281f910b411SAlan Cox struct drm_display_mode *sdvo_lvds_vbt_mode = 282f910b411SAlan Cox dev_priv->sdvo_lvds_vbt_mode; 283f910b411SAlan Cox struct drm_display_mode *lfp_lvds_vbt_mode = 284f910b411SAlan Cox dev_priv->lfp_lvds_vbt_mode; 285f910b411SAlan Cox struct bdb_lvds_backlight *lvds_bl = 286f910b411SAlan Cox dev_priv->lvds_bl; 287f910b411SAlan Cox 288f910b411SAlan Cox /*free sdvo panel mode*/ 289f910b411SAlan Cox if (sdvo_lvds_vbt_mode) { 290f910b411SAlan Cox dev_priv->sdvo_lvds_vbt_mode = NULL; 291f910b411SAlan Cox kfree(sdvo_lvds_vbt_mode); 292f910b411SAlan Cox } 293f910b411SAlan Cox 294f910b411SAlan Cox if (lfp_lvds_vbt_mode) { 295f910b411SAlan Cox dev_priv->lfp_lvds_vbt_mode = NULL; 296f910b411SAlan Cox kfree(lfp_lvds_vbt_mode); 297f910b411SAlan Cox } 298f910b411SAlan Cox 299f910b411SAlan Cox if (lvds_bl) { 300f910b411SAlan Cox dev_priv->lvds_bl = NULL; 301f910b411SAlan Cox kfree(lvds_bl); 302f910b411SAlan Cox } 303f910b411SAlan Cox } 304