drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c

Source file repositories/reference/linux-study-clean/drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c

File Facts

System
Linux kernel
Corpus path
drivers/media/platform/samsung/s5p-mfc/s5p_mfc.c
Extension
.c
Size
48911 bytes
Lines
1754
Domain
Driver Families
Bucket
drivers/media
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

if (device_add(child) == 0) {
		ret = of_reserved_mem_device_init_by_idx(child, dev->of_node,
							 idx);
		if (ret == 0)
			return child;
		device_del(child);
	}
err:
	put_device(child);
	return NULL;
}

static int s5p_mfc_configure_2port_memory(struct s5p_mfc_dev *mfc_dev)
{
	struct device *dev = &mfc_dev->plat_dev->dev;
	void *bank2_virt;
	dma_addr_t bank2_dma_addr;
	unsigned long align_size = 1 << MFC_BASE_ALIGN_ORDER;
	int ret;

	/*
	 * Create and initialize virtual devices for accessing
	 * reserved memory regions.
	 */
	mfc_dev->mem_dev[BANK_L_CTX] = s5p_mfc_alloc_memdev(dev, "left",
							   BANK_L_CTX);
	if (!mfc_dev->mem_dev[BANK_L_CTX])
		return -ENODEV;
	mfc_dev->mem_dev[BANK_R_CTX] = s5p_mfc_alloc_memdev(dev, "right",
							   BANK_R_CTX);
	if (!mfc_dev->mem_dev[BANK_R_CTX]) {
		device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
		return -ENODEV;
	}

	/* Allocate memory for firmware and initialize both banks addresses */
	ret = s5p_mfc_alloc_firmware(mfc_dev);
	if (ret) {
		device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
		device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
		return ret;
	}

	mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->fw_buf.dma;

	bank2_virt = dma_alloc_coherent(mfc_dev->mem_dev[BANK_R_CTX],
				       align_size, &bank2_dma_addr, GFP_KERNEL);
	if (!bank2_virt) {
		s5p_mfc_release_firmware(mfc_dev);
		device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
		device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
		return -ENOMEM;
	}

	/* Valid buffers passed to MFC encoder with LAST_FRAME command
	 * should not have address of bank2 - MFC will treat it as a null frame.
	 * To avoid such situation we set bank2 address below the pool address.
	 */
	mfc_dev->dma_base[BANK_R_CTX] = bank2_dma_addr - align_size;

	dma_free_coherent(mfc_dev->mem_dev[BANK_R_CTX], align_size, bank2_virt,
			  bank2_dma_addr);

	vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX],
					DMA_BIT_MASK(32));
	vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX],
					DMA_BIT_MASK(32));

	return 0;
}

static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev)
{
	device_unregister(mfc_dev->mem_dev[BANK_L_CTX]);
	device_unregister(mfc_dev->mem_dev[BANK_R_CTX]);
	vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX]);
	vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX]);
}

static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev)
{
	struct device *dev = &mfc_dev->plat_dev->dev;
	unsigned long mem_size = SZ_4M;

	if (IS_ENABLED(CONFIG_DMA_CMA) || exynos_is_iommu_available(dev))
		mem_size = SZ_8M;

	if (mfc_mem_size)
		mem_size = memparse(mfc_mem_size, NULL);

Annotation

Implementation Notes