162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
462306a36Sopenharmony_ci * flexcop-dma.c - configuring and controlling the DMA of the FlexCop
562306a36Sopenharmony_ci * see flexcop.c for copyright information
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci#include "flexcop.h"
862306a36Sopenharmony_ci
962306a36Sopenharmony_ciint flexcop_dma_allocate(struct pci_dev *pdev,
1062306a36Sopenharmony_ci		struct flexcop_dma *dma, u32 size)
1162306a36Sopenharmony_ci{
1262306a36Sopenharmony_ci	u8 *tcpu;
1362306a36Sopenharmony_ci	dma_addr_t tdma = 0;
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci	if (size % 2) {
1662306a36Sopenharmony_ci		err("dma buffersize has to be even.");
1762306a36Sopenharmony_ci		return -EINVAL;
1862306a36Sopenharmony_ci	}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	tcpu = dma_alloc_coherent(&pdev->dev, size, &tdma, GFP_KERNEL);
2162306a36Sopenharmony_ci	if (tcpu != NULL) {
2262306a36Sopenharmony_ci		dma->pdev = pdev;
2362306a36Sopenharmony_ci		dma->cpu_addr0 = tcpu;
2462306a36Sopenharmony_ci		dma->dma_addr0 = tdma;
2562306a36Sopenharmony_ci		dma->cpu_addr1 = tcpu + size/2;
2662306a36Sopenharmony_ci		dma->dma_addr1 = tdma + size/2;
2762306a36Sopenharmony_ci		dma->size = size/2;
2862306a36Sopenharmony_ci		return 0;
2962306a36Sopenharmony_ci	}
3062306a36Sopenharmony_ci	return -ENOMEM;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_allocate);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_civoid flexcop_dma_free(struct flexcop_dma *dma)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	dma_free_coherent(&dma->pdev->dev, dma->size * 2, dma->cpu_addr0,
3762306a36Sopenharmony_ci			  dma->dma_addr0);
3862306a36Sopenharmony_ci	memset(dma, 0, sizeof(struct flexcop_dma));
3962306a36Sopenharmony_ci}
4062306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_free);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ciint flexcop_dma_config(struct flexcop_device *fc,
4362306a36Sopenharmony_ci		struct flexcop_dma *dma,
4462306a36Sopenharmony_ci		flexcop_dma_index_t dma_idx)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	flexcop_ibi_value v0x0, v0x4, v0xc;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	v0x0.raw = v0x4.raw = v0xc.raw = 0;
4962306a36Sopenharmony_ci	v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2;
5062306a36Sopenharmony_ci	v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2;
5162306a36Sopenharmony_ci	v0x4.dma_0x4_write.dma_addr_size = dma->size / 4;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if ((dma_idx & FC_DMA_1) == dma_idx) {
5462306a36Sopenharmony_ci		fc->write_ibi_reg(fc, dma1_000, v0x0);
5562306a36Sopenharmony_ci		fc->write_ibi_reg(fc, dma1_004, v0x4);
5662306a36Sopenharmony_ci		fc->write_ibi_reg(fc, dma1_00c, v0xc);
5762306a36Sopenharmony_ci	} else if ((dma_idx & FC_DMA_2) == dma_idx) {
5862306a36Sopenharmony_ci		fc->write_ibi_reg(fc, dma2_010, v0x0);
5962306a36Sopenharmony_ci		fc->write_ibi_reg(fc, dma2_014, v0x4);
6062306a36Sopenharmony_ci		fc->write_ibi_reg(fc, dma2_01c, v0xc);
6162306a36Sopenharmony_ci	} else {
6262306a36Sopenharmony_ci		err("either DMA1 or DMA2 can be configured within one %s call.",
6362306a36Sopenharmony_ci			__func__);
6462306a36Sopenharmony_ci		return -EINVAL;
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	return 0;
6862306a36Sopenharmony_ci}
6962306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_config);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci/* start the DMA transfers, but not the DMA IRQs */
7262306a36Sopenharmony_ciint flexcop_dma_xfer_control(struct flexcop_device *fc,
7362306a36Sopenharmony_ci		flexcop_dma_index_t dma_idx,
7462306a36Sopenharmony_ci		flexcop_dma_addr_index_t index,
7562306a36Sopenharmony_ci		int onoff)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	flexcop_ibi_value v0x0, v0xc;
7862306a36Sopenharmony_ci	flexcop_ibi_register r0x0, r0xc;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	if ((dma_idx & FC_DMA_1) == dma_idx) {
8162306a36Sopenharmony_ci		r0x0 = dma1_000;
8262306a36Sopenharmony_ci		r0xc = dma1_00c;
8362306a36Sopenharmony_ci	} else if ((dma_idx & FC_DMA_2) == dma_idx) {
8462306a36Sopenharmony_ci		r0x0 = dma2_010;
8562306a36Sopenharmony_ci		r0xc = dma2_01c;
8662306a36Sopenharmony_ci	} else {
8762306a36Sopenharmony_ci		err("transfer DMA1 or DMA2 can be started within one %s call.",
8862306a36Sopenharmony_ci			__func__);
8962306a36Sopenharmony_ci		return -EINVAL;
9062306a36Sopenharmony_ci	}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	v0x0 = fc->read_ibi_reg(fc, r0x0);
9362306a36Sopenharmony_ci	v0xc = fc->read_ibi_reg(fc, r0xc);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	deb_rdump("reg: %03x: %x\n", r0x0, v0x0.raw);
9662306a36Sopenharmony_ci	deb_rdump("reg: %03x: %x\n", r0xc, v0xc.raw);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	if (index & FC_DMA_SUBADDR_0)
9962306a36Sopenharmony_ci		v0x0.dma_0x0.dma_0start = onoff;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (index & FC_DMA_SUBADDR_1)
10262306a36Sopenharmony_ci		v0xc.dma_0xc.dma_1start = onoff;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	fc->write_ibi_reg(fc, r0x0, v0x0);
10562306a36Sopenharmony_ci	fc->write_ibi_reg(fc, r0xc, v0xc);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	deb_rdump("reg: %03x: %x\n", r0x0, v0x0.raw);
10862306a36Sopenharmony_ci	deb_rdump("reg: %03x: %x\n", r0xc, v0xc.raw);
10962306a36Sopenharmony_ci	return 0;
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_xfer_control);
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int flexcop_dma_remap(struct flexcop_device *fc,
11462306a36Sopenharmony_ci		flexcop_dma_index_t dma_idx,
11562306a36Sopenharmony_ci		int onoff)
11662306a36Sopenharmony_ci{
11762306a36Sopenharmony_ci	flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_00c : dma2_01c;
11862306a36Sopenharmony_ci	flexcop_ibi_value v = fc->read_ibi_reg(fc, r);
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	deb_info("%s\n", __func__);
12162306a36Sopenharmony_ci	v.dma_0xc.remap_enable = onoff;
12262306a36Sopenharmony_ci	fc->write_ibi_reg(fc, r, v);
12362306a36Sopenharmony_ci	return 0;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ciint flexcop_dma_control_size_irq(struct flexcop_device *fc,
12762306a36Sopenharmony_ci		flexcop_dma_index_t no,
12862306a36Sopenharmony_ci		int onoff)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	flexcop_ibi_value v = fc->read_ibi_reg(fc, ctrl_208);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	if (no & FC_DMA_1)
13362306a36Sopenharmony_ci		v.ctrl_208.DMA1_IRQ_Enable_sig = onoff;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (no & FC_DMA_2)
13662306a36Sopenharmony_ci		v.ctrl_208.DMA2_IRQ_Enable_sig = onoff;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	fc->write_ibi_reg(fc, ctrl_208, v);
13962306a36Sopenharmony_ci	return 0;
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_control_size_irq);
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ciint flexcop_dma_control_timer_irq(struct flexcop_device *fc,
14462306a36Sopenharmony_ci		flexcop_dma_index_t no,
14562306a36Sopenharmony_ci		int onoff)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	flexcop_ibi_value v = fc->read_ibi_reg(fc, ctrl_208);
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	if (no & FC_DMA_1)
15062306a36Sopenharmony_ci		v.ctrl_208.DMA1_Timer_Enable_sig = onoff;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	if (no & FC_DMA_2)
15362306a36Sopenharmony_ci		v.ctrl_208.DMA2_Timer_Enable_sig = onoff;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	fc->write_ibi_reg(fc, ctrl_208, v);
15662306a36Sopenharmony_ci	return 0;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_control_timer_irq);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci/* 1 cycles = 1.97 msec */
16162306a36Sopenharmony_ciint flexcop_dma_config_timer(struct flexcop_device *fc,
16262306a36Sopenharmony_ci		flexcop_dma_index_t dma_idx, u8 cycles)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014;
16562306a36Sopenharmony_ci	flexcop_ibi_value v = fc->read_ibi_reg(fc, r);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	flexcop_dma_remap(fc, dma_idx, 0);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	deb_info("%s\n", __func__);
17062306a36Sopenharmony_ci	v.dma_0x4_write.dmatimer = cycles;
17162306a36Sopenharmony_ci	fc->write_ibi_reg(fc, r, v);
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ciEXPORT_SYMBOL(flexcop_dma_config_timer);
17562306a36Sopenharmony_ci
176