Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Alan Cox | 2060 | 84.88% | 6 | 16.22% |
Patrik Jakobsson | 227 | 9.35% | 12 | 32.43% |
Daniel Vetter | 42 | 1.73% | 2 | 5.41% |
Thomas Zimmermann | 38 | 1.57% | 3 | 8.11% |
Rob Clark | 20 | 0.82% | 1 | 2.70% |
Kirill A. Shutemov | 12 | 0.49% | 2 | 5.41% |
Sam Ravnborg | 8 | 0.33% | 1 | 2.70% |
Lee Jones | 4 | 0.16% | 1 | 2.70% |
Yakui Zhao | 3 | 0.12% | 1 | 2.70% |
Laurent Pinchart | 3 | 0.12% | 2 | 5.41% |
Joe Perches | 2 | 0.08% | 1 | 2.70% |
Thomas Gleixner | 2 | 0.08% | 1 | 2.70% |
Luc Van Oostenryck | 2 | 0.08% | 1 | 2.70% |
Matt Roper | 2 | 0.08% | 1 | 2.70% |
Krzysztof Kozlowski | 1 | 0.04% | 1 | 2.70% |
Jani Nikula | 1 | 0.04% | 1 | 2.70% |
Total | 2427 | 37 |
// SPDX-License-Identifier: GPL-2.0-only /* * Copyright © 2006-2011 Intel Corporation * * Authors: * Eric Anholt <eric@anholt.net> * Dave Airlie <airlied@linux.ie> * Jesse Barnes <jesse.barnes@intel.com> */ #include <linux/dmi.h> #include <linux/i2c.h> #include <linux/pm_runtime.h> #include <drm/drm_crtc_helper.h> #include <drm/drm_modeset_helper_vtables.h> #include <drm/drm_simple_kms_helper.h> #include "cdv_device.h" #include "intel_bios.h" #include "power.h" #include "psb_drv.h" #include "psb_intel_drv.h" #include "psb_intel_reg.h" /* * LVDS I2C backlight control macros */ #define BRIGHTNESS_MAX_LEVEL 100 #define BRIGHTNESS_MASK 0xFF #define BLC_I2C_TYPE 0x01 #define BLC_PWM_TYPT 0x02 #define BLC_POLARITY_NORMAL 0 #define BLC_POLARITY_INVERSE 1 #define PSB_BLC_MAX_PWM_REG_FREQ (0xFFFE) #define PSB_BLC_MIN_PWM_REG_FREQ (0x2) #define PSB_BLC_PWM_PRECISION_FACTOR (10) #define PSB_BACKLIGHT_PWM_CTL_SHIFT (16) #define PSB_BACKLIGHT_PWM_POLARITY_BIT_CLEAR (0xFFFE) struct cdv_intel_lvds_priv { /** * Saved LVDO output states */ uint32_t savePP_ON; uint32_t savePP_OFF; uint32_t saveLVDS; uint32_t savePP_CONTROL; uint32_t savePP_CYCLE; uint32_t savePFIT_CONTROL; uint32_t savePFIT_PGM_RATIOS; uint32_t saveBLC_PWM_CTL; }; /* * Returns the maximum level of the backlight duty cycle field. */ static u32 cdv_intel_lvds_get_max_backlight(struct drm_device *dev) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 retval; if (gma_power_begin(dev, false)) { retval = ((REG_READ(BLC_PWM_CTL) & BACKLIGHT_MODULATION_FREQ_MASK) >> BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; gma_power_end(dev); } else retval = ((dev_priv->regs.saveBLC_PWM_CTL & BACKLIGHT_MODULATION_FREQ_MASK) >> BACKLIGHT_MODULATION_FREQ_SHIFT) * 2; return retval; } /* * Sets the backlight level. * * level backlight level, from 0 to cdv_intel_lvds_get_max_backlight(). */ static void cdv_intel_lvds_set_backlight(struct drm_device *dev, int level) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 blc_pwm_ctl; if (gma_power_begin(dev, false)) { blc_pwm_ctl = REG_READ(BLC_PWM_CTL) & ~BACKLIGHT_DUTY_CYCLE_MASK; REG_WRITE(BLC_PWM_CTL, (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT))); gma_power_end(dev); } else { blc_pwm_ctl = dev_priv->regs.saveBLC_PWM_CTL & ~BACKLIGHT_DUTY_CYCLE_MASK; dev_priv->regs.saveBLC_PWM_CTL = (blc_pwm_ctl | (level << BACKLIGHT_DUTY_CYCLE_SHIFT)); } } /* * Sets the power state for the panel. */ static void cdv_intel_lvds_set_power(struct drm_device *dev, struct drm_encoder *encoder, bool on) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); u32 pp_status; if (!gma_power_begin(dev, true)) return; if (on) { REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) | POWER_TARGET_ON); do { pp_status = REG_READ(PP_STATUS); } while ((pp_status & PP_ON) == 0); cdv_intel_lvds_set_backlight(dev, dev_priv->mode_dev.backlight_duty_cycle); } else { cdv_intel_lvds_set_backlight(dev, 0); REG_WRITE(PP_CONTROL, REG_READ(PP_CONTROL) & ~POWER_TARGET_ON); do { pp_status = REG_READ(PP_STATUS); } while (pp_status & PP_ON); } gma_power_end(dev); } static void cdv_intel_lvds_encoder_dpms(struct drm_encoder *encoder, int mode) { struct drm_device *dev = encoder->dev; if (mode == DRM_MODE_DPMS_ON) cdv_intel_lvds_set_power(dev, encoder, true); else cdv_intel_lvds_set_power(dev, encoder, false); /* XXX: We never power down the LVDS pairs. */ } static void cdv_intel_lvds_save(struct drm_connector *connector) { } static void cdv_intel_lvds_restore(struct drm_connector *connector) { } static enum drm_mode_status cdv_intel_lvds_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct drm_display_mode *fixed_mode = dev_priv->mode_dev.panel_fixed_mode; /* just in case */ if (mode->flags & DRM_MODE_FLAG_DBLSCAN) return MODE_NO_DBLESCAN; /* just in case */ if (mode->flags & DRM_MODE_FLAG_INTERLACE) return MODE_NO_INTERLACE; if (fixed_mode) { if (mode->hdisplay > fixed_mode->hdisplay) return MODE_PANEL; if (mode->vdisplay > fixed_mode->vdisplay) return MODE_PANEL; } return MODE_OK; } static bool cdv_intel_lvds_mode_fixup(struct drm_encoder *encoder, const struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; struct drm_encoder *tmp_encoder; struct drm_display_mode *panel_fixed_mode = mode_dev->panel_fixed_mode; /* Should never happen!! */ list_for_each_entry(tmp_encoder, &dev->mode_config.encoder_list, head) { if (tmp_encoder != encoder && tmp_encoder->crtc == encoder->crtc) { pr_err("Can't enable LVDS and another encoder on the same pipe\n"); return false; } } /* * If we have timings from the BIOS for the panel, put them in * to the adjusted mode. The CRTC will be set up for this mode, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ if (panel_fixed_mode != NULL) { adjusted_mode->hdisplay = panel_fixed_mode->hdisplay; adjusted_mode->hsync_start = panel_fixed_mode->hsync_start; adjusted_mode->hsync_end = panel_fixed_mode->hsync_end; adjusted_mode->htotal = panel_fixed_mode->htotal; adjusted_mode->vdisplay = panel_fixed_mode->vdisplay; adjusted_mode->vsync_start = panel_fixed_mode->vsync_start; adjusted_mode->vsync_end = panel_fixed_mode->vsync_end; adjusted_mode->vtotal = panel_fixed_mode->vtotal; adjusted_mode->clock = panel_fixed_mode->clock; drm_mode_set_crtcinfo(adjusted_mode, CRTC_INTERLACE_HALVE_V); } /* * XXX: It would be nice to support lower refresh rates on the * panels to reduce power consumption, and perhaps match the * user's requested refresh rate. */ return true; } static void cdv_intel_lvds_prepare(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; if (!gma_power_begin(dev, true)) return; mode_dev->saveBLC_PWM_CTL = REG_READ(BLC_PWM_CTL); mode_dev->backlight_duty_cycle = (mode_dev->saveBLC_PWM_CTL & BACKLIGHT_DUTY_CYCLE_MASK); cdv_intel_lvds_set_power(dev, encoder, false); gma_power_end(dev); } static void cdv_intel_lvds_commit(struct drm_encoder *encoder) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; if (mode_dev->backlight_duty_cycle == 0) mode_dev->backlight_duty_cycle = cdv_intel_lvds_get_max_backlight(dev); cdv_intel_lvds_set_power(dev, encoder, true); } static void cdv_intel_lvds_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode, struct drm_display_mode *adjusted_mode) { struct drm_device *dev = encoder->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct gma_crtc *gma_crtc = to_gma_crtc(encoder->crtc); u32 pfit_control; /* * The LVDS pin pair will already have been turned on in the * cdv_intel_crtc_mode_set since it has a large impact on the DPLL * settings. */ /* * Enable automatic panel scaling so that non-native modes fill the * screen. Should be enabled before the pipe is enabled, according to * register description and PRM. */ if (mode->hdisplay != adjusted_mode->hdisplay || mode->vdisplay != adjusted_mode->vdisplay) pfit_control = (PFIT_ENABLE | VERT_AUTO_SCALE | HORIZ_AUTO_SCALE | VERT_INTERP_BILINEAR | HORIZ_INTERP_BILINEAR); else pfit_control = 0; pfit_control |= gma_crtc->pipe << PFIT_PIPE_SHIFT; if (dev_priv->lvds_dither) pfit_control |= PANEL_8TO6_DITHER_ENABLE; REG_WRITE(PFIT_CONTROL, pfit_control); } /* * Return the list of DDC modes if available, or the BIOS fixed mode otherwise. */ static int cdv_intel_lvds_get_modes(struct drm_connector *connector) { struct drm_device *dev = connector->dev; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct psb_intel_mode_device *mode_dev = &dev_priv->mode_dev; int ret; ret = psb_intel_ddc_get_modes(connector, connector->ddc); if (ret) return ret; if (mode_dev->panel_fixed_mode != NULL) { struct drm_display_mode *mode = drm_mode_duplicate(dev, mode_dev->panel_fixed_mode); drm_mode_probed_add(connector, mode); return 1; } return 0; } static void cdv_intel_lvds_destroy(struct drm_connector *connector) { struct gma_connector *gma_connector = to_gma_connector(connector); struct gma_encoder *gma_encoder = gma_attached_encoder(connector); gma_i2c_destroy(to_gma_i2c_chan(connector->ddc)); gma_i2c_destroy(gma_encoder->i2c_bus); drm_connector_cleanup(connector); kfree(gma_connector); } static int cdv_intel_lvds_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t value) { struct drm_encoder *encoder = connector->encoder; if (!strcmp(property->name, "scaling mode") && encoder) { struct gma_crtc *crtc = to_gma_crtc(encoder->crtc); uint64_t curValue; if (!crtc) return -1; switch (value) { case DRM_MODE_SCALE_FULLSCREEN: break; case DRM_MODE_SCALE_NO_SCALE: break; case DRM_MODE_SCALE_ASPECT: break; default: return -1; } if (drm_object_property_get_value(&connector->base, property, &curValue)) return -1; if (curValue == value) return 0; if (drm_object_property_set_value(&connector->base, property, value)) return -1; if (crtc->saved_mode.hdisplay != 0 && crtc->saved_mode.vdisplay != 0) { if (!drm_crtc_helper_set_mode(encoder->crtc, &crtc->saved_mode, encoder->crtc->x, encoder->crtc->y, encoder->crtc->primary->fb)) return -1; } } else if (!strcmp(property->name, "backlight") && encoder) { if (drm_object_property_set_value(&connector->base, property, value)) return -1; else gma_backlight_set(encoder->dev, value); } else if (!strcmp(property->name, "DPMS") && encoder) { const struct drm_encoder_helper_funcs *helpers = encoder->helper_private; helpers->dpms(encoder, value); } return 0; } static const struct drm_encoder_helper_funcs cdv_intel_lvds_helper_funcs = { .dpms = cdv_intel_lvds_encoder_dpms, .mode_fixup = cdv_intel_lvds_mode_fixup, .prepare = cdv_intel_lvds_prepare, .mode_set = cdv_intel_lvds_mode_set, .commit = cdv_intel_lvds_commit, }; static const struct drm_connector_helper_funcs cdv_intel_lvds_connector_helper_funcs = { .get_modes = cdv_intel_lvds_get_modes, .mode_valid = cdv_intel_lvds_mode_valid, .best_encoder = gma_best_encoder, }; static const struct drm_connector_funcs cdv_intel_lvds_connector_funcs = { .dpms = drm_helper_connector_dpms, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = cdv_intel_lvds_set_property, .destroy = cdv_intel_lvds_destroy, }; /* * Enumerate the child dev array parsed from VBT to check whether * the LVDS is present. * If it is present, return 1. * If it is not present, return false. * If no child dev is parsed from VBT, it assumes that the LVDS is present. */ static bool lvds_is_present_in_vbt(struct drm_device *dev, u8 *i2c_pin) { struct drm_psb_private *dev_priv = to_drm_psb_private(dev); int i; if (!dev_priv->child_dev_num) return true; for (i = 0; i < dev_priv->child_dev_num; i++) { struct child_device_config *child = dev_priv->child_dev + i; /* If the device type is not LFP, continue. * We have to check both the new identifiers as well as the * old for compatibility with some BIOSes. */ if (child->device_type != DEVICE_TYPE_INT_LFP && child->device_type != DEVICE_TYPE_LFP) continue; if (child->i2c_pin) *i2c_pin = child->i2c_pin; /* However, we cannot trust the BIOS writers to populate * the VBT correctly. Since LVDS requires additional * information from AIM blocks, a non-zero addin offset is * a good indicator that the LVDS is actually present. */ if (child->addin_offset) return true; /* But even then some BIOS writers perform some black magic * and instantiate the device without reference to any * additional data. Trust that if the VBT was written into * the OpRegion then they have validated the LVDS's existence. */ if (dev_priv->opregion.vbt) return true; } return false; } /** * cdv_intel_lvds_init - setup LVDS connectors on this device * @dev: drm device * @mode_dev: PSB mode device * * Create the connector, register the LVDS DDC bus, and try to figure out what * modes we can display on the LVDS panel (if present). */ void cdv_intel_lvds_init(struct drm_device *dev, struct psb_intel_mode_device *mode_dev) { struct gma_encoder *gma_encoder; struct gma_connector *gma_connector; struct cdv_intel_lvds_priv *lvds_priv; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_display_mode *scan; struct drm_crtc *crtc; struct drm_psb_private *dev_priv = to_drm_psb_private(dev); struct gma_i2c_chan *ddc_bus; u32 lvds; int pipe; int ret; u8 pin; if (!dev_priv->lvds_enabled_in_vbt) return; pin = GMBUS_PORT_PANEL; if (!lvds_is_present_in_vbt(dev, &pin)) { DRM_DEBUG_KMS("LVDS is not present in VBT\n"); return; } gma_encoder = kzalloc(sizeof(struct gma_encoder), GFP_KERNEL); if (!gma_encoder) return; gma_connector = kzalloc(sizeof(struct gma_connector), GFP_KERNEL); if (!gma_connector) goto err_free_encoder; lvds_priv = kzalloc(sizeof(struct cdv_intel_lvds_priv), GFP_KERNEL); if (!lvds_priv) goto err_free_connector; gma_encoder->dev_priv = lvds_priv; connector = &gma_connector->base; gma_connector->save = cdv_intel_lvds_save; gma_connector->restore = cdv_intel_lvds_restore; encoder = &gma_encoder->base; /* Set up the DDC bus. */ ddc_bus = gma_i2c_create(dev, GPIOC, "LVDSDDC_C"); if (!ddc_bus) { dev_printk(KERN_ERR, dev->dev, "DDC bus registration " "failed.\n"); goto err_free_lvds_priv; } ret = drm_connector_init_with_ddc(dev, connector, &cdv_intel_lvds_connector_funcs, DRM_MODE_CONNECTOR_LVDS, &ddc_bus->base); if (ret) goto err_destroy_ddc; ret = drm_simple_encoder_init(dev, encoder, DRM_MODE_ENCODER_LVDS); if (ret) goto err_connector_cleanup; gma_connector_attach_encoder(gma_connector, gma_encoder); gma_encoder->type = INTEL_OUTPUT_LVDS; drm_encoder_helper_add(encoder, &cdv_intel_lvds_helper_funcs); drm_connector_helper_add(connector, &cdv_intel_lvds_connector_helper_funcs); connector->display_info.subpixel_order = SubPixelHorizontalRGB; connector->interlace_allowed = false; connector->doublescan_allowed = false; /*Attach connector properties*/ drm_object_attach_property(&connector->base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); drm_object_attach_property(&connector->base, dev_priv->backlight_property, BRIGHTNESS_MAX_LEVEL); /** * Set up I2C bus * FIXME: distroy i2c_bus when exit */ gma_encoder->i2c_bus = gma_i2c_create(dev, GPIOB, "LVDSBLC_B"); if (!gma_encoder->i2c_bus) { dev_printk(KERN_ERR, dev->dev, "I2C bus registration failed.\n"); goto err_encoder_cleanup; } gma_encoder->i2c_bus->slave_addr = 0x2C; dev_priv->lvds_i2c_bus = gma_encoder->i2c_bus; /* * LVDS discovery: * 1) check for EDID on DDC * 2) check for VBT data * 3) check to see if LVDS is already on * if none of the above, no panel * 4) make sure lid is open * if closed, act like it's not there for now */ /* * Attempt to get the fixed panel mode from DDC. Assume that the * preferred mode is the right one. */ mutex_lock(&dev->mode_config.mutex); psb_intel_ddc_get_modes(connector, &ddc_bus->base); list_for_each_entry(scan, &connector->probed_modes, head) { if (scan->type & DRM_MODE_TYPE_PREFERRED) { mode_dev->panel_fixed_mode = drm_mode_duplicate(dev, scan); goto out; /* FIXME: check for quirks */ } } /* Failed to get EDID, what about VBT? do we need this?*/ if (dev_priv->lfp_lvds_vbt_mode) { mode_dev->panel_fixed_mode = drm_mode_duplicate(dev, dev_priv->lfp_lvds_vbt_mode); if (mode_dev->panel_fixed_mode) { mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; /* FIXME: check for quirks */ } } /* * If we didn't get EDID, try checking if the panel is already turned * on. If so, assume that whatever is currently programmed is the * correct mode. */ lvds = REG_READ(LVDS); pipe = (lvds & LVDS_PIPEB_SELECT) ? 1 : 0; crtc = psb_intel_get_crtc_from_pipe(dev, pipe); if (crtc && (lvds & LVDS_PORT_EN)) { mode_dev->panel_fixed_mode = cdv_intel_crtc_mode_get(dev, crtc); if (mode_dev->panel_fixed_mode) { mode_dev->panel_fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; goto out; /* FIXME: check for quirks */ } } /* If we still don't have a mode after all that, give up. */ if (!mode_dev->panel_fixed_mode) { DRM_DEBUG ("Found no modes on the lvds, ignoring the LVDS\n"); goto err_unlock; } /* setup PWM */ { u32 pwm; pwm = REG_READ(BLC_PWM_CTL2); if (pipe == 1) pwm |= PWM_PIPE_B; else pwm &= ~PWM_PIPE_B; pwm |= PWM_ENABLE; REG_WRITE(BLC_PWM_CTL2, pwm); } out: mutex_unlock(&dev->mode_config.mutex); return; err_unlock: mutex_unlock(&dev->mode_config.mutex); gma_i2c_destroy(gma_encoder->i2c_bus); err_encoder_cleanup: drm_encoder_cleanup(encoder); err_connector_cleanup: drm_connector_cleanup(connector); err_destroy_ddc: gma_i2c_destroy(ddc_bus); err_free_lvds_priv: kfree(lvds_priv); err_free_connector: kfree(gma_connector); err_free_encoder: kfree(gma_encoder); }
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1