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