static int intelfb_create(struct intel_fbdev *ifbdev, struct drm_fb_helper_surface_size *sizes) { struct drm_device *dev = ifbdev->helper.dev; struct fb_info *info; struct drm_framebuffer *fb; struct drm_mode_fb_cmd mode_cmd; struct drm_i915_gem_object *obj; struct device *device = &dev->pdev->dev; int size, ret; /* we don't do packed 24bpp */ if (sizes->surface_bpp == 24) sizes->surface_bpp = 32; mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; mode_cmd.bpp = sizes->surface_bpp; mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 7) / 8), 64); mode_cmd.depth = sizes->surface_depth; size = mode_cmd.pitch * mode_cmd.height; size = ALIGN(size, PAGE_SIZE); obj = i915_gem_alloc_object(dev, size); if (!obj) { DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; goto out; } mutex_lock(&dev->struct_mutex); /* Flush everything out, we'll be doing GTT only from now on */ ret = intel_pin_and_fence_fb_obj(dev, obj, false); if (ret) { DRM_ERROR("failed to pin fb: %d\n", ret); goto out_unref; } info = framebuffer_alloc(0, device); if (!info) { ret = -ENOMEM; goto out_unpin; } info->par = ifbdev; ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj); if (ret) goto out_unpin; fb = &ifbdev->ifb.base; ifbdev->helper.fb = fb; ifbdev->helper.fbdev = info; strcpy(info->fix.id, "inteldrmfb"); info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &intelfb_ops; ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret) { ret = -ENOMEM; goto out_unpin; } /* setup aperture base/size for vesafb takeover */ info->aperture_base = dev->mode_config.fb_base; if (!IS_GEN2(dev)) info->aperture_size = pci_resource_len(dev->pdev, 2); else info->aperture_size = pci_resource_len(dev->pdev, 0); info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; info->screen_base = ioremap_wc(dev->agp->base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; goto out_unpin; } info->screen_size = size; // memset(info->screen_base, 0, size); drm_fb_helper_fill_fix(info, fb->pitch, fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); info->pixmap.size = 64*1024; info->pixmap.buf_align = 8; info->pixmap.access_align = 32; info->pixmap.flags = FB_PIXMAP_SYSTEM; info->pixmap.scan_align = 1; DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n", fb->width, fb->height, obj->gtt_offset, obj); mutex_unlock(&dev->struct_mutex); vga_switcheroo_client_fb_set(dev->pdev, info); return 0; out_unpin: i915_gem_object_unpin(obj); out_unref: drm_gem_object_unreference(&obj->base); mutex_unlock(&dev->struct_mutex); out: return ret; }
void hsw_fdi_link_train(struct drm_crtc *crtc) { struct drm_device *dev = crtc->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(crtc); u32 temp, i, rx_ctl_val; /* Set the FDI_RX_MISC pwrdn lanes and the 2 workarounds listed at the * mode set "sequence for CRT port" document: * - TP1 to TP2 time with the default value * - FDI delay to 90h * * WaFDIAutoLinkSetTimingOverrride:hsw */ I915_WRITE(_FDI_RXA_MISC, FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2) | FDI_RX_TP1_TO_TP2_48 | FDI_RX_FDI_DELAY_90); /* Enable the PCH Receiver FDI PLL */ rx_ctl_val = dev_priv->fdi_rx_config | FDI_RX_ENHANCE_FRAME_ENABLE | FDI_RX_PLL_ENABLE | FDI_DP_PORT_WIDTH(intel_crtc->config.fdi_lanes); I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); udelay(220); /* Switch from Rawclk to PCDclk */ rx_ctl_val |= FDI_PCDCLK; I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); /* Configure Port Clock Select */ I915_WRITE(PORT_CLK_SEL(PORT_E), intel_crtc->ddi_pll_sel); /* Start the training iterating through available voltages and emphasis, * testing each value twice. */ for (i = 0; i < ARRAY_SIZE(hsw_ddi_buf_ctl_values) * 2; i++) { /* Configure DP_TP_CTL with auto-training */ I915_WRITE(DP_TP_CTL(PORT_E), DP_TP_CTL_FDI_AUTOTRAIN | DP_TP_CTL_ENHANCED_FRAME_ENABLE | DP_TP_CTL_LINK_TRAIN_PAT1 | DP_TP_CTL_ENABLE); /* Configure and enable DDI_BUF_CTL for DDI E with next voltage. * DDI E does not support port reversal, the functionality is * achieved on the PCH side in FDI_RX_CTL, so no need to set the * port reversal bit */ I915_WRITE(DDI_BUF_CTL(PORT_E), DDI_BUF_CTL_ENABLE | ((intel_crtc->config.fdi_lanes - 1) << 1) | hsw_ddi_buf_ctl_values[i / 2]); POSTING_READ(DDI_BUF_CTL(PORT_E)); udelay(600); /* Program PCH FDI Receiver TU */ I915_WRITE(_FDI_RXA_TUSIZE1, TU_SIZE(64)); /* Enable PCH FDI Receiver with auto-training */ rx_ctl_val |= FDI_RX_ENABLE | FDI_LINK_TRAIN_AUTO; I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); /* Wait for FDI receiver lane calibration */ udelay(30); /* Unset FDI_RX_MISC pwrdn lanes */ temp = I915_READ(_FDI_RXA_MISC); temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); I915_WRITE(_FDI_RXA_MISC, temp); POSTING_READ(_FDI_RXA_MISC); /* Wait for FDI auto training time */ udelay(5); temp = I915_READ(DP_TP_STATUS(PORT_E)); if (temp & DP_TP_STATUS_AUTOTRAIN_DONE) { DRM_DEBUG_KMS("FDI link training done on step %d\n", i); /* Enable normal pixel sending for FDI */ I915_WRITE(DP_TP_CTL(PORT_E), DP_TP_CTL_FDI_AUTOTRAIN | DP_TP_CTL_LINK_TRAIN_NORMAL | DP_TP_CTL_ENHANCED_FRAME_ENABLE | DP_TP_CTL_ENABLE); return; } temp = I915_READ(DDI_BUF_CTL(PORT_E)); temp &= ~DDI_BUF_CTL_ENABLE; I915_WRITE(DDI_BUF_CTL(PORT_E), temp); POSTING_READ(DDI_BUF_CTL(PORT_E)); /* Disable DP_TP_CTL and FDI_RX_CTL and retry */ temp = I915_READ(DP_TP_CTL(PORT_E)); temp &= ~(DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_MASK); temp |= DP_TP_CTL_LINK_TRAIN_PAT1; I915_WRITE(DP_TP_CTL(PORT_E), temp); POSTING_READ(DP_TP_CTL(PORT_E)); intel_wait_ddi_buf_idle(dev_priv, PORT_E); rx_ctl_val &= ~FDI_RX_ENABLE; I915_WRITE(_FDI_RXA_CTL, rx_ctl_val); POSTING_READ(_FDI_RXA_CTL); /* Reset FDI_RX_MISC pwrdn lanes */ temp = I915_READ(_FDI_RXA_MISC); temp &= ~(FDI_RX_PWRDN_LANE1_MASK | FDI_RX_PWRDN_LANE0_MASK); temp |= FDI_RX_PWRDN_LANE1_VAL(2) | FDI_RX_PWRDN_LANE0_VAL(2); I915_WRITE(_FDI_RXA_MISC, temp); POSTING_READ(_FDI_RXA_MISC); } DRM_ERROR("FDI link training failed!\n"); }
/* create encoder */ struct drm_encoder *xilinx_drm_encoder_create(struct drm_device *drm) { struct xilinx_drm_encoder *encoder; struct device_node *sub_node; struct i2c_driver *i2c_driver; struct drm_i2c_encoder_driver *drm_i2c_driver; struct device_driver *device_driver; struct platform_driver *platform_driver; struct drm_platform_encoder_driver *drm_platform_driver; int ret = 0; encoder = devm_kzalloc(drm->dev, sizeof(*encoder), GFP_KERNEL); if (!encoder) return ERR_PTR(-ENOMEM); encoder->dpms = DRM_MODE_DPMS_OFF; /* initialize encoder */ encoder->slave.base.possible_crtcs = 1; ret = drm_encoder_init(drm, &encoder->slave.base, &xilinx_drm_encoder_funcs, DRM_MODE_ENCODER_TMDS); if (ret) { DRM_ERROR("failed to initialize drm encoder\n"); return ERR_PTR(ret); } drm_encoder_helper_add(&encoder->slave.base, &xilinx_drm_encoder_helper_funcs); /* get slave encoder */ sub_node = of_parse_phandle(drm->dev->of_node, "xlnx,encoder-slave", 0); if (!sub_node) { DRM_ERROR("failed to get an encoder slave node\n"); return ERR_PTR(-ENODEV); } /* initialize slave encoder */ encoder->i2c_slv = of_find_i2c_device_by_node(sub_node); if (encoder->i2c_slv) { i2c_driver = to_i2c_driver(encoder->i2c_slv->dev.driver); drm_i2c_driver = to_drm_i2c_encoder_driver(i2c_driver); if (!drm_i2c_driver) { DRM_ERROR("failed to initialize i2c slave\n"); ret = -EPROBE_DEFER; goto err_out; } ret = drm_i2c_driver->encoder_init(encoder->i2c_slv, drm, &encoder->slave); } else { encoder->platform_slv = of_find_device_by_node(sub_node); if (!encoder->platform_slv) { DRM_DEBUG_KMS("failed to get an encoder slv\n"); return ERR_PTR(-EPROBE_DEFER); } device_driver = encoder->platform_slv->dev.driver; if (!device_driver) { DRM_DEBUG_KMS("failed to get device driver\n"); return ERR_PTR(-EPROBE_DEFER); } platform_driver = to_platform_driver(device_driver); drm_platform_driver = to_drm_platform_encoder_driver(platform_driver); if (!drm_platform_driver) { DRM_ERROR("failed to initialize platform slave\n"); ret = -EPROBE_DEFER; goto err_out; } ret = drm_platform_driver->encoder_init(encoder->platform_slv, drm, &encoder->slave); } of_node_put(sub_node); if (ret) { DRM_ERROR("failed to initialize encoder slave\n"); goto err_out; } if (!encoder->slave.slave_funcs) { DRM_ERROR("there's no encoder slave function\n"); ret = -ENODEV; goto err_out; } return &encoder->slave.base; err_out: return ERR_PTR(ret); }
void intel_dsi_init(struct drm_device *dev) { struct intel_dsi *intel_dsi; struct intel_encoder *intel_encoder; struct drm_encoder *encoder; struct intel_connector *intel_connector; struct drm_connector *connector; struct drm_display_mode *fixed_mode = NULL; struct drm_i915_private *dev_priv = dev->dev_private; const struct intel_dsi_device *dsi; unsigned int i; DRM_DEBUG_KMS("\n"); /* There is no detection method for MIPI so rely on VBT */ if (!dev_priv->vbt.has_mipi) return; if (IS_VALLEYVIEW(dev)) { dev_priv->mipi_mmio_base = VLV_MIPI_BASE; } else { DRM_ERROR("Unsupported Mipi device to reg base"); return; } intel_dsi = kzalloc(sizeof(*intel_dsi), GFP_KERNEL); if (!intel_dsi) return; intel_connector = kzalloc(sizeof(*intel_connector), GFP_KERNEL); if (!intel_connector) { kfree(intel_dsi); return; } intel_encoder = &intel_dsi->base; encoder = &intel_encoder->base; intel_dsi->attached_connector = intel_connector; connector = &intel_connector->base; drm_encoder_init(dev, encoder, &intel_dsi_funcs, DRM_MODE_ENCODER_DSI); /* XXX: very likely not all of these are needed */ intel_encoder->hot_plug = intel_dsi_hot_plug; intel_encoder->compute_config = intel_dsi_compute_config; intel_encoder->pre_pll_enable = intel_dsi_pre_pll_enable; intel_encoder->pre_enable = intel_dsi_pre_enable; intel_encoder->enable = intel_dsi_enable_nop; intel_encoder->disable = intel_dsi_pre_disable; intel_encoder->post_disable = intel_dsi_post_disable; intel_encoder->get_hw_state = intel_dsi_get_hw_state; intel_encoder->get_config = intel_dsi_get_config; intel_connector->get_hw_state = intel_connector_get_hw_state; intel_connector->unregister = intel_connector_unregister; for (i = 0; i < ARRAY_SIZE(intel_dsi_devices); i++) { dsi = &intel_dsi_devices[i]; intel_dsi->dev = *dsi; if (dsi->dev_ops->init(&intel_dsi->dev)) break; } if (i == ARRAY_SIZE(intel_dsi_devices)) { DRM_DEBUG_KMS("no device found\n"); goto err; } intel_encoder->type = INTEL_OUTPUT_DSI; intel_encoder->crtc_mask = (1 << 0); /* XXX */ intel_encoder->cloneable = 0; drm_connector_init(dev, connector, &intel_dsi_connector_funcs, DRM_MODE_CONNECTOR_DSI); drm_connector_helper_add(connector, &intel_dsi_connector_helper_funcs); connector->display_info.subpixel_order = SubPixelHorizontalRGB; /*XXX*/ connector->interlace_allowed = false; connector->doublescan_allowed = false; intel_connector_attach_encoder(intel_connector, intel_encoder); drm_connector_register(connector); fixed_mode = dsi->dev_ops->get_modes(&intel_dsi->dev); if (!fixed_mode) { DRM_DEBUG_KMS("no fixed mode\n"); goto err; } fixed_mode->type |= DRM_MODE_TYPE_PREFERRED; intel_panel_init(&intel_connector->panel, fixed_mode, NULL); return; err: drm_encoder_cleanup(&intel_encoder->base); kfree(intel_dsi); kfree(intel_connector); }
static int intelfb_create(struct intel_fbdev *ifbdev, struct drm_fb_helper_surface_size *sizes) { struct drm_device *dev = ifbdev->helper.dev; #if 0 struct drm_i915_private *dev_priv = dev->dev_private; #endif struct fb_info *info; struct drm_framebuffer *fb; struct drm_mode_fb_cmd2 mode_cmd; struct drm_i915_gem_object *obj; int size, ret; /* we don't do packed 24bpp */ if (sizes->surface_bpp == 24) sizes->surface_bpp = 32; mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; mode_cmd.pitches[0] = roundup2(mode_cmd.width * ((sizes->surface_bpp + 7) / 8), 64); mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); size = mode_cmd.pitches[0] * mode_cmd.height; size = roundup2(size, PAGE_SIZE); obj = i915_gem_alloc_object(dev, size); if (!obj) { DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; goto out; } DRM_LOCK(dev); /* Flush everything out, we'll be doing GTT only from now on */ ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); if (ret) { DRM_ERROR("failed to pin fb: %d\n", ret); goto out_unref; } info = framebuffer_alloc(); if (!info) { ret = -ENOMEM; goto out_unpin; } #if 0 info->par = ifbdev; #else info->fb_size = size; info->fb_bpp = sizes->surface_bpp; info->fb_pbase = dev->agp->base + obj->gtt_offset; info->fb_vbase = (vm_offset_t)pmap_mapdev_attr(info->fb_pbase, size, PAT_WRITE_COMBINING); #endif ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj); if (ret) goto out_unpin; fb = &ifbdev->ifb.base; ifbdev->helper.fb = fb; ifbdev->helper.fbdev = info; #if 0 strcpy(info->fix.id, "inteldrmfb"); info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &intelfb_ops; ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret) { ret = -ENOMEM; goto out_unpin; } /* setup aperture base/size for vesafb takeover */ info->apertures = alloc_apertures(1); if (!info->apertures) { ret = -ENOMEM; goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; info->apertures->ranges[0].size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; info->screen_base = ioremap_wc(dev->agp->base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; goto out_unpin; } info->screen_size = size; // memset(info->screen_base, 0, size); #endif drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ DRM_DEBUG_KMS("allocated %dx%d (s %dbits) fb: 0x%08x, bo %p\n", fb->width, fb->height, fb->depth, obj->gtt_offset, obj); DRM_UNLOCK(dev); #if 1 KIB_NOTYET(); #else vga_switcheroo_client_fb_set(dev->pdev, info); #endif return 0; out_unpin: i915_gem_object_unpin(obj); out_unref: drm_gem_object_unreference(&obj->base); DRM_UNLOCK(dev); out: return ret; }
static int radeon_process_i2c_ch(struct radeon_i2c_chan *chan, u8 slave_addr, u8 flags, u8 *buf, u8 num) { struct drm_device *dev = chan->dev; struct radeon_device *rdev = dev->dev_private; PROCESS_I2C_CHANNEL_TRANSACTION_PS_ALLOCATION args; int index = GetIndexIntoMasterTable(COMMAND, ProcessI2cChannelTransaction); unsigned char *base; u16 out = cpu_to_le16(0); int r = 0; memset(&args, 0, sizeof(args)); lockmgr(&chan->mutex, LK_EXCLUSIVE); lockmgr(&rdev->mode_info.atom_context->scratch_mutex, LK_EXCLUSIVE); base = (unsigned char *)rdev->mode_info.atom_context->scratch; if (flags & HW_I2C_WRITE) { if (num > ATOM_MAX_HW_I2C_WRITE) { DRM_ERROR("hw i2c: tried to write too many bytes (%d vs 3)\n", num); r = -EINVAL; goto done; } if (buf == NULL) args.ucRegIndex = 0; else args.ucRegIndex = buf[0]; if (num) num--; if (num) memcpy(&out, &buf[1], num); args.lpI2CDataOut = cpu_to_le16(out); } else { if (num > ATOM_MAX_HW_I2C_READ) { DRM_ERROR("hw i2c: tried to read too many bytes (%d vs 255)\n", num); r = -EINVAL; goto done; } args.ucRegIndex = 0; args.lpI2CDataOut = 0; } args.ucFlag = flags; args.ucI2CSpeed = TARGET_HW_I2C_CLOCK; args.ucTransBytes = num; args.ucSlaveAddr = slave_addr << 1; args.ucLineNumber = chan->rec.i2c_id; atom_execute_table_scratch_unlocked(rdev->mode_info.atom_context, index, (uint32_t *)&args); /* error */ if (args.ucStatus != HW_ASSISTED_I2C_STATUS_SUCCESS) { DRM_DEBUG_KMS("hw_i2c error\n"); r = -EIO; goto done; } if (!(flags & HW_I2C_WRITE)) radeon_atom_copy_swap(buf, base, num, false); done: lockmgr(&rdev->mode_info.atom_context->scratch_mutex, LK_RELEASE); lockmgr(&chan->mutex, LK_RELEASE); return r; }
/** * radeon_info_ioctl - answer a device specific request. * * @rdev: radeon device pointer * @data: request object * @filp: drm filp * * This function is used to pass device specific parameters to the userspace * drivers. Examples include: pci device id, pipeline parms, tiling params, * etc. (all asics). * Returns 0 on success, -EINVAL on failure. */ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct radeon_device *rdev = dev->dev_private; struct drm_radeon_info *info = data; struct radeon_mode_info *minfo = &rdev->mode_info; uint32_t *value, value_tmp, *value_ptr, value_size; uint64_t value64; struct drm_crtc *crtc; int i, found; value_ptr = (uint32_t *)((unsigned long)info->value); value = &value_tmp; value_size = sizeof(uint32_t); switch (info->request) { case RADEON_INFO_DEVICE_ID: *value = dev->pdev->device; break; case RADEON_INFO_NUM_GB_PIPES: *value = rdev->num_gb_pipes; break; case RADEON_INFO_NUM_Z_PIPES: *value = rdev->num_z_pipes; break; case RADEON_INFO_ACCEL_WORKING: /* xf86-video-ati 6.13.0 relies on this being false for evergreen */ if ((rdev->family >= CHIP_CEDAR) && (rdev->family <= CHIP_HEMLOCK)) *value = false; else *value = rdev->accel_working; break; case RADEON_INFO_CRTC_FROM_ID: if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); return -EFAULT; } for (i = 0, found = 0; i < rdev->num_crtc; i++) { crtc = (struct drm_crtc *)minfo->crtcs[i]; if (crtc && crtc->base.id == *value) { struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc); *value = radeon_crtc->crtc_id; found = 1; break; } } if (!found) { DRM_DEBUG_KMS("unknown crtc id %d\n", *value); return -EINVAL; } break; case RADEON_INFO_ACCEL_WORKING2: *value = rdev->accel_working; break; case RADEON_INFO_TILING_CONFIG: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.tile_config; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.tile_config; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.tile_config; else if (rdev->family >= CHIP_CEDAR) *value = rdev->config.evergreen.tile_config; else if (rdev->family >= CHIP_RV770) *value = rdev->config.rv770.tile_config; else if (rdev->family >= CHIP_R600) *value = rdev->config.r600.tile_config; else { DRM_DEBUG_KMS("tiling config is r6xx+ only!\n"); return -EINVAL; } break; case RADEON_INFO_WANT_HYPERZ: /* The "value" here is both an input and output parameter. * If the input value is 1, filp requests hyper-z access. * If the input value is 0, filp revokes its hyper-z access. * * When returning, the value is 1 if filp owns hyper-z access, * 0 otherwise. */ if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); return -EFAULT; } if (*value >= 2) { DRM_DEBUG_KMS("WANT_HYPERZ: invalid value %d\n", *value); return -EINVAL; } radeon_set_filp_rights(dev, &rdev->hyperz_filp, filp, value); break; case RADEON_INFO_WANT_CMASK: /* The same logic as Hyper-Z. */ if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); return -EFAULT; } if (*value >= 2) { DRM_DEBUG_KMS("WANT_CMASK: invalid value %d\n", *value); return -EINVAL; } radeon_set_filp_rights(dev, &rdev->cmask_filp, filp, value); break; case RADEON_INFO_CLOCK_CRYSTAL_FREQ: /* return clock value in KHz */ if (rdev->asic->get_xclk) *value = radeon_get_xclk(rdev) * 10; else *value = rdev->clock.spll.reference_freq * 10; break; case RADEON_INFO_NUM_BACKENDS: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.max_backends_per_se * rdev->config.cik.max_shader_engines; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_backends_per_se * rdev->config.si.max_shader_engines; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_backends_per_se * rdev->config.cayman.max_shader_engines; else if (rdev->family >= CHIP_CEDAR) *value = rdev->config.evergreen.max_backends; else if (rdev->family >= CHIP_RV770) *value = rdev->config.rv770.max_backends; else if (rdev->family >= CHIP_R600) *value = rdev->config.r600.max_backends; else { return -EINVAL; } break; case RADEON_INFO_NUM_TILE_PIPES: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.max_tile_pipes; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_tile_pipes; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_tile_pipes; else if (rdev->family >= CHIP_CEDAR) *value = rdev->config.evergreen.max_tile_pipes; else if (rdev->family >= CHIP_RV770) *value = rdev->config.rv770.max_tile_pipes; else if (rdev->family >= CHIP_R600) *value = rdev->config.r600.max_tile_pipes; else { return -EINVAL; } break; case RADEON_INFO_FUSION_GART_WORKING: *value = 1; break; case RADEON_INFO_BACKEND_MAP: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.backend_map; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.backend_map; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.backend_map; else if (rdev->family >= CHIP_CEDAR) *value = rdev->config.evergreen.backend_map; else if (rdev->family >= CHIP_RV770) *value = rdev->config.rv770.backend_map; else if (rdev->family >= CHIP_R600) *value = rdev->config.r600.backend_map; else { return -EINVAL; } break; case RADEON_INFO_VA_START: /* this is where we report if vm is supported or not */ if (rdev->family < CHIP_CAYMAN) return -EINVAL; *value = RADEON_VA_RESERVED_SIZE; break; case RADEON_INFO_IB_VM_MAX_SIZE: /* this is where we report if vm is supported or not */ if (rdev->family < CHIP_CAYMAN) return -EINVAL; *value = RADEON_IB_VM_MAX_SIZE; break; case RADEON_INFO_MAX_PIPES: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.max_cu_per_sh; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_cu_per_sh; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_pipes_per_simd; else if (rdev->family >= CHIP_CEDAR) *value = rdev->config.evergreen.max_pipes; else if (rdev->family >= CHIP_RV770) *value = rdev->config.rv770.max_pipes; else if (rdev->family >= CHIP_R600) *value = rdev->config.r600.max_pipes; else { return -EINVAL; } break; case RADEON_INFO_TIMESTAMP: if (rdev->family < CHIP_R600) { DRM_DEBUG_KMS("timestamp is r6xx+ only!\n"); return -EINVAL; } value = (uint32_t*)&value64; value_size = sizeof(uint64_t); value64 = radeon_get_gpu_clock_counter(rdev); break; case RADEON_INFO_MAX_SE: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.max_shader_engines; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_shader_engines; else if (rdev->family >= CHIP_CAYMAN) *value = rdev->config.cayman.max_shader_engines; else if (rdev->family >= CHIP_CEDAR) *value = rdev->config.evergreen.num_ses; else *value = 1; break; case RADEON_INFO_MAX_SH_PER_SE: if (rdev->family >= CHIP_BONAIRE) *value = rdev->config.cik.max_sh_per_se; else if (rdev->family >= CHIP_TAHITI) *value = rdev->config.si.max_sh_per_se; else return -EINVAL; break; case RADEON_INFO_FASTFB_WORKING: *value = rdev->fastfb_working; break; case RADEON_INFO_RING_WORKING: if (DRM_COPY_FROM_USER(value, value_ptr, sizeof(uint32_t))) { DRM_ERROR("copy_from_user %s:%u\n", __func__, __LINE__); return -EFAULT; } switch (*value) { case RADEON_CS_RING_GFX: case RADEON_CS_RING_COMPUTE: *value = rdev->ring[RADEON_RING_TYPE_GFX_INDEX].ready; break; case RADEON_CS_RING_DMA: *value = rdev->ring[R600_RING_TYPE_DMA_INDEX].ready; *value |= rdev->ring[CAYMAN_RING_TYPE_DMA1_INDEX].ready; break; case RADEON_CS_RING_UVD: *value = rdev->ring[R600_RING_TYPE_UVD_INDEX].ready; break; default: return -EINVAL; } break; case RADEON_INFO_SI_TILE_MODE_ARRAY: if (rdev->family >= CHIP_BONAIRE) { value = rdev->config.cik.tile_mode_array; value_size = sizeof(uint32_t)*32; } else if (rdev->family >= CHIP_TAHITI) { value = rdev->config.si.tile_mode_array; value_size = sizeof(uint32_t)*32; } else { DRM_DEBUG_KMS("tile mode array is si+ only!\n"); return -EINVAL; } break; case RADEON_INFO_CIK_MACROTILE_MODE_ARRAY: if (rdev->family >= CHIP_BONAIRE) { value = rdev->config.cik.macrotile_mode_array; value_size = sizeof(uint32_t)*16; } else { DRM_DEBUG_KMS("macrotile mode array is cik+ only!\n"); return -EINVAL; } break; case RADEON_INFO_SI_CP_DMA_COMPUTE: *value = 1; break; case RADEON_INFO_SI_BACKEND_ENABLED_MASK: if (rdev->family >= CHIP_BONAIRE) { *value = rdev->config.cik.backend_enable_mask; } else if (rdev->family >= CHIP_TAHITI) { *value = rdev->config.si.backend_enable_mask; } else { DRM_DEBUG_KMS("BACKEND_ENABLED_MASK is si+ only!\n"); } break; default: DRM_DEBUG_KMS("Invalid request %d\n", info->request); return -EINVAL; } if (DRM_COPY_TO_USER(value_ptr, (char*)value, value_size)) { DRM_ERROR("copy_to_user %s:%u\n", __func__, __LINE__); return -EFAULT; } return 0; }
void intel_detect_pch(struct drm_device *dev) { struct drm_i915_private *dev_priv = dev->dev_private; struct pci_dev *pch = NULL; /* In all current cases, num_pipes is equivalent to the PCH_NOP setting * (which really amounts to a PCH but no South Display). */ if (INTEL_INFO(dev)->num_pipes == 0) { dev_priv->pch_type = PCH_NOP; return; } /* * The reason to probe ISA bridge instead of Dev31:Fun0 is to * make graphics device passthrough work easy for VMM, that only * need to expose ISA bridge to let driver know the real hardware * underneath. This is a requirement from virtualization team. * * In some virtualized environments (e.g. XEN), there is irrelevant * ISA bridge in the system. To work reliably, we should scan trhough * all the ISA bridge devices and check for the first match, instead * of only checking the first one. */ while ((pch = pci_get_class(PCI_CLASS_BRIDGE_ISA << 8, pch))) { if (pch->vendor == PCI_VENDOR_ID_INTEL) { unsigned short id = pch->device & INTEL_PCH_DEVICE_ID_MASK; dev_priv->pch_id = id; if (id == INTEL_PCH_IBX_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_IBX; DRM_DEBUG_KMS("Found Ibex Peak PCH\n"); WARN_ON(!IS_GEN5(dev)); } else if (id == INTEL_PCH_CPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_CPT; DRM_DEBUG_KMS("Found CougarPoint PCH\n"); WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev))); } else if (id == INTEL_PCH_PPT_DEVICE_ID_TYPE) { /* PantherPoint is CPT compatible */ dev_priv->pch_type = PCH_CPT; DRM_DEBUG_KMS("Found PantherPoint PCH\n"); WARN_ON(!(IS_GEN6(dev) || IS_IVYBRIDGE(dev))); } else if (id == INTEL_PCH_LPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; DRM_DEBUG_KMS("Found LynxPoint PCH\n"); WARN_ON(!IS_HASWELL(dev) && !IS_BROADWELL(dev)); WARN_ON(IS_HSW_ULT(dev) || IS_BDW_ULT(dev)); } else if (id == INTEL_PCH_LPT_LP_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_LPT; DRM_DEBUG_KMS("Found LynxPoint LP PCH\n"); WARN_ON(!IS_HASWELL(dev) && !IS_BROADWELL(dev)); WARN_ON(!IS_HSW_ULT(dev) && !IS_BDW_ULT(dev)); } else if (id == INTEL_PCH_SPT_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_SPT; DRM_DEBUG_KMS("Found SunrisePoint PCH\n"); WARN_ON(!IS_SKYLAKE(dev) && !IS_KABYLAKE(dev)); } else if (id == INTEL_PCH_SPT_LP_DEVICE_ID_TYPE) { dev_priv->pch_type = PCH_SPT; DRM_DEBUG_KMS("Found SunrisePoint LP PCH\n"); WARN_ON(!IS_SKYLAKE(dev) && !IS_KABYLAKE(dev)); } else if ((id == INTEL_PCH_P2X_DEVICE_ID_TYPE) || (id == INTEL_PCH_QEMU_DEVICE_ID_TYPE)) { dev_priv->pch_type = intel_virt_detect_pch(dev); } else continue; break; } } if (!pch) DRM_DEBUG_KMS("No PCH found.\n"); pci_dev_put(pch); }
static void exynos_drm_crtc_prepare(struct drm_crtc *crtc) { DRM_DEBUG_KMS("%s\n", __FILE__); /* drm framework doesn't check NULL. */ }
/** * amdgpu_info_ioctl - answer a device specific request. * * @adev: amdgpu device pointer * @data: request object * @filp: drm filp * * This function is used to pass device specific parameters to the userspace * drivers. Examples include: pci device id, pipeline parms, tiling params, * etc. (all asics). * Returns 0 on success, -EINVAL on failure. */ static int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { struct amdgpu_device *adev = dev->dev_private; struct drm_amdgpu_info *info = data; struct amdgpu_mode_info *minfo = &adev->mode_info; void __user *out = (void __user *)(long)info->return_pointer; uint32_t size = info->return_size; struct drm_crtc *crtc; uint32_t ui32 = 0; uint64_t ui64 = 0; int i, found; if (!info->return_size || !info->return_pointer) return -EINVAL; switch (info->query) { case AMDGPU_INFO_ACCEL_WORKING: ui32 = adev->accel_working; return copy_to_user(out, &ui32, min(size, 4u)) ? -EFAULT : 0; case AMDGPU_INFO_CRTC_FROM_ID: for (i = 0, found = 0; i < adev->mode_info.num_crtc; i++) { crtc = (struct drm_crtc *)minfo->crtcs[i]; if (crtc && crtc->base.id == info->mode_crtc.id) { struct amdgpu_crtc *amdgpu_crtc = to_amdgpu_crtc(crtc); ui32 = amdgpu_crtc->crtc_id; found = 1; break; } } if (!found) { DRM_DEBUG_KMS("unknown crtc id %d\n", info->mode_crtc.id); return -EINVAL; } return copy_to_user(out, &ui32, min(size, 4u)) ? -EFAULT : 0; case AMDGPU_INFO_HW_IP_INFO: { struct drm_amdgpu_info_hw_ip ip = {}; enum amd_ip_block_type type; uint32_t ring_mask = 0; uint32_t ib_start_alignment = 0; uint32_t ib_size_alignment = 0; if (info->query_hw_ip.ip_instance >= AMDGPU_HW_IP_INSTANCE_MAX_COUNT) return -EINVAL; switch (info->query_hw_ip.type) { case AMDGPU_HW_IP_GFX: type = AMD_IP_BLOCK_TYPE_GFX; for (i = 0; i < adev->gfx.num_gfx_rings; i++) ring_mask |= ((adev->gfx.gfx_ring[i].ready ? 1 : 0) << i); ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; ib_size_alignment = 8; break; case AMDGPU_HW_IP_COMPUTE: type = AMD_IP_BLOCK_TYPE_GFX; for (i = 0; i < adev->gfx.num_compute_rings; i++) ring_mask |= ((adev->gfx.compute_ring[i].ready ? 1 : 0) << i); ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; ib_size_alignment = 8; break; case AMDGPU_HW_IP_DMA: type = AMD_IP_BLOCK_TYPE_SDMA; ring_mask = adev->sdma[0].ring.ready ? 1 : 0; ring_mask |= ((adev->sdma[1].ring.ready ? 1 : 0) << 1); ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; ib_size_alignment = 1; break; case AMDGPU_HW_IP_UVD: type = AMD_IP_BLOCK_TYPE_UVD; ring_mask = adev->uvd.ring.ready ? 1 : 0; ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; ib_size_alignment = 8; break; case AMDGPU_HW_IP_VCE: type = AMD_IP_BLOCK_TYPE_VCE; for (i = 0; i < AMDGPU_MAX_VCE_RINGS; i++) ring_mask |= ((adev->vce.ring[i].ready ? 1 : 0) << i); ib_start_alignment = AMDGPU_GPU_PAGE_SIZE; ib_size_alignment = 8; break; default: return -EINVAL; } for (i = 0; i < adev->num_ip_blocks; i++) { if (adev->ip_blocks[i].type == type && adev->ip_block_status[i].valid) { ip.hw_ip_version_major = adev->ip_blocks[i].major; ip.hw_ip_version_minor = adev->ip_blocks[i].minor; ip.capabilities_flags = 0; ip.available_rings = ring_mask; ip.ib_start_alignment = ib_start_alignment; ip.ib_size_alignment = ib_size_alignment; break; } } return copy_to_user(out, &ip, min((size_t)size, sizeof(ip))) ? -EFAULT : 0; } case AMDGPU_INFO_HW_IP_COUNT: { enum amd_ip_block_type type; uint32_t count = 0; switch (info->query_hw_ip.type) { case AMDGPU_HW_IP_GFX: type = AMD_IP_BLOCK_TYPE_GFX; break; case AMDGPU_HW_IP_COMPUTE: type = AMD_IP_BLOCK_TYPE_GFX; break; case AMDGPU_HW_IP_DMA: type = AMD_IP_BLOCK_TYPE_SDMA; break; case AMDGPU_HW_IP_UVD: type = AMD_IP_BLOCK_TYPE_UVD; break; case AMDGPU_HW_IP_VCE: type = AMD_IP_BLOCK_TYPE_VCE; break; default: return -EINVAL; } for (i = 0; i < adev->num_ip_blocks; i++) if (adev->ip_blocks[i].type == type && adev->ip_block_status[i].valid && count < AMDGPU_HW_IP_INSTANCE_MAX_COUNT) count++; return copy_to_user(out, &count, min(size, 4u)) ? -EFAULT : 0; } case AMDGPU_INFO_TIMESTAMP: ui64 = amdgpu_asic_get_gpu_clock_counter(adev); return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; case AMDGPU_INFO_FW_VERSION: { struct drm_amdgpu_info_firmware fw_info; /* We only support one instance of each IP block right now. */ if (info->query_fw.ip_instance != 0) return -EINVAL; switch (info->query_fw.fw_type) { case AMDGPU_INFO_FW_VCE: fw_info.ver = adev->vce.fw_version; fw_info.feature = adev->vce.fb_version; break; case AMDGPU_INFO_FW_UVD: fw_info.ver = 0; fw_info.feature = 0; break; case AMDGPU_INFO_FW_GMC: fw_info.ver = adev->mc.fw_version; fw_info.feature = 0; break; case AMDGPU_INFO_FW_GFX_ME: fw_info.ver = adev->gfx.me_fw_version; fw_info.feature = adev->gfx.me_feature_version; break; case AMDGPU_INFO_FW_GFX_PFP: fw_info.ver = adev->gfx.pfp_fw_version; fw_info.feature = adev->gfx.pfp_feature_version; break; case AMDGPU_INFO_FW_GFX_CE: fw_info.ver = adev->gfx.ce_fw_version; fw_info.feature = adev->gfx.ce_feature_version; break; case AMDGPU_INFO_FW_GFX_RLC: fw_info.ver = adev->gfx.rlc_fw_version; fw_info.feature = adev->gfx.rlc_feature_version; break; case AMDGPU_INFO_FW_GFX_MEC: if (info->query_fw.index == 0) { fw_info.ver = adev->gfx.mec_fw_version; fw_info.feature = adev->gfx.mec_feature_version; } else if (info->query_fw.index == 1) { fw_info.ver = adev->gfx.mec2_fw_version; fw_info.feature = adev->gfx.mec2_feature_version; } else return -EINVAL; break; case AMDGPU_INFO_FW_SMC: fw_info.ver = adev->pm.fw_version; fw_info.feature = 0; break; case AMDGPU_INFO_FW_SDMA: if (info->query_fw.index >= 2) return -EINVAL; fw_info.ver = adev->sdma[info->query_fw.index].fw_version; fw_info.feature = adev->sdma[info->query_fw.index].feature_version; break; default: return -EINVAL; } return copy_to_user(out, &fw_info, min((size_t)size, sizeof(fw_info))) ? -EFAULT : 0; } case AMDGPU_INFO_NUM_BYTES_MOVED: ui64 = atomic64_read(&adev->num_bytes_moved); return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; case AMDGPU_INFO_VRAM_USAGE: ui64 = atomic64_read(&adev->vram_usage); return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; case AMDGPU_INFO_VIS_VRAM_USAGE: ui64 = atomic64_read(&adev->vram_vis_usage); return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; case AMDGPU_INFO_GTT_USAGE: ui64 = atomic64_read(&adev->gtt_usage); return copy_to_user(out, &ui64, min(size, 8u)) ? -EFAULT : 0; case AMDGPU_INFO_GDS_CONFIG: { struct drm_amdgpu_info_gds gds_info; memset(&gds_info, 0, sizeof(gds_info)); gds_info.gds_gfx_partition_size = adev->gds.mem.gfx_partition_size >> AMDGPU_GDS_SHIFT; gds_info.compute_partition_size = adev->gds.mem.cs_partition_size >> AMDGPU_GDS_SHIFT; gds_info.gds_total_size = adev->gds.mem.total_size >> AMDGPU_GDS_SHIFT; gds_info.gws_per_gfx_partition = adev->gds.gws.gfx_partition_size >> AMDGPU_GWS_SHIFT; gds_info.gws_per_compute_partition = adev->gds.gws.cs_partition_size >> AMDGPU_GWS_SHIFT; gds_info.oa_per_gfx_partition = adev->gds.oa.gfx_partition_size >> AMDGPU_OA_SHIFT; gds_info.oa_per_compute_partition = adev->gds.oa.cs_partition_size >> AMDGPU_OA_SHIFT; return copy_to_user(out, &gds_info, min((size_t)size, sizeof(gds_info))) ? -EFAULT : 0; } case AMDGPU_INFO_VRAM_GTT: { struct drm_amdgpu_info_vram_gtt vram_gtt; vram_gtt.vram_size = adev->mc.real_vram_size; vram_gtt.vram_cpu_accessible_size = adev->mc.visible_vram_size; vram_gtt.vram_cpu_accessible_size -= adev->vram_pin_size; vram_gtt.gtt_size = adev->mc.gtt_size; vram_gtt.gtt_size -= adev->gart_pin_size; return copy_to_user(out, &vram_gtt, min((size_t)size, sizeof(vram_gtt))) ? -EFAULT : 0; } case AMDGPU_INFO_READ_MMR_REG: { unsigned n, alloc_size; uint32_t *regs; unsigned se_num = (info->read_mmr_reg.instance >> AMDGPU_INFO_MMR_SE_INDEX_SHIFT) & AMDGPU_INFO_MMR_SE_INDEX_MASK; unsigned sh_num = (info->read_mmr_reg.instance >> AMDGPU_INFO_MMR_SH_INDEX_SHIFT) & AMDGPU_INFO_MMR_SH_INDEX_MASK; /* set full masks if the userspace set all bits * in the bitfields */ if (se_num == AMDGPU_INFO_MMR_SE_INDEX_MASK) se_num = 0xffffffff; if (sh_num == AMDGPU_INFO_MMR_SH_INDEX_MASK) sh_num = 0xffffffff; regs = kmalloc_array(info->read_mmr_reg.count, sizeof(*regs), GFP_KERNEL); if (!regs) return -ENOMEM; alloc_size = info->read_mmr_reg.count * sizeof(*regs); for (i = 0; i < info->read_mmr_reg.count; i++) if (amdgpu_asic_read_register(adev, se_num, sh_num, info->read_mmr_reg.dword_offset + i, ®s[i])) { DRM_DEBUG_KMS("unallowed offset %#x\n", info->read_mmr_reg.dword_offset + i); kfree(regs); return -EFAULT; } n = copy_to_user(out, regs, min(size, alloc_size)); kfree(regs); return n ? -EFAULT : 0; } case AMDGPU_INFO_DEV_INFO: { struct drm_amdgpu_info_device dev_info = {}; struct amdgpu_cu_info cu_info; dev_info.device_id = dev->pdev->device; dev_info.chip_rev = adev->rev_id; dev_info.external_rev = adev->external_rev_id; dev_info.pci_rev = dev->pdev->revision; dev_info.family = adev->family; dev_info.num_shader_engines = adev->gfx.config.max_shader_engines; dev_info.num_shader_arrays_per_engine = adev->gfx.config.max_sh_per_se; /* return all clocks in KHz */ dev_info.gpu_counter_freq = amdgpu_asic_get_xclk(adev) * 10; if (adev->pm.dpm_enabled) { dev_info.max_engine_clock = adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.sclk * 10; dev_info.max_memory_clock = adev->pm.dpm.dyn_state.max_clock_voltage_on_ac.mclk * 10; } else { dev_info.max_engine_clock = adev->pm.default_sclk * 10; dev_info.max_memory_clock = adev->pm.default_mclk * 10; } dev_info.enabled_rb_pipes_mask = adev->gfx.config.backend_enable_mask; dev_info.num_rb_pipes = adev->gfx.config.max_backends_per_se * adev->gfx.config.max_shader_engines; dev_info.num_hw_gfx_contexts = adev->gfx.config.max_hw_contexts; dev_info._pad = 0; dev_info.ids_flags = 0; if (adev->flags & AMD_IS_APU) dev_info.ids_flags |= AMDGPU_IDS_FLAGS_FUSION; dev_info.virtual_address_offset = AMDGPU_VA_RESERVED_SIZE; dev_info.virtual_address_max = (uint64_t)adev->vm_manager.max_pfn * AMDGPU_GPU_PAGE_SIZE; dev_info.virtual_address_alignment = max((int)PAGE_SIZE, AMDGPU_GPU_PAGE_SIZE); dev_info.pte_fragment_size = (1 << AMDGPU_LOG2_PAGES_PER_FRAG) * AMDGPU_GPU_PAGE_SIZE; dev_info.gart_page_size = AMDGPU_GPU_PAGE_SIZE; amdgpu_asic_get_cu_info(adev, &cu_info); dev_info.cu_active_number = cu_info.number; dev_info.cu_ao_mask = cu_info.ao_cu_mask; dev_info.ce_ram_size = adev->gfx.ce_ram_size; memcpy(&dev_info.cu_bitmap[0], &cu_info.bitmap[0], sizeof(cu_info.bitmap)); dev_info.vram_type = adev->mc.vram_type; dev_info.vram_bit_width = adev->mc.vram_width; dev_info.vce_harvest_config = adev->vce.harvest_config; return copy_to_user(out, &dev_info, min((size_t)size, sizeof(dev_info))) ? -EFAULT : 0; } default: DRM_DEBUG_KMS("Invalid request %d\n", info->query); return -EINVAL; } return 0; }
/* * Try to read the BIOS display configuration and use it for the initial * fb configuration. * * The BIOS or boot loader will generally create an initial display * configuration for us that includes some set of active pipes and displays. * This routine tries to figure out which pipes and connectors are active * and stuffs them into the crtcs and modes array given to us by the * drm_fb_helper code. * * The overall sequence is: * intel_fbdev_init - from driver load * intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data * drm_fb_helper_init - build fb helper structs * drm_fb_helper_single_add_all_connectors - more fb helper structs * intel_fbdev_initial_config - apply the config * drm_fb_helper_initial_config - call ->probe then register_framebuffer() * drm_setup_crtcs - build crtc config for fbdev * intel_fb_initial_config - find active connectors etc * drm_fb_helper_single_fb_probe - set up fbdev * intelfb_create - re-use or alloc fb, build out fbdev structs * * Note that we don't make special consideration whether we could actually * switch to the selected modes without a full modeset. E.g. when the display * is in VGA mode we need to recalculate watermarks and set a new high-res * framebuffer anyway. */ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, struct drm_fb_helper_crtc **crtcs, struct drm_display_mode **modes, bool *enabled, int width, int height) { struct drm_device *dev = fb_helper->dev; int i, j; bool *save_enabled; bool fallback = true; int num_connectors_enabled = 0; int num_connectors_detected = 0; /* * If the user specified any force options, just bail here * and use that config. */ for (i = 0; i < fb_helper->connector_count; i++) { struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; fb_conn = fb_helper->connector_info[i]; connector = fb_conn->connector; if (!enabled[i]) continue; if (connector->force != DRM_FORCE_UNSPECIFIED) return false; } save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), GFP_KERNEL); if (!save_enabled) return false; memcpy(save_enabled, enabled, dev->mode_config.num_connector); for (i = 0; i < fb_helper->connector_count; i++) { struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_fb_helper_crtc *new_crtc; fb_conn = fb_helper->connector_info[i]; connector = fb_conn->connector; if (connector->status == connector_status_connected) num_connectors_detected++; if (!enabled[i]) { DRM_DEBUG_KMS("connector %d not enabled, skipping\n", connector->base.id); continue; } encoder = connector->encoder; if (!encoder || WARN_ON(!encoder->crtc)) { DRM_DEBUG_KMS("connector %d has no encoder or crtc, skipping\n", connector->base.id); enabled[i] = false; continue; } num_connectors_enabled++; new_crtc = intel_fb_helper_crtc(fb_helper, encoder->crtc); /* * Make sure we're not trying to drive multiple connectors * with a single CRTC, since our cloning support may not * match the BIOS. */ for (j = 0; j < fb_helper->connector_count; j++) { if (crtcs[j] == new_crtc) { DRM_DEBUG_KMS("fallback: cloned configuration\n"); fallback = true; goto out; } } DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", fb_conn->connector->base.id); /* go for command line mode first */ modes[i] = drm_pick_cmdline_mode(fb_conn, width, height); /* try for preferred next */ if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", fb_conn->connector->base.id); modes[i] = drm_has_preferred_mode(fb_conn, width, height); } /* No preferred mode marked by the EDID? Are there any modes? */ if (!modes[i] && !list_empty(&connector->modes)) { DRM_DEBUG_KMS("using first mode listed on connector %s\n", drm_get_connector_name(connector)); modes[i] = list_first_entry(&connector->modes, struct drm_display_mode, head); } /* last resort: use current mode */ if (!modes[i]) { /* * IMPORTANT: We want to use the adjusted mode (i.e. * after the panel fitter upscaling) as the initial * config, not the input mode, which is what crtc->mode * usually contains. But since our current fastboot * code puts a mode derived from the post-pfit timings * into crtc->mode this works out correctly. We don't * use hwmode anywhere right now, so use it for this * since the fb helper layer wants a pointer to * something we own. */ intel_mode_from_pipe_config(&encoder->crtc->hwmode, &to_intel_crtc(encoder->crtc)->config); modes[i] = &encoder->crtc->hwmode; } crtcs[i] = new_crtc; DRM_DEBUG_KMS("connector %s on crtc %d: %s\n", drm_get_connector_name(connector), encoder->crtc->base.id, modes[i]->name); fallback = false; }
static int intelfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { struct intel_fbdev *ifbdev = container_of(helper, struct intel_fbdev, helper); struct intel_framebuffer *intel_fb = ifbdev->fb; struct drm_device *dev = helper->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct fb_info *info; struct drm_framebuffer *fb; struct drm_i915_gem_object *obj; int size, ret; bool prealloc = false; mutex_lock(&dev->struct_mutex); if (intel_fb && (sizes->fb_width > intel_fb->base.width || sizes->fb_height > intel_fb->base.height)) { DRM_DEBUG_KMS("BIOS fb too small (%dx%d), we require (%dx%d)," " releasing it\n", intel_fb->base.width, intel_fb->base.height, sizes->fb_width, sizes->fb_height); drm_framebuffer_unreference(&intel_fb->base); intel_fb = ifbdev->fb = NULL; } if (!intel_fb || WARN_ON(!intel_fb->obj)) { DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n"); ret = intelfb_alloc(helper, sizes); if (ret) goto out_unlock; intel_fb = ifbdev->fb; } else { DRM_DEBUG_KMS("re-using BIOS fb\n"); prealloc = true; sizes->fb_width = intel_fb->base.width; sizes->fb_height = intel_fb->base.height; } obj = intel_fb->obj; size = obj->base.size; info = framebuffer_alloc(0, &dev->pdev->dev); if (!info) { ret = -ENOMEM; goto out_unpin; } info->par = helper; fb = &ifbdev->fb->base; ifbdev->helper.fb = fb; ifbdev->helper.fbdev = info; strcpy(info->fix.id, "inteldrmfb"); info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &intelfb_ops; ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret) { ret = -ENOMEM; goto out_unpin; } /* setup aperture base/size for vesafb takeover */ info->apertures = alloc_apertures(1); if (!info->apertures) { ret = -ENOMEM; goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; info->apertures->ranges[0].size = dev_priv->gtt.mappable_end; info->fix.smem_start = dev->mode_config.fb_base + i915_gem_obj_ggtt_offset(obj); info->fix.smem_len = size; info->screen_base = ioremap_wc(dev_priv->gtt.mappable_base + i915_gem_obj_ggtt_offset(obj), size); if (!info->screen_base) { ret = -ENOSPC; goto out_unpin; } info->screen_size = size; /* This driver doesn't need a VT switch to restore the mode on resume */ info->skip_vt_switch = true; drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); /* If the object is shmemfs backed, it will have given us zeroed pages. * If the object is stolen however, it will be full of whatever * garbage was left in there. */ if (ifbdev->fb->obj->stolen && !prealloc) memset_io(info->screen_base, 0, info->screen_size); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08lx, bo %p\n", fb->width, fb->height, i915_gem_obj_ggtt_offset(obj), obj); mutex_unlock(&dev->struct_mutex); vga_switcheroo_client_fb_set(dev->pdev, info); return 0; out_unpin: i915_gem_object_ggtt_unpin(obj); drm_gem_object_unreference(&obj->base); out_unlock: mutex_unlock(&dev->struct_mutex); return ret; }
static void intel_dsi_hot_plug(struct intel_encoder *encoder) { DRM_DEBUG_KMS("\n"); }
/* * Sanity check the EDID block (base or extension). Return 0 if the block * doesn't check out, or 1 if it's valid. */ static bool drm_edid_block_valid(u8 *raw_edid) { int i; u8 csum = 0; struct edid *edid = (struct edid *)raw_edid; if (raw_edid[0] == 0x00) { int score = drm_edid_header_is_valid(raw_edid); if (score == 8) ; else if (score >= 6) { DRM_DEBUG("Fixing EDID header, your hardware may be failing\n"); memcpy(raw_edid, edid_header, sizeof(edid_header)); } else { goto bad; } } for (i = 0; i < EDID_LENGTH; i++) csum += raw_edid[i]; if (csum) { DRM_DEBUG_KMS("EDID checksum is invalid, remainder is %d\n", csum); /* allow CEA to slide through, switches mangle this */ if (raw_edid[0] != 0x02) goto bad; } /* per-block-type checks */ switch (raw_edid[0]) { case 0: /* base */ if (edid->version != 1) { DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version); goto bad; } if (edid->revision > 4) DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n"); break; default: break; } return 1; bad: if (raw_edid) { DRM_DEBUG_KMS("Raw EDID:\n"); if ((drm_debug_flag & DRM_DEBUGBITS_KMS) != 0) { for (i = 0; i < EDID_LENGTH; ) { printf("%02x", raw_edid[i]); i++; if (i % 16 == 0 || i == EDID_LENGTH) printf("\n"); else if (i % 8 == 0) printf(" "); else printf(" "); } } } return 0; }
/** * drm_primary_helper_update() - Helper for primary plane update * @plane: plane object to update * @crtc: owning CRTC of owning plane * @fb: framebuffer to flip onto plane * @crtc_x: x offset of primary plane on crtc * @crtc_y: y offset of primary plane on crtc * @crtc_w: width of primary plane rectangle on crtc * @crtc_h: height of primary plane rectangle on crtc * @src_x: x offset of @fb for panning * @src_y: y offset of @fb for panning * @src_w: width of source rectangle in @fb * @src_h: height of source rectangle in @fb * * Provides a default plane update handler for primary planes. This is handler * is called in response to a userspace SetPlane operation on the plane with a * non-NULL framebuffer. We call the driver's modeset handler to update the * framebuffer. * * SetPlane() on a primary plane of a disabled CRTC is not supported, and will * return an error. * * Note that we make some assumptions about hardware limitations that may not be * true for all hardware -- * 1) Primary plane cannot be repositioned. * 2) Primary plane cannot be scaled. * 3) Primary plane must cover the entire CRTC. * 4) Subpixel positioning is not supported. * Drivers for hardware that don't have these restrictions can provide their * own implementation rather than using this helper. * * RETURNS: * Zero on success, error code on failure */ int drm_primary_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { struct drm_mode_set set = { .crtc = crtc, .fb = fb, .mode = &crtc->mode, .x = src_x >> 16, .y = src_y >> 16, }; struct drm_rect src = { .x1 = src_x, .y1 = src_y, .x2 = src_x + src_w, .y2 = src_y + src_h, }; struct drm_rect dest = { .x1 = crtc_x, .y1 = crtc_y, .x2 = crtc_x + crtc_w, .y2 = crtc_y + crtc_h, }; const struct drm_rect clip = { .x2 = crtc->mode.hdisplay, .y2 = crtc->mode.vdisplay, }; struct drm_connector **connector_list; int num_connectors, ret; bool visible; ret = drm_plane_helper_check_update(plane, crtc, fb, &src, &dest, &clip, DRM_PLANE_HELPER_NO_SCALING, DRM_PLANE_HELPER_NO_SCALING, false, false, &visible); if (ret) return ret; if (!visible) /* * Primary plane isn't visible. Note that unless a driver * provides their own disable function, this will just * wind up returning -EINVAL to userspace. */ return plane->funcs->disable_plane(plane); /* Find current connectors for CRTC */ num_connectors = get_connectors_for_crtc(crtc, NULL, 0); BUG_ON(num_connectors == 0); connector_list = kzalloc(num_connectors * sizeof(*connector_list), GFP_KERNEL); if (!connector_list) return -ENOMEM; get_connectors_for_crtc(crtc, connector_list, num_connectors); set.connectors = connector_list; set.num_connectors = num_connectors; /* * We call set_config() directly here rather than using * drm_mode_set_config_internal. We're reprogramming the same * connectors that were already in use, so we shouldn't need the extra * cross-CRTC fb refcounting to accomodate stealing connectors. * drm_mode_setplane() already handles the basic refcounting for the * framebuffers involved in this operation. */ ret = crtc->funcs->set_config(&set); kfree(connector_list); return ret; } EXPORT_SYMBOL(drm_primary_helper_update); /** * drm_primary_helper_disable() - Helper for primary plane disable * @plane: plane to disable * * Provides a default plane disable handler for primary planes. This is handler * is called in response to a userspace SetPlane operation on the plane with a * NULL framebuffer parameter. It unconditionally fails the disable call with * -EINVAL the only way to disable the primary plane without driver support is * to disable the entier CRTC. Which does not match the plane ->disable hook. * * Note that some hardware may be able to disable the primary plane without * disabling the whole CRTC. Drivers for such hardware should provide their * own disable handler that disables just the primary plane (and they'll likely * need to provide their own update handler as well to properly re-enable a * disabled primary plane). * * RETURNS: * Unconditionally returns -EINVAL. */ int drm_primary_helper_disable(struct drm_plane *plane) { return -EINVAL; } EXPORT_SYMBOL(drm_primary_helper_disable); /** * drm_primary_helper_destroy() - Helper for primary plane destruction * @plane: plane to destroy * * Provides a default plane destroy handler for primary planes. This handler * is called during CRTC destruction. We disable the primary plane, remove * it from the DRM plane list, and deallocate the plane structure. */ void drm_primary_helper_destroy(struct drm_plane *plane) { drm_plane_cleanup(plane); kfree(plane); } EXPORT_SYMBOL(drm_primary_helper_destroy); const struct drm_plane_funcs drm_primary_helper_funcs = { .update_plane = drm_primary_helper_update, .disable_plane = drm_primary_helper_disable, .destroy = drm_primary_helper_destroy, }; EXPORT_SYMBOL(drm_primary_helper_funcs); static struct drm_plane *create_primary_plane(struct drm_device *dev) { struct drm_plane *primary; int ret; primary = kzalloc(sizeof(*primary), GFP_KERNEL); if (primary == NULL) { DRM_DEBUG_KMS("Failed to allocate primary plane\n"); return NULL; } /* * Remove the format_default field from drm_plane when dropping * this helper. */ primary->format_default = true; /* possible_crtc's will be filled in later by crtc_init */ ret = drm_universal_plane_init(dev, primary, 0, &drm_primary_helper_funcs, safe_modeset_formats, ARRAY_SIZE(safe_modeset_formats), DRM_PLANE_TYPE_PRIMARY, NULL); if (ret) { kfree(primary); primary = NULL; } return primary; } /** * drm_crtc_init - Legacy CRTC initialization function * @dev: DRM device * @crtc: CRTC object to init * @funcs: callbacks for the new CRTC * * Initialize a CRTC object with a default helper-provided primary plane and no * cursor plane. * * Returns: * Zero on success, error code on failure. */ int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, const struct drm_crtc_funcs *funcs) { struct drm_plane *primary; primary = create_primary_plane(dev); return drm_crtc_init_with_planes(dev, crtc, primary, NULL, funcs, NULL); } EXPORT_SYMBOL(drm_crtc_init); int drm_plane_helper_commit(struct drm_plane *plane, struct drm_plane_state *plane_state, struct drm_framebuffer *old_fb) { const struct drm_plane_helper_funcs *plane_funcs; struct drm_crtc *crtc[2]; const struct drm_crtc_helper_funcs *crtc_funcs[2]; int i, ret = 0; plane_funcs = plane->helper_private; /* Since this is a transitional helper we can't assume that plane->state * is always valid. Hence we need to use plane->crtc instead of * plane->state->crtc as the old crtc. */ crtc[0] = plane->crtc; crtc[1] = crtc[0] != plane_state->crtc ? plane_state->crtc : NULL; for (i = 0; i < 2; i++) crtc_funcs[i] = crtc[i] ? crtc[i]->helper_private : NULL; if (plane_funcs->atomic_check) { ret = plane_funcs->atomic_check(plane, plane_state); if (ret) goto out; } if (plane_funcs->prepare_fb && plane_state->fb && plane_state->fb != old_fb) { ret = plane_funcs->prepare_fb(plane, plane_state); if (ret) goto out; } /* Point of no return, commit sw state. */ swap(plane->state, plane_state); for (i = 0; i < 2; i++) { if (crtc_funcs[i] && crtc_funcs[i]->atomic_begin) crtc_funcs[i]->atomic_begin(crtc[i], crtc[i]->state); } /* * Drivers may optionally implement the ->atomic_disable callback, so * special-case that here. */ if (drm_atomic_plane_disabling(plane, plane_state) && plane_funcs->atomic_disable) plane_funcs->atomic_disable(plane, plane_state); else plane_funcs->atomic_update(plane, plane_state); for (i = 0; i < 2; i++) { if (crtc_funcs[i] && crtc_funcs[i]->atomic_flush) crtc_funcs[i]->atomic_flush(crtc[i], crtc[i]->state); } /* * If we only moved the plane and didn't change fb's, there's no need to * wait for vblank. */ if (plane->state->fb == old_fb) goto out; for (i = 0; i < 2; i++) { if (!crtc[i]) continue; if (crtc[i]->cursor == plane) continue; /* There's no other way to figure out whether the crtc is running. */ ret = drm_crtc_vblank_get(crtc[i]); if (ret == 0) { drm_crtc_wait_one_vblank(crtc[i]); drm_crtc_vblank_put(crtc[i]); } ret = 0; } if (plane_funcs->cleanup_fb) plane_funcs->cleanup_fb(plane, plane_state); out: if (plane_state) { if (plane->funcs->atomic_destroy_state) plane->funcs->atomic_destroy_state(plane, plane_state); else drm_atomic_helper_plane_destroy_state(plane, plane_state); } return ret; } /** * drm_plane_helper_update() - Transitional helper for plane update * @plane: plane object to update * @crtc: owning CRTC of owning plane * @fb: framebuffer to flip onto plane * @crtc_x: x offset of primary plane on crtc * @crtc_y: y offset of primary plane on crtc * @crtc_w: width of primary plane rectangle on crtc * @crtc_h: height of primary plane rectangle on crtc * @src_x: x offset of @fb for panning * @src_y: y offset of @fb for panning * @src_w: width of source rectangle in @fb * @src_h: height of source rectangle in @fb * * Provides a default plane update handler using the atomic plane update * functions. It is fully left to the driver to check plane constraints and * handle corner-cases like a fully occluded or otherwise invisible plane. * * This is useful for piecewise transitioning of a driver to the atomic helpers. * * RETURNS: * Zero on success, error code on failure */ int drm_plane_helper_update(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int crtc_x, int crtc_y, unsigned int crtc_w, unsigned int crtc_h, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { struct drm_plane_state *plane_state; if (plane->funcs->atomic_duplicate_state) plane_state = plane->funcs->atomic_duplicate_state(plane); else { if (!plane->state) drm_atomic_helper_plane_reset(plane); plane_state = drm_atomic_helper_plane_duplicate_state(plane); } if (!plane_state) return -ENOMEM; plane_state->plane = plane; plane_state->crtc = crtc; drm_atomic_set_fb_for_plane(plane_state, fb); plane_state->crtc_x = crtc_x; plane_state->crtc_y = crtc_y; plane_state->crtc_h = crtc_h; plane_state->crtc_w = crtc_w; plane_state->src_x = src_x; plane_state->src_y = src_y; plane_state->src_h = src_h; plane_state->src_w = src_w; return drm_plane_helper_commit(plane, plane_state, plane->fb); } EXPORT_SYMBOL(drm_plane_helper_update); /** * drm_plane_helper_disable() - Transitional helper for plane disable * @plane: plane to disable * * Provides a default plane disable handler using the atomic plane update * functions. It is fully left to the driver to check plane constraints and * handle corner-cases like a fully occluded or otherwise invisible plane. * * This is useful for piecewise transitioning of a driver to the atomic helpers. * * RETURNS: * Zero on success, error code on failure */ int drm_plane_helper_disable(struct drm_plane *plane) { struct drm_plane_state *plane_state; /* crtc helpers love to call disable functions for already disabled hw * functions. So cope with that. */ if (!plane->crtc) return 0; if (plane->funcs->atomic_duplicate_state) plane_state = plane->funcs->atomic_duplicate_state(plane); else { if (!plane->state) drm_atomic_helper_plane_reset(plane); plane_state = drm_atomic_helper_plane_duplicate_state(plane); } if (!plane_state) return -ENOMEM; plane_state->plane = plane; plane_state->crtc = NULL; drm_atomic_set_fb_for_plane(plane_state, NULL); return drm_plane_helper_commit(plane, plane_state, plane->fb); } EXPORT_SYMBOL(drm_plane_helper_disable);
static int intel_runtime_suspend(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct drm_device *dev = pci_get_drvdata(pdev); struct drm_i915_private *dev_priv = dev->dev_private; int ret; if (WARN_ON_ONCE(!(dev_priv->rps.enabled && intel_enable_rc6(dev)))) return -ENODEV; if (WARN_ON_ONCE(!HAS_RUNTIME_PM(dev))) return -ENODEV; DRM_DEBUG_KMS("Suspending device\n"); /* * We could deadlock here in case another thread holding struct_mutex * calls RPM suspend concurrently, since the RPM suspend will wait * first for this RPM suspend to finish. In this case the concurrent * RPM resume will be followed by its RPM suspend counterpart. Still * for consistency return -EAGAIN, which will reschedule this suspend. */ if (!mutex_trylock(&dev->struct_mutex)) { DRM_DEBUG_KMS("device lock contention, deffering suspend\n"); /* * Bump the expiration timestamp, otherwise the suspend won't * be rescheduled. */ pm_runtime_mark_last_busy(device); return -EAGAIN; } disable_rpm_wakeref_asserts(dev_priv); /* * We are safe here against re-faults, since the fault handler takes * an RPM reference. */ i915_gem_release_all_mmaps(dev_priv); mutex_unlock(&dev->struct_mutex); cancel_delayed_work_sync(&dev_priv->gpu_error.hangcheck_work); intel_guc_suspend(dev); intel_suspend_gt_powersave(dev); intel_runtime_pm_disable_interrupts(dev_priv); ret = intel_suspend_complete(dev_priv); if (ret) { DRM_ERROR("Runtime suspend failed, disabling it (%d)\n", ret); intel_runtime_pm_enable_interrupts(dev_priv); enable_rpm_wakeref_asserts(dev_priv); return ret; } intel_uncore_forcewake_reset(dev, false); enable_rpm_wakeref_asserts(dev_priv); WARN_ON_ONCE(atomic_read(&dev_priv->pm.wakeref_count)); dev_priv->pm.suspended = true; /* * FIXME: We really should find a document that references the arguments * used below! */ if (IS_BROADWELL(dev)) { /* * On Broadwell, if we use PCI_D1 the PCH DDI ports will stop * being detected, and the call we do at intel_runtime_resume() * won't be able to restore them. Since PCI_D3hot matches the * actual specification and appears to be working, use it. */ intel_opregion_notify_adapter(dev, PCI_D3hot); } else { /* * current versions of firmware which depend on this opregion * notification have repurposed the D1 definition to mean * "runtime suspended" vs. what you would normally expect (D3) * to distinguish it from notifications that might be sent via * the suspend path. */ intel_opregion_notify_adapter(dev, PCI_D1); } assert_forcewakes_inactive(dev_priv); DRM_DEBUG_KMS("Device suspended\n"); return 0; }
int exynos_dmabuf_prime_handle_to_fd(struct drm_device *drm_dev, struct drm_file *file, unsigned int handle, int *prime_fd) { struct drm_gem_object *obj; struct exynos_drm_gem_obj *exynos_gem_obj; int ret = 0; DRM_DEBUG_KMS("%s\n", __FILE__); ret = mutex_lock_interruptible(&drm_dev->struct_mutex); if (ret < 0) return ret; obj = drm_gem_object_lookup(drm_dev, file, handle); if (!obj) { DRM_DEBUG_KMS("failed to lookup gem object.\n"); ret = -EINVAL; goto err1; } exynos_gem_obj = to_exynos_gem_obj(obj); if (obj->prime_fd != -1) { /* we have a prime fd already referencing the object. */ goto have_fd; } /* * get the dmabuf object for a gem object after registering * the gem object to allocated dmabuf. * * P.S. dma_buf_export function performs the followings: * - create a new dmabuf object. * - dmabuf->priv = gem object. * - file->private_data = dmabuf. */ obj->export_dma_buf = dma_buf_export(obj, &exynos_dmabuf_ops, obj->size, 0600); if (!obj->export_dma_buf) { ret = PTR_ERR(obj->export_dma_buf); goto err2; } /* get file descriptor for a given dmabuf object. */ obj->prime_fd = dma_buf_fd(obj->export_dma_buf); if (obj->prime_fd < 0) { DRM_DEBUG_KMS("failed to get fd from dmabuf.\n"); dma_buf_put(obj->export_dma_buf); ret = obj->prime_fd; goto err2; } /* * this gem object is referenced by the fd so * the object refcount should be increased. * after that when dmabuf_ops->release() is called, * it will be decreased again. */ drm_gem_object_reference(obj); have_fd: *prime_fd = obj->prime_fd; err2: drm_gem_object_unreference(obj); err1: mutex_unlock(&drm_dev->struct_mutex); return ret; }
int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode, u8 write_byte, u8 *read_byte) { struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data; struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter; u16 address = algo_data->address; u8 msg[5]; u8 reply[2]; unsigned retry; int msg_bytes; int reply_bytes = 1; int ret; u8 ack; /* Set up the command byte */ if (mode & MODE_I2C_READ) msg[2] = DP_AUX_I2C_READ << 4; else msg[2] = DP_AUX_I2C_WRITE << 4; if (!(mode & MODE_I2C_STOP)) msg[2] |= DP_AUX_I2C_MOT << 4; msg[0] = address; msg[1] = address >> 8; switch (mode) { case MODE_I2C_WRITE: msg_bytes = 5; msg[3] = msg_bytes << 4; msg[4] = write_byte; break; case MODE_I2C_READ: msg_bytes = 4; msg[3] = msg_bytes << 4; break; default: msg_bytes = 4; msg[3] = 3 << 4; break; } for (retry = 0; retry < 7; retry++) { ret = radeon_process_aux_ch(auxch, msg, msg_bytes, reply, reply_bytes, 0, &ack); if (ret == -EBUSY) continue; else if (ret < 0) { DRM_DEBUG_KMS("aux_ch failed %d\n", ret); return ret; } switch ((ack >> 4) & DP_AUX_NATIVE_REPLY_MASK) { case DP_AUX_NATIVE_REPLY_ACK: /* I2C-over-AUX Reply field is only valid * when paired with AUX ACK. */ break; case DP_AUX_NATIVE_REPLY_NACK: DRM_DEBUG_KMS("aux_ch native nack\n"); return -EREMOTEIO; case DP_AUX_NATIVE_REPLY_DEFER: DRM_DEBUG_KMS("aux_ch native defer\n"); usleep_range(500, 600); continue; default: DRM_ERROR("aux_ch invalid native reply 0x%02x\n", ack); return -EREMOTEIO; } switch ((ack >> 4) & DP_AUX_I2C_REPLY_MASK) { case DP_AUX_I2C_REPLY_ACK: if (mode == MODE_I2C_READ) *read_byte = reply[0]; return ret; case DP_AUX_I2C_REPLY_NACK: DRM_DEBUG_KMS("aux_i2c nack\n"); return -EREMOTEIO; case DP_AUX_I2C_REPLY_DEFER: DRM_DEBUG_KMS("aux_i2c defer\n"); usleep_range(400, 500); break; default: DRM_ERROR("aux_i2c invalid reply 0x%02x\n", ack); return -EREMOTEIO; } } DRM_DEBUG_KMS("aux i2c too many retries, giving up\n"); return -EREMOTEIO; }
int exynos_dmabuf_prime_fd_to_handle(struct drm_device *drm_dev, struct drm_file *file, int prime_fd, unsigned int *handle) { struct drm_exynos_file_private *file_priv = file->driver_priv; struct dma_buf_attachment *attach; struct dma_buf *dmabuf; struct sg_table *sgt; struct exynos_drm_gem_obj *exynos_gem_obj; struct exynos_drm_gem_buf *buffer; int ret; DRM_DEBUG_KMS("%s\n", __FILE__); ret = mutex_lock_interruptible(&drm_dev->struct_mutex); if (ret < 0) return ret; dmabuf = dma_buf_get(prime_fd); if (IS_ERR(dmabuf)) { ret = PTR_ERR(dmabuf); goto out; } /* * if there is same dmabuf as the one to prime_fd * in file_priv->prime list then return the handle. * * Note: * but if the prime_fd from user belongs to another process * then there couldn't be the dmabuf in file_priv->prime list * because file_priv is unique to process. */ ret = drm_prime_lookup_fd_handle_mapping(&file_priv->prime, dmabuf, handle); if (!ret) { /* drop reference we got above. */ dma_buf_put(dmabuf); goto out; } attach = dma_buf_attach(dmabuf, drm_dev->dev); if (IS_ERR(attach)) { ret = PTR_ERR(attach); goto fail_put; } sgt = dma_buf_map_attachment(attach, DMA_BIDIRECTIONAL); if (IS_ERR(sgt)) { ret = PTR_ERR(sgt); goto fail_detach; } buffer = kzalloc(sizeof(*buffer), GFP_KERNEL); if (!buffer) { DRM_ERROR("failed to allocate exynos_drm_gem_buf.\n"); ret = -ENOMEM; goto fail_unmap; } exynos_gem_obj = exynos_drm_gem_init(drm_dev, dmabuf->size); if (!exynos_gem_obj) { ret = -ENOMEM; goto fail_unmap; } ret = drm_gem_handle_create(file, &exynos_gem_obj->base, handle); if (ret < 0) goto fail_handle; /* consider physically non-continuous memory with IOMMU. */ buffer->dma_addr = sg_dma_address(sgt->sgl); buffer->size = sg_dma_len(sgt->sgl); buffer->sgt = sgt; /* * import(fd to handle) means that the physical memory region * from the sgt is being shared with others so shared_refcount * should be 1. */ atomic_set(&buffer->shared_refcount, 1); exynos_gem_obj->base.import_attach = attach; ret = drm_prime_insert_fd_handle_mapping(&file_priv->prime, dmabuf, *handle); if (ret < 0) goto fail_handle; /* register buffer information to private buffer manager. */ ret = register_buf_to_priv_mgr(exynos_gem_obj, &exynos_gem_obj->priv_handle, &exynos_gem_obj->priv_id); if (ret < 0) { drm_prime_remove_fd_handle_mapping(&file_priv->prime, dmabuf); goto fail_handle; } DRM_DEBUG_KMS("fd = %d, handle = %d, dma_addr = 0x%x, size = 0x%lx\n", prime_fd, *handle, buffer->dma_addr, buffer->size); drm_gem_object_unreference(&exynos_gem_obj->base); mutex_unlock(&drm_dev->struct_mutex); return 0; fail_handle: drm_gem_object_unreference(&exynos_gem_obj->base); kfree(buffer); drm_gem_object_release(&exynos_gem_obj->base); kfree(exynos_gem_obj); fail_unmap: dma_buf_unmap_attachment(attach, sgt); fail_detach: dma_buf_detach(dmabuf, attach); fail_put: dma_buf_put(dmabuf); out: mutex_unlock(&drm_dev->struct_mutex); return ret; }
static int radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *send, int send_bytes, u8 *recv, int recv_size, u8 delay, u8 *ack) { struct drm_device *dev = chan->dev; struct radeon_device *rdev = dev->dev_private; union aux_channel_transaction args; int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction); unsigned char *base; int recv_bytes; int r = 0; memset(&args, 0, sizeof(args)); mutex_lock(&chan->mutex); base = (unsigned char *)(rdev->mode_info.atom_context->scratch + 1); radeon_atom_copy_swap(base, send, send_bytes, true); args.v1.lpAuxRequest = cpu_to_le16((u16)(0 + 4)); args.v1.lpDataOut = cpu_to_le16((u16)(16 + 4)); args.v1.ucDataOutLen = 0; args.v1.ucChannelID = chan->rec.i2c_id; args.v1.ucDelay = delay / 10; if (ASIC_IS_DCE4(rdev)) args.v2.ucHPD_ID = chan->rec.hpd; atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args); *ack = args.v1.ucReplyStatus; /* timeout */ if (args.v1.ucReplyStatus == 1) { DRM_DEBUG_KMS("dp_aux_ch timeout\n"); r = -ETIMEDOUT; goto done; } /* flags not zero */ if (args.v1.ucReplyStatus == 2) { DRM_DEBUG_KMS("dp_aux_ch flags not zero\n"); r = -EBUSY; goto done; } /* error */ if (args.v1.ucReplyStatus == 3) { DRM_DEBUG_KMS("dp_aux_ch error\n"); r = -EIO; goto done; } recv_bytes = args.v1.ucDataOutLen; if (recv_bytes > recv_size) recv_bytes = recv_size; if (recv && recv_size) radeon_atom_copy_swap(recv, base + 16, recv_bytes, false); r = recv_bytes; done: mutex_unlock(&chan->mutex); return r; }
static int rotator_ippdrv_check_property(struct device *dev, struct drm_exynos_ipp_property *property) { struct drm_exynos_ipp_config *src_config = &property->config[EXYNOS_DRM_OPS_SRC]; struct drm_exynos_ipp_config *dst_config = &property->config[EXYNOS_DRM_OPS_DST]; struct drm_exynos_pos *src_pos = &src_config->pos; struct drm_exynos_pos *dst_pos = &dst_config->pos; struct drm_exynos_sz *src_sz = &src_config->sz; struct drm_exynos_sz *dst_sz = &dst_config->sz; bool swap = false; /* Check format configuration */ if (src_config->fmt != dst_config->fmt) { DRM_DEBUG_KMS("not support csc feature\n"); return -EINVAL; } if (!rotator_check_drm_fmt(dst_config->fmt)) { DRM_DEBUG_KMS("invalid format\n"); return -EINVAL; } /* Check transform configuration */ if (src_config->degree != EXYNOS_DRM_DEGREE_0) { DRM_DEBUG_KMS("not support source-side rotation\n"); return -EINVAL; } switch (dst_config->degree) { case EXYNOS_DRM_DEGREE_90: case EXYNOS_DRM_DEGREE_270: swap = true; case EXYNOS_DRM_DEGREE_0: case EXYNOS_DRM_DEGREE_180: /* No problem */ break; default: DRM_DEBUG_KMS("invalid degree\n"); return -EINVAL; } if (src_config->flip != EXYNOS_DRM_FLIP_NONE) { DRM_DEBUG_KMS("not support source-side flip\n"); return -EINVAL; } if (!rotator_check_drm_flip(dst_config->flip)) { DRM_DEBUG_KMS("invalid flip\n"); return -EINVAL; } /* Check size configuration */ if ((src_pos->x + src_pos->w > src_sz->hsize) || (src_pos->y + src_pos->h > src_sz->vsize)) { DRM_DEBUG_KMS("out of source buffer bound\n"); return -EINVAL; } if (swap) { if ((dst_pos->x + dst_pos->h > dst_sz->vsize) || (dst_pos->y + dst_pos->w > dst_sz->hsize)) { DRM_DEBUG_KMS("out of destination buffer bound\n"); return -EINVAL; } if ((src_pos->w != dst_pos->h) || (src_pos->h != dst_pos->w)) { DRM_DEBUG_KMS("not support scale feature\n"); return -EINVAL; } } else { if ((dst_pos->x + dst_pos->w > dst_sz->hsize) || (dst_pos->y + dst_pos->h > dst_sz->vsize)) { DRM_DEBUG_KMS("out of destination buffer bound\n"); return -EINVAL; } if ((src_pos->w != dst_pos->w) || (src_pos->h != dst_pos->h)) { DRM_DEBUG_KMS("not support scale feature\n"); return -EINVAL; } } return 0; }
void intel_hdmi_init(struct drm_device *dev, int sdvox_reg) { struct drm_i915_private *dev_priv = dev->dev_private; struct drm_connector *connector; struct intel_output *intel_output; struct intel_hdmi_priv *hdmi_priv; if (!hdmi_is_present_in_vbt(dev, sdvox_reg)) { DRM_DEBUG_KMS("HDMI is not present. Ignored it \n"); return; } intel_output = kcalloc(sizeof(struct intel_output) + sizeof(struct intel_hdmi_priv), 1, GFP_KERNEL); if (!intel_output) return; hdmi_priv = (struct intel_hdmi_priv *)(intel_output + 1); connector = &intel_output->base; drm_connector_init(dev, connector, &intel_hdmi_connector_funcs, DRM_MODE_CONNECTOR_HDMIA); drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs); intel_output->type = INTEL_OUTPUT_HDMI; connector->interlace_allowed = 0; connector->doublescan_allowed = 0; intel_output->crtc_mask = (1 << 0) | (1 << 1); /* Set up the DDC bus. */ if (sdvox_reg == SDVOB) { intel_output->clone_mask = (1 << INTEL_HDMIB_CLONE_BIT); intel_output->ddc_bus = intel_i2c_create(dev, GPIOE, "HDMIB"); dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; } else if (sdvox_reg == SDVOC) { intel_output->clone_mask = (1 << INTEL_HDMIC_CLONE_BIT); intel_output->ddc_bus = intel_i2c_create(dev, GPIOD, "HDMIC"); dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMIB) { intel_output->clone_mask = (1 << INTEL_HDMID_CLONE_BIT); intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOE, "HDMIB"); dev_priv->hotplug_supported_mask |= HDMIB_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMIC) { intel_output->clone_mask = (1 << INTEL_HDMIE_CLONE_BIT); intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOD, "HDMIC"); dev_priv->hotplug_supported_mask |= HDMIC_HOTPLUG_INT_STATUS; } else if (sdvox_reg == HDMID) { intel_output->clone_mask = (1 << INTEL_HDMIF_CLONE_BIT); intel_output->ddc_bus = intel_i2c_create(dev, PCH_GPIOF, "HDMID"); dev_priv->hotplug_supported_mask |= HDMID_HOTPLUG_INT_STATUS; } if (!intel_output->ddc_bus) goto err_connector; hdmi_priv->sdvox_reg = sdvox_reg; intel_output->dev_priv = hdmi_priv; drm_encoder_init(dev, &intel_output->enc, &intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS); drm_encoder_helper_add(&intel_output->enc, &intel_hdmi_helper_funcs); drm_mode_connector_attach_encoder(&intel_output->base, &intel_output->enc); drm_sysfs_connector_add(connector); /* 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 * generated on the port when a cable is not attached. */ if (IS_G4X(dev) && !IS_GM45(dev)) { u32 temp = I915_READ(PEG_BAND_GAP_DATA); I915_WRITE(PEG_BAND_GAP_DATA, (temp & ~0xf) | 0xd); } return; err_connector: drm_connector_cleanup(connector); kfree(intel_output); return; }
static int rotator_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rot_context *rot; struct exynos_drm_ippdrv *ippdrv; int ret; rot = devm_kzalloc(dev, sizeof(*rot), GFP_KERNEL); if (!rot) { dev_err(dev, "failed to allocate rot\n"); return -ENOMEM; } rot->limit_tbl = (struct rot_limit_table *) platform_get_device_id(pdev)->driver_data; rot->regs_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); rot->regs = devm_ioremap_resource(dev, rot->regs_res); if (IS_ERR(rot->regs)) return PTR_ERR(rot->regs); rot->irq = platform_get_irq(pdev, 0); if (rot->irq < 0) { dev_err(dev, "failed to get irq\n"); return rot->irq; } ret = devm_request_threaded_irq(dev, rot->irq, NULL, rotator_irq_handler, IRQF_ONESHOT, "drm_rotator", rot); if (ret < 0) { dev_err(dev, "failed to request irq\n"); return ret; } rot->clock = devm_clk_get(dev, "rotator"); if (IS_ERR(rot->clock)) { dev_err(dev, "failed to get clock\n"); return PTR_ERR(rot->clock); } pm_runtime_enable(dev); ippdrv = &rot->ippdrv; ippdrv->dev = dev; ippdrv->ops[EXYNOS_DRM_OPS_SRC] = &rot_src_ops; ippdrv->ops[EXYNOS_DRM_OPS_DST] = &rot_dst_ops; ippdrv->check_property = rotator_ippdrv_check_property; ippdrv->start = rotator_ippdrv_start; ret = rotator_init_prop_list(ippdrv); if (ret < 0) { dev_err(dev, "failed to init property list.\n"); goto err_ippdrv_register; } DRM_DEBUG_KMS("ippdrv[0x%x]\n", (int)ippdrv); platform_set_drvdata(pdev, rot); ret = exynos_drm_ippdrv_register(ippdrv); if (ret < 0) { dev_err(dev, "failed to register drm rotator device\n"); goto err_ippdrv_register; } dev_info(dev, "The exynos rotator is probed successfully\n"); return 0; err_ippdrv_register: pm_runtime_disable(dev); return ret; }
static int intelfb_create(struct intel_fbdev *ifbdev, struct drm_fb_helper_surface_size *sizes) { struct drm_device *dev = ifbdev->helper.dev; struct drm_i915_private *dev_priv = dev->dev_private; #if 0 struct fb_info *info; #endif struct drm_framebuffer *fb; struct drm_mode_fb_cmd2 mode_cmd = {}; struct drm_i915_gem_object *obj; int size, ret; /* we don't do packed 24bpp */ if (sizes->surface_bpp == 24) sizes->surface_bpp = 32; mode_cmd.width = sizes->surface_width; mode_cmd.height = sizes->surface_height; mode_cmd.pitches[0] = roundup2(mode_cmd.width * ((sizes->surface_bpp + 7) / 8), 64); mode_cmd.pixel_format = drm_mode_legacy_fb_format(sizes->surface_bpp, sizes->surface_depth); size = mode_cmd.pitches[0] * mode_cmd.height; size = roundup2(size, PAGE_SIZE); obj = i915_gem_alloc_object(dev, size); if (!obj) { DRM_ERROR("failed to allocate framebuffer\n"); ret = -ENOMEM; goto out; } DRM_LOCK(); /* Flush everything out, we'll be doing GTT only from now on */ ret = intel_pin_and_fence_fb_obj(dev, obj, false); if (ret) { DRM_ERROR("failed to pin fb: %d\n", ret); goto out_unref; } #if 0 info = framebuffer_alloc(0, device); if (!info) { ret = -ENOMEM; goto out_unpin; } info->par = ifbdev; #endif ret = intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, obj); if (ret) goto out_unpin; fb = &ifbdev->ifb.base; ifbdev->helper.fb = fb; #if 0 ifbdev->helper.fbdev = info; strlcpy(info->fix.id, "inteldrmfb", sizeof(info->fix.id)); info->flags = FBINFO_DEFAULT | FBINFO_CAN_FORCE_OUTPUT; info->fbops = &intelfb_ops; ret = fb_alloc_cmap(&info->cmap, 256, 0); if (ret) { ret = -ENOMEM; goto out_unpin; } /* setup aperture base/size for vesafb takeover */ info->apertures = alloc_apertures(1); if (!info->apertures) { ret = -ENOMEM; goto out_unpin; } info->apertures->ranges[0].base = dev->mode_config.fb_base; info->apertures->ranges[0].size = dev_priv->mm.gtt->gtt_mappable_entries << PAGE_SHIFT; info->fix.smem_start = dev->mode_config.fb_base + obj->gtt_offset; info->fix.smem_len = size; info->screen_base = ioremap_wc(dev->agp->base + obj->gtt_offset, size); if (!info->screen_base) { ret = -ENOSPC; goto out_unpin; } info->screen_size = size; // memset(info->screen_base, 0, size); drm_fb_helper_fill_fix(info, fb->pitches[0], fb->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ #else { struct rasops_info *ri = &dev_priv->ro; bus_space_handle_t bsh; int err; err = agp_map_subregion(dev_priv->agph, obj->gtt_offset, size, &bsh); if (err) { ret = -err; goto out_unpin; } ri->ri_bits = bus_space_vaddr(dev->bst, bsh); ri->ri_depth = fb->bits_per_pixel; ri->ri_stride = fb->pitches[0]; ri->ri_width = sizes->fb_width; ri->ri_height = sizes->fb_height; switch (fb->pixel_format) { case DRM_FORMAT_XRGB8888: ri->ri_rnum = 8; ri->ri_rpos = 16; ri->ri_gnum = 8; ri->ri_gpos = 8; ri->ri_bnum = 8; ri->ri_bpos = 0; break; case DRM_FORMAT_RGB565: ri->ri_rnum = 5; ri->ri_rpos = 11; ri->ri_gnum = 6; ri->ri_gpos = 5; ri->ri_bnum = 5; ri->ri_bpos = 0; break; } } #endif DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n", fb->width, fb->height, obj->gtt_offset, obj); DRM_UNLOCK(); #if 1 DRM_DEBUG_KMS("skipping call to vga_switcheroo_client_fb_set\n"); #else vga_switcheroo_client_fb_set(dev->pdev, info); #endif return 0; out_unpin: i915_gem_object_unpin(obj); out_unref: drm_gem_object_unreference(&obj->base); DRM_UNLOCK(); out: return ret; }
static void intel_dsi_prepare(struct intel_encoder *intel_encoder) { struct drm_encoder *encoder = &intel_encoder->base; struct drm_device *dev = encoder->dev; struct drm_i915_private *dev_priv = dev->dev_private; struct intel_crtc *intel_crtc = to_intel_crtc(encoder->crtc); struct intel_dsi *intel_dsi = enc_to_intel_dsi(encoder); struct drm_display_mode *adjusted_mode = &intel_crtc->config.adjusted_mode; int pipe = intel_crtc->pipe; unsigned int bpp = intel_crtc->config.pipe_bpp; u32 val, tmp; DRM_DEBUG_KMS("pipe %c\n", pipe_name(pipe)); /* escape clock divider, 20MHz, shared for A and C. device ready must be * off when doing this! txclkesc? */ tmp = I915_READ(MIPI_CTRL(0)); tmp &= ~ESCAPE_CLOCK_DIVIDER_MASK; I915_WRITE(MIPI_CTRL(0), tmp | ESCAPE_CLOCK_DIVIDER_1); /* read request priority is per pipe */ tmp = I915_READ(MIPI_CTRL(pipe)); tmp &= ~READ_REQUEST_PRIORITY_MASK; I915_WRITE(MIPI_CTRL(pipe), tmp | READ_REQUEST_PRIORITY_HIGH); /* XXX: why here, why like this? handling in irq handler?! */ I915_WRITE(MIPI_INTR_STAT(pipe), 0xffffffff); I915_WRITE(MIPI_INTR_EN(pipe), 0xffffffff); I915_WRITE(MIPI_DPHY_PARAM(pipe), intel_dsi->dphy_reg); I915_WRITE(MIPI_DPI_RESOLUTION(pipe), adjusted_mode->vdisplay << VERTICAL_ADDRESS_SHIFT | adjusted_mode->hdisplay << HORIZONTAL_ADDRESS_SHIFT); set_dsi_timings(encoder, adjusted_mode); val = intel_dsi->lane_count << DATA_LANES_PRG_REG_SHIFT; if (is_cmd_mode(intel_dsi)) { val |= intel_dsi->channel << CMD_MODE_CHANNEL_NUMBER_SHIFT; val |= CMD_MODE_DATA_WIDTH_8_BIT; /* XXX */ } else { val |= intel_dsi->channel << VID_MODE_CHANNEL_NUMBER_SHIFT; /* XXX: cross-check bpp vs. pixel format? */ val |= intel_dsi->pixel_format; } I915_WRITE(MIPI_DSI_FUNC_PRG(pipe), val); /* timeouts for recovery. one frame IIUC. if counter expires, EOT and * stop state. */ /* * In burst mode, value greater than one DPI line Time in byte clock * (txbyteclkhs) To timeout this timer 1+ of the above said value is * recommended. * * In non-burst mode, Value greater than one DPI frame time in byte * clock(txbyteclkhs) To timeout this timer 1+ of the above said value * is recommended. * * In DBI only mode, value greater than one DBI frame time in byte * clock(txbyteclkhs) To timeout this timer 1+ of the above said value * is recommended. */ if (is_vid_mode(intel_dsi) && intel_dsi->video_mode_format == VIDEO_MODE_BURST) { I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe), txbyteclkhs(adjusted_mode->htotal, bpp, intel_dsi->lane_count, intel_dsi->burst_mode_ratio) + 1); } else { I915_WRITE(MIPI_HS_TX_TIMEOUT(pipe), txbyteclkhs(adjusted_mode->vtotal * adjusted_mode->htotal, bpp, intel_dsi->lane_count, intel_dsi->burst_mode_ratio) + 1); } I915_WRITE(MIPI_LP_RX_TIMEOUT(pipe), intel_dsi->lp_rx_timeout); I915_WRITE(MIPI_TURN_AROUND_TIMEOUT(pipe), intel_dsi->turn_arnd_val); I915_WRITE(MIPI_DEVICE_RESET_TIMER(pipe), intel_dsi->rst_timer_val); /* dphy stuff */ /* in terms of low power clock */ I915_WRITE(MIPI_INIT_COUNT(pipe), txclkesc(intel_dsi->escape_clk_div, 100)); val = 0; if (intel_dsi->eotp_pkt == 0) val |= EOT_DISABLE; if (intel_dsi->clock_stop) val |= CLOCKSTOP; /* recovery disables */ I915_WRITE(MIPI_EOT_DISABLE(pipe), val); /* in terms of low power clock */ I915_WRITE(MIPI_INIT_COUNT(pipe), intel_dsi->init_count); /* in terms of txbyteclkhs. actual high to low switch + * MIPI_STOP_STATE_STALL * MIPI_LP_BYTECLK. * * XXX: write MIPI_STOP_STATE_STALL? */ I915_WRITE(MIPI_HIGH_LOW_SWITCH_COUNT(pipe), intel_dsi->hs_to_lp_count); /* XXX: low power clock equivalence in terms of byte clock. the number * of byte clocks occupied in one low power clock. based on txbyteclkhs * and txclkesc. txclkesc time / txbyteclk time * (105 + * MIPI_STOP_STATE_STALL) / 105.??? */ I915_WRITE(MIPI_LP_BYTECLK(pipe), intel_dsi->lp_byte_clk); /* the bw essential for transmitting 16 long packets containing 252 * bytes meant for dcs write memory command is programmed in this * register in terms of byte clocks. based on dsi transfer rate and the * number of lanes configured the time taken to transmit 16 long packets * in a dsi stream varies. */ I915_WRITE(MIPI_DBI_BW_CTRL(pipe), intel_dsi->bw_timer); I915_WRITE(MIPI_CLK_LANE_SWITCH_TIME_CNT(pipe), intel_dsi->clk_lp_to_hs_count << LP_HS_SSW_CNT_SHIFT | intel_dsi->clk_hs_to_lp_count << HS_LP_PWR_SW_CNT_SHIFT); if (is_vid_mode(intel_dsi)) /* Some panels might have resolution which is not a multiple of * 64 like 1366 x 768. Enable RANDOM resolution support for such * panels by default */ I915_WRITE(MIPI_VIDEO_MODE_FORMAT(pipe), intel_dsi->video_frmt_cfg_bits | intel_dsi->video_mode_format | IP_TG_CONFIG | RANDOM_DPI_DISPLAY_RESOLUTION); }