int knav_init_acc_range()

in ti/knav_qmss_acc.c [471:583]


int knav_init_acc_range(struct knav_device *kdev,
			struct device_node *node,
			struct knav_range_info *range)
{
	struct knav_acc_channel *acc;
	struct knav_pdsp_info *pdsp;
	struct knav_acc_info *info;
	int ret, channel, channels;
	int list_size, mem_size;
	dma_addr_t list_dma;
	void *list_mem;
	u32 config[5];

	range->flags |= RANGE_HAS_ACCUMULATOR;
	info = &range->acc_info;

	ret = of_property_read_u32_array(node, "accumulator", config, 5);
	if (ret)
		return ret;

	info->pdsp_id		= config[0];
	info->start_channel	= config[1];
	info->list_entries	= config[2];
	info->pacing_mode	= config[3];
	info->timer_count	= config[4] / ACC_DEFAULT_PERIOD;

	if (info->start_channel > ACC_MAX_CHANNEL) {
		dev_err(kdev->dev, "channel %d invalid for range %s\n",
			info->start_channel, range->name);
		return -EINVAL;
	}

	if (info->pacing_mode > 3) {
		dev_err(kdev->dev, "pacing mode %d invalid for range %s\n",
			info->pacing_mode, range->name);
		return -EINVAL;
	}

	pdsp = knav_find_pdsp(kdev, info->pdsp_id);
	if (!pdsp) {
		dev_err(kdev->dev, "pdsp id %d not found for range %s\n",
			info->pdsp_id, range->name);
		return -EINVAL;
	}

	if (!pdsp->started) {
		dev_err(kdev->dev, "pdsp id %d not started for range %s\n",
			info->pdsp_id, range->name);
		return -ENODEV;
	}

	info->pdsp = pdsp;
	channels = range->num_queues;
	if (of_get_property(node, "multi-queue", NULL)) {
		range->flags |= RANGE_MULTI_QUEUE;
		channels = 1;
		if (range->queue_base & (32 - 1)) {
			dev_err(kdev->dev,
				"misaligned multi-queue accumulator range %s\n",
				range->name);
			return -EINVAL;
		}
		if (range->num_queues > 32) {
			dev_err(kdev->dev,
				"too many queues in accumulator range %s\n",
				range->name);
			return -EINVAL;
		}
	}

	/* figure out list size */
	list_size  = info->list_entries;
	list_size *= ACC_LIST_ENTRY_WORDS * sizeof(u32);
	info->list_size = list_size;
	mem_size   = PAGE_ALIGN(list_size * 2);
	info->mem_size  = mem_size;
	range->acc = devm_kcalloc(kdev->dev, channels, sizeof(*range->acc),
				  GFP_KERNEL);
	if (!range->acc)
		return -ENOMEM;

	for (channel = 0; channel < channels; channel++) {
		acc = range->acc + channel;
		acc->channel = info->start_channel + channel;

		/* allocate memory for the two lists */
		list_mem = alloc_pages_exact(mem_size, GFP_KERNEL | GFP_DMA);
		if (!list_mem)
			return -ENOMEM;

		list_dma = dma_map_single(kdev->dev, list_mem, mem_size,
					  DMA_BIDIRECTIONAL);
		if (dma_mapping_error(kdev->dev, list_dma)) {
			free_pages_exact(list_mem, mem_size);
			return -ENOMEM;
		}

		memset(list_mem, 0, mem_size);
		dma_sync_single_for_device(kdev->dev, list_dma, mem_size,
					   DMA_TO_DEVICE);
		scnprintf(acc->name, sizeof(acc->name), "hwqueue-acc-%d",
			  acc->channel);
		acc->list_cpu[0] = list_mem;
		acc->list_cpu[1] = list_mem + list_size;
		acc->list_dma[0] = list_dma;
		acc->list_dma[1] = list_dma + list_size;
		dev_dbg(kdev->dev, "%s: channel %d, dma %pad, virt %8p\n",
			acc->name, acc->channel, &list_dma, list_mem);
	}

	range->ops = &knav_acc_range_ops;
	return 0;
}