zl程序教程

您现在的位置是:首页 >  系统

当前栏目

linux内核radeon gpu源码解析7 —— radeon_driver_load_kms函数详解2

Linux内核源码 详解 函数 解析 GPU load
2023-09-14 09:15:40 时间

继续在radeon_driver_load_kms函数中徜徉。本篇解析radeon_driver_load_kms函数中的两大核心函数之一:radeon_device_init。

调用处代码:

r = radeon_device_init(rdev, dev, dev->pdev, flags);

源码如下:

drivers/gpu/drm/radeon/radeon_device.c

/**
 * radeon_device_init - initialize the driver
 *
 * @rdev: radeon_device pointer
 * @pdev: drm dev pointer
 * @pdev: pci dev pointer
 * @flags: driver flags
 *
 * Initializes the driver info and hw (all asics).
 * Returns 0 for success or an error on failure.
 * Called at driver startup.
 */
int radeon_device_init(struct radeon_device *rdev,
		       struct drm_device *ddev,
		       struct pci_dev *pdev,
		       uint32_t flags)
{
	int r, i;
	int dma_bits;
	bool runtime = false;

	rdev->shutdown = false;
	rdev->dev = &pdev->dev;
	rdev->ddev = ddev;
	rdev->pdev = pdev;
	rdev->flags = flags;
	rdev->family = flags & RADEON_FAMILY_MASK;
	rdev->is_atom_bios = false;
	rdev->usec_timeout = RADEON_MAX_USEC_TIMEOUT;
	rdev->mc.gtt_size = 512 * 1024 * 1024;
	rdev->accel_working = false;
	/* set up ring ids */
	for (i = 0; i < RADEON_NUM_RINGS; i++) {
		rdev->ring[i].idx = i;
	}
	rdev->fence_context = dma_fence_context_alloc(RADEON_NUM_RINGS);

	DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X 0x%04X:0x%04X 0x%02X).\n",
		 radeon_family_name[rdev->family], pdev->vendor, pdev->device,
		 pdev->subsystem_vendor, pdev->subsystem_device, pdev->revision);

	/* mutex initialization are all done here so we
	 * can recall function without having locking issues */
	mutex_init(&rdev->ring_lock);
	mutex_init(&rdev->dc_hw_i2c_mutex);
	atomic_set(&rdev->ih.lock, 0);
	mutex_init(&rdev->gem.mutex);
	mutex_init(&rdev->pm.mutex);
	mutex_init(&rdev->gpu_clock_mutex);
	mutex_init(&rdev->srbm_mutex);
	init_rwsem(&rdev->pm.mclk_lock);
	init_rwsem(&rdev->exclusive_lock);
	init_waitqueue_head(&rdev->irq.vblank_queue);
	mutex_init(&rdev->mn_lock);
	hash_init(rdev->mn_hash);
	r = radeon_gem_init(rdev);
	if (r)
		return r;

	radeon_check_arguments(rdev);
	/* Adjust VM size here.
	 * Max GPUVM size for cayman+ is 40 bits.
	 */
	rdev->vm_manager.max_pfn = radeon_vm_size << 18;

	/* Set asic functions */
	r = radeon_asic_setup(rdev);
	if (r)
		return r;

	/* all of the newer IGP chips have an internal gart
	 * However some rs4xx report as AGP, so remove that here.
	 */
	if ((rdev->family >= CHIP_RS400) &&
	    (rdev->flags & RADEON_IS_IGP)) {
		rdev->flags &= ~RADEON_IS_AGP;
	}

	if (rdev->flags & RADEON_IS_AGP && radeon_agpmode == -1) {
		radeon_agp_disable(rdev);
	}

	/* Set the internal MC address mask
	 * This is the max address of the GPU's
	 * internal address space.
	 */
	if (rdev->family >= CHIP_CAYMAN)
		rdev->mc.mc_mask = 0xffffffffffULL; /* 40 bit MC */
	else if (rdev->family >= CHIP_CEDAR)
		rdev->mc.mc_mask = 0xfffffffffULL; /* 36 bit MC */
	else
		rdev->mc.mc_mask = 0xffffffffULL; /* 32 bit MC */

	/* set DMA mask + need_dma32 flags.
	 * PCIE - can handle 40-bits.
	 * IGP - can handle 40-bits
	 * AGP - generally dma32 is safest
	 * PCI - dma32 for legacy pci gart, 40 bits on newer asics
	 */
	rdev->need_dma32 = false;
	if (rdev->flags & RADEON_IS_AGP)
		rdev->need_dma32 = true;
	if ((rdev->flags & RADEON_IS_PCI) &&
	    (rdev->family <= CHIP_RS740))
		rdev->need_dma32 = true;
#ifdef CONFIG_PPC64
	if (rdev->family == CHIP_CEDAR)
		rdev->need_dma32 = true;
