#include "amdgpu_i2c.h"
#include "atom.h"
#include "amdgpu_connectors.h"
+#include "amdgpu_display.h"
#include <asm/div64.h>
#include <linux/pm_runtime.h>
#include <drm/drm_edid.h>
#include <drm/drm_fb_helper.h>
-static void amdgpu_flip_callback(struct dma_fence *f, struct dma_fence_cb *cb)
+static void amdgpu_display_flip_callback(struct dma_fence *f,
+ struct dma_fence_cb *cb)
{
struct amdgpu_flip_work *work =
container_of(cb, struct amdgpu_flip_work, cb);
schedule_work(&work->flip_work.work);
}
-static bool amdgpu_flip_handle_fence(struct amdgpu_flip_work *work,
- struct dma_fence **f)
+static bool amdgpu_display_flip_handle_fence(struct amdgpu_flip_work *work,
+ struct dma_fence **f)
{
struct dma_fence *fence= *f;
*f = NULL;
- if (!dma_fence_add_callback(fence, &work->cb, amdgpu_flip_callback))
+ if (!dma_fence_add_callback(fence, &work->cb,
+ amdgpu_display_flip_callback))
return true;
dma_fence_put(fence);
return false;
}
-static void amdgpu_flip_work_func(struct work_struct *__work)
+static void amdgpu_display_flip_work_func(struct work_struct *__work)
{
struct delayed_work *delayed_work =
container_of(__work, struct delayed_work, work);
unsigned i;
int vpos, hpos;
- if (amdgpu_flip_handle_fence(work, &work->excl))
+ if (amdgpu_display_flip_handle_fence(work, &work->excl))
return;
for (i = 0; i < work->shared_count; ++i)
- if (amdgpu_flip_handle_fence(work, &work->shared[i]))
+ if (amdgpu_display_flip_handle_fence(work, &work->shared[i]))
return;
/* Wait until we're out of the vertical blank period before the one
* targeted by the flip
*/
if (amdgpu_crtc->enabled &&
- (amdgpu_get_crtc_scanoutpos(adev->ddev, work->crtc_id, 0,
- &vpos, &hpos, NULL, NULL,
- &crtc->hwmode)
+ (amdgpu_display_get_crtc_scanoutpos(adev->ddev, work->crtc_id, 0,
+ &vpos, &hpos, NULL, NULL,
+ &crtc->hwmode)
& (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
(DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
(int)(work->target_vblank -
/*
* Handle unpin events outside the interrupt handler proper.
*/
-static void amdgpu_unpin_work_func(struct work_struct *__work)
+static void amdgpu_display_unpin_work_func(struct work_struct *__work)
{
struct amdgpu_flip_work *work =
container_of(__work, struct amdgpu_flip_work, unpin_work);
kfree(work);
}
-int amdgpu_crtc_page_flip_target(struct drm_crtc *crtc,
- struct drm_framebuffer *fb,
- struct drm_pending_vblank_event *event,
- uint32_t page_flip_flags, uint32_t target,
- struct drm_modeset_acquire_ctx *ctx)
+int amdgpu_display_crtc_page_flip_target(struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ struct drm_pending_vblank_event *event,
+ uint32_t page_flip_flags, uint32_t target,
+ struct drm_modeset_acquire_ctx *ctx)
{
struct drm_device *dev = crtc->dev;
struct amdgpu_device *adev = dev->dev_private;
if (work == NULL)
return -ENOMEM;
- INIT_DELAYED_WORK(&work->flip_work, amdgpu_flip_work_func);
- INIT_WORK(&work->unpin_work, amdgpu_unpin_work_func);
+ INIT_DELAYED_WORK(&work->flip_work, amdgpu_display_flip_work_func);
+ INIT_WORK(&work->unpin_work, amdgpu_display_unpin_work_func);
work->event = event;
work->adev = adev;
goto cleanup;
}
- r = amdgpu_bo_pin(new_abo, AMDGPU_GEM_DOMAIN_VRAM, &base);
+ r = amdgpu_bo_pin(new_abo, amdgpu_display_framebuffer_domains(adev), &base);
if (unlikely(r != 0)) {
DRM_ERROR("failed to pin new abo buffer before flip\n");
goto unreserve;
amdgpu_bo_unreserve(new_abo);
work->base = base;
- work->target_vblank = target - drm_crtc_vblank_count(crtc) +
+ work->target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) +
amdgpu_get_vblank_counter_kms(dev, work->crtc_id);
/* we borrow the event spin lock for protecting flip_wrok */
/* update crtc fb */
crtc->primary->fb = fb;
spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
- amdgpu_flip_work_func(&work->flip_work.work);
+ amdgpu_display_flip_work_func(&work->flip_work.work);
return 0;
pflip_cleanup:
return r;
}
-int amdgpu_crtc_set_config(struct drm_mode_set *set,
- struct drm_modeset_acquire_ctx *ctx)
+int amdgpu_display_crtc_set_config(struct drm_mode_set *set,
+ struct drm_modeset_acquire_ctx *ctx)
{
struct drm_device *dev;
struct amdgpu_device *adev;
"HPD6",
};
-void amdgpu_print_display_setup(struct drm_device *dev)
+void amdgpu_display_print_display_setup(struct drm_device *dev)
{
struct drm_connector *connector;
struct amdgpu_connector *amdgpu_connector;
}
/**
- * amdgpu_ddc_probe
+ * amdgpu_display_ddc_probe
*
*/
-bool amdgpu_ddc_probe(struct amdgpu_connector *amdgpu_connector,
- bool use_aux)
+bool amdgpu_display_ddc_probe(struct amdgpu_connector *amdgpu_connector,
+ bool use_aux)
{
u8 out = 0x0;
u8 buf[8];
return true;
}
-static void amdgpu_user_framebuffer_destroy(struct drm_framebuffer *fb)
+static void amdgpu_display_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
struct amdgpu_framebuffer *amdgpu_fb = to_amdgpu_framebuffer(fb);
kfree(amdgpu_fb);
}
-static int amdgpu_user_framebuffer_create_handle(struct drm_framebuffer *fb,
- struct drm_file *file_priv,
- unsigned int *handle)
+static int amdgpu_display_user_framebuffer_create_handle(
+ struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle)
{
struct amdgpu_framebuffer *amdgpu_fb = to_amdgpu_framebuffer(fb);
}
static const struct drm_framebuffer_funcs amdgpu_fb_funcs = {
- .destroy = amdgpu_user_framebuffer_destroy,
- .create_handle = amdgpu_user_framebuffer_create_handle,
+ .destroy = amdgpu_display_user_framebuffer_destroy,
+ .create_handle = amdgpu_display_user_framebuffer_create_handle,
};
-int
-amdgpu_framebuffer_init(struct drm_device *dev,
- struct amdgpu_framebuffer *rfb,
- const struct drm_mode_fb_cmd2 *mode_cmd,
- struct drm_gem_object *obj)
+uint32_t amdgpu_display_framebuffer_domains(struct amdgpu_device *adev)
+{
+ uint32_t domain = AMDGPU_GEM_DOMAIN_VRAM;
+
+#if defined(CONFIG_DRM_AMD_DC)
+ if (adev->asic_type >= CHIP_CARRIZO && adev->asic_type < CHIP_RAVEN &&
+ adev->flags & AMD_IS_APU &&
+ amdgpu_device_asic_has_dc_support(adev->asic_type))
+ domain |= AMDGPU_GEM_DOMAIN_GTT;
+#endif
+
+ return domain;
+}
+
+int amdgpu_display_framebuffer_init(struct drm_device *dev,
+ struct amdgpu_framebuffer *rfb,
+ const struct drm_mode_fb_cmd2 *mode_cmd,
+ struct drm_gem_object *obj)
{
int ret;
rfb->obj = obj;
}
struct drm_framebuffer *
-amdgpu_user_framebuffer_create(struct drm_device *dev,
- struct drm_file *file_priv,
- const struct drm_mode_fb_cmd2 *mode_cmd)
+amdgpu_display_user_framebuffer_create(struct drm_device *dev,
+ struct drm_file *file_priv,
+ const struct drm_mode_fb_cmd2 *mode_cmd)
{
struct drm_gem_object *obj;
struct amdgpu_framebuffer *amdgpu_fb;
return ERR_PTR(-ENOMEM);
}
- ret = amdgpu_framebuffer_init(dev, amdgpu_fb, mode_cmd, obj);
+ ret = amdgpu_display_framebuffer_init(dev, amdgpu_fb, mode_cmd, obj);
if (ret) {
kfree(amdgpu_fb);
drm_gem_object_put_unlocked(obj);
}
const struct drm_mode_config_funcs amdgpu_mode_funcs = {
- .fb_create = amdgpu_user_framebuffer_create,
+ .fb_create = amdgpu_display_user_framebuffer_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
};
{ AMDGPU_FMT_DITHER_ENABLE, "on" },
};
-int amdgpu_modeset_create_props(struct amdgpu_device *adev)
+int amdgpu_display_modeset_create_props(struct amdgpu_device *adev)
{
int sz;
return 0;
}
-void amdgpu_update_display_priority(struct amdgpu_device *adev)
+void amdgpu_display_update_priority(struct amdgpu_device *adev)
{
/* adjustment options for the display watermarks */
if ((amdgpu_disp_priority == 0) || (amdgpu_disp_priority > 2))
}
-static bool is_hdtv_mode(const struct drm_display_mode *mode)
+static bool amdgpu_display_is_hdtv_mode(const struct drm_display_mode *mode)
{
/* try and guess if this is a tv or a monitor */
if ((mode->vdisplay == 480 && mode->hdisplay == 720) || /* 480p */
return false;
}
-bool amdgpu_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
- const struct drm_display_mode *mode,
- struct drm_display_mode *adjusted_mode)
+bool amdgpu_display_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
+ const struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
{
struct drm_device *dev = crtc->dev;
struct drm_encoder *encoder;
((amdgpu_encoder->underscan_type == UNDERSCAN_ON) ||
((amdgpu_encoder->underscan_type == UNDERSCAN_AUTO) &&
drm_detect_hdmi_monitor(amdgpu_connector_edid(connector)) &&
- is_hdtv_mode(mode)))) {
+ amdgpu_display_is_hdtv_mode(mode)))) {
if (amdgpu_encoder->underscan_hborder != 0)
amdgpu_crtc->h_border = amdgpu_encoder->underscan_hborder;
else
* unknown small number of scanlines wrt. real scanout position.
*
*/
-int amdgpu_get_crtc_scanoutpos(struct drm_device *dev, unsigned int pipe,
- unsigned int flags, int *vpos, int *hpos,
- ktime_t *stime, ktime_t *etime,
- const struct drm_display_mode *mode)
+int amdgpu_display_get_crtc_scanoutpos(struct drm_device *dev,
+ unsigned int pipe, unsigned int flags, int *vpos,
+ int *hpos, ktime_t *stime, ktime_t *etime,
+ const struct drm_display_mode *mode)
{
u32 vbl = 0, position = 0;
int vbl_start, vbl_end, vtotal, ret = 0;
return ret;
}
-int amdgpu_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc)
+int amdgpu_display_crtc_idx_to_irq_type(struct amdgpu_device *adev, int crtc)
{
if (crtc < 0 || crtc >= adev->mode_info.num_crtc)
return AMDGPU_CRTC_IRQ_NONE;
#include "dcn/dcn_1_0_offset.h"
#include "dcn/dcn_1_0_sh_mask.h"
-#include "soc15ip.h"
+#include "soc15_hw_ip.h"
+#include "vega10_ip_offset.h"
#include "soc15_common.h"
#endif
crtc_index = acrtc->crtc_id;
drm_handle_vblank(adev->ddev, crtc_index);
+ amdgpu_dm_crtc_handle_crc_irq(&acrtc->base);
}
static int dm_set_clockgating_state(void *handle,
}
#if defined(CONFIG_DRM_AMD_DC_FBC)
-#include "dal_asic_id.h"
/* Allocate memory for FBC compressed data */
-/* TODO: Dynamic allocation */
-#define AMDGPU_FBC_SIZE (3840 * 2160 * 4)
-
-static void amdgpu_dm_initialize_fbc(struct amdgpu_device *adev)
+static void amdgpu_dm_fbc_init(struct drm_connector *connector)
{
- int r;
+ struct drm_device *dev = connector->dev;
+ struct amdgpu_device *adev = dev->dev_private;
struct dm_comressor_info *compressor = &adev->dm.compressor;
+ struct amdgpu_dm_connector *aconn = to_amdgpu_dm_connector(connector);
+ struct drm_display_mode *mode;
+ unsigned long max_size = 0;
+
+ if (adev->dm.dc->fbc_compressor == NULL)
+ return;
+
+ if (aconn->dc_link->connector_signal != SIGNAL_TYPE_EDP)
+ return;
+
+ if (compressor->bo_ptr)
+ return;
+
- if (!compressor->bo_ptr) {
- r = amdgpu_bo_create_kernel(adev, AMDGPU_FBC_SIZE, PAGE_SIZE,
- AMDGPU_GEM_DOMAIN_VRAM, &compressor->bo_ptr,
- &compressor->gpu_addr, &compressor->cpu_addr);
+ list_for_each_entry(mode, &connector->modes, head) {
+ if (max_size < mode->htotal * mode->vtotal)
+ max_size = mode->htotal * mode->vtotal;
+ }
+
+ if (max_size) {
+ int r = amdgpu_bo_create_kernel(adev, max_size * 4, PAGE_SIZE,
+ AMDGPU_GEM_DOMAIN_VRAM, &compressor->bo_ptr,
+ &compressor->gpu_addr, &compressor->cpu_addr);
if (r)
- DRM_ERROR("DM: Failed to initialize fbc\n");
+ DRM_ERROR("DM: Failed to initialize FBC\n");
+ else {
+ adev->dm.dc->ctx->fbc_gpu_addr = compressor->gpu_addr;
+ DRM_INFO("DM: FBC alloc %lu\n", max_size*4);
+ }
+
}
}
/* Zero all the fields */
memset(&init_data, 0, sizeof(init_data));
- /* initialize DAL's lock (for SYNC context use) */
- spin_lock_init(&adev->dm.dal_lock);
-
- /* initialize DAL's mutex */
- mutex_init(&adev->dm.dal_mutex);
-
if(amdgpu_dm_irq_init(adev)) {
DRM_ERROR("amdgpu: failed to initialize DM IRQ support.\n");
goto error;
init_data.asic_id.pci_revision_id = adev->rev_id;
init_data.asic_id.hw_internal_rev = adev->external_rev_id;
- init_data.asic_id.vram_width = adev->mc.vram_width;
+ init_data.asic_id.vram_width = adev->gmc.vram_width;
/* TODO: initialize init_data.asic_id.vram_type here!!!! */
init_data.asic_id.atombios_base_address =
adev->mode_info.atom_context->bios;
else
init_data.log_mask = DC_MIN_LOG_MASK;
-#if defined(CONFIG_DRM_AMD_DC_FBC)
- if (adev->family == FAMILY_CZ)
- amdgpu_dm_initialize_fbc(adev);
- init_data.fbc_gpu_addr = adev->dm.compressor.gpu_addr;
-#endif
+ /*
+ * TODO debug why this doesn't work on Raven
+ */
+ if (adev->flags & AMD_IS_APU &&
+ adev->asic_type >= CHIP_CARRIZO &&
+ adev->asic_type < CHIP_RAVEN)
+ init_data.flags.gpu_vm_support = true;
+
/* Display Core create. */
adev->dm.dc = dc_create(&init_data);
DRM_DEBUG_DRIVER("amdgpu: freesync_module init done %p.\n",
adev->dm.freesync_module);
+ amdgpu_dm_init_color_mod();
+
if (amdgpu_dm_initialize_drm_device(adev)) {
DRM_ERROR(
"amdgpu: failed to initialize sw for display support.\n");
static int dm_late_init(void *handle)
{
- struct drm_device *dev = ((struct amdgpu_device *)handle)->ddev;
+ struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- return detect_mst_link_for_all_connectors(dev);
+ return detect_mst_link_for_all_connectors(adev->ddev);
}
static void s3_handle_mst(struct drm_device *dev, bool suspend)
{
struct amdgpu_device *adev = handle;
struct amdgpu_display_manager *dm = &adev->dm;
+ int ret = 0;
/* power on hardware */
dc_set_power_state(dm->dc, DC_ACPI_CM_POWER_STATE_D0);
- return 0;
+ ret = amdgpu_dm_display_resume(adev);
+ return ret;
}
int amdgpu_dm_display_resume(struct amdgpu_device *adev)
}
static const struct drm_mode_config_funcs amdgpu_dm_mode_funcs = {
- .fb_create = amdgpu_user_framebuffer_create,
+ .fb_create = amdgpu_display_user_framebuffer_create,
.output_poll_changed = drm_fb_helper_output_poll_changed,
.atomic_check = amdgpu_dm_atomic_check,
.atomic_commit = amdgpu_dm_atomic_commit,
/* indicate support of immediate flip */
adev->ddev->mode_config.async_page_flip = true;
- adev->ddev->mode_config.fb_base = adev->mc.aper_base;
+ adev->ddev->mode_config.fb_base = adev->gmc.aper_base;
- r = amdgpu_modeset_create_props(adev);
+ r = amdgpu_display_modeset_create_props(adev);
if (r)
return r;
static const struct amdgpu_display_funcs dm_display_funcs = {
.bandwidth_update = dm_bandwidth_update, /* called unconditionally */
.vblank_get_counter = dm_vblank_get_counter,/* called unconditionally */
- .vblank_wait = NULL,
.backlight_set_level =
dm_set_backlight_level,/* called unconditionally */
.backlight_get_level =
{
struct amdgpu_device *adev = (struct amdgpu_device *)handle;
- adev->ddev->driver->driver_features |= DRIVER_ATOMIC;
-
switch (adev->asic_type) {
case CHIP_BONAIRE:
case CHIP_HAWAII:
}
-static void fill_gamma_from_crtc_state(const struct drm_crtc_state *crtc_state,
- struct dc_plane_state *plane_state)
-{
- int i;
- struct dc_gamma *gamma;
- struct drm_color_lut *lut =
- (struct drm_color_lut *) crtc_state->gamma_lut->data;
-
- gamma = dc_create_gamma();
-
- if (gamma == NULL) {
- WARN_ON(1);
- return;
- }
-
- gamma->type = GAMMA_RGB_256;
- gamma->num_entries = GAMMA_RGB_256_ENTRIES;
- for (i = 0; i < GAMMA_RGB_256_ENTRIES; i++) {
- gamma->entries.red[i] = dal_fixed31_32_from_int(lut[i].red);
- gamma->entries.green[i] = dal_fixed31_32_from_int(lut[i].green);
- gamma->entries.blue[i] = dal_fixed31_32_from_int(lut[i].blue);
- }
-
- plane_state->gamma_correction = gamma;
-}
-
static int fill_plane_attributes(struct amdgpu_device *adev,
struct dc_plane_state *dc_plane_state,
struct drm_plane_state *plane_state,
if (input_tf == NULL)
return -ENOMEM;
- input_tf->type = TF_TYPE_PREDEFINED;
- input_tf->tf = TRANSFER_FUNCTION_SRGB;
-
dc_plane_state->in_transfer_func = input_tf;
- /* In case of gamma set, update gamma value */
- if (crtc_state->gamma_lut)
- fill_gamma_from_crtc_state(crtc_state, dc_plane_state);
+ /*
+ * Always set input transfer function, since plane state is refreshed
+ * every time.
+ */
+ ret = amdgpu_dm_set_degamma_lut(crtc_state, dc_plane_state);
return ret;
}
dst.width = stream->timing.h_addressable;
dst.height = stream->timing.v_addressable;
- rmx_type = dm_state->scaling;
- if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
- if (src.width * dst.height <
- src.height * dst.width) {
- /* height needs less upscaling/more downscaling */
- dst.width = src.width *
- dst.height / src.height;
- } else {
- /* width needs less upscaling/more downscaling */
- dst.height = src.height *
- dst.width / src.width;
+ if (dm_state) {
+ rmx_type = dm_state->scaling;
+ if (rmx_type == RMX_ASPECT || rmx_type == RMX_OFF) {
+ if (src.width * dst.height <
+ src.height * dst.width) {
+ /* height needs less upscaling/more downscaling */
+ dst.width = src.width *
+ dst.height / src.height;
+ } else {
+ /* width needs less upscaling/more downscaling */
+ dst.height = src.height *
+ dst.width / src.width;
+ }
+ } else if (rmx_type == RMX_CENTER) {
+ dst = src;
}
- } else if (rmx_type == RMX_CENTER) {
- dst = src;
- }
- dst.x = (stream->timing.h_addressable - dst.width) / 2;
- dst.y = (stream->timing.v_addressable - dst.height) / 2;
+ dst.x = (stream->timing.h_addressable - dst.width) / 2;
+ dst.y = (stream->timing.v_addressable - dst.height) / 2;
- if (dm_state->underscan_enable) {
- dst.x += dm_state->underscan_hborder / 2;
- dst.y += dm_state->underscan_vborder / 2;
- dst.width -= dm_state->underscan_hborder;
- dst.height -= dm_state->underscan_vborder;
+ if (dm_state->underscan_enable) {
+ dst.x += dm_state->underscan_hborder / 2;
+ dst.y += dm_state->underscan_vborder / 2;
+ dst.width -= dm_state->underscan_hborder;
+ dst.height -= dm_state->underscan_vborder;
+ }
}
stream->src = src;
}
}
for (j = 0; j < stream_count; j++) {
- if (stream_set[j] && j != master_stream)
+ if (stream_set[j])
stream_set[j]->triggered_crtc_reset.event_source = stream_set[master_stream];
}
}
if (aconnector == NULL) {
DRM_ERROR("aconnector is NULL!\n");
- goto drm_connector_null;
- }
-
- if (dm_state == NULL) {
- DRM_ERROR("dm_state is NULL!\n");
- goto dm_state_null;
+ return stream;
}
drm_connector = &aconnector->base;
*/
if (aconnector->mst_port) {
dm_dp_mst_dc_sink_create(drm_connector);
- goto mst_dc_sink_create_done;
+ return stream;
}
if (create_fake_sink(aconnector))
- goto stream_create_fail;
+ return stream;
}
stream = dc_create_stream_for_sink(aconnector->dc_sink);
if (stream == NULL) {
DRM_ERROR("Failed to create stream for sink!\n");
- goto stream_create_fail;
+ return stream;
}
list_for_each_entry(preferred_mode, &aconnector->base.modes, head) {
} else {
decide_crtc_timing_for_drm_display_mode(
&mode, preferred_mode,
- dm_state->scaling != RMX_OFF);
+ dm_state ? (dm_state->scaling != RMX_OFF) : false);
}
+ if (!dm_state)
+ drm_mode_set_crtcinfo(&mode, 0);
+
fill_stream_properties_from_drm_display_mode(stream,
&mode, &aconnector->base);
update_stream_scaling_settings(&mode, dm_state, stream);
drm_connector,
aconnector->dc_sink);
-stream_create_fail:
-dm_state_null:
-drm_connector_null:
-mst_dc_sink_create_done:
+ update_stream_signal(stream);
+
return stream;
}
.page_flip = drm_atomic_helper_page_flip,
.atomic_duplicate_state = dm_crtc_duplicate_state,
.atomic_destroy_state = dm_crtc_destroy_state,
+ .set_crc_source = amdgpu_dm_crtc_set_crc_source,
};
static enum drm_connector_status
/* TODO: Unhardcode stream count */
struct dc_stream_state *stream;
struct amdgpu_dm_connector *aconnector = to_amdgpu_dm_connector(connector);
+ enum dc_status dc_result = DC_OK;
if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
(mode->flags & DRM_MODE_FLAG_DBLSCAN))
goto fail;
}
- stream = dc_create_stream_for_sink(dc_sink);
+ stream = create_stream_for_sink(aconnector, mode, NULL);
if (stream == NULL) {
DRM_ERROR("Failed to create stream for sink!\n");
goto fail;
}
- drm_mode_set_crtcinfo(mode, 0);
- fill_stream_properties_from_drm_display_mode(stream, mode, connector);
-
- stream->src.width = mode->hdisplay;
- stream->src.height = mode->vdisplay;
- stream->dst = stream->src;
+ dc_result = dc_validate_stream(adev->dm.dc, stream);
- if (dc_validate_stream(adev->dm.dc, stream) == DC_OK)
+ if (dc_result == DC_OK)
result = MODE_OK;
+ else
+ DRM_DEBUG_KMS("Mode %dx%d (clk %d) failed DC validation with error %d\n",
+ mode->vdisplay,
+ mode->hdisplay,
+ mode->clock,
+ dc_result);
dc_stream_release(stream);
{
struct amdgpu_framebuffer *afb;
struct drm_gem_object *obj;
+ struct amdgpu_device *adev;
struct amdgpu_bo *rbo;
uint64_t chroma_addr = 0;
- int r;
struct dm_plane_state *dm_plane_state_new, *dm_plane_state_old;
unsigned int awidth;
+ uint32_t domain;
+ int r;
dm_plane_state_old = to_dm_plane_state(plane->state);
dm_plane_state_new = to_dm_plane_state(new_state);
obj = afb->obj;
rbo = gem_to_amdgpu_bo(obj);
+ adev = amdgpu_ttm_adev(rbo->tbo.bdev);
r = amdgpu_bo_reserve(rbo, false);
if (unlikely(r != 0))
return r;
- r = amdgpu_bo_pin(rbo, AMDGPU_GEM_DOMAIN_VRAM, &afb->address);
+ if (plane->type != DRM_PLANE_TYPE_CURSOR)
+ domain = amdgpu_display_framebuffer_domains(adev);
+ else
+ domain = AMDGPU_GEM_DOMAIN_VRAM;
+ r = amdgpu_bo_pin(rbo, domain, &afb->address);
amdgpu_bo_unreserve(rbo);
acrtc->base.enabled = false;
dm->adev->mode_info.crtcs[crtc_index] = acrtc;
- drm_mode_crtc_set_gamma_size(&acrtc->base, 256);
+ drm_crtc_enable_color_mgmt(&acrtc->base, MAX_COLOR_LUT_ENTRIES,
+ true, MAX_COLOR_LUT_ENTRIES);
+ drm_mode_crtc_set_gamma_size(&acrtc->base, MAX_COLOR_LUT_ENTRIES);
return 0;
struct edid *edid = amdgpu_dm_connector->edid;
encoder = helper->best_encoder(connector);
-
amdgpu_dm_connector_ddc_get_modes(connector, edid);
amdgpu_dm_connector_add_common_modes(encoder, connector);
+
+#if defined(CONFIG_DRM_AMD_DC_FBC)
+ amdgpu_dm_fbc_init(connector);
+#endif
return amdgpu_dm_connector->num_modes;
}
* constant is the same as PFLIP
*/
int irq_type =
- amdgpu_crtc_idx_to_irq_type(
+ amdgpu_display_crtc_idx_to_irq_type(
adev,
acrtc->crtc_id);
/* Prepare wait for target vblank early - before the fence-waits */
- target_vblank = target - drm_crtc_vblank_count(crtc) +
+ target_vblank = target - (uint32_t)drm_crtc_vblank_count(crtc) +
amdgpu_get_vblank_counter_kms(crtc->dev, acrtc->crtc_id);
/* TODO This might fail and hence better not used, wait
* targeted by the flip
*/
while ((acrtc->enabled &&
- (amdgpu_get_crtc_scanoutpos(adev->ddev, acrtc->crtc_id, 0,
- &vpos, &hpos, NULL, NULL,
- &crtc->hwmode)
+ (amdgpu_display_get_crtc_scanoutpos(adev->ddev, acrtc->crtc_id,
+ 0, &vpos, &hpos, NULL,
+ NULL, &crtc->hwmode)
& (DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK)) ==
(DRM_SCANOUTPOS_VALID | DRM_SCANOUTPOS_IN_VBLANK) &&
(int)(target_vblank -
amdgpu_dm_do_flip(
crtc,
fb,
- drm_crtc_vblank_count(crtc) + *wait_for_vblank,
+ (uint32_t)drm_crtc_vblank_count(crtc) + *wait_for_vblank,
dm_state->context);
}
/* Release extra reference */
if (new_stream)
dc_stream_release(new_stream);
+
+ /*
+ * We want to do dc stream updates that do not require a
+ * full modeset below.
+ */
+ if (!enable || !aconnector || modereset_required(new_crtc_state))
+ continue;
+ /*
+ * Given above conditions, the dc state cannot be NULL because:
+ * 1. We're attempting to enable a CRTC. Which has a...
+ * 2. Valid connector attached, and
+ * 3. User does not want to reset it (disable or mark inactive,
+ * which can happen on a CRTC that's already disabled).
+ * => It currently exists.
+ */
+ BUG_ON(dm_new_crtc_state->stream == NULL);
+
+ /* Color managment settings */
+ if (dm_new_crtc_state->base.color_mgmt_changed) {
+ ret = amdgpu_dm_set_regamma_lut(dm_new_crtc_state);
+ if (ret)
+ goto fail;
+ amdgpu_dm_set_ctm(dm_new_crtc_state);
+ }
}
return ret;
if (ret)
return ret;
-
if (!dc_add_plane_to_context(
dc,
dm_new_crtc_state->stream,
unsigned int format_modifier_count = 0;
int ret;
+ /* plane index is used with 32bit bitmasks */
+ if (WARN_ON(config->num_total_plane >= 32))
+ return -EINVAL;
+
ret = drm_mode_object_add(dev, &plane->base, DRM_MODE_OBJECT_PLANE);
if (ret)
return ret;
if (r)
return r;
- current_vblank = drm_crtc_vblank_count(crtc);
+ current_vblank = (u32)drm_crtc_vblank_count(crtc);
switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) {
case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE:
subdir-ccflags-y += $(call cc-disable-warning, type-limits)
subdir-ccflags-y += $(call cc-disable-warning, missing-field-initializers)
subdir-ccflags-y += $(call cc-disable-warning, implicit-fallthrough)
+ subdir-ccflags-y += $(call cc-disable-warning, unused-but-set-variable)
subdir-ccflags-$(CONFIG_DRM_I915_WERROR) += -Werror
# Fine grained warnings disable
intel_fbc.o \
intel_fifo_underrun.o \
intel_frontbuffer.o \
+ intel_hdcp.o \
intel_hotplug.o \
intel_modes.o \
intel_overlay.o \
info->size << PAGE_SHIFT);
i915_gem_object_init(obj, &intel_vgpu_gem_ops);
- obj->base.read_domains = I915_GEM_DOMAIN_GTT;
- obj->base.write_domain = 0;
+ obj->read_domains = I915_GEM_DOMAIN_GTT;
+ obj->write_domain = 0;
if (IS_SKYLAKE(dev_priv) || IS_KABYLAKE(dev_priv)) {
unsigned int tiling_mode = 0;
unsigned int stride = 0;
ret = PTR_ERR(dmabuf);
goto out_free_gem;
}
- obj->base.dma_buf = dmabuf;
i915_gem_object_put(obj);
#define DRIVER_NAME "i915"
#define DRIVER_DESC "Intel Graphics"
- #define DRIVER_DATE "20180207"
- #define DRIVER_TIMESTAMP 1517988364
+ #define DRIVER_DATE "20180221"
+ #define DRIVER_TIMESTAMP 1519219289
/* Use I915_STATE_WARN(x) and I915_STATE_WARN_ON() (rather than WARN() and
* WARN_ON()) for hw state sanity checks to check for unexpected conditions
u32 reset_count;
u32 suspend_count;
struct intel_device_info device_info;
+ struct intel_driver_caps driver_caps;
struct i915_params params;
struct i915_error_uc {
*/
struct intel_fbc_state_cache {
struct i915_vma *vma;
+ unsigned long flags;
struct {
unsigned int mode_flags;
*/
struct intel_fbc_reg_params {
struct i915_vma *vma;
+ unsigned long flags;
struct {
enum pipe pipe;
struct intel_fbc_work {
bool scheduled;
- u32 scheduled_vblank;
+ u64 scheduled_vblank;
struct work_struct work;
} work;
struct intel_rc6 {
bool enabled;
+ u64 prev_hw_residency[4];
+ u64 cur_residency[4];
};
struct intel_llc_pstate {
struct llist_head free_list;
struct work_struct free_work;
spinlock_t free_lock;
+ /**
+ * Count of objects pending destructions. Used to skip needlessly
+ * waiting on an RCU barrier if no objects are waiting to be freed.
+ */
+ atomic_t free_count;
/**
* Small stash of WC pages
u32 size;
u8 *data;
const u8 *sequence[MIPI_SEQ_MAX];
+ u8 *deassert_seq; /* Used by fixup_mipi_sequences() */
} dsi;
int crt_ddc_pin;
struct kmem_cache *priorities;
const struct intel_device_info info;
+ struct intel_driver_caps caps;
/**
* Data Stolen Memory - aka "i915 stolen memory" gives us the start and
* We have one bit per pipe and per scanout plane type.
*/
#define INTEL_FRONTBUFFER_BITS_PER_PIPE 8
- #define INTEL_FRONTBUFFER(pipe, plane_id) \
- (1 << ((plane_id) + INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+ #define INTEL_FRONTBUFFER(pipe, plane_id) ({ \
+ BUILD_BUG_ON(INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES > 32); \
+ BUILD_BUG_ON(I915_MAX_PLANES > INTEL_FRONTBUFFER_BITS_PER_PIPE); \
+ BIT((plane_id) + INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)); \
+ })
#define INTEL_FRONTBUFFER_OVERLAY(pipe) \
- (1 << (INTEL_FRONTBUFFER_BITS_PER_PIPE - 1 + INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+ BIT(INTEL_FRONTBUFFER_BITS_PER_PIPE - 1 + INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))
#define INTEL_FRONTBUFFER_ALL_MASK(pipe) \
- (0xff << (INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe)))
+ GENMASK(INTEL_FRONTBUFFER_BITS_PER_PIPE * ((pipe) + 1) - 1, \
+ INTEL_FRONTBUFFER_BITS_PER_PIPE * (pipe))
/*
* Optimised SGL iterator for GEM objects
#define HAS_FW_BLC(dev_priv) (INTEL_GEN(dev_priv) > 2)
#define HAS_FBC(dev_priv) ((dev_priv)->info.has_fbc)
- #define HAS_CUR_FBC(dev_priv) (!HAS_GMCH_DISPLAY(dev_priv) && INTEL_INFO(dev_priv)->gen >= 7)
+ #define HAS_CUR_FBC(dev_priv) (!HAS_GMCH_DISPLAY(dev_priv) && INTEL_GEN(dev_priv) >= 7)
#define HAS_IPS(dev_priv) (IS_HSW_ULT(dev_priv) || IS_BROADWELL(dev_priv))
#define INTEL_PCH_QEMU_DEVICE_ID_TYPE 0x2900 /* qemu q35 has 2918 */
#define INTEL_PCH_TYPE(dev_priv) ((dev_priv)->pch_type)
+ #define INTEL_PCH_ID(dev_priv) ((dev_priv)->pch_id)
#define HAS_PCH_ICP(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_ICP)
#define HAS_PCH_CNP(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_CNP)
#define HAS_PCH_CNP_LP(dev_priv) \
- ((dev_priv)->pch_id == INTEL_PCH_CNP_LP_DEVICE_ID_TYPE)
+ (INTEL_PCH_ID(dev_priv) == INTEL_PCH_CNP_LP_DEVICE_ID_TYPE)
#define HAS_PCH_KBP(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_KBP)
#define HAS_PCH_SPT(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_SPT)
#define HAS_PCH_LPT(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_LPT)
#define HAS_PCH_LPT_LP(dev_priv) \
- ((dev_priv)->pch_id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE || \
- (dev_priv)->pch_id == INTEL_PCH_WPT_LP_DEVICE_ID_TYPE)
+ (INTEL_PCH_ID(dev_priv) == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE || \
+ INTEL_PCH_ID(dev_priv) == INTEL_PCH_WPT_LP_DEVICE_ID_TYPE)
#define HAS_PCH_LPT_H(dev_priv) \
- ((dev_priv)->pch_id == INTEL_PCH_LPT_DEVICE_ID_TYPE || \
- (dev_priv)->pch_id == INTEL_PCH_WPT_DEVICE_ID_TYPE)
+ (INTEL_PCH_ID(dev_priv) == INTEL_PCH_LPT_DEVICE_ID_TYPE || \
+ INTEL_PCH_ID(dev_priv) == INTEL_PCH_WPT_DEVICE_ID_TYPE)
#define HAS_PCH_CPT(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_CPT)
#define HAS_PCH_IBX(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_IBX)
#define HAS_PCH_NOP(dev_priv) (INTEL_PCH_TYPE(dev_priv) == PCH_NOP)
struct drm_file *file_priv);
int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
- int i915_gem_execbuffer(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
- int i915_gem_execbuffer2(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
+ int i915_gem_execbuffer_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+ int i915_gem_execbuffer2_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
int i915_gem_busy_ioctl(struct drm_device *dev, void *data,
struct drm_file *file_priv);
int i915_gem_get_caching_ioctl(struct drm_device *dev, void *data,
static inline void i915_gem_drain_freed_objects(struct drm_i915_private *i915)
{
+ if (!atomic_read(&i915->mm.free_count))
+ return;
+
/* A single pass should suffice to release all the freed objects (along
* most call paths) , but be a little more paranoid in that freeing
* the objects does take a little amount of time, during which the rcu
struct i915_vma * __must_check
i915_gem_object_pin_to_display_plane(struct drm_i915_gem_object *obj,
u32 alignment,
- const struct i915_ggtt_view *view);
+ const struct i915_ggtt_view *view,
+ unsigned int flags);
void i915_gem_object_unpin_from_display_plane(struct i915_vma *vma);
int i915_gem_object_attach_phys(struct drm_i915_gem_object *obj,
int align);
extern void intel_teardown_gmbus(struct drm_i915_private *dev_priv);
extern bool intel_gmbus_is_valid_pin(struct drm_i915_private *dev_priv,
unsigned int pin);
+extern int intel_gmbus_output_aksv(struct i2c_adapter *adapter);
extern struct i2c_adapter *
intel_gmbus_get_adapter(struct drm_i915_private *dev_priv, unsigned int pin);
/* intel_bios.c */
void intel_bios_init(struct drm_i915_private *dev_priv);
+ void intel_bios_cleanup(struct drm_i915_private *dev_priv);
bool intel_bios_is_valid_vbt(const void *buf, size_t size);
bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv);
bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin);
#define CL_POWER_DOWN_ENABLE (1 << 4)
#define SUS_CLOCK_CONFIG (3 << 0)
+ #define _ICL_PORT_CL_DW5_A 0x162014
+ #define _ICL_PORT_CL_DW5_B 0x6C014
+ #define ICL_PORT_CL_DW5(port) _MMIO_PORT(port, _ICL_PORT_CL_DW5_A, \
+ _ICL_PORT_CL_DW5_B)
+
#define _PORT_CL1CM_DW9_A 0x162024
#define _PORT_CL1CM_DW9_BC 0x6C024
#define IREF0RC_OFFSET_SHIFT 8
#define _CNL_PORT_TX_DW5_LN0_AE 0x162454
#define _CNL_PORT_TX_DW5_LN0_B 0x162654
#define _CNL_PORT_TX_DW5_LN0_C 0x162C54
- #define _CNL_PORT_TX_DW5_LN0_D 0x162ED4
+ #define _CNL_PORT_TX_DW5_LN0_D 0x162E54
#define _CNL_PORT_TX_DW5_LN0_F 0x162854
#define CNL_PORT_TX_DW5_GRP(port) _MMIO_PORT6(port, \
_CNL_PORT_TX_DW5_GRP_AE, \
#define _CNL_PORT_TX_DW7_LN0_AE 0x16245C
#define _CNL_PORT_TX_DW7_LN0_B 0x16265C
#define _CNL_PORT_TX_DW7_LN0_C 0x162C5C
- #define _CNL_PORT_TX_DW7_LN0_D 0x162EDC
+ #define _CNL_PORT_TX_DW7_LN0_D 0x162E5C
#define _CNL_PORT_TX_DW7_LN0_F 0x16285C
#define CNL_PORT_TX_DW7_GRP(port) _MMIO_PORT6(port, \
_CNL_PORT_TX_DW7_GRP_AE, \
#define CNL_PORT_COMP_DW9 _MMIO(0x162124)
#define CNL_PORT_COMP_DW10 _MMIO(0x162128)
+ #define _ICL_PORT_COMP_DW0_A 0x162100
+ #define _ICL_PORT_COMP_DW0_B 0x6C100
+ #define ICL_PORT_COMP_DW0(port) _MMIO_PORT(port, _ICL_PORT_COMP_DW0_A, \
+ _ICL_PORT_COMP_DW0_B)
+ #define _ICL_PORT_COMP_DW1_A 0x162104
+ #define _ICL_PORT_COMP_DW1_B 0x6C104
+ #define ICL_PORT_COMP_DW1(port) _MMIO_PORT(port, _ICL_PORT_COMP_DW1_A, \
+ _ICL_PORT_COMP_DW1_B)
+ #define _ICL_PORT_COMP_DW3_A 0x16210C
+ #define _ICL_PORT_COMP_DW3_B 0x6C10C
+ #define ICL_PORT_COMP_DW3(port) _MMIO_PORT(port, _ICL_PORT_COMP_DW3_A, \
+ _ICL_PORT_COMP_DW3_B)
+ #define _ICL_PORT_COMP_DW9_A 0x162124
+ #define _ICL_PORT_COMP_DW9_B 0x6C124
+ #define ICL_PORT_COMP_DW9(port) _MMIO_PORT(port, _ICL_PORT_COMP_DW9_A, \
+ _ICL_PORT_COMP_DW9_B)
+ #define _ICL_PORT_COMP_DW10_A 0x162128
+ #define _ICL_PORT_COMP_DW10_B 0x6C128
+ #define ICL_PORT_COMP_DW10(port) _MMIO_PORT(port, \
+ _ICL_PORT_COMP_DW10_A, \
+ _ICL_PORT_COMP_DW10_B)
+
/* BXT PHY Ref registers */
#define _PORT_REF_DW3_A 0x16218C
#define _PORT_REF_DW3_BC 0x6C18C
# define GPIO_DATA_PULLUP_DISABLE (1 << 13)
#define GMBUS0 _MMIO(dev_priv->gpio_mmio_base + 0x5100) /* clock/port select */
+#define GMBUS_AKSV_SELECT (1<<11)
#define GMBUS_RATE_100KHZ (0<<8)
#define GMBUS_RATE_50KHZ (1<<8)
#define GMBUS_RATE_400KHZ (2<<8) /* reserved on Pineview */
#define DISP_DATA_PARTITION_5_6 (1<<6)
#define DISP_IPC_ENABLE (1<<3)
#define DBUF_CTL _MMIO(0x45008)
+ #define DBUF_CTL_S1 _MMIO(0x45008)
+ #define DBUF_CTL_S2 _MMIO(0x44FE8)
#define DBUF_POWER_REQUEST (1<<31)
#define DBUF_POWER_STATE (1<<30)
#define GEN7_MSG_CTL _MMIO(0x45010)
#define RESET_PCH_HANDSHAKE_ENABLE (1<<4)
#define GEN8_CHICKEN_DCPR_1 _MMIO(0x46430)
- #define SKL_SELECT_ALTERNATE_DC_EXIT (1<<30)
- #define MASK_WAKEMEM (1<<13)
+ #define SKL_SELECT_ALTERNATE_DC_EXIT (1 << 30)
+ #define MASK_WAKEMEM (1 << 13)
+ #define CNL_DDI_CLOCK_REG_ACCESS_ON (1 << 7)
#define SKL_DFSM _MMIO(0x51000)
#define SKL_DFSM_CDCLK_LIMIT_MASK (3 << 23)
#define SKL_DFSM_PIPE_B_DISABLE (1 << 21)
#define SKL_DFSM_PIPE_C_DISABLE (1 << 28)
- #define SKL_DSSM _MMIO(0x51004)
- #define CNL_DSSM_CDCLK_PLL_REFCLK_24MHz (1 << 31)
+ #define SKL_DSSM _MMIO(0x51004)
+ #define CNL_DSSM_CDCLK_PLL_REFCLK_24MHz (1 << 31)
+ #define ICL_DSSM_CDCLK_PLL_REFCLK_MASK (7 << 29)
+ #define ICL_DSSM_CDCLK_PLL_REFCLK_24MHz (0 << 29)
+ #define ICL_DSSM_CDCLK_PLL_REFCLK_19_2MHz (1 << 29)
+ #define ICL_DSSM_CDCLK_PLL_REFCLK_38_4MHz (2 << 29)
#define GEN7_FF_SLICE_CS_CHICKEN1 _MMIO(0x20e0)
#define GEN9_FFSC_PERCTX_PREEMPT_CTRL (1<<14)
#define GEN9_MEM_LATENCY_LEVEL_1_5_SHIFT 8
#define GEN9_MEM_LATENCY_LEVEL_2_6_SHIFT 16
#define GEN9_MEM_LATENCY_LEVEL_3_7_SHIFT 24
+#define SKL_PCODE_LOAD_HDCP_KEYS 0x5
#define SKL_PCODE_CDCLK_CONTROL 0x7
#define SKL_CDCLK_PREPARE_FOR_CHANGE 0x3
#define SKL_CDCLK_READY_FOR_CHANGE 0x1
#define CNL_AUX_ANAOVRD1_ENABLE (1<<16)
#define CNL_AUX_ANAOVRD1_LDO_BYPASS (1<<23)
+/* HDCP Key Registers */
+#define HDCP_KEY_CONF _MMIO(0x66c00)
+#define HDCP_AKSV_SEND_TRIGGER BIT(31)
+#define HDCP_CLEAR_KEYS_TRIGGER BIT(30)
+#define HDCP_KEY_LOAD_TRIGGER BIT(8)
+#define HDCP_KEY_STATUS _MMIO(0x66c04)
+#define HDCP_FUSE_IN_PROGRESS BIT(7)
+#define HDCP_FUSE_ERROR BIT(6)
+#define HDCP_FUSE_DONE BIT(5)
+#define HDCP_KEY_LOAD_STATUS BIT(1)
+#define HDCP_KEY_LOAD_DONE BIT(0)
+#define HDCP_AKSV_LO _MMIO(0x66c10)
+#define HDCP_AKSV_HI _MMIO(0x66c14)
+
+/* HDCP Repeater Registers */
+#define HDCP_REP_CTL _MMIO(0x66d00)
+#define HDCP_DDIB_REP_PRESENT BIT(30)
+#define HDCP_DDIA_REP_PRESENT BIT(29)
+#define HDCP_DDIC_REP_PRESENT BIT(28)
+#define HDCP_DDID_REP_PRESENT BIT(27)
+#define HDCP_DDIF_REP_PRESENT BIT(26)
+#define HDCP_DDIE_REP_PRESENT BIT(25)
+#define HDCP_DDIB_SHA1_M0 (1 << 20)
+#define HDCP_DDIA_SHA1_M0 (2 << 20)
+#define HDCP_DDIC_SHA1_M0 (3 << 20)
+#define HDCP_DDID_SHA1_M0 (4 << 20)
+#define HDCP_DDIF_SHA1_M0 (5 << 20)
+#define HDCP_DDIE_SHA1_M0 (6 << 20) /* Bspec says 5? */
+#define HDCP_SHA1_BUSY BIT(16)
+#define HDCP_SHA1_READY BIT(17)
+#define HDCP_SHA1_COMPLETE BIT(18)
+#define HDCP_SHA1_V_MATCH BIT(19)
+#define HDCP_SHA1_TEXT_32 (1 << 1)
+#define HDCP_SHA1_COMPLETE_HASH (2 << 1)
+#define HDCP_SHA1_TEXT_24 (4 << 1)
+#define HDCP_SHA1_TEXT_16 (5 << 1)
+#define HDCP_SHA1_TEXT_8 (6 << 1)
+#define HDCP_SHA1_TEXT_0 (7 << 1)
+#define HDCP_SHA_V_PRIME_H0 _MMIO(0x66d04)
+#define HDCP_SHA_V_PRIME_H1 _MMIO(0x66d08)
+#define HDCP_SHA_V_PRIME_H2 _MMIO(0x66d0C)
+#define HDCP_SHA_V_PRIME_H3 _MMIO(0x66d10)
+#define HDCP_SHA_V_PRIME_H4 _MMIO(0x66d14)
+#define HDCP_SHA_V_PRIME(h) _MMIO((0x66d04 + h * 4))
+#define HDCP_SHA_TEXT _MMIO(0x66d18)
+
+/* HDCP Auth Registers */
+#define _PORTA_HDCP_AUTHENC 0x66800
+#define _PORTB_HDCP_AUTHENC 0x66500
+#define _PORTC_HDCP_AUTHENC 0x66600
+#define _PORTD_HDCP_AUTHENC 0x66700
+#define _PORTE_HDCP_AUTHENC 0x66A00
+#define _PORTF_HDCP_AUTHENC 0x66900
+#define _PORT_HDCP_AUTHENC(port, x) _MMIO(_PICK(port, \
+ _PORTA_HDCP_AUTHENC, \
+ _PORTB_HDCP_AUTHENC, \
+ _PORTC_HDCP_AUTHENC, \
+ _PORTD_HDCP_AUTHENC, \
+ _PORTE_HDCP_AUTHENC, \
+ _PORTF_HDCP_AUTHENC) + x)
+#define PORT_HDCP_CONF(port) _PORT_HDCP_AUTHENC(port, 0x0)
+#define HDCP_CONF_CAPTURE_AN BIT(0)
+#define HDCP_CONF_AUTH_AND_ENC (BIT(1) | BIT(0))
+#define PORT_HDCP_ANINIT(port) _PORT_HDCP_AUTHENC(port, 0x4)
+#define PORT_HDCP_ANLO(port) _PORT_HDCP_AUTHENC(port, 0x8)
+#define PORT_HDCP_ANHI(port) _PORT_HDCP_AUTHENC(port, 0xC)
+#define PORT_HDCP_BKSVLO(port) _PORT_HDCP_AUTHENC(port, 0x10)
+#define PORT_HDCP_BKSVHI(port) _PORT_HDCP_AUTHENC(port, 0x14)
+#define PORT_HDCP_RPRIME(port) _PORT_HDCP_AUTHENC(port, 0x18)
+#define PORT_HDCP_STATUS(port) _PORT_HDCP_AUTHENC(port, 0x1C)
+#define HDCP_STATUS_STREAM_A_ENC BIT(31)
+#define HDCP_STATUS_STREAM_B_ENC BIT(30)
+#define HDCP_STATUS_STREAM_C_ENC BIT(29)
+#define HDCP_STATUS_STREAM_D_ENC BIT(28)
+#define HDCP_STATUS_AUTH BIT(21)
+#define HDCP_STATUS_ENC BIT(20)
+#define HDCP_STATUS_RI_MATCH BIT(19)
+#define HDCP_STATUS_R0_READY BIT(18)
+#define HDCP_STATUS_AN_READY BIT(17)
+#define HDCP_STATUS_CIPHER BIT(16)
+#define HDCP_STATUS_FRAME_CNT(x) ((x >> 8) & 0xff)
+
/* Per-pipe DDI Function Control */
#define _TRANS_DDI_FUNC_CTL_A 0x60400
#define _TRANS_DDI_FUNC_CTL_B 0x61400
#define TRANS_DDI_EDP_INPUT_A_ONOFF (4<<12)
#define TRANS_DDI_EDP_INPUT_B_ONOFF (5<<12)
#define TRANS_DDI_EDP_INPUT_C_ONOFF (6<<12)
+#define TRANS_DDI_HDCP_SIGNALLING (1<<9)
#define TRANS_DDI_DP_VC_PAYLOAD_ALLOC (1<<8)
#define TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE (1<<7)
#define TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ (1<<6)
/* CDCLK_CTL */
#define CDCLK_CTL _MMIO(0x46000)
- #define CDCLK_FREQ_SEL_MASK (3<<26)
- #define CDCLK_FREQ_450_432 (0<<26)
- #define CDCLK_FREQ_540 (1<<26)
- #define CDCLK_FREQ_337_308 (2<<26)
- #define CDCLK_FREQ_675_617 (3<<26)
- #define BXT_CDCLK_CD2X_DIV_SEL_MASK (3<<22)
- #define BXT_CDCLK_CD2X_DIV_SEL_1 (0<<22)
- #define BXT_CDCLK_CD2X_DIV_SEL_1_5 (1<<22)
- #define BXT_CDCLK_CD2X_DIV_SEL_2 (2<<22)
- #define BXT_CDCLK_CD2X_DIV_SEL_4 (3<<22)
- #define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe)<<20)
- #define CDCLK_DIVMUX_CD_OVERRIDE (1<<19)
+ #define CDCLK_FREQ_SEL_MASK (3 << 26)
+ #define CDCLK_FREQ_450_432 (0 << 26)
+ #define CDCLK_FREQ_540 (1 << 26)
+ #define CDCLK_FREQ_337_308 (2 << 26)
+ #define CDCLK_FREQ_675_617 (3 << 26)
+ #define BXT_CDCLK_CD2X_DIV_SEL_MASK (3 << 22)
+ #define BXT_CDCLK_CD2X_DIV_SEL_1 (0 << 22)
+ #define BXT_CDCLK_CD2X_DIV_SEL_1_5 (1 << 22)
+ #define BXT_CDCLK_CD2X_DIV_SEL_2 (2 << 22)
+ #define BXT_CDCLK_CD2X_DIV_SEL_4 (3 << 22)
+ #define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe) << 20)
+ #define CDCLK_DIVMUX_CD_OVERRIDE (1 << 19)
#define BXT_CDCLK_CD2X_PIPE_NONE BXT_CDCLK_CD2X_PIPE(3)
- #define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1<<16)
+ #define ICL_CDCLK_CD2X_PIPE_NONE (7 << 19)
+ #define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1 << 16)
#define CDCLK_FREQ_DECIMAL_MASK (0x7ff)
/* LCPLL_CTL */
#define MMCD_PCLA (1 << 31)
#define MMCD_HOTSPOT_EN (1 << 27)
+ #define _ICL_PHY_MISC_A 0x64C00
+ #define _ICL_PHY_MISC_B 0x64C04
+ #define ICL_PHY_MISC(port) _MMIO_PORT(port, _ICL_PHY_MISC_A, \
+ _ICL_PHY_MISC_B)
+ #define ICL_PHY_MISC_DE_IO_COMP_PWR_DOWN (1 << 23)
+
#endif /* _I915_REG_H_ */
to_intel_digital_connector_state(old_state);
struct drm_crtc_state *crtc_state;
+ intel_hdcp_atomic_check(conn, old_state, new_state);
+
if (!new_state->crtc)
return 0;
/**
* intel_crtc_destroy_state - destroy crtc state
* @crtc: drm crtc
+ * @state: the state to destroy
*
* Destroys the crtc state (both common and Intel-specific) for the
* specified crtc.
*/
void
intel_crtc_destroy_state(struct drm_crtc *crtc,
- struct drm_crtc_state *state)
+ struct drm_crtc_state *state)
{
drm_atomic_helper_crtc_destroy_state(crtc, state);
}
/**
* intel_atomic_setup_scalers() - setup scalers for crtc per staged requests
* @dev_priv: i915 device
- * @crtc: intel crtc
+ * @intel_crtc: intel crtc
* @crtc_state: incoming crtc_state to validate and setup scalers
*
* This function sets up scalers based on staged scaling requests for
__drm_atomic_helper_plane_duplicate_state(plane, state);
intel_state->vma = NULL;
+ intel_state->flags = 0;
return state;
}
if (!intel_state->base.crtc && !old_plane_state->base.crtc)
return 0;
- /* Clip all planes to CRTC size, or 0x0 if CRTC is disabled */
- intel_state->clip.x1 = 0;
- intel_state->clip.y1 = 0;
- intel_state->clip.x2 =
- crtc_state->base.enable ? crtc_state->pipe_src_w : 0;
- intel_state->clip.y2 =
- crtc_state->base.enable ? crtc_state->pipe_src_h : 0;
-
if (state->fb && drm_rotation_90_or_270(state->rotation)) {
struct drm_format_name_buf format_name;
int max_dotclk = dev_priv->max_dotclk_freq;
int max_clock;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
- return MODE_NO_DBLESCAN;
-
if (mode->clock < 25000)
return MODE_CLOCK_LOW;
return ret;
}
- /**
- * Uses CRT_HOTPLUG_EN and CRT_HOTPLUG_STAT to detect CRT presence.
- *
- * Not for i915G/i915GM
- *
- * \return true if CRT is connected.
- * \return false if CRT is disconnected.
- */
static bool intel_crt_detect_hotplug(struct drm_connector *connector)
{
struct drm_device *dev = connector->dev;
else
status = connector_status_unknown;
intel_release_load_detect_pipe(connector, &tmp, ctx);
- } else if (ret == 0)
+ } else if (ret == 0) {
status = connector_status_unknown;
- else if (ret < 0)
+ } else {
status = ret;
+ }
out:
intel_display_power_put(dev_priv, intel_encoder->power_domain);
I915_WRITE(reg, val);
}
+int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
+ bool enable)
+{
+ struct drm_device *dev = intel_encoder->base.dev;
+ struct drm_i915_private *dev_priv = to_i915(dev);
+ enum pipe pipe = 0;
+ int ret = 0;
+ uint32_t tmp;
+
+ if (WARN_ON(!intel_display_power_get_if_enabled(dev_priv,
+ intel_encoder->power_domain)))
+ return -ENXIO;
+
+ if (WARN_ON(!intel_encoder->get_hw_state(intel_encoder, &pipe))) {
+ ret = -EIO;
+ goto out;
+ }
+
+ tmp = I915_READ(TRANS_DDI_FUNC_CTL(pipe));
+ if (enable)
+ tmp |= TRANS_DDI_HDCP_SIGNALLING;
+ else
+ tmp &= ~TRANS_DDI_HDCP_SIGNALLING;
+ I915_WRITE(TRANS_DDI_FUNC_CTL(pipe), tmp);
+out:
+ intel_display_power_put(dev_priv, intel_encoder->power_domain);
+ return ret;
+}
+
bool intel_ddi_connector_get_hw_state(struct intel_connector *intel_connector)
{
struct drm_device *dev = intel_connector->base.dev;
I915_WRITE(DPLL_CTRL2, val);
- } else if (INTEL_INFO(dev_priv)->gen < 9) {
+ } else if (INTEL_GEN(dev_priv) < 9) {
I915_WRITE(PORT_CLK_SEL(port), hsw_pll_to_ddi_pll_sel(pll));
}
intel_enable_ddi_hdmi(encoder, crtc_state, conn_state);
else
intel_enable_ddi_dp(encoder, crtc_state, conn_state);
+
+ /* Enable hdcp if it's desired */
+ if (conn_state->content_protection ==
+ DRM_MODE_CONTENT_PROTECTION_DESIRED)
+ intel_hdcp_enable(to_intel_connector(conn_state->connector));
}
static void intel_disable_ddi_dp(struct intel_encoder *encoder,
const struct intel_crtc_state *old_crtc_state,
const struct drm_connector_state *old_conn_state)
{
+ intel_hdcp_disable(to_intel_connector(old_conn_state->connector));
+
if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI))
intel_disable_ddi_hdmi(encoder, old_crtc_state, old_conn_state);
else
}
#define INTELPllInvalid(s) do { /* DRM_DEBUG(s); */ return false; } while (0)
- /**
+
+ /*
* Returns whether the given set of divisors are valid for a given refclk with
* the given connectors.
*/
-
static bool intel_PLL_is_valid(struct drm_i915_private *dev_priv,
const struct intel_limit *limit,
const struct dpll *clock)
static unsigned int intel_linear_alignment(const struct drm_i915_private *dev_priv)
{
- if (INTEL_INFO(dev_priv)->gen >= 9)
+ if (INTEL_GEN(dev_priv) >= 9)
return 256 * 1024;
else if (IS_I965G(dev_priv) || IS_I965GM(dev_priv) ||
IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv))
return 128 * 1024;
- else if (INTEL_INFO(dev_priv)->gen >= 4)
+ else if (INTEL_GEN(dev_priv) >= 4)
return 4 * 1024;
else
return 0;
}
struct i915_vma *
- intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation)
+ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
+ unsigned int rotation,
+ unsigned long *out_flags)
{
struct drm_device *dev = fb->dev;
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_i915_gem_object *obj = intel_fb_obj(fb);
struct i915_ggtt_view view;
struct i915_vma *vma;
+ unsigned int pinctl;
u32 alignment;
WARN_ON(!mutex_is_locked(&dev->struct_mutex));
atomic_inc(&dev_priv->gpu_error.pending_fb_pin);
- vma = i915_gem_object_pin_to_display_plane(obj, alignment, &view);
+ pinctl = 0;
+
+ /* Valleyview is definitely limited to scanning out the first
+ * 512MiB. Lets presume this behaviour was inherited from the
+ * g4x display engine and that all earlier gen are similarly
+ * limited. Testing suggests that it is a little more
+ * complicated than this. For example, Cherryview appears quite
+ * happy to scanout from anywhere within its global aperture.
+ */
+ if (HAS_GMCH_DISPLAY(dev_priv))
+ pinctl |= PIN_MAPPABLE;
+
+ vma = i915_gem_object_pin_to_display_plane(obj,
+ alignment, &view, pinctl);
if (IS_ERR(vma))
goto err;
* something and try to run the system in a "less than optimal"
* mode that matches the user configuration.
*/
- i915_vma_pin_fence(vma);
+ if (i915_vma_pin_fence(vma) == 0 && vma->fence)
+ *out_flags |= PLANE_HAS_FENCE;
}
i915_vma_get(vma);
return vma;
}
- void intel_unpin_fb_vma(struct i915_vma *vma)
+ void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags)
{
lockdep_assert_held(&vma->vm->i915->drm.struct_mutex);
- i915_vma_unpin_fence(vma);
+ if (flags & PLANE_HAS_FENCE)
+ i915_vma_unpin_fence(vma);
i915_gem_object_unpin_from_display_plane(vma);
i915_vma_put(vma);
}
valid_fb:
mutex_lock(&dev->struct_mutex);
intel_state->vma =
- intel_pin_and_fence_fb_obj(fb, primary->state->rotation);
+ intel_pin_and_fence_fb_obj(fb,
+ primary->state->rotation,
+ &intel_state->flags);
mutex_unlock(&dev->struct_mutex);
if (IS_ERR(intel_state->vma)) {
DRM_ERROR("failed to pin boot fb on pipe %d: %li\n",
if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
dspcntr |= DISPPLANE_PIPE_CSC_ENABLE;
- if (INTEL_GEN(dev_priv) < 4)
+ if (INTEL_GEN(dev_priv) < 5)
dspcntr |= DISPPLANE_SEL_PIPE(crtc->pipe);
switch (fb->format->format) {
/**
* skl_update_scaler_plane - Stages update to scaler state for a given plane.
- *
- * @state: crtc's scaler state
+ * @crtc_state: crtc's scaler state
* @plane_state: atomic plane state to update
*
* Return
/**
* intel_post_enable_primary - Perform operations after enabling primary plane
* @crtc: the CRTC whose primary plane was just enabled
+ * @new_crtc_state: the enabling state
*
* Performs potentially sleeping operations that must be done after the primary
* plane is enabled, such as updating FBC and IPS. Note that this may be
I915_WRITE(CLKGATE_DIS_PSL(pipe), val);
}
+ static void icl_pipe_mbus_enable(struct intel_crtc *crtc)
+ {
+ struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
+ enum pipe pipe = crtc->pipe;
+ uint32_t val;
+
+ val = MBUS_DBOX_BW_CREDIT(1) | MBUS_DBOX_A_CREDIT(2);
+
+ /* Program B credit equally to all pipes */
+ val |= MBUS_DBOX_B_CREDIT(24 / INTEL_INFO(dev_priv)->num_pipes);
+
+ I915_WRITE(PIPE_MBUS_DBOX_CTL(pipe), val);
+ }
+
static void haswell_crtc_enable(struct intel_crtc_state *pipe_config,
struct drm_atomic_state *old_state)
{
if (dev_priv->display.initial_watermarks != NULL)
dev_priv->display.initial_watermarks(old_intel_state, pipe_config);
+ if (INTEL_GEN(dev_priv) >= 11)
+ icl_pipe_mbus_enable(intel_crtc);
+
/* XXX: Do the pipe assertions at the right place for BXT DSI. */
if (!transcoder_is_dsi(cpu_transcoder))
intel_enable_pipe(pipe_config);
const struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
/* GDG double wide on either pipe, otherwise pipe A only */
- return INTEL_INFO(dev_priv)->gen < 4 &&
+ return INTEL_GEN(dev_priv) < 4 &&
(crtc->pipe == PIPE_A || IS_I915G(dev_priv));
}
* - LVDS dual channel mode
* - Double wide pipe
*/
- if ((intel_crtc_has_type(pipe_config, INTEL_OUTPUT_LVDS) &&
- intel_is_dual_link_lvds(dev)) || pipe_config->double_wide)
- pipe_config->pipe_src_w &= ~1;
+ if (pipe_config->pipe_src_w & 1) {
+ if (pipe_config->double_wide) {
+ DRM_DEBUG_KMS("Odd pipe source width not supported with double wide pipe\n");
+ return -EINVAL;
+ }
+
+ if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_LVDS) &&
+ intel_is_dual_link_lvds(dev)) {
+ DRM_DEBUG_KMS("Odd pipe source width not supported with dual link LVDS\n");
+ return -EINVAL;
+ }
+ }
/* Cantiga+ cannot handle modes with a hsync front porch of 0.
* WaPruneModeWithIncorrectHsyncOffset:ctg,elk,ilk,snb,ivb,vlv,hsw.
struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_crtc_state *config = intel_crtc->config;
- if (IS_BROADWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 9) {
+ if (IS_BROADWELL(dev_priv) || INTEL_GEN(dev_priv) >= 9) {
u32 val = 0;
switch (intel_crtc->config->pipe_bpp) {
struct intel_plane_state *plane_state)
{
const struct drm_framebuffer *fb = plane_state->base.fb;
+ struct drm_rect clip = {};
int src_x, src_y;
u32 offset;
int ret;
+ if (crtc_state->base.enable)
+ drm_mode_get_hv_timing(&crtc_state->base.mode,
+ &clip.x2, &clip.y2);
+
ret = drm_atomic_helper_check_plane_state(&plane_state->base,
&crtc_state->base,
- &plane_state->clip,
+ &clip,
DRM_PLANE_HELPER_NO_SCALING,
DRM_PLANE_HELPER_NO_SCALING,
true, true);
if (HAS_DDI(dev_priv))
cntl |= CURSOR_PIPE_CSC_ENABLE;
- cntl |= MCURSOR_PIPE_SELECT(crtc->pipe);
+ if (INTEL_GEN(dev_priv) < 5 && !IS_G4X(dev_priv))
+ cntl |= MCURSOR_PIPE_SELECT(crtc->pipe);
switch (plane_state->base.crtc_w) {
case 64:
struct drm_connector_list_iter conn_iter;
unsigned int used_ports = 0;
unsigned int used_mst_ports = 0;
+ bool ret = true;
/*
* Walk the connector list instead of the encoder
/* the same port mustn't appear more than once */
if (used_ports & port_mask)
- return false;
+ ret = false;
used_ports |= port_mask;
break;
if (used_ports & used_mst_ports)
return false;
- return true;
+ return ret;
}
static void
struct drm_device *dev = crtc->base.dev;
if (!dev->max_vblank_count)
- return drm_crtc_accurate_vblank_count(&crtc->base);
+ return (u32)drm_crtc_accurate_vblank_count(&crtc->base);
return dev->driver->get_vblank_counter(dev, crtc->pipe);
}
/**
* intel_prepare_plane_fb - Prepare fb for usage on plane
* @plane: drm plane to prepare for
- * @fb: framebuffer to prepare for presentation
+ * @new_state: the plane state being prepared
*
* Prepares a framebuffer for usage on a display plane. Generally this
* involves pinning the underlying object and updating the frontbuffer tracking
} else {
struct i915_vma *vma;
- vma = intel_pin_and_fence_fb_obj(fb, new_state->rotation);
+ vma = intel_pin_and_fence_fb_obj(fb,
+ new_state->rotation,
+ &to_intel_plane_state(new_state)->flags);
if (!IS_ERR(vma))
to_intel_plane_state(new_state)->vma = vma;
else
/**
* intel_cleanup_plane_fb - Cleans up an fb after plane use
* @plane: drm plane to clean up for
- * @fb: old framebuffer that was on plane
+ * @old_state: the state from the previous modeset
*
* Cleans up a framebuffer that has just been removed from a plane.
*
vma = fetch_and_zero(&to_intel_plane_state(old_state)->vma);
if (vma) {
mutex_lock(&plane->dev->struct_mutex);
- intel_unpin_fb_vma(vma);
+ intel_unpin_fb_vma(vma, to_intel_plane_state(old_state)->flags);
mutex_unlock(&plane->dev->struct_mutex);
}
}
int min_scale = DRM_PLANE_HELPER_NO_SCALING;
int max_scale = DRM_PLANE_HELPER_NO_SCALING;
bool can_position = false;
+ struct drm_rect clip = {};
int ret;
if (INTEL_GEN(dev_priv) >= 9) {
can_position = true;
}
+ if (crtc_state->base.enable)
+ drm_mode_get_hv_timing(&crtc_state->base.mode,
+ &clip.x2, &clip.y2);
+
ret = drm_atomic_helper_check_plane_state(&state->base,
&crtc_state->base,
- &state->clip,
+ &clip,
min_scale, max_scale,
can_position, true);
if (ret)
goto out_unlock;
}
} else {
- vma = intel_pin_and_fence_fb_obj(fb, new_plane_state->rotation);
+ vma = intel_pin_and_fence_fb_obj(fb,
+ new_plane_state->rotation,
+ &to_intel_plane_state(new_plane_state)->flags);
if (IS_ERR(vma)) {
DRM_DEBUG_KMS("failed to pin object\n");
old_vma = fetch_and_zero(&to_intel_plane_state(old_plane_state)->vma);
if (old_vma)
- intel_unpin_fb_vma(old_vma);
+ intel_unpin_fb_vma(old_vma,
+ to_intel_plane_state(old_plane_state)->flags);
out_unlock:
mutex_unlock(&dev_priv->drm.struct_mutex);
return to_intel_crtc(connector->base.state->crtc)->pipe;
}
- int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
- struct drm_file *file)
+ int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file)
{
struct drm_i915_get_pipe_from_crtc_id *pipe_from_crtc_id = data;
struct drm_crtc *drmmode_crtc;
* gen2/3 display engine uses the fence if present,
* so the tiling mode must match the fb modifier exactly.
*/
- if (INTEL_INFO(dev_priv)->gen < 4 &&
+ if (INTEL_GEN(dev_priv) < 4 &&
tiling != intel_fb_modifier_to_tiling(mode_cmd->modifier[0])) {
DRM_DEBUG_KMS("tiling_mode must match fb modifier exactly on gen2/3\n");
goto err;
kfree(state);
}
+static enum drm_mode_status
+intel_mode_valid(struct drm_device *dev,
+ const struct drm_display_mode *mode)
+{
+ if (mode->vscan > 1)
+ return MODE_NO_VSCAN;
+
+ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
+ return MODE_NO_DBLESCAN;
+
+ if (mode->flags & DRM_MODE_FLAG_HSKEW)
+ return MODE_H_ILLEGAL;
+
+ if (mode->flags & (DRM_MODE_FLAG_CSYNC |
+ DRM_MODE_FLAG_NCSYNC |
+ DRM_MODE_FLAG_PCSYNC))
+ return MODE_HSYNC;
+
+ if (mode->flags & (DRM_MODE_FLAG_BCAST |
+ DRM_MODE_FLAG_PIXMUX |
+ DRM_MODE_FLAG_CLKDIV2))
+ return MODE_BAD;
+
+ return MODE_OK;
+}
+
static const struct drm_mode_config_funcs intel_mode_funcs = {
.fb_create = intel_user_framebuffer_create,
.get_format_info = intel_get_format_info,
.output_poll_changed = intel_fbdev_output_poll_changed,
+ .mode_valid = intel_mode_valid,
.atomic_check = intel_atomic_check,
.atomic_commit = intel_atomic_commit,
.atomic_state_alloc = intel_atomic_state_alloc,
{
intel_init_cdclk_hooks(dev_priv);
- if (INTEL_INFO(dev_priv)->gen >= 9) {
+ if (INTEL_GEN(dev_priv) >= 9) {
dev_priv->display.get_pipe_config = haswell_get_pipe_config;
dev_priv->display.get_initial_plane_config =
skylake_get_initial_plane_config;
for_each_intel_connector_iter(connector, &conn_iter) {
if (connector->modeset_retry_work.func)
cancel_work_sync(&connector->modeset_retry_work);
+ if (connector->hdcp_shim) {
+ cancel_delayed_work_sync(&connector->hdcp_check_work);
+ cancel_work_sync(&connector->hdcp_prop_work);
+ }
}
drm_connector_list_iter_end(&conn_iter);
}
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_dp_helper.h>
#include <drm/drm_edid.h>
+#include <drm/drm_hdcp.h>
#include "intel_drv.h"
#include <drm/i915_drm.h>
#include "i915_drv.h"
if (IS_CNL_WITH_PORT_F(dev_priv))
return 810000;
- /* For other SKUs, max rate on ports A and B is 5.4G */
+ /* For other SKUs, max rate on ports A and D is 5.4G */
if (port == PORT_A || port == PORT_D)
return 540000;
DP_AUX_CH_CTL_SYNC_PULSE_SKL(32);
}
+static uint32_t intel_dp_get_aux_send_ctl(struct intel_dp *intel_dp,
+ bool has_aux_irq,
+ int send_bytes,
+ uint32_t aux_clock_divider,
+ bool aksv_write)
+{
+ uint32_t val = 0;
+
+ if (aksv_write) {
+ send_bytes += 5;
+ val |= DP_AUX_CH_CTL_AUX_AKSV_SELECT;
+ }
+
+ return val | intel_dp->get_aux_send_ctl(intel_dp,
+ has_aux_irq,
+ send_bytes,
+ aux_clock_divider);
+}
+
static int
intel_dp_aux_ch(struct intel_dp *intel_dp,
const uint8_t *send, int send_bytes,
- uint8_t *recv, int recv_size)
+ uint8_t *recv, int recv_size, bool aksv_write)
{
struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
struct drm_i915_private *dev_priv =
}
while ((aux_clock_divider = intel_dp->get_aux_clock_divider(intel_dp, clock++))) {
- u32 send_ctl = intel_dp->get_aux_send_ctl(intel_dp,
- has_aux_irq,
- send_bytes,
- aux_clock_divider);
+ u32 send_ctl = intel_dp_get_aux_send_ctl(intel_dp,
+ has_aux_irq,
+ send_bytes,
+ aux_clock_divider,
+ aksv_write);
/* Must try at least 3 times according to DP spec */
for (try = 0; try < 5; try++) {
if (msg->buffer)
memcpy(txbuf + HEADER_SIZE, msg->buffer, msg->size);
- ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
+ ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize,
+ false);
if (ret > 0) {
msg->reply = rxbuf[0] >> 4;
if (WARN_ON(rxsize > 20))
return -E2BIG;
- ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize);
+ ret = intel_dp_aux_ch(intel_dp, txbuf, txsize, rxbuf, rxsize,
+ false);
if (ret > 0) {
msg->reply = rxbuf[0] >> 4;
/*
static i915_reg_t intel_aux_ctl_reg(struct drm_i915_private *dev_priv,
enum port port)
{
- if (INTEL_INFO(dev_priv)->gen >= 9)
+ if (INTEL_GEN(dev_priv) >= 9)
return skl_aux_ctl_reg(dev_priv, port);
else if (HAS_PCH_SPLIT(dev_priv))
return ilk_aux_ctl_reg(dev_priv, port);
static i915_reg_t intel_aux_data_reg(struct drm_i915_private *dev_priv,
enum port port, int index)
{
- if (INTEL_INFO(dev_priv)->gen >= 9)
+ if (INTEL_GEN(dev_priv) >= 9)
return skl_aux_data_reg(dev_priv, port, index);
else if (HAS_PCH_SPLIT(dev_priv))
return ilk_aux_data_reg(dev_priv, port, index);
pps_unlock(intel_dp);
}
+static
+int intel_dp_hdcp_write_an_aksv(struct intel_digital_port *intel_dig_port,
+ u8 *an)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(&intel_dig_port->base.base);
+ uint8_t txbuf[4], rxbuf[2], reply = 0;
+ ssize_t dpcd_ret;
+ int ret;
+
+ /* Output An first, that's easy */
+ dpcd_ret = drm_dp_dpcd_write(&intel_dig_port->dp.aux, DP_AUX_HDCP_AN,
+ an, DRM_HDCP_AN_LEN);
+ if (dpcd_ret != DRM_HDCP_AN_LEN) {
+ DRM_ERROR("Failed to write An over DP/AUX (%zd)\n", dpcd_ret);
+ return dpcd_ret >= 0 ? -EIO : dpcd_ret;
+ }
+
+ /*
+ * Since Aksv is Oh-So-Secret, we can't access it in software. So in
+ * order to get it on the wire, we need to create the AUX header as if
+ * we were writing the data, and then tickle the hardware to output the
+ * data once the header is sent out.
+ */
+ txbuf[0] = (DP_AUX_NATIVE_WRITE << 4) |
+ ((DP_AUX_HDCP_AKSV >> 16) & 0xf);
+ txbuf[1] = (DP_AUX_HDCP_AKSV >> 8) & 0xff;
+ txbuf[2] = DP_AUX_HDCP_AKSV & 0xff;
+ txbuf[3] = DRM_HDCP_KSV_LEN - 1;
+
+ ret = intel_dp_aux_ch(intel_dp, txbuf, sizeof(txbuf), rxbuf,
+ sizeof(rxbuf), true);
+ if (ret < 0) {
+ DRM_ERROR("Write Aksv over DP/AUX failed (%d)\n", ret);
+ return ret;
+ } else if (ret == 0) {
+ DRM_ERROR("Aksv write over DP/AUX was empty\n");
+ return -EIO;
+ }
+
+ reply = (rxbuf[0] >> 4) & DP_AUX_NATIVE_REPLY_MASK;
+ return reply == DP_AUX_NATIVE_REPLY_ACK ? 0 : -EIO;
+}
+
+static int intel_dp_hdcp_read_bksv(struct intel_digital_port *intel_dig_port,
+ u8 *bksv)
+{
+ ssize_t ret;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BKSV, bksv,
+ DRM_HDCP_KSV_LEN);
+ if (ret != DRM_HDCP_KSV_LEN) {
+ DRM_ERROR("Read Bksv from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *intel_dig_port,
+ u8 *bstatus)
+{
+ ssize_t ret;
+ /*
+ * For some reason the HDMI and DP HDCP specs call this register
+ * definition by different names. In the HDMI spec, it's called BSTATUS,
+ * but in DP it's called BINFO.
+ */
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BINFO,
+ bstatus, DRM_HDCP_BSTATUS_LEN);
+ if (ret != DRM_HDCP_BSTATUS_LEN) {
+ DRM_ERROR("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_bcaps(struct intel_digital_port *intel_dig_port,
+ u8 *bcaps)
+{
+ ssize_t ret;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BCAPS,
+ bcaps, 1);
+ if (ret != 1) {
+ DRM_ERROR("Read bcaps from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+
+ return 0;
+}
+
+static
+int intel_dp_hdcp_repeater_present(struct intel_digital_port *intel_dig_port,
+ bool *repeater_present)
+{
+ ssize_t ret;
+ u8 bcaps;
+
+ ret = intel_dp_hdcp_read_bcaps(intel_dig_port, &bcaps);
+ if (ret)
+ return ret;
+
+ *repeater_present = bcaps & DP_BCAPS_REPEATER_PRESENT;
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_ri_prime(struct intel_digital_port *intel_dig_port,
+ u8 *ri_prime)
+{
+ ssize_t ret;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_RI_PRIME,
+ ri_prime, DRM_HDCP_RI_LEN);
+ if (ret != DRM_HDCP_RI_LEN) {
+ DRM_ERROR("Read Ri' from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_ksv_ready(struct intel_digital_port *intel_dig_port,
+ bool *ksv_ready)
+{
+ ssize_t ret;
+ u8 bstatus;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
+ &bstatus, 1);
+ if (ret != 1) {
+ DRM_ERROR("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ *ksv_ready = bstatus & DP_BSTATUS_READY;
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_ksv_fifo(struct intel_digital_port *intel_dig_port,
+ int num_downstream, u8 *ksv_fifo)
+{
+ ssize_t ret;
+ int i;
+
+ /* KSV list is read via 15 byte window (3 entries @ 5 bytes each) */
+ for (i = 0; i < num_downstream; i += 3) {
+ size_t len = min(num_downstream - i, 3) * DRM_HDCP_KSV_LEN;
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_AUX_HDCP_KSV_FIFO,
+ ksv_fifo + i * DRM_HDCP_KSV_LEN,
+ len);
+ if (ret != len) {
+ DRM_ERROR("Read ksv[%d] from DP/AUX failed (%zd)\n", i,
+ ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_read_v_prime_part(struct intel_digital_port *intel_dig_port,
+ int i, u32 *part)
+{
+ ssize_t ret;
+
+ if (i >= DRM_HDCP_V_PRIME_NUM_PARTS)
+ return -EINVAL;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux,
+ DP_AUX_HDCP_V_PRIME(i), part,
+ DRM_HDCP_V_PRIME_PART_LEN);
+ if (ret != DRM_HDCP_V_PRIME_PART_LEN) {
+ DRM_ERROR("Read v'[%d] from DP/AUX failed (%zd)\n", i, ret);
+ return ret >= 0 ? -EIO : ret;
+ }
+ return 0;
+}
+
+static
+int intel_dp_hdcp_toggle_signalling(struct intel_digital_port *intel_dig_port,
+ bool enable)
+{
+ /* Not used for single stream DisplayPort setups */
+ return 0;
+}
+
+static
+bool intel_dp_hdcp_check_link(struct intel_digital_port *intel_dig_port)
+{
+ ssize_t ret;
+ u8 bstatus;
+
+ ret = drm_dp_dpcd_read(&intel_dig_port->dp.aux, DP_AUX_HDCP_BSTATUS,
+ &bstatus, 1);
+ if (ret != 1) {
+ DRM_ERROR("Read bstatus from DP/AUX failed (%zd)\n", ret);
+ return false;
+ }
+
+ return !(bstatus & (DP_BSTATUS_LINK_FAILURE | DP_BSTATUS_REAUTH_REQ));
+}
+
+static
+int intel_dp_hdcp_capable(struct intel_digital_port *intel_dig_port,
+ bool *hdcp_capable)
+{
+ ssize_t ret;
+ u8 bcaps;
+
+ ret = intel_dp_hdcp_read_bcaps(intel_dig_port, &bcaps);
+ if (ret)
+ return ret;
+
+ *hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE;
+ return 0;
+}
+
+static const struct intel_hdcp_shim intel_dp_hdcp_shim = {
+ .write_an_aksv = intel_dp_hdcp_write_an_aksv,
+ .read_bksv = intel_dp_hdcp_read_bksv,
+ .read_bstatus = intel_dp_hdcp_read_bstatus,
+ .repeater_present = intel_dp_hdcp_repeater_present,
+ .read_ri_prime = intel_dp_hdcp_read_ri_prime,
+ .read_ksv_ready = intel_dp_hdcp_read_ksv_ready,
+ .read_ksv_fifo = intel_dp_hdcp_read_ksv_fifo,
+ .read_v_prime_part = intel_dp_hdcp_read_v_prime_part,
+ .toggle_signalling = intel_dp_hdcp_toggle_signalling,
+ .check_link = intel_dp_hdcp_check_link,
+ .hdcp_capable = intel_dp_hdcp_capable,
+};
+
static void intel_edp_panel_vdd_sanitize(struct intel_dp *intel_dp)
{
struct drm_i915_private *dev_priv = to_i915(intel_dp_to_dev(intel_dp));
drm_modeset_acquire_fini(&ctx);
WARN(iret, "Acquiring modeset locks failed with %i\n", iret);
+ /* Short pulse can signify loss of hdcp authentication */
+ intel_hdcp_check_link(intel_dp->attached_connector);
+
if (!handled) {
intel_dp->detect_done = false;
goto put_power;
}
intel_dp_add_properties(intel_dp, connector);
+
+ if (is_hdcp_supported(dev_priv, port) && !intel_dp_is_edp(intel_dp)) {
+ int ret = intel_hdcp_init(intel_connector, &intel_dp_hdcp_shim);
+ if (ret)
+ DRM_DEBUG_KMS("HDCP init failed, skipping.\n");
+ }
/* For G4X desktop chip, PEG_BAND_GAP_DATA 3:0 must first be written
* 0xd. Failure to do so will result in spurious interrupts being
#include <drm/drm_atomic.h>
/**
- * _wait_for - magic (register) wait macro
+ * __wait_for - magic wait macro
*
- * Does the right thing for modeset paths when run under kdgb or similar atomic
- * contexts. Note that it's important that we check the condition again after
- * having timed out, since the timeout could be due to preemption or similar and
- * we've never had a chance to check the condition before the timeout.
+ * Macro to help avoid open coding check/wait/timeout patterns. Note that it's
+ * important that we check the condition again after having timed out, since the
+ * timeout could be due to preemption or similar and we've never had a chance to
+ * check the condition before the timeout.
*/
-#define _wait_for(COND, US, Wmin, Wmax) ({ \
+#define __wait_for(OP, COND, US, Wmin, Wmax) ({ \
unsigned long timeout__ = jiffies + usecs_to_jiffies(US) + 1; \
long wait__ = (Wmin); /* recommended min for usleep is 10 us */ \
int ret__; \
might_sleep(); \
for (;;) { \
bool expired__ = time_after(jiffies, timeout__); \
+ OP; \
if (COND) { \
ret__ = 0; \
break; \
ret__; \
})
-#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000)
+#define _wait_for(COND, US, Wmin, Wmax) __wait_for(, (COND), (US), (Wmin), \
+ (Wmax))
+#define wait_for(COND, MS) _wait_for((COND), (MS) * 1000, 10, 1000)
/* If CONFIG_PREEMPT_COUNT is disabled, in_atomic() always reports false. */
#if defined(CONFIG_DRM_I915_DEBUG) && defined(CONFIG_PREEMPT_COUNT)
struct drm_fb_helper helper;
struct intel_framebuffer *fb;
struct i915_vma *vma;
+ unsigned long vma_flags;
async_cookie_t cookie;
int preferred_bpp;
};
} backlight;
};
+/*
+ * This structure serves as a translation layer between the generic HDCP code
+ * and the bus-specific code. What that means is that HDCP over HDMI differs
+ * from HDCP over DP, so to account for these differences, we need to
+ * communicate with the receiver through this shim.
+ *
+ * For completeness, the 2 buses differ in the following ways:
+ * - DP AUX vs. DDC
+ * HDCP registers on the receiver are set via DP AUX for DP, and
+ * they are set via DDC for HDMI.
+ * - Receiver register offsets
+ * The offsets of the registers are different for DP vs. HDMI
+ * - Receiver register masks/offsets
+ * For instance, the ready bit for the KSV fifo is in a different
+ * place on DP vs HDMI
+ * - Receiver register names
+ * Seriously. In the DP spec, the 16-bit register containing
+ * downstream information is called BINFO, on HDMI it's called
+ * BSTATUS. To confuse matters further, DP has a BSTATUS register
+ * with a completely different definition.
+ * - KSV FIFO
+ * On HDMI, the ksv fifo is read all at once, whereas on DP it must
+ * be read 3 keys at a time
+ * - Aksv output
+ * Since Aksv is hidden in hardware, there's different procedures
+ * to send it over DP AUX vs DDC
+ */
+struct intel_hdcp_shim {
+ /* Outputs the transmitter's An and Aksv values to the receiver. */
+ int (*write_an_aksv)(struct intel_digital_port *intel_dig_port, u8 *an);
+
+ /* Reads the receiver's key selection vector */
+ int (*read_bksv)(struct intel_digital_port *intel_dig_port, u8 *bksv);
+
+ /*
+ * Reads BINFO from DP receivers and BSTATUS from HDMI receivers. The
+ * definitions are the same in the respective specs, but the names are
+ * different. Call it BSTATUS since that's the name the HDMI spec
+ * uses and it was there first.
+ */
+ int (*read_bstatus)(struct intel_digital_port *intel_dig_port,
+ u8 *bstatus);
+
+ /* Determines whether a repeater is present downstream */
+ int (*repeater_present)(struct intel_digital_port *intel_dig_port,
+ bool *repeater_present);
+
+ /* Reads the receiver's Ri' value */
+ int (*read_ri_prime)(struct intel_digital_port *intel_dig_port, u8 *ri);
+
+ /* Determines if the receiver's KSV FIFO is ready for consumption */
+ int (*read_ksv_ready)(struct intel_digital_port *intel_dig_port,
+ bool *ksv_ready);
+
+ /* Reads the ksv fifo for num_downstream devices */
+ int (*read_ksv_fifo)(struct intel_digital_port *intel_dig_port,
+ int num_downstream, u8 *ksv_fifo);
+
+ /* Reads a 32-bit part of V' from the receiver */
+ int (*read_v_prime_part)(struct intel_digital_port *intel_dig_port,
+ int i, u32 *part);
+
+ /* Enables HDCP signalling on the port */
+ int (*toggle_signalling)(struct intel_digital_port *intel_dig_port,
+ bool enable);
+
+ /* Ensures the link is still protected */
+ bool (*check_link)(struct intel_digital_port *intel_dig_port);
+
+ /* Detects panel's hdcp capability. This is optional for HDMI. */
+ int (*hdcp_capable)(struct intel_digital_port *intel_dig_port,
+ bool *hdcp_capable);
+};
+
struct intel_connector {
struct drm_connector base;
/*
/* Work struct to schedule a uevent on link train failure */
struct work_struct modeset_retry_work;
+
+ const struct intel_hdcp_shim *hdcp_shim;
+ struct mutex hdcp_mutex;
+ uint64_t hdcp_value; /* protected by hdcp_mutex */
+ struct delayed_work hdcp_check_work;
+ struct work_struct hdcp_prop_work;
};
struct intel_digital_connector_state {
struct intel_plane_state {
struct drm_plane_state base;
- struct drm_rect clip;
struct i915_vma *vma;
+ unsigned long flags;
+ #define PLANE_HAS_FENCE BIT(0)
struct {
u32 offset;
u32 bxt_signal_levels(struct intel_dp *intel_dp);
uint32_t ddi_signal_levels(struct intel_dp *intel_dp);
u8 intel_ddi_dp_voltage_max(struct intel_encoder *encoder);
+int intel_ddi_toggle_hdcp_signalling(struct intel_encoder *intel_encoder,
+ bool enable);
unsigned int intel_fb_align_height(const struct drm_framebuffer *fb,
int plane, unsigned int height);
void cnl_uninit_cdclk(struct drm_i915_private *dev_priv);
void bxt_init_cdclk(struct drm_i915_private *dev_priv);
void bxt_uninit_cdclk(struct drm_i915_private *dev_priv);
+ void icl_init_cdclk(struct drm_i915_private *dev_priv);
+ void icl_uninit_cdclk(struct drm_i915_private *dev_priv);
void intel_init_cdclk_hooks(struct drm_i915_private *dev_priv);
void intel_update_max_cdclk(struct drm_i915_private *dev_priv);
void intel_update_cdclk(struct drm_i915_private *dev_priv);
intel_encoder_current_mode(struct intel_encoder *encoder);
enum pipe intel_get_pipe_from_connector(struct intel_connector *connector);
- int intel_get_pipe_from_crtc_id(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
+ int intel_get_pipe_from_crtc_id_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
enum transcoder intel_pipe_to_cpu_transcoder(struct drm_i915_private *dev_priv,
enum pipe pipe);
static inline bool
struct intel_load_detect_pipe *old,
struct drm_modeset_acquire_ctx *ctx);
struct i915_vma *
- intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb, unsigned int rotation);
- void intel_unpin_fb_vma(struct i915_vma *vma);
+ intel_pin_and_fence_fb_obj(struct drm_framebuffer *fb,
+ unsigned int rotation,
+ unsigned long *out_flags);
+ void intel_unpin_fb_vma(struct i915_vma *vma, unsigned long flags);
struct drm_framebuffer *
intel_framebuffer_create(struct drm_i915_gem_object *obj,
struct drm_mode_fb_cmd2 *mode_cmd);
}
#endif /* CONFIG_BACKLIGHT_CLASS_DEVICE */
+/* intel_hdcp.c */
+void intel_hdcp_atomic_check(struct drm_connector *connector,
+ struct drm_connector_state *old_state,
+ struct drm_connector_state *new_state);
+int intel_hdcp_init(struct intel_connector *connector,
+ const struct intel_hdcp_shim *hdcp_shim);
+int intel_hdcp_enable(struct intel_connector *connector);
+int intel_hdcp_disable(struct intel_connector *connector);
+int intel_hdcp_check_link(struct intel_connector *connector);
+bool is_hdcp_supported(struct drm_i915_private *dev_priv, enum port port);
/* intel_psr.c */
#define CAN_PSR(dev_priv) (HAS_PSR(dev_priv) && dev_priv->psr.sink_support)
int usecs);
struct intel_plane *intel_sprite_plane_create(struct drm_i915_private *dev_priv,
enum pipe pipe, int plane);
- int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
- struct drm_file *file_priv);
+ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
void intel_pipe_update_start(const struct intel_crtc_state *new_crtc_state);
void intel_pipe_update_end(struct intel_crtc_state *new_crtc_state);
void skl_update_plane(struct intel_plane *plane,
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
int target_clock = mode->clock;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
- return MODE_NO_DBLESCAN;
-
/* XXX: Validate clock range */
if (fixed_mode) {
intel_dvo->attached_connector->panel.fixed_mode;
struct drm_display_mode *adjusted_mode = &pipe_config->base.adjusted_mode;
- /* If we have timings from the BIOS for the panel, put them in
+ /*
+ * 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.
I915_WRITE(dvo_reg, dvo_val);
}
- /**
- * Detect the output connection on our DVO device.
- *
- * Unimplemented.
- */
static enum drm_connector_status
intel_dvo_detect(struct drm_connector *connector, bool force)
{
const struct drm_display_mode *fixed_mode =
to_intel_connector(connector)->panel.fixed_mode;
- /* We should probably have an i2c driver get_modes function for those
+ /*
+ * We should probably have an i2c driver get_modes function for those
* devices which will have a fixed set of modes determined by the chip
* (TV-out, for example), but for now with just TMDS and LVDS,
* that's not the case.
.destroy = intel_dvo_enc_destroy,
};
- /**
+ /*
* Attempts to get a fixed panel timing for LVDS (currently only the i830).
*
* Other chips with DVO LVDS will need to extend this to deal with the LVDS
uint32_t dpll[I915_MAX_PIPES];
enum port port;
- /* Allow the I2C driver info to specify the GPIO to be used in
+ /*
+ * Allow the I2C driver info to specify the GPIO to be used in
* special cases, but otherwise default to what's defined
* in the spec.
*/
else
gpio = GMBUS_PIN_DPB;
- /* Set up the I2C bus necessary for the chip we're probing.
+ /*
+ * Set up the I2C bus necessary for the chip we're probing.
* It appears that everything is on GPIOE except for panels
* on i830 laptops, which are on GPIOB (DVOA).
*/
intel_dvo->dev = *dvo;
- /* GMBUS NAK handling seems to be unstable, hence let the
+ /*
+ * GMBUS NAK handling seems to be unstable, hence let the
* transmitter detection run in bit banging mode for now.
*/
intel_gmbus_force_bit(i2c, true);
- /* ns2501 requires the DVO 2x clock before it will
+ /*
+ * ns2501 requires the DVO 2x clock before it will
* respond to i2c accesses, so make sure we have
* have the clock enabled before we attempt to
* initialize the device.
intel_connector_attach_encoder(intel_connector, intel_encoder);
if (dvo->type == INTEL_DVO_CHIP_LVDS) {
- /* For our LVDS chipsets, we should hopefully be able
+ /*
+ * For our LVDS chipsets, we should hopefully be able
* to dig the fixed panel mode out of the BIOS data.
* However, it's in a different format from the BIOS
* data on chipsets with integrated LVDS (stored in AIM
else
dpfc_ctl |= DPFC_CTL_LIMIT_1X;
- if (params->vma->fence) {
+ if (params->flags & PLANE_HAS_FENCE) {
dpfc_ctl |= DPFC_CTL_FENCE_EN | params->vma->fence->id;
I915_WRITE(DPFC_FENCE_YOFF, params->crtc.fence_y_offset);
} else {
break;
}
- if (params->vma->fence) {
+ if (params->flags & PLANE_HAS_FENCE) {
dpfc_ctl |= DPFC_CTL_FENCE_EN;
if (IS_GEN5(dev_priv))
dpfc_ctl |= params->vma->fence->id;
break;
}
- if (params->vma->fence) {
+ if (params->flags & PLANE_HAS_FENCE) {
dpfc_ctl |= IVB_DPFC_CTL_FENCE_EN;
I915_WRITE(SNB_DPFC_CTL_SA,
SNB_CPU_FENCE_ENABLE |
struct drm_framebuffer *fb = plane_state->base.fb;
cache->vma = NULL;
+ cache->flags = 0;
cache->crtc.mode_flags = crtc_state->base.adjusted_mode.flags;
if (IS_HASWELL(dev_priv) || IS_BROADWELL(dev_priv))
cache->fb.stride = fb->pitches[0];
cache->vma = plane_state->vma;
+ cache->flags = plane_state->flags;
+ if (WARN_ON(cache->flags & PLANE_HAS_FENCE && !cache->vma->fence))
+ cache->flags &= ~PLANE_HAS_FENCE;
}
static bool intel_fbc_can_activate(struct intel_crtc *crtc)
return false;
}
- if ((cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) ||
- (cache->crtc.mode_flags & DRM_MODE_FLAG_DBLSCAN)) {
+ if (cache->crtc.mode_flags & DRM_MODE_FLAG_INTERLACE) {
fbc->no_fbc_reason = "incompatible mode";
return false;
}
* so have no fence associated with it) due to aperture constaints
* at the time of pinning.
*/
- if (!cache->vma->fence) {
+ if (!(cache->flags & PLANE_HAS_FENCE)) {
fbc->no_fbc_reason = "framebuffer not tiled or fenced";
return false;
}
memset(params, 0, sizeof(*params));
params->vma = cache->vma;
+ params->flags = cache->flags;
params->crtc.pipe = crtc->pipe;
params->crtc.i9xx_plane = to_intel_plane(crtc->base.primary)->i9xx_plane;
intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo,
struct intel_sdvo_connector *intel_sdvo_connector);
- /**
+ /*
* Writes the SDVOB or SDVOC with the given value, but always writes both
* SDVOB and SDVOC to work around apparent hardware issues (according to
* comments in the BIOS).
* writing them only once doesn't appear to 'stick'.
* The BIOS does this too. Yay, magic
*/
- for (i = 0; i < 2; i++)
- {
+ for (i = 0; i < 2; i++) {
I915_WRITE(GEN3_SDVOB, bval);
POSTING_READ(GEN3_SDVOB);
+
I915_WRITE(GEN3_SDVOC, cval);
POSTING_READ(GEN3_SDVOC);
}
&targets, sizeof(targets));
}
- /**
+ /*
* Return whether each input is trained.
*
* This function is making an assumption about the layout of the response,
return true;
}
- /* Asks the sdvo controller for the preferred input mode given the output mode.
- * Unfortunately we have to set up the full output mode to do that. */
+ /*
+ * Asks the sdvo controller for the preferred input mode given the output mode.
+ * Unfortunately we have to set up the full output mode to do that.
+ */
static bool
intel_sdvo_get_preferred_input_mode(struct intel_sdvo *intel_sdvo,
const struct drm_display_mode *mode,
unsigned dotclock = pipe_config->port_clock;
struct dpll *clock = &pipe_config->dpll;
- /* SDVO TV has fixed PLL values depend on its clock range,
- this mirrors vbios setting. */
+ /*
+ * SDVO TV has fixed PLL values depend on its clock range,
+ * this mirrors vbios setting.
+ */
if (dotclock >= 100000 && dotclock < 140500) {
clock->p1 = 2;
clock->p2 = 10;
if (HAS_PCH_SPLIT(to_i915(encoder->base.dev)))
pipe_config->has_pch_encoder = true;
- /* We need to construct preferred input timings based on our
+ /*
+ * We need to construct preferred input timings based on our
* output timings. To do that, we have to set the output
* timings, even though this isn't really the right place in
* the sequence to do it. Oh well.
adjusted_mode);
}
- /* Make the CRTC code factor in the SDVO pixel multiplier. The
+ /*
+ * Make the CRTC code factor in the SDVO pixel multiplier. The
* SDVO device will factor out the multiplier during mode_set.
*/
pipe_config->pixel_multiplier =
pipe_config->has_audio = true;
if (intel_sdvo_state->base.broadcast_rgb == INTEL_BROADCAST_RGB_AUTO) {
- /* See CEA-861-E - 5.1 Default Encoding Parameters */
- /* FIXME: This bit is only valid when using TMDS encoding and 8
- * bit per color mode. */
+ /*
+ * See CEA-861-E - 5.1 Default Encoding Parameters
+ *
+ * FIXME: This bit is only valid when using TMDS encoding and 8
+ * bit per color mode.
+ */
if (pipe_config->has_hdmi_sink &&
drm_match_cea_mode(adjusted_mode) > 1)
pipe_config->limited_color_range = true;
intel_sdvo_update_props(intel_sdvo, sdvo_state);
- /* First, set the input mapping for the first input to our controlled
+ /*
+ * First, set the input mapping for the first input to our controlled
* output. This is only correct if we're a single-input device, in
* which case the first input is the output from the appropriate SDVO
* channel on the motherboard. In a two-input device, the first input
ret = intel_sdvo_get_input_timing(intel_sdvo, &dtd);
if (!ret) {
- /* Some sdvo encoders are not spec compliant and don't
- * implement the mandatory get_timings function. */
+ /*
+ * Some sdvo encoders are not spec compliant and don't
+ * implement the mandatory get_timings function.
+ */
DRM_DEBUG_DRIVER("failed to retrieve SDVO DTD\n");
pipe_config->quirks |= PIPE_CONFIG_QUIRK_MODE_SYNC_FLAGS;
} else {
intel_wait_for_vblank(dev_priv, intel_crtc->pipe);
success = intel_sdvo_get_trained_inputs(intel_sdvo, &input1, &input2);
- /* Warn if the device reported failure to sync.
+ /*
+ * Warn if the device reported failure to sync.
+ *
* A lot of SDVO devices fail to notify of sync, but it's
* a given it the status is a success, we succeeded.
*/
struct intel_sdvo *intel_sdvo = intel_attached_sdvo(connector);
int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
- return MODE_NO_DBLESCAN;
-
if (intel_sdvo->pixel_clock_min > mode->clock)
return MODE_CLOCK_LOW;
if (!I915_HAS_HOTPLUG(dev_priv))
return 0;
- /* HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise
- * on the line. */
+ /*
+ * HW Erratum: SDVO Hotplug is broken on all i945G chips, there's noise
+ * on the line.
+ */
if (IS_I945G(dev_priv) || IS_I945GM(dev_priv))
return 0;
DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
connector->base.id, connector->name);
- /* Read the list of supported input resolutions for the selected TV
+ /*
+ * Read the list of supported input resolutions for the selected TV
* format.
*/
format_map = 1 << conn_state->tv.mode;
uint16_t mask = 0;
unsigned int num_bits;
- /* Make a mask of outputs less than or equal to our own priority in the
+ /*
+ * Make a mask of outputs less than or equal to our own priority in the
* list.
*/
switch (sdvo->controlled_output) {
sdvo->ddc_bus = 1 << num_bits;
}
- /**
+ /*
* Choose the appropriate DDC bus for control bus switch command for this
* SDVO output based on the controlled output.
*
sdvo->i2c = intel_gmbus_get_adapter(dev_priv, pin);
- /* With gmbus we should be able to drive sdvo i2c at 2MHz, but somehow
+ /*
+ * With gmbus we should be able to drive sdvo i2c at 2MHz, but somehow
* our code totally fails once we start using gmbus. Hence fall back to
- * bit banging for now. */
+ * bit banging for now.
+ */
intel_gmbus_force_bit(sdvo->i2c, true);
}
if (my_mapping->slave_addr)
return my_mapping->slave_addr;
- /* If the BIOS only described a different SDVO device, use the
+ /*
+ * If the BIOS only described a different SDVO device, use the
* address that it isn't using.
*/
if (other_mapping->slave_addr) {
return 0x70;
}
- /* No SDVO device info is found for another DVO port,
+ /*
+ * No SDVO device info is found for another DVO port,
* so use mapping assumption we had before BIOS parsing.
*/
if (sdvo->port == PORT_B)
if (intel_sdvo_get_hotplug_support(intel_sdvo) &
intel_sdvo_connector->output_flag) {
intel_sdvo->hotplug_active |= intel_sdvo_connector->output_flag;
- /* Some SDVO devices have one-shot hotplug interrupts.
+ /*
+ * Some SDVO devices have one-shot hotplug interrupts.
* Ensure that they get re-enabled when an interrupt happens.
*/
intel_encoder->hot_plug = intel_sdvo_enable_hotplug;
to_intel_sdvo_connector_state(conn_state);
uint16_t response, data_value[2];
- /* when horizontal overscan is supported, Add the left/right property */
+ /* when horizontal overscan is supported, Add the left/right property */
if (enhancements.overscan_h) {
if (!intel_sdvo_get_value(intel_sdvo,
SDVO_CMD_GET_MAX_OVERSCAN_H,
goto err_output;
}
- /* Only enable the hotplug irq if we need it, to work around noisy
+ /*
+ * Only enable the hotplug irq if we need it, to work around noisy
* hotplug lines.
*/
if (intel_sdvo->hotplug_active) {
uint32_t src_x, src_y, src_w, src_h;
struct drm_rect *src = &state->base.src;
struct drm_rect *dst = &state->base.dst;
- const struct drm_rect *clip = &state->clip;
+ struct drm_rect clip = {};
int max_stride = INTEL_GEN(dev_priv) >= 9 ? 32768 : 16384;
int hscale, vscale;
int max_scale, min_scale;
vscale = drm_rect_calc_vscale_relaxed(src, dst, min_scale, max_scale);
BUG_ON(vscale < 0);
- state->base.visible = drm_rect_clip_scaled(src, dst, clip, hscale, vscale);
+ if (crtc_state->base.enable)
+ drm_mode_get_hv_timing(&crtc_state->base.mode,
+ &clip.x2, &clip.y2);
+
+ state->base.visible = drm_rect_clip_scaled(src, dst, &clip, hscale, vscale);
crtc_x = dst->x1;
crtc_y = dst->y1;
return 0;
}
- int intel_sprite_set_colorkey(struct drm_device *dev, void *data,
- struct drm_file *file_priv)
+ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
{
struct drm_i915_private *dev_priv = to_i915(dev);
struct drm_intel_sprite_colorkey *set = data;
/* ignore the pointless "none" flag */
set->flags &= ~I915_SET_COLORKEY_NONE;
+ if (set->flags & ~(I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
+ return -EINVAL;
+
/* Make sure we don't try to enable both src & dest simultaneously */
if ((set->flags & (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE)) == (I915_SET_COLORKEY_DESTINATION | I915_SET_COLORKEY_SOURCE))
return -EINVAL;
engine->name);
I915_WRITE_FW(RING_HEAD(base), I915_READ_FW(RING_TAIL(base)));
+ POSTING_READ_FW(RING_HEAD(base)); /* paranoia */
I915_WRITE_FW(RING_HEAD(base), 0);
I915_WRITE_FW(RING_TAIL(base), 0);
+ POSTING_READ_FW(RING_TAIL(base));
/* The ring must be empty before it is disabled */
I915_WRITE_FW(RING_CTL(base), 0);
gen3_stop_engine(engine);
}
- static bool i915_reset_complete(struct pci_dev *pdev)
+ static bool i915_in_reset(struct pci_dev *pdev)
{
u8 gdrst;
pci_read_config_byte(pdev, I915_GDRST, &gdrst);
- return (gdrst & GRDOM_RESET_STATUS) == 0;
+ return gdrst & GRDOM_RESET_STATUS;
}
static int i915_do_reset(struct drm_i915_private *dev_priv, unsigned engine_mask)
{
struct pci_dev *pdev = dev_priv->drm.pdev;
+ int err;
- /* assert reset for at least 20 usec */
+ /* Assert reset for at least 20 usec, and wait for acknowledgement. */
pci_write_config_byte(pdev, I915_GDRST, GRDOM_RESET_ENABLE);
usleep_range(50, 200);
+ err = wait_for(i915_in_reset(pdev), 500);
+
+ /* Clear the reset request. */
pci_write_config_byte(pdev, I915_GDRST, 0);
+ usleep_range(50, 200);
+ if (!err)
+ err = wait_for(!i915_in_reset(pdev), 500);
- return wait_for(i915_reset_complete(pdev), 500);
+ return err;
}
static bool g4x_reset_complete(struct pci_dev *pdev)
}
/**
- * intel_wait_for_register - wait until register matches expected state
+ * __intel_wait_for_register - wait until register matches expected state
* @dev_priv: the i915 device
* @reg: the register to read
* @mask: mask to apply to register value
* @value: expected value
- * @timeout_ms: timeout in millisecond
+ * @fast_timeout_us: fast timeout in microsecond for atomic/tight wait
+ * @slow_timeout_ms: slow timeout in millisecond
+ * @out_value: optional placeholder to hold registry value
*
* This routine waits until the target register @reg contains the expected
* @value after applying the @mask, i.e. it waits until ::
*
* Returns 0 if the register matches the desired condition, or -ETIMEOUT.
*/
-int intel_wait_for_register(struct drm_i915_private *dev_priv,
+int __intel_wait_for_register(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
u32 value,
- unsigned int timeout_ms)
+ unsigned int fast_timeout_us,
+ unsigned int slow_timeout_ms,
+ u32 *out_value)
{
unsigned fw =
intel_uncore_forcewake_for_reg(dev_priv, reg, FW_REG_READ);
+ u32 reg_value;
int ret;
might_sleep();
ret = __intel_wait_for_register_fw(dev_priv,
reg, mask, value,
- 2, 0, NULL);
+ fast_timeout_us, 0, ®_value);
intel_uncore_forcewake_put__locked(dev_priv, fw);
spin_unlock_irq(&dev_priv->uncore.lock);
if (ret)
- ret = wait_for((I915_READ_NOTRACE(reg) & mask) == value,
- timeout_ms);
+ ret = __wait_for(reg_value = I915_READ_NOTRACE(reg),
+ (reg_value & mask) == value,
+ slow_timeout_ms * 1000, 10, 1000);
+
+ if (out_value)
+ *out_value = reg_value;
return ret;
}
if (!i915_modparams.reset)
return NULL;
- if (INTEL_INFO(dev_priv)->gen >= 8)
+ if (INTEL_GEN(dev_priv) >= 8)
return gen8_reset_engines;
- else if (INTEL_INFO(dev_priv)->gen >= 6)
+ else if (INTEL_GEN(dev_priv) >= 6)
return gen6_reset_engines;
else if (IS_GEN5(dev_priv))
return ironlake_do_reset;
return g4x_do_reset;
else if (IS_G33(dev_priv) || IS_PINEVIEW(dev_priv))
return g33_do_reset;
- else if (INTEL_INFO(dev_priv)->gen >= 3)
+ else if (INTEL_GEN(dev_priv) >= 3)
return i915_do_reset;
else
return NULL;
void intel_uncore_forcewake_user_get(struct drm_i915_private *dev_priv);
void intel_uncore_forcewake_user_put(struct drm_i915_private *dev_priv);
+int __intel_wait_for_register(struct drm_i915_private *dev_priv,
+ i915_reg_t reg,
+ u32 mask,
+ u32 value,
+ unsigned int fast_timeout_us,
+ unsigned int slow_timeout_ms,
+ u32 *out_value);
+static inline
int intel_wait_for_register(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
u32 value,
- unsigned int timeout_ms);
+ unsigned int timeout_ms)
+{
+ return __intel_wait_for_register(dev_priv, reg, mask, value, 2,
+ timeout_ms, NULL);
+}
int __intel_wait_for_register_fw(struct drm_i915_private *dev_priv,
i915_reg_t reg,
u32 mask,
2, timeout_ms, NULL);
}
+ #define raw_reg_read(base, reg) \
+ readl(base + i915_mmio_reg_offset(reg))
+ #define raw_reg_write(base, reg, value) \
+ writel(value, base + i915_mmio_reg_offset(reg))
+
#endif /* !__INTEL_UNCORE_H__ */
* &drm_pending_vblank_event pointer to clean up private events.
*/
struct drm_pending_vblank_event *event;
+
+ /**
+ * @abort_completion:
+ *
+ * A flag that's set after drm_atomic_helper_setup_commit takes a second
+ * reference for the completion of $drm_crtc_state.event. It's used by
+ * the free code to remove the second reference if commit fails.
+ */
+ bool abort_completion;
};
struct __drm_planes_state {
struct drm_crtc *ptr;
struct drm_crtc_state *state, *old_state, *new_state;
s32 __user *out_fence_ptr;
- unsigned last_vblank_count;
+ u64 last_vblank_count;
};
struct __drm_connnectors_state {
* @event: Actual event which will be sent to userspace.
*/
union {
+ /**
+ * @event.base: DRM event base class.
+ */
struct drm_event base;
+
+ /**
+ * @event.vbl:
+ *
+ * Event payload for vblank events, requested through
+ * either the MODE_PAGE_FLIP or MODE_ATOMIC IOCTL. Also
+ * generated by the legacy WAIT_VBLANK IOCTL, but new userspace
+ * should use MODE_QUEUE_SEQUENCE and &event.seq instead.
+ */
struct drm_event_vblank vbl;
+
+ /**
+ * @event.seq: Event payload for the MODE_QUEUEU_SEQUENCE IOCTL.
+ */
struct drm_event_crtc_sequence seq;
} event;
};
void drm_crtc_vblank_off(struct drm_crtc *crtc);
void drm_crtc_vblank_reset(struct drm_crtc *crtc);
void drm_crtc_vblank_on(struct drm_crtc *crtc);
- u32 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
+ u64 drm_crtc_accurate_vblank_count(struct drm_crtc *crtc);
+ void drm_vblank_restore(struct drm_device *dev, unsigned int pipe);
+ void drm_crtc_vblank_restore(struct drm_crtc *crtc);
bool drm_calc_vbltimestamp_from_scanoutpos(struct drm_device *dev,
unsigned int pipe, int *max_error,