#include <drm/i915_drm.h>
#include "display/intel_display.h"
+#include "display/intel_display_types.h"
#include "display/intel_gmbus.h"
#include "i915_drv.h"
/* Wrapper for VBT child device config */
struct display_device_data {
struct child_device_config child;
+ struct dsc_compression_parameters_entry *dsc;
struct list_head node;
};
return (const struct lvds_fp_timing *)((const u8 *)bdb + ofs);
}
-/* Try to find integrated panel data */
+/* Parse general panel options */
static void
-parse_lfp_panel_data(struct drm_i915_private *dev_priv,
- const struct bdb_header *bdb)
+parse_panel_options(struct drm_i915_private *dev_priv,
+ const struct bdb_header *bdb)
{
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;
- const struct lvds_fp_timing *fp_timing;
- struct drm_display_mode *panel_fixed_mode;
int panel_type;
int drrs_mode;
int ret;
DRM_DEBUG_KMS("DRRS not supported (VBT input)\n");
break;
}
+}
+
+/* Try to find integrated panel timing data */
+static void
+parse_lfp_panel_dtd(struct drm_i915_private *dev_priv,
+ const struct bdb_header *bdb)
+{
+ 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;
+ const struct lvds_fp_timing *fp_timing;
+ struct drm_display_mode *panel_fixed_mode;
+ int panel_type = dev_priv->vbt.panel_type;
lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
if (!lvds_lfp_data)
dev_priv->vbt.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 legacy lfp table:\n");
drm_mode_debug_printmodeline(panel_fixed_mode);
fp_timing = get_lvds_fp_timing(bdb, lvds_lfp_data,
}
}
+static void
+parse_generic_dtd(struct drm_i915_private *dev_priv,
+ const struct bdb_header *bdb)
+{
+ const struct bdb_generic_dtd *generic_dtd;
+ const struct generic_dtd_entry *dtd;
+ struct drm_display_mode *panel_fixed_mode;
+ int num_dtd;
+
+ generic_dtd = find_section(bdb, BDB_GENERIC_DTD);
+ if (!generic_dtd)
+ return;
+
+ if (generic_dtd->gdtd_size < sizeof(struct generic_dtd_entry)) {
+ DRM_ERROR("GDTD size %u is too small.\n",
+ generic_dtd->gdtd_size);
+ return;
+ } else if (generic_dtd->gdtd_size !=
+ sizeof(struct generic_dtd_entry)) {
+ DRM_ERROR("Unexpected GDTD size %u\n", generic_dtd->gdtd_size);
+ /* DTD has unknown fields, but keep going */
+ }
+
+ num_dtd = (get_blocksize(generic_dtd) -
+ sizeof(struct bdb_generic_dtd)) / generic_dtd->gdtd_size;
+ if (dev_priv->vbt.panel_type > num_dtd) {
+ DRM_ERROR("Panel type %d not found in table of %d DTD's\n",
+ dev_priv->vbt.panel_type, num_dtd);
+ return;
+ }
+
+ dtd = &generic_dtd->dtd[dev_priv->vbt.panel_type];
+
+ panel_fixed_mode = kzalloc(sizeof(*panel_fixed_mode), GFP_KERNEL);
+ if (!panel_fixed_mode)
+ return;
+
+ panel_fixed_mode->hdisplay = dtd->hactive;
+ panel_fixed_mode->hsync_start =
+ panel_fixed_mode->hdisplay + dtd->hfront_porch;
+ panel_fixed_mode->hsync_end =
+ panel_fixed_mode->hsync_start + dtd->hsync;
+ panel_fixed_mode->htotal = panel_fixed_mode->hsync_end;
+
+ panel_fixed_mode->vdisplay = dtd->vactive;
+ panel_fixed_mode->vsync_start =
+ panel_fixed_mode->vdisplay + dtd->vfront_porch;
+ panel_fixed_mode->vsync_end =
+ panel_fixed_mode->vsync_start + dtd->vsync;
+ panel_fixed_mode->vtotal = panel_fixed_mode->vsync_end;
+
+ panel_fixed_mode->clock = dtd->pixel_clock;
+ panel_fixed_mode->width_mm = dtd->width_mm;
+ panel_fixed_mode->height_mm = dtd->height_mm;
+
+ panel_fixed_mode->type = DRM_MODE_TYPE_PREFERRED;
+ drm_mode_set_name(panel_fixed_mode);
+
+ if (dtd->hsync_positive_polarity)
+ panel_fixed_mode->flags |= DRM_MODE_FLAG_PHSYNC;
+ else
+ panel_fixed_mode->flags |= DRM_MODE_FLAG_NHSYNC;
+
+ if (dtd->vsync_positive_polarity)
+ panel_fixed_mode->flags |= DRM_MODE_FLAG_PVSYNC;
+ else
+ panel_fixed_mode->flags |= DRM_MODE_FLAG_NVSYNC;
+
+ DRM_DEBUG_KMS("Found panel mode in BIOS VBT generic dtd table:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
+
+ dev_priv->vbt.lfp_lvds_vbt_mode = panel_fixed_mode;
+}
+
+static void
+parse_panel_dtd(struct drm_i915_private *dev_priv,
+ const struct bdb_header *bdb)
+{
+ /*
+ * Older VBTs provided provided DTD information for internal displays
+ * through the "LFP panel DTD" block (42). As of VBT revision 229,
+ * that block is now deprecated and DTD information should be provided
+ * via a newer "generic DTD" block (58). Just to be safe, we'll
+ * try the new generic DTD block first on VBT >= 229, but still fall
+ * back to trying the old LFP block if that fails.
+ */
+ if (bdb->version >= 229)
+ parse_generic_dtd(dev_priv, bdb);
+ if (!dev_priv->vbt.lfp_lvds_vbt_mode)
+ parse_lfp_panel_dtd(dev_priv, bdb);
+}
+
static void
parse_lfp_backlight(struct drm_i915_private *dev_priv,
const struct bdb_header *bdb)
dev_priv->vbt.int_lvds_support = 0;
}
- DRM_DEBUG_KMS("DRRS State Enabled:%d\n", driver->drrs_enabled);
+ if (bdb->version < 228) {
+ DRM_DEBUG_KMS("DRRS State Enabled:%d\n", driver->drrs_enabled);
+ /*
+ * If DRRS is not supported, drrs_type has to be set to 0.
+ * This is because, VBT is configured in such a way that
+ * static DRRS is 0 and DRRS not supported is represented by
+ * driver->drrs_enabled=false
+ */
+ if (!driver->drrs_enabled)
+ dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
+
+ dev_priv->vbt.psr.enable = driver->psr_enabled;
+ }
+}
+
+static void
+parse_power_conservation_features(struct drm_i915_private *dev_priv,
+ const struct bdb_header *bdb)
+{
+ const struct bdb_lfp_power *power;
+ u8 panel_type = dev_priv->vbt.panel_type;
+
+ if (bdb->version < 228)
+ return;
+
+ power = find_section(bdb, BDB_LVDS_POWER);
+ if (!power)
+ return;
+
+ dev_priv->vbt.psr.enable = power->psr & BIT(panel_type);
+
/*
* If DRRS is not supported, drrs_type has to be set to 0.
* This is because, VBT is configured in such a way that
* static DRRS is 0 and DRRS not supported is represented by
- * driver->drrs_enabled=false
+ * power->drrs & BIT(panel_type)=false
*/
- if (!driver->drrs_enabled)
+ if (!(power->drrs & BIT(panel_type)))
dev_priv->vbt.drrs_type = DRRS_NOT_SUPPORTED;
- dev_priv->vbt.psr.enable = driver->psr_enabled;
}
static void
memset(dev_priv->vbt.dsi.sequence, 0, sizeof(dev_priv->vbt.dsi.sequence));
}
+static void
+parse_compression_parameters(struct drm_i915_private *i915,
+ const struct bdb_header *bdb)
+{
+ const struct bdb_compression_parameters *params;
+ struct display_device_data *devdata;
+ const struct child_device_config *child;
+ u16 block_size;
+ int index;
+
+ if (bdb->version < 198)
+ return;
+
+ params = find_section(bdb, BDB_COMPRESSION_PARAMETERS);
+ if (params) {
+ /* Sanity checks */
+ if (params->entry_size != sizeof(params->data[0])) {
+ DRM_DEBUG_KMS("VBT: unsupported compression param entry size\n");
+ return;
+ }
+
+ block_size = get_blocksize(params);
+ if (block_size < sizeof(*params)) {
+ DRM_DEBUG_KMS("VBT: expected 16 compression param entries\n");
+ return;
+ }
+ }
+
+ list_for_each_entry(devdata, &i915->vbt.display_devices, node) {
+ child = &devdata->child;
+
+ if (!child->compression_enable)
+ continue;
+
+ if (!params) {
+ DRM_DEBUG_KMS("VBT: compression params not available\n");
+ continue;
+ }
+
+ if (child->compression_method_cps) {
+ DRM_DEBUG_KMS("VBT: CPS compression not supported\n");
+ continue;
+ }
+
+ index = child->compression_structure_index;
+
+ devdata->dsc = kmemdup(¶ms->data[index],
+ sizeof(*devdata->dsc), GFP_KERNEL);
+ }
+}
+
static u8 translate_iboost(u8 val)
{
static const u8 mapping[] = { 1, 3, 7 }; /* See VBT spec */
}
static void parse_ddi_port(struct drm_i915_private *dev_priv,
- const struct child_device_config *child,
+ struct display_device_data *devdata,
u8 bdb_version)
{
+ const struct child_device_config *child = &devdata->child;
struct ddi_vbt_port_info *info;
bool is_dvi, is_hdmi, is_dp, is_edp, is_crt;
enum port port;
is_hdmi = is_dvi && (child->device_type & DEVICE_TYPE_NOT_HDMI_OUTPUT) == 0;
is_edp = is_dp && (child->device_type & DEVICE_TYPE_INTERNAL_CONNECTOR);
- if (port == PORT_A && is_dvi) {
+ if (port == PORT_A && is_dvi && INTEL_GEN(dev_priv) < 12) {
DRM_DEBUG_KMS("VBT claims port A supports DVI%s, ignoring\n",
is_hdmi ? "/HDMI" : "");
is_dvi = false;
if (bdb_version >= 209)
info->supports_tbt = child->tbt;
- DRM_DEBUG_KMS("Port %c VBT info: CRT:%d DVI:%d HDMI:%d DP:%d eDP:%d LSPCON:%d USB-Type-C:%d TBT:%d\n",
+ DRM_DEBUG_KMS("Port %c VBT info: CRT:%d DVI:%d HDMI:%d DP:%d eDP:%d LSPCON:%d USB-Type-C:%d TBT:%d DSC:%d\n",
port_name(port), is_crt, is_dvi, is_hdmi, is_dp, is_edp,
HAS_LSPCON(dev_priv) && child->lspcon,
- info->supports_typec_usb, info->supports_tbt);
+ info->supports_typec_usb, info->supports_tbt,
+ devdata->dsc != NULL);
if (is_edp && is_dvi)
DRM_DEBUG_KMS("Internal DP port %c is TMDS compatible\n",
static void parse_ddi_ports(struct drm_i915_private *dev_priv, u8 bdb_version)
{
- const struct display_device_data *devdata;
+ struct display_device_data *devdata;
if (!HAS_DDI(dev_priv) && !IS_CHERRYVIEW(dev_priv))
return;
return;
list_for_each_entry(devdata, &dev_priv->vbt.display_devices, node)
- parse_ddi_port(dev_priv, &devdata->child, bdb_version);
+ parse_ddi_port(dev_priv, devdata, bdb_version);
}
static void
return vbt;
}
-static const struct vbt_header *find_vbt(void __iomem *oprom, size_t size)
+static struct vbt_header *oprom_get_vbt(struct drm_i915_private *dev_priv)
{
- size_t i;
+ struct pci_dev *pdev = dev_priv->drm.pdev;
+ void __iomem *p = NULL, *oprom;
+ struct vbt_header *vbt;
+ u16 vbt_size;
+ size_t i, size;
- /* Scour memory looking for the VBT signature. */
- for (i = 0; i + 4 < size; i++) {
- void *vbt;
+ oprom = pci_map_rom(pdev, &size);
+ if (!oprom)
+ return NULL;
+ /* Scour memory looking for the VBT signature. */
+ for (i = 0; i + 4 < size; i += 4) {
if (ioread32(oprom + i) != *((const u32 *)"$VBT"))
continue;
- /*
- * This is the one place where we explicitly discard the address
- * space (__iomem) of the BIOS/VBT.
- */
- vbt = (void __force *)oprom + i;
- if (intel_bios_is_valid_vbt(vbt, size - i))
- return vbt;
-
+ p = oprom + i;
+ size -= i;
break;
}
+ if (!p)
+ goto err_unmap_oprom;
+
+ if (sizeof(struct vbt_header) > size) {
+ DRM_DEBUG_DRIVER("VBT header incomplete\n");
+ goto err_unmap_oprom;
+ }
+
+ vbt_size = ioread16(p + offsetof(struct vbt_header, vbt_size));
+ if (vbt_size > size) {
+ DRM_DEBUG_DRIVER("VBT incomplete (vbt_size overflows)\n");
+ goto err_unmap_oprom;
+ }
+
+ /* The rest will be validated by intel_bios_is_valid_vbt() */
+ vbt = kmalloc(vbt_size, GFP_KERNEL);
+ if (!vbt)
+ goto err_unmap_oprom;
+
+ memcpy_fromio(vbt, p, vbt_size);
+
+ if (!intel_bios_is_valid_vbt(vbt, vbt_size))
+ goto err_free_vbt;
+
+ pci_unmap_rom(pdev, oprom);
+
+ return vbt;
+
+err_free_vbt:
+ kfree(vbt);
+err_unmap_oprom:
+ pci_unmap_rom(pdev, oprom);
+
return NULL;
}
*/
void intel_bios_init(struct drm_i915_private *dev_priv)
{
- struct pci_dev *pdev = dev_priv->drm.pdev;
const struct vbt_header *vbt = dev_priv->opregion.vbt;
+ struct vbt_header *oprom_vbt = NULL;
const struct bdb_header *bdb;
- u8 __iomem *oprom = NULL;
INIT_LIST_HEAD(&dev_priv->vbt.display_devices);
/* If the OpRegion does not have VBT, look in PCI ROM. */
if (!vbt) {
- size_t size;
-
- oprom = pci_map_rom(pdev, &size);
- if (!oprom)
+ oprom_vbt = oprom_get_vbt(dev_priv);
+ if (!oprom_vbt)
goto out;
- vbt = find_vbt(oprom, size);
- if (!vbt)
- goto out;
+ vbt = oprom_vbt;
DRM_DEBUG_KMS("Found valid VBT in PCI ROM\n");
}
/* Grab useful general definitions */
parse_general_features(dev_priv, bdb);
parse_general_definitions(dev_priv, bdb);
- parse_lfp_panel_data(dev_priv, bdb);
+ parse_panel_options(dev_priv, bdb);
+ parse_panel_dtd(dev_priv, bdb);
parse_lfp_backlight(dev_priv, bdb);
parse_sdvo_panel_data(dev_priv, bdb);
parse_driver_features(dev_priv, bdb);
+ parse_power_conservation_features(dev_priv, bdb);
parse_edp(dev_priv, bdb);
parse_psr(dev_priv, bdb);
parse_mipi_config(dev_priv, bdb);
parse_mipi_sequence(dev_priv, bdb);
+ /* Depends on child device list */
+ parse_compression_parameters(dev_priv, bdb);
+
/* Further processing on pre-parsed data */
parse_sdvo_device_mapping(dev_priv, bdb->version);
parse_ddi_ports(dev_priv, bdb->version);
init_vbt_missing_defaults(dev_priv);
}
- if (oprom)
- pci_unmap_rom(pdev, oprom);
+ kfree(oprom_vbt);
}
/**
list_for_each_entry_safe(devdata, n, &dev_priv->vbt.display_devices, node) {
list_del(&devdata->node);
+ kfree(devdata->dsc);
kfree(devdata);
}
return false;
}
+static void fill_dsc(struct intel_crtc_state *crtc_state,
+ struct dsc_compression_parameters_entry *dsc,
+ int dsc_max_bpc)
+{
+ struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config;
+ int bpc = 8;
+
+ vdsc_cfg->dsc_version_major = dsc->version_major;
+ vdsc_cfg->dsc_version_minor = dsc->version_minor;
+
+ if (dsc->support_12bpc && dsc_max_bpc >= 12)
+ bpc = 12;
+ else if (dsc->support_10bpc && dsc_max_bpc >= 10)
+ bpc = 10;
+ else if (dsc->support_8bpc && dsc_max_bpc >= 8)
+ bpc = 8;
+ else
+ DRM_DEBUG_KMS("VBT: Unsupported BPC %d for DCS\n",
+ dsc_max_bpc);
+
+ crtc_state->pipe_bpp = bpc * 3;
+
+ crtc_state->dsc.compressed_bpp = min(crtc_state->pipe_bpp,
+ VBT_DSC_MAX_BPP(dsc->max_bpp));
+
+ /*
+ * FIXME: This is ugly, and slice count should take DSC engine
+ * throughput etc. into account.
+ *
+ * Also, per spec DSI supports 1, 2, 3 or 4 horizontal slices.
+ */
+ if (dsc->slices_per_line & BIT(2)) {
+ crtc_state->dsc.slice_count = 4;
+ } else if (dsc->slices_per_line & BIT(1)) {
+ crtc_state->dsc.slice_count = 2;
+ } else {
+ /* FIXME */
+ if (!(dsc->slices_per_line & BIT(0)))
+ DRM_DEBUG_KMS("VBT: Unsupported DSC slice count for DSI\n");
+
+ crtc_state->dsc.slice_count = 1;
+ }
+
+ if (crtc_state->hw.adjusted_mode.crtc_hdisplay %
+ crtc_state->dsc.slice_count != 0)
+ DRM_DEBUG_KMS("VBT: DSC hdisplay %d not divisible by slice count %d\n",
+ crtc_state->hw.adjusted_mode.crtc_hdisplay,
+ crtc_state->dsc.slice_count);
+
+ /*
+ * FIXME: Use VBT rc_buffer_block_size and rc_buffer_size for the
+ * implementation specific physical rate buffer size. Currently we use
+ * the required rate buffer model size calculated in
+ * drm_dsc_compute_rc_parameters() according to VESA DSC Annex E.
+ *
+ * The VBT rc_buffer_block_size and rc_buffer_size definitions
+ * correspond to DP 1.4 DPCD offsets 0x62 and 0x63. The DP DSC
+ * implementation should also use the DPCD (or perhaps VBT for eDP)
+ * provided value for the buffer size.
+ */
+
+ /* FIXME: DSI spec says bpc + 1 for this one */
+ vdsc_cfg->line_buf_depth = VBT_DSC_LINE_BUFFER_DEPTH(dsc->line_buffer_depth);
+
+ vdsc_cfg->block_pred_enable = dsc->block_prediction_enable;
+
+ vdsc_cfg->slice_height = dsc->slice_height;
+}
+
+/* FIXME: initially DSI specific */
+bool intel_bios_get_dsc_params(struct intel_encoder *encoder,
+ struct intel_crtc_state *crtc_state,
+ int dsc_max_bpc)
+{
+ struct drm_i915_private *i915 = to_i915(encoder->base.dev);
+ const struct display_device_data *devdata;
+ const struct child_device_config *child;
+
+ list_for_each_entry(devdata, &i915->vbt.display_devices, node) {
+ child = &devdata->child;
+
+ if (!(child->device_type & DEVICE_TYPE_MIPI_OUTPUT))
+ continue;
+
+ if (child->dvo_port - DVO_PORT_MIPIA == encoder->port) {
+ if (!devdata->dsc)
+ return false;
+
+ if (crtc_state)
+ fill_dsc(crtc_state, devdata->dsc, dsc_max_bpc);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
/**
* intel_bios_is_port_hpd_inverted - is HPD inverted for %port
* @i915: i915 device instance