#endif

	dma_bits = rdev->need_dma32 ? 32 : 40;
	r = pci_set_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits));
	if (r) {
		rdev->need_dma32 = true;
		dma_bits = 32;
		pr_warn("radeon: No suitable DMA available\n");
	}
	r = pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(dma_bits));
	if (r) {
		pci_set_consistent_dma_mask(rdev->pdev, DMA_BIT_MASK(32));
		pr_warn("radeon: No coherent DMA available\n");
	}
	rdev->need_swiotlb = drm_get_max_iomem() > ((u64)1 << dma_bits);

	/* Registers mapping */
	/* TODO: block userspace mapping of io register */
	spin_lock_init(&rdev->mmio_idx_lock);
	spin_lock_init(&rdev->smc_idx_lock);
	spin_lock_init(&rdev->pll_idx_lock);
	spin_lock_init(&rdev->mc_idx_lock);
	spin_lock_init(&rdev->pcie_idx_lock);
	spin_lock_init(&rdev->pciep_idx_lock);
	spin_lock_init(&rdev->pif_idx_lock);
	spin_lock_init(&rdev->cg_idx_lock);
	spin_lock_init(&rdev->uvd_idx_lock);
	spin_lock_init(&rdev->rcu_idx_lock);
	spin_lock_init(&rdev->didt_idx_lock);
	spin_lock_init(&rdev->end_idx_lock);
	if (rdev->family >= CHIP_BONAIRE) {
		rdev->rmmio_base = pci_resource_start(rdev->pdev, 5);
		rdev->rmmio_size = pci_resource_len(rdev->pdev, 5);
	} else {
		rdev->rmmio_base = pci_resource_start(rdev->pdev, 2);
		rdev->rmmio_size = pci_resource_len(rdev->pdev, 2);
	}
	rdev->rmmio = ioremap(rdev->rmmio_base, rdev->rmmio_size);
	if (rdev->rmmio == NULL)
		return -ENOMEM;

	/* doorbell bar mapping */
	if (rdev->family >= CHIP_BONAIRE)
		radeon_doorbell_init(rdev);

	/* io port mapping */
	for (i = 0; i < DEVICE_COUNT_RESOURCE; i++) {
		if (pci_resource_flags(rdev->pdev, i) & IORESOURCE_IO) {
			rdev->rio_mem_size = pci_resource_len(rdev->pdev, i);
			rdev->rio_mem = pci_iomap(rdev->pdev, i, rdev->rio_mem_size);
			break;
		}
	}
	if (rdev->rio_mem == NULL)
		DRM_ERROR("Unable to find PCI I/O BAR\n");

	if (rdev->flags & RADEON_IS_PX)
		radeon_device_handle_px_quirks(rdev);

	/* if we have > 1 VGA cards, then disable the radeon VGA resources */
	/* this will fail for cards that aren't VGA class devices, just
	 * ignore it */
	vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);

	if (rdev->flags & RADEON_IS_PX)
		runtime = true;
	if (!pci_is_thunderbolt_attached(rdev->pdev))
		vga_switcheroo_register_client(rdev->pdev,
					       &radeon_switcheroo_ops, runtime);
	if (runtime)
		vga_switcheroo_init_domain_pm_ops(rdev->dev, &rdev->vga_pm_domain);

	r = radeon_asic_init(rdev);
	if (r)
		goto failed;

	r = radeon_gem_debugfs_init(rdev);
	if (r) {
		DRM_ERROR("registering gem debugfs failed (%d).\n", r);
	}

	r = radeon_mst_debugfs_init(rdev);
	if (r) {
		DRM_ERROR("registering mst debugfs failed (%d).\n", r);
	}

	if (rdev->flags & RADEON_IS_AGP && !rdev->accel_working) {
		/* Acceleration not working on AGP card try again
		 * with fallback to PCI or PCIE GART
		 */
		radeon_asic_reset(rdev);
		radeon_asic_fini(rdev);
		radeon_agp_disable(rdev);
		r = radeon_asic_init(rdev);
		if (r)
			goto failed;
	}

	r = radeon_ib_ring_tests(rdev);
	if (r)
		DRM_ERROR("ib ring test failed (%d).\n", r);

	/*
	 * Turks/Thames GPU will freeze whole laptop if DPM is not restarted
	 * after the CP ring have chew one packet at least. Hence here we stop
	 * and restart DPM after the radeon_ib_ring_tests().
	 */
	if (rdev->pm.dpm_enabled &&
	    (rdev->pm.pm_method == PM_METHOD_DPM) &&
	    (rdev->family == CHIP_TURKS) &&
	    (rdev->flags & RADEON_IS_MOBILITY)) {
		mutex_lock(&rdev->pm.mutex);
		radeon_dpm_disable(rdev);
		radeon_dpm_enable(rdev);
		mutex_unlock(&rdev->pm.mutex);
	}

	if ((radeon_testing & 1)) {
		if (rdev->accel_working)
			radeon_test_moves(rdev);
		else
			DRM_INFO("radeon: acceleration disabled, skipping move tests\n");
	}
	if ((radeon_testing & 2)) {
		if (rdev->accel_working)
			radeon_test_syncing(rdev);
		else
			DRM_INFO("radeon: acceleration disabled, skipping sync tests\n");
	}
	if (radeon_benchmarking) {
		if (rdev->accel_working)
			radeon_benchmark(rdev, radeon_benchmarking);
		else
			DRM_INFO("radeon: acceleration disabled, skipping benchmarks\n");
	}
	return 0;

failed:
	/* balance pm_runtime_get_sync() in radeon_driver_unload_kms() */
	if (radeon_is_px(ddev))
		pm_runtime_put_noidle(ddev->dev);
	if (runtime)
		vga_switcheroo_fini_domain_pm_ops(rdev->dev);
	return r;
}

前文已说过,radeon_device_init函数的作用是初始化芯片的非显示部分(asic init, CP, writeback等)。

这个函数首先会初始化一大堆的驱动需要使用的结构,然后调用radeon_asic_init(),这个函数用于设置电路相关的一些函数指针,比如睡眠/恢复调用,硬件重置,设置和处理中断请求,设置和获取时钟等等。